chk4ubin.c revision 9507:e6271d3ec9f2
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
90static char	*whoami;
91static int	verbose		= 0;
92static int	inject_err	= 0;
93static int	no_err		= 0;
94static int	exitcode	= 0;
95static uint_t	pagemask	= 0x1fff;
96
97static 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};
104static int nsun4ubootables = sizeof (sun4u_bootables) / sizeof (char *);
105
106/*
107 * size check should be:
108 *	size &= 0x1fff, size > 0x1fe1 && size <= 0x1ff0
109 */
110static uint_t toxic_start	= 0x1fe2;
111static uint_t toxic_end		= 0x1ff0;
112
113/*
114 * Tag each error message so it shows up in the build summary mail
115 */
116static 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
124static int
125chk4ubin(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
229static void
230usage()
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
248int
249main(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