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