mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-08 08:33:13 +00:00
g++ will sometimes use a typedef to give a name to an otherwise anonymous type for linkage purposes. gdb tries to handle this odd scenario, which is enforced by anon-struct.exp. It's difficult to detect this problem in the current tree, but the cooked index does not include an entry for these DIEs. This patch changes gdb to add these to the index. This is needed by subsequent changes in this series. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32519 Acked-By: Simon Marchi <simon.marchi@efficios.com>
720 lines
21 KiB
C
720 lines
21 KiB
C
/* DWARF indexer
|
|
|
|
Copyright (C) 2022-2025 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/>. */
|
|
|
|
#include "dwarf2/cooked-indexer.h"
|
|
#include "dwarf2/cooked-index-worker.h"
|
|
#include "dwarf2/error.h"
|
|
#include "dwarf2/read.h"
|
|
#include "cp-support.h"
|
|
#include "demangle.h"
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
cooked_indexer::cooked_indexer (cooked_index_worker_result *storage,
|
|
dwarf2_per_cu *per_cu, enum language language)
|
|
: m_index_storage (storage),
|
|
m_per_cu (per_cu),
|
|
m_language (language),
|
|
m_die_range_map (storage->get_parent_map ())
|
|
{
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
void
|
|
cooked_indexer::check_bounds (cutu_reader *reader)
|
|
{
|
|
dwarf2_cu *cu = reader->cu ();
|
|
|
|
if (cu->per_cu->addresses_seen)
|
|
return;
|
|
|
|
unrelocated_addr best_lowpc = {}, best_highpc = {};
|
|
/* Possibly set the default values of LOWPC and HIGHPC from
|
|
`DW_AT_ranges'. */
|
|
dwarf2_find_base_address (reader->top_level_die (), cu);
|
|
enum pc_bounds_kind cu_bounds_kind
|
|
= dwarf2_get_pc_bounds (reader->top_level_die (), &best_lowpc, &best_highpc,
|
|
cu, m_index_storage->get_addrmap (), cu->per_cu);
|
|
if (cu_bounds_kind == PC_BOUNDS_HIGH_LOW && best_lowpc < best_highpc)
|
|
{
|
|
/* Store the contiguous range if it is not empty; it can be
|
|
empty for CUs with no code. addrmap requires CORE_ADDR, so
|
|
we cast here. */
|
|
m_index_storage->get_addrmap ()->set_empty ((CORE_ADDR) best_lowpc,
|
|
(CORE_ADDR) best_highpc - 1,
|
|
cu->per_cu);
|
|
|
|
cu->per_cu->addresses_seen = true;
|
|
}
|
|
}
|
|
|
|
/* Helper function that returns true if TAG can have a linkage
|
|
name. */
|
|
|
|
static bool
|
|
tag_can_have_linkage_name (enum dwarf_tag tag)
|
|
{
|
|
switch (tag)
|
|
{
|
|
case DW_TAG_variable:
|
|
case DW_TAG_subprogram:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
cutu_reader *
|
|
cooked_indexer::ensure_cu_exists (cutu_reader *reader,
|
|
const section_and_offset §_off,
|
|
bool for_scanning)
|
|
{
|
|
/* Lookups for type unit references are always in the CU, and
|
|
cross-CU references will crash. */
|
|
if (reader->section () == sect_off.section
|
|
&& reader->cu ()->header.offset_in_unit_p (sect_off.offset))
|
|
return reader;
|
|
|
|
dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile;
|
|
dwarf2_per_cu *per_cu = dwarf2_find_containing_unit (sect_off, per_objfile);
|
|
|
|
/* When scanning, we only want to visit a given CU a single time.
|
|
Doing this check here avoids self-imports as well. */
|
|
if (for_scanning)
|
|
{
|
|
bool nope = false;
|
|
if (!per_cu->scanned.compare_exchange_strong (nope, true))
|
|
return nullptr;
|
|
}
|
|
|
|
cutu_reader *result = m_index_storage->get_reader (per_cu);
|
|
if (result == nullptr)
|
|
{
|
|
const abbrev_table_cache &abbrev_table_cache
|
|
= m_index_storage->get_abbrev_table_cache ();
|
|
auto new_reader
|
|
= std::make_unique<cutu_reader> (*per_cu, *per_objfile, nullptr,
|
|
nullptr, false, language_minimal,
|
|
&abbrev_table_cache);
|
|
|
|
if (new_reader->is_dummy ())
|
|
return nullptr;
|
|
|
|
result = m_index_storage->preserve (std::move (new_reader));
|
|
}
|
|
|
|
if (result->is_dummy ())
|
|
return nullptr;
|
|
|
|
if (for_scanning)
|
|
check_bounds (result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
const gdb_byte *
|
|
cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
|
|
cutu_reader *reader,
|
|
const gdb_byte *watermark_ptr,
|
|
const gdb_byte *info_ptr,
|
|
const abbrev_info *abbrev,
|
|
const char **name,
|
|
const char **linkage_name,
|
|
cooked_index_flag *flags,
|
|
sect_offset *sibling_offset,
|
|
const cooked_index_entry **parent_entry,
|
|
parent_map::addr_type *maybe_defer,
|
|
bool *is_enum_class,
|
|
bool for_specification)
|
|
{
|
|
bool is_declaration = false;
|
|
std::optional<section_and_offset> origin;
|
|
std::optional<unrelocated_addr> low_pc;
|
|
std::optional<unrelocated_addr> high_pc;
|
|
bool high_pc_relative = false;
|
|
|
|
for (int i = 0; i < abbrev->num_attrs; ++i)
|
|
{
|
|
attribute attr;
|
|
info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
|
|
|
|
/* Store the data if it is of an attribute we want to keep in a
|
|
partial symbol table. */
|
|
switch (attr.name)
|
|
{
|
|
case DW_AT_name:
|
|
switch (abbrev->tag)
|
|
{
|
|
case DW_TAG_compile_unit:
|
|
case DW_TAG_partial_unit:
|
|
case DW_TAG_type_unit:
|
|
/* Compilation units have a DW_AT_name that is a filename, not
|
|
a source language identifier. */
|
|
break;
|
|
|
|
default:
|
|
if (*name == nullptr)
|
|
*name = attr.as_string ();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DW_AT_linkage_name:
|
|
case DW_AT_MIPS_linkage_name:
|
|
/* Note that both forms of linkage name might appear. We
|
|
assume they will be the same, and we only store the last
|
|
one we see. */
|
|
if (*linkage_name == nullptr)
|
|
*linkage_name = attr.as_string ();
|
|
break;
|
|
|
|
/* DWARF 4 has defined a dedicated DW_AT_main_subprogram
|
|
attribute to indicate the starting function of the program... */
|
|
case DW_AT_main_subprogram:
|
|
if (attr.as_boolean ())
|
|
*flags |= IS_MAIN;
|
|
break;
|
|
|
|
/* ... however with older versions the DW_CC_program value of
|
|
the DW_AT_calling_convention attribute was used instead as
|
|
the only means available. We handle both variants then. */
|
|
case DW_AT_calling_convention:
|
|
{
|
|
std::optional<ULONGEST> value = attr.unsigned_constant ();
|
|
if (value.has_value () && *value == DW_CC_program)
|
|
*flags |= IS_MAIN;
|
|
}
|
|
break;
|
|
|
|
case DW_AT_declaration:
|
|
is_declaration = attr.as_boolean ();
|
|
break;
|
|
|
|
case DW_AT_sibling:
|
|
if (sibling_offset != nullptr)
|
|
*sibling_offset = attr.get_ref_die_offset ();
|
|
break;
|
|
|
|
case DW_AT_specification:
|
|
case DW_AT_abstract_origin:
|
|
case DW_AT_extension:
|
|
origin = { &get_section_for_ref (attr, reader->cu ()),
|
|
attr.get_ref_die_offset () };
|
|
break;
|
|
|
|
case DW_AT_external:
|
|
if (attr.as_boolean ())
|
|
*flags &= ~IS_STATIC;
|
|
break;
|
|
|
|
case DW_AT_enum_class:
|
|
if (attr.as_boolean ())
|
|
*is_enum_class = true;
|
|
break;
|
|
|
|
case DW_AT_low_pc:
|
|
low_pc = attr.as_address ();
|
|
break;
|
|
|
|
case DW_AT_high_pc:
|
|
high_pc = attr.as_address ();
|
|
if (reader->cu ()->header.version >= 4 && attr.form_is_constant ())
|
|
high_pc_relative = true;
|
|
break;
|
|
|
|
case DW_AT_location:
|
|
if (!scanning_per_cu->addresses_seen && attr.form_is_block ())
|
|
{
|
|
struct dwarf_block *locdesc = attr.as_block ();
|
|
CORE_ADDR addr;
|
|
dwarf2_cu *cu = reader->cu ();
|
|
|
|
if (decode_locdesc (locdesc, cu, &addr)
|
|
&& (addr != 0
|
|
|| cu->per_objfile->per_bfd->has_section_at_zero))
|
|
{
|
|
low_pc = (unrelocated_addr) addr;
|
|
/* For variables, we don't want to try decoding the
|
|
type just to find the size -- for gdb's purposes
|
|
we only need the address of a variable. */
|
|
high_pc = (unrelocated_addr) (addr + 1);
|
|
high_pc_relative = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DW_AT_ranges:
|
|
if (!scanning_per_cu->addresses_seen)
|
|
{
|
|
/* 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. */
|
|
ranges_offset += reader->cu ()->gnu_ranges_base;
|
|
|
|
unrelocated_addr lowpc, highpc;
|
|
dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, reader->cu (),
|
|
m_index_storage->get_addrmap (),
|
|
scanning_per_cu, abbrev->tag);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We don't want to examine declarations, but if we found a
|
|
declaration when handling DW_AT_specification or the like, then
|
|
that is ok. Similarly, we allow an external variable without a
|
|
location; those are resolved via minimal symbols. */
|
|
if (is_declaration && !for_specification
|
|
&& !(abbrev->tag == DW_TAG_variable && (*flags & IS_STATIC) == 0))
|
|
{
|
|
/* We always want to recurse into some types, but we may not
|
|
want to treat them as definitions. */
|
|
if ((abbrev->tag == DW_TAG_class_type
|
|
|| abbrev->tag == DW_TAG_structure_type
|
|
|| abbrev->tag == DW_TAG_union_type
|
|
|| abbrev->tag == DW_TAG_namespace)
|
|
&& abbrev->has_children)
|
|
*flags |= IS_TYPE_DECLARATION;
|
|
else if (!is_ada_import_or_export (reader->cu (), *name, *linkage_name))
|
|
{
|
|
*linkage_name = nullptr;
|
|
*name = nullptr;
|
|
}
|
|
}
|
|
else if ((*name == nullptr
|
|
|| (*linkage_name == nullptr
|
|
&& tag_can_have_linkage_name (abbrev->tag))
|
|
|| (*parent_entry == nullptr && m_language != language_c))
|
|
&& origin.has_value ())
|
|
{
|
|
cutu_reader *new_reader
|
|
= ensure_cu_exists (reader, *origin, false);
|
|
if (new_reader == nullptr)
|
|
error (_(DWARF_ERROR_PREFIX
|
|
"cannot follow reference to DIE at %s"
|
|
" [in module %s]"),
|
|
sect_offset_str (origin->offset),
|
|
bfd_get_filename (reader->abfd ()));
|
|
|
|
const gdb_byte *new_info_ptr
|
|
= (new_reader->buffer () + to_underlying (origin->offset));
|
|
|
|
if (*parent_entry == nullptr)
|
|
{
|
|
/* We only perform immediate lookups of parents for DIEs
|
|
from earlier in this CU. This avoids any problem
|
|
with a NULL result when when we see a reference to a
|
|
DIE in another CU that we may or may not have
|
|
imported locally. */
|
|
parent_map::addr_type addr = parent_map::form_addr (new_info_ptr);
|
|
if (new_reader->cu () != reader->cu ()
|
|
|| new_info_ptr > watermark_ptr)
|
|
*maybe_defer = addr;
|
|
else
|
|
*parent_entry = m_die_range_map->find (addr);
|
|
}
|
|
|
|
unsigned int bytes_read;
|
|
const abbrev_info *new_abbrev
|
|
= new_reader->peek_die_abbrev (new_info_ptr, &bytes_read);
|
|
|
|
if (new_abbrev == nullptr)
|
|
error (_(DWARF_ERROR_PREFIX
|
|
"Unexpected null DIE at offset %s [in module %s]"),
|
|
sect_offset_str (origin->offset),
|
|
bfd_get_filename (new_reader->abfd ()));
|
|
|
|
new_info_ptr += bytes_read;
|
|
|
|
if (new_reader->cu () == reader->cu () && new_info_ptr == watermark_ptr)
|
|
{
|
|
/* Self-reference, we're done. */
|
|
}
|
|
else
|
|
scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
|
|
new_info_ptr, new_abbrev, name, linkage_name,
|
|
flags, nullptr, parent_entry, maybe_defer,
|
|
is_enum_class, true);
|
|
}
|
|
|
|
if (!for_specification)
|
|
{
|
|
/* Older versions of GNAT emit full-qualified encoded names. In
|
|
this case, also use this name as the linkage name. */
|
|
if (m_language == language_ada
|
|
&& *linkage_name == nullptr
|
|
&& *name != nullptr
|
|
&& strstr (*name, "__") != nullptr)
|
|
*linkage_name = *name;
|
|
|
|
if (!scanning_per_cu->addresses_seen && low_pc.has_value ()
|
|
&& (reader->cu ()->per_objfile->per_bfd->has_section_at_zero
|
|
|| *low_pc != (unrelocated_addr) 0)
|
|
&& high_pc.has_value ())
|
|
{
|
|
if (high_pc_relative)
|
|
high_pc = (unrelocated_addr) ((ULONGEST) *high_pc
|
|
+ (ULONGEST) *low_pc);
|
|
|
|
if (*high_pc > *low_pc)
|
|
{
|
|
/* Need CORE_ADDR casts for addrmap. */
|
|
m_index_storage->get_addrmap ()->set_empty
|
|
((CORE_ADDR) *low_pc, (CORE_ADDR) *high_pc - 1,
|
|
scanning_per_cu);
|
|
}
|
|
}
|
|
|
|
if (abbrev->tag == DW_TAG_namespace && *name == nullptr)
|
|
*name = "(anonymous namespace)";
|
|
|
|
/* Keep in sync with new_symbol. */
|
|
if (abbrev->tag == DW_TAG_subprogram
|
|
&& (m_language == language_ada
|
|
|| m_language == language_fortran))
|
|
*flags &= ~IS_STATIC;
|
|
}
|
|
|
|
return info_ptr;
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
const gdb_byte *
|
|
cooked_indexer::index_imported_unit (cutu_reader *reader,
|
|
const gdb_byte *info_ptr,
|
|
const abbrev_info *abbrev)
|
|
{
|
|
std::optional<section_and_offset> target;
|
|
|
|
for (int i = 0; i < abbrev->num_attrs; ++i)
|
|
{
|
|
/* Note that we never need to reprocess attributes here. */
|
|
attribute attr;
|
|
info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
|
|
|
|
if (attr.name == DW_AT_import)
|
|
target = { &get_section_for_ref (attr, reader->cu ()),
|
|
attr.get_ref_die_offset () };
|
|
}
|
|
|
|
/* Did not find DW_AT_import. */
|
|
if (!target.has_value ())
|
|
return info_ptr;
|
|
|
|
cutu_reader *new_reader
|
|
= ensure_cu_exists (reader, *target, true);
|
|
if (new_reader != nullptr)
|
|
{
|
|
index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
|
|
|
|
reader->cu ()->add_dependence (new_reader->cu ()->per_cu);
|
|
}
|
|
|
|
return info_ptr;
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
const gdb_byte *
|
|
cooked_indexer::recurse (cutu_reader *reader,
|
|
const gdb_byte *info_ptr,
|
|
std::variant<const cooked_index_entry *,
|
|
parent_map::addr_type> parent,
|
|
bool fully)
|
|
{
|
|
info_ptr = index_dies (reader, info_ptr, parent, fully);
|
|
|
|
if (!std::holds_alternative<const cooked_index_entry *> (parent))
|
|
return info_ptr;
|
|
const cooked_index_entry *parent_entry
|
|
= std::get<const cooked_index_entry *> (parent);
|
|
|
|
if (parent_entry != nullptr)
|
|
{
|
|
/* Both start and end are inclusive, so use both "+ 1" and "- 1" to
|
|
limit the range to the children of parent_entry. */
|
|
parent_map::addr_type start
|
|
= parent_map::form_addr (reader->buffer ()
|
|
+ to_underlying (parent_entry->die_offset)
|
|
+ 1);
|
|
parent_map::addr_type end = parent_map::form_addr (info_ptr - 1);
|
|
m_die_range_map->add_entry (start, end, parent_entry);
|
|
}
|
|
|
|
return info_ptr;
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
const gdb_byte *
|
|
cooked_indexer::index_dies (cutu_reader *reader,
|
|
const gdb_byte *info_ptr,
|
|
std::variant<const cooked_index_entry *,
|
|
parent_map::addr_type> parent,
|
|
bool fully)
|
|
{
|
|
const gdb_byte *end_ptr
|
|
= (reader->buffer () + to_underlying (reader->cu ()->header.sect_off)
|
|
+ reader->cu ()->header.get_length_with_initial ());
|
|
|
|
while (info_ptr < end_ptr)
|
|
{
|
|
sect_offset this_die = (sect_offset) (info_ptr - reader->buffer ());
|
|
unsigned int bytes_read;
|
|
const abbrev_info *abbrev
|
|
= reader->peek_die_abbrev (info_ptr, &bytes_read);
|
|
info_ptr += bytes_read;
|
|
if (abbrev == nullptr)
|
|
break;
|
|
|
|
if (abbrev->tag == DW_TAG_imported_unit)
|
|
{
|
|
info_ptr = index_imported_unit (reader, info_ptr, abbrev);
|
|
continue;
|
|
}
|
|
|
|
parent_map::addr_type defer {};
|
|
if (std::holds_alternative<parent_map::addr_type> (parent))
|
|
defer = std::get<parent_map::addr_type> (parent);
|
|
const cooked_index_entry *parent_entry = nullptr;
|
|
if (std::holds_alternative<const cooked_index_entry *> (parent))
|
|
parent_entry = std::get<const cooked_index_entry *> (parent);
|
|
|
|
/* If a DIE parent is a DW_TAG_subprogram, then the DIE is only
|
|
interesting if it's a DW_TAG_subprogram or a DW_TAG_entry_point. */
|
|
bool die_interesting
|
|
= ((abbrev->interesting
|
|
|| (m_language == language_ada && abbrev->maybe_ada_import))
|
|
&& (parent_entry == nullptr
|
|
|| parent_entry->tag != DW_TAG_subprogram
|
|
|| abbrev->tag == DW_TAG_subprogram
|
|
|| abbrev->tag == DW_TAG_entry_point));
|
|
|
|
if (!die_interesting)
|
|
{
|
|
info_ptr = reader->skip_one_die (info_ptr, abbrev, !fully);
|
|
if (fully && abbrev->has_children)
|
|
info_ptr = index_dies (reader, info_ptr, parent, fully);
|
|
continue;
|
|
}
|
|
|
|
const char *name = nullptr;
|
|
const char *linkage_name = nullptr;
|
|
cooked_index_flag flags = IS_STATIC;
|
|
sect_offset sibling {};
|
|
const cooked_index_entry *this_parent_entry = parent_entry;
|
|
bool is_enum_class = false;
|
|
|
|
/* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
|
|
its surrounding subroutine. */
|
|
if (abbrev->tag == DW_TAG_entry_point)
|
|
this_parent_entry = parent_entry->get_parent ();
|
|
info_ptr
|
|
= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
|
|
abbrev, &name, &linkage_name, &flags, &sibling,
|
|
&this_parent_entry, &defer, &is_enum_class, false);
|
|
/* A DW_TAG_entry_point inherits its static/extern property from
|
|
the enclosing subroutine. */
|
|
if (abbrev->tag == DW_TAG_entry_point)
|
|
{
|
|
flags &= ~IS_STATIC;
|
|
flags |= parent_entry->flags & IS_STATIC;
|
|
}
|
|
else if (abbrev->tag == DW_TAG_imported_declaration)
|
|
{
|
|
/* Match the full reader. */
|
|
flags &= ~IS_STATIC;
|
|
}
|
|
|
|
if (abbrev->tag == DW_TAG_namespace
|
|
&& m_language == language_cplus
|
|
&& strcmp (name, "::") == 0)
|
|
{
|
|
/* GCC 4.0 and 4.1 had a bug (PR c++/28460) where they
|
|
generated bogus DW_TAG_namespace DIEs with a name of "::"
|
|
for the global namespace. Work around this problem
|
|
here. */
|
|
name = nullptr;
|
|
}
|
|
|
|
/* An otherwise anonymous type might be given a name (via
|
|
typedef) for linkage purposes, and gdb tries to handle this
|
|
case. See anon-struct.exp. In this case, GCC will emit a
|
|
funny thing: a linkage name for the type, but in "type" form.
|
|
That is, it will not start with _Z. */
|
|
if ((abbrev->tag == DW_TAG_class_type
|
|
|| abbrev->tag == DW_TAG_structure_type
|
|
|| abbrev->tag == DW_TAG_union_type)
|
|
&& m_language == language_cplus
|
|
&& name == nullptr
|
|
&& linkage_name != nullptr)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> dem
|
|
= gdb_demangle (linkage_name, DMGL_GNU_V3 | DMGL_TYPES);
|
|
if (dem != nullptr)
|
|
{
|
|
/* We're only interested in the last component. */
|
|
std::vector<std::string_view> split
|
|
= split_name (dem.get (), split_style::CXX);
|
|
name = m_index_storage->add (split.back ());
|
|
linkage_name = nullptr;
|
|
}
|
|
}
|
|
|
|
cooked_index_entry *this_entry = nullptr;
|
|
if (name != nullptr)
|
|
{
|
|
if (defer != 0)
|
|
this_entry
|
|
= m_index_storage->add (this_die, abbrev->tag,
|
|
flags | IS_PARENT_DEFERRED, name,
|
|
defer, m_per_cu);
|
|
else
|
|
this_entry
|
|
= m_index_storage->add (this_die, abbrev->tag, flags, name,
|
|
this_parent_entry, m_per_cu);
|
|
}
|
|
|
|
if (linkage_name != nullptr)
|
|
{
|
|
/* We only want this to be "main" if it has a linkage name
|
|
but not an ordinary name. */
|
|
if (name != nullptr)
|
|
flags = flags & ~IS_MAIN;
|
|
/* Set the IS_LINKAGE on for everything except when functions
|
|
have linkage name present but name is absent. */
|
|
if (name != nullptr
|
|
|| (abbrev->tag != DW_TAG_subprogram
|
|
&& abbrev->tag != DW_TAG_inlined_subroutine
|
|
&& abbrev->tag != DW_TAG_entry_point))
|
|
flags = flags | IS_LINKAGE;
|
|
m_index_storage->add (this_die, abbrev->tag, flags,
|
|
linkage_name, nullptr, m_per_cu);
|
|
}
|
|
|
|
if (abbrev->has_children)
|
|
{
|
|
switch (abbrev->tag)
|
|
{
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_interface_type:
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type:
|
|
if (m_language != language_c && this_entry != nullptr)
|
|
{
|
|
info_ptr = recurse (reader, info_ptr, this_entry, fully);
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_enumeration_type:
|
|
/* Some versions of gdc could emit an "enum class"
|
|
without a name, which is nonsensical. These are
|
|
skipped. */
|
|
if (is_enum_class && this_entry == nullptr)
|
|
continue;
|
|
|
|
/* We need to recurse even for an anonymous enumeration.
|
|
Which scope we record as the parent scope depends on
|
|
whether we're reading an "enum class". If so, we use
|
|
the enum itself as the parent, yielding names like
|
|
"enum_class::enumerator"; otherwise we inject the
|
|
names into our own parent scope. */
|
|
{
|
|
std::variant<const cooked_index_entry *,
|
|
parent_map::addr_type> recurse_parent;
|
|
if (is_enum_class)
|
|
{
|
|
gdb_assert (this_entry != nullptr);
|
|
recurse_parent = this_entry;
|
|
}
|
|
else if (defer != 0)
|
|
recurse_parent = defer;
|
|
else
|
|
recurse_parent = this_parent_entry;
|
|
|
|
info_ptr = recurse (reader, info_ptr, recurse_parent, fully);
|
|
}
|
|
continue;
|
|
|
|
case DW_TAG_module:
|
|
if (this_entry == nullptr)
|
|
break;
|
|
[[fallthrough]];
|
|
case DW_TAG_namespace:
|
|
/* We don't check THIS_ENTRY for a namespace, to handle
|
|
the ancient G++ workaround pointed out above. */
|
|
info_ptr = recurse (reader, info_ptr, this_entry, fully);
|
|
continue;
|
|
|
|
case DW_TAG_subprogram:
|
|
if ((m_language == language_fortran
|
|
|| m_language == language_ada)
|
|
&& this_entry != nullptr)
|
|
{
|
|
info_ptr = recurse (reader, info_ptr, this_entry, true);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (sibling != sect_offset (0))
|
|
{
|
|
const gdb_byte *sibling_ptr
|
|
= reader->buffer () + to_underlying (sibling);
|
|
|
|
if (sibling_ptr < info_ptr)
|
|
complaint (_("DW_AT_sibling points backwards"));
|
|
else if (sibling_ptr > reader->buffer_end ())
|
|
reader->section ()->overflow_complaint ();
|
|
else
|
|
info_ptr = sibling_ptr;
|
|
}
|
|
else
|
|
info_ptr = reader->skip_children (info_ptr);
|
|
}
|
|
}
|
|
|
|
return info_ptr;
|
|
}
|
|
|
|
/* See cooked-indexer.h. */
|
|
|
|
void
|
|
cooked_indexer::make_index (cutu_reader *reader)
|
|
{
|
|
check_bounds (reader);
|
|
find_file_and_directory (reader->top_level_die (), reader->cu ());
|
|
|
|
if (!reader->top_level_die ()->has_children)
|
|
return;
|
|
|
|
index_dies (reader, reader->info_ptr (), nullptr, false);
|
|
}
|