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
59union hdr {
60	Elf32_Ehdr elf32;
61	Elf64_Ehdr elf64;
62};
63
64int armap;
65int demangle;
66int non_object_warning;
67int print_only_external_symbols;
68int print_only_undefined_symbols;
69int print_all_symbols;
70int print_file_each_line;
71int show_extensions;
72int issize;
73char posix_fmtstr[6];
74int posix_output;
75char posix_radix = 'x';
76int usemmap = 1;
77int dynamic_only;
78
79/* size vars */
80unsigned long total_text, total_data, total_bss, total_total;
81int non_object_warning, print_totals;
82
83int rev;
84int fname(const void *, const void *);
85int rname(const void *, const void *);
86int value(const void *, const void *);
87int (*sfunc)(const void *, const void *) = fname;
88char typeletter(struct xnlist *);
89int mmbr_name(struct ar_hdr *, char **, int, int *, FILE *);
90int show_symtab(off_t, u_long, const char *, FILE *);
91int 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
97void	 pipe2cppfilt(void);
98void	 usage(void);
99char	*symname(struct xnlist *);
100int	process_file(int, const char *);
101int	show_archive(int, const char *, FILE *);
102int	show_file(int, int, const char *, FILE *fp, off_t, union hdr *);
103void	print_symbol(const char *, struct xnlist *);
104
105#define	OPTSTRING_NM	"aABCDegnopPrst:uvw"
106const 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 */
128int
129main(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 */
248int
249process_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
295char *nametab;
296
297/*
298 *
299 *	given the archive member header -- produce member name
300 */
301int
302mmbr_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 */
362int
363show_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 */
422int
423show_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 */
483int
484show_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)
613skip:		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
625char *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 */
633int
634show_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
755char *
756symname(struct xnlist *sym)
757{
758	return sym->nl.n_un.n_name;
759}
760
761/*
762 * print_symbol()
763 *	show one symbol
764 */
765void
766print_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 */
810char
811typeletter(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
843int
844fname(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
851int
852rname(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
859int
860value(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
884void
885pipe2cppfilt(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
911void
912usage(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