1991-10-21 17:48:10 +01:00
|
|
|
|
/* nm.c -- Describe symbol table of a rel file.
|
|
|
|
|
Copyright (C) 1991 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
1991-03-21 22:29:06 +01:00
|
|
|
|
#include "bfd.h"
|
1991-10-21 17:48:10 +01:00
|
|
|
|
#include "sysdep.h"
|
1992-01-24 23:49:24 +01:00
|
|
|
|
#include "bucomm.h"
|
1991-03-21 22:29:06 +01:00
|
|
|
|
#include "getopt.h"
|
1991-12-01 03:58:37 +01:00
|
|
|
|
#include "aout/stab_gnu.h"
|
1992-01-24 23:49:24 +01:00
|
|
|
|
#include "aout/ranlib.h"
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PROTO(static boolean, display_file, (char *filename));
|
1991-10-21 17:48:10 +01:00
|
|
|
|
PROTO(static void, do_one_rel_file, (bfd *file));
|
1991-03-21 22:29:06 +01:00
|
|
|
|
PROTO(static unsigned int, filter_symbols, (bfd *file, asymbol **syms,
|
|
|
|
|
unsigned long symcount));
|
|
|
|
|
|
|
|
|
|
PROTO(static void, print_symbols, (bfd *file, asymbol **syms,
|
|
|
|
|
unsigned long symcount));
|
|
|
|
|
extern PROTO(int, (*sorters[2][2]), (char *x, char *y));
|
|
|
|
|
PROTO(static void, print_symdef_entry, (bfd * abfd));
|
|
|
|
|
|
|
|
|
|
/* Command options. */
|
|
|
|
|
|
|
|
|
|
int external_only = 0; /* print external symbols only */
|
|
|
|
|
int file_on_each_line = 0; /* print file name on each line */
|
|
|
|
|
int no_sort = 0; /* don't sort; print syms in order found */
|
|
|
|
|
int print_debug_syms = 0; /* print debugger-only symbols too */
|
|
|
|
|
int print_armap = 0; /* describe __.SYMDEF data in archive files. */
|
|
|
|
|
int reverse_sort = 0; /* sort in downward(alpha or numeric) order */
|
|
|
|
|
int sort_numerically = 0; /* sort in numeric rather than alpha order */
|
|
|
|
|
int undefined_only = 0; /* print undefined symbols only */
|
|
|
|
|
|
|
|
|
|
boolean print_each_filename = false; /* Ick. Used in archives. */
|
|
|
|
|
|
|
|
|
|
/* IMPORT */
|
|
|
|
|
extern char *program_name;
|
|
|
|
|
extern char *program_version;
|
|
|
|
|
extern char *target;
|
|
|
|
|
|
|
|
|
|
struct option long_options[] = {
|
1992-06-24 22:58:17 +02:00
|
|
|
|
{"debug-syms", no_argument, &print_debug_syms, 1},
|
|
|
|
|
{"extern-only", no_argument, &external_only, 1},
|
|
|
|
|
{"no-sort", no_argument, &no_sort, 1},
|
|
|
|
|
{"numeric-sort", no_argument, &sort_numerically, 1},
|
|
|
|
|
{"print-armap", no_argument, &print_armap, 1},
|
|
|
|
|
{"print-file-name", no_argument, &file_on_each_line, 1},
|
|
|
|
|
{"reverse-sort", no_argument, &reverse_sort, 1},
|
|
|
|
|
{"target", optional_argument, (int *)NULL, 0},
|
|
|
|
|
{"undefined-only", no_argument, &undefined_only, 1},
|
|
|
|
|
{0, no_argument, 0, 0}
|
1991-03-21 22:29:06 +01:00
|
|
|
|
};
|
1991-10-21 17:48:10 +01:00
|
|
|
|
|
|
|
|
|
int show_names = 0;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
/* Some error-reporting functions */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
usage ()
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "nm %s\nUsage: %s [-agnoprsu] filename...\n",
|
|
|
|
|
program_version, program_name);
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
1991-10-21 17:48:10 +01:00
|
|
|
|
int c; /* sez which option char */
|
1992-06-24 22:58:17 +02:00
|
|
|
|
int option_index = 0; /* used by getopt and ignored by us */
|
1991-10-21 17:48:10 +01:00
|
|
|
|
int retval;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
program_name = *argv;
|
1991-10-21 17:48:10 +01:00
|
|
|
|
|
|
|
|
|
bfd_init();
|
|
|
|
|
|
1992-06-24 22:58:17 +02:00
|
|
|
|
while ((c = getopt_long(argc, argv, "agnoprsu", long_options, &option_index)) != EOF) {
|
1991-03-21 22:29:06 +01:00
|
|
|
|
switch (c) {
|
|
|
|
|
case 'a': print_debug_syms = 1; break;
|
|
|
|
|
case 'g': external_only = 1; break;
|
|
|
|
|
case 'n': sort_numerically = 1; break;
|
|
|
|
|
case 'o': file_on_each_line = 1; break;
|
|
|
|
|
case 'p': no_sort = 1; break;
|
|
|
|
|
case 'r': reverse_sort = 1; break;
|
|
|
|
|
case 's': print_armap = 1; break;
|
|
|
|
|
case 'u': undefined_only = 1; break;
|
|
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
|
if (!strcmp("target",(long_options[option_index]).name)) {
|
|
|
|
|
target = optarg;
|
|
|
|
|
}
|
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
break; /* we've been given a long option */
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
usage ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Strangely, for the shell you should return only a nonzero value
|
|
|
|
|
on sucess -- the inverse of the C sense. */
|
|
|
|
|
|
|
|
|
|
/* OK, all options now parsed. If no filename specified, do a.out. */
|
1992-06-24 22:58:17 +02:00
|
|
|
|
if (option_index == argc) return !display_file ("a.out");
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
retval = 0;
|
|
|
|
|
show_names = (argc -optind)>1;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
/* We were given several filenames to do: */
|
1991-10-21 17:48:10 +01:00
|
|
|
|
while (optind < argc) {
|
|
|
|
|
if (!display_file (argv[optind++])) {
|
|
|
|
|
retval++;
|
|
|
|
|
}
|
|
|
|
|
}
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
return retval;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Display a file's stats */
|
|
|
|
|
|
|
|
|
|
/* goto here is marginally cleaner than the nested if syntax */
|
|
|
|
|
|
|
|
|
|
static boolean
|
|
|
|
|
display_file (filename)
|
|
|
|
|
char *filename;
|
|
|
|
|
{
|
1991-10-21 17:48:10 +01:00
|
|
|
|
boolean retval = true;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
bfd *file;
|
|
|
|
|
bfd *arfile = NULL;
|
|
|
|
|
|
|
|
|
|
file = bfd_openr(filename, target);
|
|
|
|
|
if (file == NULL) {
|
1991-10-21 17:48:10 +01:00
|
|
|
|
fprintf (stderr, "\n%s: can't open '%s'.\n", program_name, filename);
|
|
|
|
|
return false;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
if (bfd_check_format(file, bfd_object))
|
|
|
|
|
{
|
|
|
|
|
if (show_names) {
|
|
|
|
|
printf ("\n%s:\n",filename);
|
|
|
|
|
}
|
|
|
|
|
do_one_rel_file (file);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (bfd_check_format (file, bfd_archive)) {
|
|
|
|
|
if (!bfd_check_format (file, bfd_archive)) {
|
|
|
|
|
fprintf (stderr, "%s: %s: unknown format.\n", program_name, filename);
|
|
|
|
|
retval = false;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
goto closer;
|
|
|
|
|
}
|
1991-10-21 17:48:10 +01:00
|
|
|
|
|
|
|
|
|
printf("\n%s:\n", filename);
|
|
|
|
|
if (print_armap) print_symdef_entry (file);
|
|
|
|
|
for (;;) {
|
|
|
|
|
arfile = bfd_openr_next_archived_file (file, arfile);
|
|
|
|
|
|
|
|
|
|
if (arfile == NULL) {
|
|
|
|
|
if (bfd_error != no_more_archived_files)
|
|
|
|
|
bfd_fatal (filename);
|
|
|
|
|
goto closer;
|
|
|
|
|
}
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
if (!bfd_check_format(arfile, bfd_object))
|
|
|
|
|
printf("%s: not an object file\n", arfile->filename);
|
|
|
|
|
else {
|
|
|
|
|
printf ("\n%s:\n", arfile->filename);
|
|
|
|
|
do_one_rel_file (arfile) ;
|
|
|
|
|
}
|
1991-03-21 22:29:06 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
1991-10-21 17:48:10 +01:00
|
|
|
|
else {
|
|
|
|
|
fprintf (stderr, "\n%s: %s: unknown format.\n", program_name, filename);
|
|
|
|
|
retval = false;
|
|
|
|
|
}
|
|
|
|
|
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
closer:
|
|
|
|
|
if (bfd_close(file) == false)
|
|
|
|
|
bfd_fatal (filename);
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1991-10-21 17:48:10 +01:00
|
|
|
|
static void
|
1991-03-21 22:29:06 +01:00
|
|
|
|
do_one_rel_file (abfd)
|
|
|
|
|
bfd *abfd;
|
|
|
|
|
{
|
|
|
|
|
unsigned int storage;
|
|
|
|
|
asymbol **syms;
|
|
|
|
|
unsigned int symcount = 0;
|
|
|
|
|
|
|
|
|
|
if (!(bfd_get_file_flags (abfd) & HAS_SYMS)) {
|
|
|
|
|
(void) printf ("No symbols in \"%s\".\n", bfd_get_filename (abfd));
|
1991-10-21 17:48:10 +01:00
|
|
|
|
return;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
storage = get_symtab_upper_bound (abfd);
|
|
|
|
|
if (storage == 0) {
|
|
|
|
|
nosymz:
|
|
|
|
|
fprintf (stderr, "%s: Symflags set but there are none?\n",
|
|
|
|
|
bfd_get_filename (abfd));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
syms = (asymbol **) xmalloc (storage);
|
|
|
|
|
|
|
|
|
|
symcount = bfd_canonicalize_symtab (abfd, syms);
|
|
|
|
|
if (symcount == 0) goto nosymz;
|
|
|
|
|
|
|
|
|
|
/* Discard the symbols we don't want to print.
|
|
|
|
|
It's OK to do this in place; we'll free the storage anyway
|
|
|
|
|
(after printing) */
|
|
|
|
|
|
|
|
|
|
symcount = filter_symbols (abfd, syms, symcount);
|
|
|
|
|
|
|
|
|
|
if (!no_sort)
|
|
|
|
|
qsort((char *) syms, symcount, sizeof (asymbol *),
|
|
|
|
|
sorters[sort_numerically][reverse_sort]);
|
|
|
|
|
|
|
|
|
|
if (print_each_filename && !file_on_each_line)
|
|
|
|
|
printf("\n%s:\n", bfd_get_filename(abfd));
|
|
|
|
|
|
|
|
|
|
print_symbols (abfd, syms, symcount);
|
|
|
|
|
free (syms);
|
1991-10-21 17:48:10 +01:00
|
|
|
|
|
1991-03-21 22:29:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Symbol-sorting predicates */
|
1992-06-24 22:58:17 +02:00
|
|
|
|
#define valueof(x) ((x)->section->vma + (x)->value)
|
1991-03-21 22:29:06 +01:00
|
|
|
|
int
|
|
|
|
|
numeric_forward (x, y)
|
|
|
|
|
char *x;
|
|
|
|
|
char *y;
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return (valueof(*(asymbol **)x) - valueof(*(asymbol **) y));;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
numeric_reverse (x, y)
|
|
|
|
|
char *x;
|
|
|
|
|
char *y;
|
|
|
|
|
{
|
|
|
|
|
return (valueof(*(asymbol **)y) - valueof(*(asymbol **) x));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
non_numeric_forward (x, y)
|
|
|
|
|
char *x;
|
|
|
|
|
char *y;
|
|
|
|
|
{
|
1991-05-21 01:15:15 +02:00
|
|
|
|
CONST char *xn = (*(asymbol **) x)->name;
|
|
|
|
|
CONST char *yn = (*(asymbol **) y)->name;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
|
|
|
|
|
return ((xn == NULL) ? ((yn == NULL) ? 0 : -1) :
|
|
|
|
|
((yn == NULL) ? 1 : strcmp (xn, yn)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
non_numeric_reverse (x, y)
|
|
|
|
|
char *x;
|
|
|
|
|
char *y;
|
|
|
|
|
{
|
|
|
|
|
return -(non_numeric_forward (x, y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int (*sorters[2][2])() = {
|
|
|
|
|
{non_numeric_forward, non_numeric_reverse},
|
|
|
|
|
{numeric_forward, numeric_reverse},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Choose which symbol entries to print;
|
|
|
|
|
compact them downward to get rid of the rest.
|
|
|
|
|
Return the number of symbols to be printed. */
|
|
|
|
|
static unsigned int
|
|
|
|
|
filter_symbols (abfd, syms, symcount)
|
|
|
|
|
bfd *abfd;
|
|
|
|
|
asymbol **syms;
|
|
|
|
|
unsigned long symcount;
|
|
|
|
|
{
|
|
|
|
|
asymbol **from, **to;
|
|
|
|
|
unsigned int dst_count = 0;
|
1992-06-24 22:58:17 +02:00
|
|
|
|
asymbol *sym;
|
|
|
|
|
|
1991-03-21 22:29:06 +01:00
|
|
|
|
unsigned int src_count;
|
|
|
|
|
for (from = to = syms, src_count = 0; src_count <symcount; src_count++) {
|
|
|
|
|
int keep = 0;
|
|
|
|
|
|
1992-06-24 22:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flagword flags = (from[src_count])->flags;
|
|
|
|
|
sym = from[src_count];
|
1991-03-21 22:29:06 +01:00
|
|
|
|
if (undefined_only) {
|
1992-06-24 22:58:17 +02:00
|
|
|
|
keep = sym->section == &bfd_und_section;
|
1991-03-21 22:29:06 +01:00
|
|
|
|
} else if (external_only) {
|
1992-06-24 22:58:17 +02:00
|
|
|
|
keep = ((flags & BSF_GLOBAL)
|
|
|
|
|
|| (sym->section == &bfd_und_section)
|
|
|
|
|
|| (sym->section == &bfd_com_section));
|
|
|
|
|
|
1991-03-21 22:29:06 +01:00
|
|
|
|
} else {
|
|
|
|
|
keep = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!print_debug_syms && ((flags & BSF_DEBUGGING) != 0)) {
|
|
|
|
|
keep = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keep) {
|
|
|
|
|
to[dst_count++] = from[src_count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dst_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_symbols (abfd, syms, symcount)
|
|
|
|
|
bfd *abfd;
|
|
|
|
|
asymbol **syms;
|
|
|
|
|
unsigned long symcount;
|
|
|
|
|
{
|
|
|
|
|
asymbol **sym = syms, **end = syms + symcount;
|
|
|
|
|
|
|
|
|
|
for (; sym < end; ++sym) {
|
|
|
|
|
if (file_on_each_line) printf("%s:", bfd_get_filename(abfd));
|
|
|
|
|
|
|
|
|
|
if (undefined_only) {
|
1992-06-24 22:58:17 +02:00
|
|
|
|
if ((*sym)->section == &bfd_und_section)
|
1991-03-21 22:29:06 +01:00
|
|
|
|
puts ((*sym)->name);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
asymbol *p = *sym;
|
|
|
|
|
if (p) {
|
1991-12-01 03:58:37 +01:00
|
|
|
|
bfd_print_symbol(abfd, stdout, p, bfd_print_symbol_nm);
|
|
|
|
|
putchar('\n');
|
|
|
|
|
}
|
1991-03-21 22:29:06 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_symdef_entry (abfd)
|
|
|
|
|
bfd * abfd;
|
|
|
|
|
{
|
|
|
|
|
symindex idx = BFD_NO_MORE_SYMBOLS;
|
|
|
|
|
carsym *thesym;
|
|
|
|
|
boolean everprinted = false;
|
|
|
|
|
|
|
|
|
|
for (idx = bfd_get_next_mapent (abfd, idx, &thesym);
|
|
|
|
|
idx != BFD_NO_MORE_SYMBOLS;
|
|
|
|
|
idx = bfd_get_next_mapent (abfd, idx, &thesym)) {
|
|
|
|
|
bfd *elt;
|
|
|
|
|
if (!everprinted) {
|
|
|
|
|
printf ("\nArchive index:\n");
|
|
|
|
|
everprinted = true;
|
|
|
|
|
}
|
|
|
|
|
elt = bfd_get_elt_at_index (abfd, idx);
|
|
|
|
|
if (thesym->name != (char *)NULL) {
|
|
|
|
|
printf ("%s in %s\n", thesym->name, bfd_get_filename (elt));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|