This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

readelf: Displaying "raw' .debug_aranges


Hi,

I was chasing a anomaly with .debug_aranges and found the eu-readelf
--debug-dump=aranges output not very helpful. It uses libdw
dwarf_getaranges which hides lots of details and sorts the addresses.
Not helpful if you are trying to see why something might be wrong.
binutils readelf also wasn't a big help because it errored out before
displaying anything. So I implemented a "raw" .debug_aranges for
eu-readelf. The output is as follows:

Table at offset 560:

 Length:            60
 DWARF version:      2
 CU offset:      2bb78
 Address size:       8
 Segment size:       0

   0x000000000043c3c0 <expr_unwind>..0x000000000043e0b1 <evalexp+0xe1>
   0x000000000041bba4 <_is_arithop>..0x000000000041bbee <_is_arithop+0x4a>

This did allow me to find what the issue was. But I am not sure yet what
the root cause is. The following comment in the new code summarizes what
is happening:

+    next_table:
+      if (readp != nexthdr)
+       {
+         size_t padding = nexthdr - readp;
+         printf (gettext ("   %Zu padding bytes\n"), padding);
+         // To skip, or not to skip, that is the question...
+         // dwarf_getaranges doesn't skip, it assumes that after
+         // seeing the terminator sequence there must be another
+         // table and ignores the length field.  Strangely enough
+         // that seems to be the right thing sometimes...
+         readp = nexthdr;
+       }

The above seems to be "correct" according to the DWARF spec. You have to
skip till the next table based on table header length field. But on the
buggy (?) .debug_aranges I am looking at not skipping the padding is the
right thing. This is also what dwarf_getaranges does. So maybe that is
correct? But it also isn't what binutils readelf and dwz seem to do...

I'll try to figure out what is going on. Meanwhile comments on the code
welcome.

Thanks,

Mark
>From 7f66062892d3b4d85872a9f5cd05f8b11eeacf5d Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Thu, 21 Mar 2013 11:04:53 +0100
Subject: [PATCH] readelf: Display raw .debug_aranges. Don't use libdw
 dwarf_getaranges.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 src/ChangeLog |   4 ++
 src/readelf.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 138 insertions(+), 35 deletions(-)

diff --git a/src/ChangeLog b/src/ChangeLog
index a641d33..2588c9e 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2013-03-21  Mark Wielaard  <mjw@redhat.com>
+
+	* readelf.c (print_debug_aranges_section): Reimplement.
+
 2013-03-19  Mark Wielaard  <mjw@redhat.com>
 
 	* readelf.c (print_gdb_index_section): Free format_dwarf_addr results.
diff --git a/src/readelf.c b/src/readelf.c
index 1412bed..2d72059 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -4326,61 +4326,160 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 }
 
 
-/* Print content of DWARF .debug_aranges section.  We fortunately do
-   not have to know a bit about the structure of the section, libdwarf
-   takes care of it.  */
+/* Print content of DWARF .debug_aranges section.  */
 static void
 print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 			     Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
 			     GElf_Shdr *shdr, Dwarf *dbg)
 {
-  Dwarf_Aranges *aranges;
-  size_t cnt;
-  if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0))
+  Elf_Data *data = elf_rawdata (scn, NULL);
+
+  if (unlikely (data == NULL))
     {
       error (0, 0, gettext ("cannot get .debug_aranges content: %s"),
-	     dwarf_errmsg (-1));
+	     elf_errmsg (-1));
       return;
     }
 
-  printf (ngettext ("\
-\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entry:\n",
-		    "\
-\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entries:\n",
-		    cnt),
+  printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
 	  elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
-	  (uint64_t) shdr->sh_offset, cnt);
+	  (uint64_t) shdr->sh_offset);
 
-  /* Compute floor(log16(cnt)).  */
-  size_t tmp = cnt;
-  int digits = 1;
-  while (tmp >= 16)
-    {
-      ++digits;
-      tmp >>= 4;
-    }
+  const unsigned char *readp = data->d_buf;
+  const unsigned char *readendp = readp + data->d_size;
 
-  for (size_t n = 0; n < cnt; ++n)
+  while (readp < readendp)
     {
-      Dwarf_Arange *runp = dwarf_onearange (aranges, n);
-      if (unlikely (runp == NULL))
+      const unsigned char *hdrstart = readp;
+      size_t start_offset = hdrstart - (const unsigned char *) data->d_buf;
+
+      printf (gettext ("\nTable at offset %Zu:\n"), start_offset);
+      if (readp + 4 > readendp)
 	{
-	  printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1));
+	invalid_data:
+	  error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+		 elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
 	  return;
 	}
 
