diff options
Diffstat (limited to 'usr.bin/nm/nm.c')
-rw-r--r-- | usr.bin/nm/nm.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/usr.bin/nm/nm.c b/usr.bin/nm/nm.c new file mode 100644 index 0000000..6744cf6 --- /dev/null +++ b/usr.bin/nm/nm.c @@ -0,0 +1,584 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nm.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <a.out.h> +#include <stab.h> +#include <ar.h> +#include <ranlib.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int ignore_bad_archive_entries = 1; +int print_only_external_symbols; +int print_only_undefined_symbols; +int print_all_symbols; +int print_file_each_line; +int fcount; + +int rev; +int fname(), rname(), value(); +int (*sfunc)() = fname; + +/* 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 *emalloc(); + +/* + * main() + * parse command line, execute process_file() for each file + * specified on the command line. + */ +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ch, errors; + + while ((ch = getopt(argc, argv, "agnopruw")) != EOF) { + switch (ch) { + case 'a': + print_all_symbols = 1; + break; + case 'g': + print_only_external_symbols = 1; + break; + case 'n': + sfunc = value; + break; + case 'o': + print_file_each_line = 1; + break; + case 'p': + sfunc = NULL; + break; + case 'r': + rev = 1; + break; + case 'u': + print_only_undefined_symbols = 1; + break; + case 'w': + ignore_bad_archive_entries = 0; + break; + case '?': + default: + usage(); + } + } + fcount = argc - optind; + argv += optind; + + if (rev && sfunc == fname) + sfunc = rname; + + if (!fcount) + errors = process_file("a.out"); + else { + errors = 0; + do { + errors |= process_file(*argv); + } while (*++argv); + } + exit(errors); +} + +/* + * process_file() + * show symbols in the file given as an argument. Accepts archive and + * object files as input. + */ +process_file(fname) + char *fname; +{ + struct exec exec_head; + FILE *fp; + int retval; + char magic[SARMAG]; + + if (!(fp = fopen(fname, "r"))) { + (void)fprintf(stderr, "nm: cannot read %s.\n", fname); + return(1); + } + + if (fcount > 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) { + (void)fprintf(stderr, "nm: %s: bad format.\n", fname); + (void)fclose(fp); + return(1); + } + rewind(fp); + + /* this could be an archive */ + if (N_BADMAG(exec_head)) { + if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 || + strncmp(magic, ARMAG, SARMAG)) { + (void)fprintf(stderr, + "nm: %s: not object file or archive.\n", fname); + (void)fclose(fp); + return(1); + } + retval = show_archive(fname, fp); + } else + retval = show_objfile(fname, fp); + (void)fclose(fp); + return(retval); +} + +/* + * show_archive() + * show symbols in the given archive file + */ +show_archive(fname, fp) + char *fname; + FILE *fp; +{ + struct ar_hdr ar_head; + struct exec exec_head; + int i, rval; + long last_ar_off; + char *p, *name; + + name = emalloc(sizeof(ar_head.ar_name) + strlen(fname) + 3); + + rval = 0; + + /* while there are more entries in the archive */ + while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) { + /* bad archive entry - stop processing this archive */ + if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { + (void)fprintf(stderr, + "nm: %s: bad format archive header", fname); + (void)free(name); + return(1); + } + + /* remember start position of current archive object */ + last_ar_off = ftell(fp); + + /* skip ranlib entries */ + if (!strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1)) + 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 + */ + p = name; + if (print_file_each_line) + p += sprintf(p, "%s:", fname); + for (i = 0; i < sizeof(ar_head.ar_name); ++i) + if (ar_head.ar_name[i] && ar_head.ar_name[i] != ' ') + *p++ = ar_head.ar_name[i]; + *p++ = '\0'; + + /* get and check current object's header */ + if (fread((char *)&exec_head, sizeof(exec_head), + (size_t)1, fp) != 1) { + (void)fprintf(stderr, "nm: %s: premature EOF.\n", name); + (void)free(name); + return(1); + } + + if (N_BADMAG(exec_head)) { + if (!ignore_bad_archive_entries) { + (void)fprintf(stderr, + "nm: %s: bad format.\n", name); + rval = 1; + } + } else { + (void)fseek(fp, (long)-sizeof(exec_head), + SEEK_CUR); + if (!print_file_each_line) + (void)printf("\n%s:\n", name); + rval |= show_objfile(name, fp); + } + + /* + * skip to next archive object - it starts at the next + * even byte boundary + */ +#define even(x) (((x) + 1) & ~1) +skip: if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)), + SEEK_SET)) { + (void)fprintf(stderr, + "nm: %s: %s\n", fname, strerror(errno)); + (void)free(name); + return(1); + } + } + (void)free(name); + return(rval); +} + +/* + * show_objfile() + * 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. + */ +show_objfile(objname, fp) + char *objname; + FILE *fp; +{ + register struct nlist *names, *np; + register int i, nnames, nrawnames; + struct exec head; + long stabsize; + char *stab; + + /* read a.out header */ + if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) { + (void)fprintf(stderr, + "nm: %s: cannot read header.\n", objname); + return(1); + } + + /* + * skip back to the header - the N_-macros return values relative + * to the beginning of the a.out header + */ + if (fseek(fp, (long)-sizeof(head), SEEK_CUR)) { + (void)fprintf(stderr, + "nm: %s: %s\n", objname, strerror(errno)); + return(1); + } + + /* stop if this is no valid object file */ + if (N_BADMAG(head)) { + (void)fprintf(stderr, + "nm: %s: bad format.\n", objname); + return(1); + } + + /* stop if the object file contains no symbol table */ + if (!head.a_syms) { + (void)fprintf(stderr, + "nm: %s: no name list.\n", objname); + return(1); + } + + if (fseek(fp, (long)N_SYMOFF(head), SEEK_CUR)) { + (void)fprintf(stderr, + "nm: %s: %s\n", objname, strerror(errno)); + return(1); + } + + /* get memory for the symbol table */ + names = emalloc((size_t)head.a_syms); + nrawnames = head.a_syms / sizeof(*names); + if (fread((char *)names, (size_t)head.a_syms, (size_t)1, fp) != 1) { + (void)fprintf(stderr, + "nm: %s: cannot read symbol table.\n", objname); + (void)free((char *)names); + return(1); + } + + /* + * 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((char *)&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) { + (void)fprintf(stderr, + "nm: %s: cannot read stab size.\n", objname); + (void)free((char *)names); + return(1); + } + stab = emalloc((size_t)stabsize); + + /* + * read the string table offset by 4 - all indices into the string + * table include the size specification. + */ + stabsize -= 4; /* we already have the size */ + if (fread(stab + 4, (size_t)stabsize, (size_t)1, fp) != 1) { + (void)fprintf(stderr, + "nm: %s: stab truncated..\n", objname); + (void)free((char *)names); + (void)free(stab); + return(1); + } + + /* + * 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++) { + if (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; + + /* + * 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 = ""; + names[nnames++] = *np; + } + + /* sort the symbol table if applicable */ + if (sfunc) + qsort((char *)names, (size_t)nnames, sizeof(*names), sfunc); + + /* print out symbols */ + for (np = names, i = 0; i < nnames; np++, i++) + print_symbol(objname, np); + + (void)free((char *)names); + (void)free(stab); + return(0); +} + +/* + * print_symbol() + * show one symbol + */ +print_symbol(objname, sym) + char *objname; + register struct nlist *sym; +{ + char *typestring(), typeletter(); + + if (print_file_each_line) + (void)printf("%s:", objname); + + /* + * handle undefined-only format seperately (no space is + * left for symbol values, no type field is printed) + */ + if (print_only_undefined_symbols) { + (void)puts(sym->n_un.n_name); + return; + } + + /* print symbol's value */ + if (SYMBOL_TYPE(sym->n_type) == N_UNDF) + (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 + (void)printf(" %c ", typeletter(sym->n_type)); + + /* print the symbol's name */ + (void)puts(sym->n_un.n_name); +} + +/* + * typestring() + * return the a description string for an STAB entry + */ +char * +typestring(type) + register u_char 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(type) + u_char type; +{ + switch(SYMBOL_TYPE(type)) { + case N_ABS: + return(IS_EXTERNAL(type) ? 'A' : 'a'); + case N_BSS: + return(IS_EXTERNAL(type) ? 'B' : 'b'); + case N_COMM: + return(IS_EXTERNAL(type) ? 'C' : 'c'); + case N_DATA: + return(IS_EXTERNAL(type) ? 'D' : 'd'); + case N_FN: + return(IS_EXTERNAL(type) ? 'F' : 'f'); + case N_TEXT: + return(IS_EXTERNAL(type) ? 'T' : 't'); + case N_UNDF: + return(IS_EXTERNAL(type) ? 'U' : 'u'); + } + return('?'); +} + +fname(a0, b0) + void *a0, *b0; +{ + struct nlist *a = a0, *b = b0; + + return(strcmp(a->n_un.n_name, b->n_un.n_name)); +} + +rname(a0, b0) + void *a0, *b0; +{ + struct nlist *a = a0, *b = b0; + + return(strcmp(b->n_un.n_name, a->n_un.n_name)); +} + +value(a0, b0) + void *a0, *b0; +{ + register struct nlist *a = a0, *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); + } +} + +void * +emalloc(size) + size_t size; +{ + char *p; + + /* NOSTRICT */ + if (p = malloc(size)) + return(p); + (void)fprintf(stderr, "nm: %s\n", strerror(errno)); + exit(1); +} + +usage() +{ + (void)fprintf(stderr, "usage: nm [-agnopruw] [file ...]\n"); + exit(1); +} |