gdb/dwarf: split dwarf2_cu::ranges_base in two
authorSimon Marchi <simon.marchi@efficios.com>
Tue, 2 Feb 2021 15:41:59 +0000 (10:41 -0500)
committerSimon Marchi <simon.marchi@polymtl.ca>
Tue, 2 Feb 2021 15:42:11 +0000 (10:42 -0500)
Consider the test case added in this patch.  It defines a compilation
unit with a DW_AT_rnglists_base attribute (used for attributes of form
DW_FORM_rnglistx), but also uses DW_AT_ranges of form
DW_FORM_sec_offset:

    0x00000027: DW_TAG_compile_unit
                  DW_AT_ranges [DW_FORM_sec_offset] (0x0000004c
                     [0x0000000000005000, 0x0000000000006000))
                  DW_AT_rnglists_base [DW_FORM_sec_offset]  (0x00000044)

The DW_AT_rnglists_base does not play a role in reading the DW_AT_ranges of
form DW_FORM_sec_offset, but it should also not do any harm.

This case is currently not handled correctly by GDB.  This is not
something that a compiler is likely to emit, but in my opinion there's
no reason why GDB should fail reading it.

The problem is that in partial_die_info::read and a few other places
where the same logic is replicated, the cu->ranges_base value,
containing the DW_AT_rnglists_base value, is wrongfully added to the
DW_AT_ranges value.

It is quite messy how to decide whether cu->ranges_base should be added
to the attribute's value or not.  But to summarize, the only time we
want to add it is when the attribute comes from a pre-DWARF 5 split unit
file (a .dwo) [1].  In this case, the DW_AT_ranges attribute from the
split unit file will have form DW_FORM_sec_offset, pointing somewhere in
the linked file's .debug_ranges section.  *But* it's not a "true"
DW_FORM_sec_offset, in that it's an offset relative to the beginning of
that CU's contribution in the section, not relative to the beginning of
the section.  So in that case, and only that case, do we want to add the
ranges base value, which we found from the DW_AT_GNU_ranges_base
attribute on the skeleton unit.

Almost all instances of the DW_AT_ranges attribute will be found in the
split unit (on DW_TAG_subprogram, for example), and therefore need to
have the ranges base added.  However, the DW_TAG_compile_unit DIE in the
skeleton may also have a DW_AT_ranges attribute.  For that one, the
ranges base must not be added.  Once the DIEs have been loaded in GDB,
however, the distinction between what's coming from the skeleton and
what's coming from the split unit is not clear.  It is all merged in one
big happy tree.  So how do we know if a given attribute comes from the
split unit or not?

We use the fact that in pre-DWARF 5 split DWARF, DW_AT_ranges is found
on the skeleton's DW_TAG_compile_unit (in the linked file) and never in
the split unit's DW_TAG_compile_unit.  This is why you have this in
partial_die_info::read:

      int need_ranges_base = (tag != DW_TAG_compile_unit
      && attr.form != DW_FORM_rnglistx);

However, with the corner case described above (where we have a
DW_AT_rnglists_base attribute and a DW_AT_ranges attribute of form
DW_FORM_sec_offset) the condition gets it wrong when it encounters an
attribute like DW_TAG_subprogram with a DW_AT_ranges attribute of
DW_FORM_sec_offset form: it thinks that it is necessary to add the base,
when it reality it is not.

The problem boils down to failing to differentiate these cases:

  - a DW_AT_ranges attribute of form DW_FORM_sec_offset in a
    pre-DWARF 5 split unit (in which case we need to add the base)
  - a DW_AT_ranges attribute of form DW_FORM_sec_offset in a DWARF 5
    non-split unit (in which case we must not add the base)

What makes it unnecessarily complex is that the cu->ranges_base field is
overloaded, used to hold the pre-DWARF 5, non-standard
DW_AT_GNU_ranges_base and the DWARF 5 DW_AT_rnglists_base.  In reality,
these two are called "bases" but are not the same thing.  The result is
that we need twisted conditions to try to determine whether or not we
should add the base to the attribute's value.

