[PATCH] readelf: Add .debug_rnglists support.

Mark Wielaard mark@klomp.org
Wed May 23 14:30:00 GMT 2018


Parse the .debug_rnglists section for DWARF5 --debug-dump=ranges.
Add testcase to show both "normal" and "split" DWARF variants are
handled for DWARF4 and DWARF5.

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libdw/ChangeLog             |  11 +
 libdw/dwarf.h               |  13 ++
 libdw/dwarf_begin_elf.c     |   1 +
 libdw/dwarf_error.c         |   1 +
 libdw/dwarf_formudata.c     |  23 +-
 libdw/dwarf_getscopes.c     |   4 +-
 libdw/libdwP.h              |   2 +
 src/ChangeLog               |  13 ++
 src/readelf.c               | 529 ++++++++++++++++++++++++++++++++++++++++++--
 tests/ChangeLog             |   6 +
 tests/Makefile.am           |   3 +-
 tests/run-readelf-ranges.sh | 236 ++++++++++++++++++++
 12 files changed, 820 insertions(+), 22 deletions(-)
 create mode 100755 tests/run-readelf-ranges.sh

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 3045359..b47fac7 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,14 @@
+2018-04-11  Mark Wielaard  <mark@klomp.org>
+
+	* dwarf.h: Add DWARF5 range list entry DW_RLE encodings.
+	* begin_elf.c (dwarf_scnnames): Add IDX_debug_rnglists.
+	* dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_RNGLISTS.
+	* dwarf_formudata.c (dwarf_formudata): Handle DW_AT_rnglists_base
+	and DW_FORM_rnglistx.
+	* dwarf_getscopes.c (pc_match): Also check for
+	DWARF_E_NO_DEBUG_RNGLISTS.
+	* libdwP.h: Add IDX_debug_rnglists.
+
 2018-05-22  Mark Wielaard  <mark@klomp.org>
 
 	* dwarf_getlocation.c (__libdw_cu_base_address): Treat errors of
diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index c438399..9c2495e 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -886,6 +886,19 @@ enum
 #define DW_MACRO_GNU_hi_user		 DW_MACRO_hi_user
 
 
