diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 24bf65ebc7..9f60a77168 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,18 @@ +2021-02-02 Simon Marchi + + * dwarf2/die.h (struct die_info) : Split in... + : ... this... + : ... and this. + * dwarf2/read.c (struct dwarf2_cu) : Split in... + : ... this... + : ... 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 PR gdb/26813 diff --git a/gdb/dwarf2/die.h b/gdb/dwarf2/die.h index 587b9f66a4..792b721402 100644 --- a/gdb/dwarf2/die.h +++ b/gdb/dwarf2/die.h @@ -55,26 +55,40 @@ struct die_info return gdb::optional (); } - /* 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; diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index ab135dcbb1..dd2a885691 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -551,16 +551,41 @@ public: Note this value comes from the Fission stub CU/TU's DIE. */ gdb::optional 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. + /* Offset in the .debug_ranges or .debug_rnglist section (depending + on DWARF version). */ + ULONGEST ranges_offset = attr->as_unsigned (); - 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)); + /* 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. + /* Offset in the .debug_ranges or .debug_rnglist section (depending + on DWARF version). */ + ULONGEST ranges_offset = attr->as_unsigned (); - 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. */ - 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 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. + /* Offset in the .debug_ranges or .debug_rnglist section (depending + on DWARF version). */ + ULONGEST ranges_offset = attr.as_unsigned (); - 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)); + /* 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; - /* 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, 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 = diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ece1047c08..be5afad904 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2021-02-02 Simon Marchi + + * 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 PR gdb/26813 diff --git a/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.c b/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.c index 2bffbf2ac4..924c6ccd41 100644 --- a/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.c +++ b/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.c @@ -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 (); } diff --git a/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.exp b/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.exp index 9a9188b107..ecd85ee116 100644 --- a/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.exp +++ b/gdb/testsuite/gdb.dwarf2/loclists-sec-offset.exp @@ -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" + } } diff --git a/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp b/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp index d898d11c0d..2bcbe0aeea 100644 --- a/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp +++ b/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp @@ -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" }