diff mbox

[binutils] DWARF-5: readelf: .debug_names

Message ID 20170526181552.GA15442@host1.jankratochvil.net
State New
Headers show

Commit Message

Jan Kratochvil May 26, 2017, 6:15 p.m. UTC
Hi,

display DWARF-5 .debug_names (standardized .gdb_index).

Producer is going to be posted along for GDB.


Jan


Contents of the .debug_names section:
Version 5
Augmentation string: 47 44 42 00

CU table:
[  0] 0x0
...

TU table:

Foreign TU table:

Used 65987 of 131072 buckets.
Out of 92058 items there are 26071 bucket clashes (longest of 6 entries).

Symbol table:
[  0] #60aa0000 signed_immed12_overflow:
	<1> DW_TAG_variable DW_IDX_compile_unit=71 DW_IDX_GNU_external=1
	<2> DW_TAG_variable DW_IDX_compile_unit=629 DW_IDX_GNU_static=1
[  1] #b2900002 xc16x_cgen_opval_grb_names_entries: <2> DW_TAG_variable DW_IDX_compile_unit=712 DW_IDX_GNU_static=1
[  2] #db720002 Master_Completion_Sleep: <1> DW_TAG_variable DW_IDX_compile_unit=293 DW_IDX_GNU_external=1
...
binutils/ChangeLog
2017-05-26  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".

Comments

Nick Clifton June 6, 2017, 1:41 p.m. UTC | #1
Hi Jan,

> binutils/ChangeLog
> 2017-05-26  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".

Approved - please apply.

Note:

> +      printf (_("Version %ld\n"), (long) dwarf_version);
> +      if (dwarf_version != 5)

Shouldn't this test be for >= 5 ?

Cheers
  Nick
Jan Kratochvil June 10, 2017, 7:13 a.m. UTC | #2
Hi Nick,

On Tue, 06 Jun 2017 15:41:15 +0200, Nick Clifton wrote:
> > binutils/ChangeLog
> > 2017-05-26  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".
> 
> Approved - please apply.

thanks for the approval but I cannot check it in yet as it depends on:
	[gcc patch] DWARF-5: Define DW_IDX_GNU_static and DW_IDX_GNU_external
	https://gcc.gnu.org/ml/gcc-patches/2017-05/msg02074.html
	Message-ID: <20170526181408.GA15337@host1.jankratochvil.net>
Which may additionally be changed depending on DW_IDX_* preallocation by DWARF
committee.


> > +      printf (_("Version %ld\n"), (long) dwarf_version);
> > +      if (dwarf_version != 5)
> 
> Shouldn't this test be for >= 5 ?

0..4: Such .debug_names format has never been defined anywhere.
5: The supported version.
6..65535: Their format may/will change, I do not think readelf should attempt
	  decoding future unknown data format.

It is written similarly as .gdb_index version check is written:
  /* Prior versions are obsolete, and future versions may not be
     backwards compatible.  */
  if (version < 3 || version > 8)
    {
      warn (_("Unsupported version %lu.\n"), (unsigned long) version);
      return 0;
    }


Jan
Nick Clifton June 12, 2017, 9:56 a.m. UTC | #3
Hi Jan,

> thanks for the approval but I cannot check it in yet as it depends on:
> 	[gcc patch] DWARF-5: Define DW_IDX_GNU_static and DW_IDX_GNU_external

OK.

>>> +      printf (_("Version %ld\n"), (long) dwarf_version);
>>> +      if (dwarf_version != 5)
>>
>> Shouldn't this test be for >= 5 ?
> 
> 0..4: Such .debug_names format has never been defined anywhere.
> 5: The supported version.
> 6..65535: Their format may/will change, I do not think readelf should attempt
> 	  decoding future unknown data format.
> 
> It is written similarly as .gdb_index version check is written:
>    /* Prior versions are obsolete, and future versions may not be
>       backwards compatible.  */
>    if (version < 3 || version > 8)
>      {
>        warn (_("Unsupported version %lu.\n"), (unsigned long) version);
>        return 0;
>      }

Fair enough - but please could you add a comment to this effect so that future
readers of the code do wonder about the version check.

Cheers
  Nick
Jan Kratochvil July 2, 2017, 8:17 p.m. UTC | #4
On Mon, 12 Jun 2017 11:56:14 +0200, Nick Clifton wrote:
> Fair enough - but please could you add a comment to this effect so that future
> readers of the code do wonder about the version check.

+      /* Prior versions did not exist, and future versions may not be
+        backwards compatible.  */
+      if (dwarf_version != 5)

Checked in:
	613643582c4f6145063fe1522d57bd01fe7bba02


Jan
diff mbox

Patch

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index cdedbb2..a3feb6b 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 <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);
 
@@ -932,6 +938,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,
@@ -7596,6 +7618,344 @@  display_debug_frames (struct dwarf_section *section,
 #undef SLEB
 
 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);
+      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)
 {
@@ -8590,6 +8950,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 939c2e8..4d3330c 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 bb6bb79..ab2c8d8 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -6045,7 +6045,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