Bug 99490 - [11 Regression] -gdwarf-5 -gsplit-dwarf puts .debug_rnglists to main file, not .dwo file
Summary: [11 Regression] -gdwarf-5 -gsplit-dwarf puts .debug_rnglists to main file, no...
Alias: None
Product: gcc
Classification: Unclassified
Component: debug (show other bugs)
Version: 11.0
: P3 normal
Target Milestone: 11.0
Assignee: Not yet assigned to anyone
Keywords: wrong-debug
Depends on:
Reported: 2021-03-09 16:47 UTC by Jan Kratochvil
Modified: 2022-01-01 01:22 UTC (History)
4 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed: 2021-03-09 00:00:00

range-clang.s (2.82 KB, text/plain)
2021-03-09 16:48 UTC, Jan Kratochvil
range-gcc.s (1.87 KB, text/plain)
2021-03-09 16:48 UTC, Jan Kratochvil
gcc11-pr99490-wip.patch (2.19 KB, patch)
2021-03-10 11:35 UTC, Jakub Jelinek
Details | Diff
gcc11-pr99490-wip.patch (2.62 KB, patch)
2021-03-10 12:03 UTC, Jakub Jelinek
Details | Diff
pr99490.tar.xz (4.39 KB, application/x-xz)
2021-03-10 12:06 UTC, Jakub Jelinek
gcc11-pr99490.patch (4.17 KB, patch)
2021-03-11 12:20 UTC, Jakub Jelinek
Details | Diff
pr99490-2.tar.xz (3.87 KB, application/x-xz)
2021-03-11 12:46 UTC, Jakub Jelinek

Note You need to log in before you can comment on or make changes to this bug.
Description Jan Kratochvil 2021-03-09 16:47:44 UTC
FAIL: gcc-11.0.0-0.19.fc35.x86_64
PASS: clang-12.0.0-0.1.rc1.fc35.x86_64

