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:
Jan Kratochvil 2017-07-02 22:15:05 +02:00
parent de837d77bc
commit 613643582c
4 changed files with 376 additions and 1 deletions

View File

@ -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>
PR binutils/21665

View File

@ -28,6 +28,12 @@
#include "dwarf2.h"
#include "dwarf.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);
@ -961,6 +967,22 @@ get_FORM_name (unsigned long form)
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 *
display_block (unsigned char *data,
dwarf_vma length,
@ -7633,6 +7655,347 @@ display_debug_frames (struct dwarf_section *section,
#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
display_gdb_index (struct dwarf_section *section,
void *file ATTRIBUTE_UNUSED)
@ -8628,6 +8991,8 @@ struct dwarf_section_display debug_displays[] =
display_debug_not_supported, NULL, FALSE },
{ { ".gdb_index", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
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 },
display_trace_info, &do_trace_info, TRUE },
{ { ".trace_abbrev", "", NULL, NULL, 0, 0, 0, NULL, 0, NULL },

View File

@ -99,6 +99,7 @@ enum dwarf_section_display_enum
types,
weaknames,
gdb_index,
debug_names,
trace_info,
trace_abbrev,
trace_aranges,

View File

@ -6060,7 +6060,8 @@ process_section_headers (FILE * file)
request_dump_bynumber (i, DEBUG_DUMP);
else if (do_debug_frames && streq (name, ".eh_frame"))
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);
/* Trace sections for Itanium VMS. */
else if ((do_debugging || do_trace_info || do_trace_abbrevs