To fix it, split the field in two distinct fields.  I renamed everything
related to the "old" ranges base to "gnu_ranges_base", to make it clear
that it's about the non-standard, pre-DWARF 5 thing.  And everything
related to the DWARF 5 thing gets renamed "rnglists".  I think it
becomes much easier to reason this way.

The issue described above gets fixed by the fact that the
DW_AT_rnglists_base value does not end up in cu->gnu_ranges_base, so
cu->gnu_ranges_base stays 0.  The condition to determine whether
gnu_ranges_base should be added can therefore be simplified back to:

  tag != DW_TAG_compile_unit

... as it was before rnglistx support was added.

Extend the gdb.dwarf2/rnglists-sec-offset.exp to cover this case.  I
also extended the test case for loclists similarly, just to see if there
would be some similar problem.  There wasn't, but I think it's not a bad
idea to test that case for loclists as well, so I left it in the patch.

[1] https://gcc.gnu.org/wiki/DebugFission

gdb/ChangeLog:

* dwarf2/die.h (struct die_info) <ranges_base>: Split in...
<gnu_ranges_base>: ... this...
<rnglists_base>: ... and this.
* dwarf2/read.c (struct dwarf2_cu) <ranges_base>: Split in...
<gnu_ranges_base>: ... this...
<rnglists_base>: ... and this.
(read_cutu_die_from_dwo): Adjust
(dwarf2_get_pc_bounds): Adjust
(dwarf2_record_block_ranges): Adjust.
(read_full_die_1): Adjust
(partial_die_info::read): Adjust.
(read_rnglist_index): Adjust.

gdb/testsuite/ChangeLog:

* gdb.dwarf2/rnglists-sec-offset.exp: Add test for DW_AT_ranges
of DW_FORM_sec_offset form plus DW_AT_rnglists_base attribute.
* gdb.dwarf2/loclists-sec-offset.exp: Add test for
DW_AT_location of DW_FORM_sec_offset plus DW_AT_loclists_base
attribute

Change-Id: Icd109038634b75d0e6e9d7d1dcb62fb9eb951d83

gdb/ChangeLog
gdb/dwarf2/die.h
gdb/dwarf2/read.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.dwarf2/loclists-sec-offset.c
gdb/testsuite/gdb.dwarf2/loclists-sec-offset.exp
gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp

index 24bf65ebc7fc7494ea0b08e376d4cdd21b3b4e37..9f60a77168e3fee3cca377902fc2d9d87b5eef84 100644 (file)
@@ -1,3 +1,18 @@
+2021-02-02  Simon Marchi  <simon.marchi@efficios.com>
+
+       * dwarf2/die.h (struct die_info) <ranges_base>: Split in...
+       <gnu_ranges_base>: ... this...
+       <rnglists_base>: ... and this.
+       * dwarf2/read.c (struct dwarf2_cu) <ranges_base>: Split in...
+       <gnu_ranges_base>: ... this...
+       <rnglists_base>: ... and this.
+       (read_cutu_die_from_dwo): Adjust
+       (dwarf2_get_pc_bounds): Adjust
+       (dwarf2_record_block_ranges): Adjust.
+       (read_full_die_1): Adjust
+       (partial_die_info::read): Adjust.
+       (read_rnglist_index): Adjust.
+
 2021-02-02  Simon Marchi  <simon.marchi@efficios.com>
 
        PR gdb/26813
index 587b9f66a4391b7a0c8e6c8374d0d187f7acc1da..792b7214023aa14b2fecf41ea08f60cec8a822d2 100644 (file)
@@ -55,26 +55,40 @@ struct die_info
     return gdb::optional<ULONGEST> ();
   }
 
