From 0ffceff80252797a8d7619b19a3b1418954dac4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9Cbhkumarn=E2=80=9D?= <Bhuvanendra.KumarN@amd.com>
Date: Tue, 28 Jun 2022 00:50:14 +0530
Subject: [PATCH] [PATCH] Fix range lists issues (dwarf-5)

For GCC and/or clang compiled objects with dwarf-5, following issues
related to range lists dump are fixed.
1. range lists offset address dump under DW_AT_ranges is corrected,
where DW_FORM_rnglistx is used. While dumping the range lists offset,
the address dumped is wrong where it was referring to .debug_addr
instead of .debug_rnglists
2. .debug_rnglists section dump for multiple CU is fixed.
For each CU, range lists header followed by range lists is dumped now.

	* dwarf.c (fetch_indexed_value): Use base_address to access the
	section offset for rnglists also.
	(read_and_display_attr_value): Fix support for DW_FORM_rnglistx form.
	Read attribute DW_AT_rnglists_base which can come before or after
	DW_AT_ranges attribute.
	Update range_lists array for forms DW_FORM_rnglistx(clang) or
	DW_FORM_sec_offset(GCC).
	(display_debug_rnglists_list): Add new parameter called start_header
	and pass on the starting address of the next CU's range lists header.
	(display_debug_rnglists_header): New function to dump range lists
	header for each CU.
	(display_debug_ranges): Dump the range lists header before printing
	the range list entries for each CU.
	Capture the starting address of the next CU's range lists header.
