xref: /openbsd/usr.bin/nm/nm.c (revision ce7279d8)
1 /*	$OpenBSD: nm.c,v 1.56 2024/05/21 05:00:48 jsg Exp $	*/
2 /*	$NetBSD: nm.c,v 1.7 1996/01/14 23:04:03 pk Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Hans Huebner.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/mman.h>
38 #include <a.out.h>
39 #include <elf.h>
40 #include <ar.h>
41 #include <ranlib.h>
42 #include <unistd.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <ctype.h>
46 #include <link.h>
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <getopt.h>
52 #include "util.h"
53 #include "elfuncs.h"
54 
55 #define	SYMTABMAG	"/ "
56 #define	STRTABMAG	"//"
57 #define	SYM64MAG	"/SYM64/         "
58 
59 union hdr {
60 	Elf32_Ehdr elf32;
61 	Elf64_Ehdr elf64;
62 };
63 
64 int armap;
65 int demangle;
66 int non_object_warning;
67 int print_only_external_symbols;
68 int print_only_undefined_symbols;
69 int print_all_symbols;
70 int print_file_each_line;
71 int show_extensions;
72 int issize;
73 char posix_fmtstr[6];
74 int posix_output;
75 char posix_radix = 'x';
76 int usemmap = 1;
77 int dynamic_only;
78 
79 /* size vars */
80 unsigned long total_text, total_data, total_bss, total_total;
81 int non_object_warning, print_totals;
82 
83 int rev;
84 int fname(const void *, const void *);
85 int rname(const void *, const void *);
86 int value(const void *, const void *);
87 int (*sfunc)(const void *, const void *) = fname;
88 char typeletter(struct xnlist *);
89 int mmbr_name(struct ar_hdr *, char **, int, int *, FILE *);
90 int show_symtab(off_t, u_long, const char *, FILE *);
91 int show_symdef(off_t, u_long, const char *, FILE *);
92 
93 /* some macros for symbol type (nlist.n_type) handling */
94 #define	IS_EXTERNAL(x)		((x) & N_EXT)
95 #define	SYMBOL_TYPE(x)		((x) & (N_TYPE | N_STAB))
96 
97 void	 pipe2cppfilt(void);
98 void	 usage(void);
99 char	*symname(struct xnlist *);
100 int	process_file(int, const char *);
101 int	show_archive(int, const char *, FILE *);
102 int	show_file(int, int, const char *, FILE *fp, off_t, union hdr *);
103 void	print_symbol(const char *, struct xnlist *);
104 
105 #define	OPTSTRING_NM	"aABCDegnopPrst:uvw"
106 const struct option longopts_nm[] = {
107 	{ "debug-syms",		no_argument,		0,	'a' },
108 	{ "demangle",		no_argument,		0,	'C' },
109 	{ "dynamic",		no_argument,		0,	'D' },
110 	{ "extern-only",	no_argument,		0,	'g' },
111 /*	{ "line-numbers",	no_argument,		0,	'l' }, */
112 	{ "no-sort",		no_argument,		0,	'p' },
113 	{ "numeric-sort",	no_argument,		0,	'n' },
114 	{ "print-armap",	no_argument,		0,	's' },
115 	{ "print-file-name",	no_argument,		0,	'o' },
116 	{ "reverse-sort",	no_argument,		0,	'r' },
117 /*	{ "size-sort",		no_argument,		&szval,	1 }, */
118 	{ "undefined-only",	no_argument,		0,	'u' },
119 	{ "help",		no_argument,		0,	'?' },
120 	{ NULL }
121 };
122 
123 /*
124  * main()
125  *	parse command line, execute process_file() for each file
126  *	specified on the command line.
127  */
128 int
main(int argc,char * argv[])129 main(int argc, char *argv[])
130 {
131 	extern char *__progname;
132 	extern int optind;
133 	const char *optstr;
134 	const struct option *lopts;
135 	int ch, eval;
136 
137 	if (pledge("stdio rpath proc exec", NULL) == -1)
138 		err(1, "pledge");
139 
140 	optstr = OPTSTRING_NM;
141 	lopts = longopts_nm;
142 	if (!strcmp(__progname, "size")) {
143 		if (pledge("stdio rpath", NULL) == -1)
144 			err(1, "pledge");
145 
146 		issize = 1;
147 		optstr = "tw";
148 		lopts = NULL;
149 	}
150 
151 	while ((ch = getopt_long(argc, argv, optstr, lopts, NULL)) != -1) {
152 		switch (ch) {
153 		case 'a':
154 			print_all_symbols = 1;
155 			break;
156 		case 'B':
157 			/* no-op, compat with gnu-nm */
158 			break;
159 		case 'C':
160 			demangle = 1;
161 			break;
162 		case 'D':
163 			dynamic_only = 1;
164 			break;
165 		case 'e':
166 			show_extensions = 1;
167 			break;
168 		case 'g':
169 			print_only_external_symbols = 1;
170 			break;
171 		case 'n':
172 		case 'v':
173 			sfunc = value;
174 			break;
175 		case 'A':
176 		case 'o':
177 			print_file_each_line = 1;
178 			break;
179 		case 'p':
180 			sfunc = NULL;
181 			break;
182 		case 'P':
183 			posix_output = 1;
184 			break;
185 		case 'r':
186 			rev = 1;
187 			break;
188 		case 's':
189 			armap = 1;
190 			break;
191 		case 'u':
192 			print_only_undefined_symbols = 1;
193 			break;
194 		case 'w':
195 			non_object_warning = 1;
196 			break;
197 		case 't':
198 			if (issize) {
199 				print_totals = 1;
200 			} else {
201 				posix_radix = *optarg;
202 				if (strlen(optarg) != 1 ||
203 				    (posix_radix != 'd' && posix_radix != 'o' &&
204 				     posix_radix != 'x'))
205 					usage();
206 			}
207 			break;
208 		default:
209 			usage();
210 		}
211 	}
212 
213 	if (posix_output)
214 		(void)snprintf(posix_fmtstr, sizeof posix_fmtstr, "%%%c %%%c",
215 		    posix_radix, posix_radix);
216 	if (demangle)
217 		pipe2cppfilt();
218 
219 	if (pledge("stdio rpath", NULL) == -1)
220 		err(1, "pledge");
221 
222 	argv += optind;
223 	argc -= optind;
224 
225 	if (rev && sfunc == fname)
226 		sfunc = rname;
227 
228 	eval = 0;
229 	if (*argv)
230 		do {
231 			eval |= process_file(argc, *argv);
232 		} while (*++argv);
233 	else
234 		eval |= process_file(1, "a.out");
235 
236 	if (issize && print_totals)
237 		printf("\n%lu\t%lu\t%lu\t%lu\t%lx\tTOTAL\n",
238 		    total_text, total_data, total_bss,
239 		    total_total, total_total);
240 	exit(eval);
241 }
242 
243 /*
244  * process_file()
245  *	show symbols in the file given as an argument.  Accepts archive and
246  *	object files as input.
247  */
248 int
process_file(int count,const char * fname)249 process_file(int count, const char *fname)
250 {
251 	union hdr exec_head;
252 	FILE *fp;
253 	int retval;
254 	size_t bytes;
255 	char magic[SARMAG];
256 
257 	if (!(fp = fopen(fname, "r"))) {
258 		warn("cannot read %s", fname);
259 		return(1);
260 	}
261 
262 	if (!issize && count > 1)
263 		(void)printf("\n%s:\n", fname);
264 
265 	/*
266 	 * first check whether this is an object file - read a object
267 	 * header, and skip back to the beginning
268 	 */
269 	bzero(&exec_head, sizeof(exec_head));
270 	bytes = fread((char *)&exec_head, 1, sizeof(exec_head), fp);
271 	if (bytes < sizeof(exec_head)) {
272 		if (bytes < sizeof(exec_head.elf32) || IS_ELF(exec_head.elf32)) {
273 			warnx("%s: bad format", fname);
274 			(void)fclose(fp);
275 			return(1);
276 		}
277 	}
278 	rewind(fp);
279 
280 	/* this could be an archive */
281 	if (!IS_ELF(exec_head.elf32)) {
282 		if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 ||
283 		    strncmp(magic, ARMAG, SARMAG)) {
284 			warnx("%s: not object file or archive", fname);
285 			(void)fclose(fp);
286 			return(1);
287 		}
288 		retval = show_archive(count, fname, fp);
289 	} else
290 		retval = show_file(count, 1, fname, fp, 0, &exec_head);
291 	(void)fclose(fp);
292 	return(retval);
293 }
294 
295 char *nametab;
296 
297 /*
298  *
299  *	given the archive member header -- produce member name
300  */
301 int
mmbr_name(struct ar_hdr * arh,char ** name,int baselen,int * namelen,FILE * fp)302 mmbr_name(struct ar_hdr *arh, char **name, int baselen, int *namelen, FILE *fp)
303 {
304 	char *p = *name + strlen(*name);
305 	long i;
306 
307 	if (nametab && arh->ar_name[0] == '/') {
308 		int len;
309 
310 		i = atol(&arh->ar_name[1]);
311 		len = strlen(&nametab[i]) + 1;
312 		if (len > *namelen) {
313 			p -= (long)*name;
314 			if ((*name = realloc(*name, baselen+len)) == NULL)
315 				err(1, NULL);
316 			*namelen = len;
317 			p += (long)*name;
318 		}
319 		strlcpy(p, &nametab[i], len);
320 		p += len - 1;
321 	} else
322 #ifdef AR_EFMT1
323 	/*
324 	 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
325 	 * first <namelen> bytes of the file
326 	 */
327 	if ((arh->ar_name[0] == '#') &&
328 	    (arh->ar_name[1] == '1') &&
329 	    (arh->ar_name[2] == '/') &&
330 	    (isdigit((unsigned char)arh->ar_name[3]))) {
331 		int len = atoi(&arh->ar_name[3]);
332 
333 		if (len > *namelen) {
334 			p -= (long)*name;
335 			if ((*name = realloc(*name, baselen+len)) == NULL)
336 				err(1, NULL);
337 			*namelen = len;
338 			p += (long)*name;
339 		}
340 		if (fread(p, len, 1, fp) != 1) {
341 			warnx("%s: premature EOF", *name);
342 			free(*name);
343 			return(1);
344 		}
345 		p += len;
346 	} else
347 #endif
348 	for (i = 0; i < sizeof(arh->ar_name); ++i)
349 		if (arh->ar_name[i] && arh->ar_name[i] != ' ')
350 			*p++ = arh->ar_name[i];
351 	*p = '\0';
352 	if (p[-1] == '/')
353 		*--p = '\0';
354 
355 	return (0);
356 }
357 
358 /*
359  * show_symtab()
360  *	show archive ranlib index (fs5)
361  */
362 int
show_symtab(off_t off,u_long len,const char * name,FILE * fp)363 show_symtab(off_t off, u_long len, const char *name, FILE *fp)
364 {
365 	struct ar_hdr ar_head;
366 	int *symtab, *ps;
367 	char *strtab, *p;
368 	int num, rval = 0;
369 	int namelen;
370 	off_t restore;
371 
372 	restore = ftello(fp);
373 
374 	MMAP(symtab, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off);
375 	if (symtab == MAP_FAILED)
376 		return (1);
377 
378 	namelen = sizeof(ar_head.ar_name);
379 	if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) {
380 		warn("%s: malloc", name);
381 		MUNMAP(symtab, len);
382 		return (1);
383 	}
384 
385 	printf("\nArchive index:\n");
386 	num = betoh32(*symtab);
387 	strtab = (char *)(symtab + num + 1);
388 	for (ps = symtab + 1; num--; ps++, strtab += strlen(strtab) + 1) {
389 		if (fseeko(fp, betoh32(*ps), SEEK_SET)) {
390 			warn("%s: fseeko", name);
391 			rval = 1;
392 			break;
393 		}
394 
395 		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
396 		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
397 			warnx("%s: member fseeko", name);
398 			rval = 1;
399 			break;
400 		}
401 
402 		*p = '\0';
403 		if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) {
404 			rval = 1;
405 			break;
406 		}
407 
408 		printf("%s in %s\n", strtab, p);
409 	}
410 
411 	fseeko(fp, restore, SEEK_SET);
412 
413 	free(p);
414 	MUNMAP(symtab, len);
415 	return (rval);
416 }
417 
418 /*
419  * show_symdef()
420  *	show archive ranlib index (gob)
421  */
422 int
show_symdef(off_t off,u_long len,const char * name,FILE * fp)423 show_symdef(off_t off, u_long len, const char *name, FILE *fp)
424 {
425 	struct ranlib *prn, *eprn;
426 	struct ar_hdr ar_head;
427 	char *symdef;
428 	char *strtab, *p;
429 	u_long size;
430 	int namelen, rval = 0;
431 
432 	MMAP(symdef, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off);
433 	if (symdef == MAP_FAILED)
434 		return (1);
435 	if (usemmap)
436 		(void)madvise(symdef, len, MADV_SEQUENTIAL);
437 
438 	namelen = sizeof(ar_head.ar_name);
439 	if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) {
440 		warn("%s: malloc", name);
441 		MUNMAP(symdef, len);
442 		return (1);
443 	}
444 
445 	size = *(u_long *)symdef;
446 	prn = (struct ranlib *)(symdef + sizeof(u_long));
447 	eprn = prn + size / sizeof(*prn);
448 	strtab = symdef + sizeof(u_long) + size + sizeof(u_long);
449 
450 	printf("\nArchive index:\n");
451 	for (; prn < eprn; prn++) {
452 		if (fseeko(fp, prn->ran_off, SEEK_SET)) {
453 			warn("%s: fseeko", name);
454 			rval = 1;
455 			break;
456 		}
457 
458 		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
459 		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
460 			warnx("%s: member fseeko", name);
461 			rval = 1;
462 			break;
463 		}
464 
465 		*p = '\0';
466 		if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) {
467 			rval = 1;
468 			break;
469 		}
470 
471 		printf("%s in %s\n", strtab + prn->ran_un.ran_strx, p);
472 	}
473 
474 	free(p);
475 	MUNMAP(symdef, len);
476 	return (rval);
477 }
478 
479 /*
480  * show_archive()
481  *	show symbols in the given archive file
482  */
483 int
show_archive(int count,const char * fname,FILE * fp)484 show_archive(int count, const char *fname, FILE *fp)
485 {
486 	struct ar_hdr ar_head;
487 	union hdr exec_head;
488 	int i, rval;
489 	off_t last_ar_off, foff, symtaboff;
490 	char *name;
491 	int baselen, namelen;
492 	u_long mmbrlen, symtablen;
493 
494 	baselen = strlen(fname) + 3;
495 	if (posix_output)
496 		baselen += 2;
497 	namelen = sizeof(ar_head.ar_name);
498 	if ((name = malloc(baselen + namelen)) == NULL)
499 		err(1, NULL);
500 
501 	rval = 0;
502 	nametab = NULL;
503 	symtaboff = 0;
504 	symtablen = 0;
505 
506 	/* while there are more entries in the archive */
507 	while (fread(&ar_head, sizeof(ar_head), 1, fp) == 1) {
508 		/* bad archive entry - stop processing this archive */
509 		if (memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
510 			warnx("%s: bad format archive header", fname);
511 			rval = 1;
512 			break;
513 		}
514 
515 		/* remember start position of current archive object */
516 		last_ar_off = ftello(fp);
517 		mmbrlen = atol(ar_head.ar_size);
518 
519 		if (strncmp(ar_head.ar_name, RANLIBMAG,
520 		    sizeof(RANLIBMAG) - 1) == 0) {
521 			if (!issize && armap &&
522 			    show_symdef(last_ar_off, mmbrlen, fname, fp)) {
523 				rval = 1;
524 				break;
525 			}
526 			goto skip;
527 		} else if (strncmp(ar_head.ar_name, SYMTABMAG,
528 		    sizeof(SYMTABMAG) - 1) == 0) {
529 			/* if nametab hasn't been seen yet -- doit later */
530 			if (!nametab) {
531 				symtablen = mmbrlen;
532 				symtaboff = last_ar_off;
533 				goto skip;
534 			}
535 
536 			/* load the Sys5 long names table */
537 		} else if (strncmp(ar_head.ar_name, STRTABMAG,
538 		    sizeof(STRTABMAG) - 1) == 0) {
539 			char *p;
540 
541 			if ((nametab = malloc(mmbrlen)) == NULL) {
542 				warn("%s: nametab", fname);
543 				rval = 1;
544 				break;
545 			}
546 
547 			if (fread(nametab, mmbrlen, (size_t)1, fp) != 1) {
548 				warnx("%s: premature EOF", fname);
549 				rval = 1;
550 				break;
551 			}
552 
553 			for (p = nametab, i = mmbrlen; i--; p++)
554 				if (*p == '\n')
555 					*p = '\0';
556 
557 			if (issize || !armap || !symtablen || !symtaboff)
558 				goto skip;
559 		}
560 #ifdef __mips64
561 		else if (memcmp(ar_head.ar_name, SYM64MAG,
562 		    sizeof(ar_head.ar_name)) == 0) {
563 			/* IRIX6-compatible archive map */
564 			goto skip;
565 		}
566 #endif
567 
568 		if (!issize && armap && symtablen && symtaboff) {
569 			if (show_symtab(symtaboff, symtablen, fname, fp)) {
570 				rval = 1;
571 				break;
572 			} else {
573 				symtaboff = 0;
574 				symtablen = 0;
575 			}
576 		}
577 
578 		/*
579 		 * construct a name of the form "archive.a:obj.o:" for the
580 		 * current archive entry if the object name is to be printed
581 		 * on each output line
582 		 */
583 		*name = '\0';
584 		if (posix_output)
585 			snprintf(name, baselen - 1, "%s[", fname);
586 		else if (count > 1)
587 			snprintf(name, baselen - 1, "%s:", fname);
588 
589 		if (mmbr_name(&ar_head, &name, baselen, &namelen, fp)) {
590 			rval = 1;
591 			break;
592 		}
593 
594 		if (posix_output)
595 			strlcat(name, "]", baselen + namelen);
596 
597 		foff = ftello(fp);
598 
599 		/* get and check current object's header */
600 		if (fread((char *)&exec_head, sizeof(exec_head),
601 		    (size_t)1, fp) != 1) {
602 			warnx("%s: premature EOF", fname);
603 			rval = 1;
604 			break;
605 		}
606 
607 		rval |= show_file(2, non_object_warning, name, fp, foff, &exec_head);
608 		/*
609 		 * skip to next archive object - it starts at the next
610 		 * even byte boundary
611 		 */
612 #define even(x) (((x) + 1) & ~1)
613 skip:		if (fseeko(fp, last_ar_off + even(mmbrlen), SEEK_SET)) {
614 			warn("%s", fname);
615 			rval = 1;
616 			break;
617 		}
618 	}
619 	free(nametab);
620 	nametab = NULL;
621 	free(name);
622 	return(rval);
623 }
624 
625 char *stab;
626 
627 /*
628  * show_file()
629  *	show symbols from the object file pointed to by fp.  The current
630  *	file pointer for fp is expected to be at the beginning of an object
631  *	file header.
632  */
633 int
show_file(int count,int warn_fmt,const char * name,FILE * fp,off_t foff,union hdr * head)634 show_file(int count, int warn_fmt, const char *name, FILE *fp, off_t foff, union hdr *head)
635 {
636 	u_long text, data, bss, total;
637 	struct xnlist *np, *names, **snames;
638 	int i, nrawnames, nnames;
639 	size_t stabsize;
640 
641 	if (IS_ELF(head->elf32) &&
642 	    head->elf32.e_ident[EI_CLASS] == ELFCLASS32 &&
643 	    head->elf32.e_ident[EI_VERSION] == ELF_TARG_VER) {
644 		void *shdr;
645 
646 		if (!(shdr = elf32_load_shdrs(name, fp, foff, &head->elf32)))
647 			return (1);
648 
649 		i = issize?
650 		    elf32_size(&head->elf32, shdr, &text, &data, &bss) :
651 		    elf32_symload(name, fp, foff, &head->elf32, shdr,
652 			&names, &snames, &stabsize, &nrawnames);
653 		free(shdr);
654 		if (i)
655 			return (i);
656 
657 	} else if (IS_ELF(head->elf64) &&
658 	    head->elf64.e_ident[EI_CLASS] == ELFCLASS64 &&
659 	    head->elf64.e_ident[EI_VERSION] == ELF_TARG_VER) {
660 		void *shdr;
661 
662 		if (!(shdr = elf64_load_shdrs(name, fp, foff, &head->elf64)))
663 			return (1);
664 
665 		i = issize?
666 		    elf64_size(&head->elf64, shdr, &text, &data, &bss) :
667 		    elf64_symload(name, fp, foff, &head->elf64, shdr,
668 			&names, &snames, &stabsize, &nrawnames);
669 		free(shdr);
670 		if (i)
671 			return (i);
672 	} else {
673 		if (warn_fmt)
674 			warnx("%s: bad format", name);
675 		return (1);
676 	}
677 
678 	if (issize) {
679 		static int first = 1;
680 
681 		if (first) {
682 			first = 0;
683 			printf("text\tdata\tbss\tdec\thex\n");
684 		}
685 
686 		total = text + data + bss;
687 		printf("%lu\t%lu\t%lu\t%lu\t%lx",
688 		    text, data, bss, total, total);
689 		if (count > 1)
690 			(void)printf("\t%s", name);
691 
692 		total_text += text;
693 		total_data += data;
694 		total_bss += bss;
695 		total_total += total;
696 
697 		printf("\n");
698 		return (0);
699 	}
700 	/* else we are nm */
701 
702 	/*
703 	 * it seems that string table is sequential
704 	 * relative to the symbol table order
705 	 */
706 	if (sfunc == NULL && usemmap)
707 		(void)madvise(stab, stabsize, MADV_SEQUENTIAL);
708 
709 	/*
710 	 * fix up the symbol table and filter out unwanted entries
711 	 *
712 	 * common symbols are characterized by a n_type of N_UNDF and a
713 	 * non-zero n_value -- change n_type to N_COMM for all such
714 	 * symbols to make life easier later.
715 	 *
716 	 * filter out all entries which we don't want to print anyway
717 	 */
718 	for (np = names, i = nnames = 0; i < nrawnames; np++, i++) {
719 		/*
720 		 * make n_un.n_name a character pointer by adding the string
721 		 * table's base to n_un.n_strx
722 		 *
723 		 * don't mess with zero offsets
724 		 */
725 		if (np->nl.n_un.n_strx)
726 			np->nl.n_un.n_name = stab + np->nl.n_un.n_strx;
727 		else
728 			np->nl.n_un.n_name = "";
729 		if (print_only_external_symbols && !IS_EXTERNAL(np->nl.n_type))
730 			continue;
731 		if (print_only_undefined_symbols &&
732 		    SYMBOL_TYPE(np->nl.n_type) != N_UNDF)
733 			continue;
734 
735 		snames[nnames++] = np;
736 	}
737 
738 	/* sort the symbol table if applicable */
739 	if (sfunc)
740 		qsort(snames, (size_t)nnames, sizeof(*snames), sfunc);
741 
742 	if (count > 1)
743 		(void)printf("\n%s:\n", name);
744 
745 	/* print out symbols */
746 	for (i = 0; i < nnames; i++)
747 		print_symbol(name, snames[i]);
748 
749 	free(snames);
750 	free(names);
751 	MUNMAP(stab, stabsize);
752 	return(0);
753 }
754 
755 char *
symname(struct xnlist * sym)756 symname(struct xnlist *sym)
757 {
758 	return sym->nl.n_un.n_name;
759 }
760 
761 /*
762  * print_symbol()
763  *	show one symbol
764  */
765 void
print_symbol(const char * name,struct xnlist * sym)766 print_symbol(const char *name, struct xnlist *sym)
767 {
768 	if (print_file_each_line) {
769 		if (posix_output)
770 			(void)printf("%s: ", name);
771 		else
772 			(void)printf("%s:", name);
773 	}
774 
775 	if (posix_output) {
776 		(void)printf("%s %c ", symname(sym), typeletter(sym));
777 		if (SYMBOL_TYPE(sym->nl.n_type) != N_UNDF)
778 			(void)printf(posix_fmtstr, sym->nl.n_value,
779 			    sym->n_size);
780 		(void)printf("\n");
781 	} else {
782 		/*
783 		 * handle undefined-only format especially (no space is
784 		 * left for symbol values, no type field is printed)
785 		 */
786 		if (!print_only_undefined_symbols) {
787 			/* print symbol's value */
788 			if (SYMBOL_TYPE(sym->nl.n_type) == N_UNDF)
789 				(void)printf("        ");
790 			else
791 				(void)printf("%08lx", sym->nl.n_value);
792 
793 			/* print type information */
794 			if (show_extensions)
795 				(void)printf(" %c   ", typeletter(sym));
796 			else
797 				(void)printf(" %c ", typeletter(sym));
798 		}
799 
800 		(void)puts(symname(sym));
801 	}
802 }
803 
804 /*
805  * typeletter()
806  *	return a description letter for the given basic type code of an
807  *	symbol table entry.  The return value will be upper case for
808  *	external, lower case for internal symbols.
809  */
810 char
typeletter(struct xnlist * np)811 typeletter(struct xnlist *np)
812 {
813 	int ext = IS_EXTERNAL(np->nl.n_type);
814 
815 	if (np->nl.n_other)
816 		return np->nl.n_other;
817 
818 	switch(SYMBOL_TYPE(np->nl.n_type)) {
819 	case N_ABS:
820 		return(ext? 'A' : 'a');
821 	case N_BSS:
822 		return(ext? 'B' : 'b');
823 	case N_COMM:
824 		return(ext? 'C' : 'c');
825 	case N_DATA:
826 		return(ext? 'D' : 'd');
827 	case N_FN:
828 		/* NOTE: N_FN == N_WARNING,
829 		 * in this case, the N_EXT bit is to considered as
830 		 * part of the symbol's type itself.
831 		 */
832 		return(ext? 'F' : 'W');
833 	case N_TEXT:
834 		return(ext? 'T' : 't');
835 	case N_SIZE:
836 		return(ext? 'S' : 's');
837 	case N_UNDF:
838 		return(ext? 'U' : 'u');
839 	}
840 	return('?');
841 }
842 
843 int
fname(const void * a0,const void * b0)844 fname(const void *a0, const void *b0)
845 {
846 	struct xnlist * const *a = a0, * const *b = b0;
847 
848 	return(strcmp((*a)->nl.n_un.n_name, (*b)->nl.n_un.n_name));
849 }
850 
851 int
rname(const void * a0,const void * b0)852 rname(const void *a0, const void *b0)
853 {
854 	struct xnlist * const *a = a0, * const *b = b0;
855 
856 	return(strcmp((*b)->nl.n_un.n_name, (*a)->nl.n_un.n_name));
857 }
858 
859 int
value(const void * a0,const void * b0)860 value(const void *a0, const void *b0)
861 {
862 	struct xnlist * const *a = a0, * const *b = b0;
863 
864 	if (SYMBOL_TYPE((*a)->nl.n_type) == N_UNDF)
865 		if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF)
866 			return(0);
867 		else
868 			return(-1);
869 	else if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF)
870 		return(1);
871 	if (rev) {
872 		if ((*a)->nl.n_value == (*b)->nl.n_value)
873 			return(rname(a0, b0));
874 		return((*b)->nl.n_value > (*a)->nl.n_value ? 1 : -1);
875 	} else {
876 		if ((*a)->nl.n_value == (*b)->nl.n_value)
877 			return(fname(a0, b0));
878 		return((*a)->nl.n_value > (*b)->nl.n_value ? 1 : -1);
879 	}
880 }
881 
882 #define CPPFILT	"/usr/bin/c++filt"
883 
884 void
pipe2cppfilt(void)885 pipe2cppfilt(void)
886 {
887 	int pip[2];
888 	char *argv[2];
889 
890 	argv[0] = "c++filt";
891 	argv[1] = NULL;
892 
893 	if (pipe(pip) == -1)
894 		err(1, "pipe");
895 	switch(fork()) {
896 	case -1:
897 		err(1, "fork");
898 	default:
899 		dup2(pip[0], 0);
900 		close(pip[0]);
901 		close(pip[1]);
902 		execve(CPPFILT, argv, NULL);
903 		err(1, "execve");
904 	case 0:
905 		dup2(pip[1], 1);
906 		close(pip[1]);
907 		close(pip[0]);
908 	}
909 }
910 
911 void
usage(void)912 usage(void)
913 {
914 	extern char *__progname;
915 
916 	if (issize)
917 		fprintf(stderr, "usage: %s [-tw] [file ...]\n", __progname);
918 	else
919 		fprintf(stderr, "usage: %s [-AaCDegnoPprsuw] [-t d|o|x] [file ...]\n",
920 		    __progname);
921 	exit(1);
922 }
923