double p_ext=3.14,e_ext=2.71;
int main(void) {
  double q,f;
  { volatile double p=p_ext;
  { volatile double e=e_ext;
  return q+f;

clang -o range-clang range.c -Wall -O3 -gdwarf-5 -gsplit-dwarf;gcc -o range-gcc.s range.c -Wall -O3 -gdwarf-5 -gsplit-dwarf
llvm-readelf -WS range-{clang,gcc}{,.dwo} | egrep '^File:|debug_rnglists'
File: range-clang
File: range-clang.dwo
  [ 6] .debug_rnglists.dwo PROGBITS      0000000000000000 0001d4 000017 00   E  0   0  1
File: range-gcc
  [33] .debug_rnglists   PROGBITS        0000000000000000 004c0a 000049 00      0   0  1
File: range-gcc.dwo
File: range-gcc
File: range-gcc.dwo
  [33] .debug_rnglists   PROGBITS        0000000000000000 004c0a 000049 00      0   0  1

DWARF-5 spec:
"The .debug_rnglists.dwosection contains range lists referenced by any DW_AT_ranges attributes in the split DWARF object."
"Range lists are contained in a separate object file section called .debug_rnglists or .debug_rnglists.dwo (in split units)."

Reproducer from attached .s files:
: clang -S -o range-clang.s range.c -Wall -O3 -gdwarf-5 -gsplit-dwarf;clang -o range-clang range-clang.s -Wall -gdwarf-5 -gsplit-dwarf;: gcc -S -o range-gcc.s range.c -Wall -O3 -gdwarf-5 -gsplit-dwarf;gcc -o range-gcc range-gcc.s -Wall -gdwarf-5 -gsplit-dwarf
Comment 1 Jan Kratochvil 2021-03-09 16:48:15 UTC
Created attachment 50340 [details]
Comment 2 Jan Kratochvil 2021-03-09 16:48:36 UTC
Created attachment 50341 [details]
Comment 3 Jan Kratochvil 2021-03-09 20:10:59 UTC
LLDB testsuite failure from it is:

PASS: LLDB (/usr/bin/gcc-x86_64) :: test_with_run_command_dwarf (TestTypeCompletion.TypeCompletionTestCase)
error: a.out {0x00005850}: DIE has DW_AT_ranges(0x0) attribute, but range extraction failed (missing or invalid range list table), please file a bug and attach the file at the start of this error message
FAIL: LLDB (/usr/bin/gcc-x86_64) :: test_with_run_command_dwo (TestTypeCompletion.TypeCompletionTestCase)
Failed Tests (1):
  lldb-api :: functionalities/type_completion/TestTypeCompletion.py
Comment 4 Jakub Jelinek 2021-03-10 09:53:41 UTC
The DWARF4 split DWARF extension used .debug_ranges rather than .debug_ranges.dwo and the code hasn't been adjusted for -gdwarf-5 -gsplit-dwarf.  It needs quite a lot of changes though.
Comment 5 Mark Wielaard 2021-03-10 10:17:06 UTC
I don't believe it is a requirement to generate a separate .debug_rnglists.dwo section, the spec says the same data can be provided in the .debug_rnglists section and gdb and elfutils handle that just fine for split dwarf. If we would generate a .debug_rnglists.dwo section then we have to make sure it only contains DW_RLE_ entries that don't require relocations (like we already do for .loclists and DW_LLE_ entries).
Comment 6 Jakub Jelinek 2021-03-10 11:35:02 UTC
Created attachment 50348 [details]

Incomplete patch I've been playing with.  It clearly isn't enough though.
If I add void foo () {} void bar () {} to your testcase and compile with
clang -gdwarf-5 -gsplit-dwarf -O2 -ffunction-sections, I see it emits both
.debug_rnglists and .debug_rnglists.dwo, the former contains only the single range list that the DW_TAG_skeleton_unit needs (and uses DW_AT_ranges and DW_AT_rnglists_base in it), while the latter contains what the DW_AT_ranges in .debug_info.dwo DIEs need (and doesn't contain DW_AT_rnglists_base).

The question is if it has to be that way.  Can't e.g. the DW_AT_ranges attribute
for the CU not be present on the DW_TAG_skeleton_unit in .debug_info, but instead on the DW_TAG_compile_unit DIE in .debug_info.dwo?  In that case we wouldn't need the .debug_rnglists section, nor DW_AT_rnglists_base attribute.
Comment 7 Jan Kratochvil 2021-03-10 11:43:22 UTC
It would be nice if you could provide the .s/.o/.dwo files so that one does not have to rebuild the patched GCC.
Comment 8 Jakub Jelinek 2021-03-10 12:03:52 UTC
Created attachment 50349 [details]

Updated patch that removes DW_AT_rnglists_base and moves DW_AT_ranges from the skeleton unit to corresponding compile unit.
Comment 9 Jakub Jelinek 2021-03-10 12:06:40 UTC
Created attachment 50350 [details]

Tarball with the updated patch and 4 versions of assembly, all with -gdwarf-5 -gsplit-dwarf -O2, s0 is vanilla trunk (from a few days ago), s1 the same but with -ffunction-sections, s2 with the latest patch and s3 with the latest patch and -ffunction-sections.
Comment 10 Jan Kratochvil 2021-03-10 12:29:37 UTC
clang is using DW_AT_ranges+DW_FORM_rnglistx+DW_AT_rnglists_base in the main file but in the DWO file it assumes DW_AT_rnglists_base is right after the .debug_rnglists header (as it does not make sense to point it anywhere else in a DWO file having single CU). It makes sense although I do not see it specified in DWARF-5:

.debug_info contents:
0x00000014: DW_TAG_skeleton_unit [1]  
              DW_AT_ranges [DW_FORM_rnglistx]   (indexed (0x0) rangelist = 0x00000010
                 [0x0000000000401110, 0x0000000000401111)
                 [0x0000000000401120, 0x0000000000401121)
                 [0x0000000000401130, 0x0000000000401171))
              DW_AT_rnglists_base [DW_FORM_sec_offset]  (0x0000000c)

.debug_info.dwo contents:
0x00000014: DW_TAG_compile_unit [1] *
                  DW_AT_ranges [DW_FORM_rnglistx]       (indexed (0x0) rangelist = 0x00000010
                     [0x0000000000000004, 0x0000000000000016)
                     [0x0000000000000022, 0x0000000000000034))

Comment 11 Jakub Jelinek 2021-03-10 22:10:44 UTC
DWARF5 3.1.3 says:
The following attributes are not part of a split full compilation unit entry but instead are inherited (if present) from the corresponding skeleton compilation unit: DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_stmt_list, DW_AT_comp_dir,
DW_AT_str_offsets_base, DW_AT_addr_base and DW_AT_rnglists_base.
So, my current patch is not 100% correct.
Comment 12 Jan Kratochvil 2021-03-10 22:17:21 UTC
That looks as a DWARF5 bug to me. DW_AT_str_offsets_base, DW_AT_addr_base and DW_AT_rnglists_base do not make any sense for a split unit. Split unit contains only one CompileUnit (and optionally one TypeUnit where only DW_AT_str_offsets_base would have some sense).
Comment 13 Jakub Jelinek 2021-03-10 22:21:16 UTC
That sentence says that they should not be in the DW_TAG_compile_unit in .debug_info.dwo, but may be instead (if present at all) in DW_TAG_skeleton_unit in .debug_info.  Haven't checked all, but DW_AT_addr_base is definitely needed whenever something in the dwo refers to .debug_addr (so that one knows to which part of .debug_addr it refers to).
Comment 14 Jan Kratochvil 2021-03-10 23:18:45 UTC
DW_AT_addr_base is for .debug_addr in the main file, I agree, my mistake. That should be inherited from the skeleton to the split-unit.

But DW_AT_loclists_base, DW_AT_rnglists_base and DW_AT_str_offsets_base point to .debug_loclists.dwo, .debug_rnglists.dwo and .debug_str_offsets.dwo respectively.
As there is only one CU in a DWO they make no sense to be inherited from the skeleton unit to split-unit.
Comment 15 Jakub Jelinek 2021-03-11 12:20:30 UTC
Created attachment 50364 [details]

Updated patch that implements what has been discussed on dwarf mailing list.
Comment 16 Jakub Jelinek 2021-03-11 12:46:54 UTC
Created attachment 50365 [details]

And for Jan, assembly for the pr99490.c from the first tarball with the latest patch.
Comment 17 Jan Kratochvil 2021-03-12 09:00:26 UTC
Yes, the testcase TestTypeCompletion.py category 'dwo' is now fixed with the patch from Comment 15.
Comment 18 Jakub Jelinek 2021-03-12 09:22:23 UTC
Marking this as a regression because we've switched the default from -gdwarf-4 to -gdwarf-5, so code compiled with -g -gsplit-dwarf used to produce something valid that consumers could handle and now it doesn't, even when they have DWARF5 support.
Comment 19 CVS Commits 2021-03-31 19:27:27 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:


commit r11-7938-g4b33c5aaab9e863da162942ab8bcd54070b705af
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Wed Mar 31 21:25:58 2021 +0200

    dwarf2out: Fix up ranges for -gdwarf-5 -gsplit-dwarf [PR99490]
    For -gdwarf-4 -gsplit-dwarf we used to emit .debug_ranges section
    (so in the binaries/shared libraries) with DW_AT_ranges from skeleton
    units as well as .debug_info.dwo pointing to it through DW_FORM_sec_offset
    (and DW_AT_GNU_ranges_base pointing into section, not sure for what
    reason exactly).
    When DWARF5 support was being added, we've started using .debug_rnglists
    section, added DW_AT_rnglists_base to the DW_TAG_skeleton_unit, kept
    DW_AT_ranges with DW_FORM_sec_offset in the skeleton and switched
    over to DW_FORM_rnglistx for DW_AT_ranges in .debug_info.dwo.
    But the DWARF5 spec actually means for the ranges section (at least
    everything for those DW_AT_ranges in .debug_info.dwo) to sit
    in .debug_rnglists.dwo section next to the .debug_info.dwo, rather than
    having consumers look it up in the binary/shared library instead.
    Based on some discussions in the DWARF discuss mailing list:
    this patch mostly follows what LLVM emits for that right now:
    1) small .debug_rnglists section (when needed) just to cover the
       skeleton DW_AT_ranges (if present); the content of the section
       uses the Split DWARFy DW_RLE_* codes with addrx encodings where
    2) DW_AT_ranges in the skeleton uses DW_FORM_sec_offset (difference
       from LLVM which uses DW_FORM_rnglistx, which makes it larger
       and ambiguous)
    3) DW_AT_rnglists_base attribute is gone from the skeleton (again,
       unlike LLVM where it is just confusing what exactly it means because
       it is inherited; it would make sense if we emitted DW_FORM_rnglistx
       in non-split DWARF, but unless ranges are shared, I'm afraid we'd
       make DWARF larger with fewer relocations by that)
    4) usually big .debug_rnglists.dwo section again with using DW_RLE_*x*
       where possible
    5) DW_AT_ranges with DW_FORM_rnglistx from .debug_info.dwo referring to
       that .debug_rnglists.dwo ranges
    2021-03-31  Jakub Jelinek  <jakub@redhat.com>
            PR debug/99490
            * dwarf2out.c (debug_ranges_dwo_section): New variable.
            (DW_RANGES_IDX_SKELETON): Define.
            (struct dw_ranges): Add begin_entry and end_entry members.
            (DEBUG_DWO_RNGLISTS_SECTION): Define.
            (add_ranges_num): Adjust r initializer for addition of *_entry
            (add_ranges_by_labels): For -gsplit-dwarf and force_direct,
            set idx to DW_RANGES_IDX_SKELETON.
            (use_distinct_base_address_for_range): New function.
            (index_rnglists): Don't set r->idx if it is equal to
            DW_RANGES_IDX_SKELETON.  Initialize r->begin_entry and
            r->end_entry for -gsplit-dwarf if those will be needed by
            (output_rnglists): Add DWO argument.  If true, switch to
            debug_ranges_dwo_section rather than debug_ranges_section.
            Adjust l1/l2 label indexes.  Only output the offset table when
            dwo is true and don't include in there the skeleton range
            entry if present.  For -gsplit-dwarf, skip ranges that belong
            to the other rnglists section.  Change return type from void
            to bool and return true if there are any range entries for
            the other section.  For dwarf_split_debug_info use
            DW_RLE_startx_endx, DW_RLE_startx_length and DW_RLE_base_addressx
            entries instead of DW_RLE_start_end, DW_RLE_start_length and
            DW_RLE_base_address.  Use use_distinct_base_address_for_range.
            (init_sections_and_labels): Initialize debug_ranges_dwo_section
            if -gsplit-dwarf and DWARF >= 5.  Adjust ranges_section_label
            and range_base_label indexes.
            (dwarf2out_finish): Call index_rnglists earlier before finalizing
            .debug_addr.  Never emit DW_AT_rnglists_base attribute.  For
            -gsplit-dwarf and DWARF >= 5 call output_rnglists up to twice
            with different dwo arguments.
            (dwarf2out_c_finalize): Clear debug_ranges_dwo_section.
Comment 20 Jakub Jelinek 2021-03-31 19:39:08 UTC