xref: /illumos-gate/usr/src/tools/chk4ubin/chk4ubin.c (revision c60a820f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2019 Peter Tribble.
29  */
30 
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <libgen.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <malloc.h>
41 #include <memory.h>
42 #include <libelf.h>
43 #include <gelf.h>
44 
45 /*
46  * Tool to inspect a sun4u bootable module for a symbol table size
47  * that will trigger a fatal error on older versions of OBP.
48  *
49  * The failure mode when booting is recorded in CR 6828121
50  * and appears as follows:
51  *
52  *	Executing last command: boot
53  *	Boot device: /pci@1f,0/pci@1/scsi@8/disk@0,0:a  File and args: kmdb
54  *
55  *	Error in Fcode execution !!!
56  *	Evaluating: to load-base init-program
57  *	Out of memory
58  *	Warning: Fcode sequence resulted in a net stack depth change of 1
59  *
60  *	Error in Fcode execution !!!
61  *	Evaluating: to load-base init-program
62  *
63  *	Evaluating: to load-base init-program
64  *	The file just loaded does not appear to be executable.
65  *	ok
66  *
67  * The OBP bug is CR 4777088, fixed in OBP versions 4.12.1 and forward.
68  *
69  * The OBP memory allocator for the memory into which the module's
70  * symbol table is read fails for a specific memory range on
71  * each page, where the size &= 0x1fff is > 0x1fe1 && <= 0x1ff0.
72  * Note the symbol table size is the size of both the SYMTAB
73  * and the STRTAB ELF sections.
74  *
75  * To prevent this problem on a given machine, update or patch the OBP.
76  *
77  * If this tool reports that a module has a symbol table size in
78  * the failing range, that build will not boot on any machine with
79  * this OBP problem.  The only known work-around is to make some
80  * source change to add or remove symbols to adjust the symbol table
81  * size outside the triggering range.
82  *
83  * Each sun4u bootable module is in theory affected by this, including
84  * cprboot, bootlst, and each unix module.  Although the serengeti
85  * (Sun-Fire) and opl (SPARC-Enterprise) OBP implementations never
86  * included this bug.  The bug only occurs for allocations
87  * pagesize or greater, and the only such OBP allocation is for a
88  * module's symbol table, for the sum of the SYMTAB and STRTAB
89  * sections.  The inetboot binary does not include these sections
90  * and is therefore also unaffected.
91  */
92 
93 static char	*whoami;
94 static int	verbose		= 0;
95 static int	inject_err	= 0;
96 static int	no_err		= 0;
97 static int	exitcode	= 0;
98 static uint_t	pagemask	= 0x1fff;
99 
100 static char *sun4u_bootables[] = {
101 	"platform/sun4u/kernel/sparcv9/unix",
102 	"platform/sun4u/cprboot",
103 	"platform/sun4u/bootlst"
104 };
105 static int nsun4ubootables = sizeof (sun4u_bootables) / sizeof (char *);
106 
107 /*
108  * size check should be:
109  *	size &= 0x1fff, size > 0x1fe1 && size <= 0x1ff0
110  */
111 static uint_t toxic_start	= 0x1fe2;
112 static uint_t toxic_end		= 0x1ff0;
113 
114 /*
115  * Tag each error message so it shows up in the build summary mail
116  */
117 static char *detailed_error_msg =
118 	"ERROR: This binary will not boot on any machine with an older\n"
119 	"ERROR: version of OBP.  See CR 4777088 and 6828121 for more details.\n"
120 	"ERROR: No work-around is possible other than making changes to\n"
121 	"ERROR: add/remove symbols from the module to move the symbol\n"
122 	"ERROR: table size outside the toxic range.\n";
123 
124 
125 static int
chk4ubin(char * root,char * binary)126 chk4ubin(char *root, char *binary)
127 {
128 	int		fd;
129 	Elf		*elf;
130 	Elf_Scn		*symscn;
131 	Elf_Scn		*strscn;
132 	GElf_Shdr	symhdr;
133 	GElf_Shdr	strhdr;
134 	int64_t		symtab_size;
135 	int64_t		strtab_size;
136 	int64_t		total;
137 	int		found_symtab = 0;
138 	int		found_strtab = 0;
139 	uint_t		off;
140 	int		rv = 1;
141 	char		path[MAXPATHLEN];
142 
143 	if (root == NULL) {
144 		(void) snprintf(path, sizeof (path), "%s", binary);
145 	} else {
146 		(void) snprintf(path, sizeof (path), "%s/%s", root, binary);
147 	}
148 
149 	if ((fd = open(path, O_RDONLY)) == -1) {
150 		(void) printf("%s: cannot open %s - %s\n",
151 		    whoami, path, strerror(errno));
152 		return (1);
153 	}
154 
155 	if (elf_version(EV_CURRENT) == EV_NONE) {
156 		(void) printf("%s: unsupported version.\n", whoami);
157 		(void) close(fd);
158 		return (1);
159 	}
160 
161 	elf = elf_begin(fd, ELF_C_READ, NULL);
162 	if (elf == NULL) {
163 		(void) printf("%s: elf_begin() failed.\n", whoami);
164 		(void) close(fd);
165 		return (1);
166 	}
167 
168 	symscn = NULL;
169 	while ((symscn = elf_nextscn(elf, symscn)) != NULL) {
170 		(void) gelf_getshdr(symscn, &symhdr);
171 		switch (symhdr.sh_type) {
172 		case SHT_SYMTAB:
173 			found_symtab = 1;
174 			symtab_size = symhdr.sh_size;
175 			strscn = elf_getscn(elf, symhdr.sh_link);
176 			if (strscn != NULL) {
177 				(void) gelf_getshdr(strscn, &strhdr);
178 				strtab_size = strhdr.sh_size;
179 				found_strtab = 1;
180 			}
181 			break;
182 		}
183 		if (found_symtab && found_strtab)
184 			break;
185 	}
186 
187 	(void) elf_end(elf);
188 	(void) close(fd);
189 
190 	if (found_symtab && found_strtab) {
191 		int err;
192 		total = symtab_size + strtab_size;
193 		off = total & pagemask;
194 		err = (off >= toxic_start && off <= toxic_end);
195 		if (inject_err || err) {
196 			(void) printf("%s: ERROR: %s\n", whoami, binary);
197 			(void) printf("ERROR: symbol table size 0x%llx is "
198 			    "in toxic range (0x%x - 0x%x)!\n",
199 			    total, toxic_start, toxic_end);
200 			(void) printf("%s", detailed_error_msg);
201 		} else {
202 			rv = 0;
203 			(void) printf("%s: %s ok\n", whoami, binary);
204 			if (verbose) {
205 				(void) printf("symbol table size 0x%llx "
206 				    "not in toxic range (0x%x - 0x%x)\n",
207 				    total, toxic_start, toxic_end);
208 			}
209 		}
210 		if (verbose) {
211 			(void) printf(".symtab size: 0x%llx\n",
212 			    symtab_size);
213 			(void) printf(".strtab size: 0x%llx\n",
214 			    strtab_size);
215 			(void) printf("total:        0x%llx "
216 			    "(0x%llx, 0x%llx)\n", total, (total & ~pagemask),
217 			    (total & pagemask));
218 		}
219 		if (verbose || err || inject_err)
220 			(void) printf("\n");
221 	} else {
222 		if (!found_symtab && !found_strtab) {
223 			(void) fprintf(stderr,
224 			    "%s: %s - no symtab or strtab section found\n",
225 			    whoami, binary);
226 		} else if (!found_symtab) {
227 			(void) fprintf(stderr,
228 			    "%s: %s - no symtab section found\n",
229 			    whoami, binary);
230 		} else if (!found_strtab) {
231 			(void) fprintf(stderr,
232 			    "%s: %s - no strtab section found\n",
233 			    whoami, binary);
234 		}
235 	}
236 
237 	return (rv);
238 }
239 
240 static void
usage()241 usage()
242 {
243 	int i;
244 
245 	(void) fprintf(stderr,
246 	    "usage: %s [-n] [-v] [-r <root>] [<binary>] ...\n", whoami);
247 	(void) fprintf(stderr,
248 	    "    -n: exit with 0 even with an error detected to allow\n");
249 	(void) fprintf(stderr,
250 	    "        a build to succeed even with a failing binary\n");
251 	(void) fprintf(stderr,
252 	    "The default list of binaries checked if none supplied is:\n");
253 	for (i = 0; i < nsun4ubootables; i++) {
254 		(void) fprintf(stderr, "    %s\n", sun4u_bootables[i]);
255 	}
256 	exit(0);
257 }
258 
259 int
main(int argc,char * argv[])260 main(int argc, char *argv[])
261 {
262 	int	i;
263 	char	*root = NULL;
264 
265 	whoami = basename(argv[0]);
266 
267 	opterr = 0;
268 	while ((i = getopt(argc, argv, "enr:R:v")) != -1) {
269 		switch (i) {
270 		case 'v':
271 			verbose = 1;
272 			break;
273 		case 'e':
274 			inject_err = 1;
275 			break;
276 		case 'n':
277 			no_err = 1;
278 			break;
279 		case 'r':
280 		case 'R':
281 			root = optarg;
282 			break;
283 		default:
284 			usage();
285 			break;
286 		}
287 	}
288 
289 	if (optind < argc) {
290 		for (i = optind; i < argc; i++) {
291 			if (chk4ubin(root, argv[i]) != 0)
292 				exitcode = 1;
293 		}
294 	} else {
295 		for (i = 0; i < nsun4ubootables; i++) {
296 			if (root == NULL)
297 				root = "/";
298 			if (chk4ubin(root, sun4u_bootables[i]) != 0)
299 				exitcode = 1;
300 		}
301 	}
302 
303 	return (no_err ? 0 : exitcode);
304 }
305