public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Fix issues with reading rnglists, especially from dwo files,  for DWARF v5
@ 2020-06-01 17:16 Caroline Tice
  2020-06-01 20:33 ` Tom Tromey
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-06-01 17:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Caroline Tice, Eric Christopher, Stan Shebs

[-- Attachment #1: Type: text/plain, Size: 1842 bytes --]

While experimenting with GDB on DWARF 5 with split debug (dwo files),
I discovered that GDB was not reading the rnglist index
properly (it needed to be reprocessed in the same way the loclist
index does), and that there was no code for reading rnglists out of
dwo files at all.  Also, the rnglist address reading function
(dwarf2_rnglists_process) was adding the base address to all rnglist
entries, when it's only supposed to add it to the DW_RLE_offset_pair
entries (http://dwarfstd.org/doc/DWARF5.pdf, p. 53), and was not
handling several entry types.

This patch fixes these issues.  I verified that it fixes the issues I
saw and that it does not cause any testsuite regressions (on x86_64
ubuntu linux).  Is there anything else I need to do? Is this Ok to
commit?

-- Caroline Tice
cmtice@google.com

gdb/ChangeList:

2020-06-01  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwo_sectionds): Add rnglists field.
        (struct virtual_v2_dwo_sections): Add loclists_offset, loclists_size,
        rnglists_offset, rnglists_size.
        (cu_debug_rnglist_section): New function (decl & definition).
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges).
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

[-- Attachment #2: gdb-dwo-rnglists.patch --]
[-- Type: application/octet-stream, Size: 9666 bytes --]

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e6566f9649..bcc5d3b90d 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -650,6 +652,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -793,12 +796,18 @@ struct virtual_v2_dwo_sections
   bfd_size_type loc_offset;
   bfd_size_type loc_size;
 
+  bfd_size_type loclists_offset;
+  bfd_size_type loclists_size;
+
   bfd_size_type macinfo_offset;
   bfd_size_type macinfo_size;
 
   bfd_size_type macro_offset;
   bfd_size_type macro_size;
 
+  bfd_size_type rnglists_offset;
+  bfd_size_type rnglists_size;
+
   bfd_size_type str_offsets_offset;
   bfd_size_type str_offsets_size;
 
@@ -1384,6 +1393,10 @@ static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglist_section (struct
+							     dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12397,6 +12410,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13751,6 +13769,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
 
   base = cu->base_address;
 
@@ -13792,6 +13811,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13810,6 +13834,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+        case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13838,7 +13875,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
-	default:
+        case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
+ 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
 	}
@@ -13866,8 +13916,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -18115,6 +18169,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr =  die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -19043,6 +19102,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+
+  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
+  if (section == nullptr)
+    error(_("Cannot find .debug_rnglists section [in module %s]"),
+	  objfile_name(objfile));
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error(_("DW_FORM_rnglistx used without .debug_rnglists section "
+	    "[in module %s]"),
+       objfile_name (objfile));
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error(_("DW_FORM_rnglistx index pointing outside of "
+	    ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+  if (rnglist_base + rnglist_index * cu->header.offset_size
+        >= section->size)
+    error(_("DW_FORM_rnglistx pointing outside of "
+            ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+  const gdb_byte *info_ptr
+    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19061,6 +19163,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19242,8 +19347,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23332,6 +23439,24 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglist_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files,  for DWARF v5
  2020-06-01 17:16 [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5 Caroline Tice
@ 2020-06-01 20:33 ` Tom Tromey
  2020-06-02 17:04   ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Tromey @ 2020-06-01 20:33 UTC (permalink / raw)
  To: Caroline Tice via Gdb-patches; +Cc: Caroline Tice, Eric Christopher

>>>>> "Caroline" == Caroline Tice via Gdb-patches <gdb-patches@sourceware.org> writes:

Caroline> This patch fixes these issues.  I verified that it fixes the
Caroline> issues I saw and that it does not cause any testsuite
Caroline> regressions (on x86_64 ubuntu linux).

Is there a way to reproduce the failures with the current test suite?
Say, by running some test using some board file?

Caroline> @@ -793,12 +796,18 @@ struct virtual_v2_dwo_sections
Caroline>    bfd_size_type loc_offset;
Caroline>    bfd_size_type loc_size;
 
Caroline> +  bfd_size_type loclists_offset;
Caroline> +  bfd_size_type loclists_size;
Caroline> +
Caroline>    bfd_size_type macinfo_offset;
Caroline>    bfd_size_type macinfo_size;
 
Caroline>    bfd_size_type macro_offset;
Caroline>    bfd_size_type macro_size;
 
Caroline> +  bfd_size_type rnglists_offset;
Caroline> +  bfd_size_type rnglists_size;

These new members don't seem to be used anywhere.

Caroline> +static struct dwarf2_section_info *cu_debug_rnglist_section (struct
Caroline> +							     dwarf2_cu *cu);

Probably better to split before the "(" and then indent the continuation
line 2 spaces.

Caroline> -	default:
Caroline> +        case DW_RLE_startx_endx:

Looks like the indentation here is incorrect.

Caroline> +
Caroline> +  attr =  die->attr (DW_AT_rnglists_base);

Extra space after the "=".

Tom

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-01 20:33 ` Tom Tromey
@ 2020-06-02 17:04   ` Caroline Tice
  2020-06-03 14:49     ` Tom Tromey
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-06-02 17:04 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

[-- Attachment #1: Type: text/plain, Size: 3141 bytes --]

On Mon, Jun 1, 2020 at 1:33 PM Tom Tromey <tom@tromey.com> wrote:
>
> >>>>> "Caroline" == Caroline Tice via Gdb-patches <gdb-patches@sourceware.org> writes:
>
> Caroline> This patch fixes these issues.  I verified that it fixes the
> Caroline> issues I saw and that it does not cause any testsuite
> Caroline> regressions (on x86_64 ubuntu linux).
>
> Is there a way to reproduce the failures with the current test suite?
> Say, by running some test using some board file?

I have not been able to reproduce it with the current testsuite.  The
issue is largely related to processing DW_FORM_rmglistx, which I have
not been able to get GCC to produce (it generated a DW_FORM_sec_offset
instead); I get my issue using llvm.  I will attempt to attach a
gzip'ed tarball (if gmail will let me) containing my small test case.
It has 2 .cpp files, 1 .h file, the two clang-generated .dwo files,
and the final binary.  The command I used to compile the sources, and
obtain the .dwo & binary files is:

$ clang++ -gdwarf-5 -O0 -gsplit-dwarf pre-order.cpp
pre-order-common.cpp -o pre-order

To see the first of the issues, you need to enable complaints (change
the value of "stop_whining" in complaints.c to something like 10).
When you do that, rebuild gdb, and load the binary into gdb, it
immediately complains:

$ ~/fsf-gdb.clean.obj/gdb/gdb pre-order
...
Reading symbols from pre-order...
During symbol reading: Invalid .debug_rnglists data (no base address)
During symbol reading: Invalid .debug_rnglists data (no base address)
(gdb) quit

This is because of the incorrect reading of the DW_FORM_rnglistx
index.  GDB then ignores the rnglists altogether because it can't read
them.



>
> Caroline> @@ -793,12 +796,18 @@ struct virtual_v2_dwo_sections
> Caroline>    bfd_size_type loc_offset;
> Caroline>    bfd_size_type loc_size;
>
> Caroline> +  bfd_size_type loclists_offset;
> Caroline> +  bfd_size_type loclists_size;
> Caroline> +
> Caroline>    bfd_size_type macinfo_offset;
> Caroline>    bfd_size_type macinfo_size;
>
> Caroline>    bfd_size_type macro_offset;
> Caroline>    bfd_size_type macro_size;
>
> Caroline> +  bfd_size_type rnglists_offset;
> Caroline> +  bfd_size_type rnglists_size;
>
> These new members don't seem to be used anywhere.

Oh.  Those get used in my next upcoming patch (where I update GDB to
handle DWARFv5 .dwp files).  I can either leave them in this patch, or
remove them from here and put them in the next one, whichever you
prefer.

>
> Caroline> +static struct dwarf2_section_info *cu_debug_rnglist_section (struct
> Caroline> +                                                          dwarf2_cu *cu);
>
> Probably better to split before the "(" and then indent the continuation
> line 2 spaces.
>
> Caroline> -     default:
> Caroline> +        case DW_RLE_startx_endx:
>
> Looks like the indentation here is incorrect.
>
> Caroline> +
> Caroline> +  attr =  die->attr (DW_AT_rnglists_base);
>
> Extra space after the "=".

Thanks for the review!  I will fix the formatting issues, and resubmit
as soon as you indicate whether to leave in or take out the currently
unused fields.

-- Caroline

>
> Tom

[-- Attachment #2: gdb-rnglist-test.tar.gz --]
[-- Type: application/x-gzip, Size: 27275 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-02 17:04   ` Caroline Tice
@ 2020-06-03 14:49     ` Tom Tromey
  2020-06-04 21:39       ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Tom Tromey @ 2020-06-03 14:49 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Tom Tromey, Caroline Tice via Gdb-patches, Eric Christopher

Caroline> $ clang++ -gdwarf-5 -O0 -gsplit-dwarf pre-order.cpp
Caroline> pre-order-common.cpp -o pre-order

I wonder if the test suite can be run this way.

Caroline> Oh.  Those get used in my next upcoming patch (where I update GDB to
Caroline> handle DWARFv5 .dwp files).  I can either leave them in this patch, or
Caroline> remove them from here and put them in the next one, whichever you
Caroline> prefer.

I think it's better for patches to be reasonably self-contained when
possible.

Also, there were other patches for DWARFv5 on the list in "recent" (this
year) times.  I wonder if those are relevant / related.

Tom

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-03 14:49     ` Tom Tromey
@ 2020-06-04 21:39       ` Caroline Tice
  2020-06-09 23:32         ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-06-04 21:39 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

[-- Attachment #1: Type: text/plain, Size: 3080 bytes --]

I've been playing with running the testsuite with -gdwarf-5
-gsplit-dwarf and using the clang compiler. I did not find any tests
that showed the issues I was running into (but I did discover a small
bug in my patch, which my newer patch fixes).  With my fixed patch,
there are no testsuite differences.  I have created a new testsuite
test case, which does fail with current ToT and passes with my patch,
but that only works if you compile it with clang -- if you compile it
with GCC it passes in both cases (because GCC is not generating the
DW_FORM_rmglistx that my patch is handling/fixing).

I've updated the patch to include the requested format changes, remove
the pieces that are only used in an upcoming patch,  fix the small
issue I found while testing, and I've added the test to the testsuite.
To run the test and see the issue:

$  make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"

gdb/ChangeLog (updated)

2020-06-04  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwo_sections): Add rnglists field.
        (cu_debug_rnglist_section): New function (decl & definition).
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges).
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog:

2020-06-04  Caroline Tice  <cmtice@google.com>

        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.





On Wed, Jun 3, 2020 at 7:49 AM Tom Tromey <tom@tromey.com> wrote:
>
> Caroline> $ clang++ -gdwarf-5 -O0 -gsplit-dwarf pre-order.cpp
> Caroline> pre-order-common.cpp -o pre-order
>
> I wonder if the test suite can be run this way.
>
> Caroline> Oh.  Those get used in my next upcoming patch (where I update GDB to
> Caroline> handle DWARFv5 .dwp files).  I can either leave them in this patch, or
> Caroline> remove them from here and put them in the next one, whichever you
> Caroline> prefer.
>
> I think it's better for patches to be reasonably self-contained when
> possible.
>
> Also, there were other patches for DWARFv5 on the list in "recent" (this
> year) times.  I wonder if those are relevant / related.
>
> Tom

[-- Attachment #2: gdb-dwo-rnglists.patch --]
[-- Type: application/octet-stream, Size: 14304 bytes --]

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e6566f9649..0f904e73e0 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -650,6 +652,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1384,6 +1387,10 @@ static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglist_section
+  (struct dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12397,6 +12404,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13751,6 +13763,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
 
   base = cu->base_address;
 
@@ -13792,6 +13805,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13810,6 +13828,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13838,7 +13869,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
-	default:
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
+ 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
 	}
@@ -13866,8 +13910,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -14105,7 +14153,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
 	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14276,7 +14325,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
 	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -18115,6 +18165,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -18633,7 +18688,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
 	       but that requires a full DIE, so instead we just
 	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -19043,6 +19099,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+
+  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
+  if (section == nullptr)
+    error(_("Cannot find .debug_rnglists section [in module %s]"),
+	  objfile_name(objfile));
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error(_("DW_FORM_rnglistx used without .debug_rnglists section "
+	    "[in module %s]"),
+       objfile_name (objfile));
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error(_("DW_FORM_rnglistx index pointing outside of "
+	    ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+  if (rnglist_base + rnglist_index * cu->header.offset_size
+        >= section->size)
+    error(_("DW_FORM_rnglistx pointing outside of "
+            ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+  const gdb_byte *info_ptr
+    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19061,6 +19160,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19242,8 +19344,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23332,6 +23436,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglist_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000..4d650e1279
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,71 @@
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node * make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void inorder(node *root) {
+  std::vector<node *> todo;
+  todo.push_back(root);
+  while (!todo.empty()){
+    node *curr = todo.back();
+    todo.pop_back(); /* break-here */
+    if (curr->visited) {
+      std::cout << curr->id << " ";
+    } else {
+      curr->visited = true;
+      if (curr->right) { todo.push_back(curr->right); }
+      todo.push_back(curr);
+      if (curr->left) { todo.push_back(curr->left); }
+    }
+  }
+}
+
+int main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000..b5c6c3bfe3
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2011-2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-04 21:39       ` Caroline Tice
@ 2020-06-09 23:32         ` Caroline Tice
  2020-06-16 15:37           ` Caroline Tice
  2020-06-18 20:27           ` Tom Tromey
  0 siblings, 2 replies; 29+ messages in thread
From: Caroline Tice @ 2020-06-09 23:32 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

[-- Attachment #1: Type: text/plain, Size: 7542 bytes --]

While playing with this a bit more (getting ready to work on my .dwp
file patch), I noticed that there was an important bit I left out of
the original patch: dwarf2_rnglists_process, in the original code,
always reads out of the rnglist section in the main objfile, even when
there is a .rnglist section in a .dwo file that it should be reading
instead.  I had some local code that fixes this, but it didn't make it
into my previous patch (I apologize for that).  The attached patch has
been updated to contain this fix as well.  It also contains my fixes
and testcase from the previous patch.

I ran the testsuite again (adding the -gdwarf-5 and -gsplit-dwarf
flags in testsuite/lib/gdb.exp), and compiling everything with clang:

$ make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
CXX_FOR_TARGET=${CLANG_CXX}"

This time my patched GDB passed quite a few tests that the unpatched
version failed (when compiled with clang and passed the flags):

Testsuite summary from unpatched GDB:

=== gdb Summary ===
# of expected passes 55725
# of unexpected failures 2956
# of unexpected successes 83
# of expected failures 169
# of known failures 57
# of unresolved testcases 115
# of untested testcases 175
# of unsupported tests 272
# of paths in test names 1
# of duplicate test names 269


Testsuite summary from patched GDB:

=== gdb Summary ===

# of expected passes 56173
# of unexpected failures 2523
# of unexpected successes 2
# of expected failures 250
# of known failures 57
# of unresolved testcases 111
# of untested testcases 174
# of unsupported tests 272
# of paths in test names 1
# of duplicate test names 269

Some of the tests that have more passes with the patch include:
gdb.base/break-entry.exp, gdb.base/info-fun.exp,
gdb.base/info-shared.exp,gdb.base/return-nodebug.exp,
gdb.dwarf2/fission-base.exp
(I can send you a complete list if you really want it)

Anyway below is the updated patch for review.

-- Caroline
cmtice@google.com

gdb/ChangeLog:

2020-06-09  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwarf2_cu): Add cu_ranges_from_skeleton field.
        (struct dwo_sections): Add rnglists field.
        (dwarf2_ranges_read): Add tag parameter.
        (cu_debug_rnglist_section): New function (decl & definition).
        (cutu_reader::cutu_reader): Before replacing the skeleton unit
        comp_unit_die with the dwo comp_unit_die, check to see if the skeleton
        unit die has a DW_AT_ranges, and if so set the cu_ranges_from_skeleton
        field in the cu.
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind of
        die whose range is being checked; add code to read the rnglist section
        from the dwo file rather than from the main objfile, if appropriate.
        Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges).
        (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
        dwarf2_rnglists_process.
        (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
        dwarf2_ranges_process.
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog:

2020-06-09  Caroline Tice  <cmtice@google.com>

        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.


On Thu, Jun 4, 2020 at 2:39 PM Caroline Tice <cmtice@google.com> wrote:
>
> I've been playing with running the testsuite with -gdwarf-5
> -gsplit-dwarf and using the clang compiler. I did not find any tests
> that showed the issues I was running into (but I did discover a small
> bug in my patch, which my newer patch fixes).  With my fixed patch,
> there are no testsuite differences.  I have created a new testsuite
> test case, which does fail with current ToT and passes with my patch,
> but that only works if you compile it with clang -- if you compile it
> with GCC it passes in both cases (because GCC is not generating the
> DW_FORM_rmglistx that my patch is handling/fixing).
>
> I've updated the patch to include the requested format changes, remove
> the pieces that are only used in an upcoming patch,  fix the small
> issue I found while testing, and I've added the test to the testsuite.
> To run the test and see the issue:
>
> $  make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
> CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"
>
> gdb/ChangeLog (updated)
>
> 2020-06-04  Caroline Tice  <cmtice@google.com>
>
>         * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
>         (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
>         (struct dwo_sections): Add rnglists field.
>         (cu_debug_rnglist_section): New function (decl & definition).
>         (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
>         (dwarf2_rnglists_process): Add cases for DW_RLE_base_addressx,
>         DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
>         the base address to DW_RLE_offset_pairs (not to all ranges).
>         (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
>         need_ranges_base.
>         (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
>         need_ranges_base.
>         (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
>         cu->ranges_base.
>         (partial_die_info::read): Check for DW_FORM_rnglistx when setting
>         need_ranges_base.
>         (read_rnglist_index): New function.
>         (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
>         (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.
>
> gdb/testsuite/ChangeLog:
>
> 2020-06-04  Caroline Tice  <cmtice@google.com>
>
>         * gdb.dwarf2/dw5-rnglist-test.cc: New file.
>         * gdb.dwarf2/dw5-rnglist-test.exp: New file.
>
>
>
>
>
> On Wed, Jun 3, 2020 at 7:49 AM Tom Tromey <tom@tromey.com> wrote:
> >
> > Caroline> $ clang++ -gdwarf-5 -O0 -gsplit-dwarf pre-order.cpp
> > Caroline> pre-order-common.cpp -o pre-order
> >
> > I wonder if the test suite can be run this way.
> >
> > Caroline> Oh.  Those get used in my next upcoming patch (where I update GDB to
> > Caroline> handle DWARFv5 .dwp files).  I can either leave them in this patch, or
> > Caroline> remove them from here and put them in the next one, whichever you
> > Caroline> prefer.
> >
> > I think it's better for patches to be reasonably self-contained when
> > possible.
> >
> > Also, there were other patches for DWARFv5 on the list in "recent" (this
> > year) times.  I wonder if those are relevant / related.
> >
> > Tom

[-- Attachment #2: gdb-dwo-rnglists.updated.patch --]
[-- Type: application/octet-stream, Size: 19982 bytes --]

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e6566f9649..e6031c4683 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -448,6 +450,9 @@ struct dwarf2_cu
   /* Base address of this compilation unit.  */
   gdb::optional<CORE_ADDR> base_address;
 
+  /* CU rnglist ranges came from skeleton unit. */
+  bool cu_ranges_from_skeleton = false;
+
   /* The language we are debugging.  */
   enum language language = language_unknown;
   const struct language_defn *language_defn = nullptr;
@@ -650,6 +655,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1379,11 +1385,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
 static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
+			       struct dwarf2_cu *, dwarf2_psymtab *,
+			       dwarf_tag);
 
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglist_section
+  (struct dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -7181,6 +7192,11 @@ cutu_reader::cutu_reader (dwarf2_per_cu_data *this_cu,
 	      dummy_p = true;
 	      return;
 	    }
+          /* if (dwarf2_attr_no_follow (comp_unit_die, DW_AT_ranges)) */
+          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
+            {
+              cu->cu_ranges_from_skeleton = true;
+            }
 	  comp_unit_die = dwo_comp_unit_die;
 	}
       else
@@ -12397,6 +12413,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13741,7 +13762,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13751,17 +13772,40 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  bool ignore_dwo_unit = false;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  /* If the DW_AT_ranges attribute was part of a DW_TAG_skeleton_unit that was
+     changed into a DW_TAG_compile_unit after calling read_cutu_die_from_dwo,
+     we want to use the rnglist in the objfile rather than the one in the
+     dwo_unit. */
+  if (tag == DW_TAG_compile_unit &&
+      cu->cu_ranges_from_skeleton)
+    ignore_dwo_unit = true;
+
+  /* Elsee, if there's a dwo_unit, we want the rnglist from the dwo file.  */
+  if (cu->dwo_unit &&
+      cu->dwo_unit->dwo_file->sections.rnglists.size > 0 &&
+      !ignore_dwo_unit)
+    {
+      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
+    }
+  else
+    {
+      rnglists_section = &per_objfile->per_bfd->rnglists;
+    }
+
+  rnglists_section->read (objfile);
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13769,8 +13813,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13792,6 +13836,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13810,6 +13859,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13838,7 +13900,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
-	default:
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
+ 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
 	}
@@ -13866,8 +13941,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13900,7 +13979,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13916,7 +13995,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14002,7 +14081,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14012,7 +14091,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14105,7 +14184,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
 	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14113,7 +14193,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14276,7 +14357,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
 	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14284,7 +14366,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18115,6 +18197,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -18633,7 +18720,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
 	       but that requires a full DIE, so instead we just
 	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18642,7 +18730,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19043,6 +19131,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+
+  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
+  if (section == nullptr)
+    error(_("Cannot find .debug_rnglists section [in module %s]"),
+	  objfile_name(objfile));
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error(_("DW_FORM_rnglistx used without .debug_rnglists section "
+	    "[in module %s]"),
+       objfile_name (objfile));
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error(_("DW_FORM_rnglistx index pointing outside of "
+	    ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+  if (rnglist_base + rnglist_index * cu->header.offset_size
+        >= section->size)
+    error(_("DW_FORM_rnglistx pointing outside of "
+            ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+  const gdb_byte *info_ptr
+    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19061,6 +19192,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19242,8 +19376,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23332,6 +23468,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglist_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000..4d650e1279
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,71 @@
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node * make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void inorder(node *root) {
+  std::vector<node *> todo;
+  todo.push_back(root);
+  while (!todo.empty()){
+    node *curr = todo.back();
+    todo.pop_back(); /* break-here */
+    if (curr->visited) {
+      std::cout << curr->id << " ";
+    } else {
+      curr->visited = true;
+      if (curr->right) { todo.push_back(curr->right); }
+      todo.push_back(curr);
+      if (curr->left) { todo.push_back(curr->left); }
+    }
+  }
+}
+
+int main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000..b5c6c3bfe3
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2011-2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-09 23:32         ` Caroline Tice
@ 2020-06-16 15:37           ` Caroline Tice
  2020-06-18 20:27           ` Tom Tromey
  1 sibling, 0 replies; 29+ messages in thread
From: Caroline Tice @ 2020-06-16 15:37 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

Ping?  Could somebody please review this?

-- Caroline
cmtice@google.com


On Tue, Jun 9, 2020 at 4:32 PM Caroline Tice <cmtice@google.com> wrote:

> While playing with this a bit more (getting ready to work on my .dwp
> file patch), I noticed that there was an important bit I left out of
> the original patch: dwarf2_rnglists_process, in the original code,
> always reads out of the rnglist section in the main objfile, even when
> there is a .rnglist section in a .dwo file that it should be reading
> instead.  I had some local code that fixes this, but it didn't make it
> into my previous patch (I apologize for that).  The attached patch has
> been updated to contain this fix as well.  It also contains my fixes
> and testcase from the previous patch.
>
> I ran the testsuite again (adding the -gdwarf-5 and -gsplit-dwarf
> flags in testsuite/lib/gdb.exp), and compiling everything with clang:
>
> $ make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
> CXX_FOR_TARGET=${CLANG_CXX}"
>
> This time my patched GDB passed quite a few tests that the unpatched
> version failed (when compiled with clang and passed the flags):
>
> Testsuite summary from unpatched GDB:
>
> === gdb Summary ===
> # of expected passes 55725
> # of unexpected failures 2956
> # of unexpected successes 83
> # of expected failures 169
> # of known failures 57
> # of unresolved testcases 115
> # of untested testcases 175
> # of unsupported tests 272
> # of paths in test names 1
> # of duplicate test names 269
>
>
> Testsuite summary from patched GDB:
>
> === gdb Summary ===
>
> # of expected passes 56173
> # of unexpected failures 2523
> # of unexpected successes 2
> # of expected failures 250
> # of known failures 57
> # of unresolved testcases 111
> # of untested testcases 174
> # of unsupported tests 272
> # of paths in test names 1
> # of duplicate test names 269
>
> Some of the tests that have more passes with the patch include:
> gdb.base/break-entry.exp, gdb.base/info-fun.exp,
> gdb.base/info-shared.exp,gdb.base/return-nodebug.exp,
> gdb.dwarf2/fission-base.exp
> (I can send you a complete list if you really want it)
>
> Anyway below is the updated patch for review.
>
> -- Caroline
> cmtice@google.com
>
> gdb/ChangeLog:
>
> 2020-06-09  Caroline Tice  <cmtice@google.com>
>
>         * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
>         (dwop_section_names): Add .debug_rnglists.dwo,
> .zdebug_rnglists.dwo.
>         (struct dwarf2_cu): Add cu_ranges_from_skeleton field.
>         (struct dwo_sections): Add rnglists field.
>         (dwarf2_ranges_read): Add tag parameter.
>         (cu_debug_rnglist_section): New function (decl & definition).
>         (cutu_reader::cutu_reader): Before replacing the skeleton unit
>         comp_unit_die with the dwo comp_unit_die, check to see if the
> skeleton
>         unit die has a DW_AT_ranges, and if so set the
> cu_ranges_from_skeleton
>         field in the cu.
>         (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo
> section.
>         (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind
> of
>         die whose range is being checked; add code to read the rnglist
> section
>         from the dwo file rather than from the main objfile, if
> appropriate.
>         Add cases for DW_RLE_base_addressx,
>         DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
>         the base address to DW_RLE_offset_pairs (not to all ranges).
>         (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
>         dwarf2_rnglists_process.
>         (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
>         dwarf2_ranges_process.
>         (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
>         need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
>         (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when
> setting
>         need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
>         (read_full_die_1): Add code to read DW_AT_rnglists_base and assign
> to
>         cu->ranges_base.
>         (partial_die_info::read): Check for DW_FORM_rnglistx when setting
>         need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
>         (read_rnglist_index): New function.
>         (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
>         (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.
>
> gdb/testsuite/ChangeLog:
>
> 2020-06-09  Caroline Tice  <cmtice@google.com>
>
>         * gdb.dwarf2/dw5-rnglist-test.cc: New file.
>         * gdb.dwarf2/dw5-rnglist-test.exp: New file.
>
>
> On Thu, Jun 4, 2020 at 2:39 PM Caroline Tice <cmtice@google.com> wrote:
> >
> > I've been playing with running the testsuite with -gdwarf-5
> > -gsplit-dwarf and using the clang compiler. I did not find any tests
> > that showed the issues I was running into (but I did discover a small
> > bug in my patch, which my newer patch fixes).  With my fixed patch,
> > there are no testsuite differences.  I have created a new testsuite
> > test case, which does fail with current ToT and passes with my patch,
> > but that only works if you compile it with clang -- if you compile it
> > with GCC it passes in both cases (because GCC is not generating the
> > DW_FORM_rmglistx that my patch is handling/fixing).
> >
> > I've updated the patch to include the requested format changes, remove
> > the pieces that are only used in an upcoming patch,  fix the small
> > issue I found while testing, and I've added the test to the testsuite.
> > To run the test and see the issue:
> >
> > $  make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
> > CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"
> >
> > gdb/ChangeLog (updated)
> >
> > 2020-06-04  Caroline Tice  <cmtice@google.com>
> >
> >         * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
> >         (dwop_section_names): Add .debug_rnglists.dwo,
> .zdebug_rnglists.dwo.
> >         (struct dwo_sections): Add rnglists field.
> >         (cu_debug_rnglist_section): New function (decl & definition).
> >         (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo
> section.
> >         (dwarf2_rnglists_process): Add cases for DW_RLE_base_addressx,
> >         DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only
> add
> >         the base address to DW_RLE_offset_pairs (not to all ranges).
> >         (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
> >         need_ranges_base.
> >         (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when
> setting
> >         need_ranges_base.
> >         (read_full_die_1): Add code to read DW_AT_rnglists_base and
> assign to
> >         cu->ranges_base.
> >         (partial_die_info::read): Check for DW_FORM_rnglistx when setting
> >         need_ranges_base.
> >         (read_rnglist_index): New function.
> >         (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
> >         (read_attribute_value): Mark DW_FORM_rnglistx with
> need_reprocess.
> >
> > gdb/testsuite/ChangeLog:
> >
> > 2020-06-04  Caroline Tice  <cmtice@google.com>
> >
> >         * gdb.dwarf2/dw5-rnglist-test.cc: New file.
> >         * gdb.dwarf2/dw5-rnglist-test.exp: New file.
> >
> >
> >
> >
> >
> > On Wed, Jun 3, 2020 at 7:49 AM Tom Tromey <tom@tromey.com> wrote:
> > >
> > > Caroline> $ clang++ -gdwarf-5 -O0 -gsplit-dwarf pre-order.cpp
> > > Caroline> pre-order-common.cpp -o pre-order
> > >
> > > I wonder if the test suite can be run this way.
> > >
> > > Caroline> Oh.  Those get used in my next upcoming patch (where I
> update GDB to
> > > Caroline> handle DWARFv5 .dwp files).  I can either leave them in this
> patch, or
> > > Caroline> remove them from here and put them in the next one,
> whichever you
> > > Caroline> prefer.
> > >
> > > I think it's better for patches to be reasonably self-contained when
> > > possible.
> > >
> > > Also, there were other patches for DWARFv5 on the list in "recent"
> (this
> > > year) times.  I wonder if those are relevant / related.
> > >
> > > Tom
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-09 23:32         ` Caroline Tice
  2020-06-16 15:37           ` Caroline Tice
@ 2020-06-18 20:27           ` Tom Tromey
  2020-06-23 19:04             ` Caroline Tice
  1 sibling, 1 reply; 29+ messages in thread
From: Tom Tromey @ 2020-06-18 20:27 UTC (permalink / raw)
  To: Caroline Tice via Gdb-patches; +Cc: Tom Tromey, Caroline Tice, Eric Christopher

>>>>> "Caroline" == Caroline Tice via Gdb-patches <gdb-patches@sourceware.org> writes:

Thanks for the patch.

Caroline> +          /* if (dwarf2_attr_no_follow (comp_unit_die, DW_AT_ranges)) */

No need for commented-out code.

Caroline> +          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
Caroline> +            {
Caroline> +              cu->cu_ranges_from_skeleton = true;
Caroline> +            }

You can remove the braces here.  Normally in gdb there are only braces
if there are multiple statements (or a comment).

Caroline> +  if (tag == DW_TAG_compile_unit &&
Caroline> +      cu->cu_ranges_from_skeleton)

The GNU / gdb style is to break before operators, not after (including
assignment FWIW).  There are several occurrences of this, I didn't mark
them all.

Caroline> +  if (section == nullptr)
Caroline> +    error(_("Cannot find .debug_rnglists section [in module %s]"),

Space before "(".

Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
Caroline> new file mode 100644
Caroline> index 0000000..4d650e1279
Caroline> --- /dev/null
Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
Caroline> @@ -0,0 +1,71 @@
Caroline> +#include <iostream>

Tests also need the copyright header.

Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
Caroline> new file mode 100644
Caroline> index 0000000..b5c6c3bfe3
Caroline> --- /dev/null
Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
Caroline> @@ -0,0 +1,40 @@
Caroline> +# Copyright 2011-2020 Free Software Foundation, Inc.

This should probably just be 2020.

Tom

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-18 20:27           ` Tom Tromey
@ 2020-06-23 19:04             ` Caroline Tice
  2020-07-01  0:09               ` Caroline Tice
  2020-07-01  0:34               ` Simon Marchi
  0 siblings, 2 replies; 29+ messages in thread
From: Caroline Tice @ 2020-06-23 19:04 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

[-- Attachment #1: Type: text/plain, Size: 2049 bytes --]

On Thu, Jun 18, 2020 at 1:27 PM Tom Tromey <tom@tromey.com> wrote:

> >>>>> "Caroline" == Caroline Tice via Gdb-patches <
> gdb-patches@sourceware.org> writes:
>
> Thanks for the patch.
>
> Caroline> +          /* if (dwarf2_attr_no_follow (comp_unit_die,
> DW_AT_ranges)) */
>
> No need for commented-out code.
>
> Done.


> Caroline> +          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
> Caroline> +            {
> Caroline> +              cu->cu_ranges_from_skeleton = true;
> Caroline> +            }
>
> You can remove the braces here.  Normally in gdb there are only braces
> if there are multiple statements (or a comment).
>
> Done.


> Caroline> +  if (tag == DW_TAG_compile_unit &&
> Caroline> +      cu->cu_ranges_from_skeleton)
>
> The GNU / gdb style is to break before operators, not after (including
> assignment FWIW).  There are several occurrences of this, I didn't mark
> them all.
>
> Done.


> Caroline> +  if (section == nullptr)
> Caroline> +    error(_("Cannot find .debug_rnglists section [in module
> %s]"),
>
> Space before "(".
>
>
Done.

> Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> Caroline> new file mode 100644
> Caroline> index 0000000..4d650e1279
> Caroline> --- /dev/null
> Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> Caroline> @@ -0,0 +1,71 @@
> Caroline> +#include <iostream>
>
> Tests also need the copyright header.
>
> Done.



> Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
> b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
> Caroline> new file mode 100644
> Caroline> index 0000000..b5c6c3bfe3
> Caroline> --- /dev/null
> Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
> Caroline> @@ -0,0 +1,40 @@
> Caroline> +# Copyright 2011-2020 Free Software Foundation, Inc.
>
> This should probably just be 2020.
>
>
Done.

> Tom
>

I believe I have addressed all of your comments.  The updated patch is
attached. Please let me know if this is ok now.  Thanks!

-- Caroline Tice
cmtice@google.com

[-- Attachment #2: gdb-dwo-rnglists.updated2.patch --]
[-- Type: application/octet-stream, Size: 20617 bytes --]

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e6566f9649..26ce971fea 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -448,6 +450,9 @@ struct dwarf2_cu
   /* Base address of this compilation unit.  */
   gdb::optional<CORE_ADDR> base_address;
 
+  /* CU rnglist ranges came from skeleton unit. */
+  bool cu_ranges_from_skeleton = false;
+
   /* The language we are debugging.  */
   enum language language = language_unknown;
   const struct language_defn *language_defn = nullptr;
@@ -650,6 +655,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1379,11 +1385,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
 static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
+			       struct dwarf2_cu *, dwarf2_psymtab *,
+			       dwarf_tag);
 
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglist_section
+  (struct dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -7181,6 +7192,8 @@ cutu_reader::cutu_reader (dwarf2_per_cu_data *this_cu,
 	      dummy_p = true;
 	      return;
 	    }
+          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
+              cu->cu_ranges_from_skeleton = true;
 	  comp_unit_die = dwo_comp_unit_die;
 	}
       else
@@ -12397,6 +12410,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13741,7 +13759,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13751,17 +13769,36 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  bool ignore_dwo_unit = false;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  /* If the DW_AT_ranges attribute was part of a DW_TAG_skeleton_unit that was
+     changed into a DW_TAG_compile_unit after calling read_cutu_die_from_dwo,
+     we want to use the rnglist in the objfile rather than the one in the
+     dwo_unit. */
+  if (tag == DW_TAG_compile_unit
+      && cu->cu_ranges_from_skeleton)
+    ignore_dwo_unit = true;
+
+  /* Else, if there's a dwo_unit, we want the rnglist from the dwo file.  */
+  if (cu->dwo_unit
+      && cu->dwo_unit->dwo_file->sections.rnglists.size > 0
+      && !ignore_dwo_unit)
+      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
+  else
+      rnglists_section = &per_objfile->per_bfd->rnglists;
+
+  rnglists_section->read (objfile);
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13769,8 +13806,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13792,6 +13829,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13810,6 +13852,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13838,7 +13893,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
-	default:
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
+ 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
 	}
@@ -13866,8 +13934,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13900,7 +13972,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13916,7 +13988,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14002,7 +14074,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14012,7 +14084,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14105,7 +14177,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
 	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14113,7 +14186,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14276,7 +14350,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
 	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14284,7 +14359,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18115,6 +18190,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -18633,7 +18713,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
 	       but that requires a full DIE, so instead we just
 	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18642,7 +18723,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19043,6 +19124,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+
+  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
+  if (section == nullptr)
+    error (_("Cannot find .debug_rnglists section [in module %s]"),
+	   objfile_name(objfile));
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+       objfile_name (objfile));
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+  if (rnglist_base + rnglist_index * cu->header.offset_size
+        >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+  const gdb_byte *info_ptr
+    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19061,6 +19185,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19242,8 +19369,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23332,6 +23461,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglist_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000..17f78843d2
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,88 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node * make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void inorder(node *root) {
+  std::vector<node *> todo;
+  todo.push_back(root);
+  while (!todo.empty()){
+    node *curr = todo.back();
+    todo.pop_back(); /* break-here */
+    if (curr->visited) {
+      std::cout << curr->id << " ";
+    } else {
+      curr->visited = true;
+      if (curr->right) { todo.push_back(curr->right); }
+      todo.push_back(curr);
+      if (curr->left) { todo.push_back(curr->left); }
+    }
+  }
+}
+
+int main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000..af6c34b5dd
--- /dev/null
+++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-23 19:04             ` Caroline Tice
@ 2020-07-01  0:09               ` Caroline Tice
  2020-07-01  0:34               ` Simon Marchi
  1 sibling, 0 replies; 29+ messages in thread
From: Caroline Tice @ 2020-07-01  0:09 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Caroline Tice via Gdb-patches, Eric Christopher

Ping?

-- Caroline
cmtice@google.com

On Tue, Jun 23, 2020 at 12:04 PM Caroline Tice <cmtice@google.com> wrote:
>
>
>
> On Thu, Jun 18, 2020 at 1:27 PM Tom Tromey <tom@tromey.com> wrote:
>>
>> >>>>> "Caroline" == Caroline Tice via Gdb-patches <gdb-patches@sourceware.org> writes:
>>
>> Thanks for the patch.
>>
>> Caroline> +          /* if (dwarf2_attr_no_follow (comp_unit_die, DW_AT_ranges)) */
>>
>> No need for commented-out code.
>>
> Done.
>
>>
>> Caroline> +          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
>> Caroline> +            {
>> Caroline> +              cu->cu_ranges_from_skeleton = true;
>> Caroline> +            }
>>
>> You can remove the braces here.  Normally in gdb there are only braces
>> if there are multiple statements (or a comment).
>>
> Done.
>
>>
>> Caroline> +  if (tag == DW_TAG_compile_unit &&
>> Caroline> +      cu->cu_ranges_from_skeleton)
>>
>> The GNU / gdb style is to break before operators, not after (including
>> assignment FWIW).  There are several occurrences of this, I didn't mark
>> them all.
>>
> Done.
>
>>
>> Caroline> +  if (section == nullptr)
>> Caroline> +    error(_("Cannot find .debug_rnglists section [in module %s]"),
>>
>> Space before "(".
>>
>
> Done.
>>
>> Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
>> Caroline> new file mode 100644
>> Caroline> index 0000000..4d650e1279
>> Caroline> --- /dev/null
>> Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
>> Caroline> @@ -0,0 +1,71 @@
>> Caroline> +#include <iostream>
>>
>> Tests also need the copyright header.
>>
> Done.
>
>
>>
>> Caroline> diff --git a/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
>> Caroline> new file mode 100644
>> Caroline> index 0000000..b5c6c3bfe3
>> Caroline> --- /dev/null
>> Caroline> +++ b/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
>> Caroline> @@ -0,0 +1,40 @@
>> Caroline> +# Copyright 2011-2020 Free Software Foundation, Inc.
>>
>> This should probably just be 2020.
>>
>
> Done.
>>
>> Tom
>
>
> I believe I have addressed all of your comments.  The updated patch is attached. Please let me know if this is ok now.  Thanks!
>
> -- Caroline Tice
> cmtice@google.com
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-06-23 19:04             ` Caroline Tice
  2020-07-01  0:09               ` Caroline Tice
@ 2020-07-01  0:34               ` Simon Marchi
  2020-07-01  0:36                 ` Simon Marchi
  1 sibling, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-01  0:34 UTC (permalink / raw)
  To: Caroline Tice, Tom Tromey; +Cc: Eric Christopher, Caroline Tice via Gdb-patches

On 2020-06-23 3:04 p.m., Caroline Tice via Gdb-patches wrote:
> I believe I have addressed all of your comments.  The updated patch is
> attached. Please let me know if this is ok now.  Thanks!

Hi Caroline,

Just to be sure, could you please send the complete patch, including the commit message (using
either git-format-patch or git-send-email)?  It would be nice to see the patch as it will be
merged, which includes the commit message.

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-01  0:34               ` Simon Marchi
@ 2020-07-01  0:36                 ` Simon Marchi
  2020-07-01 19:57                   ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-01  0:36 UTC (permalink / raw)
  To: Caroline Tice, Tom Tromey; +Cc: Eric Christopher, Caroline Tice via Gdb-patches

On 2020-06-30 8:34 p.m., Simon Marchi wrote:
> On 2020-06-23 3:04 p.m., Caroline Tice via Gdb-patches wrote:
>> I believe I have addressed all of your comments.  The updated patch is
>> attached. Please let me know if this is ok now.  Thanks!
> 
> Hi Caroline,
> 
> Just to be sure, could you please send the complete patch, including the commit message (using
> either git-format-patch or git-send-email)?  It would be nice to see the patch as it will be
> merged, which includes the commit message.
> 
> Simon
> 

Oh, and just a nit, git told me this when applying:

/home/simark/patches/gdb-dwo-rnglists.updated2.patch:188: space before tab in indent.
 	default:
warning: 1 line adds whitespace errors.

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-01  0:36                 ` Simon Marchi
@ 2020-07-01 19:57                   ` Caroline Tice
  2020-07-02  5:41                     ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-01 19:57 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Eric Christopher, Caroline Tice via Gdb-patches

[-- Attachment #1: Type: text/plain, Size: 3358 bytes --]

I created the attached patch with git format-patch; I've never used
that before, so I'm hoping I got it right. :-)

Please let me know  if there's anything else you need/want for approval.

-- Caroline
cmtice@google.com

gdb/ChangeLog:

2020-07-01  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwarf2_cu): Add cu_ranges_from_skeleton field.
        (struct dwo_sections): Add rnglists field.
        (dwarf2_ranges_read): Add tag parameter.
        (cu_debug_rnglist_section): New function (decl & definition).
        (cutu_reader::cutu_reader): Before replacing the skeleton unit
        comp_unit_die with the dwo comp_unit_die, check to see if the skeleton
        unit die has a DW_AT_ranges, and if so set the cu_ranges_from_skeleton
        field in the cu.
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind of
        die whose range is being checked; add code to read the rnglist section
        from the dwo file rather than from the main objfile, if appropriate.
        Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges).
        (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
        dwarf2_rnglists_process.
        (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
        dwarf2_ranges_process.
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog entry:

2020-07-01  Caroline Tice  <cmtice@google.com>
        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.



On Tue, Jun 30, 2020 at 5:36 PM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-06-30 8:34 p.m., Simon Marchi wrote:
> > On 2020-06-23 3:04 p.m., Caroline Tice via Gdb-patches wrote:
> >> I believe I have addressed all of your comments.  The updated patch is
> >> attached. Please let me know if this is ok now.  Thanks!
> >
> > Hi Caroline,
> >
> > Just to be sure, could you please send the complete patch, including the commit message (using
> > either git-format-patch or git-send-email)?  It would be nice to see the patch as it will be
> > merged, which includes the commit message.
> >
> > Simon
> >
>
> Oh, and just a nit, git told me this when applying:
>
> /home/simark/patches/gdb-dwo-rnglists.updated2.patch:188: space before tab in indent.
>         default:
> warning: 1 line adds whitespace errors.
>
> Simon

[-- Attachment #2: v2-0001-Update-GDB-to-fix-issues-with-handling-DWARF-v5-r.patch --]
[-- Type: application/octet-stream, Size: 21799 bytes --]

From 78c59a0bf626a7dea8394472c8af39146f3b8cc9 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice@google.com>
Date: Wed, 1 Jul 2020 12:39:08 -0700
Subject: [PATCH v2] Update GDB to fix issues with handling DWARF v5 rnglists &
 .dwo files.

- Added 'reprocessing' for reading rnglist index (as is done for loclist
  index).
- Added code for reading rnglists out of .dwo files.
- Added several missing rnglist forms to dwarf2_rnglists_process.
- Fixed bug that was alwayas adding base address for rnglists (only
  one form needs that).
- Updated dwarf2_rnglists_process to read rnglist out of dwo file when
  appropriate.
- Added new functions cu_debug_rnglist_section & read_rnglist_index.
- Added new testcase, dw5-rnglist-test.{cc,exp}
---
 gdb/dwarf2/read.c                             | 190 ++++++++++++++++--
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc  |  88 ++++++++
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp |  40 ++++
 3 files changed, 297 insertions(+), 21 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4622d14a05..a15af12172 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -448,6 +450,9 @@ struct dwarf2_cu
   /* Base address of this compilation unit.  */
   gdb::optional<CORE_ADDR> base_address;
 
+  /* CU rnglist ranges came from skeleton unit. */
+  bool cu_ranges_from_skeleton = false;
+
   /* The language we are debugging.  */
   enum language language = language_unknown;
   const struct language_defn *language_defn = nullptr;
@@ -650,6 +655,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1379,11 +1385,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
 static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
+			       struct dwarf2_cu *, dwarf2_psymtab *,
+			       dwarf_tag);
 
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglist_section
+  (struct dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -7231,6 +7242,8 @@ cutu_reader::cutu_reader (dwarf2_per_cu_data *this_cu,
 	      dummy_p = true;
 	      return;
 	    }
+          if (comp_unit_die->attr (DW_AT_ranges) != nullptr)
+              cu->cu_ranges_from_skeleton = true;
 	  comp_unit_die = dwo_comp_unit_die;
 	}
       else
@@ -12448,6 +12461,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13792,7 +13810,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13802,17 +13820,36 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  bool ignore_dwo_unit = false;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  /* If the DW_AT_ranges attribute was part of a DW_TAG_skeleton_unit that was
+     changed into a DW_TAG_compile_unit after calling read_cutu_die_from_dwo,
+     we want to use the rnglist in the objfile rather than the one in the
+     dwo_unit. */
+  if (tag == DW_TAG_compile_unit
+      && cu->cu_ranges_from_skeleton)
+    ignore_dwo_unit = true;
+
+  /* Else, if there's a dwo_unit, we want the rnglist from the dwo file.  */
+  if (cu->dwo_unit
+      && cu->dwo_unit->dwo_file->sections.rnglists.size > 0
+      && !ignore_dwo_unit)
+      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
+  else
+      rnglists_section = &per_objfile->per_bfd->rnglists;
+
+  rnglists_section->read (objfile);
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13820,8 +13857,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13843,6 +13880,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13861,6 +13903,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13889,7 +13944,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
-	default:
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
+ 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
 	}
@@ -13917,8 +13985,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13951,7 +14023,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13967,7 +14039,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14053,7 +14125,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14063,7 +14135,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14156,7 +14228,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
 	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14164,7 +14237,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14327,7 +14401,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
 	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14335,7 +14410,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18165,6 +18240,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
 	       but that requires a full DIE, so instead we just
 	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18693,7 +18774,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19094,6 +19175,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+
+  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
+  if (section == nullptr)
+    error (_("Cannot find .debug_rnglists section [in module %s]"),
+	   objfile_name(objfile));
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+       objfile_name (objfile));
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+  if (rnglist_base + rnglist_index * cu->header.offset_size
+        >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+  const gdb_byte *info_ptr
+    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19112,6 +19236,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19293,8 +19420,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23382,6 +23511,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglist_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000000..17f78843d2
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,88 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node * make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void inorder(node *root) {
+  std::vector<node *> todo;
+  todo.push_back(root);
+  while (!todo.empty()){
+    node *curr = todo.back();
+    todo.pop_back(); /* break-here */
+    if (curr->visited) {
+      std::cout << curr->id << " ";
+    } else {
+      curr->visited = true;
+      if (curr->right) { todo.push_back(curr->right); }
+      todo.push_back(curr);
+      if (curr->left) { todo.push_back(curr->left); }
+    }
+  }
+}
+
+int main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000000..af6c34b5dd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"
-- 
2.27.0.212.ge8ba1cc988-goog


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-01 19:57                   ` Caroline Tice
@ 2020-07-02  5:41                     ` Simon Marchi
  2020-07-03 22:47                       ` [PATCH V3] " Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-02  5:41 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-01 3:57 p.m., Caroline Tice via Gdb-patches wrote:
> I created the attached patch with git format-patch; I've never used
> that before, so I'm hoping I got it right. :-)

Very nice, thanks.

> Please let me know  if there's anything else you need/want for approval.

Thanks for that.  I'd appreciate if the commit message contained a bit more
of the rationale for the change (in addition to what you have already put
in there).  Basically, what you explained in your first message.

> @@ -1379,11 +1385,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
>  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
>
>  static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> -			       struct dwarf2_cu *, dwarf2_psymtab *);
> +			       struct dwarf2_cu *, dwarf2_psymtab *,
> +			       dwarf_tag);
>
>  /* Return the .debug_loclists section to use for cu.  */
>  static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
>
> +/* Return the .debug_rnglists section to use for cu.  */
> +static struct dwarf2_section_info *cu_debug_rnglist_section
> +  (struct dwarf2_cu *cu);

Nit, this function should probably be called cu_debug_rnglists_section (with an `s`
to rnglists).

> @@ -13802,17 +13820,36 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>    const gdb_byte *buffer;
>    CORE_ADDR baseaddr;
>    bool overflow = false;
> +  ULONGEST addr_index;
> +  bool ignore_dwo_unit = false;
> +  struct dwarf2_section_info *rnglists_section;
>
>    base = cu->base_address;
>
> -  per_objfile->per_bfd->rnglists.read (objfile);
> -  if (offset >= per_objfile->per_bfd->rnglists.size)
> +  /* If the DW_AT_ranges attribute was part of a DW_TAG_skeleton_unit that was
> +     changed into a DW_TAG_compile_unit after calling read_cutu_die_from_dwo,
> +     we want to use the rnglist in the objfile rather than the one in the
> +     dwo_unit. */

I think that can be explained more simply as: we want to use the .debug_rnglists
section from the file the DW_AT_ranges attribute we're reading came from.  If it's
on DW_TAG_compile_unit, it was actually on the DW_TAG_skeleton_unit DIE, in the
objfile / linked program.  Otherwise, it was on some other DIE in the dwo.

> +  if (tag == DW_TAG_compile_unit
> +      && cu->cu_ranges_from_skeleton)

I'm wondering if this `cu_ranges_from_skeleton` boolean is really necessary.  There
cannot be a DW_AT_ranges attribute on a DW_TAG_compile_unit inside a dwo file (see
DWARF 5, section 3.1.3).  So if we're reading a DW_AT_ranges on a DW_TAG_compile_unit,
it's either because:

- it's a standard non-split DW_TAG_compile_unit
- it's a DW_TAG_skeleton_unit that was transformed into a DW_TAG_compile_unit by
  read_cutu_die_from_dwo, as you said (I didn't know it did that)

In both of these cases, the rnglists section to use should be the one from the objfile,
not the one from the dwo.  The DW_TAG_skeleton_unit in the main objfile can have
a DW_AT_ranges attribute, while the associated DW_TAG_compile_unit in the dwo can't.

So the condition could probably just be:

  if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
    rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
  else
    rnglists_section = &per_objfile->per_bfd->rnglists;

If there's a dwo_unit, the DW_TAG_compile_unit (transformed from DW_TAG_skeleton_unit)
will be the only DIE in the CU, so the only one to use the rnglists from the main objfile.
All the conceptual children DIEs contained in the dwo will use the rnglists from the dwo.

> +    ignore_dwo_unit = true;
> +
> +  /* Else, if there's a dwo_unit, we want the rnglist from the dwo file.  */
> +  if (cu->dwo_unit
> +      && cu->dwo_unit->dwo_file->sections.rnglists.size > 0
> +      && !ignore_dwo_unit)
> +      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
> +  else
> +      rnglists_section = &per_objfile->per_bfd->rnglists;

I'm not sure the `cu->dwo_unit->dwo_file->sections.rnglists.size > 0` part of the condition
is right.  Let's say you have some DIE inside the dwo with a DW_AT_ranges attriute, but the
rnglists section from the dwo is either missing or empty.  That code will end up reading the
rnglists from the objfile, which is wrong: a DW_AT_ranges in a dwo always refers to the
rnglists section of the dwo, right?

It would be a good thing to test for fun, strip the .debug_rnglists.dwo section from the dwo
and see what happens.

> @@ -13889,7 +13944,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>  	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
>  	  buffer += bytes_read;
>  	  break;
> -	default:
> +	case DW_RLE_startx_endx:
> +          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
> +          buffer += bytes_read;
> +          range_beginning = read_addr_index (cu, addr_index);
> +          if (buffer > buf_end)
> +            {
> +              overflow = true;
> +              break;
> +            }
> +          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
> +          buffer += bytes_read;
> +          range_end = read_addr_index (cu, addr_index);
> +          break;
> + 	default:

Remove the space at the beginning of the line (before the tab).

>  	  complaint (_("Invalid .debug_rnglists data (no base address)"));
>  	  return false;
>  	}
> @@ -13917,8 +13985,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>        if (range_beginning == range_end)
>  	continue;
>
> -      range_beginning += *base;
> -      range_end += *base;
> +      /* Only DW_RLE_offset_pair needs the base address added.  */
> +      if (rlet == DW_RLE_offset_pair)
> +	{
> +	  range_beginning += *base;
> +	  range_end += *base;
> +	}

That makes sense.  The DWARF standard says, about DW_RLE_base_address:

    ...that indicates the applicable base address used by following
    DW_RLE_offset_pair entries.

However, see this check a few lines above:

      if (!base.has_value ())
	{
	  /* We have no valid base address for the ranges
	     data.  */
	  complaint (_("Invalid .debug_rnglists data (no base address)"));
	  return false;
	}

Should that be moved inside the `if (rlet == DW_RLE_offset_pair)`?  It
only matters that we don't have a base if we're going to use it, and that's
if `rlet == DW_RLE_offset_pair`.

> @@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
>  	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
>  	       but that requires a full DIE, so instead we just
>  	       reimplement it.  */
> -	    int need_ranges_base = tag != DW_TAG_compile_unit;
> +	    int need_ranges_base = (tag != DW_TAG_compile_unit
> +				    && attr.form != DW_FORM_rnglistx);

It looks like this fix is unrelated from the rest of the patch, which deals
about reading rnglists from dwo files.  It's better to have on fix per patch.
So I think this fix would deserve its own patch, with a commit message that
clearly explains the issue and the fix.

I'm also not sure I understand it: when the form is DW_FORM_rnglistx, don't
we want to sometimes apply the base coming from DW_AT_rnglists_base?

> @@ -19094,6 +19175,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
>      return bfd_get_64 (abfd, info_ptr) + loclist_base;
>  }
>
> +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
> +   array of offsets in the .debug_rnglists section.  */
> +static CORE_ADDR
> +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
> +{
> +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> +  bfd *abfd = objfile->obfd;
> +  ULONGEST rnglist_header_size =
> +    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
> +     : LOCLIST_HEADER_SIZE64);
> +  ULONGEST rnglist_base =
> +      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
> +
> +  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
> +  if (section == nullptr)
> +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> +	   objfile_name(objfile));
> +  section->read (objfile);
> +  if (section->buffer == NULL)
> +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> +	     "[in module %s]"),
> +       objfile_name (objfile));
> +  struct loclist_header header;
> +  read_loclist_header (&header, section);
> +  if (rnglist_index >= header.offset_entry_count)
> +    error (_("DW_FORM_rnglistx index pointing outside of "
> +	     ".debug_rnglists offset array [in module %s]"),
> +	    objfile_name(objfile));
> +  if (rnglist_base + rnglist_index * cu->header.offset_size
> +        >= section->size)
> +    error (_("DW_FORM_rnglistx pointing outside of "
> +             ".debug_rnglists section [in module %s]"),
> +          objfile_name(objfile));
> +  const gdb_byte *info_ptr
> +    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;

Factor out `rnglist_base + rnglist_index * cu->header.offset_size` into
an `offset` variable.

> +
> +  if (cu->header.offset_size == 4)
> +    return read_4_bytes (abfd, info_ptr) + rnglist_base;
> +  else
> +    return read_8_bytes (abfd, info_ptr) + rnglist_base;

Please use some empty lines in the code to separate logical block.  I find
it really hard to follow, packed like this.  At the very least, after the
`if` statements, put an empty line.  Comments that indicate what each block
does at high level are also helpful:

 +  /* Get rnglists section.  */
 +  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
 +  if (section == nullptr)
 +    error (_("Cannot find .debug_rnglists section [in module %s]"),
 +	   objfile_name(objfile));
 +
 +  /* Read rnglists section content.  */
 +  section->read (objfile);
 +  if (section->buffer == NULL)
 +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
 +	     "[in module %s]"),
 +       objfile_name (objfile));
 +
 +  /* Validate that the index is with the offset list range. */
 +  struct loclist_header header;
 +  read_loclist_header (&header, section);
 +  if (rnglist_index >= header.offset_entry_count)
 +    error (_("DW_FORM_rnglistx index pointing outside of "
 +	     ".debug_rnglists offset array [in module %s]"),
 +	    objfile_name(objfile));
 +
 +  /* Validate that the offset is in the section's range.  */
 +  if (rnglist_base + rnglist_index * cu->header.offset_size
 +        >= section->size)
 +    error (_("DW_FORM_rnglistx pointing outside of "
 +             ".debug_rnglists section [in module %s]"),
 +          objfile_name(objfile));
 +
 +  const gdb_byte *info_ptr
 +    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;


> @@ -23382,6 +23511,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
>  				  : &per_objfile->per_bfd->loc);
>  }
>
> +/* Return the .debug_rnglists section to use for CU.  */
> +static struct dwarf2_section_info *
> +cu_debug_rnglist_section (struct dwarf2_cu *cu)
> +{
> +  if (cu->header.version < 5)
> +    error (_(".debug_rnglists section cannot be used in DWARF %d"),
> +	   cu->header.version);
> +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> +
> +  if (cu->dwo_unit)

When checking pointers, use an explicit ` == nullptr` / ` != nullptr`.

> +    {
> +      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
> +
> +      if (sections->rnglists.size > 0)
> +	return &sections->rnglists;
> +    }
> +  return &dwarf2_per_objfile->per_bfd->rnglists;

This function is called to get the right rnglists section in the
read_rnglist_index function.  I don't understand how this can work.
When there is a dwo, the read_rnglist_index can be used to read a
range list either for the main file (the DW_AT_ranges on the
DW_TAG_skeleton_unit) or for the dwo file (the DW_AT_ranges on a
DW_TAG_lexical_block).

This function here always return the dwo section if a dwo is
present... which would be wrong for when reading the DW_AT_ranges
on the DW_TAG_skeleton_unit?

Ok, I debugged GDB to see when this gets called.  It works because
when reading the DW_TAG_skeleton_unit, `cu->dwo_unit` is still
NULL... so the first time cu_debug_rnglist_section gets called, it
returns the section in the main file, and the following times, it
returns the section in the dwo file.  That seems a bit fragile to
me.  For example, if the order of things change, like if we change
GDB to read the ranges lazily later, and `cu->dwo_unit` is not NULL
anymore when reading the DW_AT_ranges on DW_TAG_skeleton_unit, this
will return the wrong section.

Perhaps a more robust way to decide which section to return would
be to use the same `cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit`
trick from earlier?  In fact, perhaps that other spot should call
cu_debug_rnglist_section instead of replicating the logic?

>  static void
> diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> new file mode 100644
> index 0000000000..17f78843d2
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> @@ -0,0 +1,88 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2020 Free Software Foundation, Inc.
> +
> +   This program 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.
> +
> +   This program 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/>.  */
> +
> +#include <iostream>
> +#include <vector>
> +
> +struct node {
> +  int id;
> +  node *left;
> +  node *right;
> +  bool visited;
> +};
> +
> +node  node_array[50];
> +unsigned int CUR_IDX = 0;
> +
> +node * make_node (int val)
> +{
> +  node *new_node = &(node_array[CUR_IDX++]);
> +  new_node->left = NULL;
> +  new_node->right = NULL;
> +  new_node->id = val;
> +  new_node->visited = false;
> +
> +  return new_node;
> +}
> +
> +void tree_insert (node *root, int val)
> +{
> +  if (val < root->id)
> +    {
> +      if (root->left)
> +        tree_insert (root->left, val);
> +      else
> +        root->left = make_node(val);
> +    }
> +  else if (val > root->id)
> +    {
> +      if (root->right)
> +        tree_insert (root->right, val);
> +      else
> +        root->right = make_node(val);
> +    }
> +}
> +
> +void inorder(node *root) {
> +  std::vector<node *> todo;
> +  todo.push_back(root);
> +  while (!todo.empty()){
> +    node *curr = todo.back();
> +    todo.pop_back(); /* break-here */
> +    if (curr->visited) {
> +      std::cout << curr->id << " ";
> +    } else {
> +      curr->visited = true;
> +      if (curr->right) { todo.push_back(curr->right); }
> +      todo.push_back(curr);
> +      if (curr->left) { todo.push_back(curr->left); }
> +    }
> +  }
> +}
> +
> +int main (int argc, char **argv)
> +{
> +  node *root = make_node (35);
> +
> +  tree_insert (root, 28);
> +  tree_insert (root, 20);
> +  tree_insert (root, 60);
> +
> +  inorder (root);
> +
> +  return 0;
> +}

We try to format the test cases using the same style as the rest of the
source code.  Could you please update this to follow the same rules?

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH V3] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-02  5:41                     ` Simon Marchi
@ 2020-07-03 22:47                       ` Caroline Tice
  2020-07-04  5:11                         ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-03 22:47 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

[-- Attachment #1: Type: text/plain, Size: 21808 bytes --]

"

I have made most of your requested changes, which are in the attached
patch.  I disagree with one of your comments, and I had a question
about another:

First the disagreement.  You said:

>> @@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
>>           /* It would be nice to reuse dwarf2_get_pc_bounds here,
>>              but that requires a full DIE, so instead we just
>>              reimplement it.  */
>> -         int need_ranges_base = tag != DW_TAG_compile_unit;
>> +         int need_ranges_base = (tag != DW_TAG_compile_unit
>> +                                 && attr.form != DW_FORM_rnglistx);
>
> It looks like this fix is unrelated from the rest of the patch, which deals
> about reading rnglists from dwo files.  It's better to have on fix per patch.
> So I think this fix would deserve its own patch, with a commit message that
> clearly explains the issue and the fix.
>
This is actually a required fix for dealing with rnglists.  Without
it, when the DW_AT_ranges uses the form
DW_FORM_rnglistx, the wrong value is calculated and the entire rnglist
is ignored (if you turn on complaints this will happen).  So without
this fix, I can't test any of the other code for fixing rnglists, with
or without .dwo files.

> I'm also not sure I understand it: when the form is DW_FORM_rnglistx, don't
> we want to sometimes apply the base coming from DW_AT_rnglists_base?

It all depends on what form is being used. If the DW_AT_ranges is
using DW_FORM_sec_offset (which is what
GCC generates), then you do need to add the base value.  But if you
are using the DW_FORM_rnglisx (which is what clang generates), the
base value has already been considered/incorporated into the attribute
so adding it in (again) causes the value to be incorrect (again, turn
on complaints and see what happens if you try to add the base into
something with DW_FORM_rnglistx).  From the DWARF5 spec: "[A] rnglist
is either represented as: An index into the .debug_rnglists section
(DW_FORM_rnglistx). The ...operand identifies an offset location
relative to the base of that section...[or] An offset into the
.debug_rnglists section (DW_FORM_sec_offset).  The operand consists of
a byte offset from the beginning of the .debug_rnglists section."
Note the DW_FORM_rnglistx is already relative to the base.

Now, my question:  I'm not quite clear as to whether you really want
me to change the code in
cu_debug_rnglists_section or not.   I wrote the code mostly by copying
the code for loclists as closely as possible, and making whatever
changes to that were required to make it correct for rnglists.  Is it
better to have the code more consistent with the loclists code?  If
you really want me to make the changes there you suggest, then I will;
I just wanted confirmation that you really want that change (i.e.
adding the dwarf tag and checking vs. DW_TAG_compile_unit, & calling
it from dwarf2_rnglists_process).

The test of your requested changes are in the attached patch.

-- Caroline
cmtice@google.com

2020-07-03  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwo_sections): Add rnglists field.
        (dwarf2_ranges_read): Add tag parameter.
        (cu_debug_rnglist_section): New function (decl & definition).
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind of
        die whose range is being checked; add code to read the rnglist section
        from the dwo file rather than from the main objfile, if appropriate.
        Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges).
        (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
        dwarf2_rnglists_process.
        (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
        dwarf2_ranges_process.
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base.  Also pass die tag to dwarf2_ranges_read.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add code for DW_FORM_rnglistx.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog entry:

2020-07-03  Caroline Tice  <cmtice@google.com>

        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.


On Wed, Jul 1, 2020 at 10:41 PM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-07-01 3:57 p.m., Caroline Tice via Gdb-patches wrote:
> > I created the attached patch with git format-patch; I've never used
> > that before, so I'm hoping I got it right. :-)
>
> Very nice, thanks.
>
> > Please let me know  if there's anything else you need/want for approval.
>
> Thanks for that.  I'd appreciate if the commit message contained a bit more
> of the rationale for the change (in addition to what you have already put
> in there).  Basically, what you explained in your first message.
>
> > @@ -1379,11 +1385,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
> >  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
> >
> >  static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> > -                            struct dwarf2_cu *, dwarf2_psymtab *);
> > +                            struct dwarf2_cu *, dwarf2_psymtab *,
> > +                            dwarf_tag);
> >
> >  /* Return the .debug_loclists section to use for cu.  */
> >  static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
> >
> > +/* Return the .debug_rnglists section to use for cu.  */
> > +static struct dwarf2_section_info *cu_debug_rnglist_section
> > +  (struct dwarf2_cu *cu);
>
> Nit, this function should probably be called cu_debug_rnglists_section (with an `s`
> to rnglists).
>
> > @@ -13802,17 +13820,36 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
> >    const gdb_byte *buffer;
> >    CORE_ADDR baseaddr;
> >    bool overflow = false;
> > +  ULONGEST addr_index;
> > +  bool ignore_dwo_unit = false;
> > +  struct dwarf2_section_info *rnglists_section;
> >
> >    base = cu->base_address;
> >
> > -  per_objfile->per_bfd->rnglists.read (objfile);
> > -  if (offset >= per_objfile->per_bfd->rnglists.size)
> > +  /* If the DW_AT_ranges attribute was part of a DW_TAG_skeleton_unit that was
> > +     changed into a DW_TAG_compile_unit after calling read_cutu_die_from_dwo,
> > +     we want to use the rnglist in the objfile rather than the one in the
> > +     dwo_unit. */
>
> I think that can be explained more simply as: we want to use the .debug_rnglists
> section from the file the DW_AT_ranges attribute we're reading came from.  If it's
> on DW_TAG_compile_unit, it was actually on the DW_TAG_skeleton_unit DIE, in the
> objfile / linked program.  Otherwise, it was on some other DIE in the dwo.
>
> > +  if (tag == DW_TAG_compile_unit
> > +      && cu->cu_ranges_from_skeleton)
>
> I'm wondering if this `cu_ranges_from_skeleton` boolean is really necessary.  There
> cannot be a DW_AT_ranges attribute on a DW_TAG_compile_unit inside a dwo file (see
> DWARF 5, section 3.1.3).  So if we're reading a DW_AT_ranges on a DW_TAG_compile_unit,
> it's either because:
>
> - it's a standard non-split DW_TAG_compile_unit
> - it's a DW_TAG_skeleton_unit that was transformed into a DW_TAG_compile_unit by
>   read_cutu_die_from_dwo, as you said (I didn't know it did that)
>
> In both of these cases, the rnglists section to use should be the one from the objfile,
> not the one from the dwo.  The DW_TAG_skeleton_unit in the main objfile can have
> a DW_AT_ranges attribute, while the associated DW_TAG_compile_unit in the dwo can't.
>
> So the condition could probably just be:
>
>   if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
>     rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
>   else
>     rnglists_section = &per_objfile->per_bfd->rnglists;
>
> If there's a dwo_unit, the DW_TAG_compile_unit (transformed from DW_TAG_skeleton_unit)
> will be the only DIE in the CU, so the only one to use the rnglists from the main objfile.
> All the conceptual children DIEs contained in the dwo will use the rnglists from the dwo.
>
> > +    ignore_dwo_unit = true;
> > +
> > +  /* Else, if there's a dwo_unit, we want the rnglist from the dwo file.  */
> > +  if (cu->dwo_unit
> > +      && cu->dwo_unit->dwo_file->sections.rnglists.size > 0
> > +      && !ignore_dwo_unit)
> > +      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
> > +  else
> > +      rnglists_section = &per_objfile->per_bfd->rnglists;
>
> I'm not sure the `cu->dwo_unit->dwo_file->sections.rnglists.size > 0` part of the condition
> is right.  Let's say you have some DIE inside the dwo with a DW_AT_ranges attriute, but the
> rnglists section from the dwo is either missing or empty.  That code will end up reading the
> rnglists from the objfile, which is wrong: a DW_AT_ranges in a dwo always refers to the
> rnglists section of the dwo, right?
>
> It would be a good thing to test for fun, strip the .debug_rnglists.dwo section from the dwo
> and see what happens.
>
> > @@ -13889,7 +13944,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
> >         range_end = cu->header.read_address (obfd, buffer, &bytes_read);
> >         buffer += bytes_read;
> >         break;
> > -     default:
> > +     case DW_RLE_startx_endx:
> > +          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
> > +          buffer += bytes_read;
> > +          range_beginning = read_addr_index (cu, addr_index);
> > +          if (buffer > buf_end)
> > +            {
> > +              overflow = true;
> > +              break;
> > +            }
> > +          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
> > +          buffer += bytes_read;
> > +          range_end = read_addr_index (cu, addr_index);
> > +          break;
> > +     default:
>
> Remove the space at the beginning of the line (before the tab).
>
> >         complaint (_("Invalid .debug_rnglists data (no base address)"));
> >         return false;
> >       }
> > @@ -13917,8 +13985,12 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
> >        if (range_beginning == range_end)
> >       continue;
> >
> > -      range_beginning += *base;
> > -      range_end += *base;
> > +      /* Only DW_RLE_offset_pair needs the base address added.  */
> > +      if (rlet == DW_RLE_offset_pair)
> > +     {
> > +       range_beginning += *base;
> > +       range_end += *base;
> > +     }
>
> That makes sense.  The DWARF standard says, about DW_RLE_base_address:
>
>     ...that indicates the applicable base address used by following
>     DW_RLE_offset_pair entries.
>
> However, see this check a few lines above:
>
>       if (!base.has_value ())
>         {
>           /* We have no valid base address for the ranges
>              data.  */
>           complaint (_("Invalid .debug_rnglists data (no base address)"));
>           return false;
>         }
>
> Should that be moved inside the `if (rlet == DW_RLE_offset_pair)`?  It
> only matters that we don't have a base if we're going to use it, and that's
> if `rlet == DW_RLE_offset_pair`.
>
> > @@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
> >           /* It would be nice to reuse dwarf2_get_pc_bounds here,
> >              but that requires a full DIE, so instead we just
> >              reimplement it.  */
> > -         int need_ranges_base = tag != DW_TAG_compile_unit;
> > +         int need_ranges_base = (tag != DW_TAG_compile_unit
> > +                                 && attr.form != DW_FORM_rnglistx);
>
> It looks like this fix is unrelated from the rest of the patch, which deals
> about reading rnglists from dwo files.  It's better to have on fix per patch.
> So I think this fix would deserve its own patch, with a commit message that
> clearly explains the issue and the fix.
>
> I'm also not sure I understand it: when the form is DW_FORM_rnglistx, don't
> we want to sometimes apply the base coming from DW_AT_rnglists_base?
>
> > @@ -19094,6 +19175,49 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
> >      return bfd_get_64 (abfd, info_ptr) + loclist_base;
> >  }
> >
> > +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
> > +   array of offsets in the .debug_rnglists section.  */
> > +static CORE_ADDR
> > +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
> > +{
> > +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> > +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> > +  bfd *abfd = objfile->obfd;
> > +  ULONGEST rnglist_header_size =
> > +    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
> > +     : LOCLIST_HEADER_SIZE64);
> > +  ULONGEST rnglist_base =
> > +      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
> > +
> > +  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
> > +  if (section == nullptr)
> > +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> > +        objfile_name(objfile));
> > +  section->read (objfile);
> > +  if (section->buffer == NULL)
> > +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> > +          "[in module %s]"),
> > +       objfile_name (objfile));
> > +  struct loclist_header header;
> > +  read_loclist_header (&header, section);
> > +  if (rnglist_index >= header.offset_entry_count)
> > +    error (_("DW_FORM_rnglistx index pointing outside of "
> > +          ".debug_rnglists offset array [in module %s]"),
> > +         objfile_name(objfile));
> > +  if (rnglist_base + rnglist_index * cu->header.offset_size
> > +        >= section->size)
> > +    error (_("DW_FORM_rnglistx pointing outside of "
> > +             ".debug_rnglists section [in module %s]"),
> > +          objfile_name(objfile));
> > +  const gdb_byte *info_ptr
> > +    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
>
> Factor out `rnglist_base + rnglist_index * cu->header.offset_size` into
> an `offset` variable.
>
> > +
> > +  if (cu->header.offset_size == 4)
> > +    return read_4_bytes (abfd, info_ptr) + rnglist_base;
> > +  else
> > +    return read_8_bytes (abfd, info_ptr) + rnglist_base;
>
> Please use some empty lines in the code to separate logical block.  I find
> it really hard to follow, packed like this.  At the very least, after the
> `if` statements, put an empty line.  Comments that indicate what each block
> does at high level are also helpful:
>
>  +  /* Get rnglists section.  */
>  +  struct dwarf2_section_info *section = cu_debug_rnglist_section (cu);
>  +  if (section == nullptr)
>  +    error (_("Cannot find .debug_rnglists section [in module %s]"),
>  +         objfile_name(objfile));
>  +
>  +  /* Read rnglists section content.  */
>  +  section->read (objfile);
>  +  if (section->buffer == NULL)
>  +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
>  +           "[in module %s]"),
>  +       objfile_name (objfile));
>  +
>  +  /* Validate that the index is with the offset list range. */
>  +  struct loclist_header header;
>  +  read_loclist_header (&header, section);
>  +  if (rnglist_index >= header.offset_entry_count)
>  +    error (_("DW_FORM_rnglistx index pointing outside of "
>  +           ".debug_rnglists offset array [in module %s]"),
>  +          objfile_name(objfile));
>  +
>  +  /* Validate that the offset is in the section's range.  */
>  +  if (rnglist_base + rnglist_index * cu->header.offset_size
>  +        >= section->size)
>  +    error (_("DW_FORM_rnglistx pointing outside of "
>  +             ".debug_rnglists section [in module %s]"),
>  +          objfile_name(objfile));
>  +
>  +  const gdb_byte *info_ptr
>  +    = section->buffer + rnglist_base + rnglist_index * cu->header.offset_size;
>
>
> > @@ -23382,6 +23511,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
> >                                 : &per_objfile->per_bfd->loc);
> >  }
> >
> > +/* Return the .debug_rnglists section to use for CU.  */
> > +static struct dwarf2_section_info *
> > +cu_debug_rnglist_section (struct dwarf2_cu *cu)
> > +{
> > +  if (cu->header.version < 5)
> > +    error (_(".debug_rnglists section cannot be used in DWARF %d"),
> > +        cu->header.version);
> > +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> > +
> > +  if (cu->dwo_unit)
>
> When checking pointers, use an explicit ` == nullptr` / ` != nullptr`.
>
> > +    {
> > +      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
> > +
> > +      if (sections->rnglists.size > 0)
> > +     return &sections->rnglists;
> > +    }
> > +  return &dwarf2_per_objfile->per_bfd->rnglists;
>
> This function is called to get the right rnglists section in the
> read_rnglist_index function.  I don't understand how this can work.
> When there is a dwo, the read_rnglist_index can be used to read a
> range list either for the main file (the DW_AT_ranges on the
> DW_TAG_skeleton_unit) or for the dwo file (the DW_AT_ranges on a
> DW_TAG_lexical_block).
>
> This function here always return the dwo section if a dwo is
> present... which would be wrong for when reading the DW_AT_ranges
> on the DW_TAG_skeleton_unit?
>
> Ok, I debugged GDB to see when this gets called.  It works because
> when reading the DW_TAG_skeleton_unit, `cu->dwo_unit` is still
> NULL... so the first time cu_debug_rnglist_section gets called, it
> returns the section in the main file, and the following times, it
> returns the section in the dwo file.  That seems a bit fragile to
> me.  For example, if the order of things change, like if we change
> GDB to read the ranges lazily later, and `cu->dwo_unit` is not NULL
> anymore when reading the DW_AT_ranges on DW_TAG_skeleton_unit, this
> will return the wrong section.
>
> Perhaps a more robust way to decide which section to return would
> be to use the same `cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit`
> trick from earlier?  In fact, perhaps that other spot should call
> cu_debug_rnglist_section instead of replicating the logic?
>
> >  static void
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> > new file mode 100644
> > index 0000000000..17f78843d2
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
> > @@ -0,0 +1,88 @@
> > +/* This testcase is part of GDB, the GNU debugger.
> > +
> > +   Copyright 2020 Free Software Foundation, Inc.
> > +
> > +   This program 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.
> > +
> > +   This program 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/>.  */
> > +
> > +#include <iostream>
> > +#include <vector>
> > +
> > +struct node {
> > +  int id;
> > +  node *left;
> > +  node *right;
> > +  bool visited;
> > +};
> > +
> > +node  node_array[50];
> > +unsigned int CUR_IDX = 0;
> > +
> > +node * make_node (int val)
> > +{
> > +  node *new_node = &(node_array[CUR_IDX++]);
> > +  new_node->left = NULL;
> > +  new_node->right = NULL;
> > +  new_node->id = val;
> > +  new_node->visited = false;
> > +
> > +  return new_node;
> > +}
> > +
> > +void tree_insert (node *root, int val)
> > +{
> > +  if (val < root->id)
> > +    {
> > +      if (root->left)
> > +        tree_insert (root->left, val);
> > +      else
> > +        root->left = make_node(val);
> > +    }
> > +  else if (val > root->id)
> > +    {
> > +      if (root->right)
> > +        tree_insert (root->right, val);
> > +      else
> > +        root->right = make_node(val);
> > +    }
> > +}
> > +
> > +void inorder(node *root) {
> > +  std::vector<node *> todo;
> > +  todo.push_back(root);
> > +  while (!todo.empty()){
> > +    node *curr = todo.back();
> > +    todo.pop_back(); /* break-here */
> > +    if (curr->visited) {
> > +      std::cout << curr->id << " ";
> > +    } else {
> > +      curr->visited = true;
> > +      if (curr->right) { todo.push_back(curr->right); }
> > +      todo.push_back(curr);
> > +      if (curr->left) { todo.push_back(curr->left); }
> > +    }
> > +  }
> > +}
> > +
> > +int main (int argc, char **argv)
> > +{
> > +  node *root = make_node (35);
> > +
> > +  tree_insert (root, 28);
> > +  tree_insert (root, 20);
> > +  tree_insert (root, 60);
> > +
> > +  inorder (root);
> > +
> > +  return 0;
> > +}
>
> We try to format the test cases using the same style as the rest of the
> source code.  Could you please update this to follow the same rules?
>
> Simon

[-- Attachment #2: v3-0001-Update-GDB-to-fix-issues-with-handling-DWARF-v5-r.patch --]
[-- Type: application/octet-stream, Size: 22558 bytes --]

From f75fbfd7a800cea7a4f1c5a43d40948061afdb82 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice@google.com>
Date: Wed, 1 Jul 2020 12:39:08 -0700
Subject: [PATCH v3] Update GDB to fix issues with handling DWARF v5 rnglists &
 .dwo files.

While experimenting with GDB on DWARF 5 with split debug (dwo files),
I discovered that GDB was not reading the rnglist index
properly (it needed to be reprocessed in the same way the loclist
index does), and that there was no code for reading rnglists out of
dwo files at all.  Also, the rnglist address reading function
(dwarf2_rnglists_process) was adding the base address to all rnglist
entries, when it's only supposed to add it to the DW_RLE_offset_pair
entries (http://dwarfstd.org/doc/DWARF5.pdf, p. 53), and was not
handling several entry types.

- Added 'reprocessing' for reading rnglist index (as is done for loclist
  index).
- Added code for reading rnglists out of .dwo files.
- Added several missing rnglist forms to dwarf2_rnglists_process.
- Fixed bug that was alwayas adding base address for rnglists (only
  one form needs that).
- Updated dwarf2_rnglists_process to read rnglist out of dwo file when
  appropriate.
- Added new functions cu_debug_rnglist_section & read_rnglist_index.
- Added new testcase, dw5-rnglist-test.{cc,exp}
---
 gdb/dwarf2/read.c                             | 204 +++++++++++++++---
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc  |  97 +++++++++
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp |  40 ++++
 3 files changed, 313 insertions(+), 28 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4622d14a05..1d89c81e0d 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -340,6 +340,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +356,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -650,6 +652,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1379,11 +1382,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
 static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
+			       struct dwarf2_cu *, dwarf2_psymtab *,
+			       dwarf_tag);
 
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglists_section
+  (struct dwarf2_cu *cu);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12448,6 +12456,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13792,7 +13805,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13802,17 +13815,33 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  bool ignore_dwo_unit = false;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  /* Make sure we read the .debug_rnglists section from the file that
+     contains the DW_AT_ranges attribute we are reading.  Normally that
+     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
+     we always want to read from objfile/linked program (which would be the
+     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
+  if (tag == DW_TAG_compile_unit)
+    ignore_dwo_unit = true;
+
+  if (cu->dwo_unit != nullptr && !ignore_dwo_unit)
+      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
+  else
+      rnglists_section = &per_objfile->per_bfd->rnglists;
+
+  rnglists_section->read (objfile);
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13820,8 +13849,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13843,6 +13872,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13861,6 +13895,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13889,6 +13936,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
@@ -13898,14 +13958,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (rlet == DW_RLE_base_address)
 	continue;
 
-      if (!base.has_value ())
-	{
-	  /* We have no valid base address for the ranges
-	     data.  */
-	  complaint (_("Invalid .debug_rnglists data (no base address)"));
-	  return false;
-	}
-
       if (range_beginning > range_end)
 	{
 	  /* Inverted range entries are invalid.  */
@@ -13917,8 +13969,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  if (!base.has_value ())
+	    {
+	      /* We have no valid base address for the ranges
+		 data.  */
+	      complaint (_("Invalid .debug_rnglists data (no base address)"));
+	      return false;
+	    }
+
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13951,7 +14015,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13967,7 +14031,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14053,7 +14117,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14063,7 +14127,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14156,7 +14220,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
 	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14164,7 +14229,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14327,7 +14393,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
 	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14335,7 +14402,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18165,6 +18232,11 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
     read_attribute_reprocess (reader, &die->attrs[index]);
   *diep = die;
@@ -18684,7 +18756,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
 	       but that requires a full DIE, so instead we just
 	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18693,7 +18766,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19094,6 +19167,57 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
+     : LOCLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
+  ULONGEST start_offset =
+    rnglist_base + rnglist_index * cu->header.offset_size;
+
+  /* Get rnglists section.  */
+  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu);
+  if (section == nullptr)
+    error (_("Cannot find .debug_rnglists section [in module %s]"),
+	   objfile_name(objfile));
+
+  /* Read the rnglists section content.  */
+  section->read (objfile);
+  if (section->buffer == NULL)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+       objfile_name (objfile));
+
+  /* Verify the rnglist header is valid (same as loclist header).  */
+  struct loclist_header header;
+  read_loclist_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	    objfile_name(objfile));
+
+  /* Validate that the offset is within the section's range.  */
+  if (start_offset >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+          objfile_name(objfile));
+
+  const gdb_byte *info_ptr = section->buffer + start_offset;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
@@ -19112,6 +19236,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr));
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19293,8 +19420,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23382,6 +23511,25 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglists_section (struct dwarf2_cu *cu)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  if (cu->dwo_unit != nullptr)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000000..81693f569d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,97 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node *
+make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void
+tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void
+inorder (node *root)
+{
+  std::vector<node *> todo;
+  todo.push_back (root);
+  while (!todo.empty())
+    {
+      node *curr = todo.back();
+      todo.pop_back(); /* break-here */
+      if (curr->visited)
+        std::cout << curr->id << " ";
+      else
+        {
+          curr->visited = true;
+          if (curr->right)
+            todo.push_back (curr->right);
+          todo.push_back (curr);
+          if (curr->left)
+            todo.push_back (curr->left);
+        }
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000000..af6c34b5dd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"
-- 
2.27.0.212.ge8ba1cc988-goog


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V3] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-03 22:47                       ` [PATCH V3] " Caroline Tice
@ 2020-07-04  5:11                         ` Simon Marchi
  2020-07-09 15:48                           ` [PATCH V4] " Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-04  5:11 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-03 6:47 p.m., Caroline Tice wrote:
> "
> 
> I have made most of your requested changes, which are in the attached
> patch.  I disagree with one of your comments, and I had a question
> about another:
> 
> First the disagreement.  You said:
> 
>>> @@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
>>>           /* It would be nice to reuse dwarf2_get_pc_bounds here,
>>>              but that requires a full DIE, so instead we just
>>>              reimplement it.  */
>>> -         int need_ranges_base = tag != DW_TAG_compile_unit;
>>> +         int need_ranges_base = (tag != DW_TAG_compile_unit
>>> +                                 && attr.form != DW_FORM_rnglistx);
>>
>> It looks like this fix is unrelated from the rest of the patch, which deals
>> about reading rnglists from dwo files.  It's better to have on fix per patch.
>> So I think this fix would deserve its own patch, with a commit message that
>> clearly explains the issue and the fix.
>>
> This is actually a required fix for dealing with rnglists.  Without
> it, when the DW_AT_ranges uses the form
> DW_FORM_rnglistx, the wrong value is calculated and the entire rnglist
> is ignored (if you turn on complaints this will happen).  So without
> this fix, I can't test any of the other code for fixing rnglists, with
> or without .dwo files.

Ah ok, I must have understood it wrong then.  I find all of this really confusing.

I'll summarize what I think I understand, please let me know if I say anything wrong.
We are dealing with two similar ways of dealing with address ranges, one is the
pre-standard GNU version here, which was using DW_AT_GNU_ranges_base:

  https://gcc.gnu.org/wiki/DebugFission

and the version in DWARF 5.

In the pre-standard version, only one range section was present, and it was in the
main linked file.  DW_AT_ranges attributes in the dwo, the form DW_FORM_sec_offset,
are actually pointing at the section in the main file, at offset

  CU's DW_AT_GNU_ranges_base value (found in the skeleton, in the main file) + DIE's DW_AT_ranges

If there is a DW_AT_ranges attribute in the skeleton CU, in the main linked file,
then it is absolute, it is _not_ added to the DW_AT_GNU_ranges_base value.  Hence
the `needs_range_base` thingy.  The `ranges_offset` variable in this function is
the direct offset to the range list to read.

In DWARF 5, we have a ranges section in the main linked file and one in the dwo.  DW_AT_ranges
attributes refer to the ranges section of the file they are in.  The DW_AT_rnglists_base
points to the beginning of the range table for that CU.  The actual value of DW_AT_ranges
attribute (when using the DW_FORM_rnglistx form) is an index in the offset table.  However,
due to the reprocessing you added in this patch, the attribute value is now in fact the
offset with `DW_AT_rnglists_base` already added.  And _that's_ the reason why
DW_FORM_rnglistx attributes should not have the range base applied.

I think the comment above in the code is misleading and needs to be updated, this one:

	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
	     We take advantage of the fact that DW_AT_ranges does not appear
	     in DW_TAG_compile_unit of DWO files.  */

In commit 18a8505e38fc ("Dwarf 5: Handle debug_str_offsets and indexed attributes
that have base offsets."), the it was changed like so:

-         /* DW_AT_ranges_base does not apply to DIEs from the DWO skeleton.
+         /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.

I think it was a wrong change, because it mixes two different things.  DW_AT_rnglists_base
is a DWARF 5 thing, and the range base not applying to the values from the skeleton is a
pre-standard thing.  I think it should say something like:

	  /* DW_AT_GNU_ranges_base does not apply to DIEs from the DWO skeleton.
	     We take advantage of the fact that DW_AT_ranges does not appear
	     in DW_TAG_compile_unit of DWO files.

	     Attributes of form DW_AT_rnglistx have already had their value changed
	     by read_rnglist_index and already include DW_AT_rnglists_base, so don't
	     add the ranges base either.  */

> 
>> I'm also not sure I understand it: when the form is DW_FORM_rnglistx, don't
>> we want to sometimes apply the base coming from DW_AT_rnglists_base?
> 
> It all depends on what form is being used. If the DW_AT_ranges is
> using DW_FORM_sec_offset (which is what
> GCC generates), then you do need to add the base value.But if you
> are using the DW_FORM_rnglisx (which is what clang generates), the
> base value has already been considered/incorporated into the attribute
> so adding it in (again) causes the value to be incorrect (again, turn
> on complaints and see what happens if you try to add the base into
> something with DW_FORM_rnglistx).  From the DWARF5 spec: "[A] rnglist
> is either represented as: An index into the .debug_rnglists section
> (DW_FORM_rnglistx). The ...operand identifies an offset location
> relative to the base of that section...[or] An offset into the
> .debug_rnglists section (DW_FORM_sec_offset).  The operand consists of
> a byte offset from the beginning of the .debug_rnglists section."
> Note the DW_FORM_rnglistx is already relative to the base.

Ok.  What was not clear to me in the first pass was that read_rnglist_index
already has modified the attribute value to include DW_AT_rnglists_base.

Now, about values of class rnglist and form DW_FORM_sec_offset in DWARF 5.
The spec says this about them:

  - An offset into the .debug_rnglists section (DW_FORM_sec_offset).
  The operand consists of a byte offset from the beginning of the
  .debug_rnglists section. It is relocatable in a relocatable object file,
  and relocated in an executable or shared object file. In the 32-bit
  DWARF format, this offset is a 4-byte unsigned value; in the 64-bit
  DWARF format, it is an 8-byte unsigned value (see Section 7.4 on
  page 196).

Since it says that the value is already relocated in an executable, does it
mean we should not add the base for these, in DWARF 5?

> Now, my question:  I'm not quite clear as to whether you really want
> me to change the code in
> cu_debug_rnglists_section or not.   I wrote the code mostly by copying
> the code for loclists as closely as possible, and making whatever
> changes to that were required to make it correct for rnglists.  Is it
> better to have the code more consistent with the loclists code?If
> you really want me to make the changes there you suggest, then I will;

I think the situation is a bit different than for `cu_debug_loc_section`:
when you have a dwo, I don't think there is the possibility of having an
attribute referring to a location list in the skeleton.  So if you have a
dwo, you'll always want the loc section from the dwo.  So there, the pattern

  if (there is a dwo)
    return the dwo section;

  return the main file section;

makes more sense.  But in the case of range lists, we have attributes
in both the skeleton and the dwo that require range lists, and the right
section to return depends on where the attribute is.  That's why, when I
see this pattern:

  if (there is a dwo)
    return the dwo section;

  return the main file section;

that pattern, I find it looks wrong, because the right section to return
does not depend (only) on whether there is dwo.  As we saw, it only works
"by chance" because this is only called once for the skeleton (when
reading DW_AT_ranges) and the `cu->dwo_unit` pointer happens not to be set
up yet.  It is also one refactor away from breaking, if for some reason we
reorder some code and the `cu->dwo_unit` pointer gets initialized earlier.

> I just wanted confirmation that you really want that change (i.e.
> adding the dwarf tag and checking vs. DW_TAG_compile_unit, & calling
> it from dwarf2_rnglists_process).

I think that checking DW_TAG_compile_unit is still a bit of a hack - ideally,
the caller could just tell us if the DIE comes from a dwo or not.  But at
least it's logical and based on some DWARF knowledge.  It would certainly
require a comment to explain it, because it wouldn't be obvious.

> @@ -1379,11 +1382,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
>  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
>
>  static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> -			       struct dwarf2_cu *, dwarf2_psymtab *);
> +			       struct dwarf2_cu *, dwarf2_psymtab *,
> +			       dwarf_tag);

This declaration is unneeded and can simply be removed.  Could you push an
obvious patch that does this?.


> @@ -13917,8 +13969,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>        if (range_beginning == range_end)
>  	continue;
>
> -      range_beginning += *base;
> -      range_end += *base;
> +      /* Only DW_RLE_offset_pair needs the base address added.  */
> +      if (rlet == DW_RLE_offset_pair)
> +	{
> +	  if (!base.has_value ())
> +	    {
> +	      /* We have no valid base address for the ranges
> +		 data.  */
> +	      complaint (_("Invalid .debug_rnglists data (no base address)"));

The comment fits on a single line.  But it can probably be changed to be more
specific to DW_RLE_offset_pair.  Maybe the complaint could be more specific too,
and mention DW_RLE_offset_pair.  It would be valid to have .debug_rnglists without
a base address (I think?), but here it's invalid because there is not base address
_and_ we have encountered a DW_RLE_offset_pair.

> @@ -19094,6 +19167,57 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
>      return bfd_get_64 (abfd, info_ptr) + loclist_base;
>  }
>
> +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
> +   array of offsets in the .debug_rnglists section.  */
> +static CORE_ADDR
> +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
> +{
> +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> +  bfd *abfd = objfile->obfd;
> +  ULONGEST rnglist_header_size =
> +    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
> +     : LOCLIST_HEADER_SIZE64);

Should this really refer to loclist header sizes?  If they are the same
sizes as range list table headers, I suppose it's just a coincidence.

> +  ULONGEST rnglist_base =
> +      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;

cu->dwo_unit != nullptr

> +  ULONGEST start_offset =
> +    rnglist_base + rnglist_index * cu->header.offset_size;
> +
> +  /* Get rnglists section.  */
> +  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu);
> +  if (section == nullptr)
> +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> +	   objfile_name(objfile));

Space before paren.

> +
> +  /* Read the rnglists section content.  */
> +  section->read (objfile);
> +  if (section->buffer == NULL)

In new code, I suggest using nullptr instead of NULL, just for consistency.

> +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> +	     "[in module %s]"),
> +       objfile_name (objfile));
> +
> +  /* Verify the rnglist header is valid (same as loclist header).  */
> +  struct loclist_header header;
> +  read_loclist_header (&header, section);

Ok well, if we are going to take advantage that they are the same, the name
of the function and types should reflect it.  `read_loclist_header` should
become `read_loclists_rnglists_header` (using the plural to match the section
names).

> +  if (rnglist_index >= header.offset_entry_count)
> +    error (_("DW_FORM_rnglistx index pointing outside of "
> +	     ".debug_rnglists offset array [in module %s]"),
> +	    objfile_name(objfile));

Space before paren.

> +
> +  /* Validate that the offset is within the section's range.  */
> +  if (start_offset >= section->size)
> +    error (_("DW_FORM_rnglistx pointing outside of "
> +             ".debug_rnglists section [in module %s]"),
> +          objfile_name(objfile));
> +
> +  const gdb_byte *info_ptr = section->buffer + start_offset;
> +
> +  if (cu->header.offset_size == 4)
> +    return read_4_bytes (abfd, info_ptr) + rnglist_base;
> +  else
> +    return read_8_bytes (abfd, info_ptr) + rnglist_base;

An idea for a subsequent cleanup, we could have a `read_offset` function
that does

  if (cu->header.offset_size == 4)
    return read_4_bytes (abfd, info_ptr);
  else
    return read_8_bytes (abfd, info_ptr);

There are a few spots that could use it.

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V4] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-04  5:11                         ` Simon Marchi
@ 2020-07-09 15:48                           ` Caroline Tice
  2020-07-11 17:54                             ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-09 15:48 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

[-- Attachment #1: Type: text/plain, Size: 16219 bytes --]

I think your description of how ranges work is correct.  I've made all
your requested changes (I think).  Attached is the latest patch for
review. :-)

-- Caroline
cmtice@google.com

gdb/ChangeLog:

2020-07-09  Caroline Tice  <cmtice@google.com>

       * dwarf2/read.c (RNGLIST_HEADER_SIZE32) New constant definition.
        (RNGLIST_HEADER_SIZE64): New constant definition.
        (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct dwo_sections): Add rnglists field.
        (read_attribut_reprocess): Add tag parameter.
        (dwarf2_ranges_read): Add tag parameter.
        (cu_debug_rnglists_section): New function (decl & definition).
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind of
        die whose range is being checked; add code to read the rnglist section
        from the dwo file rather than from the main objfile, if appropriate.
        Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges), moving
        test inside if-condition and updating complaint message.
        (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
        dwarf2_rnglists_process.
        (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
        dwarf2_ranges_process.
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to dwarf2_ranges_read.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to dwarf2_ranges_read.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.  Also pass die tag to read_attribute_reprocess.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to read_attribute_reprocess and dwarf2_ranges_read.
        (read_loclist_header): Rename function to read_loclists_rnglists_header,
        and update function comment appropriately.
        (read_loclist_index): Call read_loclists_rnglists_header instead of
        read_loclist_header.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add tag parameter. Add code for
        DW_FORM_rnglistx, passing tag to read_rnglist_index.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog:

2020-07-09  Caroline Tice  <cmtice@google.com>

        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.

On Fri, Jul 3, 2020 at 10:11 PM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-07-03 6:47 p.m., Caroline Tice wrote:
> > "
> >
> > I have made most of your requested changes, which are in the attached
> > patch.  I disagree with one of your comments, and I had a question
> > about another:
> >
> > First the disagreement.  You said:
> >
> >>> @@ -18684,7 +18764,8 @@ partial_die_info::read (const struct die_reader_specs *reader,
> >>>           /* It would be nice to reuse dwarf2_get_pc_bounds here,
> >>>              but that requires a full DIE, so instead we just
> >>>              reimplement it.  */
> >>> -         int need_ranges_base = tag != DW_TAG_compile_unit;
> >>> +         int need_ranges_base = (tag != DW_TAG_compile_unit
> >>> +                                 && attr.form != DW_FORM_rnglistx);
> >>
> >> It looks like this fix is unrelated from the rest of the patch, which deals
> >> about reading rnglists from dwo files.  It's better to have on fix per patch.
> >> So I think this fix would deserve its own patch, with a commit message that
> >> clearly explains the issue and the fix.
> >>
> > This is actually a required fix for dealing with rnglists.  Without
> > it, when the DW_AT_ranges uses the form
> > DW_FORM_rnglistx, the wrong value is calculated and the entire rnglist
> > is ignored (if you turn on complaints this will happen).  So without
> > this fix, I can't test any of the other code for fixing rnglists, with
> > or without .dwo files.
>
> Ah ok, I must have understood it wrong then.  I find all of this really confusing.
>
> I'll summarize what I think I understand, please let me know if I say anything wrong.
> We are dealing with two similar ways of dealing with address ranges, one is the
> pre-standard GNU version here, which was using DW_AT_GNU_ranges_base:
>
>   https://gcc.gnu.org/wiki/DebugFission
>
> and the version in DWARF 5.
>
> In the pre-standard version, only one range section was present, and it was in the
> main linked file.  DW_AT_ranges attributes in the dwo, the form DW_FORM_sec_offset,
> are actually pointing at the section in the main file, at offset
>
>   CU's DW_AT_GNU_ranges_base value (found in the skeleton, in the main file) + DIE's DW_AT_ranges
>
> If there is a DW_AT_ranges attribute in the skeleton CU, in the main linked file,
> then it is absolute, it is _not_ added to the DW_AT_GNU_ranges_base value.  Hence
> the `needs_range_base` thingy.  The `ranges_offset` variable in this function is
> the direct offset to the range list to read.
>
> In DWARF 5, we have a ranges section in the main linked file and one in the dwo.  DW_AT_ranges
> attributes refer to the ranges section of the file they are in.  The DW_AT_rnglists_base
> points to the beginning of the range table for that CU.  The actual value of DW_AT_ranges
> attribute (when using the DW_FORM_rnglistx form) is an index in the offset table.  However,
> due to the reprocessing you added in this patch, the attribute value is now in fact the
> offset with `DW_AT_rnglists_base` already added.  And _that's_ the reason why
> DW_FORM_rnglistx attributes should not have the range base applied.
>
> I think the comment above in the code is misleading and needs to be updated, this one:
>
>           /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
>              We take advantage of the fact that DW_AT_ranges does not appear
>              in DW_TAG_compile_unit of DWO files.  */
>
> In commit 18a8505e38fc ("Dwarf 5: Handle debug_str_offsets and indexed attributes
> that have base offsets."), the it was changed like so:
>
> -         /* DW_AT_ranges_base does not apply to DIEs from the DWO skeleton.
> +         /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
>
> I think it was a wrong change, because it mixes two different things.  DW_AT_rnglists_base
> is a DWARF 5 thing, and the range base not applying to the values from the skeleton is a
> pre-standard thing.  I think it should say something like:
>
>           /* DW_AT_GNU_ranges_base does not apply to DIEs from the DWO skeleton.
>              We take advantage of the fact that DW_AT_ranges does not appear
>              in DW_TAG_compile_unit of DWO files.
>
>              Attributes of form DW_AT_rnglistx have already had their value changed
>              by read_rnglist_index and already include DW_AT_rnglists_base, so don't
>              add the ranges base either.  */
>
> >
> >> I'm also not sure I understand it: when the form is DW_FORM_rnglistx, don't
> >> we want to sometimes apply the base coming from DW_AT_rnglists_base?
> >
> > It all depends on what form is being used. If the DW_AT_ranges is
> > using DW_FORM_sec_offset (which is what
> > GCC generates), then you do need to add the base value.But if you
> > are using the DW_FORM_rnglisx (which is what clang generates), the
> > base value has already been considered/incorporated into the attribute
> > so adding it in (again) causes the value to be incorrect (again, turn
> > on complaints and see what happens if you try to add the base into
> > something with DW_FORM_rnglistx).  From the DWARF5 spec: "[A] rnglist
> > is either represented as: An index into the .debug_rnglists section
> > (DW_FORM_rnglistx). The ...operand identifies an offset location
> > relative to the base of that section...[or] An offset into the
> > .debug_rnglists section (DW_FORM_sec_offset).  The operand consists of
> > a byte offset from the beginning of the .debug_rnglists section."
> > Note the DW_FORM_rnglistx is already relative to the base.
>
> Ok.  What was not clear to me in the first pass was that read_rnglist_index
> already has modified the attribute value to include DW_AT_rnglists_base.
>
> Now, about values of class rnglist and form DW_FORM_sec_offset in DWARF 5.
> The spec says this about them:
>
>   - An offset into the .debug_rnglists section (DW_FORM_sec_offset).
>   The operand consists of a byte offset from the beginning of the
>   .debug_rnglists section. It is relocatable in a relocatable object file,
>   and relocated in an executable or shared object file. In the 32-bit
>   DWARF format, this offset is a 4-byte unsigned value; in the 64-bit
>   DWARF format, it is an 8-byte unsigned value (see Section 7.4 on
>   page 196).
>
> Since it says that the value is already relocated in an executable, does it
> mean we should not add the base for these, in DWARF 5?
>
> > Now, my question:  I'm not quite clear as to whether you really want
> > me to change the code in
> > cu_debug_rnglists_section or not.   I wrote the code mostly by copying
> > the code for loclists as closely as possible, and making whatever
> > changes to that were required to make it correct for rnglists.  Is it
> > better to have the code more consistent with the loclists code?If
> > you really want me to make the changes there you suggest, then I will;
>
> I think the situation is a bit different than for `cu_debug_loc_section`:
> when you have a dwo, I don't think there is the possibility of having an
> attribute referring to a location list in the skeleton.  So if you have a
> dwo, you'll always want the loc section from the dwo.  So there, the pattern
>
>   if (there is a dwo)
>     return the dwo section;
>
>   return the main file section;
>
> makes more sense.  But in the case of range lists, we have attributes
> in both the skeleton and the dwo that require range lists, and the right
> section to return depends on where the attribute is.  That's why, when I
> see this pattern:
>
>   if (there is a dwo)
>     return the dwo section;
>
>   return the main file section;
>
> that pattern, I find it looks wrong, because the right section to return
> does not depend (only) on whether there is dwo.  As we saw, it only works
> "by chance" because this is only called once for the skeleton (when
> reading DW_AT_ranges) and the `cu->dwo_unit` pointer happens not to be set
> up yet.  It is also one refactor away from breaking, if for some reason we
> reorder some code and the `cu->dwo_unit` pointer gets initialized earlier.
>
> > I just wanted confirmation that you really want that change (i.e.
> > adding the dwarf tag and checking vs. DW_TAG_compile_unit, & calling
> > it from dwarf2_rnglists_process).
>
> I think that checking DW_TAG_compile_unit is still a bit of a hack - ideally,
> the caller could just tell us if the DIE comes from a dwo or not.  But at
> least it's logical and based on some DWARF knowledge.  It would certainly
> require a comment to explain it, because it wouldn't be obvious.
>
> > @@ -1379,11 +1382,16 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
> >  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
> >
> >  static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> > -                            struct dwarf2_cu *, dwarf2_psymtab *);
> > +                            struct dwarf2_cu *, dwarf2_psymtab *,
> > +                            dwarf_tag);
>
> This declaration is unneeded and can simply be removed.  Could you push an
> obvious patch that does this?.
>
>
> > @@ -13917,8 +13969,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
> >        if (range_beginning == range_end)
> >       continue;
> >
> > -      range_beginning += *base;
> > -      range_end += *base;
> > +      /* Only DW_RLE_offset_pair needs the base address added.  */
> > +      if (rlet == DW_RLE_offset_pair)
> > +     {
> > +       if (!base.has_value ())
> > +         {
> > +           /* We have no valid base address for the ranges
> > +              data.  */
> > +           complaint (_("Invalid .debug_rnglists data (no base address)"));
>
> The comment fits on a single line.  But it can probably be changed to be more
> specific to DW_RLE_offset_pair.  Maybe the complaint could be more specific too,
> and mention DW_RLE_offset_pair.  It would be valid to have .debug_rnglists without
> a base address (I think?), but here it's invalid because there is not base address
> _and_ we have encountered a DW_RLE_offset_pair.
>
> > @@ -19094,6 +19167,57 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
> >      return bfd_get_64 (abfd, info_ptr) + loclist_base;
> >  }
> >
> > +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
> > +   array of offsets in the .debug_rnglists section.  */
> > +static CORE_ADDR
> > +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index)
> > +{
> > +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> > +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> > +  bfd *abfd = objfile->obfd;
> > +  ULONGEST rnglist_header_size =
> > +    (cu->header.initial_length_size == 4 ? LOCLIST_HEADER_SIZE32
> > +     : LOCLIST_HEADER_SIZE64);
>
> Should this really refer to loclist header sizes?  If they are the same
> sizes as range list table headers, I suppose it's just a coincidence.
>
> > +  ULONGEST rnglist_base =
> > +      (cu->dwo_unit) ? rnglist_header_size : cu->ranges_base;
>
> cu->dwo_unit != nullptr
>
> > +  ULONGEST start_offset =
> > +    rnglist_base + rnglist_index * cu->header.offset_size;
> > +
> > +  /* Get rnglists section.  */
> > +  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu);
> > +  if (section == nullptr)
> > +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> > +        objfile_name(objfile));
>
> Space before paren.
>
> > +
> > +  /* Read the rnglists section content.  */
> > +  section->read (objfile);
> > +  if (section->buffer == NULL)
>
> In new code, I suggest using nullptr instead of NULL, just for consistency.
>
> > +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> > +          "[in module %s]"),
> > +       objfile_name (objfile));
> > +
> > +  /* Verify the rnglist header is valid (same as loclist header).  */
> > +  struct loclist_header header;
> > +  read_loclist_header (&header, section);
>
> Ok well, if we are going to take advantage that they are the same, the name
> of the function and types should reflect it.  `read_loclist_header` should
> become `read_loclists_rnglists_header` (using the plural to match the section
> names).
>
> > +  if (rnglist_index >= header.offset_entry_count)
> > +    error (_("DW_FORM_rnglistx index pointing outside of "
> > +          ".debug_rnglists offset array [in module %s]"),
> > +         objfile_name(objfile));
>
> Space before paren.
>
> > +
> > +  /* Validate that the offset is within the section's range.  */
> > +  if (start_offset >= section->size)
> > +    error (_("DW_FORM_rnglistx pointing outside of "
> > +             ".debug_rnglists section [in module %s]"),
> > +          objfile_name(objfile));
> > +
> > +  const gdb_byte *info_ptr = section->buffer + start_offset;
> > +
> > +  if (cu->header.offset_size == 4)
> > +    return read_4_bytes (abfd, info_ptr) + rnglist_base;
> > +  else
> > +    return read_8_bytes (abfd, info_ptr) + rnglist_base;
>
> An idea for a subsequent cleanup, we could have a `read_offset` function
> that does
>
>   if (cu->header.offset_size == 4)
>     return read_4_bytes (abfd, info_ptr);
>   else
>     return read_8_bytes (abfd, info_ptr);
>
> There are a few spots that could use it.
>
> Simon

[-- Attachment #2: v4-0001-Update-GDB-to-fix-issues-with-handling-DWARF-v5-r.patch --]
[-- Type: application/octet-stream, Size: 26969 bytes --]

From f88972d75ec7d6b5efc596ed8f316d141fbafffa Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice@google.com>
Date: Wed, 1 Jul 2020 12:39:08 -0700
Subject: [PATCH v4] Update GDB to fix issues with handling DWARF v5 rnglists &
 .dwo files.

While experimenting with GDB on DWARF 5 with split debug (dwo files),
I discovered that GDB was not reading the rnglist index
properly (it needed to be reprocessed in the same way the loclist
index does), and that there was no code for reading rnglists out of
dwo files at all.  Also, the rnglist address reading function
(dwarf2_rnglists_process) was adding the base address to all rnglist
entries, when it's only supposed to add it to the DW_RLE_offset_pair
entries (http://dwarfstd.org/doc/DWARF5.pdf, p. 53), and was not
handling several entry types.

- Added 'reprocessing' for reading rnglist index (as is done for loclist
  index).
- Added code for reading rnglists out of .dwo files.
- Added several missing rnglist forms to dwarf2_rnglists_process.
- Fixed bug that was alwayas adding base address for rnglists (only
  one form needs that).
- Updated dwarf2_rnglists_process to read rnglist out of dwo file when
  appropriate.
- Added new functions cu_debug_rnglist_section & read_rnglist_index.
- Added new testcase, dw5-rnglist-test.{cc,exp}
---
 gdb/dwarf2/read.c                             | 259 +++++++++++++++---
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc  |  97 +++++++
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp |  40 +++
 3 files changed, 352 insertions(+), 44 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 405b5fb334..b1f54fb8bf 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -132,6 +132,12 @@ static int dwarf2_loclist_block_index;
 /* Size of .debug_loclists section header for 64-bit DWARF format.  */
 #define LOCLIST_HEADER_SIZE64 20
 
+/* Size of .debug_rnglists section header for 32-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE32 12
+
+/* Size of .debug_rnglists section header for 64-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE64 20
+
 /* An index into a (C++) symbol name component in a symbol name as
    recorded in the mapped_index's symbol table.  For each C++ symbol
    in the symbol table, we record one entry for the start of each
@@ -340,6 +346,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +362,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -650,6 +658,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1274,7 +1283,7 @@ static const gdb_byte *read_attribute (const struct die_reader_specs *,
 				       const gdb_byte *, bool *need_reprocess);
 
 static void read_attribute_reprocess (const struct die_reader_specs *reader,
-				      struct attribute *attr);
+				      struct attribute *attr, dwarf_tag tag);
 
 static CORE_ADDR read_addr_index (struct dwarf2_cu *cu, unsigned int addr_index);
 
@@ -1378,12 +1387,13 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
-static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
-
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglists_section
+(struct dwarf2_cu *cu, dwarf_tag tag);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12448,6 +12458,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13792,7 +13807,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13802,17 +13817,33 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  bool ignore_dwo_unit = false;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  /* Make sure we read the .debug_rnglists section from the file that
+     contains the DW_AT_ranges attribute we are reading.  Normally that
+     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
+     we always want to read from objfile/linked program (which would be the
+     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
+  if (tag == DW_TAG_compile_unit)
+    ignore_dwo_unit = true;
+
+  if (cu->dwo_unit != nullptr && !ignore_dwo_unit)
+      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
+  else
+      rnglists_section = &per_objfile->per_bfd->rnglists;
+
+  rnglists_section->read (objfile);
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13820,8 +13851,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13843,6 +13874,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13861,6 +13897,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13889,6 +13938,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
@@ -13898,14 +13960,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (rlet == DW_RLE_base_address)
 	continue;
 
-      if (!base.has_value ())
-	{
-	  /* We have no valid base address for the ranges
-	     data.  */
-	  complaint (_("Invalid .debug_rnglists data (no base address)"));
-	  return false;
-	}
-
       if (range_beginning > range_end)
 	{
 	  /* Inverted range entries are invalid.  */
@@ -13917,8 +13971,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  if (!base.has_value ())
+	    {
+	      /* We have no valid base address for the DW_RLE_offset_pair.  */
+	      complaint (_("Invalid .debug_rnglists data (no base address for "
+			   "DW_RLE_offset_pair)"));
+	      return false;
+	    }
+
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13951,7 +14017,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13967,7 +14033,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14053,7 +14119,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14063,7 +14129,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14155,8 +14221,14 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	{
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
-	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	     in DW_TAG_compile_unit of DWO files.
+
+	     Attributes of the form DW_FORM_rnglistx have already had their
+	     value changed by read_rnglist_index and already include
+	     DW_AT_rnglists_base, so don't need to add the ranges base,
+	     either.  */
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14164,7 +14236,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14326,8 +14399,14 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
     {
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
-	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	 in DW_TAG_compile_unit of DWO files.
+
+	 Attributes of the form DW_FORM_rnglistx have already had their
+	 value changed by read_rnglist_index and already include
+	 DW_AT_rnglists_base, so don't need to add the ranges base,
+	 either.  */
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14335,7 +14414,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18165,8 +18244,13 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
-    read_attribute_reprocess (reader, &die->attrs[index]);
+    read_attribute_reprocess (reader, &die->attrs[index], die->tag);
   *diep = die;
   return info_ptr;
 }
@@ -18535,7 +18619,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
          already been read at this point, so there is no need to wait until
 	 the loop terminates to do the reprocessing.  */
       if (need_reprocess)
-	read_attribute_reprocess (reader, &attr);
+	read_attribute_reprocess (reader, &attr, tag);
       /* Store the data if it is of an attribute we want to keep in a
          partial symbol table.  */
       switch (attr.name)
@@ -18681,10 +18765,16 @@ partial_die_info::read (const struct die_reader_specs *reader,
 
 	case DW_AT_ranges:
 	  {
-	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
-	       but that requires a full DIE, so instead we just
-	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    /* DW_AT_rnglists_base does not apply to DIEs from the DWO
+	       skeleton.  We take advantage of the fact the DW_AT_ranges
+	       does not appear in DW_TAG_compile_unit of DWO files.
+
+	       Attributes of the form DW_FORM_rnglistx have already had
+               their value changed by read_rnglist_index and already
+	       include DW_AT_rnglists_base, so don't need to add the ranges
+	       base, either.  */
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18693,7 +18783,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19017,11 +19107,11 @@ partial_die_info::fixup (struct dwarf2_cu *cu)
   fixup_called = 1;
 }
 
-/* Read the .debug_loclists header contents from the given SECTION in the
-   HEADER.  */
+/* Read the .debug_loclists or .debug_rnglists header (they are the same format)
+   contents from the given SECTION in the HEADER.  */
 static void
-read_loclist_header (struct loclist_header *header,
-		      struct dwarf2_section_info *section)
+read_loclists_rnglists_header (struct loclist_header *header,
+			       struct dwarf2_section_info *section)
 {
   unsigned int bytes_read;
   bfd *abfd = section->get_bfd_owner ();
@@ -19075,7 +19165,7 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     complaint (_("DW_FORM_loclistx used without .debug_loclists "
 	        "section [in module %s]"), objfile_name (objfile));
   struct loclist_header header;
-  read_loclist_header (&header, section);
+  read_loclists_rnglists_header (&header, section);
   if (loclist_index >= header.offset_entry_count)
     complaint (_("DW_FORM_loclistx pointing outside of "
 	        ".debug_loclists offset array [in module %s]"),
@@ -19094,13 +19184,65 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
+		    dwarf_tag tag)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
+     : RNGLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
+  ULONGEST start_offset =
+    rnglist_base + rnglist_index * cu->header.offset_size;
+
+  /* Get rnglists section.  */
+  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu, tag);
+  if (section == nullptr)
+    error (_("Cannot find .debug_rnglists section [in module %s]"),
+	   objfile_name (objfile));
+
+  /* Read the rnglists section content.  */
+  section->read (objfile);
+  if (section->buffer == nullptr)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+       objfile_name (objfile));
+
+  /* Verify the rnglist header is valid (same as loclist header).  */
+  struct loclist_header header;
+  read_loclists_rnglists_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	    objfile_name (objfile));
+
+  /* Validate that the offset is within the section's range.  */
+  if (start_offset >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+          objfile_name (objfile));
+
+  const gdb_byte *info_ptr = section->buffer + start_offset;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
    the values of str_offsets_base or addr_base may not have been known.  */
 static void
 read_attribute_reprocess (const struct die_reader_specs *reader,
-			  struct attribute *attr)
+			  struct attribute *attr, dwarf_tag tag)
 {
   struct dwarf2_cu *cu = reader->cu;
   switch (attr->form)
@@ -19112,6 +19254,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr), tag);
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19293,8 +19438,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23383,6 +23530,30 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglists_section (struct dwarf2_cu *cu, dwarf_tag tag)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  /* Make sure we read the .debug_rnglists section from the file that
+     contains the DW_AT_ranges attribute we are reading.  Normally that
+     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
+     we always want to read from objfile/linked program (which would be the
+     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
+  if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000000..81693f569d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,97 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node *
+make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void
+tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void
+inorder (node *root)
+{
+  std::vector<node *> todo;
+  todo.push_back (root);
+  while (!todo.empty())
+    {
+      node *curr = todo.back();
+      todo.pop_back(); /* break-here */
+      if (curr->visited)
+        std::cout << curr->id << " ";
+      else
+        {
+          curr->visited = true;
+          if (curr->right)
+            todo.push_back (curr->right);
+          todo.push_back (curr);
+          if (curr->left)
+            todo.push_back (curr->left);
+        }
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000000..af6c34b5dd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"
-- 
2.27.0.383.g050319c2ae-goog


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V4] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-09 15:48                           ` [PATCH V4] " Caroline Tice
@ 2020-07-11 17:54                             ` Simon Marchi
  2020-07-14 15:47                               ` [PATCH V5] " Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-11 17:54 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

> @@ -1378,12 +1387,13 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
>
>  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
>
> -static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> -			       struct dwarf2_cu *, dwarf2_psymtab *);
> -
>  /* Return the .debug_loclists section to use for cu.  */
>  static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
>
> +/* Return the .debug_rnglists section to use for cu.  */
> +static struct dwarf2_section_info *cu_debug_rnglists_section
> +(struct dwarf2_cu *cu, dwarf_tag tag);