-      Dwarf_Addr start;
-      Dwarf_Word length;
-      Dwarf_Off offset;
+      Dwarf_Word length = read_4ubyte_unaligned_inc (dbg, readp);
+      unsigned int length_bytes = 4;
+      if (length == DWARF3_LENGTH_64_BIT)
+	{
+	  if (readp + 8 > readendp)
+	    goto invalid_data;
+	  length = read_8ubyte_unaligned_inc (dbg, readp);
+	  length_bytes = 8;
+	}
+
+      const unsigned char *nexthdr = readp + length;
+      printf (gettext ("\n Length:        %6" PRIu64 "\n"),
+	      (uint64_t) length);
+
+      if (nexthdr > readendp)
+	goto invalid_data;
+
+      if (length == 0)
+	continue;
+
+      if (readp + 2 > readendp)
+	goto invalid_data;
+      uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" DWARF version: %6" PRIuFAST16 "\n"),
+	      version);
+      if (version != 2)
+	{
+	  error (0, 0, gettext ("unsupported aranges version"));
+	  goto next_table;
+	}
 
-      if (unlikely (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0))
-	printf (gettext (" [%*zu] ???\n"), digits, n);
+      Dwarf_Word offset;
+      if (readp + length_bytes > readendp)
+	goto invalid_data;
+      if (length_bytes == 8)
+	offset = read_8ubyte_unaligned_inc (dbg, readp);
       else
-	printf (gettext (" [%*zu] start: %0#*" PRIx64
-			 ", length: %5" PRIu64 ", CU DIE offset: %6"
-			 PRId64 "\n"),
-		digits, n, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 10 : 18,
-		(uint64_t) start, (uint64_t) length, (int64_t) offset);
+	offset = read_4ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" CU offset:     %6" PRIx64 "\n"),
+	      (uint64_t) offset);
+
+      if (readp + 1 > readendp)
+	goto invalid_data;
+      unsigned int address_size = *readp++;
+      printf (gettext (" Address size:  %6" PRIu64 "\n"),
+	      (uint64_t) address_size);
+      if (address_size != 4 && address_size != 8)
+	{
+	  error (0, 0, gettext ("unsupported address size"));
+	  goto next_table;
+	}
+
+      unsigned int segment_size = *readp++;
+      printf (gettext (" Segment size:  %6" PRIu64 "\n\n"),
+	      (uint64_t) segment_size);
+      if (segment_size != 0 && segment_size != 4 && segment_size != 8)
+	{
+	  error (0, 0, gettext ("unsupported segment size"));
+	  goto next_table;
+	}
+
+      /* Round the address to the next multiple of 2*address_size.  */
+      readp += ((2 * address_size - ((readp - hdrstart) % (2 * address_size)))
+		% (2 * address_size));
+
+      while (readp < nexthdr)
+	{
+	  Dwarf_Word range_address;
+	  Dwarf_Word range_length;
+	  Dwarf_Word segment = 0;
+	  if (readp + 2 * address_size + segment_size > readendp)
+	    goto invalid_data;
+	  if (address_size == 4)
+	    {
+	      range_address = read_4ubyte_unaligned_inc (dbg, readp);
+	      range_length = read_4ubyte_unaligned_inc (dbg, readp);
+	    }
+	  else
+	    {
+	      range_address = read_8ubyte_unaligned_inc (dbg, readp);
+	      range_length = read_8ubyte_unaligned_inc (dbg, readp);
+	    }
+
+	  if (segment_size == 4)
+	    segment = read_4ubyte_unaligned_inc (dbg, readp);
+	  else if (segment_size == 8)
+	    segment = read_8ubyte_unaligned_inc (dbg, readp);
+
+	  if (range_address == 0 && range_length == 0 && segment == 0)
+	    break;
+
+	  char *b = format_dwarf_addr (dwflmod, address_size, range_address,
+				       range_address);
+	  char *e = format_dwarf_addr (dwflmod, address_size,
+				       range_address + range_length - 1,
+				       range_length);
+	  if (segment_size != 0)
+	    printf (gettext ("   %s..%s (%" PRIx64 ")\n"), b, e,
+		    (uint64_t) segment);
+	  else
+	    printf (gettext ("   %s..%s\n"), b, e);
+	  free (b);
+	  free (e);
+	}
+
+    next_table:
+      if (readp != nexthdr)
+	{
+	  size_t padding = nexthdr - readp;
+	  printf (gettext ("   %Zu padding bytes\n"), padding);
+	  // To skip, or not to skip, that is the question...
+	  // dwarf_getaranges doesn't skip, it assumes that after
+	  // seeing the terminator sequence there must be another
+	  // table and ignores the length field.  Strangely enough
+	  // that seems to be the right thing sometimes...
+	  readp = nexthdr;
+	}
     }
 }
 
-- 
1.8.1.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]