-  /* Return range lists base of the compile unit, which, if exists, is
-     stored either at the attribute DW_AT_rnglists_base or
-     DW_AT_GNU_ranges_base.  */
-  ULONGEST ranges_base ()
+  /* Return the base address of the compile unit into the .debug_ranges section,
+     which, if exists, is stored in the DW_AT_GNU_ranges_base attribute.  This
+     value is only relevant in pre-DWARF 5 split-unit scenarios.  */
+  ULONGEST gnu_ranges_base ()
   {
     for (unsigned i = 0; i < num_attrs; ++i)
-      if (attrs[i].name == DW_AT_rnglists_base
-         || attrs[i].name == DW_AT_GNU_ranges_base)
+      if (attrs[i].name == DW_AT_GNU_ranges_base)
        {
          if (attrs[i].form_is_unsigned ())
-           {
-             /* If both exist, just use the first one.  */
-             return attrs[i].as_unsigned ();
-           }
-         complaint (_("ranges base attribute (offset %s) as wrong form"),
+           return attrs[i].as_unsigned ();
+
+         complaint (_("ranges base attribute (offset %s) has wrong form"),
                     sect_offset_str (sect_off));
        }
+
     return 0;
   }
 
+  /* Return the rnglists base of the compile unit, which, if exists, is stored
+     in the DW_AT_rnglists_base attribute.  */
+  ULONGEST rnglists_base ()
+  {
+    for (unsigned i = 0; i < num_attrs; ++i)
+      if (attrs[i].name == DW_AT_rnglists_base)
+       {
+         if (attrs[i].form_is_unsigned ())
+           return attrs[i].as_unsigned ();
+
+         complaint (_("rnglists base attribute (offset %s) has wrong form"),
+                    sect_offset_str (sect_off));
+       }
+
+    return 0;
+  }
 
   /* DWARF-2 tag for this DIE.  */
   ENUM_BITFIELD(dwarf_tag) tag : 16;
index ab135dcbb11778284fa1334bef628964f9c22e56..dd2a885691bf1eec00b51e214f801d86518a66e1 100644 (file)
@@ -551,16 +551,41 @@ public:
      Note this value comes from the Fission stub CU/TU's DIE.  */
   gdb::optional<ULONGEST> addr_base;
 
-  /* The DW_AT_rnglists_base attribute if present.
-     Note this value comes from the Fission stub CU/TU's DIE.
-     Also note that the value is zero in the non-DWO case so this value can
-     be used without needing to know whether DWO files are in use or not.
-     N.B. This does not apply to DW_AT_ranges appearing in
-     DW_TAG_compile_unit dies.  This is a bit of a wart, consider if ever
-     DW_AT_ranges appeared in the DW_TAG_compile_unit of DWO DIEs: then
-     DW_AT_rnglists_base *would* have to be applied, and we'd have to care
-     whether the DW_AT_ranges attribute came from the skeleton or DWO.  */
-  ULONGEST ranges_base = 0;
+  /* The DW_AT_GNU_ranges_base attribute, if present.
+
+     This is only relevant in the context of pre-DWARF 5 split units.  In this
+     context, there is a .debug_ranges section in the linked executable,
+     containing all the ranges data for all the compilation units.  Each
+     skeleton/stub unit has (if needed) a DW_AT_GNU_ranges_base attribute that
+     indicates the base of its contribution to that section.  The DW_AT_ranges
+     attributes in the split-unit are of the form DW_FORM_sec_offset and point
+     into the .debug_ranges section of the linked file.  However, they are not
+     "true" DW_FORM_sec_offset, because they are relative to the base of their
+     compilation unit's contribution, rather than relative to the beginning of
+     the section.  The DW_AT_GNU_ranges_base value must be added to it to make
+     it relative to the beginning of the section.
+
+     Note that the value is zero when we are not in a pre-DWARF 5 split-unit
+     case, so this value can be added without needing to know whether we are in
+     this case or not.
+
+     N.B. If a DW_AT_ranges attribute is found on the DW_TAG_compile_unit in the
+     skeleton/stub, it must not have the base added, as it already points to the
+     right place.  And since the DW_TAG_compile_unit DIE in the split-unit can't
+     have a DW_AT_ranges attribute, we can use the
+
+       die->tag != DW_AT_compile_unit
+
+     to determine whether the base should be added or not.  */
+  ULONGEST gnu_ranges_base = 0;
+
+  /* The DW_AT_rnglists_base attribute, if present.
+
+     This is used when processing attributes of form DW_FORM_rnglistx in
+     non-split units.  Attributes of this form found in a split unit don't
+     use it, as split-unit files have their own non-shared .debug_rnglists.dwo
+     section.  */
+  ULONGEST rnglists_base = 0;
 
   /* The DW_AT_loclists_base attribute if present.  */
   ULONGEST loclist_base = 0;
@@ -6967,10 +6992,17 @@ read_cutu_die_from_dwo (dwarf2_cu *cu,
 
       cu->addr_base = stub_comp_unit_die->addr_base ();
 
-      /* There should be a DW_AT_rnglists_base (DW_AT_GNU_ranges_base) attribute
-        here (if needed). We need the value before we can process
-        DW_AT_ranges.  */
-      cu->ranges_base = stub_comp_unit_die->ranges_base ();
+      /* There should be a DW_AT_GNU_ranges_base attribute here (if needed).
+         We need the value before we can process DW_AT_ranges values from the
+         DWO.  */
+      cu->gnu_ranges_base = stub_comp_unit_die->gnu_ranges_base ();
+
+      /* For DWARF5: record the DW_AT_rnglists_base value from the skeleton.  If
+         there are attributes of form DW_FORM_rnglistx in the skeleton, they'll
+         need the rnglists base.  Attributes of form DW_FORM_rnglistx in the
+         split unit don't use it, as the DWO has its own .debug_rnglists.dwo
+         section.  */
+      cu->rnglists_base = stub_comp_unit_die->rnglists_base ();
     }
   else if (stub_comp_dir != NULL)
     {
@@ -14659,20 +14691,14 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
       attr = dwarf2_attr (die, DW_AT_ranges, cu);
       if (attr != nullptr && attr->form_is_unsigned ())
        {
-         /* 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.
-
-            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 = (attr->as_unsigned ()
-                                       + (need_ranges_base
-                                          ? cu->ranges_base
-                                          : 0));
+         /* Offset in the .debug_ranges or .debug_rnglist section (depending
+            on DWARF version).  */
+         ULONGEST ranges_offset = attr->as_unsigned ();
+
+         /* See dwarf2_cu::gnu_ranges_base's doc for why we might want to add
+            this value.  */
+         if (die->tag != DW_TAG_compile_unit)
+           ranges_offset += cu->gnu_ranges_base;
 
          /* Value of the DW_AT_ranges attribute is the offset in the
             .debug_ranges section.  */
@@ -14837,24 +14863,17 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
   attr = dwarf2_attr (die, DW_AT_ranges, cu);
   if (attr != nullptr && attr->form_is_unsigned ())
     {
-      /* 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.
-
-        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);
+      /* Offset in the .debug_ranges or .debug_rnglist section (depending
+        on DWARF version).  */
+      ULONGEST ranges_offset = attr->as_unsigned ();
 
-      /* The value of the DW_AT_ranges attribute is the offset of the
-        address range list in the .debug_ranges section.  */
-      unsigned long offset = (attr->as_unsigned ()
-                             + (need_ranges_base ? cu->ranges_base : 0));
+      /* See dwarf2_cu::gnu_ranges_base's doc for why we might want to add
+        this value.  */
+      if (die->tag != DW_TAG_compile_unit)
+       ranges_offset += cu->gnu_ranges_base;
 
       std::vector<blockrange> blockvec;
-      dwarf2_ranges_process (offset, cu, die->tag,
+      dwarf2_ranges_process (ranges_offset, cu, die->tag,
        [&] (CORE_ADDR start, CORE_ADDR end)
        {
          start += baseaddr;
@@ -19292,7 +19311,7 @@ read_full_die_1 (const struct die_reader_specs *reader,
 
   attr = die->attr (DW_AT_rnglists_base);
   if (attr != nullptr)
-    cu->ranges_base = attr->as_unsigned ();
+    cu->rnglists_base = attr->as_unsigned ();
 
   if (any_need_reprocess)
     {
@@ -19820,26 +19839,15 @@ partial_die_info::read (const struct die_reader_specs *reader,
 
        case DW_AT_ranges:
          {
-           /* 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);
-           /* It would be nice to reuse dwarf2_get_pc_bounds here,
-              but that requires a full DIE, so instead we just
-              reimplement it.  */
-           unsigned int ranges_offset = (attr.as_unsigned ()
-                                         + (need_ranges_base
-                                            ? cu->ranges_base
-                                            : 0));
-
-           /* Value of the DW_AT_ranges attribute is the offset in the
-              .debug_ranges section.  */
+           /* Offset in the .debug_ranges or .debug_rnglist section (depending
+              on DWARF version).  */
+           ULONGEST ranges_offset = attr.as_unsigned ();
+
+           /* See dwarf2_cu::gnu_ranges_base's doc for why we might want to add
+              this value.  */
+           if (tag != DW_TAG_compile_unit)
+             ranges_offset += cu->gnu_ranges_base;
+
            if (dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, cu,
                                    nullptr, tag))
              has_pc_info = 1;
@@ -20282,8 +20290,12 @@ read_rnglist_index (struct dwarf2_cu *cu, ULONGEST rnglist_index,
   ULONGEST rnglist_header_size =
     (cu->header.initial_length_size == 4 ? RNGLIST_HEADER_SIZE32
      : RNGLIST_HEADER_SIZE64);
+
+  /* When reading a DW_FORM_rnglistx from a DWO, we read from the DWO's
+     .debug_rnglists.dwo section.  The rnglists base given in the skeleton
+     doesn't apply.  */
   ULONGEST rnglist_base =
-      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->ranges_base;
+      (cu->dwo_unit != nullptr) ? rnglist_header_size : cu->rnglists_base;
 
   /* Offset in .debug_rnglists of the offset for RNGLIST_INDEX.  */
   ULONGEST start_offset =
index ece1047c08c72d91adcf8d3aeb1aa2e4700052d9..be5afad904ebefe07ccb2284daca66a35543f6ce 100644 (file)
@@ -1,3 +1,11 @@
+2021-02-02  Simon Marchi  <simon.marchi@efficios.com>
+
+       * gdb.dwarf2/rnglists-sec-offset.exp: Add test for DW_AT_ranges
+       of DW_FORM_sec_offset form plus DW_AT_rnglists_base attribute.
+       * gdb.dwarf2/loclists-sec-offset.exp: Add test for
+       DW_AT_location of DW_FORM_sec_offset plus DW_AT_loclists_base
+       attribute
+
 2021-02-02  Simon Marchi  <simon.marchi@efficios.com>
 
        PR gdb/26813
index 2bffbf2ac4c038db814bd1a90a8da5312f50de2a..924c6ccd4126e3f330f7632f867dc6b8f8928f31 100644 (file)
@@ -29,9 +29,25 @@ func2 (void)
   return 2;
 }
 
+static int
+func3 (void)
+{
+  asm ("func3_label: .global func3_label\n");
+  return 3;
+}
+
+static int
+func4 (void)
+{
+  asm ("func4_label: .global func4_label\n");
+  return 4;
+}
+
 int
 main (void)
 {
   func1 ();
   func2 ();
+  func3 ();
+  func4 ();
 }
index 9a9188b10744055f2a0ee0cddb4374469f18f4f5..ecd85ee116c1e89e834c06fb6a39894ac176ca2f 100644 (file)
@@ -32,14 +32,18 @@ foreach_with_prefix is_64 {false true} {
        set testfile ${testfile}-dw32
     }
 
-    # Get the addresses / lengths of func1 and func2.
+    # Get the addresses / lengths of functions.
     lassign [function_range func1 $srcdir/$subdir/$srcfile] func1_addr func1_len
     lassign [function_range func2 $srcdir/$subdir/$srcfile] func2_addr func2_len
+    lassign [function_range func3 $srcdir/$subdir/$srcfile] func3_addr func3_len
+    lassign [function_range func4 $srcdir/$subdir/$srcfile] func4_addr func4_len
 
     set asm_file [standard_output_file $srcfile2]
     Dwarf::assemble $asm_file {
        global func1_addr func1_len
        global func2_addr func2_len
+       global func3_addr func3_len
+       global func4_addr func4_len
        global is_64
 
        declare_labels cu_range_list foo_range_list
@@ -50,12 +54,13 @@ foreach_with_prefix is_64 {false true} {
            version 5
            is_64 $is_64
        } {
-           declare_labels int_type
-           declare_labels foo_location_list
+           declare_labels int_type1
+           declare_labels int_type2
+           declare_labels foo_location_list bar_location_list
 
            DW_TAG_compile_unit {
            } {
-               int_type: DW_TAG_base_type {
+               int_type1: DW_TAG_base_type {
                    {DW_AT_byte_size 4 DW_FORM_data1}
                    {DW_AT_encoding @DW_ATE_signed}
                    {DW_AT_name "int"}
@@ -64,7 +69,7 @@ foreach_with_prefix is_64 {false true} {
                DW_TAG_variable {
                    {DW_AT_name "foo"}
                    {DW_AT_location $foo_location_list DW_FORM_sec_offset}
-                   {DW_AT_type :$int_type}
+                   {DW_AT_type :$int_type1}
                }
 
                DW_TAG_subprogram {
@@ -81,6 +86,43 @@ foreach_with_prefix is_64 {false true} {
            }
        }
 
+       # This CU uses the DW_FORM_sec_offset form to refer to the
+       # .debug_loclists section, but also has the DW_AT_loclists_base
+       # attribute present.  The DW_AT_loclists_base is not used to interpret
+       # the DW_AT_location value, but it should also do no harm.
+       cu {
+           version 5
+           is_64 $is_64
+       } {
+           DW_TAG_compile_unit {
+               {DW_AT_loclists_base cu2_table DW_FORM_sec_offset}
+           } {
+               int_type2: DW_TAG_base_type {
+                   {DW_AT_byte_size 4 DW_FORM_data1}
+                   {DW_AT_encoding @DW_ATE_signed}
+                   {DW_AT_name "int"}
+               }
+
+               DW_TAG_variable {
+                   {DW_AT_name "bar"}
+                   {DW_AT_location $bar_location_list DW_FORM_sec_offset}
+                   {DW_AT_type :$int_type2}
+               }
+
+               DW_TAG_subprogram {
+                   {DW_AT_name "func3"}
+                   {DW_AT_low_pc $func3_addr}
+                   {DW_AT_high_pc $func3_len DW_FORM_udata}
+               }
+
+               DW_TAG_subprogram {
+                   {DW_AT_name "func4"}
+                   {DW_AT_low_pc $func4_addr}
+                   {DW_AT_high_pc $func4_len DW_FORM_udata}
+               }
+           }
+       }
+
        loclists -is-64 $is_64 {
            # The lists in this table are accessed by direct offset
            # (DW_FORM_sec_offset).
@@ -97,6 +139,20 @@ foreach_with_prefix is_64 {false true} {
                    }
                }
            }
+
+           table -post-header-label cu2_table {
+               bar_location_list: list_ {
+                   start_length $func3_addr $func3_len {
+                       DW_OP_constu 0x345678
+                       DW_OP_stack_value
+                   }
+
+                   start_length $func4_addr $func4_len {
+                       DW_OP_constu 0x456789
+                       DW_OP_stack_value
+                   }
+               }
+           }
        }
     }
 
@@ -112,6 +168,8 @@ foreach_with_prefix is_64 {false true} {
 
     gdb_breakpoint "func1"
     gdb_breakpoint "func2"
+    gdb_breakpoint "func3"
+    gdb_breakpoint "func4"
 
     gdb_continue_to_breakpoint "func1"
     with_test_prefix "at func1" {
@@ -122,4 +180,14 @@ foreach_with_prefix is_64 {false true} {
     with_test_prefix "at func2" {
        gdb_test "print /x foo" " = 0x234567"
     }
+
+    gdb_continue_to_breakpoint "func3"
+    with_test_prefix "at func3" {
+       gdb_test "print /x bar" " = 0x345678"
+    }
+
+    gdb_continue_to_breakpoint "func4"
+    with_test_prefix "at func4" {
+       gdb_test "print /x bar" " = 0x456789"
+    }
 }
index d898d11c0dc9c91d940eb7372c1cf6fcdf2b863b..2bcbe0aeea8a519f15213fd4a6fdb15f0d88552f 100644 (file)
@@ -35,7 +35,8 @@ foreach_with_prefix is_64 {false true} {
     Dwarf::assemble $asm_file {
        global is_64
 
-       declare_labels cu_range_list foo_range_list
+       declare_labels cu1_range_list cu2_range_list
+       declare_labels foo_range_list bar_range_list
 
        # This CU uses the DW_FORM_sec_offset form to refer to the .debug_rnglists
        # section.
@@ -44,7 +45,7 @@ foreach_with_prefix is_64 {false true} {
            is_64 $is_64
        } {
            DW_TAG_compile_unit {
-               {DW_AT_ranges $cu_range_list DW_FORM_sec_offset}
+               {DW_AT_ranges $cu1_range_list DW_FORM_sec_offset}
            } {
                DW_TAG_subprogram {
                    {DW_AT_name "foo"}
@@ -53,12 +54,31 @@ foreach_with_prefix is_64 {false true} {
            }
        }
 
+       # This CU uses the DW_FORM_sec_offset form to refer to the
+       # .debug_rnglists section, but also has the DW_AT_rnglists_base
+       # attribute present.  The DW_AT_rnglists_base attribute is not used to
+       # interpret the DW_AT_ranges value, but it should also do no harm.
+       cu {
+           version 5
+           is_64 $is_64
+       } {
+           DW_TAG_compile_unit {
+               {DW_AT_ranges $cu2_range_list DW_FORM_sec_offset}
+               {DW_AT_rnglists_base cu2_table DW_FORM_sec_offset}
+           } {
+               DW_TAG_subprogram {
+                   {DW_AT_name "bar"}
+                   {DW_AT_ranges $bar_range_list DW_FORM_sec_offset}
+               }
+           }
+       }
+
        rnglists -is-64 $is_64 {
            # The lists in this table are accessed by direct offset
            # (DW_FORM_sec_offset).
            table {
-               # For the CU.
-               cu_range_list: list_ {
+               # For the first CU.
+               cu1_range_list: list_ {
                    start_end 0x4000 0x5000
                }
 
@@ -67,6 +87,18 @@ foreach_with_prefix is_64 {false true} {
                    start_end 0x4000 0x4010
                }
            }
+
+           table -post-header-label cu2_table {
+               # For the second CU.
+               cu2_range_list: list_ {
+                   start_end 0x5000 0x6000
+               }
+
+               # For the bar function.
+               bar_range_list: list_ {
+                   start_end 0x5000 0x5010
+               }
+           }
        }
     }
 
@@ -77,4 +109,5 @@ foreach_with_prefix is_64 {false true} {
 
     # Sanity checks to make sure GDB slurped the symbols correctly.
     gdb_test "p/x &foo" " = 0x4000"
+    gdb_test "p/x &bar" " = 0x5000"
 }