Indent this last line with two spaces.

> @@ -13802,17 +13817,33 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>    const gdb_byte *buffer;
>    CORE_ADDR baseaddr;
>    bool overflow = false;
> +  ULONGEST addr_index;
> +  bool ignore_dwo_unit = false;
> +  struct dwarf2_section_info *rnglists_section;
>
>    base = cu->base_address;
>
> -  per_objfile->per_bfd->rnglists.read (objfile);
> -  if (offset >= per_objfile->per_bfd->rnglists.size)
> +  /* Make sure we read the .debug_rnglists section from the file that
> +     contains the DW_AT_ranges attribute we are reading.  Normally that
> +     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
> +     we always want to read from objfile/linked program (which would be the
> +     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
> +  if (tag == DW_TAG_compile_unit)
> +    ignore_dwo_unit = true;
> +
> +  if (cu->dwo_unit != nullptr && !ignore_dwo_unit)
> +      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
> +  else
> +      rnglists_section = &per_objfile->per_bfd->rnglists;

If think you can omit the `ignore_dwo_unit` variable and write this directly,
without loss of readability:

  if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)

Also, should this use cu_debug_rnglists_section?

> @@ -19094,13 +19184,65 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
>      return bfd_get_64 (abfd, info_ptr) + loclist_base;
>  }
>
> +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the

rnglist_index -> RNGLIST_INDEX

> +   array of offsets in the .debug_rnglists section.  */
> +static CORE_ADDR
> +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
> +		    dwarf_tag tag)
> +{
> +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> +  bfd *abfd = objfile->obfd;
> +  ULONGEST rnglist_header_size =
> +    (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
> +     : RNGLIST_HEADER_SIZE64);
> +  ULONGEST rnglist_base =
> +      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
> +  ULONGEST start_offset =
> +    rnglist_base + rnglist_index * cu->header.offset_size;
> +
> +  /* Get rnglists section.  */
> +  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu, tag);
> +  if (section == nullptr)
> +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> +	   objfile_name (objfile));

