1145522Sdarrenr/*	$OpenBSD: nm.c,v 1.56 2024/05/21 05:00:48 jsg Exp $	*/
2145522Sdarrenr/*	$NetBSD: nm.c,v 1.7 1996/01/14 23:04:03 pk Exp $	*/
353642Sguido
4255332Scy/*
553642Sguido * Copyright (c) 1989, 1993
680482Sdarrenr *	The Regents of the University of California.  All rights reserved.
753642Sguido *
857126Sguido * This code is derived from software contributed to Berkeley by
9145522Sdarrenr * Hans Huebner.
1053642Sguido *
1153642Sguido * Redistribution and use in source and binary forms, with or without
1253642Sguido * modification, are permitted provided that the following conditions
1353642Sguido * are met:
1453642Sguido * 1. Redistributions of source code must retain the above copyright
1553642Sguido *    notice, this list of conditions and the following disclaimer.
1653642Sguido * 2. Redistributions in binary form must reproduce the above copyright
1753642Sguido *    notice, this list of conditions and the following disclaimer in the
1853642Sguido *    documentation and/or other materials provided with the distribution.
19153876Sguido * 3. Neither the name of the University nor the names of its contributors
20145579Sdarrenr *    may be used to endorse or promote products derived from this software
21145579Sdarrenr *    without specific prior written permission.
22145579Sdarrenr *
23145579Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24145579Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2553642Sguido * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2653642Sguido * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2753642Sguido * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2853642Sguido * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2953642Sguido * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3053642Sguido * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3153642Sguido * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32145579Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3353642Sguido * SUCH DAMAGE.
3453642Sguido */
3553642Sguido
3653642Sguido#include <sys/types.h>
3753642Sguido#include <sys/mman.h>
3853642Sguido#include <a.out.h>
39145579Sdarrenr#include <elf.h>
4053642Sguido#include <ar.h>
41145579Sdarrenr#include <ranlib.h>
4253642Sguido#include <unistd.h>
4353642Sguido#include <err.h>
4453642Sguido#include <errno.h>
4553642Sguido#include <ctype.h>
4653642Sguido#include <link.h>
4753642Sguido
4853642Sguido#include <stdio.h>
4953642Sguido#include <stdlib.h>
5053642Sguido#include <string.h>
5153642Sguido#include <getopt.h>
5253642Sguido#include "util.h"
5353642Sguido#include "elfuncs.h"
5453642Sguido
5553642Sguido#define	SYMTABMAG	"/ "
5653642Sguido#define	STRTABMAG	"//"
5753642Sguido#define	SYM64MAG	"/SYM64/         "
5853642Sguido
5953642Sguidounion hdr {
6053642Sguido	Elf32_Ehdr elf32;
6153642Sguido	Elf64_Ehdr elf64;
6260855Sdarrenr};
6353642Sguido
6453642Sguidoint armap;
6553642Sguidoint demangle;
6653642Sguidoint non_object_warning;
6753642Sguidoint print_only_external_symbols;
6853642Sguidoint print_only_undefined_symbols;
6953642Sguidoint print_all_symbols;
7053642Sguidoint print_file_each_line;
7153642Sguidoint show_extensions;
7253642Sguidoint issize;
7353642Sguidochar posix_fmtstr[6];
74145522Sdarrenrint posix_output;
75145522Sdarrenrchar posix_radix = 'x';
76255332Scyint usemmap = 1;
77145522Sdarrenrint dynamic_only;
78145522Sdarrenr
79145522Sdarrenr/* size vars */
80145522Sdarrenrunsigned long total_text, total_data, total_bss, total_total;
81145522Sdarrenrint non_object_warning, print_totals;
82145522Sdarrenr
83145522Sdarrenrint rev;
84145522Sdarrenrint fname(const void *, const void *);
85145522Sdarrenrint rname(const void *, const void *);
86145522Sdarrenrint value(const void *, const void *);
87145522Sdarrenrint (*sfunc)(const void *, const void *) = fname;
88145522Sdarrenrchar typeletter(struct xnlist *);
89145522Sdarrenrint mmbr_name(struct ar_hdr *, char **, int, int *, FILE *);
90145522Sdarrenrint show_symtab(off_t, u_long, const char *, FILE *);
91145522Sdarrenrint show_symdef(off_t, u_long, const char *, FILE *);
92145522Sdarrenr
93145522Sdarrenr/* some macros for symbol type (nlist.n_type) handling */
94255332Scy#define	IS_EXTERNAL(x)		((x) & N_EXT)
95255332Scy#define	SYMBOL_TYPE(x)		((x) & (N_TYPE | N_STAB))
96145522Sdarrenr
97255332Scyvoid	 pipe2cppfilt(void);
9853642Sguidovoid	 usage(void);
9960855Sdarrenrchar	*symname(struct xnlist *);
100255332Scyint	process_file(int, const char *);
10153642Sguidoint	show_archive(int, const char *, FILE *);
102255332Scyint	show_file(int, int, const char *, FILE *fp, off_t, union hdr *);
10353642Sguidovoid	print_symbol(const char *, struct xnlist *);
104255332Scy
105255332Scy#define	OPTSTRING_NM	"aABCDegnopPrst:uvw"
106255332Scyconst struct option longopts_nm[] = {
107255332Scy	{ "debug-syms",		no_argument,		0,	'a' },
108255332Scy	{ "demangle",		no_argument,		0,	'C' },
109255332Scy	{ "dynamic",		no_argument,		0,	'D' },
110255332Scy	{ "extern-only",	no_argument,		0,	'g' },
111255332Scy/*	{ "line-numbers",	no_argument,		0,	'l' }, */
112255332Scy	{ "no-sort",		no_argument,		0,	'p' },
113255332Scy	{ "numeric-sort",	no_argument,		0,	'n' },
114255332Scy	{ "print-armap",	no_argument,		0,	's' },
115255332Scy	{ "print-file-name",	no_argument,		0,	'o' },
116255332Scy	{ "reverse-sort",	no_argument,		0,	'r' },
117255332Scy/*	{ "size-sort",		no_argument,		&szval,	1 }, */
118255332Scy	{ "undefined-only",	no_argument,		0,	'u' },
11992685Sdarrenr	{ "help",		no_argument,		0,	'?' },
120255332Scy	{ NULL }
121255332Scy};
122255332Scy
123255332Scy/*
12453642Sguido * main()
12553642Sguido *	parse command line, execute process_file() for each file
12653642Sguido *	specified on the command line.
12753642Sguido */
128145522Sdarrenrint
12960855Sdarrenrmain(int argc, char *argv[])
13060855Sdarrenr{
13153642Sguido	extern char *__progname;
132255332Scy	extern int optind;
133255332Scy	const char *optstr;
13453642Sguido	const struct option *lopts;
135145522Sdarrenr	int ch, eval;
13660855Sdarrenr
137255332Scy	if (pledge("stdio rpath proc exec", NULL) == -1)
138255332Scy		err(1, "pledge");
139255332Scy
140255332Scy	optstr = OPTSTRING_NM;
141255332Scy	lopts = longopts_nm;
142255332Scy	if (!strcmp(__progname, "size")) {
143255332Scy		if (pledge("stdio rpath", NULL) == -1)
144255332Scy			err(1, "pledge");
145255332Scy
146255332Scy		issize = 1;
147255332Scy		optstr = "tw";
148255332Scy		lopts = NULL;
149255332Scy	}
150255332Scy
151255332Scy	while ((ch = getopt_long(argc, argv, optstr, lopts, NULL)) != -1) {
152255332Scy		switch (ch) {
153255332Scy		case 'a':
154145522Sdarrenr			print_all_symbols = 1;
155255332Scy			break;
156255332Scy		case 'B':
157145522Sdarrenr			/* no-op, compat with gnu-nm */
158145522Sdarrenr			break;
159255332Scy		case 'C':
160145522Sdarrenr			demangle = 1;
161255332Scy			break;
162255332Scy		case 'D':
163145522Sdarrenr			dynamic_only = 1;
16460855Sdarrenr			break;
16560855Sdarrenr		case 'e':
16660855Sdarrenr			show_extensions = 1;
167145522Sdarrenr			break;
168102520Sdarrenr		case 'g':
16972006Sdarrenr			print_only_external_symbols = 1;
170255332Scy			break;
17192685Sdarrenr		case 'n':
172255332Scy		case 'v':
17360855Sdarrenr			sfunc = value;
17460855Sdarrenr			break;
17560855Sdarrenr		case 'A':
17660855Sdarrenr		case 'o':
17780482Sdarrenr			print_file_each_line = 1;
17880482Sdarrenr			break;
179255332Scy		case 'p':
180255332Scy			sfunc = NULL;
18160855Sdarrenr			break;
18260855Sdarrenr		case 'P':
18360855Sdarrenr			posix_output = 1;
184145522Sdarrenr			break;
18560855Sdarrenr		case 'r':
186255332Scy			rev = 1;
187255332Scy			break;
188255332Scy		case 's':
189255332Scy			armap = 1;
190255332Scy			break;
191255332Scy		case 'u':
192255332Scy			print_only_undefined_symbols = 1;
193255332Scy			break;
194255332Scy		case 'w':
195255332Scy			non_object_warning = 1;
196255332Scy			break;
197255332Scy		case 't':
198255332Scy			if (issize) {
199255332Scy				print_totals = 1;
200255332Scy			} else {
201145522Sdarrenr				posix_radix = *optarg;
202145522Sdarrenr				if (strlen(optarg) != 1 ||
203145522Sdarrenr				    (posix_radix != 'd' && posix_radix != 'o' &&
204145522Sdarrenr				     posix_radix != 'x'))
205145522Sdarrenr					usage();
206145522Sdarrenr			}
207145522Sdarrenr			break;
208145522Sdarrenr		default:
209145522Sdarrenr			usage();
210145522Sdarrenr		}
211145522Sdarrenr	}
212145522Sdarrenr
213145522Sdarrenr	if (posix_output)
214145522Sdarrenr		(void)snprintf(posix_fmtstr, sizeof posix_fmtstr, "%%%c %%%c",
215145522Sdarrenr		    posix_radix, posix_radix);
216255332Scy	if (demangle)
217255332Scy		pipe2cppfilt();
218255332Scy
219255332Scy	if (pledge("stdio rpath", NULL) == -1)
220255332Scy		err(1, "pledge");
221255332Scy
222255332Scy	argv += optind;
223255332Scy	argc -= optind;
224255332Scy
225255332Scy	if (rev && sfunc == fname)
22653642Sguido		sfunc = rname;
22753642Sguido
22892685Sdarrenr	eval = 0;
22953642Sguido	if (*argv)
23053642Sguido		do {
23153642Sguido			eval |= process_file(argc, *argv);
23253642Sguido		} while (*++argv);
23353642Sguido	else
23453642Sguido		eval |= process_file(1, "a.out");
23553642Sguido
23653642Sguido	if (issize && print_totals)
23753642Sguido		printf("\n%lu\t%lu\t%lu\t%lu\t%lx\tTOTAL\n",
23853642Sguido		    total_text, total_data, total_bss,
23953642Sguido		    total_total, total_total);
24053642Sguido	exit(eval);
24153642Sguido}
24253642Sguido
24353642Sguido/*
244145579Sdarrenr * process_file()
24553642Sguido *	show symbols in the file given as an argument.  Accepts archive and
24653642Sguido *	object files as input.
24753642Sguido */
24853642Sguidoint
24953642Sguidoprocess_file(int count, const char *fname)
25053642Sguido{
25153642Sguido	union hdr exec_head;
25253642Sguido	FILE *fp;
25353642Sguido	int retval;
25453642Sguido	size_t bytes;
25553642Sguido	char magic[SARMAG];
256145522Sdarrenr
25792685Sdarrenr	if (!(fp = fopen(fname, "r"))) {
258145522Sdarrenr		warn("cannot read %s", fname);
259145522Sdarrenr		return(1);
260145522Sdarrenr	}
261145522Sdarrenr
262145522Sdarrenr	if (!issize && count > 1)
263145522Sdarrenr		(void)printf("\n%s:\n", fname);
264145522Sdarrenr
265145522Sdarrenr	/*
266145522Sdarrenr	 * first check whether this is an object file - read a object
267145522Sdarrenr	 * header, and skip back to the beginning
268145522Sdarrenr	 */
269145522Sdarrenr	bzero(&exec_head, sizeof(exec_head));
270145522Sdarrenr	bytes = fread((char *)&exec_head, 1, sizeof(exec_head), fp);
271145522Sdarrenr	if (bytes < sizeof(exec_head)) {
272145522Sdarrenr		if (bytes < sizeof(exec_head.elf32) || IS_ELF(exec_head.elf32)) {
273145522Sdarrenr			warnx("%s: bad format", fname);
274145522Sdarrenr			(void)fclose(fp);
275145522Sdarrenr			return(1);
276145522Sdarrenr		}
277145522Sdarrenr	}
278145522Sdarrenr	rewind(fp);
279145522Sdarrenr
280145522Sdarrenr	/* this could be an archive */
281145522Sdarrenr	if (!IS_ELF(exec_head.elf32)) {
282145522Sdarrenr		if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 ||
283145522Sdarrenr		    strncmp(magic, ARMAG, SARMAG)) {
284145522Sdarrenr			warnx("%s: not object file or archive", fname);
285145522Sdarrenr			(void)fclose(fp);
286145522Sdarrenr			return(1);
287145522Sdarrenr		}
288145522Sdarrenr		retval = show_archive(count, fname, fp);
289145522Sdarrenr	} else
290145522Sdarrenr		retval = show_file(count, 1, fname, fp, 0, &exec_head);
291145522Sdarrenr	(void)fclose(fp);
292145522Sdarrenr	return(retval);
293145522Sdarrenr}
294145522Sdarrenr
295145522Sdarrenrchar *nametab;
296145522Sdarrenr
297145522Sdarrenr/*
298145522Sdarrenr *
299145522Sdarrenr *	given the archive member header -- produce member name
300145522Sdarrenr */
301145522Sdarrenrint
302145522Sdarrenrmmbr_name(struct ar_hdr *arh, char **name, int baselen, int *namelen, FILE *fp)
303145522Sdarrenr{
304145522Sdarrenr	char *p = *name + strlen(*name);
305145522Sdarrenr	long i;
306145522Sdarrenr
307145522Sdarrenr	if (nametab && arh->ar_name[0] == '/') {
308145522Sdarrenr		int len;
309145522Sdarrenr
310145522Sdarrenr		i = atol(&arh->ar_name[1]);
311145522Sdarrenr		len = strlen(&nametab[i]) + 1;
312145522Sdarrenr		if (len > *namelen) {
313145522Sdarrenr			p -= (long)*name;
314145522Sdarrenr			if ((*name = realloc(*name, baselen+len)) == NULL)
315145522Sdarrenr				err(1, NULL);
316145522Sdarrenr			*namelen = len;
317145522Sdarrenr			p += (long)*name;
318145522Sdarrenr		}
319145522Sdarrenr		strlcpy(p, &nametab[i], len);
320145522Sdarrenr		p += len - 1;
321145522Sdarrenr	} else
322145522Sdarrenr#ifdef AR_EFMT1
323145522Sdarrenr	/*
324145522Sdarrenr	 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
325145522Sdarrenr	 * first <namelen> bytes of the file
326145522Sdarrenr	 */
327145522Sdarrenr	if ((arh->ar_name[0] == '#') &&
328145522Sdarrenr	    (arh->ar_name[1] == '1') &&
329145522Sdarrenr	    (arh->ar_name[2] == '/') &&
330145522Sdarrenr	    (isdigit((unsigned char)arh->ar_name[3]))) {
331145522Sdarrenr		int len = atoi(&arh->ar_name[3]);
332145522Sdarrenr
333145522Sdarrenr		if (len > *namelen) {
334145522Sdarrenr			p -= (long)*name;
335145522Sdarrenr			if ((*name = realloc(*name, baselen+len)) == NULL)
336145522Sdarrenr				err(1, NULL);
337145522Sdarrenr			*namelen = len;
338145522Sdarrenr			p += (long)*name;
339145522Sdarrenr		}
340145522Sdarrenr		if (fread(p, len, 1, fp) != 1) {
341145522Sdarrenr			warnx("%s: premature EOF", *name);
342145522Sdarrenr			free(*name);
343145522Sdarrenr			return(1);
344145522Sdarrenr		}
345145522Sdarrenr		p += len;
346145522Sdarrenr	} else
347145522Sdarrenr#endif
348145522Sdarrenr	for (i = 0; i < sizeof(arh->ar_name); ++i)
349145522Sdarrenr		if (arh->ar_name[i] && arh->ar_name[i] != ' ')
350145522Sdarrenr			*p++ = arh->ar_name[i];
351145522Sdarrenr	*p = '\0';
352145522Sdarrenr	if (p[-1] == '/')
353145522Sdarrenr		*--p = '\0';
354145522Sdarrenr
355145522Sdarrenr	return (0);
356145522Sdarrenr}
357145522Sdarrenr
358145522Sdarrenr/*
359145522Sdarrenr * show_symtab()
360145522Sdarrenr *	show archive ranlib index (fs5)
361145522Sdarrenr */
362145522Sdarrenrint
363145522Sdarrenrshow_symtab(off_t off, u_long len, const char *name, FILE *fp)
364145522Sdarrenr{
365145522Sdarrenr	struct ar_hdr ar_head;
366145522Sdarrenr	int *symtab, *ps;
367145522Sdarrenr	char *strtab, *p;
368145522Sdarrenr	int num, rval = 0;
369145522Sdarrenr	int namelen;
370145522Sdarrenr	off_t restore;
371145522Sdarrenr
372145522Sdarrenr	restore = ftello(fp);
373145522Sdarrenr
374145522Sdarrenr	MMAP(symtab, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off);
375145522Sdarrenr	if (symtab == MAP_FAILED)
376145522Sdarrenr		return (1);
377145522Sdarrenr
378145522Sdarrenr	namelen = sizeof(ar_head.ar_name);
379145522Sdarrenr	if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) {
380145522Sdarrenr		warn("%s: malloc", name);
381145522Sdarrenr		MUNMAP(symtab, len);
382145522Sdarrenr		return (1);
383145522Sdarrenr	}
384145522Sdarrenr
385145522Sdarrenr	printf("\nArchive index:\n");
386145522Sdarrenr	num = betoh32(*symtab);
387145522Sdarrenr	strtab = (char *)(symtab + num + 1);
388145522Sdarrenr	for (ps = symtab + 1; num--; ps++, strtab += strlen(strtab) + 1) {
389145522Sdarrenr		if (fseeko(fp, betoh32(*ps), SEEK_SET)) {
390145522Sdarrenr			warn("%s: fseeko", name);
391145522Sdarrenr			rval = 1;
392145522Sdarrenr			break;
393145522Sdarrenr		}
394145522Sdarrenr
395145522Sdarrenr		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
396145522Sdarrenr		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
397145522Sdarrenr			warnx("%s: member fseeko", name);
398145522Sdarrenr			rval = 1;
399145522Sdarrenr			break;
400145522Sdarrenr		}
401145522Sdarrenr
402145522Sdarrenr		*p = '\0';
403145522Sdarrenr		if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) {
404145522Sdarrenr			rval = 1;
405145522Sdarrenr			break;
406145522Sdarrenr		}
407145522Sdarrenr
408145522Sdarrenr		printf("%s in %s\n", strtab, p);
409145522Sdarrenr	}
410145522Sdarrenr
411145522Sdarrenr	fseeko(fp, restore, SEEK_SET);
412145522Sdarrenr
413145522Sdarrenr	free(p);
414145522Sdarrenr	MUNMAP(symtab, len);
415145522Sdarrenr	return (rval);
416145522Sdarrenr}
417145522Sdarrenr
418145522Sdarrenr/*
419145522Sdarrenr * show_symdef()
420145522Sdarrenr *	show archive ranlib index (gob)
421145522Sdarrenr */
422145522Sdarrenrint
423145522Sdarrenrshow_symdef(off_t off, u_long len, const char *name, FILE *fp)
424145522Sdarrenr{
425145522Sdarrenr	struct ranlib *prn, *eprn;
426145522Sdarrenr	struct ar_hdr ar_head;
427145522Sdarrenr	char *symdef;
428145522Sdarrenr	char *strtab, *p;
429145522Sdarrenr	u_long size;
430145522Sdarrenr	int namelen, rval = 0;
431145522Sdarrenr
432145522Sdarrenr	MMAP(symdef, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off);
433145522Sdarrenr	if (symdef == MAP_FAILED)
434145522Sdarrenr		return (1);
435145522Sdarrenr	if (usemmap)
436145522Sdarrenr		(void)madvise(symdef, len, MADV_SEQUENTIAL);
437145522Sdarrenr
438145522Sdarrenr	namelen = sizeof(ar_head.ar_name);
439145522Sdarrenr	if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) {
440145522Sdarrenr		warn("%s: malloc", name);
441255332Scy		MUNMAP(symdef, len);
442255332Scy		return (1);
443255332Scy	}
444255332Scy
445255332Scy	size = *(u_long *)symdef;
446255332Scy	prn = (struct ranlib *)(symdef + sizeof(u_long));
447255332Scy	eprn = prn + size / sizeof(*prn);
448255332Scy	strtab = symdef + sizeof(u_long) + size + sizeof(u_long);
449255332Scy
450255332Scy	printf("\nArchive index:\n");
451255332Scy	for (; prn < eprn; prn++) {
452255332Scy		if (fseeko(fp, prn->ran_off, SEEK_SET)) {
453255332Scy			warn("%s: fseeko", name);
454255332Scy			rval = 1;
455255332Scy			break;
456255332Scy		}
457255332Scy
458255332Scy		if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 ||
459255332Scy		    memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
460255332Scy			warnx("%s: member fseeko", name);
461255332Scy			rval = 1;
46253642Sguido			break;
46353642Sguido		}
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