1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Hans Huebner. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)nm.c 5.7 (Berkeley) 02/22/91"; 19 #endif /* not lint */ 20 21 #include <sys/types.h> 22 #include <a.out.h> 23 #include <stab.h> 24 #include <ar.h> 25 #include <ranlib.h> 26 #include <unistd.h> 27 #include <errno.h> 28 #include <ctype.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 33 int ignore_bad_archive_entries = 1; 34 int print_only_external_symbols; 35 int print_only_undefined_symbols; 36 int print_all_symbols; 37 int print_file_each_line; 38 int cmp_value(), cmp_name(); 39 int (*sort_func)() = cmp_name; 40 41 enum { FORWARD, BACKWARD } sort_direction = FORWARD; 42 int fcount; 43 44 /* some macros for symbol type (nlist.n_type) handling */ 45 #define IS_DEBUGGER_SYMBOL(x) ((x) & N_STAB) 46 #define IS_EXTERNAL(x) ((x) & N_EXT) 47 #define SYMBOL_TYPE(x) ((x) & (N_TYPE | N_STAB)) 48 49 /* 50 * main() 51 * parse command line, execute process_file() for each file 52 * specified on the command line. 53 */ 54 main(argc, argv) 55 int argc; 56 char **argv; 57 { 58 extern int optind; 59 int ch, errors; 60 61 while ((ch = getopt(argc, argv, "agnopruw")) != EOF) { 62 switch (ch) { 63 case 'a': 64 print_all_symbols = 1; 65 break; 66 case 'g': 67 print_only_external_symbols = 1; 68 break; 69 case 'n': 70 sort_func = cmp_value; 71 break; 72 case 'o': 73 print_file_each_line = 1; 74 break; 75 case 'p': 76 sort_func = NULL; 77 break; 78 case 'r': 79 sort_direction = BACKWARD; 80 break; 81 case 'u': 82 print_only_undefined_symbols = 1; 83 break; 84 case 'w': 85 ignore_bad_archive_entries = 0; 86 break; 87 case '?': 88 default: 89 usage(); 90 } 91 } 92 fcount = argc - optind; 93 argv += optind; 94 95 if (!fcount) 96 errors = process_file("a.out"); 97 else { 98 errors = 0; 99 do { 100 errors |= process_file(*argv); 101 } while (*++argv); 102 } 103 exit(errors); 104 } 105 106 /* 107 * process_file() 108 * show symbols in the file given as an argument. Accepts archive and 109 * object files as input. 110 */ 111 process_file(fname) 112 char *fname; 113 { 114 struct exec exec_head; 115 FILE *fp; 116 int retval; 117 char magic[SARMAG]; 118 119 if (!(fp = fopen(fname, "r"))) { 120 (void)fprintf(stderr, "nm: cannot read %s.\n", fname); 121 return(1); 122 } 123 124 if (fcount > 1) 125 (void)printf("\n%s:\n", fname); 126 127 /* 128 * first check whether this is an object file - read a object 129 * header, and skip back to the beginning 130 */ 131 if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) { 132 (void)fprintf(stderr, "nm: %s: bad format.\n", fname); 133 (void)fclose(fp); 134 return(1); 135 } 136 rewind(fp); 137 138 /* this could be an archive */ 139 if (N_BADMAG(exec_head)) { 140 if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 || 141 strncmp(magic, ARMAG, SARMAG)) { 142 (void)fprintf(stderr, 143 "nm: %s: not object file or archive.\n", fname); 144 (void)fclose(fp); 145 return(1); 146 } 147 retval = show_archive(fname, fp); 148 } else 149 retval = show_objfile(fname, fp); 150 (void)fclose(fp); 151 return(retval); 152 } 153 154 /* 155 * show_archive() 156 * show symbols in the given archive file 157 */ 158 show_archive(fname, fp) 159 char *fname; 160 FILE *fp; 161 { 162 struct ar_hdr ar_head; 163 struct exec exec_head; 164 int i, rval; 165 long last_ar_off; 166 char *p, *name, *emalloc(); 167 long atol(); 168 169 name = emalloc(sizeof(ar_head.ar_name) + strlen(fname) + 3); 170 171 rval = 0; 172 173 /* while there are more entries in the archive */ 174 while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) { 175 /* bad archive entry - stop processing this archive */ 176 if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { 177 (void)fprintf(stderr, 178 "nm: %s: bad format archive header", fname); 179 (void)free(name); 180 return(1); 181 } 182 183 /* remember start position of current archive object */ 184 last_ar_off = ftell(fp); 185 186 /* skip ranlib entries */ 187 if (!strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1)) 188 goto skip; 189 190 /* 191 * construct a name of the form "archive.a:obj.o:" for the 192 * current archive entry if the object name is to be printed 193 * on each output line 194 */ 195 p = name; 196 if (print_file_each_line) 197 p += sprintf(p, "%s:", fname); 198 for (i = 0; i < sizeof(ar_head.ar_name); ++i) 199 if (ar_head.ar_name[i] && ar_head.ar_name[i] != ' ') 200 *p++ = ar_head.ar_name[i]; 201 *p++ = '\0'; 202 203 /* get and check current object's header */ 204 if (fread((char *)&exec_head, sizeof(exec_head), 205 (size_t)1, fp) != 1) { 206 (void)fprintf(stderr, "nm: %s: premature EOF.\n", name); 207 (void)free(name); 208 return(1); 209 } 210 211 if (N_BADMAG(exec_head)) { 212 if (!ignore_bad_archive_entries) { 213 (void)fprintf(stderr, 214 "nm: %s: bad format.\n", name); 215 rval = 1; 216 } 217 } else { 218 (void)fseek(fp, (long)-sizeof(exec_head), 219 SEEK_CUR); 220 if (!print_file_each_line) 221 (void)printf("\n%s:\n", name); 222 rval |= show_objfile(name, fp); 223 } 224 225 /* 226 * skip to next archive object - it starts at the next 227 * even byte boundary 228 */ 229 #define even(x) (((x) + 1) & ~1) 230 skip: if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)), 231 SEEK_SET)) { 232 (void)fprintf(stderr, 233 "nm: %s: %s\n", fname, strerror(errno)); 234 (void)free(name); 235 return(1); 236 } 237 } 238 (void)free(name); 239 return(rval); 240 } 241 242 /* 243 * show_objfile() 244 * show symbols from the object file pointed to by fp. The current 245 * file pointer for fp is expected to be at the beginning of an a.out 246 * header. 247 */ 248 show_objfile(objname, fp) 249 char *objname; 250 FILE *fp; 251 { 252 register struct nlist *names, *np; 253 register int i, nnames, nrawnames; 254 struct exec head; 255 long stabsize; 256 char *stab, *emalloc(); 257 258 /* read a.out header */ 259 if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) { 260 (void)fprintf(stderr, 261 "nm: %s: cannot read header.\n", objname); 262 return(1); 263 } 264 265 /* 266 * skip back to the header - the N_-macros return values relative 267 * to the beginning of the a.out header 268 */ 269 if (fseek(fp, (long)-sizeof(head), SEEK_CUR)) { 270 (void)fprintf(stderr, 271 "nm: %s: %s\n", objname, strerror(errno)); 272 return(1); 273 } 274 275 /* stop if this is no valid object file */ 276 if (N_BADMAG(head)) { 277 (void)fprintf(stderr, 278 "nm: %s: bad format.\n", objname); 279 return(1); 280 } 281 282 /* stop if the object file contains no symbol table */ 283 if (!head.a_syms) { 284 (void)fprintf(stderr, 285 "nm: %s: no name list.\n", objname); 286 return(1); 287 } 288 289 if (fseek(fp, (long)N_SYMOFF(head), SEEK_CUR)) { 290 (void)fprintf(stderr, 291 "nm: %s: %s\n", objname, strerror(errno)); 292 return(1); 293 } 294 295 /* get memory for the symbol table */ 296 names = (struct nlist *)emalloc((size_t)head.a_syms); 297 nrawnames = head.a_syms / sizeof(*names); 298 if (fread((char *)names, (size_t)head.a_syms, (size_t)1, fp) != 1) { 299 (void)fprintf(stderr, 300 "nm: %s: cannot read symbol table.\n", objname); 301 (void)free((char *)names); 302 return(1); 303 } 304 305 /* 306 * Following the symbol table comes the string table. The first 307 * 4-byte-integer gives the total size of the string table 308 * _including_ the size specification itself. 309 */ 310 if (fread((char *)&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) { 311 (void)fprintf(stderr, 312 "nm: %s: cannot read stab size.\n", objname); 313 (void)free((char *)names); 314 return(1); 315 } 316 stab = emalloc((size_t)stabsize); 317 318 /* 319 * read the string table offset by 4 - all indices into the string 320 * table include the size specification. 321 */ 322 stabsize -= 4; /* we already have the size */ 323 if (fread(stab + 4, (size_t)stabsize, (size_t)1, fp) != 1) { 324 (void)fprintf(stderr, 325 "nm: %s: stab truncated..\n", objname); 326 (void)free((char *)names); 327 (void)free(stab); 328 return(1); 329 } 330 331 /* 332 * fix up the symbol table and filter out unwanted entries 333 * 334 * common symbols are characterized by a n_type of N_UNDF and a 335 * non-zero n_value -- change n_type to N_COMM for all such 336 * symbols to make life easier later. 337 * 338 * filter out all entries which we don't want to print anyway 339 */ 340 for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { 341 if (SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value) 342 np->n_type = N_COMM | (np->n_type & N_EXT); 343 if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type)) 344 continue; 345 if (print_only_external_symbols && !IS_EXTERNAL(np->n_type)) 346 continue; 347 if (print_only_undefined_symbols && 348 SYMBOL_TYPE(np->n_type) != N_UNDF) 349 continue; 350 351 /* 352 * make n_un.n_name a character pointer by adding the string 353 * table's base to n_un.n_strx 354 * 355 * don't mess with zero offsets 356 */ 357 if (np->n_un.n_strx) 358 np->n_un.n_name = stab + np->n_un.n_strx; 359 else 360 np->n_un.n_name = ""; 361 names[nnames++] = *np; 362 } 363 364 /* sort the symbol table if applicable */ 365 if (sort_func) 366 qsort((char *)names, (size_t)nnames, sizeof(*names), sort_func); 367 368 /* print out symbols */ 369 for (np = names, i = 0; i < nnames; np++, i++) 370 print_symbol(objname, np); 371 372 (void)free((char *)names); 373 (void)free(stab); 374 return(0); 375 } 376 377 /* 378 * print_symbol() 379 * show one symbol 380 */ 381 print_symbol(objname, sym) 382 char *objname; 383 register struct nlist *sym; 384 { 385 char *typestring(), typeletter(); 386 387 if (print_file_each_line) 388 (void)printf("%s:", objname); 389 390 /* 391 * handle undefined-only format seperately (no space is 392 * left for symbol values, no type field is printed) 393 */ 394 if (print_only_undefined_symbols) { 395 (void)puts(sym->n_un.n_name); 396 return; 397 } 398 399 /* print symbol's value */ 400 if (SYMBOL_TYPE(sym->n_type) == N_UNDF) 401 (void)printf(" "); 402 else 403 (void)printf("%08lx", sym->n_value); 404 405 /* print type information */ 406 if (IS_DEBUGGER_SYMBOL(sym->n_type)) 407 (void)printf(" - %02x %04x %5s ", sym->n_other, 408 sym->n_desc&0xffff, typestring(sym->n_type)); 409 else 410 (void)printf(" %c ", typeletter(sym->n_type)); 411 412 /* print the symbol's name */ 413 (void)puts(sym->n_un.n_name); 414 } 415 416 /* 417 * typestring() 418 * return the a description string for an STAB entry 419 */ 420 char * 421 typestring(type) 422 register u_char type; 423 { 424 switch(type) { 425 case N_BCOMM: 426 return("BCOMM"); 427 case N_ECOML: 428 return("ECOML"); 429 case N_ECOMM: 430 return("ECOMM"); 431 case N_ENTRY: 432 return("ENTRY"); 433 case N_FNAME: 434 return("FNAME"); 435 case N_FUN: 436 return("FUN"); 437 case N_GSYM: 438 return("GSYM"); 439 case N_LBRAC: 440 return("LBRAC"); 441 case N_LCSYM: 442 return("LCSYM"); 443 case N_LENG: 444 return("LENG"); 445 case N_LSYM: 446 return("LSYM"); 447 case N_PC: 448 return("PC"); 449 case N_PSYM: 450 return("PSYM"); 451 case N_RBRAC: 452 return("RBRAC"); 453 case N_RSYM: 454 return("RSYM"); 455 case N_SLINE: 456 return("SLINE"); 457 case N_SO: 458 return("SO"); 459 case N_SOL: 460 return("SOL"); 461 case N_SSYM: 462 return("SSYM"); 463 case N_STSYM: 464 return("STSYM"); 465 } 466 return("???"); 467 } 468 469 /* 470 * typeletter() 471 * return a description letter for the given basic type code of an 472 * symbol table entry. The return value will be upper case for 473 * external, lower case for internal symbols. 474 */ 475 char 476 typeletter(type) 477 u_char type; 478 { 479 switch(SYMBOL_TYPE(type)) { 480 case N_ABS: 481 return(IS_EXTERNAL(type) ? 'A' : 'a'); 482 case N_BSS: 483 return(IS_EXTERNAL(type) ? 'B' : 'b'); 484 case N_COMM: 485 return(IS_EXTERNAL(type) ? 'C' : 'c'); 486 case N_DATA: 487 return(IS_EXTERNAL(type) ? 'D' : 'd'); 488 case N_FN: 489 return(IS_EXTERNAL(type) ? 'F' : 'f'); 490 case N_TEXT: 491 return(IS_EXTERNAL(type) ? 'T' : 't'); 492 case N_UNDF: 493 return(IS_EXTERNAL(type) ? 'U' : 'u'); 494 } 495 return('?'); 496 } 497 498 /* 499 * cmp_name() 500 * compare two symbols by their names 501 */ 502 cmp_name(a0, b0) 503 void *a0, *b0; 504 { 505 struct nlist *a = a0, *b = b0; 506 507 return(sort_direction == FORWARD ? 508 strcmp(a->n_un.n_name, b->n_un.n_name) : 509 strcmp(b->n_un.n_name, a->n_un.n_name)); 510 } 511 512 /* 513 * cmp_value() 514 * compare two symbols by their values 515 */ 516 cmp_value(a0, b0) 517 void *a0, *b0; 518 { 519 register struct nlist *a = a0, *b = b0; 520 521 if (SYMBOL_TYPE(a->n_type) == N_UNDF) 522 if (SYMBOL_TYPE(b->n_type) == N_UNDF) 523 return(0); 524 else 525 return(-1); 526 else if (SYMBOL_TYPE(b->n_type) == N_UNDF) 527 return(1); 528 if (a->n_value == b->n_value) 529 return(cmp_name((void *)a, (void *)b)); 530 return(sort_direction == FORWARD ? a->n_value > b->n_value : 531 a->n_value < b->n_value); 532 } 533 534 char * 535 emalloc(size) 536 size_t size; 537 { 538 char *p; 539 540 /* NOSTRICT */ 541 if (!(p = malloc(size))) { 542 (void)fprintf(stderr, "nm: no more memory.\n"); 543 exit(1); 544 } 545 return(p); 546 } 547 548 usage() 549 { 550 (void)fprintf(stderr, "usage: nm [-agnopruw] [file ...]\n"); 551 exit(1); 552 } 553