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