summaryrefslogtreecommitdiffstats
path: root/src/nm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm.c')
-rw-r--r--src/nm.c326
1 files changed, 252 insertions, 74 deletions
diff --git a/src/nm.c b/src/nm.c
index f78861ec..7db6fb89 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -1,28 +1,20 @@
/* Print symbol information from ELF file in human-readable form.
- Copyright (C) 2000-2008, 2009 Red Hat, Inc.
- This file is part of Red Hat elfutils.
+ Copyright (C) 2000-2008, 2009, 2011, 2012 Red Hat, Inc.
+ This file is part of elfutils.
Written by Ulrich Drepper <[email protected]>, 2000.
- Red Hat elfutils 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; version 2 of the License.
+ This file 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 3 of the License, or
+ (at your option) any later version.
- Red Hat elfutils is distributed in the hope that it will be useful, but
+ elfutils 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.
+ 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 Red Hat elfutils; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
-
- Red Hat elfutils is an included package of the Open Invention Network.
- An included package of the Open Invention Network is a package for which
- Open Invention Network licensees cross-license their patents. No patent
- license is granted, either expressly or impliedly, by designation as an
- included package. Should you wish to participate in the Open Invention
- Network licensing program, please visit www.openinventionnetwork.com
- <http://www.openinventionnetwork.com>. */
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -65,8 +57,8 @@ ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
/* Values for the parameters which have no short form. */
-#define OPT_DEFINED 0x100
-#define OPT_MARK_WEAK 0x101
+#define OPT_DEFINED 0x100
+#define OPT_MARK_SPECIAL 0x101
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
@@ -92,7 +84,8 @@ static const struct argp_option options[] =
{ NULL, 'B', NULL, 0, N_("Same as --format=bsd"), 0 },
{ "portability", 'P', NULL, 0, N_("Same as --format=posix"), 0 },
{ "radix", 't', "RADIX", 0, N_("Use RADIX for printing symbol values"), 0 },
- { "mark-weak", OPT_MARK_WEAK, NULL, 0, N_("Mark weak symbols"), 0 },
+ { "mark-special", OPT_MARK_SPECIAL, NULL, 0, N_("Mark special symbols"), 0 },
+ { "mark-weak", OPT_MARK_SPECIAL, NULL, OPTION_HIDDEN, "", 0 },
{ "print-size", 'S', NULL, 0, N_("Print size of defined symbols"), 0 },
{ NULL, 0, NULL, 0, N_("Output options:"), 0 },
@@ -100,6 +93,10 @@ static const struct argp_option options[] =
0 },
{ "no-sort", 'p', NULL, 0, N_("Do not sort the symbols"), 0 },
{ "reverse-sort", 'r', NULL, 0, N_("Reverse the sense of the sort"), 0 },
+#ifdef USE_DEMANGLE
+ { "demangle", 'C', NULL, 0,
+ N_("Decode low-level symbol names into source code names"), 0 },
+#endif
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
@@ -113,10 +110,17 @@ static const char args_doc[] = N_("[FILE...]");
/* Prototype for option handler. */
static error_t parse_opt (int key, char *arg, struct argp_state *state);
+/* Parser children. */
+static struct argp_child argp_children[] =
+ {
+ { &color_argp, 0, N_("Output formatting"), 2 },
+ { NULL, 0, NULL, 0}
+ };
+
/* Data structure to communicate with argp functions. */
static struct argp argp =
{
- options, parse_opt, args_doc, doc, NULL, NULL, NULL
+ options, parse_opt, args_doc, doc, argp_children, NULL, NULL
};
@@ -175,6 +179,11 @@ static bool print_armap;
/* If true reverse sorting. */
static bool reverse_sort;
+#ifdef USE_DEMANGLE
+/* If true demangle symbols. */
+static bool demangle;
+#endif
+
/* Type of the section we are printing. */
static GElf_Word symsec_type = SHT_SYMTAB;
@@ -194,9 +203,12 @@ static enum
radix_octal
} radix;
-/* If nonzero weak symbols are distinguished from global symbols by adding
- a `*' after the identifying letter for the symbol class and type. */
-static bool mark_weak;
+/* If nonzero mark special symbols:
+ - weak symbols are distinguished from global symbols by adding
+ a `*' after the identifying letter for the symbol class and type.
+ - TLS symbols are distinguished from normal symbols by adding
+ a '@' after the identifying letter for the symbol class and type. */
+static bool mark_special;
int
@@ -254,7 +266,7 @@ print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
Copyright (C) %s Red Hat, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2009");
+"), "2012");
fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
}
@@ -270,6 +282,12 @@ parse_opt (int key, char *arg,
/* XXX */
break;
+#ifdef USE_DEMANGLE
+ case 'C':
+ demangle = true;
+ break;
+#endif
+
case 'f':
if (strcmp (arg, "bsd") == 0)
format = format_bsd;
@@ -329,8 +347,8 @@ parse_opt (int key, char *arg,
hide_defined = false;
break;
- case OPT_MARK_WEAK:
- mark_weak = true;
+ case OPT_MARK_SPECIAL:
+ mark_special = true;
break;
case 'S':
@@ -439,7 +457,7 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
Elf_Arhdr *arhdr = NULL;
size_t arhdr_off = 0; /* Note: 0 is no valid offset. */
- puts (gettext("\nArchive index:"));
+ fputs_unlocked (gettext("\nArchive index:\n"), stdout);
while (arsym->as_off != 0)
{
@@ -775,24 +793,48 @@ show_symbols_sysv (Ebl *ebl, GElf_Word strndx, const char *fullname,
longest_where, sgettext ("sysv|Line"));
/* Which format string to use (different radix for numbers). */
- const char *fmtstr;
+ const char *number_fmtstr;
if (radix == radix_hex)
- fmtstr = "%-*s|%0*" PRIx64 "|%-6s|%-8s|%*" PRIx64 "|%*s|%s\n";
+ number_fmtstr = "%0*" PRIx64;
else if (radix == radix_decimal)
- fmtstr = "%-*s|%*" PRId64 "|%-6s|%-8s|%*" PRId64 "|%*s|%s\n";
+ number_fmtstr = "%0*" PRId64;
else
- fmtstr = "%-*s|%0*" PRIo64 "|%-6s|%-8s|%*" PRIo64 "|%*s|%s\n";
+ number_fmtstr = "%0*" PRIo64;
+
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
/* Iterate over all symbols. */
- for (cnt = 0; cnt < nsyms; ++cnt)
+ for (cnt = 1; cnt < nsyms; ++cnt)
{
+ /* In this format SECTION entries are not printed. */
+ if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_SECTION)
+ continue;
+
char symstrbuf[50];
const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name,
symstrbuf, sizeof symstrbuf);
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. */
+ if (demangle)
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
char symbindbuf[50];
char symtypebuf[50];
char secnamebuf[1024];
+ char addressbuf[(64 + 2) / 3 + 1];
+ char sizebuf[(64 + 2) / 3 + 1];
/* If we have to precede the line with the file name. */
if (print_file_name)
@@ -801,28 +843,42 @@ show_symbols_sysv (Ebl *ebl, GElf_Word strndx, const char *fullname,
putchar_unlocked (':');
}
+ /* Covert the address. */
+ if (syms[cnt].sym.st_shndx == SHN_UNDEF)
+ addressbuf[0] = sizebuf[0] = '\0';
+ else
+ {
+ snprintf (addressbuf, sizeof (addressbuf), number_fmtstr,
+ digits, syms[cnt].sym.st_value);
+ snprintf (sizebuf, sizeof (sizebuf), number_fmtstr,
+ digits, syms[cnt].sym.st_size);
+ }
+
/* Print the actual string. */
- printf (fmtstr,
- longest_name, symstr,
- digits, syms[cnt].sym.st_value,
+ printf ("%-*s|%s|%-6s|%-8s|%s|%*s|%s\n",
+ longest_name, symstr, addressbuf,
ebl_symbol_binding_name (ebl,
GELF_ST_BIND (syms[cnt].sym.st_info),
symbindbuf, sizeof (symbindbuf)),
ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info),
symtypebuf, sizeof (symtypebuf)),
- digits, syms[cnt].sym.st_size, longest_where, syms[cnt].where,
+ sizebuf, longest_where, syms[cnt].where,
ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx,
secnamebuf, sizeof (secnamebuf), scnnames,
shnum));
}
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
if (scnnames_malloced)
free (scnnames);
}
static char
-class_type_char (GElf_Sym *sym)
+class_type_char (Elf *elf, const GElf_Ehdr *ehdr, GElf_Sym *sym)
{
int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL;
@@ -834,14 +890,35 @@ class_type_char (GElf_Sym *sym)
/* Undefined symbols must be global. */
return 'U';
- char result = "NDTSFB "[GELF_ST_TYPE (sym->st_info)];
+ char result = "NDTSFBD "[GELF_ST_TYPE (sym->st_info)];
+
+ if (result == 'D')
+ {
+ /* Special handling: unique data symbols. */
+ if (ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX
+ && GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE)
+ result = 'u';
+ else
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, sym->st_shndx),
+ &shdr_mem);
+ if (shdr != NULL)
+ {
+ if ((shdr->sh_flags & SHF_WRITE) == 0)
+ result = 'R';
+ else if (shdr->sh_type == SHT_NOBITS)
+ result = 'B';
+ }
+ }
+ }
return local_p ? tolower (result) : result;
}
static void
-show_symbols_bsd (Elf *elf, GElf_Word strndx,
+show_symbols_bsd (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx,
const char *prefix, const char *fname, const char *fullname,
GElf_SymX *syms, size_t nsyms)
{
@@ -852,17 +929,22 @@ show_symbols_bsd (Elf *elf, GElf_Word strndx,
static const char *const fmtstrs[] =
{
- [radix_hex] = "%0*" PRIx64 " %c%s %s\n",
- [radix_decimal] = "%*" PRId64 " %c%s %s\n",
- [radix_octal] = "%0*" PRIo64 " %c%s %s\n"
+ [radix_hex] = "%8$s%2$0*1$" PRIx64 "%10$s %9$s%3$c%4$s %5$s",
+ [radix_decimal] = "%8$s%*" PRId64 "%10$s %9$s%3$c%4$s %5$s",
+ [radix_octal] = "%8$s%2$0*1$" PRIo64 "%10$s %9$s%3$c%4$s %5$s"
};
static const char *const sfmtstrs[] =
{
- [radix_hex] = "%2$0*1$" PRIx64 " %7$0*6$" PRIx64 " %3$c%4$s %5$s\n",
- [radix_decimal] = "%2$*1$" PRId64 " %7$*6$" PRId64 " %3$c%4$s %5$s\n",
- [radix_octal] = "%2$0*1$" PRIo64 " %7$0*6$" PRIo64 " %3$c%4$s %5$s\n"
+ [radix_hex] = "%8$s%2$0*1$" PRIx64 "%10$s %7$0*6$" PRIx64 " %9$s%3$c%4$s %5$s",
+ [radix_decimal] = "%8$s%2$*1$" PRId64 "%10$s %7$*6$" PRId64 " %9$s%3$c%4$s %5$s",
+ [radix_octal] = "%8$s%2$0*1$" PRIo64 "%10$s %7$0*6$" PRIo64 " %9$s%3$c%4$s %5$s"
};
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+
/* Iterate over all symbols. */
for (size_t cnt = 0; cnt < nsyms; ++cnt)
{
@@ -876,6 +958,23 @@ show_symbols_bsd (Elf *elf, GElf_Word strndx,
if (symstr[0] == '\0')
continue;
+ /* We do not print the entries for files. */
+ if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_FILE)
+ continue;
+
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. */
+ if (demangle)
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
/* If we have to precede the line with the file name. */
if (print_file_name)
{
@@ -883,31 +982,65 @@ show_symbols_bsd (Elf *elf, GElf_Word strndx,
putchar_unlocked (':');
}
+ bool is_tls = GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS;
+ bool is_weak = GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK;
+ const char *marker = (mark_special
+ ? (is_tls ? "@" : (is_weak ? "*" : " ")) : "");
+
if (syms[cnt].sym.st_shndx == SHN_UNDEF)
- printf ("%*s U%s %s\n",
- digits, "",
- mark_weak
- ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
- ? "*" : " ")
- : "",
- symstr);
+ {
+ const char *color = "";
+ if (color_mode)
+ {
+ if (is_tls)
+ color = color_undef_tls;
+ else if (is_weak)
+ color = color_undef_weak;
+ else
+ color = color_undef;
+ }
+
+ printf ("%*s %sU%s %s", digits, "", color, marker, symstr);
+ }
else
- printf (print_size ? sfmtstrs[radix] : fmtstrs[radix],
- digits, syms[cnt].sym.st_value,
- class_type_char (&syms[cnt].sym),
- mark_weak
- ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
- ? "*" : " ")
- : "",
- symstr,
- digits, (uint64_t) syms[cnt].sym.st_size);
+ {
+ const char *color = "";
+ if (color_mode)
+ {
+ if (is_tls)
+ color = color_tls;
+ else if (is_weak)
+ color = color_weak;
+ else
+ color = color_symbol;
+ }
+
+ printf (print_size && syms[cnt].sym.st_size != 0
+ ? sfmtstrs[radix] : fmtstrs[radix],
+ digits, syms[cnt].sym.st_value,
+ class_type_char (elf, ehdr, &syms[cnt].sym), marker,
+ symstr,
+ digits, (uint64_t) syms[cnt].sym.st_size,
+ color_mode ? color_address : "",
+ color,
+ color_mode ? color_off : "");
+ }
+
+ if (color_mode)
+ fputs_unlocked (color_off, stdout);
+ putchar_unlocked ('\n');
}
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
}
static void
-show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
- const char *fullname, GElf_SymX *syms, size_t nsyms)
+show_symbols_posix (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx,
+ const char *prefix, const char *fullname, GElf_SymX *syms,
+ size_t nsyms)
{
if (prefix != NULL && ! print_file_name)
printf ("%s:\n", fullname);
@@ -922,6 +1055,11 @@ show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
int digits = length_map[gelf_getclass (elf) - 1][radix];
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+
/* Iterate over all symbols. */
for (size_t cnt = 0; cnt < nsyms; ++cnt)
{
@@ -935,6 +1073,19 @@ show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
if (symstr[0] == '\0')
continue;
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. */
+ if (demangle)
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
/* If we have to precede the line with the file name. */
if (print_file_name)
{
@@ -945,13 +1096,20 @@ show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
printf (fmtstr,
symstr,
- class_type_char (&syms[cnt].sym),
- mark_weak
- ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ")
+ class_type_char (elf, ehdr, &syms[cnt].sym),
+ mark_special
+ ? (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS
+ ? "@"
+ : (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
+ ? "*" : " "))
: "",
digits, syms[cnt].sym.st_value,
digits, syms[cnt].sym.st_size);
}
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
}
@@ -1053,6 +1211,10 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
INTERNAL_ERROR (fullname);
/* Iterate over all symbols. */
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
int longest_name = 4;
int longest_where = 4;
size_t nentries_used = 0;
@@ -1065,7 +1227,7 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
INTERNAL_ERROR (fullname);
/* Filter out administrative symbols without a name and those
- deselected by ther user with command line options. */
+ deselected by the user with command line options. */
if ((hide_undefined && sym->st_shndx == SHN_UNDEF)
|| (hide_defined && sym->st_shndx != SHN_UNDEF)
|| (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL))
@@ -1079,6 +1241,19 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
if (symstr == NULL)
continue;
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. */
+ if (demangle)
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
longest_name = MAX ((size_t) longest_name, strlen (symstr));
if (sym->st_shndx != SHN_UNDEF
@@ -1133,7 +1308,7 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
}
}
- /* Try to find the symol among the local symbols. */
+ /* Try to find the symbol among the local symbols. */
if (sym_mem[nentries_used].where[0] == '\0')
{
struct local_name fake =
@@ -1164,6 +1339,9 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
/* We use this entry. */
++nentries_used;
}
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
/* Now we know the exact number. */
nentries = nentries_used;
@@ -1186,15 +1364,15 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
break;
case format_bsd:
- show_symbols_bsd (ebl->elf, shdr->sh_link, prefix, fname, fullname,
+ show_symbols_bsd (ebl->elf, ehdr, shdr->sh_link, prefix, fname, fullname,
sym_mem, nentries);
break;
case format_posix:
default:
assert (format == format_posix);
- show_symbols_posix (ebl->elf, shdr->sh_link, prefix, fullname, sym_mem,
- nentries);
+ show_symbols_posix (ebl->elf, ehdr, shdr->sh_link, prefix, fullname,
+ sym_mem, nentries);
break;
}