mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-10 17:43:16 +00:00
On openSUSE Leap 15.6 x86_64, with gcc 7 and test-case
gdb.base/condbreak-multi-context.exp I run into:
...
(gdb) print aaa^M
$3 = <optimized out>^M
(gdb) FAIL: $exp: start_before=true: scenario_1: print aaa
...
This is a regression since commit 86ac8c5462 ("Convert
lookup_symbol_in_objfile").
Likewise in test-cases gdb.cp/m-static.exp and gdb.cp/namespace.exp.
The failure is specific to using Dwarf v4:
- using target board unix/gdb:debug_flags=-gdwarf-5 fixes it
- using target board unix/gdb:debug_flags=-gdwarf-4 on Tumbleweed (with gcc 15
and Dwarf v5 default) triggers it
The variable we're trying to print, A::aaa is a static const int member:
...
class A : public Base
{
public:
static const int aaa = 10;
...
};
...
With Dwarf v5, we have this DIE:
...
<2><356>: Abbrev Number: 2 (DW_TAG_variable)
<357> DW_AT_name : aaa
<35c> DW_AT_linkage_name: _ZN1A3aaaE
<364> DW_AT_external : 1
<364> DW_AT_accessibility: 1 (public)
<364> DW_AT_declaration : 1
<364> DW_AT_const_value : 10
...
and the cooked index contains these corresponding entries:
...
[45] ((cooked_index_entry *) 0x7facf0004730)
name: _ZN1A3aaaE
canonical: _ZN1A3aaaE
qualified: _ZN1A3aaaE
DWARF tag: DW_TAG_variable
flags: 0x4 [IS_LINKAGE]
DIE offset: 0x356
parent: ((cooked_index_entry *) 0)
[52] ((cooked_index_entry *) 0x7facf0004700)
name: aaa
canonical: aaa
qualified: A::aaa
DWARF tag: DW_TAG_variable
flags: 0x0 []
DIE offset: 0x356
parent: ((cooked_index_entry *) 0x7facf00046d0) [A]
...
With Dwarf v4, we have instead the following DIE:
...
<2><350>: Abbrev Number: 3 (DW_TAG_member)
<351> DW_AT_name : aaa
<35b> DW_AT_external : 1
<35b> DW_AT_accessibility: 1 (public)
<35c> DW_AT_declaration : 1
<35c> DW_AT_const_value : 4 byte block: a 0 0 0
...
and there are no corresponding entries.
Fix this by adding an entry:
...
[47] ((cooked_index_entry *) 0x7f5a24004660)
name: aaa
canonical: aaa
qualified: A::aaa
DWARF tag: DW_TAG_member
flags: 0x0 []
DIE offset: 0x350
parent: ((cooked_index_entry *) 0x7f5a24004630) [A]
...
Add a regression test in the form of a dwarf assembly test-case printing the
value of variable A::aaa.
In the test-case, for A::aaa, DW_FORM_flag is used to encode
DW_AT_declaration. In v1 of this patch that mattered (because of using
has_hardcoded_declaration in abbrev_table::read), but that's no longer the
case. Nevertheless, also add an A::bbb using FORM_flag_present for
DW_AT_declaration (and while we're at it, DW_AT_external as well).
Also add two variants, for which <optimized out> is printed:
- A::ccc: a variant with DW_AT_location instead of DW_AT_const_value, and
- A::ddd: a variant without external.
This behavior is not part of the regression. I've reproduced it using a
system gdb based on 14.2. It's also not specific to using the cooked index,
it reproduces with -readnow as well.
Tested on x86_64-linux.
Approved-By: Tom Tromey <tom@tromey.com>
PR symtab/33415
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33415
330 lines
9.4 KiB
C
330 lines
9.4 KiB
C
/* DWARF 2 abbreviations
|
|
|
|
Copyright (C) 1994-2025 Free Software Foundation, Inc.
|
|
|
|
Adapted by Gary Funck (gary@intrepid.com), Intrepid Technology,
|
|
Inc. with support from Florida State University (under contract
|
|
with the Ada Joint Program Office), and Silicon Graphics, Inc.
|
|
Initial contribution by Brent Benson, Harris Computer Systems, Inc.,
|
|
based on Fred Fish's (Cygnus Support) implementation of DWARF 1
|
|
support.
|
|
|
|
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/>. */
|
|
|
|
#include "dwarf2/abbrev.h"
|
|
#include "dwarf2/leb.h"
|
|
#include "dwarf2/section.h"
|
|
#include "bfd.h"
|
|
|
|
/* Helper function that returns true if a DIE with the given tag might
|
|
plausibly be indexed. */
|
|
|
|
static bool
|
|
tag_interesting_for_index (dwarf_tag tag)
|
|
{
|
|
switch (tag)
|
|
{
|
|
case DW_TAG_array_type:
|
|
case DW_TAG_base_type:
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_constant:
|
|
case DW_TAG_entry_point:
|
|
case DW_TAG_enumeration_type:
|
|
case DW_TAG_enumerator:
|
|
case DW_TAG_imported_declaration:
|
|
case DW_TAG_imported_unit:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_interface_type:
|
|
case DW_TAG_module:
|
|
case DW_TAG_namespace:
|
|
case DW_TAG_ptr_to_member_type:
|
|
case DW_TAG_set_type:
|
|
case DW_TAG_string_type:
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_subrange_type:
|
|
case DW_TAG_generic_subrange:
|
|
case DW_TAG_subroutine_type:
|
|
case DW_TAG_typedef:
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_unspecified_type:
|
|
case DW_TAG_variable:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Read in an abbrev table. */
|
|
|
|
abbrev_table_up
|
|
abbrev_table::read (struct dwarf2_section_info *section,
|
|
sect_offset sect_off)
|
|
{
|
|
bfd *abfd = section->get_bfd_owner ();
|
|
const gdb_byte *abbrev_ptr;
|
|
struct abbrev_info *cur_abbrev;
|
|
|
|
abbrev_table_up abbrev_table (new struct abbrev_table (sect_off, section));
|
|
struct obstack *obstack = &abbrev_table->m_abbrev_obstack;
|
|
|
|
/* Caller must ensure this. */
|
|
gdb_assert (section->read_in);
|
|
abbrev_ptr = section->buffer + to_underlying (sect_off);
|
|
|
|
while (true)
|
|
{
|
|
unsigned int bytes_read;
|
|
/* Loop until we reach an abbrev number of 0. */
|
|
unsigned int abbrev_number = read_unsigned_leb128 (abfd, abbrev_ptr,
|
|
&bytes_read);
|
|
if (abbrev_number == 0)
|
|
break;
|
|
abbrev_ptr += bytes_read;
|
|
|
|
/* Start without any attrs. */
|
|
obstack_blank (obstack, offsetof (abbrev_info, attrs));
|
|
cur_abbrev = (struct abbrev_info *) obstack_base (obstack);
|
|
|
|
/* Read in abbrev header. */
|
|
cur_abbrev->number = abbrev_number;
|
|
cur_abbrev->tag
|
|
= (enum dwarf_tag) read_unsigned_leb128 (abfd, abbrev_ptr,
|
|
&bytes_read);
|
|
abbrev_ptr += bytes_read;
|
|
cur_abbrev->has_children = read_1_byte (abfd, abbrev_ptr);
|
|
abbrev_ptr += 1;
|
|
|
|
cur_abbrev->maybe_ada_import = false;
|
|
|
|
unsigned int size = 0;
|
|
unsigned int sibling_offset = -1;
|
|
bool is_csize = true;
|
|
|
|
bool has_hardcoded_declaration = false;
|
|
bool has_specification_or_origin = false;
|
|
bool has_name = false;
|
|
bool has_linkage_name = false;
|
|
bool has_external = false;
|
|
bool has_const_value = false;
|
|
|
|
/* Now read in declarations. */
|
|
int num_attrs = 0;
|
|
for (;;)
|
|
{
|
|
struct attr_abbrev cur_attr;
|
|
|
|
cur_attr.name
|
|
= (enum dwarf_attribute) read_unsigned_leb128 (abfd, abbrev_ptr,
|
|
&bytes_read);
|
|
abbrev_ptr += bytes_read;
|
|
cur_attr.form
|
|
= (enum dwarf_form) read_unsigned_leb128 (abfd, abbrev_ptr,
|
|
&bytes_read);
|
|
abbrev_ptr += bytes_read;
|
|
if (cur_attr.form == DW_FORM_implicit_const)
|
|
{
|
|
cur_attr.implicit_const = read_signed_leb128 (abfd, abbrev_ptr,
|
|
&bytes_read);
|
|
abbrev_ptr += bytes_read;
|
|
}
|
|
else
|
|
cur_attr.implicit_const = -1;
|
|
|
|
if (cur_attr.name == 0)
|
|
break;
|
|
|
|
switch (cur_attr.name)
|
|
{
|
|
case DW_AT_declaration:
|
|
if (cur_attr.form == DW_FORM_flag_present)
|
|
has_hardcoded_declaration = true;
|
|
break;
|
|
|
|
case DW_AT_external:
|
|
has_external = true;
|
|
break;
|
|
|
|
case DW_AT_specification:
|
|
case DW_AT_abstract_origin:
|
|
case DW_AT_extension:
|
|
has_specification_or_origin = true;
|
|
break;
|
|
|
|
case DW_AT_name:
|
|
has_name = true;
|
|
break;
|
|
|
|
case DW_AT_MIPS_linkage_name:
|
|
case DW_AT_linkage_name:
|
|
has_linkage_name = true;
|
|
break;
|
|
|
|
case DW_AT_sibling:
|
|
if (is_csize && cur_attr.form == DW_FORM_ref4)
|
|
sibling_offset = size;
|
|
break;
|
|
|
|
case DW_AT_const_value:
|
|
has_const_value = true;
|
|
break;
|
|
}
|
|
|
|
switch (cur_attr.form)
|
|
{
|
|
case DW_FORM_data1:
|
|
case DW_FORM_ref1:
|
|
case DW_FORM_flag:
|
|
case DW_FORM_strx1:
|
|
size += 1;
|
|
break;
|
|
case DW_FORM_flag_present:
|
|
case DW_FORM_implicit_const:
|
|
break;
|
|
case DW_FORM_data2:
|
|
case DW_FORM_ref2:
|
|
case DW_FORM_strx2:
|
|
size += 2;
|
|
break;
|
|
case DW_FORM_strx3:
|
|
size += 3;
|
|
break;
|
|
case DW_FORM_data4:
|
|
case DW_FORM_ref4:
|
|
case DW_FORM_strx4:
|
|
size += 4;
|
|
break;
|
|
case DW_FORM_data8:
|
|
case DW_FORM_ref8:
|
|
case DW_FORM_ref_sig8:
|
|
size += 8;
|
|
break;
|
|
case DW_FORM_data16:
|
|
size += 16;
|
|
break;
|
|
|
|
default:
|
|
is_csize = false;
|
|
break;
|
|
}
|
|
|
|
++num_attrs;
|
|
obstack_grow (obstack, &cur_attr, sizeof (cur_attr));
|
|
}
|
|
|
|
cur_abbrev = (struct abbrev_info *) obstack_finish (obstack);
|
|
cur_abbrev->num_attrs = num_attrs;
|
|
|
|
if (!has_name && !has_linkage_name && !has_specification_or_origin)
|
|
{
|
|
/* Some anonymous DIEs are worth examining. */
|
|
cur_abbrev->interesting
|
|
= (cur_abbrev->tag == DW_TAG_namespace
|
|
|| cur_abbrev->tag == DW_TAG_enumeration_type);
|
|
}
|
|
else if ((cur_abbrev->tag == DW_TAG_structure_type
|
|
|| cur_abbrev->tag == DW_TAG_class_type
|
|
|| cur_abbrev->tag == DW_TAG_union_type
|
|
|| cur_abbrev->tag == DW_TAG_namespace)
|
|
&& cur_abbrev->has_children)
|
|
{
|
|
/* We have to record this as interesting, regardless of how
|
|
DW_AT_declaration is set, so that any subsequent
|
|
DW_AT_specification pointing at a child of this will get
|
|
the correct scope. */
|
|
cur_abbrev->interesting = true;
|
|
}
|
|
else if (cur_abbrev->tag == DW_TAG_member && has_const_value
|
|
&& has_external)
|
|
{
|
|
/* For a static const member with initializer, gcc (and likewise
|
|
clang) generates a DW_TAG_member with a DW_AT_const_value
|
|
attribute when using DWARF v4 or earlier.
|
|
|
|
class A {
|
|
static const int aaa = 11;
|
|
};
|
|
|
|
<2><28>: Abbrev Number: 3 (DW_TAG_member)
|
|
<29> DW_AT_name : aaa
|
|
<34> DW_AT_external : 1
|
|
<34> DW_AT_declaration : 1
|
|
<34> DW_AT_const_value : 11
|
|
|
|
That's not the case if we move the initializer out of the class
|
|
declaration, we get a DW_TAG_variable representing the value:
|
|
|
|
class A {
|
|
static const int aaa;
|
|
};
|
|
const int A::aaa = 11;
|
|
|
|
<2><28>: Abbrev Number: 3 (DW_TAG_member)
|
|
<29> DW_AT_name : aaa
|
|
<34> DW_AT_external : 1
|
|
<34> DW_AT_declaration : 1
|
|
<1><41>: Abbrev Number: 6 (DW_TAG_variable)
|
|
<42> DW_AT_specification: <0x28>
|
|
<48> DW_AT_linkage_name: _ZN1A3aaaE
|
|
<4c> DW_AT_location : 9 byte block: (DW_OP_addr: 0)
|
|
|
|
We could try to cater here for the case that we have a
|
|
DW_AT_location instead of a DW_AT_const_value, but that doesn't
|
|
seem to be handled by the rest of GDB.
|
|
|
|
I did find an example of a non-external one for std::_Function_base
|
|
::_Base_manager<get_compile_file_tempdir()::<lambda()> >
|
|
::__stored_locally:
|
|
|
|
<4><27bd192>: Abbrev Number: 279 (DW_TAG_member)
|
|
<27bd194> DW_AT_name : __stored_locally
|
|
<27bd19e> DW_AT_accessibility: 2 (protected)
|
|
<27bd19f> DW_AT_declaration : 1
|
|
<27bd19f> DW_AT_const_value : 1 byte block: 1
|
|
|
|
but again that doesn't seem to be handled by the rest of GDB. */
|
|
cur_abbrev->interesting = true;
|
|
}
|
|
else if (has_hardcoded_declaration
|
|
&& (cur_abbrev->tag != DW_TAG_variable || !has_external))
|
|
{
|
|
cur_abbrev->interesting = false;
|
|
if (cur_abbrev->tag == DW_TAG_subprogram && has_name
|
|
&& has_linkage_name)
|
|
cur_abbrev->maybe_ada_import = true;
|
|
}
|
|
else if (!tag_interesting_for_index (cur_abbrev->tag))
|
|
cur_abbrev->interesting = false;
|
|
else
|
|
cur_abbrev->interesting = true;
|
|
|
|
/* If there are no children, and the abbrev has a constant size,
|
|
then we don't care about the sibling offset, because it's
|
|
simple to just skip the entire DIE without reading a sibling
|
|
offset. */
|
|
if ((!cur_abbrev->has_children && is_csize)
|
|
/* Overflow. */
|
|
|| sibling_offset != (unsigned short) sibling_offset)
|
|
sibling_offset = -1;
|
|
cur_abbrev->size_if_constant = is_csize ? size : 0;
|
|
cur_abbrev->sibling_offset = sibling_offset;
|
|
|
|
abbrev_table->add_abbrev (cur_abbrev);
|
|
}
|
|
|
|
return abbrev_table;
|
|
}
|