/* $OpenBSD: nm.c,v 1.23 2004/01/14 04:23:26 millert Exp $ */ /* $NetBSD: nm.c,v 1.7 1996/01/14 23:04:03 pk Exp $ */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Hans Huebner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 static const char sccsid[] = "@(#)nm.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$OpenBSD: nm.c,v 1.23 2004/01/14 04:23:26 millert Exp $"; #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __ELF__ #include #endif #include #include #include #include /* XXX get shared code to handle byte-order swaps */ #include "byte.c" #include "elf.c" #ifdef MID_MACHINE_OVERRIDE #undef MID_MACHINE #define MID_MACHINE MID_MACHINE_OVERRIDE #endif #define SYMTABMAG "/ " #define STRTABMAG "//" union hdr { struct exec aout; Elf_Ehdr elf; }; int armap; int demangle; int non_object_warning; int print_only_external_symbols; int print_only_undefined_symbols; int print_all_symbols; int print_file_each_line; int show_extensions; int issize; int usemmap = 1; /* size vars */ unsigned long total_text, total_data, total_bss, total_total; int non_object_warning, print_totals; int rev; int fname(const void *, const void *); int rname(const void *, const void *); int value(const void *, const void *); int (*sfunc)(const void *, const void *) = fname; char *otherstring(struct nlist *); char *typestring(unsigned int); char typeletter(struct nlist *, int); /* some macros for symbol type (nlist.n_type) handling */ #define IS_DEBUGGER_SYMBOL(x) ((x) & N_STAB) #define IS_EXTERNAL(x) ((x) & N_EXT) #define SYMBOL_TYPE(x) ((x) & (N_TYPE | N_STAB)) void pipe2cppfilt(void); void usage(void); char *symname(struct nlist *); int process_file(int, const char *); int show_archive(int, const char *, FILE *); int show_file(int, int, const char *, FILE *fp, off_t, union hdr *); void print_symbol(const char *, struct nlist *, int); int elf_symload(const char *, FILE *, off_t, Elf_Ehdr *, Elf_Shdr *); #define OPTSTRING_NM "aABCegnoprsuvVw" const struct option longopts_nm[] = { { "debug-syms", no_argument, 0, 'a' }, { "demangle", no_argument, 0, 'C' }, /* { "dynamic", no_argument, 0, 'D' }, */ { "extern-only", no_argument, 0, 'g' }, /* { "line-numbers", no_argument, 0, 'l' }, */ { "no-sort", no_argument, 0, 'p' }, { "numeric-sort", no_argument, 0, 'n' }, { "print-armap", no_argument, 0, 's' }, { "print-file-name", no_argument, 0, 'o' }, { "reverse-sort", no_argument, 0, 'r' }, /* { "size-sort", no_argument, &szval, 1 }, */ { "undefined-only", no_argument, 0, 'u' }, { "version", no_argument, 0, 'V' }, { "help", no_argument, 0, '?' }, { NULL } }; /* * main() * parse command line, execute process_file() for each file * specified on the command line. */ int main(int argc, char *argv[]) { extern char *__progname; extern int optind; const char *optstr; const struct option *lopts; int ch, eval; optstr = OPTSTRING_NM; lopts = longopts_nm; if (!strcmp(__progname, "size")) { issize++; optstr = "tw"; lopts = NULL; } while ((ch = getopt_long(argc, argv, optstr, lopts, NULL)) != -1) { switch (ch) { case 'a': print_all_symbols = 1; break; case 'B': /* no-op, compat with gnu-nm */ break; case 'C': demangle = 1; break; case 'e': show_extensions = 1; break; case 'g': print_only_external_symbols = 1; break; case 'n': case 'v': sfunc = value; break; case 'A': case 'o': print_file_each_line = 1; break; case 'p': sfunc = NULL; break; case 'r': rev = 1; break; case 's': armap = 1; break; case 'u': print_only_undefined_symbols = 1; break; case 'V': fprintf(stderr, "%s\n", rcsid); exit(0); case 'w': non_object_warning = 1; break; case 't': if (issize) { print_totals = 1; break; } case '?': default: usage(); } } if (demangle) pipe2cppfilt(); argv += optind; argc -= optind; if (rev && sfunc == fname) sfunc = rname; eval = 0; if (*argv) do { eval |= process_file(argc, *argv); } while (*++argv); else eval |= process_file(1, "a.out"); if (issize && print_totals) printf("\n%lu\t%lu\t%lu\t%lu\t%lx\tTOTAL\n", total_text, total_data, total_bss, total_total, total_total); exit(eval); } /* * process_file() * show symbols in the file given as an argument. Accepts archive and * object files as input. */ int process_file(int count, const char *fname) { union hdr exec_head; FILE *fp; int retval; char magic[SARMAG]; if (!(fp = fopen(fname, "r"))) { warn("cannot read %s", fname); return(1); } if (!issize && count > 1) (void)printf("\n%s:\n", fname); /* * first check whether this is an object file - read a object * header, and skip back to the beginning */ if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) { warnx("%s: bad format", fname); (void)fclose(fp); return(1); } rewind(fp); /* this could be an archive */ if (!IS_ELF(exec_head.elf) && N_BADMAG(exec_head.aout)) { if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 || strncmp(magic, ARMAG, SARMAG)) { warnx("%s: not object file or archive", fname); (void)fclose(fp); return(1); } retval = show_archive(count, fname, fp); } else retval = show_file(count, 1, fname, fp, 0, &exec_head); (void)fclose(fp); return(retval); } char *nametab; /* * * given the archive member header -- produce member name */ int mmbr_name(struct ar_hdr *arh, char **name, int baselen, int *namelen, FILE *fp) { char *p = *name + strlen(*name); long i; if (nametab && arh->ar_name[0] == '/') { int len; i = atol(&arh->ar_name[1]); len = strlen(&nametab[i]); if (len > *namelen) { p -= (long)*name; if ((*name = realloc(*name, baselen+len)) == NULL) err(1, NULL); *namelen = len; p += (long)*name; } strlcpy(p, &nametab[i], len); p += len; } else #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ if ((arh->ar_name[0] == '#') && (arh->ar_name[1] == '1') && (arh->ar_name[2] == '/') && (isdigit(arh->ar_name[3]))) { int len = atoi(&arh->ar_name[3]); if (len > *namelen) { p -= (long)*name; if ((*name = realloc(*name, baselen+len)) == NULL) err(1, NULL); *namelen = len; p += (long)*name; } if (fread(p, len, 1, fp) != 1) { warnx("%s: premature EOF", *name); free(*name); return(1); } p += len; } else #endif for (i = 0; i < sizeof(arh->ar_name); ++i) if (arh->ar_name[i] && arh->ar_name[i] != ' ') *p++ = arh->ar_name[i]; *p = '\0'; if (p[-1] == '/') *--p = '\0'; return (0); } #define MMAP(ptr, len, prot, flags, fd, off) do { \ if ((ptr = mmap(NULL, len, prot, flags, fd, off)) == MAP_FAILED) { \ usemmap = 0; \ if (errno != EINVAL) \ warn("mmap"); \ else if ((ptr = malloc(len)) == NULL) { \ ptr = MAP_FAILED; \ warn("malloc"); \ } else if (pread(fd, ptr, len, off) != len) { \ free(ptr); \ ptr = MAP_FAILED; \ warn("pread"); \ } \ } \ } while (0) #define MUNMAP(addr, len) do { \ if (usemmap) \ munmap(addr, len); \ else \ free(addr); \ } while (0) /* * show_symtab() * show archive ranlib index (fs5) */ int show_symtab(off_t off, u_long len, const char *name, FILE *fp) { struct ar_hdr ar_head; int *symtab, *ps; char *strtab, *p; int num, rval = 0; int namelen; MMAP(symtab, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off); if (symtab == MAP_FAILED) return (1); namelen = sizeof(ar_head.ar_name); if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) { warn("%s: malloc", name); MUNMAP(symtab, len); } printf("\nArchive index:\n"); num = betoh32(*symtab); strtab = (char *)(symtab + num + 1); for (ps = symtab + 1; num--; ps++, strtab += strlen(strtab) + 1) { if (fseeko(fp, betoh32(*ps), SEEK_SET)) { warn("%s: fseeko", name); rval = 1; break; } if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 || memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { warnx("%s: member fseeko", name); rval = 1; break; } *p = '\0'; if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) { rval = 1; break; } printf("%s in %s\n", strtab, p); } free(p); MUNMAP(symtab, len); return (rval); } /* * show_symdef() * show archive ranlib index (gob) */ int show_symdef(off_t off, u_long len, const char *name, FILE *fp) { struct ranlib *prn, *eprn; struct ar_hdr ar_head; void *symdef; char *strtab, *p; u_long size; int namelen, rval = 0; MMAP(symdef, len, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), off); if (symdef == MAP_FAILED) return (1); if (usemmap) (void)madvise(symdef, len, MADV_SEQUENTIAL); namelen = sizeof(ar_head.ar_name); if ((p = malloc(sizeof(ar_head.ar_name))) == NULL) { warn("%s: malloc", name); MUNMAP(symdef, len); return (1); } size = *(u_long *)symdef; prn = symdef + sizeof(u_long); eprn = prn + size / sizeof(*prn); strtab = symdef + sizeof(u_long) + size + sizeof(u_long); printf("\nArchive index:\n"); for (; prn < eprn; prn++) { if (fseeko(fp, prn->ran_off, SEEK_SET)) { warn("%s: fseeko", name); rval = 1; break; } if (fread(&ar_head, sizeof(ar_head), 1, fp) != 1 || memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { warnx("%s: member fseeko", name); rval = 1; break; } *p = '\0'; if (mmbr_name(&ar_head, &p, 0, &namelen, fp)) { rval = 1; break; } printf("%s in %s\n", strtab + prn->ran_un.ran_strx, p); } free(p); MUNMAP(symdef, len); return (rval); } /* * show_archive() * show symbols in the given archive file */ int show_archive(int count, const char *fname, FILE *fp) { struct ar_hdr ar_head; union hdr exec_head; int i, rval; off_t last_ar_off, foff, symtaboff; char *name; int baselen, namelen; u_long mmbrlen, symtablen; baselen = strlen(fname) + 3; namelen = sizeof(ar_head.ar_name); if ((name = malloc(baselen + namelen)) == NULL) err(1, NULL); rval = 0; nametab = NULL; symtaboff = 0; symtablen = 0; /* while there are more entries in the archive */ while (fread(&ar_head, sizeof(ar_head), 1, fp) == 1) { /* bad archive entry - stop processing this archive */ if (memcmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { warnx("%s: bad format archive header", fname); rval = 1; break; } /* remember start position of current archive object */ last_ar_off = ftello(fp); mmbrlen = atol(ar_head.ar_size); if (strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1) == 0) { if (!issize && armap && show_symdef(last_ar_off, mmbrlen, fname, fp)) { rval = 1; break; } goto skip; } else if (strncmp(ar_head.ar_name, SYMTABMAG, sizeof(SYMTABMAG) - 1) == 0) { /* if nametab hasn't been seen yet -- doit later */ if (!nametab) { symtablen = mmbrlen; symtaboff = last_ar_off; goto skip; } /* load the Sys5 long names table */ } else if (strncmp(ar_head.ar_name, STRTABMAG, sizeof(STRTABMAG) - 1) == 0) { char *p; if ((nametab = malloc(mmbrlen)) == NULL) { warn("%s: nametab", fname); rval = 1; break; } if (fread(nametab, mmbrlen, (size_t)1, fp) != 1) { warnx("%s: premature EOF", fname); rval = 1; break; } for (p = nametab, i = mmbrlen; i--; p++) if (*p == '\n') *p = '\0'; if (issize || !armap || !symtablen || !symtaboff) goto skip; } if (!issize && armap && symtablen && symtaboff) { if (show_symtab(symtaboff, symtablen, fname, fp)) { rval = 1; break; } else { symtaboff = 0; symtablen = 0; goto skip; } } /* * construct a name of the form "archive.a:obj.o:" for the * current archive entry if the object name is to be printed * on each output line */ *name = '\0'; if (count > 1) snprintf(name, baselen - 1, "%s:", fname); if (mmbr_name(&ar_head, &name, baselen, &namelen, fp)) { rval = 1; break; } foff = ftello(fp); /* get and check current object's header */ if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) { warnx("%s: premature EOF", fname); rval = 1; break; } rval |= show_file(2, non_object_warning, name, fp, foff, &exec_head); /* * skip to next archive object - it starts at the next * even byte boundary */ #define even(x) (((x) + 1) & ~1) skip: if (fseeko(fp, last_ar_off + even(mmbrlen), SEEK_SET)) { warn("%s", fname); rval = 1; break; } } if (nametab) { free(nametab); nametab = NULL; } free(name); return(rval); } struct nlist *names; struct nlist **snames; char *stab; int nnames, nrawnames, stabsize; /* * show_file() * show symbols from the object file pointed to by fp. The current * file pointer for fp is expected to be at the beginning of an a.out * header. */ int show_file(int count, int warn_fmt, const char *name, FILE *fp, off_t foff, union hdr *head) { u_long text, data, bss, total; struct nlist *np; Elf_Shdr *shdr; off_t staboff; int i, aout; aout = 0; if (IS_ELF(head->elf) && head->elf.e_ident[EI_CLASS] == ELF_TARG_CLASS && head->elf.e_ident[EI_VERSION] == ELF_TARG_VER) { elf_fix_header(&head->elf); if ((shdr = malloc(head->elf.e_shentsize * head->elf.e_shnum)) == NULL) { warn("%s: malloc shdr", name); return (1); } if (fseeko(fp, foff + head->elf.e_shoff, SEEK_SET)) { warn("%s: fseeko", name); free(shdr); return (1); } if (fread(shdr, head->elf.e_shentsize, head->elf.e_shnum, fp) != head->elf.e_shnum) { warnx("%s: premature EOF", name); free(shdr); return(1); } elf_fix_shdrs(&head->elf, shdr); if (issize) { text = data = bss = 0; for (i = 0; i < head->elf.e_shnum; i++) { if (!(shdr[i].sh_flags & SHF_ALLOC)) ; else if (shdr[i].sh_flags & SHF_EXECINSTR || !(shdr[i].sh_flags & SHF_WRITE)) text += shdr[i].sh_size; else if (shdr[i].sh_type == SHT_NOBITS) bss += shdr[i].sh_size; else data += shdr[i].sh_size; } free(shdr); } else { i = elf_symload(name, fp, foff, &head->elf, shdr); free(shdr); if (i) return (i); } } else if (BAD_OBJECT(head->aout)) { if (warn_fmt) warnx("%s: bad format", name); return (1); } else do { aout++; fix_header_order(&head->aout); if (issize) { text = head->aout.a_text; data = head->aout.a_data; bss = head->aout.a_bss; break; } /* stop if the object file contains no symbol table */ if (!head->aout.a_syms) { warnx("%s: no name list", name); return(1); } if (fseeko(fp, foff + N_SYMOFF(head->aout), SEEK_SET)) { warn("%s", name); return(1); } /* get memory for the symbol table */ if ((names = malloc(head->aout.a_syms)) == NULL) { warn("%s: malloc names", name); return (1); } nrawnames = head->aout.a_syms / sizeof(*names); if ((snames = malloc(nrawnames * sizeof(struct nlist *))) == NULL) { warn("%s: malloc snames", name); free(names); return (1); } if (fread(names, head->aout.a_syms, 1, fp) != 1) { warnx("%s: cannot read symbol table", name); free(snames); free(names); return(1); } fix_nlists_order(names, nrawnames, N_GETMID(head->aout)); staboff = ftello(fp); /* * Following the symbol table comes the string table. * The first 4-byte-integer gives the total size of the * string table _including_ the size specification itself. */ if (fread(&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) { warnx("%s: cannot read stab size", name); free(snames); free(names); return(1); } MMAP(stab, stabsize, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), staboff); if (stab == MAP_FAILED) { free(snames); free(names); return (1); } stabsize -= 4; /* we already have the size */ } while (0); if (issize) { static int first = 1; if (first) { first = 0; printf("text\tdata\tbss\tdec\thex\n"); } total = text + data + bss; printf("%lu\t%lu\t%lu\t%lu\t%lx", text, data, bss, total, total); if (count > 1) (void)printf("\t%s", name); total_text += text; total_data += data; total_bss += bss; total_total += total; printf("\n"); return (0); } /* else we are nm */ /* * it seems that string table is sequential * relative to the symbol table order */ if (sfunc == NULL && usemmap) (void)madvise(stab, stabsize, MADV_SEQUENTIAL); /* * fix up the symbol table and filter out unwanted entries * * common symbols are characterized by a n_type of N_UNDF and a * non-zero n_value -- change n_type to N_COMM for all such * symbols to make life easier later. * * filter out all entries which we don't want to print anyway */ for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { /* * make n_un.n_name a character pointer by adding the string * table's base to n_un.n_strx * * don't mess with zero offsets */ if (np->n_un.n_strx) np->n_un.n_name = stab + np->n_un.n_strx; else np->n_un.n_name = ""; if (aout && SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value) np->n_type = N_COMM | (np->n_type & N_EXT); if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type)) continue; if (print_only_external_symbols && !IS_EXTERNAL(np->n_type)) continue; if (print_only_undefined_symbols && SYMBOL_TYPE(np->n_type) != N_UNDF) continue; snames[nnames++] = np; } /* sort the symbol table if applicable */ if (sfunc) qsort(snames, (size_t)nnames, sizeof(*snames), sfunc); if (count > 1) (void)printf("\n%s:\n", name); /* print out symbols */ for (i = 0; i < nnames; i++) { if (show_extensions && snames[i] != names && SYMBOL_TYPE((snames[i] -1)->n_type) == N_INDR) continue; print_symbol(name, snames[i], aout); } free(snames); free(names); MUNMAP(stab, stabsize); return(0); } int elf_symload(const char *name, FILE *fp, off_t foff, Elf_Ehdr *eh, Elf_Shdr *shdr) { long symsize, shstrsize; struct nlist *np; Elf_Sym sbuf; char *shstr; int i; shstrsize = shdr[eh->e_shstrndx].sh_size; if ((shstr = malloc(shstrsize)) == NULL) { warn("%s: malloc shsrt", name); return (1); } if (fseeko(fp, foff + shdr[eh->e_shstrndx].sh_offset, SEEK_SET)) { warn("%s: fseeko", name); free(shstr); return (1); } if (fread(shstr, 1, shstrsize, fp) != shstrsize) { warnx("%s: premature EOF", name); free(shstr); return(1); } stab = NULL; names = NULL; snames = NULL; for (i = 0; i < eh->e_shnum; i++) { if (!strcmp(shstr + shdr[i].sh_name, ELF_STRTAB)) { stabsize = shdr[i].sh_size; if (stabsize > SIZE_T_MAX) { warnx("%s: corrupt file", name); free(shstr); return (1); } MMAP(stab, stabsize, PROT_READ, MAP_PRIVATE|MAP_FILE, fileno(fp), foff + shdr[i].sh_offset); if (stab == MAP_FAILED) { free(shstr); return (1); } } } for (i = 0; i < eh->e_shnum; i++) { if (!strcmp(shstr + shdr[i].sh_name, ELF_SYMTAB)) { symsize = shdr[i].sh_size; if (fseeko(fp, foff + shdr[i].sh_offset, SEEK_SET)) { warn("%s: fseeko", name); if (stab) MUNMAP(stab, stabsize); free(shstr); return (1); } nrawnames = symsize / sizeof(sbuf); if ((names = calloc(nrawnames, sizeof(*np))) == NULL) { warn("%s: malloc names", name); if (stab) MUNMAP(stab, stabsize); free(names); free(shstr); return (1); } if ((snames = malloc(nrawnames * sizeof(np))) == NULL) { warn("%s: malloc snames", name); if (stab) MUNMAP(stab, stabsize); free(shstr); free(names); free(snames); return (1); } for (np = names; symsize > 0; symsize -= sizeof(sbuf)) { if (fread(&sbuf, 1, sizeof(sbuf), fp) != sizeof(sbuf)) { warn("%s: read symbol", name); if (stab) MUNMAP(stab, stabsize); free(shstr); free(names); free(snames); return (1); } elf_fix_sym(eh, &sbuf); if (!sbuf.st_name) continue; elf2nlist(&sbuf, eh, shdr, shstr, np); np->n_value = sbuf.st_value; np->n_un.n_strx = sbuf.st_name; np++; } nrawnames = np - names; } } free(shstr); if (stab == NULL) { warnx("%s: no name list", name); if (names) free(names); if (snames) free(snames); return (1); } return (0); } char * symname(struct nlist *sym) { if (demangle && sym->n_un.n_name[0] == '_') return sym->n_un.n_name + 1; else return sym->n_un.n_name; } /* * print_symbol() * show one symbol */ void print_symbol(const char *name, struct nlist *sym, int aout) { if (print_file_each_line) (void)printf("%s:", name); /* * handle undefined-only format especially (no space is * left for symbol values, no type field is printed) */ if (!print_only_undefined_symbols) { /* print symbol's value */ if (SYMBOL_TYPE(sym->n_type) == N_UNDF || (show_extensions && SYMBOL_TYPE(sym->n_type) == N_INDR && sym->n_value == 0)) (void)printf(" "); else (void)printf("%08lx", sym->n_value); /* print type information */ if (IS_DEBUGGER_SYMBOL(sym->n_type)) (void)printf(" - %02x %04x %5s ", sym->n_other, sym->n_desc&0xffff, typestring(sym->n_type)); else if (show_extensions) (void)printf(" %c%2s ", typeletter(sym, aout), otherstring(sym)); else (void)printf(" %c ", typeletter(sym, aout)); } if (SYMBOL_TYPE(sym->n_type) == N_INDR && show_extensions) { printf("%s -> %s\n", symname(sym), symname(sym+1)); } else (void)puts(symname(sym)); } char * otherstring(struct nlist *sym) { static char buf[3]; char *result; result = buf; if (N_BIND(sym) == BIND_WEAK) *result++ = 'w'; if (N_AUX(sym) == AUX_OBJECT) *result++ = 'o'; else if (N_AUX(sym) == AUX_FUNC) *result++ = 'f'; *result++ = 0; return buf; } /* * typestring() * return the a description string for an STAB entry */ char * typestring(unsigned int type) { switch(type) { case N_BCOMM: return("BCOMM"); case N_ECOML: return("ECOML"); case N_ECOMM: return("ECOMM"); case N_ENTRY: return("ENTRY"); case N_FNAME: return("FNAME"); case N_FUN: return("FUN"); case N_GSYM: return("GSYM"); case N_LBRAC: return("LBRAC"); case N_LCSYM: return("LCSYM"); case N_LENG: return("LENG"); case N_LSYM: return("LSYM"); case N_PC: return("PC"); case N_PSYM: return("PSYM"); case N_RBRAC: return("RBRAC"); case N_RSYM: return("RSYM"); case N_SLINE: return("SLINE"); case N_SO: return("SO"); case N_SOL: return("SOL"); case N_SSYM: return("SSYM"); case N_STSYM: return("STSYM"); } return("???"); } /* * typeletter() * return a description letter for the given basic type code of an * symbol table entry. The return value will be upper case for * external, lower case for internal symbols. */ char typeletter(struct nlist *np, int aout) { int ext = IS_EXTERNAL(np->n_type); if (!aout && !IS_DEBUGGER_SYMBOL(np->n_type) && np->n_other) return np->n_other; switch(SYMBOL_TYPE(np->n_type)) { case N_ABS: return(ext? 'A' : 'a'); case N_BSS: return(ext? 'B' : 'b'); case N_COMM: return(ext? 'C' : 'c'); case N_DATA: return(ext? 'D' : 'd'); case N_FN: /* NOTE: N_FN == N_WARNING, * in this case, the N_EXT bit is to considered as * part of the symbol's type itself. */ return(ext? 'F' : 'W'); case N_TEXT: return(ext? 'T' : 't'); case N_INDR: return(ext? 'I' : 'i'); case N_SIZE: return(ext? 'S' : 's'); case N_UNDF: return(ext? 'U' : 'u'); } return('?'); } int fname(const void *a0, const void *b0) { struct nlist * const *a = a0, * const *b = b0; return(strcmp((*a)->n_un.n_name, (*b)->n_un.n_name)); } int rname(const void *a0, const void *b0) { struct nlist * const *a = a0, * const *b = b0; return(strcmp((*b)->n_un.n_name, (*a)->n_un.n_name)); } int value(const void *a0, const void *b0) { struct nlist * const *a = a0, * const *b = b0; if (SYMBOL_TYPE((*a)->n_type) == N_UNDF) if (SYMBOL_TYPE((*b)->n_type) == N_UNDF) return(0); else return(-1); else if (SYMBOL_TYPE((*b)->n_type) == N_UNDF) return(1); if (rev) { if ((*a)->n_value == (*b)->n_value) return(rname(a0, b0)); return((*b)->n_value > (*a)->n_value ? 1 : -1); } else { if ((*a)->n_value == (*b)->n_value) return(fname(a0, b0)); return((*a)->n_value > (*b)->n_value ? 1 : -1); } } #define CPPFILT "/usr/bin/c++filt" void pipe2cppfilt(void) { int pip[2]; char *argv[2]; argv[0] = "c++filt"; argv[1] = NULL; if (pipe(pip) == -1) err(1, "pipe"); switch(fork()) { case -1: err(1, "fork"); default: dup2(pip[0], 0); close(pip[0]); close(pip[1]); execve(CPPFILT, argv, NULL); err(1, "execve"); case 0: dup2(pip[1], 1); close(pip[1]); close(pip[0]); } } void usage(void) { extern char *__progname; if (issize) fprintf(stderr, "usage: %s [-tw] [file ...]\n", __progname); else fprintf(stderr, "usage: %s [-aABCegnoprsuvVw] [file ...]\n", __progname); exit(1); }