xref: /openbsd/usr.bin/nm/nm.c (revision 73471bf0)
1 /*	$OpenBSD: nm.c,v 1.54 2019/03/03 16:07:39 schwarze 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 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]) + 1;
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 - 1;
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 		return (1);
385 	}
386 
387 	printf("\nArchive index:\n");
388 	num = betoh32(*symtab);
389 	strtab = (char *)(symtab + num + 1);
390 	for (ps = symtab + 1; num--; ps++, strtab += strlen(strtab) + 1) {
391 		if (fseeko(fp, betoh32(*ps), SEEK_SET)) {
392 			warn("%s: fseeko", name);
393 			rval = 1;
394 			break;
395 		}
396 
397 		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
398 		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
399 			warnx("%s: member fseeko", name);
400 			rval = 1;
401 			break;
402 		}
403 
404 		*p = '\0';
405 		if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) {
406 			rval = 1;
407 			break;
408 		}
409 
410 		printf("%s in %s\n", strtab, p);
411 	}
412 
413 	fseeko(fp, restore, SEEK_SET);
414 
415 	free(p);
416 	MUNMAP(symtab, len);
417 	return (rval);
418 }
419 
420 /*
421  * show_symdef()
422  *	show archive ranlib index (gob)
423  */
424 int
425 show_symdef(off_t off, u_long len, const char *name, FILE *fp)
426 {
427 	struct ranlib *prn, *eprn;
428 	struct ar_hdr ar_head;
429 	char *symdef;
430 	char *strtab, *p;
431 	u_long size;
432 	int namelen, rval = 0;
433 
434 	MMAP(symdef, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off);
435 	if (symdef == MAP_FAILED)
436 		return (1);
437 	if (usemmap)
438 		(void)madvise(symdef, len, MADV_SEQUENTIAL);
439 
440 	namelen = sizeof(ar_head.ar_name);
441 	if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) {
442 		warn("%s: malloc", name);
443 		MUNMAP(symdef, len);
444 		return (1);
445 	}
446 
447 	size = *(u_long *)symdef;
448 	prn = (struct ranlib *)(symdef + sizeof(u_long));
449 	eprn = prn + size / sizeof(*prn);
450 	strtab = symdef + sizeof(u_long) + size + sizeof(u_long);
451 
452 	printf("\nArchive index:\n");
453 	for (; prn < eprn; prn++) {
454 		if (fseeko(fp, prn->ran_off, SEEK_SET)) {
455 			warn("%s: fseeko", name);
456 			rval = 1;
457 			break;
458 		}
459 
460 		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
461 		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
462 			warnx("%s: member fseeko", name);
463 			rval = 1;
464 			break;
465 		}
466 
467 		*p = '\0';
468 		if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) {
469 			rval = 1;
470 			break;
471 		}
472 
473 		printf("%s in %s\n", strtab + prn->ran_un.ran_strx, p);
474 	}
475 
476 	free(p);
477 	MUNMAP(symdef, len);
478 	return (rval);
479 }
480 
481 /*
482  * show_archive()
483  *	show symbols in the given archive file
484  */
485 int
486 show_archive(int count, const char *fname, FILE *fp)
487 {
488 	struct ar_hdr ar_head;
489 	union hdr exec_head;
490 	int i, rval;
491 	off_t last_ar_off, foff, symtaboff;
492 	char *name;
493 	int baselen, namelen;
494 	u_long mmbrlen, symtablen;
495 
496 	baselen = strlen(fname) + 3;
497 	if (posix_output)
498 		baselen += 2;
499 	namelen = sizeof(ar_head.ar_name);
500 	if ((name = malloc(baselen + namelen)) == NULL)
501 		err(1, NULL);
502 
503 	rval = 0;
504 	nametab = NULL;
505 	symtaboff = 0;
506 	symtablen = 0;
507 
508 	/* while there are more entries in the archive */
509 	while (fread(&ar_head, sizeof(ar_head), 1, fp) == 1) {
510 		/* bad archive entry - stop processing this archive */
511 		if (memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
512 			warnx("%s: bad format archive header", fname);
513 			rval = 1;
514 			break;
515 		}
516 
517 		/* remember start position of current archive object */
518 		last_ar_off = ftello(fp);
519 		mmbrlen = atol(ar_head.ar_size);
520 
521 		if (strncmp(ar_head.ar_name, RANLIBMAG,
522 		    sizeof(RANLIBMAG) - 1) == 0) {
523 			if (!issize && armap &&
524 			    show_symdef(last_ar_off, mmbrlen, fname, fp)) {
525 				rval = 1;
526 				break;
527 			}
528 			goto skip;
529 		} else if (strncmp(ar_head.ar_name, SYMTABMAG,
530 		    sizeof(SYMTABMAG) - 1) == 0) {
531 			/* if nametab hasn't been seen yet -- doit later */
532 			if (!nametab) {
533 				symtablen = mmbrlen;
534 				symtaboff = last_ar_off;
535 				goto skip;
536 			}
537 
538 			/* load the Sys5 long names table */
539 		} else if (strncmp(ar_head.ar_name, STRTABMAG,
540 		    sizeof(STRTABMAG) - 1) == 0) {
541 			char *p;
542 
543 			if ((nametab = malloc(mmbrlen)) == NULL) {
544 				warn("%s: nametab", fname);
545 				rval = 1;
546 				break;
547 			}
548 
549 			if (fread(nametab, mmbrlen, (size_t)1, fp) != 1) {
550 				warnx("%s: premature EOF", fname);
551 				rval = 1;
552 				break;
553 			}
554 
555 			for (p = nametab, i = mmbrlen; i--; p++)
556 				if (*p == '\n')
557 					*p = '\0';
558 
559 			if (issize || !armap || !symtablen || !symtaboff)
560 				goto skip;
561 		}
562 #ifdef __mips64
563 		else if (memcmp(ar_head.ar_name, SYM64MAG,
564 		    sizeof(ar_head.ar_name)) == 0) {
565 			/* IRIX6-compatible archive map */
566 			goto skip;
567 		}
568 #endif
569 
570 		if (!issize && armap && symtablen && symtaboff) {
571 			if (show_symtab(symtaboff, symtablen, fname, fp)) {
572 				rval = 1;
573 				break;
574 			} else {
575 				symtaboff = 0;
576 				symtablen = 0;
577 			}
578 		}
579 
580 		/*
581 		 * construct a name of the form "archive.a:obj.o:" for the
582 		 * current archive entry if the object name is to be printed
583 		 * on each output line
584 		 */
585 		*name = '\0';
586 		if (posix_output)
587 			snprintf(name, baselen - 1, "%s[", fname);
588 		else if (count > 1)
589 			snprintf(name, baselen - 1, "%s:", fname);
590 
591 		if (mmbr_name(&ar_head, &name, baselen, &namelen, fp)) {
592 			rval = 1;
593 			break;
594 		}
595 
596 		if (posix_output)
597 			strlcat(name, "]", baselen + namelen);
598 
599 		foff = ftello(fp);
600 
601 		/* get and check current object's header */
602 		if (fread((char *)&exec_head, sizeof(exec_head),
603 		    (size_t)1, fp) != 1) {
604 			warnx("%s: premature EOF", fname);
605 			rval = 1;
606 			break;
607 		}
608 
609 		rval |= show_file(2, non_object_warning, name, fp, foff, &exec_head);
610 		/*
611 		 * skip to next archive object - it starts at the next
612 		 * even byte boundary
613 		 */
614 #define even(x) (((x) + 1) & ~1)
615 skip:		if (fseeko(fp, last_ar_off + even(mmbrlen), SEEK_SET)) {
616 			warn("%s", fname);
617 			rval = 1;
618 			break;
619 		}
620 	}
621 	free(nametab);
622 	nametab = NULL;
623 	free(name);
624 	return(rval);
625 }
626 
627 char *stab;
628 
629 /*
630  * show_file()
631  *	show symbols from the object file pointed to by fp.  The current
632  *	file pointer for fp is expected to be at the beginning of an object
633  *	file header.
634  */
635 int
636 show_file(int count, int warn_fmt, const char *name, FILE *fp, off_t foff, union hdr *head)
637 {
638 	u_long text, data, bss, total;
639 	struct xnlist *np, *names, **snames;
640 	int i, nrawnames, nnames;
641 	size_t stabsize;
642 
643 	if (IS_ELF(head->elf32) &&
644 	    head->elf32.e_ident[EI_CLASS] == ELFCLASS32 &&
645 	    head->elf32.e_ident[EI_VERSION] == ELF_TARG_VER) {
646 		void *shdr;
647 
648 		if (!(shdr = elf32_load_shdrs(name, fp, foff, &head->elf32)))
649 			return (1);
650 
651 		i = issize?
652 		    elf32_size(&head->elf32, shdr, &text, &data, &bss) :
653 		    elf32_symload(name, fp, foff, &head->elf32, shdr,
654 			&names, &snames, &stabsize, &nrawnames);
655 		free(shdr);
656 		if (i)
657 			return (i);
658 
659 	} else if (IS_ELF(head->elf64) &&
660 	    head->elf64.e_ident[EI_CLASS] == ELFCLASS64 &&
661 	    head->elf64.e_ident[EI_VERSION] == ELF_TARG_VER) {
662 		void *shdr;
663 
664 		if (!(shdr = elf64_load_shdrs(name, fp, foff, &head->elf64)))
665 			return (1);
666 
667 		i = issize?
668 		    elf64_size(&head->elf64, shdr, &text, &data, &bss) :
669 		    elf64_symload(name, fp, foff, &head->elf64, shdr,
670 			&names, &snames, &stabsize, &nrawnames);
671 		free(shdr);
672 		if (i)
673 			return (i);
674 	} else {
675 		if (warn_fmt)
676 			warnx("%s: bad format", name);
677 		return (1);
678 	}
679 
680 	if (issize) {
681 		static int first = 1;
682 
683 		if (first) {
684 			first = 0;
685 			printf("text\tdata\tbss\tdec\thex\n");
686 		}
687 
688 		total = text + data + bss;
689 		printf("%lu\t%lu\t%lu\t%lu\t%lx",
690 		    text, data, bss, total, total);
691 		if (count > 1)
692 			(void)printf("\t%s", name);
693 
694 		total_text += text;
695 		total_data += data;
696 		total_bss += bss;
697 		total_total += total;
698 
699 		printf("\n");
700 		return (0);
701 	}
702 	/* else we are nm */
703 
704 	/*
705 	 * it seems that string table is sequential
706 	 * relative to the symbol table order
707 	 */
708 	if (sfunc == NULL && usemmap)
709 		(void)madvise(stab, stabsize, MADV_SEQUENTIAL);
710 
711 	/*
712 	 * fix up the symbol table and filter out unwanted entries
713 	 *
714 	 * common symbols are characterized by a n_type of N_UNDF and a
715 	 * non-zero n_value -- change n_type to N_COMM for all such
716 	 * symbols to make life easier later.
717 	 *
718 	 * filter out all entries which we don't want to print anyway
719 	 */
720 	for (np = names, i = nnames = 0; i < nrawnames; np++, i++) {
721 		/*
722 		 * make n_un.n_name a character pointer by adding the string
723 		 * table's base to n_un.n_strx
724 		 *
725 		 * don't mess with zero offsets
726 		 */
727 		if (np->nl.n_un.n_strx)
728 			np->nl.n_un.n_name = stab + np->nl.n_un.n_strx;
729 		else
730 			np->nl.n_un.n_name = "";
731 		if (print_only_external_symbols && !IS_EXTERNAL(np->nl.n_type))
732 			continue;
733 		if (print_only_undefined_symbols &&
734 		    SYMBOL_TYPE(np->nl.n_type) != N_UNDF)
735 			continue;
736 
737 		snames[nnames++] = np;
738 	}
739 
740 	/* sort the symbol table if applicable */
741 	if (sfunc)
742 		qsort(snames, (size_t)nnames, sizeof(*snames), sfunc);
743 
744 	if (count > 1)
745 		(void)printf("\n%s:\n", name);
746 
747 	/* print out symbols */
748 	for (i = 0; i < nnames; i++)
749 		print_symbol(name, snames[i]);
750 
751 	free(snames);
752 	free(names);
753 	MUNMAP(stab, stabsize);
754 	return(0);
755 }
756 
757 char *
758 symname(struct xnlist *sym)
759 {
760 	return sym->nl.n_un.n_name;
761 }
762 
763 /*
764  * print_symbol()
765  *	show one symbol
766  */
767 void
768 print_symbol(const char *name, struct xnlist *sym)
769 {
770 	if (print_file_each_line) {
771 		if (posix_output)
772 			(void)printf("%s: ", name);
773 		else
774 			(void)printf("%s:", name);
775 	}
776 
777 	if (posix_output) {
778 		(void)printf("%s %c ", symname(sym), typeletter(sym));
779 		if (SYMBOL_TYPE(sym->nl.n_type) != N_UNDF)
780 			(void)printf(posix_fmtstr, sym->nl.n_value,
781 			    sym->n_size);
782 		(void)printf("\n");
783 	} else {
784 		/*
785 		 * handle undefined-only format especially (no space is
786 		 * left for symbol values, no type field is printed)
787 		 */
788 		if (!print_only_undefined_symbols) {
789 			/* print symbol's value */
790 			if (SYMBOL_TYPE(sym->nl.n_type) == N_UNDF)
791 				(void)printf("        ");
792 			else
793 				(void)printf("%08lx", sym->nl.n_value);
794 
795 			/* print type information */
796 			if (show_extensions)
797 				(void)printf(" %c   ", typeletter(sym));
798 			else
799 				(void)printf(" %c ", typeletter(sym));
800 		}
801 
802 		(void)puts(symname(sym));
803 	}
804 }
805 
806 /*
807  * typeletter()
808  *	return a description letter for the given basic type code of an
809  *	symbol table entry.  The return value will be upper case for
810  *	external, lower case for internal symbols.
811  */
812 char
813 typeletter(struct xnlist *np)
814 {
815 	int ext = IS_EXTERNAL(np->nl.n_type);
816 
817 	if (np->nl.n_other)
818 		return np->nl.n_other;
819 
820 	switch(SYMBOL_TYPE(np->nl.n_type)) {
821 	case N_ABS:
822 		return(ext? 'A' : 'a');
823 	case N_BSS:
824 		return(ext? 'B' : 'b');
825 	case N_COMM:
826 		return(ext? 'C' : 'c');
827 	case N_DATA:
828 		return(ext? 'D' : 'd');
829 	case N_FN:
830 		/* NOTE: N_FN == N_WARNING,
831 		 * in this case, the N_EXT bit is to considered as
832 		 * part of the symbol's type itself.
833 		 */
834 		return(ext? 'F' : 'W');
835 	case N_TEXT:
836 		return(ext? 'T' : 't');
837 	case N_SIZE:
838 		return(ext? 'S' : 's');
839 	case N_UNDF:
840 		return(ext? 'U' : 'u');
841 	}
842 	return('?');
843 }
844 
845 int
846 fname(const void *a0, const void *b0)
847 {
848 	struct xnlist * const *a = a0, * const *b = b0;
849 
850 	return(strcmp((*a)->nl.n_un.n_name, (*b)->nl.n_un.n_name));
851 }
852 
853 int
854 rname(const void *a0, const void *b0)
855 {
856 	struct xnlist * const *a = a0, * const *b = b0;
857 
858 	return(strcmp((*b)->nl.n_un.n_name, (*a)->nl.n_un.n_name));
859 }
860 
861 int
862 value(const void *a0, const void *b0)
863 {
864 	struct xnlist * const *a = a0, * const *b = b0;
865 
866 	if (SYMBOL_TYPE((*a)->nl.n_type) == N_UNDF)
867 		if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF)
868 			return(0);
869 		else
870 			return(-1);
871 	else if (SYMBOL_TYPE((*b)->nl.n_type) == N_UNDF)
872 		return(1);
873 	if (rev) {
874 		if ((*a)->nl.n_value == (*b)->nl.n_value)
875 			return(rname(a0, b0));
876 		return((*b)->nl.n_value > (*a)->nl.n_value ? 1 : -1);
877 	} else {
878 		if ((*a)->nl.n_value == (*b)->nl.n_value)
879 			return(fname(a0, b0));
880 		return((*a)->nl.n_value > (*b)->nl.n_value ? 1 : -1);
881 	}
882 }
883 
884 #define CPPFILT	"/usr/bin/c++filt"
885 
886 void
887 pipe2cppfilt(void)
888 {
889 	int pip[2];
890 	char *argv[2];
891 
892 	argv[0] = "c++filt";
893 	argv[1] = NULL;
894 
895 	if (pipe(pip) == -1)
896 		err(1, "pipe");
897 	switch(fork()) {
898 	case -1:
899 		err(1, "fork");
900 	default:
901 		dup2(pip[0], 0);
902 		close(pip[0]);
903 		close(pip[1]);
904 		execve(CPPFILT, argv, NULL);
905 		err(1, "execve");
906 	case 0:
907 		dup2(pip[1], 1);
908 		close(pip[1]);
909 		close(pip[0]);
910 	}
911 }
912 
913 void
914 usage(void)
915 {
916 	extern char *__progname;
917 
918 	if (issize)
919 		fprintf(stderr, "usage: %s [-tw] [file ...]\n", __progname);
920 	else
921 		fprintf(stderr, "usage: %s [-AaCDegnoPprsuw] [-t d|o|x] [file ...]\n",
922 		    __progname);
923 	exit(1);
924 }
925