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