2021-02-02 23:40:52 +08:00
|
|
|
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
This file is part of GDB.
|
|
|
|
|
|
|
|
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/>. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
func1 (void)
|
|
|
|
{
|
|
|
|
asm ("func1_label: .global func1_label\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
func2 (void)
|
|
|
|
{
|
|
|
|
asm ("func2_label: .global func2_label\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
gdb/dwarf: split dwarf2_cu::ranges_base in two
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
2021-02-02 23:41:59 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
gdb/testsuite: add test for .debug_{rng,loc}lists section without offset array
It is possible for the tables in the .debug_{rng,loc}lists sections to
not have an array of offsets. In that case, the offset_entry_count
field of the header is 0. The forms DW_FORM_{rng,loc}listx (reference
by index) can't be used with that table. Instead, the
DW_FORM_sec_offset form, which references a {rng,loc}list by direct
offset in the section, must be used. From what I saw, this is what GCC
currently produces.
Add tests for this case. I didn't see any bug related to this, I just
think that it would be nice to have coverage for this. A new
`-with-offset-array` option is added to the `table` procs, used when
generating {rng,loc}lists, to decide whether to generate the offset
array.
gdb/testsuite/ChangeLog:
* lib/dwarf.exp (rnglists): Add -no-offset-array option to
table proc.
* gdb.dwarf2/rnglists-sec-offset.exp: Add test for
.debug_rnglists table without offset array.
* gdb.dwarf2/loclists-sec-offset.exp: Add test for
.debug_loclists table without offset array.
Change-Id: I8e34a7bf68c9682215ffbbf66600da5b7db91ef7
2021-02-02 23:40:53 +08:00
|
|
|
static int
|
|
|
|
func5 (void)
|
|
|
|
{
|
|
|
|
asm ("func5_label: .global func5_label\n");
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
func6 (void)
|
|
|
|
{
|
|
|
|
asm ("func6_label: .global func6_label\n");
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
|
2021-02-02 23:40:52 +08:00
|
|
|
int
|
|
|
|
main (void)
|
|
|
|
{
|
|
|
|
func1 ();
|
|
|
|
func2 ();
|
gdb/dwarf: split dwarf2_cu::ranges_base in two
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
2021-02-02 23:41:59 +08:00
|
|
|
func3 ();
|
|
|
|
func4 ();
|
gdb/testsuite: add test for .debug_{rng,loc}lists section without offset array
It is possible for the tables in the .debug_{rng,loc}lists sections to
not have an array of offsets. In that case, the offset_entry_count
field of the header is 0. The forms DW_FORM_{rng,loc}listx (reference
by index) can't be used with that table. Instead, the
DW_FORM_sec_offset form, which references a {rng,loc}list by direct
offset in the section, must be used. From what I saw, this is what GCC
currently produces.
Add tests for this case. I didn't see any bug related to this, I just
think that it would be nice to have coverage for this. A new
`-with-offset-array` option is added to the `table` procs, used when
generating {rng,loc}lists, to decide whether to generate the offset
array.
gdb/testsuite/ChangeLog:
* lib/dwarf.exp (rnglists): Add -no-offset-array option to
table proc.
* gdb.dwarf2/rnglists-sec-offset.exp: Add test for
.debug_rnglists table without offset array.
* gdb.dwarf2/loclists-sec-offset.exp: Add test for
.debug_loclists table without offset array.
Change-Id: I8e34a7bf68c9682215ffbbf66600da5b7db91ef7
2021-02-02 23:40:53 +08:00
|
|
|
func5 ();
|
|
|
|
func6 ();
|
2021-02-02 23:40:52 +08:00
|
|
|
}
|