As of now, cu_debug_rnglists_section can't return nullptr.  And I think it should
stay this way, it throws an error if it can't return a valid section.

> +
> +  /* Read the rnglists section content.  */
> +  section->read (objfile);
> +  if (section->buffer == nullptr)
> +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> +	     "[in module %s]"),
> +       objfile_name (objfile));

Align `objfile_name` with the opening parenthesis.

> +
> +  /* Verify the rnglist header is valid (same as loclist header).  */

Is this comment accurate?  The code validates rnglist_index, not the header, right?

> +  struct loclist_header header;

Rename this type `loclists_rnglists_header`, to match the function.

> +  read_loclists_rnglists_header (&header, section);
> +  if (rnglist_index >= header.offset_entry_count)
> +    error (_("DW_FORM_rnglistx index pointing outside of "
> +	     ".debug_rnglists offset array [in module %s]"),
> +	    objfile_name (objfile));

Same here.

> +
> +  /* Validate that the offset is within the section's range.  */
> +  if (start_offset >= section->size)

Pendantically, I suppose we should verify not only that the start_offset
is within the section's range, but also that there are enough bytes to read?

For example, if we have a 100 bytes-long section, and start_offset is 99, we
won't be able to read 4 or 8 bytes.

> +    error (_("DW_FORM_rnglistx pointing outside of "
> +             ".debug_rnglists section [in module %s]"),
> +          objfile_name (objfile));

