DWARF-5: readelf: .debug_names
authorJan Kratochvil <jan.kratochvil@redhat.com>
Sun, 2 Jul 2017 20:15:05 +0000 (22:15 +0200)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Sun, 2 Jul 2017 20:15:05 +0000 (22:15 +0200)
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".

binutils/ChangeLog
binutils/dwarf.c
binutils/dwarf.h
binutils/readelf.c

index da6eb912f4d52ef4a99265db6d5dd957dd990bbf..a7be3db57475747552b2a4aa6bf4e3a6b23598b6 100644 (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
index 5195f8d6b597821c0539fd8eb7d4448e6c33e114..70aa0111a6c9b09e2edad56e6d850a5011ef9ea2 100644 (file)
 #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 },
index 939c2e8627f9ae2865221e610e7851280bc21e4a..4d3330c2b9e4a900b4ba53445b1762283842f071 100644 (file)
@@ -99,6 +99,7 @@ enum dwarf_section_display_enum
   types,
   weaknames,
   gdb_index,
+  debug_names,
   trace_info,
   trace_abbrev,
   trace_aranges,
index 90af7cc39c1f90dbcf4c91d9dc6da6ca89bd453b..b2f75c0048bfce11205102dc543bc7ca5a6f669f 100644 (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