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