Same here.


> @@ -23383,6 +23530,30 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
>  				  : &per_objfile->per_bfd->loc);
>  }
>
> +/* Return the .debug_rnglists section to use for CU.  */
> +static struct dwarf2_section_info *
> +cu_debug_rnglists_section (struct dwarf2_cu *cu, dwarf_tag tag)
> +{
> +  if (cu->header.version < 5)
> +    error (_(".debug_rnglists section cannot be used in DWARF %d"),
> +	   cu->header.version);
> +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> +
> +  /* Make sure we read the .debug_rnglists section from the file that
> +     contains the DW_AT_ranges attribute we are reading.  Normally that
> +     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
> +     we always want to read from objfile/linked program (which would be the
> +     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
> +  if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
> +    {
> +      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
> +
> +      if (sections->rnglists.size > 0)
> +	return &sections->rnglists;
> +    }
> +  return &dwarf2_per_objfile->per_bfd->rnglists;

I expressed a concern about this earlier: if an attribute in the .dwo is of form
DW_FORM_rnglistx, but for some reason the .dwo has no .debug_rnglist section, what
happens?  It seems to me like we're going to return the section from the executable,
which would be wrong.

Also, I just debugged this function, and saw that it is called in this context:

#0  cu_debug_rnglists_section (cu=0x615000026c80, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:23557
#1  0x0000557c4612cde5 in read_rnglist_index (cu=0x615000026c80, rnglist_index=0, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:19205
#2  0x0000557c4612d66e in read_attribute_reprocess (reader=0x7fffb47cf240, attr=0x621000186198, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:19258
#3  0x0000557c4612152d in read_full_die_1 (reader=0x7fffb47cf240, diep=0x7fffb47cf280, info_ptr=0x60400001e8c0 "", num_extra_attrs=0) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:18253
#4  0x0000557c461217d9 in read_full_die (reader=0x7fffb47cf240, diep=0x7fffb47cf280, info_ptr=0x60400001e8a4 "\001") at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:18268
#5  0x0000557c46094af2 in cutu_reader::cutu_reader (this=0x7fffb47cf240, this_cu=0x621000183910, per_objfile=0x61300000a840, abbrev_table=0x60b00006ff30, existing_cu=0x0, skip_partial=false) at /
home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7202
#6  0x0000557c4609b0ca in process_psymtab_comp_unit (this_cu=0x621000183910, per_objfile=0x61300000a840, want_partial_unit=false, pretend_language=language_minimal) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7667
#7  0x0000557c460a0fd9 in dwarf2_build_psymtabs_hard (per_objfile=0x61300000a840) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:8051
#8  0x0000557c46086ae3 in dwarf2_build_psymtabs (objfile=0x614000006440) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:6106

As you can see, tag == DW_TAG_skeleton_unit.

I thought that somehow, when reading a CU that uses a DWO, we were creating
a "logical" DIE tree by combining the DW_TAG_skeleton_unit DIE and the
children of the DWO's DW_TAG_compile_unit DIE, and while doing this,
overwriting the DW_TAG_skeleton_unit's DIE to use the DW_TAG_compile_unit
tag instead.  Therefore making it appear to the rest of the DWARF reader
as if it was a "standard" DW_TAG_compile_unit DIE.  But no, maybe I just dreamed
all of this, or I can't find it anymore.

In fact, the reason the code was checking for DW_TAG_compile_unit must be that
in the GCC/pre-standard version, the skeleton DIE in the executable is a
DW_TAG_compile_unit.  With DWARF5, we'll see DW_TAG_skeleton_unit here.

So I believe we should use

  (tag != DW_TAG_compile_unit && tag != DW_TAG_skeleton_unit)

to cover both versions, GCC pre-standard and DWARF 5.  Does that make sense?

Wherever we use the logic:

	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
				  && attr->form != DW_FORM_rnglistx);

we should maybe check for DW_TAG_skeleton_unit as well?

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-11 17:54                             ` Simon Marchi
@ 2020-07-14 15:47                               ` Caroline Tice
  2020-07-15  2:04                                 ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-14 15:47 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

[-- Attachment #1: Type: text/plain, Size: 14794 bytes --]

"This time for sure!" -- Bullwinkle Moose

I think I've got all of your requested changes in now, and I've
attached the updated patch.   About what you said at the very end of
your last message:

> I thought that somehow, when reading a CU that uses a DWO, we were creating
> a "logical" DIE tree by combining the DW_TAG_skeleton_unit DIE and the
> children of the DWO's DW_TAG_compile_unit DIE, and while doing this,
> overwriting the DW_TAG_skeleton_unit's DIE to use the DW_TAG_compile_unit
> tag instead.  Therefore making it appear to the rest of the DWARF reader
> as if it was a "standard" DW_TAG_compile_unit DIE.  But no, maybe I just dreamed
> all of this, or I can't find it anymore.
>

Actually your first thought was absolutely correct.  This is done in
cutu_reader::cutu_reader.  In my patched
read.c this is at line 7244:

comp_unit_die = dwo_comp_unit_die;

It's 4 lines above the comment:

         /* Yikes, we couldn't find the rest of the DIE, we only have
             the stub.  A complaint has already been logged.  There's
             not much more we can do except pass on the stub DIE to
             die_reader_func.  We don't want to throw an error on bad
             debug info.  */

> In fact, the reason the code was checking for DW_TAG_compile_unit must be that
> in the GCC/pre-standard version, the skeleton DIE in the executable is a
> DW_TAG_compile_unit.  With DWARF5, we'll see DW_TAG_skeleton_unit here.
>
> So I believe we should use
>
>  (tag != DW_TAG_compile_unit && tag != DW_TAG_skeleton_unit)
>
> to cover both versions, GCC pre-standard and DWARF 5.  Does that make sense?

I agree that we need to check both cases in cu_debug_rnglists_section,
because sometimes it gets called before the line above in cutu_reader,
and sometimes it gets called after (now that I'm also calling it in
dwarf2_rnglists_process).

> Wherever we use the logic:
>
>          int need_ranges_base = (die->tag != DW_TAG_compile_unit
>                                  && attr->form != DW_FORM_rnglistx);
>
> we should maybe check for DW_TAG_skeleton_unit as well?"

I don't think there's any point in checking for DW_TAG_skeleton_unit
in the need_ranges_base checks, because I believe that all of those
checks are called after the call to cutu_reader, so we never have a
DW_TAG_skeleton_unit by the time we get to those checks.

Please review the updated, attached patch and let me know if it is ok
now.  Thanks! :-)


