readelf: Add .debug_rnglists support.
authorMark Wielaard <mark@klomp.org>
Fri, 24 Nov 2017 10:16:06 +0000 (11:16 +0100)
committerMark Wielaard <mark@klomp.org>
Sun, 27 May 2018 12:28:07 +0000 (14:28 +0200)
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>
12 files changed:
libdw/ChangeLog
libdw/dwarf.h
libdw/dwarf_begin_elf.c
libdw/dwarf_error.c
libdw/dwarf_formudata.c
libdw/dwarf_getscopes.c
libdw/libdwP.h
src/ChangeLog
src/readelf.c
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-ranges.sh [new file with mode: 0755]

index fd59a24..b2fc3d9 100644 (file)
@@ -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-25  Mark Wielaard  <mark@klomp.org>
 
        * dwarf_getlocation_attr.c (__libdw_cu_addr_base): Cast offset to
index c438399..9c2495e 100644 (file)
@@ -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
   {
index 5d8e79e..2e8c5f3 100644 (file)
@@ -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]))
index 63c8bbe..2e8cd77 100644 (file)
@@ -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"),
index d56e7dc..280fef2 100644 (file)
@@ -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);
index df480d3..5662eec 100644 (file)
@@ -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;
index 82ee5d0..3cfcc55 100644 (file)
@@ -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,
index 1a9f4a3..d28d89a 100644 (file)
@@ -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.
index 1858802..ce0ecc2 100644 (file)
@@ -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,393 @@ 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,
+                                             addr1, addr1);
+                     a2 = format_dwarf_addr (dwflmod, address_size,
+                                             addr2 - 1, addr2);
+                     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;
+                 if (get_indexed_addr (cu, op1, &addr1) != 0)
+                   {
+                     printf ("      ???..\n");
+                     printf ("      ???\n");
+                   }
+                 else
+                   {
+                     addr2 = addr1 + op2;
+                     a1 = format_dwarf_addr (dwflmod, address_size,
+                                             addr1, addr1);
+                     a2 = format_dwarf_addr (dwflmod, address_size,
+                                             addr2 - 1, addr2);
+                     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 +6756,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 +7250,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 +7272,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 +9740,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 +9786,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
 
   reset_listptr (&known_loclistptr);
   reset_listptr (&known_rangelistptr);
+  reset_listptr (&known_rnglistptr);
 }
 
 
index 2f92cc2..13d168d 100644 (file)
@@ -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.
index 4b13be2..54a3d1d 100644 (file)
@@ -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 (executable)
index 0000000..160ef58
--- /dev/null
@@ -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