---
 binutils/dwarf.c | 132 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 124 insertions(+), 8 deletions(-)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index bcabb61b871..2dab3ee3d96 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -811,7 +811,7 @@ fetch_indexed_value (dwarf_vma idx,
 
   /* Offsets are biased by the size of the section header
      or base address.  */
-  if (sec_enum == loclists)
+  if ((sec_enum == loclists) || (sec_enum == rnglists))
     offset += base_address;
   else
     offset += bias;
@@ -877,6 +877,7 @@ typedef struct abbrev_map
 static abbrev_map *   cu_abbrev_map = NULL;
 static unsigned long  num_abbrev_map_entries = 0;
 static unsigned long  next_free_abbrev_map_entry = 0;
+static abbrev_attr *ranges_attr = NULL;
 
 #define INITIAL_NUM_ABBREV_MAP_ENTRIES 8
 #define ABBREV_MAP_ENTRIES_INCREMENT   8
@@ -2407,6 +2408,7 @@ read_and_display_attr_value (unsigned long           attribute,
   dwarf_vma uvalue_hi = 0;
   unsigned char *block_start = NULL;
   unsigned char *orig_data = data;
+  dwarf_vma uvalue_rnglists_base;
 
   if (data > end || (data == end && form != DW_FORM_flag_present))
     {
@@ -2762,6 +2764,10 @@ read_and_display_attr_value (unsigned long           attribute,
 	      printf (_("%c(index: 0x%s): %s"), delimiter,
 	              dwarf_vmatoa ("x", uvalue),
 	              dwarf_vmatoa ("x", debug_info_p->loc_offsets [uvalue]));
+	    else if (form == DW_FORM_rnglistx)
+	      printf (_("%c(index: 0x%s): %s"), delimiter,
+	              dwarf_vmatoa ("x", uvalue),
+	              dwarf_vmatoa ("x", debug_info_p->range_lists [uvalue]));
 	    else
 	      printf (_("%c(index: 0x%s): %s"), delimiter,
 	              dwarf_vmatoa ("x", uvalue),
@@ -2771,6 +2777,10 @@ read_and_display_attr_value (unsigned long           attribute,
 	      printf (_("%c(addr_index: 0x%s): %s"), delimiter,
 	              dwarf_vmatoa ("x", uvalue),
 	              dwarf_vmatoa ("x", debug_info_p->loc_offsets [uvalue]));
+	    else if (form == DW_FORM_rnglistx)
+	      printf (_("%c(addr_index: 0x%s): %s"), delimiter,
+	              dwarf_vmatoa ("x", uvalue),
+	              dwarf_vmatoa ("x", debug_info_p->range_lists [uvalue]));
 	    else
 	      printf (_("%c(addr_index: 0x%s): %s"), delimiter,
 	              dwarf_vmatoa ("x", uvalue),
@@ -2909,6 +2919,24 @@ read_and_display_attr_value (unsigned long           attribute,
 	  break;
 
 	case DW_AT_ranges:
+	  uvalue_rnglists_base = 0;
+	  /* DW_AT_rnglists_base can appear before or after DW_AT_ranges attribute.
+	     If its found before, DW_AT_rnglists_base is used directly,
+	     otherwise its looked ahead from DW_AT_ranges and read.  */
+	  if (!debug_info_p->rnglists_base) {
+	    if (ranges_attr && ranges_attr->next && ranges_attr->next->next &&
+	        (ranges_attr->next->next->attribute == DW_AT_rnglists_base)) {
+	      unsigned char *data_dupl = data;
+	      dwarf_vma uvalue_skip;
+	      data_dupl = skip_attr_bytes (ranges_attr->next->form, data_dupl, end,
+	                                   pointer_size, offset_size,
+	                                   dwarf_version, &uvalue_skip);
+	      SAFE_BYTE_GET_AND_INC (uvalue_rnglists_base, data_dupl, offset_size, end);
+	    }
+	  }
+	  else
+	    uvalue_rnglists_base = debug_info_p->rnglists_base;
+
 	  if ((dwarf_version < 4
 	       && (form == DW_FORM_data4 || form == DW_FORM_data8))
 	      || form == DW_FORM_sec_offset
@@ -2917,6 +2945,7 @@ read_and_display_attr_value (unsigned long           attribute,
 	      /* Process range list.  */
 	      unsigned int lmax = debug_info_p->max_range_lists;
 	      unsigned int num = debug_info_p->num_range_lists;
+	      dwarf_vma uvalue_index;
 
 	      if (lmax == 0 || num >= lmax)
 		{
@@ -2927,10 +2956,15 @@ read_and_display_attr_value (unsigned long           attribute,
 		  debug_info_p->max_range_lists = lmax;
 		}
 
+	      uvalue_index = uvalue;
 	      if (form == DW_FORM_rnglistx)
-		uvalue = fetch_indexed_value (uvalue, rnglists, 0);
+		uvalue = fetch_indexed_value (uvalue, rnglists, uvalue_rnglists_base);
 
-	      debug_info_p->range_lists [num] = uvalue;
+	      uvalue += uvalue_rnglists_base;
+	      if (form == DW_FORM_rnglistx)
+	        debug_info_p->range_lists [uvalue_index] = uvalue;
+	      else
+	        debug_info_p->range_lists [num] = uvalue;
 	      debug_info_p->num_range_lists++;
 	    }
 	  break;
@@ -4043,6 +4077,8 @@ process_debug_info (struct dwarf_section * section,
 	      if (! do_loc && do_printing)
 		/* Show the offset from where the tag was extracted.  */
 		printf ("    <%lx>", (unsigned long)(tags - section_begin));
+	      if (attr->attribute == DW_AT_ranges)
+	        ranges_attr = attr;
 	      tags = read_and_display_attr (attr->attribute,
 					    attr->form,
 					    attr->implicit_const,
@@ -7947,7 +7983,8 @@ display_debug_rnglists_list (unsigned char * start,
 			     unsigned int    pointer_size,
 			     dwarf_vma       offset,
 			     dwarf_vma       base_address,
-			     unsigned int    offset_size)
+			     unsigned int    offset_size,
+			     unsigned char ** start_header)
 {
   unsigned char *next = start;
   unsigned int debug_addr_section_hdr_len;
@@ -8029,8 +8066,11 @@ display_debug_rnglists_list (unsigned char * start,
 	  break;
 	}
 
-      if (rlet == DW_RLE_end_of_list)
+      if (rlet == DW_RLE_end_of_list) {
+	/* Pass on the starting address of the next CU's range lists header.  */
+	*start_header = start;
 	break;
+      }
       if (rlet == DW_RLE_base_address || rlet == DW_RLE_base_addressx)
 	continue;
 
@@ -8053,6 +8093,69 @@ display_debug_rnglists_list (unsigned char * start,
     }
 }
 
+static void display_debug_rnglists_header (unsigned char * start,
+                                           unsigned char * finish)
+{
+      dwarf_vma initial_length;
+      unsigned char segment_selector_size;
+      unsigned int offset_size, offset_entry_count, i;
+      unsigned short version;
+      unsigned char  address_size = 0;
+
+      /* Get and check the length of the block.  */
+      SAFE_BYTE_GET_AND_INC (initial_length, start, 4, finish);
+
+      if (initial_length == 0xffffffff)
+        {
+          /* This section is 64-bit DWARF 3.  */
+          SAFE_BYTE_GET_AND_INC (initial_length, start, 8, finish);
+          offset_size = 8;
+        }
+      else
+        offset_size = 4;
+
+      /* Get the other fields in the header.  */
+      SAFE_BYTE_GET_AND_INC (version, start, 2, finish);
+      SAFE_BYTE_GET_AND_INC (address_size, start, 1, finish);
+      SAFE_BYTE_GET_AND_INC (segment_selector_size, start, 1, finish);
+      SAFE_BYTE_GET_AND_INC (offset_entry_count, start, 4, finish);
+
+      putchar ('\n');
+      printf (_("  Length:          0x%s\n"), dwarf_vmatoa ("x", initial_length));
+      printf (_("  DWARF version:   %u\n"), version);
+      printf (_("  Address size:    %u\n"), address_size);
+      printf (_("  Segment size:    %u\n"), segment_selector_size);
+      printf (_("  Offset entries:  %u\n"), offset_entry_count);
+
+      if (offset_entry_count != 0)
+        {
+          printf (_("\n   Offsets starting at 0x%lx:\n"),
+                  (long)(start - debug_displays [rnglists].section.start));
+          if (offset_size == 8)
+            {
+              for (i = 0; i < offset_entry_count; i++)
+                {
+                  dwarf_vma entry;
+
+                  SAFE_BYTE_GET_AND_INC (entry, start, 8, finish);
+                  printf (_("    [%6u] 0x%s\n"), i, dwarf_vmatoa ("x", entry));
+                }
+            }
+          else
+            {
+              for (i = 0; i < offset_entry_count; i++)
+                {
+                  uint32_t entry;
+
+                  SAFE_BYTE_GET_AND_INC (entry, start, 4, finish);
+                  printf (_("    [%6u] 0x%x\n"), i, entry);
+                }
+            }
+        }
+      putchar ('\n');
+      printf (_("    Offset   Begin    End\n"));
+}
+
 static int
 display_debug_ranges (struct dwarf_section *section,
 		      void *file ATTRIBUTE_UNUSED)
@@ -8070,6 +8173,7 @@ display_debug_ranges (struct dwarf_section *section,
   unsigned char         address_size = 0;
   dwarf_vma             last_offset = 0;
   unsigned int          offset_size = 0;
+  unsigned char *       start_header = NULL;
 
   if (bytes == 0)
     {
@@ -8230,7 +8334,8 @@ display_debug_ranges (struct dwarf_section *section,
   putchar ('\n');
   printf (_("    Offset   Begin    End\n"));
 
-  for (i = 0; i < num_range_list; i++)
+  unsigned int range_lists_count = 0;
+  for (i = 0; i < num_range_list; i++, range_lists_count++)
     {
       struct range_entry *range_entry = &range_entries[i];
       debug_info *debug_info_p = range_entry->debug_info_p;
@@ -8243,6 +8348,15 @@ display_debug_ranges (struct dwarf_section *section,
       offset = range_entry->ranges_offset;
       base_address = debug_info_p->base_address;
 
+      /* Print the range lists header before printing the range list entries for
+         each CU.  */
+      if ((i > 0) &&
+          (start_header != NULL) &&
+          (range_lists_count == range_entries[i-1].debug_info_p->num_range_lists)) {
+        range_lists_count = 0;
+        display_debug_rnglists_header (start_header, finish);
+      }
+
       /* PR 17512: file: 001-101485-0.001:0.1.  */
       if (pointer_size < 2 || pointer_size > 8)
 	{
@@ -8258,7 +8372,7 @@ display_debug_ranges (struct dwarf_section *section,
 	  continue;
 	}
 
-      next = section_begin + offset + debug_info_p->rnglists_base;
+      next = section_begin + offset;
 
       /* If multiple DWARF entities reference the same range then we will
          have multiple entries in the `range_entries' list for the same
@@ -8289,8 +8403,10 @@ display_debug_ranges (struct dwarf_section *section,
       last_start = next;
 
       if (is_rnglists)
+        /* Print the current CU's range lists, also capture the starting address
+           of the next CU's range lists header.  */
 	display_debug_rnglists_list
-	  (start, finish, pointer_size, offset, base_address, offset_size);
+	  (start, finish, pointer_size, offset, base_address, offset_size, &start_header);
       else
 	display_debug_ranges_list
 	  (start, finish, pointer_size, offset, base_address);
-- 
2.17.1