+/* Range list entry encoding.  */
+enum
+  {
+    DW_RLE_end_of_list = 0x0,
+    DW_RLE_base_addressx = 0x1,
+    DW_RLE_startx_endx = 0x2,
+    DW_RLE_startx_length = 0x3,
+    DW_RLE_offset_pair = 0x4,
+    DW_RLE_base_address = 0x5,
+    DW_RLE_start_end = 0x6,
+    DW_RLE_start_length = 0x7
+  };
+
 /* DWARF call frame instruction encodings.  */
 enum
   {
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 5d8e79e..2e8c5f3 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -64,6 +64,7 @@ static const char dwarf_scnnames[IDX_last][19] =
   [IDX_debug_macinfo] = ".debug_macinfo",
   [IDX_debug_macro] = ".debug_macro",
   [IDX_debug_ranges] = ".debug_ranges",
+  [IDX_debug_rnglists] = ".debug_rnglists",
   [IDX_gnu_debugaltlink] = ".gnu_debugaltlink"
 };
 #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0]))
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 63c8bbe..2e8cd77 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -93,6 +93,7 @@ static const char *errmsgs[] =
     [DWARF_E_NO_FLAG] = N_("no flag value"),
     [DWARF_E_INVALID_OFFSET] = N_("invalid offset"),
     [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"),
+    [DWARF_E_NO_DEBUG_RNGLISTS] = N_(".debug_rnglists section missing"),
     [DWARF_E_INVALID_CFI] = N_("invalid CFI section"),
     [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
     [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"),
diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c
index d56e7dc..280fef2 100644
--- a/libdw/dwarf_formudata.c
+++ b/libdw/dwarf_formudata.c
@@ -211,11 +211,23 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
 	    case DW_AT_ranges:
 	    case DW_AT_start_scope:
 	    case DW_AT_GNU_ranges_base:
-	      /* rangelistptr */
-	      if (__libdw_formptr (attr, IDX_debug_ranges,
-				   DWARF_E_NO_DEBUG_RANGES, NULL,
-				   return_uval) == NULL)
-		return -1;
+	    case DW_AT_rnglists_base:
+	      if (attr->cu->version < 5)
+		{
+		  /* rangelistptr */
+		  if (__libdw_formptr (attr, IDX_debug_ranges,
+				       DWARF_E_NO_DEBUG_RANGES, NULL,
+				       return_uval) == NULL)
+		    return -1;
+		}
+	      else
+		{
+		  /* rnglistsptr */
+		  if (__libdw_formptr (attr, IDX_debug_rnglists,
+				       DWARF_E_NO_DEBUG_RNGLISTS, NULL,
+				       return_uval) == NULL)
+		    return -1;
+		}
 	      break;
 
 	    case DW_AT_stmt_list:
@@ -278,6 +290,7 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
       break;
 
     case DW_FORM_udata:
+    case DW_FORM_rnglistx:
       if (datap + 1 > endp)
 	goto invalid;
       get_uleb128 (*return_uval, datap, endp);
diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c
index df480d3..5662eec 100644
--- a/libdw/dwarf_getscopes.c
+++ b/libdw/dwarf_getscopes.c
@@ -62,7 +62,9 @@ pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
       if (result < 0)
 	{
 	  int error = INTUSE(dwarf_errno) ();
-	  if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES)
+	  if (error != DWARF_E_NOERROR
+	      && error != DWARF_E_NO_DEBUG_RANGES
+	      && error != DWARF_E_NO_DEBUG_RNGLISTS)
 	    {
 	      __libdw_seterrno (error);
 	      return -1;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 82ee5d0..3cfcc55 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -84,6 +84,7 @@ enum
     IDX_debug_macinfo,
     IDX_debug_macro,
     IDX_debug_ranges,
+    IDX_debug_rnglists,
     IDX_gnu_debugaltlink,
     IDX_last
   };
@@ -131,6 +132,7 @@ enum
   DWARF_E_NO_FLAG,
   DWARF_E_INVALID_OFFSET,
   DWARF_E_NO_DEBUG_RANGES,
+  DWARF_E_NO_DEBUG_RNGLISTS,
   DWARF_E_INVALID_CFI,
   DWARF_E_NO_ALT_DEBUGLINK,
   DWARF_E_INVALID_OPCODE,
diff --git a/src/ChangeLog b/src/ChangeLog
index 1a9f4a3..d28d89a 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,16 @@
+2018-04-11  Mark Wielaard  <mark@klomp.org>
+
+	* readelf.c (dwarf_range_list_encoding_string): New function.
+	(dwarf_range_list_encoding_name): Likewise.
+	(known_rnglistptr): New static variable.
+	(listptr_cu): New function.
+	(print_debug_rnglists_section): Likewise.
+	(attr_callback): Call notice_listptr for DW_AT_ranges. Handle
+	DW_AT_rnglists_base.
+	(print_debug_units): Do (silently) scan split DWARF also for
+	debug_ranges before DWARF5 to catch all rangelistptrs.
+	(print_debug): Recognize .debug_rnglists. Reset known_rnglistptr.
+
 2018-01-21  Mark Wielaard  <mark@klomp.org>
 
 	* readelf.c (get_indexed_addr): New function.
diff --git a/src/readelf.c b/src/readelf.c
index 1858802..071906f 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -3984,6 +3984,20 @@ dwarf_unit_string (unsigned int type)
 
 
 static const char *
+dwarf_range_list_encoding_string (unsigned int kind)
+{
+  switch (kind)
+    {
+#define DWARF_ONE_KNOWN_DW_RLE(NAME, CODE) case CODE: return #NAME;
+      DWARF_ALL_KNOWN_DW_RLE
+#undef DWARF_ONE_KNOWN_DW_RLE
+    default:
+      return NULL;
+    }
+}
+
+
+static const char *
 dwarf_line_content_description_string (unsigned int kind)
 {
   switch (kind)
@@ -4145,6 +4159,14 @@ dwarf_unit_name (unsigned int type)
 
 
 static const char *
+dwarf_range_list_encoding_name (unsigned int kind)
+{
+  const char *ret = dwarf_range_list_encoding_string (kind);
+  return string_or_unknown (ret, kind, 0, 0, false);
+}
+
+
+static const char *
 dwarf_line_content_description_name (unsigned int kind)
 {
   const char *ret = dwarf_line_content_description_string (kind);
@@ -4183,6 +4205,9 @@ print_bytes (size_t n, const unsigned char *bytes)
 static int
 get_indexed_addr (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr)
 {
+  if (cu == NULL)
+    return -1;
+
   Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr];
   if (debug_addr == NULL)
     return -1;
@@ -4734,6 +4759,7 @@ struct listptr_table
 
 static struct listptr_table known_loclistptr;
 static struct listptr_table known_rangelistptr;
+static struct listptr_table known_rnglistptr;
 
 static void
 reset_listptr (struct listptr_table *table)
@@ -4851,6 +4877,32 @@ next_listptr_offset (struct listptr_table *table, size_t idx)
   return 0;
 }
 
+/* Returns the next index, base address and CU associated with the
+   list unit offsets.  If there is none false is returned, otherwise
+   true.  Assumes the table has been sorted.  */
+static bool
+listptr_cu (struct listptr_table *table, size_t *idxp,
+	    Dwarf_Off start, Dwarf_Off end,
+	    Dwarf_Addr *base, struct Dwarf_CU **cu)
+{
+  while (*idxp < table->n
+	 && table->table[*idxp].offset < start)
+    ++*idxp;
+
+  if (*idxp < table->n
+      && table->table[*idxp].offset >= start
+      && table->table[*idxp].offset < end)
+    {
+      struct listptr *p = &table->table[*idxp];
+      *base = listptr_base (p);
+      *cu = p->cu;
+      ++*idxp;
+      return true;
+    }
+
+  return false;
+}
+
 static void
 print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 			    Ebl *ebl, GElf_Ehdr *ehdr,
@@ -5151,6 +5203,396 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
     }
 }
 
+/* Print content of DWARF .debug_rnglists section.  */
+static void
+print_debug_rnglists_section (Dwfl_Module *dwflmod,
+			      Ebl *ebl, GElf_Ehdr *ehdr,
+			      Elf_Scn *scn, GElf_Shdr *shdr,
+			      Dwarf *dbg __attribute__((unused)))
+{
+  printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+	  elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+	  (uint64_t) shdr->sh_offset);
+
+  Elf_Data *data =(dbg->sectiondata[IDX_debug_rnglists]
+		   ?: elf_rawdata (scn, NULL));
+  if (unlikely (data == NULL))
+    {
+      error (0, 0, gettext ("cannot get .debug_rnglists content: %s"),
+	     elf_errmsg (-1));
+      return;
+    }
+
+  /* For the listptr to get the base address/CU.  */
+  sort_listptr (&known_rnglistptr, "rnglistptr");
+  size_t listptr_idx = 0;
+
+  const unsigned char *readp = data->d_buf;
+  const unsigned char *const dataend = ((unsigned char *) data->d_buf
+					+ data->d_size);
+  while (readp < dataend)
+    {
+      if (unlikely (readp > dataend - 4))
+	{
+	invalid_data:
+	  error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+		 elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
+	  return;
+	}
+
+      ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
+      printf (gettext ("Table at Offset 0x%" PRIx64 ":\n\n"),
+	      (uint64_t) offset);
+
+      uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp);
+      unsigned int offset_size = 4;
+      if (unlikely (unit_length == 0xffffffff))
+	{
+	  if (unlikely (readp > dataend - 8))
+	    goto invalid_data;
+
+	  unit_length = read_8ubyte_unaligned_inc (dbg, readp);
+	  offset_size = 8;
+	}
+      printf (gettext (" Length:         %8" PRIu64 "\n"), unit_length);
+
+      /* We need at least 2-bytes + 1-byte + 1-byte + 4-bytes = 8
+	 bytes to complete the header.  And this unit cannot go beyond
+	 the section data.  */
+      if (readp > dataend - 8
+	  || unit_length < 8
+	  || unit_length > (uint64_t) (dataend - readp))
+	goto invalid_data;
+
+      const unsigned char *nexthdr = readp + unit_length;
+
+      uint16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" DWARF version:  %8" PRIu16 "\n"), version);
+
+      if (version != 5)
+	{
+	  error (0, 0, gettext ("Unknown version"));
+	  goto next_table;
+	}
+
+      uint8_t address_size = *readp++;
+      printf (gettext (" Address size:   %8" PRIu64 "\n"),
+	      (uint64_t) address_size);
+
+      if (address_size != 4 && address_size != 8)
+	{
+	  error (0, 0, gettext ("unsupported address size"));
+	  goto next_table;
+	}
+
+      uint8_t segment_size = *readp++;
+      printf (gettext (" Segment size:   %8" PRIu64 "\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;
+        }
+
+      uint32_t offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" Offset entries: %8" PRIu64 "\n"),
+	      (uint64_t) offset_entry_count);
+
+      /* We need the CU that uses this unit to get the initial base address. */
+      Dwarf_Addr cu_base = 0;
+      struct Dwarf_CU *cu = NULL;
+      if (listptr_cu (&known_rnglistptr, &listptr_idx,
+		      (Dwarf_Off) offset,
+		      (Dwarf_Off) (nexthdr - (unsigned char *) data->d_buf),
+		      &cu_base, &cu))
+	{
+	  char *basestr = format_dwarf_addr (dwflmod, address_size,
+					     cu_base, cu_base);
+	  Dwarf_Die cudie;
+	  if (dwarf_cu_die (cu, &cudie,
+			    NULL, NULL, NULL, NULL,
+			    NULL, NULL) == NULL)
+	    printf (gettext (" Unknown CU base: %s\n"), basestr);
+	  else
+	    printf (gettext (" CU [%6" PRIx64 "] base: %s\n"),
+		    dwarf_dieoffset (&cudie), basestr);
+	  free (basestr);
+	}
+      else
+	printf (gettext (" Not associated with a CU.\n"));
+
+      printf ("\n");
+
+      const unsigned char *offset_array_start = readp;
+      if (offset_entry_count > 0)
+	{
+	  uint64_t needed = offset_entry_count * offset_size;
+	  if (unit_length - 8 < needed)
+	    {
+	      error (0, 0,
+		     gettext ("too many offset entries for unit length"));
+	      goto next_table;
+	    }
+
+	  printf (gettext ("  Offsets starting at 0x%" PRIx64 ":\n"),
+		  (uint64_t) (offset_array_start
+			      - (unsigned char *) data->d_buf));
+	  for (uint32_t idx = 0; idx < offset_entry_count; idx++)
+	    {
+	      printf ("   [%6" PRIu32 "] ", idx);
+	      if (offset_size == 4)
+		{
+		  uint32_t off = read_4ubyte_unaligned_inc (dbg, readp);
+		  printf ("0x%" PRIx32 "\n", off);
+		}
+	      else
+		{
+		  uint64_t off = read_8ubyte_unaligned_inc (dbg, readp);
+		  printf ("0x%" PRIx64 "\n", off);
+		}
+	    }
+	  printf ("\n");
+	}
+
+      Dwarf_Addr base = cu_base;
+      bool start_of_list = true;
+      while (readp < nexthdr)
+	{
+	  uint8_t kind = *readp++;
+	  uint64_t op1, op2;
+	  char *a1, *a2;
+
+	  /* Skip padding.  */
+	  if (start_of_list && kind == DW_RLE_end_of_list)
+	    continue;
+
+	  if (start_of_list)
+	    {
+	      base = cu_base;
+	      printf ("  Offset: %" PRIx64 ", Index: %" PRIx64 "\n",
+		      (uint64_t) (readp - (unsigned char *) data->d_buf - 1),
+		      (uint64_t) (readp - offset_array_start - 1));
+	      start_of_list = false;
+	    }
+
+	  printf ("    %s", dwarf_range_list_encoding_name (kind));
+	  switch (kind)
+	    {
+	    case DW_RLE_end_of_list:
+	      start_of_list = true;
+	      printf ("\n\n");
+	      break;
+
+	    case DW_RLE_base_addressx:
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		{
+		invalid_range:
+		  error (0, 0, gettext ("invalid range list data"));
+		  goto next_table;
+		}
+	      get_uleb128 (op1, readp, nexthdr);
+	      printf (" %" PRIx64 "\n", op1);
+	      if (! print_unresolved_addresses)
+		{
+		  Dwarf_Addr addr;
+		  if (get_indexed_addr (cu, op1, &addr) != 0)
+		    printf ("       ???\n");
+		  else
+		    {
+		      a1 = format_dwarf_addr (dwflmod, address_size,
+					      addr, addr);
+		      printf ("      %s\n", a1);
+		      free (a1);
+		    }
+		}
+	      break;
+
+	    case DW_RLE_startx_endx:
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op1, readp, nexthdr);
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op2, readp, nexthdr);
+	      printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2);
+	      if (! print_unresolved_addresses)
+		{
+		  Dwarf_Addr addr1;
+		  Dwarf_Addr addr2;
+		  if (get_indexed_addr (cu, op1, &addr1) != 0
+		      || get_indexed_addr (cu, op2, &addr2) != 0)
+		    {
+		      printf ("     ???..\n");
+		      printf ("     ???\n");
+		    }
+		  else
+		    {
+		      a1 = format_dwarf_addr (dwflmod, address_size, op1, op1);
+		      a2 = format_dwarf_addr (dwflmod, address_size,
+					      op2 - 1, op2);
+		      printf ("      %s..\n", a1);
+		      printf ("      %s\n", a2);
+		      free (a1);
+		      free (a2);
+		    }
+		}
+	      break;
+
+	    case DW_RLE_startx_length:
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op1, readp, nexthdr);
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op2, readp, nexthdr);
+	      printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2);
+	      if (! print_unresolved_addresses)
+		{
+		  Dwarf_Addr addr1;
+		  Dwarf_Addr addr2;
+		  bool resolved = get_indexed_addr (cu, op1, &addr1) == 0;
+		  if (resolved)
+		    {
+		      op2 = addr1 + op2;
+		      resolved = get_indexed_addr (cu, op2, &addr2) == 0;
+		    }
+		  if (resolved)
+		    {
+		      printf ("     ???..\n");
+		      printf ("     ???\n");
+		    }
+		  else
+		    {
+		      a1 = format_dwarf_addr (dwflmod, address_size, op1, op1);
+		      a2 = format_dwarf_addr (dwflmod, address_size,
+					      op2 - 1, op2);
+		      printf ("      %s..\n", a1);
+		      printf ("      %s\n", a2);
+		      free (a1);
+		      free (a2);
+		    }
+		}
+	      break;
+
+	    case DW_RLE_offset_pair:
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op1, readp, nexthdr);
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op2, readp, nexthdr);
+	      printf (" %" PRIx64 ", %" PRIx64 "\n", op1, op2);
+	      if (! print_unresolved_addresses)
+		{
+		  op1 += base;
+		  op2 += base;
+		  a1 = format_dwarf_addr (dwflmod, address_size, op1, op1);
+		  a2 = format_dwarf_addr (dwflmod, address_size,
+					  op2 - 1, op2);
+		  printf ("      %s..\n", a1);
+		  printf ("      %s\n", a2);
+		  free (a1);
+		  free (a2);
+		}
+	      break;
+
+	    case DW_RLE_base_address:
+	      if (address_size == 4)
+		{
+		  if ((uint64_t) (nexthdr - readp) < 4)
+		    goto invalid_range;
+		  op1 = read_4ubyte_unaligned_inc (dbg, readp);
+		}
+	      else
+		{
+		  if ((uint64_t) (nexthdr - readp) < 8)
+		    goto invalid_range;
+		  op1 = read_8ubyte_unaligned_inc (dbg, readp);
+		}
+	      base = op1;
+	      printf (" 0x%" PRIx64 "\n", base);
+	      if (! print_unresolved_addresses)
+		{
+		  a1 = format_dwarf_addr (dwflmod, address_size, base, base);
+		  printf ("      %s\n", a1);
+		  free (a1);
+		}
+	      break;
+
+	    case DW_RLE_start_end:
+	      if (address_size == 4)
+		{
+		  if ((uint64_t) (nexthdr - readp) < 8)
+		    goto invalid_range;
+		  op1 = read_4ubyte_unaligned_inc (dbg, readp);
+		  op2 = read_4ubyte_unaligned_inc (dbg, readp);
+		}
+	      else
+		{
+		  if ((uint64_t) (nexthdr - readp) < 16)
+		    goto invalid_range;
+		  op1 = read_8ubyte_unaligned_inc (dbg, readp);
+		  op2 = read_8ubyte_unaligned_inc (dbg, readp);
+		}
+	      printf (" 0x%" PRIx64 "..0x%" PRIx64 "\n", op1, op2);
+	      if (! print_unresolved_addresses)
+		{
+		  a1 = format_dwarf_addr (dwflmod, address_size, op1, op1);
+		  a2 = format_dwarf_addr (dwflmod, address_size,
+					  op2 - 1, op2);
+		  printf ("      %s..\n", a1);
+		  printf ("      %s\n", a2);
+		  free (a1);
+		  free (a2);
+		}
+	      break;
+
+	    case DW_RLE_start_length:
+	      if (address_size == 4)
+		{
+		  if ((uint64_t) (nexthdr - readp) < 4)
+		    goto invalid_range;
+		  op1 = read_4ubyte_unaligned_inc (dbg, readp);
+		}
+	      else
+		{
+		  if ((uint64_t) (nexthdr - readp) < 8)
+		    goto invalid_range;
+		  op1 = read_8ubyte_unaligned_inc (dbg, readp);
+		}
+	      if ((uint64_t) (nexthdr - readp) < 1)
+		goto invalid_range;
+	      get_uleb128 (op2, readp, nexthdr);
+	      printf (" 0x%" PRIx64 ", %" PRIx64 "\n", op1, op2);
+	      if (! print_unresolved_addresses)
+		{
+		  a1 = format_dwarf_addr (dwflmod, address_size, op1, op1);
+		  op2 = op1 + op2;
+		  a2 = format_dwarf_addr (dwflmod, address_size,
+					  op2 - 1, op2);
+		  printf ("      %s..\n", a1);
+		  printf ("      %s\n", a2);
+		  free (a1);
+		  free (a2);
+		}
+	      break;
+
+	    default:
+	      goto invalid_range;
+	    }
+	}
+
+    next_table:
+      if (readp != nexthdr)
+	{
+          size_t padding = nexthdr - readp;
+          printf (gettext ("   %zu padding bytes\n\n"), padding);
+	  readp = nexthdr;
+	}
+    }
+}
 
 /* Print content of DWARF .debug_ranges section.  */
 static void
