* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)nm.c 5.8 (Berkeley) 5/2/91";
int ignore_bad_archive_entries
= 1;
int print_only_external_symbols
;
int print_only_undefined_symbols
;
int print_file_each_line
;
int fname(), rname(), value();
/* 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))
* parse command line, execute process_file() for each file
* specified on the command line.
while ((ch
= getopt(argc
, argv
, "agnopruw")) != EOF
) {
print_only_external_symbols
= 1;
print_file_each_line
= 1;
print_only_undefined_symbols
= 1;
ignore_bad_archive_entries
= 0;
if (rev
&& sfunc
== fname
)
errors
= process_file("a.out");
errors
|= process_file(*argv
);
* show symbols in the file given as an argument. Accepts archive and
if (!(fp
= fopen(fname
, "r"))) {
(void)fprintf(stderr
, "nm: cannot read %s.\n", fname
);
(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
);
/* this could be an archive */
if (N_BADMAG(exec_head
)) {
if (fread(magic
, sizeof(magic
), (size_t)1, fp
) != 1 ||
strncmp(magic
, ARMAG
, SARMAG
)) {
"nm: %s: not object file or archive.\n", fname
);
retval
= show_archive(fname
, fp
);
retval
= show_objfile(fname
, fp
);
* show symbols in the given archive file
name
= emalloc(sizeof(ar_head
.ar_name
) + strlen(fname
) + 3);
/* 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
))) {
"nm: %s: bad format archive header", fname
);
/* remember start position of current archive object */
/* skip ranlib entries */
if (!strncmp(ar_head
.ar_name
, RANLIBMAG
, sizeof(RANLIBMAG
) - 1))
* construct a name of the form "archive.a:obj.o:" for the
* current archive entry if the object name is to be printed
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
];
/* get and check current object's header */
if (fread((char *)&exec_head
, sizeof(exec_head
),
(void)fprintf(stderr
, "nm: %s: premature EOF.\n", name
);
if (N_BADMAG(exec_head
)) {
if (!ignore_bad_archive_entries
) {
"nm: %s: bad format.\n", name
);
(void)fseek(fp
, (long)-sizeof(exec_head
),
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
#define even(x) (((x) + 1) & ~1)
skip
: if (fseek(fp
, last_ar_off
+ even(atol(ar_head
.ar_size
)),
"nm: %s: %s\n", fname
, strerror(errno
));
* 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
show_objfile(objname
, fp
)
register struct nlist
*names
, *np
;
register int i
, nnames
, nrawnames
;
if (fread((char *)&head
, sizeof(head
), (size_t)1, fp
) != 1) {
"nm: %s: cannot read header.\n", objname
);
* 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
)) {
"nm: %s: %s\n", objname
, strerror(errno
));
/* stop if this is no valid object file */
"nm: %s: bad format.\n", objname
);
/* stop if the object file contains no symbol table */
"nm: %s: no name list.\n", objname
);
if (fseek(fp
, (long)N_SYMOFF(head
), SEEK_CUR
)) {
"nm: %s: %s\n", objname
, strerror(errno
));
/* 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) {
"nm: %s: cannot read symbol table.\n", objname
);
(void)free((char *)names
);
* 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) {
"nm: %s: cannot read stab size.\n", objname
);
(void)free((char *)names
);
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) {
"nm: %s: stab truncated..\n", objname
);
(void)free((char *)names
);
* 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
))
if (print_only_external_symbols
&& !IS_EXTERNAL(np
->n_type
))
if (print_only_undefined_symbols
&&
SYMBOL_TYPE(np
->n_type
) != N_UNDF
)
* 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
np
->n_un
.n_name
= stab
+ np
->n_un
.n_strx
;
/* sort the symbol table if applicable */
qsort((char *)names
, (size_t)nnames
, sizeof(*names
), sfunc
);
for (np
= names
, i
= 0; i
< nnames
; np
++, i
++)
print_symbol(objname
, np
);
(void)free((char *)names
);
print_symbol(objname
, sym
)
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
);
/* print symbol's value */
if (SYMBOL_TYPE(sym
->n_type
) == N_UNDF
)
(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
));
(void)printf(" %c ", typeletter(sym
->n_type
));
/* print the symbol's name */
(void)puts(sym
->n_un
.n_name
);
* return the a description string for an STAB entry
* 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.
switch(SYMBOL_TYPE(type
)) {
return(IS_EXTERNAL(type
) ? 'A' : 'a');
return(IS_EXTERNAL(type
) ? 'B' : 'b');
return(IS_EXTERNAL(type
) ? 'C' : 'c');
return(IS_EXTERNAL(type
) ? 'D' : 'd');
return(IS_EXTERNAL(type
) ? 'F' : 'f');
return(IS_EXTERNAL(type
) ? 'T' : 't');
return(IS_EXTERNAL(type
) ? 'U' : 'u');
struct nlist
*a
= a0
, *b
= b0
;
return(strcmp(a
->n_un
.n_name
, b
->n_un
.n_name
));
struct nlist
*a
= a0
, *b
= b0
;
return(strcmp(b
->n_un
.n_name
, a
->n_un
.n_name
));
register struct nlist
*a
= a0
, *b
= b0
;
if (SYMBOL_TYPE(a
->n_type
) == N_UNDF
)
if (SYMBOL_TYPE(b
->n_type
) == N_UNDF
)
else if (SYMBOL_TYPE(b
->n_type
) == N_UNDF
)
if (a
->n_value
== b
->n_value
)
return(b
->n_value
> a
->n_value
? 1 : -1);
if (a
->n_value
== b
->n_value
)
return(a
->n_value
> b
->n_value
? 1 : -1);
(void)fprintf(stderr
, "nm: %s\n", strerror(errno
));
(void)fprintf(stderr
, "usage: nm [-agnopruw] [file ...]\n");