diff options
Diffstat (limited to 'usr.bin/nm')
-rw-r--r-- | usr.bin/nm/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/nm/nm.1 | 117 | ||||
-rw-r--r-- | usr.bin/nm/nm.c | 584 |
3 files changed, 706 insertions, 0 deletions
diff --git a/usr.bin/nm/Makefile b/usr.bin/nm/Makefile new file mode 100644 index 0000000..5266202 --- /dev/null +++ b/usr.bin/nm/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= nm + +.include <bsd.prog.mk> diff --git a/usr.bin/nm/nm.1 b/usr.bin/nm/nm.1 new file mode 100644 index 0000000..ff7cdb2 --- /dev/null +++ b/usr.bin/nm/nm.1 @@ -0,0 +1,117 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)nm.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt NM 1 +.Os BSD 4 +.Sh NAME +.Nm nm +.Nd display name list (symbol table) +.Sh SYNOPSIS +.Nm nm +.Op Fl agnopruw +.Ar +.Sh DESCRIPTION +The symbol table (name list) of each object in +.Ar file(s) +is displayed. +If a library (archive) is given, +.Nm +displays a list for each +object archive member. +If +.Ar file +is not present, +.Nm +searches for the file +.Pa a.out +and if present, displays the symbol +table for +.Pa a.out . +.Bl -tag -width flag +.It Fl a +Display symbol table entries inserted for use by debuggers. +.It Fl g +Restrict display to external (global) symbols. +.It Fl n +Present results in numerical order. +.It Fl o +Display full path or library name of object on every line. +.It Fl p +Do not sort at all. +.It Fl r +Reverse order sort. +.It Fl u +Display undefined symbols only. +.It Fl w +Warn about non-object archive members. +Normally, nm will silently ignore all archive members which are not +object files. +.El +.Pp +Each symbol name is preceded by its value (a blank field if the symbol +is undefined) and one of the following letters: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Fl +debugger symbol table entries (see the +.Fl a +option). +.It Li A +absolute +.It Li B +bss segment symbol +.It Li C +common symbol +.It Li D +data segment symbol +.It Li f +file name +.It Li T +text segment symbol +.It Li U +undefined +.El +.Pp +If the symbol is local (non-external) the type letter is in lower case. +The output is sorted alphabetically. +.Sh SEE ALSO +.Xr ar 1 , +.Xr ar 5 , +.Xr a.out 5 , +.Xr stab 5 +.Sh HISTORY +An +.Nm nm +command appeared in +.At v6 . 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); +} |