-- Caroline
cmtice@google.com

gdb/ChangeLog

2020-07-14  Caroline Tice  <cmtice@google.com>

        * dwarf2/read.c (RNGLIST_HEADER_SIZE32) New constant definition.
        (RNGLIST_HEADER_SIZE64): New constant definition.
        (struct dwop_section_names): Add rnglists_dwo.
        (dwop_section_names): Add .debug_rnglists.dwo, .zdebug_rnglists.dwo.
        (struct loclist_header): Rename to 'loclists_rnglists_header'.
        (struct dwo_sections): Add rnglists field.
        (read_attribut_reprocess): Add tag parameter.
        (dwarf2_ranges_read): Add tag parameter & remove forward function decl.
        (cu_debug_rnglists_section): New function (decl & definition).
        (dwarf2_locate_dwo_sections): Add code to read rnglists_dwo section.
        (dwarf2_rnglists_process): Add a dwarf_tag parameter, for the kind of
        die whose range is being checked; get rnglist section from
        cu_debug_rnglists_section, to get from either objfile or dwo file as
        appropriate.  Add cases for DW_RLE_base_addressx,
        DW_RLE_startx_length, DW_RLE_startx_endx.  Also, update to only add
        the base address to DW_RLE_offset_pairs (not to all ranges), moving
        test inside if-condition and updating complaint message.
        (dwarf2_ranges_process): Add dwarf tag parameter and pass it to
        dwarf2_rnglists_process.
        (dwarf2_ranges_read): Add dwarf tag parameter and pass it to
        dwarf2_ranges_process.
        (dwarf2_get_pc_bounds): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to dwarf2_ranges_read.
        (dwarf2_record_block_ranges): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to dwarf2_ranges_process.
        (read_full_die_1): Add code to read DW_AT_rnglists_base and assign to
        cu->ranges_base.  Also pass die tag to read_attribute_reprocess.
        (partial_die_info::read): Check for DW_FORM_rnglistx when setting
        need_ranges_base and update comment appropriately.  Also pass die tag
        to read_attribute_reprocess and dwarf2_ranges_read.
        (read_loclist_header): Rename function to read_loclists_rnglists_header,
        and update function comment appropriately.
        (read_loclist_index): Call read_loclists_rnglists_header instead of
        read_loclist_header.
        (read_rnglist_index): New function.
        (read_attribute_reprocess):  Add tag parameter. Add code for
        DW_FORM_rnglistx, passing tag to read_rnglist_index.
        (read_attribute_value): Mark DW_FORM_rnglistx with need_reprocess.

gdb/testsuite/ChangeLog:

2020-07-14  Caroline Tice  <cmtice@google.com>

        * gdb.dwarf2/dw5-rnglist-test.cc: New file.
        * gdb.dwarf2/dw5-rnglist-test.exp: New file.




-- Caroline
cmtice@google.com


