xref: /original-bsd/usr.bin/nm/nm.c (revision 2ad443ff)
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