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