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
chk4ubin(char * root,char * binary)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
usage()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
main(int argc,char * argv[])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