This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
RFC: Add --dump-function to objdump
- From: "H.J. Lu" <hongjiu dot lu at intel dot com>
- To: binutils at sourceware dot org
- Date: Fri, 19 Sep 2014 11:39:08 -0700
- Subject: RFC: Add --dump-function to objdump
- Authentication-results: sourceware.org; auth=none
- Reply-to: "H.J. Lu" <hjl dot tools at gmail dot com>
Hi,
I need to find in which section a function is defined as well as what
relocations are in the function. I come up with a new --dump-function
option to objdump:
[hjl@gnu-6 binutils]$ ./objdump --dump-function _start /tmp/sort3.o
/tmp/sort3.o: file format elf64-x86-64
Function `_start' defined in section `.text' with relocations:
R_X86_64_64 .text.func2
R_X86_64_64 .text.func2
[hjl@gnu-6 binutils]$
Is this a useful addition?
Thanks.
H.J.
---
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 413de56..850dbea 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -116,6 +116,7 @@ static bfd_boolean display_file_offsets;/* -F */
static const char *prefix; /* --prefix */
static int prefix_strip; /* --prefix-strip */
static size_t prefix_length;
+static const char *dump_function; /* --dump-function */
/* A structure to record the sections mentioned in -j switches. */
struct only
@@ -256,6 +257,7 @@ usage (FILE *stream, int status)
--insn-width=WIDTH Display WIDTH bytes on a single line for -d\n\
--adjust-vma=OFFSET Add OFFSET to all displayed section addresses\n\
--special-syms Include special symbols in symbol dumps\n\
+ --dump-function=NAME Display section and relocations for function NAME\n\
--prefix=PREFIX Add PREFIX to absolute paths for -S\n\
--prefix-strip=LEVEL Strip initial directory names for -S\n"));
fprintf (stream, _("\
@@ -295,7 +297,8 @@ enum option_values
OPTION_ADJUST_VMA,
OPTION_DWARF_DEPTH,
OPTION_DWARF_CHECK,
- OPTION_DWARF_START
+ OPTION_DWARF_START,
+ OPTION_DUMP_FUNCTION
};
static struct option long_options[]=
@@ -347,6 +350,7 @@ static struct option long_options[]=
{"dwarf-depth", required_argument, 0, OPTION_DWARF_DEPTH},
{"dwarf-start", required_argument, 0, OPTION_DWARF_START},
{"dwarf-check", no_argument, 0, OPTION_DWARF_CHECK},
+ {"dump-function", required_argument, NULL, OPTION_DUMP_FUNCTION},
{0, no_argument, 0, 0}
};
@@ -3160,6 +3164,309 @@ dump_dynamic_relocs (bfd *abfd)
}
}
+/* Sort symbols by section. */
+
+static int
+compare_symbols_by_section (const void *ap, const void *bp)
+{
+ const asymbol *a = * (const asymbol **) ap;
+ const asymbol *b = * (const asymbol **) bp;
+ if (a->section == b->section)
+ return a->value - b->value;
+ return a->section - b->section;
+}
+
+static void
+dump_reloc_function (bfd *abfd, asymbol *sym, arelent **relpp,
+ long relcount)
+{
+ arelent **p;
+ char *last_filename, *last_functionname;
+ unsigned int last_line;
+ unsigned int last_discriminator;
+ bfd_vma size;
+ bfd_vma start, stop;
+ asection *sec = sym->section;;
+
+ printf (_(" with relocations:\n"));
+
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+ size = ((elf_symbol_type *) sym)->internal_elf_sym.st_size;
+ else
+ size = 0;
+
+ if (size == 0)
+ {
+ long s_symcount;
+ long count;
+ asymbol **s_syms;
+ asymbol **current;
+
+ /* We make a copy of syms to sort. We don't want to sort syms
+ because that will screw up the relocs. */
+ s_symcount = symcount;
+ s_syms = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
+ memcpy (s_syms, syms, symcount * sizeof (asymbol *));
+
+ s_symcount = remove_useless_symbols (s_syms, s_symcount);
+
+ /* Sort the symbols into section and value order. */
+ qsort (s_syms, s_symcount, sizeof (asymbol *),
+ compare_symbols_by_section);
+
+ /* Find the symbol. */
+ current = s_syms;
+ for (count = 0; count < s_symcount; count++)
+ {
+ if (sec == (*current)->section
+ && sym->value == (*current)->value)
+ break;
+ current++;
+ }
+
+ if (count == s_symcount)
+ bfd_fatal (bfd_get_filename (abfd));
+
+ /* Find the next symbol in the same section with a higher value. */
+ for (; count < symcount; count++)
+ {
+ if (sec != (*current)->section
+ || sym->value != (*current)->value)
+ break;
+ current++;
+ }
+
+ if (count == s_symcount
+ || sec != (*current)->section)
+ size = bfd_get_section_size (sec);
+ else
+ size = (*current)->value - sym->value;
+
+ if (size == 0)
+ bfd_fatal (bfd_get_filename (abfd));
+
+ free (s_syms);
+ }
+
+ start = bfd_get_section_vma (abfd, sec) + sym->value;
+ stop = start + size;
+
+ /* Sort relocations by address. */
+ qsort (relpp, relcount, sizeof (arelent **), compare_relocs);
+
+ last_filename = NULL;
+ last_functionname = NULL;
+ last_line = 0;
+ last_discriminator = 0;
+
+ for (p = relpp; relcount && *p != NULL; p++, relcount--)
+ {
+ arelent *q = *p;
+ const char *filename, *functionname;
+ unsigned int linenumber;
+ unsigned int discriminator;
+ const char *sym_name;
+ const char *section_name;
+ bfd_vma addend2 = 0;
+
+ if (q->address < start)
+ continue;
+ if (q->address >= stop)
+ break;
+
+ if (with_line_numbers
+ && sec != NULL
+ && bfd_find_nearest_line_discriminator (abfd, sec, syms, q->address,
+ &filename, &functionname,
+ &linenumber, &discriminator))
+ {
+ if (functionname != NULL
+ && (last_functionname == NULL
+ || strcmp (functionname, last_functionname) != 0))
+ {
+ printf ("%s():\n", functionname);
+ if (last_functionname != NULL)
+ free (last_functionname);
+ last_functionname = xstrdup (functionname);
+ }
+
+ if (linenumber > 0
+ && (linenumber != last_line
+ || (filename != NULL
+ && last_filename != NULL
+ && filename_cmp (filename, last_filename) != 0)
+ || (discriminator != last_discriminator)))
+ {
+ if (discriminator > 0)
+ printf ("%s:%u\n", filename == NULL ? "???" : filename, linenumber);
+ else
+ printf ("%s:%u (discriminator %u)\n", filename == NULL ? "???" : filename,
+ linenumber, discriminator);
+ last_line = linenumber;
+ last_discriminator = discriminator;
+ if (last_filename != NULL)
+ free (last_filename);
+ if (filename == NULL)
+ last_filename = NULL;
+ else
+ last_filename = xstrdup (filename);
+ }
+ }
+
+ if (q->sym_ptr_ptr && *q->sym_ptr_ptr)
+ {
+ sym_name = (*(q->sym_ptr_ptr))->name;
+ section_name = (*(q->sym_ptr_ptr))->section->name;
+ }
+ else
+ {
+ sym_name = NULL;
+ section_name = NULL;
+ }
+
+ if (wide_output)
+ bfd_printf_vma (abfd, q->address);
+
+ if (q->howto == NULL)
+ printf (" *unknown* ");
+ else if (q->howto->name)
+ {
+ const char *name = q->howto->name;
+
+ /* R_SPARC_OLO10 relocations contain two addends.
+ But because 'arelent' lacks enough storage to
+ store them both, the 64-bit ELF Sparc backend
+ records this as two relocations. One R_SPARC_LO10
+ and one R_SPARC_13, both pointing to the same
+ address. This is merely so that we have some
+ place to store both addend fields.
+
+ Undo this transformation, otherwise the output
+ will be confusing. */
+ if (abfd->xvec->flavour == bfd_target_elf_flavour
+ && elf_tdata(abfd)->elf_header->e_machine == EM_SPARCV9
+ && relcount > 1
+ && !strcmp (q->howto->name, "R_SPARC_LO10"))
+ {
+ arelent *q2 = *(p + 1);
+ if (q2 != NULL
+ && q2->howto
+ && q->address == q2->address
+ && !strcmp (q2->howto->name, "R_SPARC_13"))
+ {
+ name = "R_SPARC_OLO10";
+ addend2 = q2->addend;
+ p++;
+ }
+ }
+ printf (" %-16s ", name);
+ }
+ else
+ printf (" %-16d ", q->howto->type);
+
+ if (sym_name)
+ {
+ objdump_print_symname (abfd, NULL, *q->sym_ptr_ptr);
+ }
+ else
+ {
+ if (section_name == NULL)
+ section_name = "*unknown*";
+ printf ("[%s]", section_name);
+ }
+
+ if (wide_output)
+ {
+ if (q->addend)
+ {
+ bfd_signed_vma addend = q->addend;
+ if (addend < 0)
+ {
+ printf ("-0x");
+ addend = -addend;
+ }
+ else
+ printf ("+0x");
+ bfd_printf_vma (abfd, addend);
+ }
+ if (addend2)
+ {
+ printf ("+0x");
+ bfd_printf_vma (abfd, addend2);
+ }
+ }
+
+ printf ("\n");
+ }
+
+ if (last_filename != NULL)
+ free (last_filename);
+ if (last_functionname != NULL)
+ free (last_functionname);
+}
+
+static void
+dump_relocs_in_function (bfd *abfd, asymbol *sym)
+{
+ arelent **relpp;
+ long relcount;
+ long relsize;
+ asection *section = sym->section;;
+
+ if ((section->flags & SEC_RELOC) == 0)
+ {
+ printf ("\n");
+ return;
+ }
+
+ relsize = bfd_get_reloc_upper_bound (abfd, section);
+ if (relsize < 0)
+ bfd_fatal (bfd_get_filename (abfd));
+
+ if (relsize == 0)
+ {
+ printf ("\n");
+ return;
+ }
+
+ relpp = (arelent **) xmalloc (relsize);
+ relcount = bfd_canonicalize_reloc (abfd, section, relpp, syms);
+
+ if (relcount < 0)
+ bfd_fatal (bfd_get_filename (abfd));
+ else if (relcount == 0)
+ return;
+
+ dump_reloc_function (abfd, sym, relpp, relcount);
+ free (relpp);
+}
+
+static void
+dump_function_info (bfd *abfd)
+{
+ asymbol **current = syms;
+ long count;
+
+ for (count = 0; count < symcount; count++)
+ {
+ const char *name = (*current)->name;
+ if (strcmp (name, dump_function) == 0)
+ {
+ asection *sec = (*current)->section;
+ if (!bfd_is_und_section (sec)
+ && ((sec->flags & (SEC_CODE | SEC_HAS_CONTENTS))
+ == (SEC_CODE | SEC_HAS_CONTENTS)))
+ {
+ printf (_("Function `%s' defined in section `%s'"),
+ dump_function, (*current)->section->name);
+ dump_relocs_in_function (abfd, *current);
+ }
+ return;
+ }
+ current++;
+ }
+}
+
/* Creates a table of paths, to search for source files. */
static void
@@ -3222,6 +3529,7 @@ dump_bfd (bfd *abfd)
if (dump_symtab
|| dump_reloc_info
|| disassemble
+ || dump_function
|| dump_debugging
|| dump_dwarf_section_info)
syms = slurp_symtab (abfd);
@@ -3256,6 +3564,8 @@ dump_bfd (bfd *abfd)
dump_data (abfd);
if (disassemble)
disassemble_data (abfd);
+ if (dump_function)
+ dump_function_info (abfd);
if (dump_debugging)
{
@@ -3637,6 +3947,10 @@ main (int argc, char **argv)
case OPTION_DWARF_CHECK:
dwarf_check = TRUE;
break;
+ case OPTION_DUMP_FUNCTION:
+ dump_function = optarg;
+ seenflag = TRUE;
+ break;
case 'G':
dump_stab_section_info = TRUE;
seenflag = TRUE;