diff --git a/binutils/ChangeLog b/binutils/ChangeLog index da6eb912f4..a7be3db574 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,11 @@ +2017-07-02 Jan Kratochvil + + * 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 PR binutils/21665 diff --git a/binutils/dwarf.c b/binutils/dwarf.c index 5195f8d6b5..70aa0111a6 100644 --- a/binutils/dwarf.c +++ b/binutils/dwarf.c @@ -28,6 +28,12 @@ #include "dwarf2.h" #include "dwarf.h" #include "gdb/gdb-index.h" +#include + +#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 (_(" ")); + 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 }, diff --git a/binutils/dwarf.h b/binutils/dwarf.h index 939c2e8627..4d3330c2b9 100644 --- a/binutils/dwarf.h +++ b/binutils/dwarf.h @@ -99,6 +99,7 @@ enum dwarf_section_display_enum types, weaknames, gdb_index, + debug_names, trace_info, trace_abbrev, trace_aranges, diff --git a/binutils/readelf.c b/binutils/readelf.c index 90af7cc39c..b2f75c0048 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -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