@@ -6317,11 +6759,51 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
 
 	case DW_AT_ranges:
 	  {
-	    bool nlpt = notice_listptr (section_ranges, &known_rangelistptr,
+	    bool nlpt;
+	    if (cbargs->cu->version < 5)
+	      nlpt = notice_listptr (section_ranges, &known_rangelistptr,
+				     cbargs->addrsize, cbargs->offset_size,
+				     cbargs->cu, num, attr);
+	    else
+	      {
+		/* Only register for a real section offset.  Otherwise
+		   it is a DW_FORM_rangelistx which is just an index
+		   number and we should already have registered the
+		   section offset for the index when we saw the
+		   DW_AT_rnglists_base CU attribute.  */
+		if (form == DW_FORM_sec_offset)
+		  nlpt = notice_listptr (section_ranges, &known_rnglistptr,
+					 cbargs->addrsize, cbargs->offset_size,
+					 cbargs->cu, num, attr);
+		else
+		  nlpt = true;
+	      }
+
+	    if (!cbargs->silent)
+	      {
+		if (cbargs->cu->version < 5 || form == DW_FORM_sec_offset)
+		  printf ("           %*s%-20s (%s) range list [%6"
+			  PRIxMAX "]%s\n",
+			  (int) (level * 2), "", dwarf_attr_name (attr),
+			  dwarf_form_name (form), (uintmax_t) num,
+			  nlpt ? "" : " <WARNING offset too big>");
+		else
+		  printf ("           %*s%-20s (%s) range index [%6"
+			  PRIxMAX "]\n",
+			  (int) (level * 2), "", dwarf_attr_name (attr),
+			  dwarf_form_name (form), (uintmax_t) num);
+	      }
+	  }
+	  return DWARF_CB_OK;
+
+	case DW_AT_rnglists_base:
+	  {
+	    bool nlpt = notice_listptr (section_ranges, &known_rnglistptr,
 					cbargs->addrsize, cbargs->offset_size,
 					cbargs->cu, num, attr);
 	    if (!cbargs->silent)
-	      printf ("           %*s%-20s (%s) range list [%6" PRIxMAX "]%s\n",
+	      printf ("           %*s%-20s (%s) range list [%6"
+		      PRIxMAX "]%s\n",
 		      (int) (level * 2), "", dwarf_attr_name (attr),
 		      dwarf_form_name (form), (uintmax_t) num,
 		      nlpt ? "" : " <WARNING offset too big>");
@@ -6771,13 +7253,21 @@ print_debug_units (Dwfl_Module *dwflmod,
     }
   while (level >= 0);
 
-  /* We might want to show the split compile unit if this was a skeleton.  */
-  if (!silent && show_split_units && unit_type == DW_UT_skeleton)
+  /* We might want to show the split compile unit if this was a skeleton.
+     We need to scan it if we are requesting printing .debug_ranges for
+     DWARF4 since GNU DebugFission uses "offsets" into the main ranges
+     section.  */
+  if (unit_type == DW_UT_skeleton
+      && ((!silent && show_split_units)
+	  || (version < 5 && (print_debug_sections & section_ranges) != 0)))
     {
       Dwarf_Die subdie;
       if (dwarf_cu_info (cu, NULL, NULL, NULL, &subdie, NULL, NULL, NULL) != 0
 	  || dwarf_tag (&subdie) == DW_TAG_invalid)
-	error (0, 0, gettext ("Could not find split compile unit"));
+	{
+	  if (!silent)
+	    error (0, 0, gettext ("Could not find split compile unit"));
+	}
       else
 	{
 	  Dwarf_CU *split_cu = subdie.cu;
@@ -6785,16 +7275,21 @@ print_debug_units (Dwfl_Module *dwflmod,
 			&addrsize, &offsize, &unit_id, &subdie_off);
 	  Dwarf_Off offset = cu->start;
 
-	  printf (gettext (" Split compilation unit at offset %" PRIu64 ":\n"
-			   " Version: %" PRIu16
-			   ", Abbreviation section offset: %" PRIu64
-			   ", Address size: %" PRIu8
-			   ", Offset size: %" PRIu8 "\n"),
-		  (uint64_t) offset, version, abbroffset, addrsize, offsize);
-	  printf (gettext (" Unit type: %s (%" PRIu8 ")"),
-		  dwarf_unit_name (unit_type), unit_type);
-	  printf (", Unit id: 0x%.16" PRIx64 "", unit_id);
-	  printf ("\n");
+	  if (!silent)
+	    {
+	      printf (gettext (" Split compilation unit at offset %"
+			       PRIu64 ":\n"
+			       " Version: %" PRIu16
+			       ", Abbreviation section offset: %" PRIu64
+			       ", Address size: %" PRIu8
+			       ", Offset size: %" PRIu8 "\n"),
+		      (uint64_t) offset, version, abbroffset,
+		      addrsize, offsize);
+	      printf (gettext (" Unit type: %s (%" PRIu8 ")"),
+		      dwarf_unit_name (unit_type), unit_type);
+	      printf (", Unit id: 0x%.16" PRIx64 "", unit_id);
+	      printf ("\n");
+	    }
 
 	  unit_type = DW_UT_split_compile;
 	  is_split = true;
@@ -9248,6 +9743,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
 	      NEW_SECTION (macinfo),
 	      NEW_SECTION (macro),
 	      NEW_SECTION (ranges),
+	      /* rnglists is ranges for DWARF5.  */
+	      { ".debug_rnglists", section_ranges,
+		print_debug_rnglists_section },
 	      { ".eh_frame", section_frame | section_exception,
 		print_debug_frame_section },
 	      { ".eh_frame_hdr", section_frame | section_exception,
@@ -9291,6 +9789,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
 
   reset_listptr (&known_loclistptr);
   reset_listptr (&known_rangelistptr);
+  reset_listptr (&known_rnglistptr);
 }
 
 
diff --git a/tests/ChangeLog b/tests/ChangeLog
index e6f9959..7a4ccf0 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2018-04-11  Mark Wielaard  <mark@klomp.org>
+
+	* run-readelf-ranges.sh: New test.
+	* Makefile.am (TESTS): Add run-readelf-ranges.sh.
+	(EXTRA_DIST): Likewise.
+
 2018-05-21  Mark Wielaard  <mark@klomp.org>
 
 	* addrx_constx-4.dwo.bz2: New testfile.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4b13be2..54a3d1d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -97,7 +97,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-varlocs-self.sh run-exprlocs-self.sh \
 	run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \
 	run-readelf-test4.sh run-readelf-twofiles.sh \
-	run-readelf-macro.sh run-readelf-loc.sh \
+	run-readelf-macro.sh run-readelf-loc.sh run-readelf-ranges.sh \
 	run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \
 	run-native-test.sh run-bug1-test.sh \
 	run-debuglink.sh run-debugaltlink.sh run-buildid.sh \
@@ -234,6 +234,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     testfile-macros-0xff.bz2 \
 	     run-readelf-macro.sh testfilemacro.bz2 \
 	     run-readelf-loc.sh testfileloc.bz2 \
+	     run-readelf-ranges.sh \
 	     run-readelf-aranges.sh run-readelf-line.sh testfilefoobarbaz.bz2 \
 	     testfile-ppc64-min-instr.bz2 \
 	     testfile-dwarf-45.source \
diff --git a/tests/run-readelf-ranges.sh b/tests/run-readelf-ranges.sh
new file mode 100755
index 0000000..160ef58
--- /dev/null
+++ b/tests/run-readelf-ranges.sh
@@ -0,0 +1,236 @@
+#! /bin/sh
+# Copyright (C) 2018 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# See run-readelf-loc.sh
+
+testfiles testfileloc
+
+# Process values as offsets from base addresses and resolve to symbols.
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfileloc<<\EOF
+
+DWARF section [34] '.debug_ranges' at offset 0xd94:
+
+ CU [     b] base: 0x0000000000400480 <main>
+ [     0] range 0, 2
+          0x0000000000400480 <main>..
+          0x0000000000400481 <main+0x1>
+          range 5, d
+          0x0000000000400485 <main+0x5>..
+          0x000000000040048c <main+0xc>
+
+ CU [    e0] base: 0x00000000004004a0 <say>
+ [    30] range d, f
+          0x00000000004004ad <say+0xd>..
+          0x00000000004004ae <say+0xe>
+          range 12, 1a
+          0x00000000004004b2 <say+0x12>..
+          0x00000000004004b9 <say+0x19>
+EOF
+
+# Don't resolve addresses to symbols.
+testrun_compare ${abs_top_builddir}/src/readelf -N --debug-dump=ranges testfileloc<<\EOF
+
+DWARF section [34] '.debug_ranges' at offset 0xd94:
+
+ CU [     b] base: 0x0000000000400480
+ [     0] range 0, 2
+          0x0000000000400480..
+          0x0000000000400481
+          range 5, d
+          0x0000000000400485..
+          0x000000000040048c
+
+ CU [    e0] base: 0x00000000004004a0
+ [    30] range d, f
+          0x00000000004004ad..
+          0x00000000004004ae
+          range 12, 1a
+          0x00000000004004b2..
+          0x00000000004004b9
+EOF
+
+# Produce "raw" unprocessed content.
+testrun_compare ${abs_top_builddir}/src/readelf -U --debug-dump=ranges testfileloc<<\EOF
+
+DWARF section [34] '.debug_ranges' at offset 0xd94:
+
+ CU [     b] base: 0x0000000000400480
+ [     0] range 0, 2
+          range 5, d
+
+ CU [    e0] base: 0x00000000004004a0
+ [    30] range d, f
+          range 12, 1a
+EOF
+
+# .debug_rnglists (DWARF5), see tests/testfile-dwarf-45.source
+testfiles testfile-dwarf-5
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-dwarf-5<<\EOF
+
+DWARF section [33] '.debug_rnglists' at offset 0x1d9a:
+Table at Offset 0x0:
+
+ Length:               45
+ DWARF version:         5
+ Address size:          8
+ Segment size:          0
+ Offset entries:        0
+ CU [   218] base: 000000000000000000
+
+  Offset: c, Index: 0
+    base_address 0x400583
+      0x0000000000400583 <calc+0x13>
+    offset_pair 0, 2
+      0x0000000000400583 <calc+0x13>..
+      0x0000000000400584 <calc+0x14>
+    offset_pair 5, 15
+      0x0000000000400588 <calc+0x18>..
+      0x0000000000400597 <calc+0x27>
+    end_of_list
+
+  Offset: 1c, Index: 10
+    start_length 0x400570, 2b
+      0x0000000000400570 <calc>..
+      0x000000000040059a <calc+0x2a>
+    start_length 0x400410, 20
+      0x0000000000400410 <main>..
+      0x000000000040042f <main+0x1f>
+    end_of_list
+
+EOF
+
+# Same as above, but for DWARF4, note no header, and base address is not
+# given, but ranges are the same.
+testfiles testfile-dwarf-4
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-dwarf-4<<\EOF
+
+DWARF section [32] '.debug_ranges' at offset 0x1f96:
+
+ CU [   21c] base: 000000000000000000
+ [     0] range 400583, 400585
+          0x0000000000400583 <calc+0x13>..
+          0x0000000000400584 <calc+0x14>
+          range 400588, 400598
+          0x0000000000400588 <calc+0x18>..
+          0x0000000000400597 <calc+0x27>
+ [    30] range 400570, 40059b
+          0x0000000000400570 <calc>..
+          0x000000000040059a <calc+0x2a>
+          range 400410, 400430
+          0x0000000000400410 <main>..
+          0x000000000040042f <main+0x1f>
+EOF
+
+# Now with split dwarf. See tests/testfile-dwarf-45.source.
+# Note that this will have an offsets table that the .dwo can refer to.
+testfiles testfile-splitdwarf-5 testfile-hello5.dwo testfile-world5.dwo
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-splitdwarf-5<<\EOF
+
+DWARF section [35] '.debug_rnglists' at offset 0x393a:
+Table at Offset 0x0:
+
+ Length:               53
+ DWARF version:         5
+ Address size:          8
+ Segment size:          0
+ Offset entries:        2
+ CU [    49] base: 000000000000000000
+
+  Offsets starting at 0xc:
+   [     0] 0x8
+   [     1] 0x18
+
+  Offset: 14, Index: 8
+    base_address 0x4011d3
+      0x00000000004011d3 <calc+0x13>
+    offset_pair 0, 2
+      0x00000000004011d3 <calc+0x13>..
+      0x00000000004011d4 <calc+0x14>
+    offset_pair 5, 15
+      0x00000000004011d8 <calc+0x18>..
+      0x00000000004011e7 <calc+0x27>
+    end_of_list
+
+  Offset: 24, Index: 18
+    start_length 0x4011c0, 2b
+      0x00000000004011c0 <calc>..
+      0x00000000004011ea <calc+0x2a>
+    start_length 0x401060, 20
+      0x0000000000401060 <main>..
+      0x000000000040107f <main+0x1f>
+    end_of_list
+
+EOF
+
+# Note that the rnglist_base attribute of the second CU points to the offsets
+# above 0xc [c].
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info testfile-splitdwarf-5<<\EOF
+
+DWARF section [28] '.debug_info' at offset 0x3102:
+ [Offset]
+ Compilation unit at offset 0:
+ Version: 5, Abbreviation section offset: 0, Address size: 8, Offset size: 4
+ Unit type: skeleton (4), Unit id: 0xc422aa5c31fec205
+ [    14]  skeleton_unit        abbrev: 1
+           low_pc               (addr) 0x0000000000401160 <foo>
+           high_pc              (data8) 81 (0x00000000004011b1)
+           stmt_list            (sec_offset) 0
+           dwo_name             (strp) "testfile-hello5.dwo"
+           comp_dir             (strp) "/home/mark/src/elfutils/tests"
+           GNU_pubnames         (flag_present) yes
+           addr_base            (sec_offset) 8
+ Compilation unit at offset 53:
+ Version: 5, Abbreviation section offset: 21, Address size: 8, Offset size: 4
+ Unit type: skeleton (4), Unit id: 0xb6c8b9d97e6dfdfe
+ [    49]  skeleton_unit        abbrev: 1
+           ranges               (sec_offset) range list [    24]
+           low_pc               (addr) 000000000000000000
+           stmt_list            (sec_offset) 655
+           dwo_name             (strp) "testfile-world5.dwo"
+           comp_dir             (strp) "/home/mark/src/elfutils/tests"
+           GNU_pubnames         (flag_present) yes
+           addr_base            (sec_offset) 168
+           rnglists_base        (sec_offset) range list [     c]
+EOF
+
+# Same for DWARF4 GNU DebugFission. But now we need to scan the .dwo
+# explicitly to know it will use the first ranges.
+testfiles testfile-splitdwarf-4 testfile-hello4.dwo testfile-world4.dwo
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=ranges testfile-splitdwarf-4<<\EOF
+
+DWARF section [32] '.debug_ranges' at offset 0x3611:
+
+ CU [     b] base: 000000000000000000
+ [     0] range 4011d3, 4011d5
+          0x00000000004011d3 <calc+0x13>..
+          0x00000000004011d4 <calc+0x14>
+          range 4011d8, 4011e8
+          0x00000000004011d8 <calc+0x18>..
+          0x00000000004011e7 <calc+0x27>
+
+ CU [    3f] base: 000000000000000000
+ [    30] range 4011c0, 4011eb
+          0x00000000004011c0 <calc>..
+          0x00000000004011ea <calc+0x2a>
+          range 401060, 401080
+          0x0000000000401060 <main>..
+          0x000000000040107f <main+0x1f>
+EOF
+
+exit 0
-- 
1.8.3.1



More information about the Elfutils-devel mailing list