On Sat, Jul 11, 2020 at 10:54 AM Simon Marchi <simark@simark.ca> wrote:
>
> > @@ -1378,12 +1387,13 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
> >
> >  static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
> >
> > -static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
> > -                            struct dwarf2_cu *, dwarf2_psymtab *);
> > -
> >  /* Return the .debug_loclists section to use for cu.  */
> >  static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
> >
> > +/* Return the .debug_rnglists section to use for cu.  */
> > +static struct dwarf2_section_info *cu_debug_rnglists_section
> > +(struct dwarf2_cu *cu, dwarf_tag tag);
>
> Indent this last line with two spaces.
>
> > @@ -13802,17 +13817,33 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
> >    const gdb_byte *buffer;
> >    CORE_ADDR baseaddr;
> >    bool overflow = false;
> > +  ULONGEST addr_index;
> > +  bool ignore_dwo_unit = false;
> > +  struct dwarf2_section_info *rnglists_section;
> >
> >    base = cu->base_address;
> >
> > -  per_objfile->per_bfd->rnglists.read (objfile);
> > -  if (offset >= per_objfile->per_bfd->rnglists.size)
> > +  /* Make sure we read the .debug_rnglists section from the file that
> > +     contains the DW_AT_ranges attribute we are reading.  Normally that
> > +     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
> > +     we always want to read from objfile/linked program (which would be the
> > +     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
> > +  if (tag == DW_TAG_compile_unit)
> > +    ignore_dwo_unit = true;
> > +
> > +  if (cu->dwo_unit != nullptr && !ignore_dwo_unit)
> > +      rnglists_section = &cu->dwo_unit->dwo_file->sections.rnglists;
> > +  else
> > +      rnglists_section = &per_objfile->per_bfd->rnglists;
>
> If think you can omit the `ignore_dwo_unit` variable and write this directly,
> without loss of readability:
>
>   if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
>
> Also, should this use cu_debug_rnglists_section?
>
> > @@ -19094,13 +19184,65 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
> >      return bfd_get_64 (abfd, info_ptr) + loclist_base;
> >  }
> >
> > +/* Given a DW_FORM_rnglistx value rnglist_index, fetch the offset from the
>
> rnglist_index -> RNGLIST_INDEX
>
> > +   array of offsets in the .debug_rnglists section.  */
> > +static CORE_ADDR
> > +read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
> > +                 dwarf_tag tag)
> > +{
> > +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> > +  struct objfile *objfile = dwarf2_per_objfile->objfile;
> > +  bfd *abfd = objfile->obfd;
> > +  ULONGEST rnglist_header_size =
> > +    (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
> > +     : RNGLIST_HEADER_SIZE64);
> > +  ULONGEST rnglist_base =
> > +      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
> > +  ULONGEST start_offset =
> > +    rnglist_base + rnglist_index * cu->header.offset_size;
> > +
> > +  /* Get rnglists section.  */
> > +  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu, tag);
> > +  if (section == nullptr)
> > +    error (_("Cannot find .debug_rnglists section [in module %s]"),
> > +        objfile_name (objfile));
>
> As of now, cu_debug_rnglists_section can't return nullptr.  And I think it should
> stay this way, it throws an error if it can't return a valid section.
>
> > +
> > +  /* Read the rnglists section content.  */
> > +  section->read (objfile);
> > +  if (section->buffer == nullptr)
> > +    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
> > +          "[in module %s]"),
> > +       objfile_name (objfile));
>
> Align `objfile_name` with the opening parenthesis.
>
> > +
> > +  /* Verify the rnglist header is valid (same as loclist header).  */
>
> Is this comment accurate?  The code validates rnglist_index, not the header, right?
>
> > +  struct loclist_header header;
>
> Rename this type `loclists_rnglists_header`, to match the function.
>
> > +  read_loclists_rnglists_header (&header, section);
> > +  if (rnglist_index >= header.offset_entry_count)
> > +    error (_("DW_FORM_rnglistx index pointing outside of "
> > +          ".debug_rnglists offset array [in module %s]"),
> > +         objfile_name (objfile));
>
> Same here.
>
> > +
> > +  /* Validate that the offset is within the section's range.  */
> > +  if (start_offset >= section->size)
>
> Pendantically, I suppose we should verify not only that the start_offset
> is within the section's range, but also that there are enough bytes to read?
>
> For example, if we have a 100 bytes-long section, and start_offset is 99, we
> won't be able to read 4 or 8 bytes.
>
> > +    error (_("DW_FORM_rnglistx pointing outside of "
> > +             ".debug_rnglists section [in module %s]"),
> > +          objfile_name (objfile));
>
> Same here.
>
>
> > @@ -23383,6 +23530,30 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
> >                                 : &per_objfile->per_bfd->loc);
> >  }
> >
> > +/* Return the .debug_rnglists section to use for CU.  */
> > +static struct dwarf2_section_info *
> > +cu_debug_rnglists_section (struct dwarf2_cu *cu, dwarf_tag tag)
> > +{
> > +  if (cu->header.version < 5)
> > +    error (_(".debug_rnglists section cannot be used in DWARF %d"),
> > +        cu->header.version);
> > +  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
> > +
> > +  /* Make sure we read the .debug_rnglists section from the file that
> > +     contains the DW_AT_ranges attribute we are reading.  Normally that
> > +     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
> > +     we always want to read from objfile/linked program (which would be the
> > +     DW_TAG_skeleton_unit DIE if we're using split dwarf).  */
> > +  if (cu->dwo_unit != nullptr && tag != DW_TAG_compile_unit)
> > +    {
> > +      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
> > +
> > +      if (sections->rnglists.size > 0)
> > +     return &sections->rnglists;
> > +    }
> > +  return &dwarf2_per_objfile->per_bfd->rnglists;
>
> I expressed a concern about this earlier: if an attribute in the .dwo is of form
> DW_FORM_rnglistx, but for some reason the .dwo has no .debug_rnglist section, what
> happens?  It seems to me like we're going to return the section from the executable,
> which would be wrong.
>
> Also, I just debugged this function, and saw that it is called in this context:
>
> #0  cu_debug_rnglists_section (cu=0x615000026c80, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:23557
> #1  0x0000557c4612cde5 in read_rnglist_index (cu=0x615000026c80, rnglist_index=0, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:19205
> #2  0x0000557c4612d66e in read_attribute_reprocess (reader=0x7fffb47cf240, attr=0x621000186198, tag=DW_TAG_skeleton_unit) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:19258
> #3  0x0000557c4612152d in read_full_die_1 (reader=0x7fffb47cf240, diep=0x7fffb47cf280, info_ptr=0x60400001e8c0 "", num_extra_attrs=0) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:18253
> #4  0x0000557c461217d9 in read_full_die (reader=0x7fffb47cf240, diep=0x7fffb47cf280, info_ptr=0x60400001e8a4 "\001") at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:18268
> #5  0x0000557c46094af2 in cutu_reader::cutu_reader (this=0x7fffb47cf240, this_cu=0x621000183910, per_objfile=0x61300000a840, abbrev_table=0x60b00006ff30, existing_cu=0x0, skip_partial=false) at /
> home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7202
> #6  0x0000557c4609b0ca in process_psymtab_comp_unit (this_cu=0x621000183910, per_objfile=0x61300000a840, want_partial_unit=false, pretend_language=language_minimal) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:7667
> #7  0x0000557c460a0fd9 in dwarf2_build_psymtabs_hard (per_objfile=0x61300000a840) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:8051
> #8  0x0000557c46086ae3 in dwarf2_build_psymtabs (objfile=0x614000006440) at /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:6106
>
> As you can see, tag == DW_TAG_skeleton_unit.
>
> I thought that somehow, when reading a CU that uses a DWO, we were creating
> a "logical" DIE tree by combining the DW_TAG_skeleton_unit DIE and the
> children of the DWO's DW_TAG_compile_unit DIE, and while doing this,
> overwriting the DW_TAG_skeleton_unit's DIE to use the DW_TAG_compile_unit
> tag instead.  Therefore making it appear to the rest of the DWARF reader
> as if it was a "standard" DW_TAG_compile_unit DIE.  But no, maybe I just dreamed
> all of this, or I can't find it anymore.
>
> In fact, the reason the code was checking for DW_TAG_compile_unit must be that
> in the GCC/pre-standard version, the skeleton DIE in the executable is a
> DW_TAG_compile_unit.  With DWARF5, we'll see DW_TAG_skeleton_unit here.
>
> So I believe we should use
>
>   (tag != DW_TAG_compile_unit && tag != DW_TAG_skeleton_unit)
>
> to cover both versions, GCC pre-standard and DWARF 5.  Does that make sense?
>
> Wherever we use the logic:
>
>           int need_ranges_base = (die->tag != DW_TAG_compile_unit
>                                   && attr->form != DW_FORM_rnglistx);
>
> we should maybe check for DW_TAG_skeleton_unit as well?
>
> Simon

[-- Attachment #2: v5-0001-Update-GDB-to-fix-issues-with-handling-DWARF-v5-r.patch --]
[-- Type: application/octet-stream, Size: 27207 bytes --]

From 738901dfd29cb542468557ce802431cdbba0189d Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice@google.com>
Date: Wed, 1 Jul 2020 12:39:08 -0700
Subject: [PATCH v5] Update GDB to fix issues with handling DWARF v5 rnglists &
 .dwo files.

While experimenting with GDB on DWARF 5 with split debug (dwo files),
I discovered that GDB was not reading the rnglist index
properly (it needed to be reprocessed in the same way the loclist
index does), and that there was no code for reading rnglists out of
dwo files at all.  Also, the rnglist address reading function
(dwarf2_rnglists_process) was adding the base address to all rnglist
entries, when it's only supposed to add it to the DW_RLE_offset_pair
entries (http://dwarfstd.org/doc/DWARF5.pdf, p. 53), and was not
handling several entry types.

- Added 'reprocessing' for reading rnglist index (as is done for loclist
  index).
- Added code for reading rnglists out of .dwo files.
- Added several missing rnglist forms to dwarf2_rnglists_process.
- Fixed bug that was alwayas adding base address for rnglists (only
  one form needs that).
- Updated dwarf2_rnglists_process to read rnglist out of dwo file when
  appropriate.
- Added new functions cu_debug_rnglist_section & read_rnglist_index.
- Added new testcase, dw5-rnglist-test.{cc,exp}
---
 gdb/dwarf2/read.c                             | 261 ++++++++++++++----
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc  |  97 +++++++
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp |  40 +++
 3 files changed, 350 insertions(+), 48 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 558fad74f8..39ed455def 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -132,6 +132,12 @@ static int dwarf2_loclist_block_index;
 /* Size of .debug_loclists section header for 64-bit DWARF format.  */
 #define LOCLIST_HEADER_SIZE64 20
 
+/* Size of .debug_rnglists section header for 32-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE32 12
+
+/* Size of .debug_rnglists section header for 64-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE64 20
+
 /* An index into a (C++) symbol name component in a symbol name as
    recorded in the mapped_index's symbol table.  For each C++ symbol
    in the symbol table, we record one entry for the start of each
@@ -340,6 +346,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +362,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -364,9 +372,9 @@ dwop_section_names =
 
 /* local data types */
 
-/* The location list section (.debug_loclists) begins with a header,
-   which contains the following information.  */
-struct loclist_header
+/* The location list and range list sections (.debug_loclists & .debug_rnglists)
+   begin with a header,  which contains the following information.  */
+struct loclists_rnglists_header
 {
   /* A 4-byte or 12-byte length containing the length of the
      set of entries for this compilation unit, not including the
@@ -650,6 +658,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1274,7 +1283,7 @@ static const gdb_byte *read_attribute (const struct die_reader_specs *,
 				       const gdb_byte *, bool *need_reprocess);
 
 static void read_attribute_reprocess (const struct die_reader_specs *reader,
-				      struct attribute *attr);
+				      struct attribute *attr, dwarf_tag tag);
 
 static CORE_ADDR read_addr_index (struct dwarf2_cu *cu, unsigned int addr_index);
 
@@ -1378,12 +1387,13 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
-static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
-
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglists_section
+  (struct dwarf2_cu *cu, dwarf_tag tag);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12447,6 +12457,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13791,7 +13806,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13801,17 +13816,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
+  rnglists_section = cu_debug_rnglists_section (cu, tag);
+  rnglists_section->read (objfile);
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13819,8 +13837,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13842,6 +13860,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13860,6 +13883,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13888,6 +13924,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
@@ -13897,14 +13946,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (rlet == DW_RLE_base_address)
 	continue;
 
-      if (!base.has_value ())
-	{
-	  /* We have no valid base address for the ranges
-	     data.  */
-	  complaint (_("Invalid .debug_rnglists data (no base address)"));
-	  return false;
-	}
-
       if (range_beginning > range_end)
 	{
 	  /* Inverted range entries are invalid.  */
@@ -13916,8 +13957,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  if (!base.has_value ())
+	    {
+	      /* We have no valid base address for the DW_RLE_offset_pair.  */
+	      complaint (_("Invalid .debug_rnglists data (no base address for "
+			   "DW_RLE_offset_pair)"));
+	      return false;
+	    }
+
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13950,7 +14003,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13966,7 +14019,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14052,7 +14105,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14062,7 +14115,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14154,8 +14207,14 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	{
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
-	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	     in DW_TAG_compile_unit of DWO files.
+
+	     Attributes of the form DW_FORM_rnglistx have already had their
+	     value changed by read_rnglist_index and already include
+	     DW_AT_rnglists_base, so don't need to add the ranges base,
+	     either.  */
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14163,7 +14222,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14325,8 +14385,14 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
     {
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
-	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	 in DW_TAG_compile_unit of DWO files.
+
+	 Attributes of the form DW_FORM_rnglistx have already had their
+	 value changed by read_rnglist_index and already include
+	 DW_AT_rnglists_base, so don't need to add the ranges base,
+	 either.  */
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14334,7 +14400,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18154,8 +18220,13 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
-    read_attribute_reprocess (reader, &die->attrs[index]);
+    read_attribute_reprocess (reader, &die->attrs[index], die->tag);
   *diep = die;
   return info_ptr;
 }
@@ -18524,7 +18595,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
          already been read at this point, so there is no need to wait until
 	 the loop terminates to do the reprocessing.  */
       if (need_reprocess)
-	read_attribute_reprocess (reader, &attr);
+	read_attribute_reprocess (reader, &attr, tag);
       /* Store the data if it is of an attribute we want to keep in a
          partial symbol table.  */
       switch (attr.name)
@@ -18670,10 +18741,16 @@ partial_die_info::read (const struct die_reader_specs *reader,
 
 	case DW_AT_ranges:
 	  {
-	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
-	       but that requires a full DIE, so instead we just
-	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    /* DW_AT_rnglists_base does not apply to DIEs from the DWO
+	       skeleton.  We take advantage of the fact the DW_AT_ranges
+	       does not appear in DW_TAG_compile_unit of DWO files.
+
+	       Attributes of the form DW_FORM_rnglistx have already had
+               their value changed by read_rnglist_index and already
+	       include DW_AT_rnglists_base, so don't need to add the ranges
+	       base, either.  */
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18682,7 +18759,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19006,11 +19083,11 @@ partial_die_info::fixup (struct dwarf2_cu *cu)
   fixup_called = 1;
 }
 
-/* Read the .debug_loclists header contents from the given SECTION in the
-   HEADER.  */
+/* Read the .debug_loclists or .debug_rnglists header (they are the same format)
+   contents from the given SECTION in the HEADER.  */
 static void
-read_loclist_header (struct loclist_header *header,
-		      struct dwarf2_section_info *section)
+read_loclists_rnglists_header (struct loclists_rnglists_header *header,
+			       struct dwarf2_section_info *section)
 {
   unsigned int bytes_read;
   bfd *abfd = section->get_bfd_owner ();
@@ -19063,8 +19140,8 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
   if (section->buffer == NULL)
     complaint (_("DW_FORM_loclistx used without .debug_loclists "
 	        "section [in module %s]"), objfile_name (objfile));
-  struct loclist_header header;
-  read_loclist_header (&header, section);
+  struct loclists_rnglists_header header;
+  read_loclists_rnglists_header (&header, section);
   if (loclist_index >= header.offset_entry_count)
     complaint (_("DW_FORM_loclistx pointing outside of "
 	        ".debug_loclists offset array [in module %s]"),
@@ -19083,13 +19160,68 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value RNGLIST_INDEX, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
+		    dwarf_tag tag)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
+     : RNGLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
+  ULONGEST start_offset =
+    rnglist_base + rnglist_index * cu->header.offset_size;
+
+  /* Get rnglists section.  */
+  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu, tag);
+
+  /* Read the rnglists section content.  */
+  section->read (objfile);
+  if (section->buffer == nullptr)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+	   objfile_name (objfile));
+
+  /* Verify the rnglist index is valid.  */
+  struct loclists_rnglists_header header;
+  read_loclists_rnglists_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	   objfile_name (objfile));
+
+  /* Validate that the offset is within the section's range.  */
+  if (start_offset >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+	   objfile_name (objfile));
+
+  /* Validate that reading won't go beyond the end of the section.  */
+  if (start_offset + cu->header.offset_size > rnglist_base + section->size)
+    error (_("Reading DW_FORM_rnglistx index beyond end of"
+	     ".debug_rnglists section [in module %s]"),
+	   objfile_name (objfile));
+
+  const gdb_byte *info_ptr = section->buffer + start_offset;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
    the values of str_offsets_base or addr_base may not have been known.  */
 static void
 read_attribute_reprocess (const struct die_reader_specs *reader,
-			  struct attribute *attr)
+			  struct attribute *attr, dwarf_tag tag)
 {
   struct dwarf2_cu *cu = reader->cu;
   switch (attr->form)
@@ -19101,6 +19233,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr), tag);
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19282,8 +19417,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23372,6 +23509,34 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglists_section (struct dwarf2_cu *cu, dwarf_tag tag)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  /* Make sure we read the .debug_rnglists section from the file that
+     contains the DW_AT_ranges attribute we are reading.  Normally that
+     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
+     or DW_TAG_skeleton unit, we always want to read from objfile/linked
+     program.  */
+  if (cu->dwo_unit != nullptr
+      && tag != DW_TAG_compile_unit
+      && tag != DW_TAG_skeleton_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+      else
+	error (_(".debug_rnglists section is missing from .dwo file."));
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000000..81693f569d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,97 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node *
+make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void
+tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void
+inorder (node *root)
+{
+  std::vector<node *> todo;
+  todo.push_back (root);
+  while (!todo.empty())
+    {
+      node *curr = todo.back();
+      todo.pop_back(); /* break-here */
+      if (curr->visited)
+        std::cout << curr->id << " ";
+      else
+        {
+          curr->visited = true;
+          if (curr->right)
+            todo.push_back (curr->right);
+          todo.push_back (curr);
+          if (curr->left)
+            todo.push_back (curr->left);
+        }
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000000..af6c34b5dd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"
-- 
2.27.0.389.gc38d7665816-goog


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-14 15:47                               ` [PATCH V5] " Caroline Tice
@ 2020-07-15  2:04                                 ` Simon Marchi
  2020-07-15  3:15                                   ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-15  2:04 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-14 11:47 a.m., Caroline Tice wrote:
> "This time for sure!" -- Bullwinkle Moose
> 
> I think I've got all of your requested changes in now, and I've
> attached the updated patch.   About what you said at the very end of
> your last message:
> 
>> I thought that somehow, when reading a CU that uses a DWO, we were creating
>> a "logical" DIE tree by combining the DW_TAG_skeleton_unit DIE and the
>> children of the DWO's DW_TAG_compile_unit DIE, and while doing this,
>> overwriting the DW_TAG_skeleton_unit's DIE to use the DW_TAG_compile_unit
>> tag instead.  Therefore making it appear to the rest of the DWARF reader
>> as if it was a "standard" DW_TAG_compile_unit DIE.  But no, maybe I just dreamed
>> all of this, or I can't find it anymore.
>>
> 
> Actually your first thought was absolutely correct.  This is done in
> cutu_reader::cutu_reader.  In my patched
> read.c this is at line 7244:
> 
> comp_unit_die = dwo_comp_unit_die;

Ah ok, we just the full tree from the dwo.  And in read_cutu_die_from_dwo we copy
over some useful attributes from the skeleton to the dwo's root DIE, like the ranges.

Looks like I need to read and understand something a few times before it stays :).

>> In fact, the reason the code was checking for DW_TAG_compile_unit must be that
>> in the GCC/pre-standard version, the skeleton DIE in the executable is a
>> DW_TAG_compile_unit.  With DWARF5, we'll see DW_TAG_skeleton_unit here.
>>
>> So I believe we should use
>>
>>  (tag != DW_TAG_compile_unit && tag != DW_TAG_skeleton_unit)
>>
>> to cover both versions, GCC pre-standard and DWARF 5.  Does that make sense?
> 
> I agree that we need to check both cases in cu_debug_rnglists_section,
> because sometimes it gets called before the line above in cutu_reader,
> and sometimes it gets called after (now that I'm also calling it in
> dwarf2_rnglists_process).

Ok.

>> Wherever we use the logic:
>>
>>          int need_ranges_base = (die->tag != DW_TAG_compile_unit
>>                                  && attr->form != DW_FORM_rnglistx);
>>
>> we should maybe check for DW_TAG_skeleton_unit as well?"
> 
> I don't think there's any point in checking for DW_TAG_skeleton_unit
> in the need_ranges_base checks, because I believe that all of those
> checks are called after the call to cutu_reader, so we never have a
> DW_TAG_skeleton_unit by the time we get to those checks.

Makes sense, I think.

I don't think I have any more comments.  Tom, are you ok with this?

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-15  2:04                                 ` Simon Marchi
@ 2020-07-15  3:15                                   ` Simon Marchi
  2020-07-15 16:57                                     ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-15  3:15 UTC (permalink / raw)
  To: Caroline Tice; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-14 10:04 p.m., Simon Marchi wrote:
> I don't think I have any more comments.  Tom, are you ok with this?

I just had a thought about the test, and testing in general of DWARF5 and
split DWARF.  Right now, it's not really testing DWARF5 range lists, as the
name implies.  It's more a general program that may happen to fail given the
right compiler and compiler version.

[In fact, could you mention (here and in the commit message) how to run your
test case so that it fails, when the rest of your patch is not applied?  The
full "make check" command line, plus the compiler version used to compiler
the test case.

Ideally, we would have:

- one test such for the very specific case you are fixing (DW_AT_ranges inside
  a dwo with DWARF5).  We would use the DWARF assembler in lib/dwarf.exp to
  generate exactly what we want to test.  It would probably need to be improved
  a bit to learn about rnglists though.
- a new board file that tests with "-gsplit-dwarf -gdwarf5".  A bit like
  fission.exp, but for DWARF5.  I think a plain "-gdwarf5" board file would
  be useful too.

Simon




^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-15  3:15                                   ` Simon Marchi
@ 2020-07-15 16:57                                     ` Caroline Tice
  2020-07-15 17:04                                       ` H.J. Lu
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-15 16:57 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On Tue, Jul 14, 2020 at 8:15 PM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-07-14 10:04 p.m., Simon Marchi wrote:
> > I don't think I have any more comments.  Tom, are you ok with this?
>
> I just had a thought about the test, and testing in general of DWARF5 and
> split DWARF.  Right now, it's not really testing DWARF5 range lists, as the
> name implies.  It's more a general program that may happen to fail given the
> right compiler and compiler version.
>
> [In fact, could you mention (here and in the commit message) how to run your
> test case so that it fails, when the rest of your patch is not applied?  The
> full "make check" command line, plus the compiler version used to compiler
> the test case.
>
> Ideally, we would have:
>
> - one test such for the very specific case you are fixing (DW_AT_ranges inside
>   a dwo with DWARF5).  We would use the DWARF assembler in lib/dwarf.exp to
>   generate exactly what we want to test.  It would probably need to be improved
>   a bit to learn about rnglists though.
> - a new board file that tests with "-gsplit-dwarf -gdwarf5".  A bit like
>   fission.exp, but for DWARF5.  I think a plain "-gdwarf5" board file would
>   be useful too.
>
> Simon
>
>

The test program is the best I could manage under difficult
constraints.  It does (currently) generate a lexical block with a
DW_AT_ranges attribute of the form DW_FORM_rnglistx, if it is compiled
with clang.  If it is compiled with GCC, then, sadly,  the
DW_AT_ranges is not of the form DW_FORM_rnglistx, so the test always
passes.  In order to test with clang, I do:

$ export CLANG_CC="/usr/local/google3/cmtice/llvm-project/build2/bin/clang"
$ export CLANG_CXX="/usr/local/google3/cmtice/llvm-project/build2/bin/clang++"

$ make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"

With ToT GDB (without my patch), the result is:

Running /usr/local/google/home/cmtice/fsf-gdb.clean.obj/gdb/testsuite/../../../fsf-gdb.clean/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
...
FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print curr
FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print *curr

=== gdb Summary ===

# of expected passes 1
# of unexpected failures 2

I did try, originally, to make this an assembly code test, as I do
understand that compilers change, and the code they generate today is
not necessarily the code they generate tomorrow.  Unfortunately the
GNU assembler cannot process the DWARF v5 that is generated by clang
(e.g. it can't handle file numbers that start at 0 instead of 1 (which
is in the DWARF v5 spec, and which clang generates); and I have not
been able to figure out a way to get GDB test to use the clang
integrated assembler.

So I'm not sure what else I can do at this point,.

-- Caroline Tice
cmtice@google.com
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-15 16:57                                     ` Caroline Tice
@ 2020-07-15 17:04                                       ` H.J. Lu
  2020-07-15 22:35                                         ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: H.J. Lu @ 2020-07-15 17:04 UTC (permalink / raw)
  To: Caroline Tice
  Cc: Simon Marchi, Eric Christopher, Tom Tromey,
	Caroline Tice via Gdb-patches

On Wed, Jul 15, 2020 at 9:58 AM Caroline Tice via Gdb-patches
<gdb-patches@sourceware.org> wrote:
>
> On Tue, Jul 14, 2020 at 8:15 PM Simon Marchi <simark@simark.ca> wrote:
> >
> > On 2020-07-14 10:04 p.m., Simon Marchi wrote:
> > > I don't think I have any more comments.  Tom, are you ok with this?
> >
> > I just had a thought about the test, and testing in general of DWARF5 and
> > split DWARF.  Right now, it's not really testing DWARF5 range lists, as the
> > name implies.  It's more a general program that may happen to fail given the
> > right compiler and compiler version.
> >
> > [In fact, could you mention (here and in the commit message) how to run your
> > test case so that it fails, when the rest of your patch is not applied?  The
> > full "make check" command line, plus the compiler version used to compiler
> > the test case.
> >
> > Ideally, we would have:
> >
> > - one test such for the very specific case you are fixing (DW_AT_ranges inside
> >   a dwo with DWARF5).  We would use the DWARF assembler in lib/dwarf.exp to
> >   generate exactly what we want to test.  It would probably need to be improved
> >   a bit to learn about rnglists though.
> > - a new board file that tests with "-gsplit-dwarf -gdwarf5".  A bit like
> >   fission.exp, but for DWARF5.  I think a plain "-gdwarf5" board file would
> >   be useful too.
> >
> > Simon
> >
> >
>
> The test program is the best I could manage under difficult
> constraints.  It does (currently) generate a lexical block with a
> DW_AT_ranges attribute of the form DW_FORM_rnglistx, if it is compiled
> with clang.  If it is compiled with GCC, then, sadly,  the
> DW_AT_ranges is not of the form DW_FORM_rnglistx, so the test always
> passes.  In order to test with clang, I do:
>
> $ export CLANG_CC="/usr/local/google3/cmtice/llvm-project/build2/bin/clang"
> $ export CLANG_CXX="/usr/local/google3/cmtice/llvm-project/build2/bin/clang++"
>
> $ make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
> CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"
>
> With ToT GDB (without my patch), the result is:
>
> Running /usr/local/google/home/cmtice/fsf-gdb.clean.obj/gdb/testsuite/../../../fsf-gdb.clean/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
> ...
> FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print curr
> FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print *curr
>
> === gdb Summary ===
>
> # of expected passes 1
> # of unexpected failures 2
>
> I did try, originally, to make this an assembly code test, as I do
> understand that compilers change, and the code they generate today is
> not necessarily the code they generate tomorrow.  Unfortunately the
> GNU assembler cannot process the DWARF v5 that is generated by clang
> (e.g. it can't handle file numbers that start at 0 instead of 1 (which
> is in the DWARF v5 spec, and which clang generates); and I have not
> been able to figure out a way to get GDB test to use the clang
> integrated assembler.
>

I have used object files in binutils tests:

commit d0dded5bc251e506ef65b13696c624ea8669ed6e
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Jun 23 09:19:05 2020 -0700

    Add a testcase for PR binutils/26160

            PR binutils/26160
            * testsuite/binutils-all/pr26160.dwp.bz2: New file.
            * testsuite/binutils-all/pr26160.r: Likewise.
            * testsuite/binutils-all/readelf.exp: Run PR binutils/26160 test.

-- 
H.J.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-15 17:04                                       ` H.J. Lu
@ 2020-07-15 22:35                                         ` Caroline Tice
  2020-07-16  2:34                                           ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-15 22:35 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Simon Marchi, Eric Christopher, Tom Tromey,
	Caroline Tice via Gdb-patches

Thank you for that suggestion!

It turns out that I CAN create the .o file with clang, then use that
for the basis of my test; linking it with GCC works and gives the
results I expect (it fails with ToT GDB , but passes with my patch).

In order to make this work, however, I have to make a small change to
gdb/testsuite/lib/gdb.exp (in build_executable_from_specs, I have to
check to see if the source file is already an object file before
trying to compile it into an object file, and if it is an object file,
copy it into the proper directory)l

Should I add that (and the .o version of the test case) to this patch,
or should I make it a separate patch?  If the latter (which is what
feels right to me), then if this patch can receive final approval (and
someone can commit it, as I do not have write privileges to the GDB
code base), then I will submit the subsequent patch to replace the
current test with the .o version and update  gdb/testsuite/lib/gdb.exp
to make it work.

-- Caroline
cmtice@google.com

On Wed, Jul 15, 2020 at 10:05 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Wed, Jul 15, 2020 at 9:58 AM Caroline Tice via Gdb-patches
> <gdb-patches@sourceware.org> wrote:
> >
> > On Tue, Jul 14, 2020 at 8:15 PM Simon Marchi <simark@simark.ca> wrote:
> > >
> > > On 2020-07-14 10:04 p.m., Simon Marchi wrote:
> > > > I don't think I have any more comments.  Tom, are you ok with this?
> > >
> > > I just had a thought about the test, and testing in general of DWARF5 and
> > > split DWARF.  Right now, it's not really testing DWARF5 range lists, as the
> > > name implies.  It's more a general program that may happen to fail given the
> > > right compiler and compiler version.
> > >
> > > [In fact, could you mention (here and in the commit message) how to run your
> > > test case so that it fails, when the rest of your patch is not applied?  The
> > > full "make check" command line, plus the compiler version used to compiler
> > > the test case.
> > >
> > > Ideally, we would have:
> > >
> > > - one test such for the very specific case you are fixing (DW_AT_ranges inside
> > >   a dwo with DWARF5).  We would use the DWARF assembler in lib/dwarf.exp to
> > >   generate exactly what we want to test.  It would probably need to be improved
> > >   a bit to learn about rnglists though.
> > > - a new board file that tests with "-gsplit-dwarf -gdwarf5".  A bit like
> > >   fission.exp, but for DWARF5.  I think a plain "-gdwarf5" board file would
> > >   be useful too.
> > >
> > > Simon
> > >
> > >
> >
> > The test program is the best I could manage under difficult
> > constraints.  It does (currently) generate a lexical block with a
> > DW_AT_ranges attribute of the form DW_FORM_rnglistx, if it is compiled
> > with clang.  If it is compiled with GCC, then, sadly,  the
> > DW_AT_ranges is not of the form DW_FORM_rnglistx, so the test always
> > passes.  In order to test with clang, I do:
> >
> > $ export CLANG_CC="/usr/local/google3/cmtice/llvm-project/build2/bin/clang"
> > $ export CLANG_CXX="/usr/local/google3/cmtice/llvm-project/build2/bin/clang++"
> >
> > $ make check RUNTESTFLAGS="CC_FOR_TARGET=${CLANG_CC}
> > CXX_FOR_TARGET=${CLANG_CXX} dw5-rnglist-test.exp"
> >
> > With ToT GDB (without my patch), the result is:
> >
> > Running /usr/local/google/home/cmtice/fsf-gdb.clean.obj/gdb/testsuite/../../../fsf-gdb.clean/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
> > ...
> > FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print curr
> > FAIL: gdb.dwarf2/dw5-rnglist-test.exp: print *curr
> >
> > === gdb Summary ===
> >
> > # of expected passes 1
> > # of unexpected failures 2
> >
> > I did try, originally, to make this an assembly code test, as I do
> > understand that compilers change, and the code they generate today is
> > not necessarily the code they generate tomorrow.  Unfortunately the
> > GNU assembler cannot process the DWARF v5 that is generated by clang
> > (e.g. it can't handle file numbers that start at 0 instead of 1 (which
> > is in the DWARF v5 spec, and which clang generates); and I have not
> > been able to figure out a way to get GDB test to use the clang
> > integrated assembler.
> >
>
> I have used object files in binutils tests:
>
> commit d0dded5bc251e506ef65b13696c624ea8669ed6e
> Author: H.J. Lu <hjl.tools@gmail.com>
> Date:   Tue Jun 23 09:19:05 2020 -0700
>
>     Add a testcase for PR binutils/26160
>
>             PR binutils/26160
>             * testsuite/binutils-all/pr26160.dwp.bz2: New file.
>             * testsuite/binutils-all/pr26160.r: Likewise.
>             * testsuite/binutils-all/readelf.exp: Run PR binutils/26160 test.
>
> --
> H.J.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-15 22:35                                         ` Caroline Tice
@ 2020-07-16  2:34                                           ` Simon Marchi
  2020-07-16  4:46                                             ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-16  2:34 UTC (permalink / raw)
  To: Caroline Tice, H.J. Lu
  Cc: Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-15 6:35 p.m., Caroline Tice wrote:
> Thank you for that suggestion!
> 
> It turns out that I CAN create the .o file with clang, then use that
> for the basis of my test; linking it with GCC works and gives the
> results I expect (it fails with ToT GDB , but passes with my patch).
> 
> In order to make this work, however, I have to make a small change to
> gdb/testsuite/lib/gdb.exp (in build_executable_from_specs, I have to
> check to see if the source file is already an object file before
> trying to compile it into an object file, and if it is an object file,
> copy it into the proper directory)l
> 
> Should I add that (and the .o version of the test case) to this patch,
> or should I make it a separate patch?  If the latter (which is what
> feels right to me), then if this patch can receive final approval (and
> someone can commit it, as I do not have write privileges to the GDB
> code base), then I will submit the subsequent patch to replace the
> current test with the .o version and update  gdb/testsuite/lib/gdb.exp
> to make it work.

I'd be fine merging the patch with the current test and keep working on
improving the test coverage.  I think that we'll need indeed need to do
a bit of infrastructure work to improve the testing of DWARF5 in general.

Can you please provide a final patch version where you mention in the commit
message how to run the test in order to provoke a failure.  Please make sure
to include the compiler version as well (output of `clang --version`) because,
as you said, the code compilers output changes with time.  So in order to be
able to reproduce the failure, it would be useful to know the compiler version.

I'm not sure what you mean by "the .o version of the test case".  Do you
mean check-in the .o in the git repository?  We currently don't check in
binary test artifacts for GDB.  However, I'm of the opinion that it could
be useful sometimes (like here).  But we'd need to have a discussion
about this first, how we want to do that, which guidelines to follow.

About this comment, in your other message:

> I did try, originally, to make this an assembly code test, as I do
> understand that compilers change, and the code they generate today is
> not necessarily the code they generate tomorrow.  Unfortunately the
> GNU assembler cannot process the DWARF v5 that is generated by clang
> (e.g. it can't handle file numbers that start at 0 instead of 1 (which
> is in the DWARF v5 spec, and which clang generates); and I have not
> been able to figure out a way to get GDB test to use the clang
> integrated assembler.

Just wondering, doesn't gcc emit at least *some* DWARF 5 by now?  If so,
can't the GNU assembler deal with it?

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-16  2:34                                           ` Simon Marchi
@ 2020-07-16  4:46                                             ` Caroline Tice
  2020-07-16 15:41                                               ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-16  4:46 UTC (permalink / raw)
  To: Simon Marchi
  Cc: H.J. Lu, Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

[-- Attachment #1: Type: text/plain, Size: 4763 bytes --]

The attached patch has an updated  commit message that explains how to
run the test and what version of clang was used.  Other than that it
is identical  to the previous patch.

On Wed, Jul 15, 2020 at 7:35 PM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-07-15 6:35 p.m., Caroline Tice wrote:
> > Thank you for that suggestion!
> >
> > It turns out that I CAN create the .o file with clang, then use that
> > for the basis of my test; linking it with GCC works and gives the
> > results I expect (it fails with ToT GDB , but passes with my patch).
> >
> > In order to make this work, however, I have to make a small change to
> > gdb/testsuite/lib/gdb.exp (in build_executable_from_specs, I have to
> > check to see if the source file is already an object file before
> > trying to compile it into an object file, and if it is an object file,
> > copy it into the proper directory)l
> >
> > Should I add that (and the .o version of the test case) to this patch,
> > or should I make it a separate patch?  If the latter (which is what
> > feels right to me), then if this patch can receive final approval (and
> > someone can commit it, as I do not have write privileges to the GDB
> > code base), then I will submit the subsequent patch to replace the
> > current test with the .o version and update  gdb/testsuite/lib/gdb.exp
> > to make it work.
>
> I'd be fine merging the patch with the current test and keep working on
> improving the test coverage.  I think that we'll need indeed need to do
> a bit of infrastructure work to improve the testing of DWARF5 in general.
>
> Can you please provide a final patch version where you mention in the commit
> message how to run the test in order to provoke a failure.  Please make sure
> to include the compiler version as well (output of `clang --version`) because,
> as you said, the code compilers output changes with time.  So in order to be
> able to reproduce the failure, it would be useful to know the compiler version.
>
Done.

> I'm not sure what you mean by "the .o version of the test case".  Do you
> mean check-in the .o in the git repository?  We currently don't check in
> binary test artifacts for GDB.  However, I'm of the opinion that it could
> be useful sometimes (like here).  But we'd need to have a discussion
> about this first, how we want to do that, which guidelines to follow.
>

I'll submit a separate patch, where it can be discussed.

> About this comment, in your other message:
>
> > I did try, originally, to make this an assembly code test, as I do
> > understand that compilers change, and the code they generate today is
> > not necessarily the code they generate tomorrow.  Unfortunately the
> > GNU assembler cannot process the DWARF v5 that is generated by clang
> > (e.g. it can't handle file numbers that start at 0 instead of 1 (which
> > is in the DWARF v5 spec, and which clang generates); and I have not
> > been able to figure out a way to get GDB test to use the clang
> > integrated assembler.
>
> Just wondering, doesn't gcc emit at least *some* DWARF 5 by now?  If so,
> can't the GNU assembler deal with it?
>

Not that it matters for this patch, but I believe you are
misunderstanding the situation. GCC *does* emit some  DWARF 5, but at
least the GCC version I'm using (Debian 9.3.0-8) does not seem to be
generating DWARF v5 line table sections; the line table section it
generates (when I compile with -gdwarf-5) is a DWARF v3 .debug_line
sections.

In fact my gcc-generated binary has the following debug sections,
which are a mix of various dwarf versions:

.debug_aranges               (dwarf v2)
.debug_info                      (dwarf v5)
.debug_abbrev                 (dwarf v5)
.debug_line                      (dwarf v3)
.debug_str
.debug_addr                    (dwarf v5)
.debug_gnu_pubnames  (dwarf v2)
.debug_gnu_pubtypes    (dwarf v2)
.debug_rnglists               (dwarf v5)

In contrast, here are the sections my clang compiler, when passed
-gdwarf-5 is generating dwarf v5 versions
of nearly all the sections:

.debug_info                      (dwarf v5)
.debug_abbrev                 (dwarf v5)
.debug_line                      (dwarf v5)
.debug_str
.debug_addr                     (dwarf v5)
.debug_rnglists                (dwarf v5)
.debug_str_offsets           (dwarf v5)
.debug_gnu_pubnames   (dwarf v2)
.debug_gnu_pubtypes     (dwarf v2)
.debug_line_str                (dwarf v5)

So either the GNU assembler has not been updated to handle DWARF v5
.debug_line file indices because GCC is not yet generating them; or
GCC is not yet generating them because the GNU assembler has not yet
been
updated to handle them.  Take your pick.

In any case, I hope my patch is now acceptable! :-)

-- Caroline Tice
cmtice@google.com

> Simon

[-- Attachment #2: v6-0001-Update-GDB-to-fix-issues-with-handling-DWARF-v5-r.patch --]
[-- Type: application/octet-stream, Size: 27644 bytes --]

From f9b52a463039cc7765445e1033cb07514afebf16 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice@google.com>
Date: Wed, 1 Jul 2020 12:39:08 -0700
Subject: [PATCH v6] Update GDB to fix issues with handling DWARF v5 rnglists &
 .dwo files.

While experimenting with GDB on DWARF 5 with split debug (dwo files),
I discovered that GDB was not reading the rnglist index
properly (it needed to be reprocessed in the same way the loclist
index does), and that there was no code for reading rnglists out of
dwo files at all.  Also, the rnglist address reading function
(dwarf2_rnglists_process) was adding the base address to all rnglist
entries, when it's only supposed to add it to the DW_RLE_offset_pair
entries (http://dwarfstd.org/doc/DWARF5.pdf, p. 53), and was not
handling several entry types.

- Added 'reprocessing' for reading rnglist index (as is done for loclist
  index).
- Added code for reading rnglists out of .dwo files.
- Added several missing rnglist forms to dwarf2_rnglists_process.
- Fixed bug that was alwayas adding base address for rnglists (only
  one form needs that).
- Updated dwarf2_rnglists_process to read rnglist out of dwo file when
  appropriate.
- Added new functions cu_debug_rnglist_section & read_rnglist_index.
- Added new testcase, dw5-rnglist-test.{cc,exp}

Special note about the new testcase:

In order for the test case to test anything meaningful, it must be
compiled with clang, not GCC.  The way to do this is as follows:

$ make check RUNTESTFLAGS="CC_FOR_TARGET=/path/to/clang CXX_FOR_TARGET=/path/to/clang++ dw5-rnglist-test.exp"

This following version of clang was used for this testing:

clang version 9.0.1-11
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
---
 gdb/dwarf2/read.c                             | 261 ++++++++++++++----
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc  |  97 +++++++
 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp |  40 +++
 3 files changed, 350 insertions(+), 48 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 558fad74f8..39ed455def 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -132,6 +132,12 @@ static int dwarf2_loclist_block_index;
 /* Size of .debug_loclists section header for 64-bit DWARF format.  */
 #define LOCLIST_HEADER_SIZE64 20
 
+/* Size of .debug_rnglists section header for 32-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE32 12
+
+/* Size of .debug_rnglists section header for 64-bit DWARF format.  */
+#define RNGLIST_HEADER_SIZE64 20
+
 /* An index into a (C++) symbol name component in a symbol name as
    recorded in the mapped_index's symbol table.  For each C++ symbol
    in the symbol table, we record one entry for the start of each
@@ -340,6 +346,7 @@ static const struct dwop_section_names
   struct dwarf2_section_names loclists_dwo;
   struct dwarf2_section_names macinfo_dwo;
   struct dwarf2_section_names macro_dwo;
+  struct dwarf2_section_names rnglists_dwo;
   struct dwarf2_section_names str_dwo;
   struct dwarf2_section_names str_offsets_dwo;
   struct dwarf2_section_names types_dwo;
@@ -355,6 +362,7 @@ dwop_section_names =
   { ".debug_loclists.dwo", ".zdebug_loclists.dwo" },
   { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo" },
   { ".debug_macro.dwo", ".zdebug_macro.dwo" },
+  { ".debug_rnglists.dwo", ".zdebug_rnglists.dwo" },
   { ".debug_str.dwo", ".zdebug_str.dwo" },
   { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo" },
   { ".debug_types.dwo", ".zdebug_types.dwo" },
@@ -364,9 +372,9 @@ dwop_section_names =
 
 /* local data types */
 
-/* The location list section (.debug_loclists) begins with a header,
-   which contains the following information.  */
-struct loclist_header
+/* The location list and range list sections (.debug_loclists & .debug_rnglists)
+   begin with a header,  which contains the following information.  */
+struct loclists_rnglists_header
 {
   /* A 4-byte or 12-byte length containing the length of the
      set of entries for this compilation unit, not including the
@@ -650,6 +658,7 @@ struct dwo_sections
   struct dwarf2_section_info loclists;
   struct dwarf2_section_info macinfo;
   struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
   struct dwarf2_section_info str;
   struct dwarf2_section_info str_offsets;
   /* In the case of a virtual DWO file, these two are unused.  */
@@ -1274,7 +1283,7 @@ static const gdb_byte *read_attribute (const struct die_reader_specs *,
 				       const gdb_byte *, bool *need_reprocess);
 
 static void read_attribute_reprocess (const struct die_reader_specs *reader,
-				      struct attribute *attr);
+				      struct attribute *attr, dwarf_tag tag);
 
 static CORE_ADDR read_addr_index (struct dwarf2_cu *cu, unsigned int addr_index);
 
@@ -1378,12 +1387,13 @@ static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
 
 static void read_variable (struct die_info *die, struct dwarf2_cu *cu);
 
-static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
-			       struct dwarf2_cu *, dwarf2_psymtab *);
-
 /* Return the .debug_loclists section to use for cu.  */
 static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 
+/* Return the .debug_rnglists section to use for cu.  */
+static struct dwarf2_section_info *cu_debug_rnglists_section
+  (struct dwarf2_cu *cu, dwarf_tag tag);
+
 /* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
    values.  Keep the items ordered with increasing constraints compliance.  */
 enum pc_bounds_kind
@@ -12447,6 +12457,11 @@ dwarf2_locate_dwo_sections (bfd *abfd, asection *sectp, void *dwo_sections_ptr)
       dwo_sections->macro.s.section = sectp;
       dwo_sections->macro.size = bfd_section_size (sectp);
     }
+  else if (section_is_p (sectp->name, &names->rnglists_dwo))
+    {
+      dwo_sections->rnglists.s.section = sectp;
+      dwo_sections->rnglists.size = bfd_section_size (sectp);
+    }
   else if (section_is_p (sectp->name, &names->str_dwo))
     {
       dwo_sections->str.s.section = sectp;
@@ -13791,7 +13806,7 @@ read_variable (struct die_info *die, struct dwarf2_cu *cu)
 template <typename Callback>
 static bool
 dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
-			 Callback &&callback)
+			 dwarf_tag tag, Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
   struct objfile *objfile = per_objfile->objfile;
@@ -13801,17 +13816,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
   const gdb_byte *buffer;
   CORE_ADDR baseaddr;
   bool overflow = false;
+  ULONGEST addr_index;
+  struct dwarf2_section_info *rnglists_section;
 
   base = cu->base_address;
+  rnglists_section = cu_debug_rnglists_section (cu, tag);
+  rnglists_section->read (objfile);
 
-  per_objfile->per_bfd->rnglists.read (objfile);
-  if (offset >= per_objfile->per_bfd->rnglists.size)
+  if (offset >= rnglists_section->size)
     {
       complaint (_("Offset %d out of bounds for DW_AT_ranges attribute"),
 		 offset);
       return false;
     }
-  buffer = per_objfile->per_bfd->rnglists.buffer + offset;
+  buffer = rnglists_section->buffer + offset;
 
   baseaddr = objfile->text_section_offset ();
 
@@ -13819,8 +13837,8 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
     {
       /* Initialize it due to a false compiler warning.  */
       CORE_ADDR range_beginning = 0, range_end = 0;
-      const gdb_byte *buf_end = (per_objfile->per_bfd->rnglists.buffer
-				 + per_objfile->per_bfd->rnglists.size);
+      const gdb_byte *buf_end = (rnglists_section->buffer
+				 + rnglists_section->size);
       unsigned int bytes_read;
 
       if (buffer == buf_end)
@@ -13842,6 +13860,11 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  base = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+        case DW_RLE_base_addressx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          base = read_addr_index (cu, addr_index);
+          break;
 	case DW_RLE_start_length:
 	  if (buffer + cu->header.addr_size > buf_end)
 	    {
@@ -13860,6 +13883,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	      break;
 	    }
 	  break;
+	case DW_RLE_startx_length:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          range_end = (range_beginning
+                       + read_unsigned_leb128 (obfd, buffer, &bytes_read));
+          buffer += bytes_read;
+          break;
 	case DW_RLE_offset_pair:
 	  range_beginning = read_unsigned_leb128 (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
@@ -13888,6 +13924,19 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  range_end = cu->header.read_address (obfd, buffer, &bytes_read);
 	  buffer += bytes_read;
 	  break;
+	case DW_RLE_startx_endx:
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_beginning = read_addr_index (cu, addr_index);
+          if (buffer > buf_end)
+            {
+              overflow = true;
+              break;
+            }
+          addr_index = read_unsigned_leb128 (obfd, buffer, &bytes_read);
+          buffer += bytes_read;
+          range_end = read_addr_index (cu, addr_index);
+          break;
 	default:
 	  complaint (_("Invalid .debug_rnglists data (no base address)"));
 	  return false;
@@ -13897,14 +13946,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (rlet == DW_RLE_base_address)
 	continue;
 
-      if (!base.has_value ())
-	{
-	  /* We have no valid base address for the ranges
-	     data.  */
-	  complaint (_("Invalid .debug_rnglists data (no base address)"));
-	  return false;
-	}
-
       if (range_beginning > range_end)
 	{
 	  /* Inverted range entries are invalid.  */
@@ -13916,8 +13957,20 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
       if (range_beginning == range_end)
 	continue;
 
-      range_beginning += *base;
-      range_end += *base;
+      /* Only DW_RLE_offset_pair needs the base address added.  */
+      if (rlet == DW_RLE_offset_pair)
+	{
+	  if (!base.has_value ())
+	    {
+	      /* We have no valid base address for the DW_RLE_offset_pair.  */
+	      complaint (_("Invalid .debug_rnglists data (no base address for "
+			   "DW_RLE_offset_pair)"));
+	      return false;
+	    }
+
+	  range_beginning += *base;
+	  range_end += *base;
+	}
 
       /* A not-uncommon case of bad debug info.
 	 Don't pollute the addrmap with bad data.  */
@@ -13950,7 +14003,7 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 
 template <typename Callback>
 static int
-dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
+dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
 		       Callback &&callback)
 {
   dwarf2_per_objfile *per_objfile = cu->per_objfile;
@@ -13966,7 +14019,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
   CORE_ADDR baseaddr;
 
   if (cu_header->version >= 5)
-    return dwarf2_rnglists_process (offset, cu, callback);
+    return dwarf2_rnglists_process (offset, cu, tag, callback);
 
   base = cu->base_address;
 
@@ -14052,7 +14105,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 static int
 dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
 		    CORE_ADDR *high_return, struct dwarf2_cu *cu,
-		    dwarf2_psymtab *ranges_pst)
+		    dwarf2_psymtab *ranges_pst, dwarf_tag tag)
 {
   struct objfile *objfile = cu->per_objfile->objfile;
   struct gdbarch *gdbarch = objfile->arch ();
@@ -14062,7 +14115,7 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   CORE_ADDR high = 0;
   int retval;
 
-  retval = dwarf2_ranges_process (offset, cu,
+  retval = dwarf2_ranges_process (offset, cu, tag,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
       if (ranges_pst != NULL)
@@ -14154,8 +14207,14 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 	{
 	  /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	     We take advantage of the fact that DW_AT_ranges does not appear
-	     in DW_TAG_compile_unit of DWO files.  */
-	  int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	     in DW_TAG_compile_unit of DWO files.
+
+	     Attributes of the form DW_FORM_rnglistx have already had their
+	     value changed by read_rnglist_index and already include
+	     DW_AT_rnglists_base, so don't need to add the ranges base,
+	     either.  */
+	  int need_ranges_base = (die->tag != DW_TAG_compile_unit
+				  && attr->form != DW_FORM_rnglistx);
 	  unsigned int ranges_offset = (DW_UNSND (attr)
 					+ (need_ranges_base
 					   ? cu->ranges_base
@@ -14163,7 +14222,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
 
 	  /* Value of the DW_AT_ranges attribute is the offset in the
 	     .debug_ranges section.  */
-	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst))
+	  if (!dwarf2_ranges_read (ranges_offset, &low, &high, cu, pst,
+				   die->tag))
 	    return PC_BOUNDS_INVALID;
 	  /* Found discontinuous range of addresses.  */
 	  ret = PC_BOUNDS_RANGES;
@@ -14325,8 +14385,14 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
     {
       /* DW_AT_rnglists_base does not apply to DIEs from the DWO skeleton.
 	 We take advantage of the fact that DW_AT_ranges does not appear
-	 in DW_TAG_compile_unit of DWO files.  */
-      int need_ranges_base = die->tag != DW_TAG_compile_unit;
+	 in DW_TAG_compile_unit of DWO files.
+
+	 Attributes of the form DW_FORM_rnglistx have already had their
+	 value changed by read_rnglist_index and already include
+	 DW_AT_rnglists_base, so don't need to add the ranges base,
+	 either.  */
+      int need_ranges_base = (die->tag != DW_TAG_compile_unit
+			      && attr->form != DW_FORM_rnglistx);
 
       /* The value of the DW_AT_ranges attribute is the offset of the
          address range list in the .debug_ranges section.  */
@@ -14334,7 +14400,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 			      + (need_ranges_base ? cu->ranges_base : 0));
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu,
+      dwarf2_ranges_process (offset, cu, die->tag,
 	[&] (CORE_ADDR start, CORE_ADDR end)
 	{
 	  start += baseaddr;
@@ -18154,8 +18220,13 @@ read_full_die_1 (const struct die_reader_specs *reader,
   auto maybe_addr_base = die->addr_base ();
   if (maybe_addr_base.has_value ())
     cu->addr_base = *maybe_addr_base;
+
+  attr = die->attr (DW_AT_rnglists_base);
+  if (attr != nullptr)
+    cu->ranges_base = DW_UNSND (attr);
+
   for (int index : indexes_that_need_reprocess)
-    read_attribute_reprocess (reader, &die->attrs[index]);
+    read_attribute_reprocess (reader, &die->attrs[index], die->tag);
   *diep = die;
   return info_ptr;
 }
@@ -18524,7 +18595,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
          already been read at this point, so there is no need to wait until
 	 the loop terminates to do the reprocessing.  */
       if (need_reprocess)
-	read_attribute_reprocess (reader, &attr);
+	read_attribute_reprocess (reader, &attr, tag);
       /* Store the data if it is of an attribute we want to keep in a
          partial symbol table.  */
       switch (attr.name)
@@ -18670,10 +18741,16 @@ partial_die_info::read (const struct die_reader_specs *reader,
 
 	case DW_AT_ranges:
 	  {
-	    /* It would be nice to reuse dwarf2_get_pc_bounds here,
-	       but that requires a full DIE, so instead we just
-	       reimplement it.  */
-	    int need_ranges_base = tag != DW_TAG_compile_unit;
+	    /* DW_AT_rnglists_base does not apply to DIEs from the DWO
+	       skeleton.  We take advantage of the fact the DW_AT_ranges
+	       does not appear in DW_TAG_compile_unit of DWO files.
+
+	       Attributes of the form DW_FORM_rnglistx have already had
+               their value changed by read_rnglist_index and already
+	       include DW_AT_rnglists_base, so don't need to add the ranges
+	       base, either.  */
+	    int need_ranges_base = (tag != DW_TAG_compile_unit
+				    && attr.form != DW_FORM_rnglistx);
 	    unsigned int ranges_offset = (DW_UNSND (&attr)
 					  + (need_ranges_base
 					     ? cu->ranges_base
@@ -18682,7 +18759,7 @@ partial_die_info::read (const struct die_reader_specs *reader,
 	    /* Value of the DW_AT_ranges attribute is the offset in the
 	       .debug_ranges section.  */
 	    if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
-				    nullptr))
+				    nullptr, tag))
 	      has_pc_info = 1;
 	  }
 	  break;
@@ -19006,11 +19083,11 @@ partial_die_info::fixup (struct dwarf2_cu *cu)
   fixup_called = 1;
 }
 
-/* Read the .debug_loclists header contents from the given SECTION in the
-   HEADER.  */
+/* Read the .debug_loclists or .debug_rnglists header (they are the same format)
+   contents from the given SECTION in the HEADER.  */
 static void
-read_loclist_header (struct loclist_header *header,
-		      struct dwarf2_section_info *section)
+read_loclists_rnglists_header (struct loclists_rnglists_header *header,
+			       struct dwarf2_section_info *section)
 {
   unsigned int bytes_read;
   bfd *abfd = section->get_bfd_owner ();
@@ -19063,8 +19140,8 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
   if (section->buffer == NULL)
     complaint (_("DW_FORM_loclistx used without .debug_loclists "
 	        "section [in module %s]"), objfile_name (objfile));
-  struct loclist_header header;
-  read_loclist_header (&header, section);
+  struct loclists_rnglists_header header;
+  read_loclists_rnglists_header (&header, section);
   if (loclist_index >= header.offset_entry_count)
     complaint (_("DW_FORM_loclistx pointing outside of "
 	        ".debug_loclists offset array [in module %s]"),
@@ -19083,13 +19160,68 @@ read_loclist_index (struct dwarf2_cu *cu, ULONGEST loclist_index)
     return bfd_get_64 (abfd, info_ptr) + loclist_base;
 }
 
+/* Given a DW_FORM_rnglistx value RNGLIST_INDEX, fetch the offset from the
+   array of offsets in the .debug_rnglists section.  */
+static CORE_ADDR
+read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
+		    dwarf_tag tag)
+{
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+  struct objfile *objfile = dwarf2_per_objfile->objfile;
+  bfd *abfd = objfile->obfd;
+  ULONGEST rnglist_header_size =
+    (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
+     : RNGLIST_HEADER_SIZE64);
+  ULONGEST rnglist_base =
+      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
+  ULONGEST start_offset =
+    rnglist_base + rnglist_index * cu->header.offset_size;
+
+  /* Get rnglists section.  */
+  struct dwarf2_section_info *section = cu_debug_rnglists_section (cu, tag);
+
+  /* Read the rnglists section content.  */
+  section->read (objfile);
+  if (section->buffer == nullptr)
+    error (_("DW_FORM_rnglistx used without .debug_rnglists section "
+	     "[in module %s]"),
+	   objfile_name (objfile));
+
+  /* Verify the rnglist index is valid.  */
+  struct loclists_rnglists_header header;
+  read_loclists_rnglists_header (&header, section);
+  if (rnglist_index >= header.offset_entry_count)
+    error (_("DW_FORM_rnglistx index pointing outside of "
+	     ".debug_rnglists offset array [in module %s]"),
+	   objfile_name (objfile));
+
+  /* Validate that the offset is within the section's range.  */
+  if (start_offset >= section->size)
+    error (_("DW_FORM_rnglistx pointing outside of "
+             ".debug_rnglists section [in module %s]"),
+	   objfile_name (objfile));
+
+  /* Validate that reading won't go beyond the end of the section.  */
+  if (start_offset + cu->header.offset_size > rnglist_base + section->size)
+    error (_("Reading DW_FORM_rnglistx index beyond end of"
+	     ".debug_rnglists section [in module %s]"),
+	   objfile_name (objfile));
+
+  const gdb_byte *info_ptr = section->buffer + start_offset;
+
+  if (cu->header.offset_size == 4)
+    return read_4_bytes (abfd, info_ptr) + rnglist_base;
+  else
+    return read_8_bytes (abfd, info_ptr) + rnglist_base;
+}
+
 /* Process the attributes that had to be skipped in the first round. These
    attributes are the ones that need str_offsets_base or addr_base attributes.
    They could not have been processed in the first round, because at the time
    the values of str_offsets_base or addr_base may not have been known.  */
 static void
 read_attribute_reprocess (const struct die_reader_specs *reader,
-			  struct attribute *attr)
+			  struct attribute *attr, dwarf_tag tag)
 {
   struct dwarf2_cu *cu = reader->cu;
   switch (attr->form)
@@ -19101,6 +19233,9 @@ read_attribute_reprocess (const struct die_reader_specs *reader,
       case DW_FORM_loclistx:
 	 DW_UNSND (attr) = read_loclist_index (cu, DW_UNSND (attr));
 	 break;
+      case DW_FORM_rnglistx:
+        DW_UNSND (attr) = read_rnglist_index (cu, DW_UNSND (attr), tag);
+        break;
       case DW_FORM_strx:
       case DW_FORM_strx1:
       case DW_FORM_strx2:
@@ -19282,8 +19417,10 @@ read_attribute_value (const struct die_reader_specs *reader,
       DW_SND (attr) = read_signed_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
-    case DW_FORM_udata:
     case DW_FORM_rnglistx:
+      *need_reprocess = true;
+      /* FALLTHROUGH */
+    case DW_FORM_udata:
       DW_UNSND (attr) = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
       info_ptr += bytes_read;
       break;
@@ -23372,6 +23509,34 @@ cu_debug_loc_section (struct dwarf2_cu *cu)
 				  : &per_objfile->per_bfd->loc);
 }
 
+/* Return the .debug_rnglists section to use for CU.  */
+static struct dwarf2_section_info *
+cu_debug_rnglists_section (struct dwarf2_cu *cu, dwarf_tag tag)
+{
+  if (cu->header.version < 5)
+    error (_(".debug_rnglists section cannot be used in DWARF %d"),
+	   cu->header.version);
+  struct dwarf2_per_objfile *dwarf2_per_objfile = cu->per_objfile;
+
+  /* Make sure we read the .debug_rnglists section from the file that
+     contains the DW_AT_ranges attribute we are reading.  Normally that
+     would be the .dwo file, if there is one.  However for DW_TAG_compile_unit
+     or DW_TAG_skeleton unit, we always want to read from objfile/linked
+     program.  */
+  if (cu->dwo_unit != nullptr
+      && tag != DW_TAG_compile_unit
+      && tag != DW_TAG_skeleton_unit)
+    {
+      struct dwo_sections *sections = &cu->dwo_unit->dwo_file->sections;
+
+      if (sections->rnglists.size > 0)
+	return &sections->rnglists;
+      else
+	error (_(".debug_rnglists section is missing from .dwo file."));
+    }
+  return &dwarf2_per_objfile->per_bfd->rnglists;
+}
+
 /* A helper function that fills in a dwarf2_loclist_baton.  */
 
 static void
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
new file mode 100644
index 0000000000..81693f569d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.cc
@@ -0,0 +1,97 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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/>.  */
+
+#include <iostream>
+#include <vector>
+
+struct node {
+  int id;
+  node *left;
+  node *right;
+  bool visited;
+};
+
+node  node_array[50];
+unsigned int CUR_IDX = 0;
+
+node *
+make_node (int val)
+{
+  node *new_node = &(node_array[CUR_IDX++]);
+  new_node->left = NULL;
+  new_node->right = NULL;
+  new_node->id = val;
+  new_node->visited = false;
+
+  return new_node;
+}
+
+void
+tree_insert (node *root, int val)
+{
+  if (val < root->id)
+    {
+      if (root->left)
+        tree_insert (root->left, val);
+      else
+        root->left = make_node(val);
+    }
+  else if (val > root->id)
+    {
+      if (root->right)
+        tree_insert (root->right, val);
+      else
+        root->right = make_node(val);
+    }
+}
+
+void
+inorder (node *root)
+{
+  std::vector<node *> todo;
+  todo.push_back (root);
+  while (!todo.empty())
+    {
+      node *curr = todo.back();
+      todo.pop_back(); /* break-here */
+      if (curr->visited)
+        std::cout << curr->id << " ";
+      else
+        {
+          curr->visited = true;
+          if (curr->right)
+            todo.push_back (curr->right);
+          todo.push_back (curr);
+          if (curr->left)
+            todo.push_back (curr->left);
+        }
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  node *root = make_node (35);
+
+  tree_insert (root, 28);
+  tree_insert (root, 20);
+  tree_insert (root, 60);
+
+  inorder (root);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
new file mode 100644
index 0000000000..af6c34b5dd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw5-rnglist-test.exp
@@ -0,0 +1,40 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check that GDB can find the variables in a lexical block with a
+# DW_FORM_rnglistx DW_AT_ranges field.  This test is intended for DWARF-5,
+# compiled with clang++.
+
+standard_testfile .cc
+
+# This test is intended for targets which support DWARF-5.
+# Since we pass an explicit -gdwarf-5 to the compiler,
+# we let that be the test of whether the target supports it.
+
+if { [prepare_for_testing "failed to prepare" "${testfile}" \
+          $srcfile {debug c++ additional_flags=-gdwarf-5 \
+                        additional_flags=-O0}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "print curr" "\\\(node \\\*\\\) $hex <node_array>"
+gdb_test "print *curr" "= {id = 35, left = $hex <node_array\\+$decimal>, right = $hex <node_array\\+$decimal>, visited = false}"
-- 
2.27.0.389.gc38d7665816-goog


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-16  4:46                                             ` Caroline Tice
@ 2020-07-16 15:41                                               ` Simon Marchi
  2020-07-16 15:46                                                 ` Caroline Tice
  0 siblings, 1 reply; 29+ messages in thread
From: Simon Marchi @ 2020-07-16 15:41 UTC (permalink / raw)
  To: Caroline Tice
  Cc: H.J. Lu, Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-16 12:46 a.m., Caroline Tice wrote:
> The attached patch has an updated  commit message that explains how to
> run the test and what version of clang was used.  Other than that it
> is identical  to the previous patch.

Thanks, I've now merged it.

>> Just wondering, doesn't gcc emit at least *some* DWARF 5 by now?  If so,
>> can't the GNU assembler deal with it?
>>
> 
> Not that it matters for this patch, but I believe you are
> misunderstanding the situation. GCC *does* emit some  DWARF 5, but at
> least the GCC version I'm using (Debian 9.3.0-8) does not seem to be
> generating DWARF v5 line table sections; the line table section it
> generates (when I compile with -gdwarf-5) is a DWARF v3 .debug_line
> sections.
> 
> In fact my gcc-generated binary has the following debug sections,
> which are a mix of various dwarf versions:
> 
> .debug_aranges               (dwarf v2)
> .debug_info                      (dwarf v5)
> .debug_abbrev                 (dwarf v5)
> .debug_line                      (dwarf v3)
> .debug_str
> .debug_addr                    (dwarf v5)
> .debug_gnu_pubnames  (dwarf v2)
> .debug_gnu_pubtypes    (dwarf v2)
> .debug_rnglists               (dwarf v5)

I see.  But if we generate a synthetic test case (using the DWARF assembler
in the GDB testsuite) for the current issue, that deals with range lists,
I don't think we even need a .debug_line section, do we?  And if we do, we
always have the option of generating a v3 one.

> In contrast, here are the sections my clang compiler, when passed
> -gdwarf-5 is generating dwarf v5 versions
> of nearly all the sections:
> 
> .debug_info                      (dwarf v5)
> .debug_abbrev                 (dwarf v5)
> .debug_line                      (dwarf v5)
> .debug_str
> .debug_addr                     (dwarf v5)
> .debug_rnglists                (dwarf v5)
> .debug_str_offsets           (dwarf v5)
> .debug_gnu_pubnames   (dwarf v2)
> .debug_gnu_pubtypes     (dwarf v2)
> .debug_line_str                (dwarf v5)
> 
> So either the GNU assembler has not been updated to handle DWARF v5
> .debug_line file indices because GCC is not yet generating them; or
> GCC is not yet generating them because the GNU assembler has not yet
> been
> updated to handle them.  Take your pick.
> 
> In any case, I hope my patch is now acceptable! :-)

Simon


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-16 15:41                                               ` Simon Marchi
@ 2020-07-16 15:46                                                 ` Caroline Tice
  2020-07-16 16:09                                                   ` Simon Marchi
  0 siblings, 1 reply; 29+ messages in thread
From: Caroline Tice @ 2020-07-16 15:46 UTC (permalink / raw)
  To: Simon Marchi
  Cc: H.J. Lu, Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On Thu, Jul 16, 2020 at 8:41 AM Simon Marchi <simark@simark.ca> wrote:
>
> On 2020-07-16 12:46 a.m., Caroline Tice wrote:
> > The attached patch has an updated  commit message that explains how to
> > run the test and what version of clang was used.  Other than that it
> > is identical  to the previous patch.
>
> Thanks, I've now merged it.

Thank you!  :-)

>
> >> Just wondering, doesn't gcc emit at least *some* DWARF 5 by now?  If so,
> >> can't the GNU assembler deal with it?
> >>
> >
> > Not that it matters for this patch, but I believe you are
> > misunderstanding the situation. GCC *does* emit some  DWARF 5, but at
> > least the GCC version I'm using (Debian 9.3.0-8) does not seem to be
> > generating DWARF v5 line table sections; the line table section it
> > generates (when I compile with -gdwarf-5) is a DWARF v3 .debug_line
> > sections.
> >
> > In fact my gcc-generated binary has the following debug sections,
> > which are a mix of various dwarf versions:
> >
> > .debug_aranges               (dwarf v2)
> > .debug_info                      (dwarf v5)
> > .debug_abbrev                 (dwarf v5)
> > .debug_line                      (dwarf v3)
> > .debug_str
> > .debug_addr                    (dwarf v5)
> > .debug_gnu_pubnames  (dwarf v2)
> > .debug_gnu_pubtypes    (dwarf v2)
> > .debug_rnglists               (dwarf v5)
>
> I see.  But if we generate a synthetic test case (using the DWARF assembler
> in the GDB testsuite) for the current issue, that deals with range lists,
> I don't think we even need a .debug_line section, do we?  And if we do, we
> always have the option of generating a v3 one.

We do in fact need the .debug_line section, because the test relies on
setting a breakpoint at a particular line, and then seeing whether or
not we can find the value of a variable in the lexical block there;
without line table information I don't think GDB will be able to do
that.

>
> > In contrast, here are the sections my clang compiler, when passed
> > -gdwarf-5 is generating dwarf v5 versions
> > of nearly all the sections:
> >
> > .debug_info                      (dwarf v5)
> > .debug_abbrev                 (dwarf v5)
> > .debug_line                      (dwarf v5)
> > .debug_str
> > .debug_addr                     (dwarf v5)
> > .debug_rnglists                (dwarf v5)
> > .debug_str_offsets           (dwarf v5)
> > .debug_gnu_pubnames   (dwarf v2)
> > .debug_gnu_pubtypes     (dwarf v2)
> > .debug_line_str                (dwarf v5)
> >
> > So either the GNU assembler has not been updated to handle DWARF v5
> > .debug_line file indices because GCC is not yet generating them; or
> > GCC is not yet generating them because the GNU assembler has not yet
> > been
> > updated to handle them.  Take your pick.
> >
> > In any case, I hope my patch is now acceptable! :-)
>
> Simon
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH V5] Fix issues with reading rnglists, especially from dwo files, for DWARF v5
  2020-07-16 15:46                                                 ` Caroline Tice
@ 2020-07-16 16:09                                                   ` Simon Marchi
  0 siblings, 0 replies; 29+ messages in thread
From: Simon Marchi @ 2020-07-16 16:09 UTC (permalink / raw)
  To: Caroline Tice
  Cc: H.J. Lu, Eric Christopher, Tom Tromey, Caroline Tice via Gdb-patches

On 2020-07-16 11:46 a.m., Caroline Tice wrote:
> We do in fact need the .debug_line section, because the test relies on
> setting a breakpoint at a particular line, and then seeing whether or
> not we can find the value of a variable in the lexical block there;
> without line table information I don't think GDB will be able to do
> that.

It's possible to set breakpoints some other way, like by label (I think).

But yeah, we can still have a v3/v4 .debug_line if needed.

Simon

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2020-07-16 16:09 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-01 17:16 [PATCH] Fix issues with reading rnglists, especially from dwo files, for DWARF v5 Caroline Tice
2020-06-01 20:33 ` Tom Tromey
2020-06-02 17:04   ` Caroline Tice
2020-06-03 14:49     ` Tom Tromey
2020-06-04 21:39       ` Caroline Tice
2020-06-09 23:32         ` Caroline Tice
2020-06-16 15:37           ` Caroline Tice
2020-06-18 20:27           ` Tom Tromey
2020-06-23 19:04             ` Caroline Tice
2020-07-01  0:09               ` Caroline Tice
2020-07-01  0:34               ` Simon Marchi
2020-07-01  0:36                 ` Simon Marchi
2020-07-01 19:57                   ` Caroline Tice
2020-07-02  5:41                     ` Simon Marchi
2020-07-03 22:47                       ` [PATCH V3] " Caroline Tice
2020-07-04  5:11                         ` Simon Marchi
2020-07-09 15:48                           ` [PATCH V4] " Caroline Tice
2020-07-11 17:54                             ` Simon Marchi
2020-07-14 15:47                               ` [PATCH V5] " Caroline Tice
2020-07-15  2:04                                 ` Simon Marchi
2020-07-15  3:15                                   ` Simon Marchi
2020-07-15 16:57                                     ` Caroline Tice
2020-07-15 17:04                                       ` H.J. Lu
2020-07-15 22:35                                         ` Caroline Tice
2020-07-16  2:34                                           ` Simon Marchi
2020-07-16  4:46                                             ` Caroline Tice
2020-07-16 15:41                                               ` Simon Marchi
2020-07-16 15:46                                                 ` Caroline Tice
2020-07-16 16:09                                                   ` Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).