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