DWARF-5: readelf: .debug_names
Display DWARF-5 .debug_names (standardized .gdb_index). binutils/ChangeLog 2017-07-02 Jan Kratochvil <jan.kratochvil@redhat.com> * dwarf.c: Include assert.h. (MAX, MIN, get_IDX_name, display_debug_names): New. (debug_displays): Add .debug_names. * dwarf.h: (enum dwarf_section_display_enum): Add debug_names. * readelf.c (process_section_headers): Add ".debug_names".
This commit is contained in:
parent
de837d77bc
commit
613643582c
|
@ -1,3 +1,11 @@
|
||||||
|
2017-07-02 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
|
* dwarf.c: Include assert.h.
|
||||||
|
(MAX, MIN, get_IDX_name, display_debug_names): New.
|
||||||
|
(debug_displays): Add .debug_names.
|
||||||
|
* dwarf.h: (enum dwarf_section_display_enum): Add debug_names.
|
||||||
|
* readelf.c (process_section_headers): Add ".debug_names".
|
||||||
|
|
||||||
2017-07-01 Alan Modra <amodra@gmail.com>
|
2017-07-01 Alan Modra <amodra@gmail.com>
|
||||||
|
|
||||||
PR binutils/21665
|
PR binutils/21665
|
||||||
|
|
365
binutils/dwarf.c
365
binutils/dwarf.c
|
@ -28,6 +28,12 @@
|
||||||
#include "dwarf2.h"
|
#include "dwarf2.h"
|
||||||
#include "dwarf.h"
|
#include "dwarf.h"
|
||||||
#include "gdb/gdb-index.h"
|
#include "gdb/gdb-index.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#undef MAX
|
||||||
|
#undef MIN
|
||||||
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
static const char *regname (unsigned int regno, int row);
|
static const char *regname (unsigned int regno, int row);
|
||||||
|
|
||||||
|
@ -961,6 +967,22 @@ get_FORM_name (unsigned long form)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
get_IDX_name (unsigned long idx)
|
||||||
|
{
|
||||||
|
const char *name = get_DW_IDX_name ((unsigned int) idx);
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
{
|
||||||
|
static char buffer[100];
|
||||||
|
|
||||||
|
snprintf (buffer, sizeof (buffer), _("Unknown IDX value: %lx"), idx);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned char *
|
static unsigned char *
|
||||||
display_block (unsigned char *data,
|
display_block (unsigned char *data,
|
||||||
dwarf_vma length,
|
dwarf_vma length,
|
||||||
|
@ -7633,6 +7655,347 @@ display_debug_frames (struct dwarf_section *section,
|
||||||
|
|
||||||
#undef GET
|
#undef GET
|
||||||
|
|
||||||
|
static int
|
||||||
|
display_debug_names (struct dwarf_section *section, void *file)
|
||||||
|
{
|
||||||
|
unsigned char *hdrptr = section->start;
|
||||||
|
dwarf_vma unit_length;
|
||||||
|
unsigned char *unit_start;
|
||||||
|
const unsigned char *const section_end = section->start + section->size;
|
||||||
|
unsigned char *unit_end;
|
||||||
|
|
||||||
|
printf (_("Contents of the %s section:\n"), section->name);
|
||||||
|
|
||||||
|
load_debug_section (str, file);
|
||||||
|
|
||||||
|
for (; hdrptr < section_end; hdrptr = unit_end)
|
||||||
|
{
|
||||||
|
unsigned int offset_size;
|
||||||
|
uint16_t dwarf_version, padding;
|
||||||
|
uint32_t comp_unit_count, local_type_unit_count, foreign_type_unit_count;
|
||||||
|
uint32_t bucket_count, name_count, abbrev_table_size;
|
||||||
|
uint32_t augmentation_string_size;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
unit_start = hdrptr;
|
||||||
|
|
||||||
|
/* Get and check the length of the block. */
|
||||||
|
SAFE_BYTE_GET_AND_INC (unit_length, hdrptr, 4, section_end);
|
||||||
|
|
||||||
|
if (unit_length == 0xffffffff)
|
||||||
|
{
|
||||||
|
/* This section is 64-bit DWARF. */
|
||||||
|
SAFE_BYTE_GET_AND_INC (unit_length, hdrptr, 8, section_end);
|
||||||
|
offset_size = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
offset_size = 4;
|
||||||
|
unit_end = hdrptr + unit_length;
|
||||||
|
|
||||||
|
if ((hdrptr - section->start) + unit_length > section->size)
|
||||||
|
{
|
||||||
|
warn (_("The length field (0x%lx) for unit 0x%lx in the debug_names "
|
||||||
|
"header is wrong - the section is too small\n"),
|
||||||
|
(long) unit_length, (long) (unit_start - section->start));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get and check the version number. */
|
||||||
|
SAFE_BYTE_GET_AND_INC (dwarf_version, hdrptr, 2, unit_end);
|
||||||
|
printf (_("Version %ld\n"), (long) dwarf_version);
|
||||||
|
|
||||||
|
/* Prior versions did not exist, and future versions may not be
|
||||||
|
backwards compatible. */
|
||||||
|
if (dwarf_version != 5)
|
||||||
|
{
|
||||||
|
warn (_("Only DWARF version 5 .debug_names "
|
||||||
|
"is currently supported.\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (padding, hdrptr, 2, unit_end);
|
||||||
|
if (padding != 0)
|
||||||
|
warn (_("Padding field of .debug_names must be 0 (found 0x%x)\n"),
|
||||||
|
padding);
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (comp_unit_count, hdrptr, 4, unit_end);
|
||||||
|
if (comp_unit_count == 0)
|
||||||
|
warn (_("Compilation unit count must be >= 1 in .debug_names\n"));
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (local_type_unit_count, hdrptr, 4, unit_end);
|
||||||
|
SAFE_BYTE_GET_AND_INC (foreign_type_unit_count, hdrptr, 4, unit_end);
|
||||||
|
SAFE_BYTE_GET_AND_INC (bucket_count, hdrptr, 4, unit_end);
|
||||||
|
SAFE_BYTE_GET_AND_INC (name_count, hdrptr, 4, unit_end);
|
||||||
|
SAFE_BYTE_GET_AND_INC (abbrev_table_size, hdrptr, 4, unit_end);
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (augmentation_string_size, hdrptr, 4, unit_end);
|
||||||
|
if (augmentation_string_size % 4 != 0)
|
||||||
|
{
|
||||||
|
warn (_("Augmentation string length %u must be rounded up "
|
||||||
|
"to a multiple of 4 in .debug_names.\n"),
|
||||||
|
augmentation_string_size);
|
||||||
|
augmentation_string_size += (-augmentation_string_size) & 3;
|
||||||
|
}
|
||||||
|
printf (_("Augmentation string:"));
|
||||||
|
for (i = 0; i < augmentation_string_size; i++)
|
||||||
|
{
|
||||||
|
unsigned char uc;
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (uc, hdrptr, 1, unit_end);
|
||||||
|
printf (" %02x", uc);
|
||||||
|
}
|
||||||
|
putchar ('\n');
|
||||||
|
putchar ('\n');
|
||||||
|
|
||||||
|
printf (_("CU table:\n"));
|
||||||
|
for (i = 0; i < comp_unit_count; i++)
|
||||||
|
{
|
||||||
|
uint64_t cu_offset;
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (cu_offset, hdrptr, offset_size, unit_end);
|
||||||
|
printf (_("[%3u] 0x%lx\n"), i, (unsigned long) cu_offset);
|
||||||
|
}
|
||||||
|
putchar ('\n');
|
||||||
|
|
||||||
|
printf (_("TU table:\n"));
|
||||||
|
for (i = 0; i < local_type_unit_count; i++)
|
||||||
|
{
|
||||||
|
uint64_t tu_offset;
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (tu_offset, hdrptr, offset_size, unit_end);
|
||||||
|
printf (_("[%3u] 0x%lx\n"), i, (unsigned long) tu_offset);
|
||||||
|
}
|
||||||
|
putchar ('\n');
|
||||||
|
|
||||||
|
printf (_("Foreign TU table:\n"));
|
||||||
|
for (i = 0; i < foreign_type_unit_count; i++)
|
||||||
|
{
|
||||||
|
uint64_t signature;
|
||||||
|
|
||||||
|
SAFE_BYTE_GET_AND_INC (signature, hdrptr, 8, unit_end);
|
||||||
|
printf (_("[%3u] "), i);
|
||||||
|
print_dwarf_vma (signature, 8);
|
||||||
|
putchar ('\n');
|
||||||
|
}
|
||||||
|
putchar ('\n');
|
||||||
|
|
||||||
|
const uint32_t *const hash_table_buckets = (uint32_t *) hdrptr;
|
||||||
|
hdrptr += bucket_count * sizeof (uint32_t);
|
||||||
|
const uint32_t *const hash_table_hashes = (uint32_t *) hdrptr;
|
||||||
|
hdrptr += name_count * sizeof (uint32_t);
|
||||||
|
unsigned char *const name_table_string_offsets = hdrptr;
|
||||||
|
hdrptr += name_count * offset_size;
|
||||||
|
unsigned char *const name_table_entry_offsets = hdrptr;
|
||||||
|
hdrptr += name_count * offset_size;
|
||||||
|
unsigned char *const abbrev_table = hdrptr;
|
||||||
|
hdrptr += abbrev_table_size;
|
||||||
|
const unsigned char *const abbrev_table_end = hdrptr;
|
||||||
|
unsigned char *const entry_pool = hdrptr;
|
||||||
|
if (hdrptr > unit_end)
|
||||||
|
{
|
||||||
|
warn (_("Entry pool offset (0x%lx) exceeds unit size 0x%lx "
|
||||||
|
"for unit 0x%lx in the debug_names\n"),
|
||||||
|
(long) (hdrptr - section->start),
|
||||||
|
(long) (unit_end - section->start),
|
||||||
|
(long) (unit_start - section->start));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t buckets_filled = 0;
|
||||||
|
size_t bucketi;
|
||||||
|
for (bucketi = 0; bucketi < bucket_count; bucketi++)
|
||||||
|
{
|
||||||
|
const uint32_t bucket = hash_table_buckets[bucketi];
|
||||||
|
|
||||||
|
if (bucket != 0)
|
||||||
|
++buckets_filled;
|
||||||
|
}
|
||||||
|
printf (_("Used %zu of %lu buckets.\n"), buckets_filled,
|
||||||
|
(unsigned long) bucket_count);
|
||||||
|
|
||||||
|
uint32_t hash_prev;
|
||||||
|
size_t hash_clash_count = 0;
|
||||||
|
size_t longest_clash = 0;
|
||||||
|
size_t this_length = 0;
|
||||||
|
size_t hashi;
|
||||||
|
for (hashi = 0; hashi < name_count; hashi++)
|
||||||
|
{
|
||||||
|
const uint32_t hash_this = hash_table_hashes[hashi];
|
||||||
|
|
||||||
|
if (hashi > 0)
|
||||||
|
{
|
||||||
|
if (hash_prev % bucket_count == hash_this % bucket_count)
|
||||||
|
{
|
||||||
|
++hash_clash_count;
|
||||||
|
++this_length;
|
||||||
|
longest_clash = MAX (longest_clash, this_length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this_length = 0;
|
||||||
|
}
|
||||||
|
hash_prev = hash_this;
|
||||||
|
}
|
||||||
|
printf (_("Out of %lu items there are %zu bucket clashes"
|
||||||
|
" (longest of %zu entries).\n"),
|
||||||
|
(unsigned long) name_count, hash_clash_count, longest_clash);
|
||||||
|
assert (name_count == buckets_filled + hash_clash_count);
|
||||||
|
|
||||||
|
struct abbrev_lookup_entry
|
||||||
|
{
|
||||||
|
dwarf_vma abbrev_tag;
|
||||||
|
unsigned char *abbrev_lookup_ptr;
|
||||||
|
};
|
||||||
|
struct abbrev_lookup_entry *abbrev_lookup = NULL;
|
||||||
|
size_t abbrev_lookup_used = 0;
|
||||||
|
size_t abbrev_lookup_allocated = 0;
|
||||||
|
|
||||||
|
unsigned char *abbrevptr = abbrev_table;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned int bytes_read;
|
||||||
|
const dwarf_vma abbrev_tag = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
if (abbrev_tag == 0)
|
||||||
|
break;
|
||||||
|
if (abbrev_lookup_used == abbrev_lookup_allocated)
|
||||||
|
{
|
||||||
|
abbrev_lookup_allocated = MAX (0x100,
|
||||||
|
abbrev_lookup_allocated * 2);
|
||||||
|
abbrev_lookup = xrealloc (abbrev_lookup,
|
||||||
|
(abbrev_lookup_allocated
|
||||||
|
* sizeof (*abbrev_lookup)));
|
||||||
|
}
|
||||||
|
assert (abbrev_lookup_used < abbrev_lookup_allocated);
|
||||||
|
struct abbrev_lookup_entry *entry;
|
||||||
|
for (entry = abbrev_lookup;
|
||||||
|
entry < abbrev_lookup + abbrev_lookup_used;
|
||||||
|
entry++)
|
||||||
|
if (entry->abbrev_tag == abbrev_tag)
|
||||||
|
{
|
||||||
|
warn (_("Duplicate abbreviation tag %lu "
|
||||||
|
"in unit 0x%lx in the debug_names\n"),
|
||||||
|
(long) abbrev_tag, (long) (unit_start - section->start));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry = &abbrev_lookup[abbrev_lookup_used++];
|
||||||
|
entry->abbrev_tag = abbrev_tag;
|
||||||
|
entry->abbrev_lookup_ptr = abbrevptr;
|
||||||
|
|
||||||
|
/* Skip DWARF tag. */
|
||||||
|
read_uleb128 (abbrevptr, &bytes_read, abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const dwarf_vma index = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
const dwarf_vma form = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
if (index == 0 && form == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf (_("\nSymbol table:\n"));
|
||||||
|
uint32_t namei;
|
||||||
|
for (namei = 0; namei < name_count; ++namei)
|
||||||
|
{
|
||||||
|
uint64_t string_offset, entry_offset;
|
||||||
|
|
||||||
|
SAFE_BYTE_GET (string_offset,
|
||||||
|
name_table_string_offsets + namei * offset_size,
|
||||||
|
offset_size, unit_end);
|
||||||
|
SAFE_BYTE_GET (entry_offset,
|
||||||
|
name_table_entry_offsets + namei * offset_size,
|
||||||
|
offset_size, unit_end);
|
||||||
|
|
||||||
|
printf ("[%3u] #%08x %s:", namei, hash_table_hashes[namei],
|
||||||
|
fetch_indirect_string (string_offset));
|
||||||
|
|
||||||
|
unsigned char *entryptr = entry_pool + entry_offset;
|
||||||
|
|
||||||
|
// We need to scan first whether there is a single or multiple
|
||||||
|
// entries. TAGNO is -2 for the first entry, it is -1 for the
|
||||||
|
// initial tag read of the second entry, then it becomes 0 for the
|
||||||
|
// first entry for real printing etc.
|
||||||
|
int tagno = -2;
|
||||||
|
/* Initialize it due to a false compiler warning. */
|
||||||
|
dwarf_vma second_abbrev_tag = -1;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned int bytes_read;
|
||||||
|
const dwarf_vma abbrev_tag = read_uleb128 (entryptr, &bytes_read,
|
||||||
|
unit_end);
|
||||||
|
entryptr += bytes_read;
|
||||||
|
if (tagno == -1)
|
||||||
|
{
|
||||||
|
second_abbrev_tag = abbrev_tag;
|
||||||
|
tagno = 0;
|
||||||
|
entryptr = entry_pool + entry_offset;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (abbrev_tag == 0)
|
||||||
|
break;
|
||||||
|
if (tagno >= 0)
|
||||||
|
printf ("%s<%lu>",
|
||||||
|
(tagno == 0 && second_abbrev_tag == 0 ? " " : "\n\t"),
|
||||||
|
(unsigned long) abbrev_tag);
|
||||||
|
|
||||||
|
const struct abbrev_lookup_entry *entry;
|
||||||
|
for (entry = abbrev_lookup;
|
||||||
|
entry < abbrev_lookup + abbrev_lookup_used;
|
||||||
|
entry++)
|
||||||
|
if (entry->abbrev_tag == abbrev_tag)
|
||||||
|
break;
|
||||||
|
if (entry >= abbrev_lookup + abbrev_lookup_used)
|
||||||
|
{
|
||||||
|
warn (_("Undefined abbreviation tag %lu "
|
||||||
|
"in unit 0x%lx in the debug_names\n"),
|
||||||
|
(long) abbrev_tag,
|
||||||
|
(long) (unit_start - section->start));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
abbrevptr = entry->abbrev_lookup_ptr;
|
||||||
|
const dwarf_vma dwarf_tag = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
if (tagno >= 0)
|
||||||
|
printf (" %s", get_TAG_name (dwarf_tag));
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const dwarf_vma index = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
const dwarf_vma form = read_uleb128 (abbrevptr, &bytes_read,
|
||||||
|
abbrev_table_end);
|
||||||
|
abbrevptr += bytes_read;
|
||||||
|
if (index == 0 && form == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (tagno >= 0)
|
||||||
|
printf (" %s", get_IDX_name (index));
|
||||||
|
entryptr = read_and_display_attr_value (0, form, 0, entryptr,
|
||||||
|
unit_end, 0, 0,
|
||||||
|
offset_size,
|
||||||
|
dwarf_version, NULL,
|
||||||
|
(tagno < 0), NULL,
|
||||||
|
NULL, '=');
|
||||||
|
}
|
||||||
|
++tagno;
|
||||||
|
}
|
||||||
|
if (tagno <= 0)
|
||||||
|
printf (_(" <no entries>"));
|
||||||
|
putchar ('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
free (abbrev_lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
display_gdb_index (struct dwarf_section *section,
|
display_gdb_index (struct dwarf_section *section,
|
||||||
void *file ATTRIBUTE_UNUSED)
|
void *file ATTRIBUTE_UNUSED)
|
||||||
|
@ -8628,6 +8991,8 @@ struct dwarf_section_display debug_displays[] =
|
||||||
display_debug_not_supported, NULL, FALSE },
|
display_debug_not_supported, NULL, FALSE },
|
||||||
{ { ".gdb_index", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
|
{ { ".gdb_index", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
|
||||||
display_gdb_index, &do_gdb_index, FALSE },
|
display_gdb_index, &do_gdb_index, FALSE },
|
||||||
|
{ { ".debug_names", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
|
||||||
|
display_debug_names, &do_gdb_index, FALSE },
|
||||||
{ { ".trace_info", "", NULL, NULL, 0, 0, trace_abbrev, NULL, 0, NULL },
|
{ { ".trace_info", "", NULL, NULL, 0, 0, trace_abbrev, NULL, 0, NULL },
|
||||||
display_trace_info, &do_trace_info, TRUE },
|
display_trace_info, &do_trace_info, TRUE },
|
||||||
{ { ".trace_abbrev", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
|
{ { ".trace_abbrev", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
|
||||||
|
|
|
@ -99,6 +99,7 @@ enum dwarf_section_display_enum
|
||||||
types,
|
types,
|
||||||
weaknames,
|
weaknames,
|
||||||
gdb_index,
|
gdb_index,
|
||||||
|
debug_names,
|
||||||
trace_info,
|
trace_info,
|
||||||
trace_abbrev,
|
trace_abbrev,
|
||||||
trace_aranges,
|
trace_aranges,
|
||||||
|
|
|
@ -6060,7 +6060,8 @@ process_section_headers (FILE * file)
|
||||||
request_dump_bynumber (i, DEBUG_DUMP);
|
request_dump_bynumber (i, DEBUG_DUMP);
|
||||||
else if (do_debug_frames && streq (name, ".eh_frame"))
|
else if (do_debug_frames && streq (name, ".eh_frame"))
|
||||||
request_dump_bynumber (i, DEBUG_DUMP);
|
request_dump_bynumber (i, DEBUG_DUMP);
|
||||||
else if (do_gdb_index && streq (name, ".gdb_index"))
|
else if (do_gdb_index && (streq (name, ".gdb_index")
|
||||||
|
|| streq (name, ".debug_names")))
|
||||||
request_dump_bynumber (i, DEBUG_DUMP);
|
request_dump_bynumber (i, DEBUG_DUMP);
|
||||||
/* Trace sections for Itanium VMS. */
|
/* Trace sections for Itanium VMS. */
|
||||||
else if ((do_debugging || do_trace_info || do_trace_abbrevs
|
else if ((do_debugging || do_trace_info || do_trace_abbrevs
|
||||||
|
|
Loading…
Reference in New Issue