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;
 
      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;
 
       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)
     {
       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.  */
   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;
 
   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)
     {
 
        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;
   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 =
 
        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
            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"}
                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 {
            }
        }
 
+       # 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).
                    }
                }
            }
+
+           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
+                   }
+               }
+           }
        }
     }
 
 
     gdb_breakpoint "func1"
     gdb_breakpoint "func2"
+    gdb_breakpoint "func3"
+    gdb_breakpoint "func4"
 
     gdb_continue_to_breakpoint "func1"
     with_test_prefix "at func1" {
     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"
+    }
 }