@@ -1098,6 +1098,7 @@ COMMON_SFILES = \
dwarf2/comp-unit-head.c \
dwarf2/cooked-index.c \
dwarf2/cooked-index-storage.c \
+ dwarf2/cooked-indexer.c \
dwarf2/cu.c \
dwarf2/die.c \
dwarf2/dwz.c \
@@ -1356,6 +1357,7 @@ HFILES_NO_SRCDIR = \
dwarf2/aranges.h \
dwarf2/cooked-index.h \
dwarf2/cooked-index-storage.h \
+ dwarf2/cooked-indexer.h \
dwarf2/cu.h \
dwarf2/frame-tailcall.h \
dwarf2/frame.h \
new file mode 100644
@@ -0,0 +1,689 @@
+/* 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-storage.h"
+#include "dwarf2/error.h"
+
+/* See cooked-indexer.h. */
+
+cooked_indexer::cooked_indexer (cooked_index_storage *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->comp_unit_die (), cu);
+ enum pc_bounds_kind cu_bounds_kind
+ = dwarf2_get_pc_bounds (reader->comp_unit_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,
+ dwarf2_per_objfile *per_objfile,
+ sect_offset sect_off, bool is_dwz,
+ bool for_scanning)
+{
+ /* Lookups for type unit references are always in the CU, and
+ cross-CU references will crash. */
+ if (reader->cu ()->per_cu->is_dwz == is_dwz
+ && reader->cu ()->header.offset_in_cu_p (sect_off))
+ return reader;
+
+ dwarf2_per_cu *per_cu
+ = dwarf2_find_containing_comp_unit (sect_off, is_dwz,
+ per_objfile->per_bfd);
+
+ /* 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)
+ {
+ cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false,
+ language_minimal,
+ &m_index_storage->get_abbrev_table_cache ());
+
+ if (new_reader.is_dummy () || new_reader.comp_unit_die () == nullptr
+ || !new_reader.comp_unit_die ()->has_children)
+ return nullptr;
+
+ auto copy = std::make_unique<cutu_reader> (std::move (new_reader));
+ result = m_index_storage->preserve (std::move (copy));
+ }
+
+ if (result->is_dummy () || result->comp_unit_die () == nullptr
+ || !result->comp_unit_die ()->has_children)
+ 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 origin_is_dwz = false;
+ bool is_declaration = false;
+ sect_offset origin_offset {};
+
+ 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:
+ if (attr.constant_value (DW_CC_normal) == 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_offset = attr.get_ref_die_offset ();
+ origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
+ 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
+ {
+ *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_offset != sect_offset (0))
+ {
+ cutu_reader *new_reader
+ = ensure_cu_exists (reader, reader->cu ()->per_objfile, origin_offset,
+ origin_is_dwz, 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)
+ {
+ if (m_language == language_ada
+ && *linkage_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)
+{
+ sect_offset sect_off {};
+ bool is_dwz = false;
+
+ 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)
+ {
+ sect_off = attr.get_ref_die_offset ();
+ is_dwz = (attr.form == DW_FORM_GNU_ref_alt
+ || reader->cu ()->per_cu->is_dwz);
+ }
+ }
+
+ /* Did not find DW_AT_import. */
+ if (sect_off == sect_offset (0))
+ return info_ptr;
+
+ dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile;
+ cutu_reader *new_reader
+ = ensure_cu_exists (reader, per_objfile, sect_off, is_dwz, 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
+ && (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;
+ }
+
+ 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;
+ }
+
+ 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->comp_unit_die (), reader->cu ());
+
+ if (!reader->comp_unit_die ()->has_children)
+ return;
+
+ index_dies (reader, reader->info_ptr (), nullptr, false);
+}
new file mode 100644
@@ -0,0 +1,120 @@
+/* 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/>. */
+
+#ifndef GDB_DWARF2_COOKED_INDEXER_H
+#define GDB_DWARF2_COOKED_INDEXER_H
+
+#include "dwarf2/cooked-index.h"
+#include "dwarf2/parent-map.h"
+#include "dwarf2/types.h"
+#include <variant>
+
+struct abbrev_info;
+struct cooked_index_storage;
+struct cutu_reader;
+struct dwarf2_per_cu;
+struct dwarf2_per_objfile;
+
+/* An instance of this is created to index a CU. */
+
+class cooked_indexer
+{
+public:
+ cooked_indexer (cooked_index_storage *storage, dwarf2_per_cu *per_cu,
+ enum language language);
+
+ DISABLE_COPY_AND_ASSIGN (cooked_indexer);
+
+ /* Index the given CU. */
+ void make_index (cutu_reader *reader);
+
+private:
+
+ /* A helper function to scan the PC bounds of READER and record them
+ in the storage's addrmap. */
+ void check_bounds (cutu_reader *reader);
+
+ /* Ensure that the indicated CU exists. The cutu_reader for it is
+ returned. FOR_SCANNING is true if the caller intends to scan all
+ the DIEs in the CU; when false, this use is assumed to be to look
+ up just a single DIE. */
+ cutu_reader *ensure_cu_exists (cutu_reader *reader,
+ dwarf2_per_objfile *per_objfile,
+ sect_offset sect_off,
+ bool is_dwz,
+ bool for_scanning);
+
+ /* Index DIEs in the READER starting at INFO_PTR. PARENT is
+ the entry for the enclosing scope (nullptr at top level). FULLY
+ is true when a full scan must be done -- in some languages,
+ function scopes must be fully explored in order to find nested
+ functions. This returns a pointer to just after the spot where
+ reading stopped. */
+ const gdb_byte *index_dies (cutu_reader *reader,
+ const gdb_byte *info_ptr,
+ std::variant<const cooked_index_entry *,
+ parent_map::addr_type> parent,
+ bool fully);
+
+ /* Scan the attributes for a given DIE and update the out
+ parameters. Returns a pointer to the byte after the DIE. */
+ const gdb_byte *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);
+
+ /* Handle DW_TAG_imported_unit, by scanning the DIE to find
+ DW_AT_import, and then scanning the referenced CU. Returns a
+ pointer to the byte after the DIE. */
+ const gdb_byte *index_imported_unit (cutu_reader *reader,
+ const gdb_byte *info_ptr,
+ const abbrev_info *abbrev);
+
+ /* Recursively read DIEs, recording the section offsets in
+ m_die_range_map and then calling index_dies. */
+ const gdb_byte *recurse (cutu_reader *reader,
+ const gdb_byte *info_ptr,
+ std::variant<const cooked_index_entry *,
+ parent_map::addr_type> parent_entry,
+ bool fully);
+
+ /* The storage object, where the results are kept. */
+ cooked_index_storage *m_index_storage;
+ /* The CU that we are reading on behalf of. This object might be
+ asked to index one CU but to treat the results as if they come
+ from some including CU; in this case the including CU would be
+ recorded here. */
+ dwarf2_per_cu *m_per_cu;
+ /* The language that we're assuming when reading. */
+ enum language m_language;
+
+ /* Map from DIE ranges to newly-created entries. */
+ parent_map *m_die_range_map;
+};
+
+#endif /* GDB_DWARF2_COOKED_INDEXER_H */
@@ -33,6 +33,7 @@
#include "dwarf2/attribute.h"
#include "dwarf2/comp-unit-head.h"
#include "dwarf2/cooked-index-storage.h"
+#include "dwarf2/cooked-indexer.h"
#include "dwarf2/cu.h"
#include "dwarf2/index-cache.h"
#include "dwarf2/leb.h"
@@ -92,7 +93,6 @@
#include "run-on-main-thread.h"
#include "dwarf2/parent-map.h"
#include "dwarf2/error.h"
-#include <variant>
#include "gdbsupport/unordered_set.h"
#include "extract-store-integer.h"
@@ -750,9 +750,6 @@ show_dwarf_synchronous (struct ui_file *file, int from_tty,
/* local function prototypes */
-static void dwarf2_find_base_address (struct die_info *die,
- struct dwarf2_cu *cu);
-
static void build_type_psymtabs_reader (cutu_reader *reader,
cooked_index_storage *storage);
@@ -869,31 +866,6 @@ static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
static struct dwarf2_section_info *cu_debug_rnglists_section
(struct dwarf2_cu *cu, dwarf_tag tag);
-/* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
- values. Keep the items ordered with increasing constraints compliance. */
-enum pc_bounds_kind
-{
- /* No attribute DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges was found. */
- PC_BOUNDS_NOT_PRESENT,
-
- /* Some of the attributes DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges
- were present but they do not form a valid range of PC addresses. */
- PC_BOUNDS_INVALID,
-
- /* Discontiguous range was found - that is DW_AT_ranges was found. */
- PC_BOUNDS_RANGES,
-
- /* Contiguous range was found - DW_AT_low_pc and DW_AT_high_pc were found. */
- PC_BOUNDS_HIGH_LOW,
-};
-
-static enum pc_bounds_kind dwarf2_get_pc_bounds (struct die_info *,
- unrelocated_addr *,
- unrelocated_addr *,
- struct dwarf2_cu *,
- addrmap_mutable *,
- void *);
-
static void get_scope_pc_bounds (struct die_info *,
unrelocated_addr *, unrelocated_addr *,
struct dwarf2_cu *);
@@ -937,9 +909,6 @@ static const char *namespace_name (struct die_info *die,
static void process_enumeration_scope (struct die_info *, struct dwarf2_cu *);
-static bool decode_locdesc (struct dwarf_block *, struct dwarf2_cu *,
- CORE_ADDR *addr);
-
static enum dwarf_array_dim_ordering read_array_order (struct die_info *,
struct dwarf2_cu *);
@@ -1004,10 +973,6 @@ static void dwarf2_symbol_mark_computed (const struct attribute *attr,
struct dwarf2_cu *cu,
int is_block);
-static dwarf2_per_cu *dwarf2_find_containing_comp_unit
- (sect_offset sect_off, unsigned int offset_in_dwz,
- dwarf2_per_bfd *per_bfd);
-
static struct type *set_die_type (struct die_info *, struct type *,
struct dwarf2_cu *, bool = false);
@@ -1091,9 +1056,6 @@ dwarf2_per_cu_deleter::operator() (dwarf2_per_cu *data)
delete data;
}
-static file_and_directory &find_file_and_directory
- (struct die_info *die, struct dwarf2_cu *cu);
-
static const char *compute_include_file_name
(const struct line_header *lh,
const file_entry &fe,
@@ -2426,15 +2388,9 @@ dwarf2_initialize_objfile (struct objfile *objfile,
return true;
}
-
-
-/* Find the base address of the compilation unit for range lists and
- location lists. It will normally be specified by DW_AT_low_pc.
- In DWARF-3 draft 4, the base address could be overridden by
- DW_AT_entry_pc. It's been removed, but GCC still uses this for
- compilation units with discontinuous ranges. */
+/* See read.h. */
-static void
+void
dwarf2_find_base_address (struct die_info *die, struct dwarf2_cu *cu)
{
struct attribute *attr;
@@ -3503,98 +3459,6 @@ get_type_unit_group (struct dwarf2_cu *cu, const struct attribute *stmt_list)
return tu_group;
}
-/* An instance of this is created to index a CU. */
-
-class cooked_indexer
-{
-public:
- cooked_indexer (cooked_index_storage *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 ())
- {
- }
-
- DISABLE_COPY_AND_ASSIGN (cooked_indexer);
-
- /* Index the given CU. */
- void make_index (cutu_reader *reader);
-
-private:
-
- /* A helper function to scan the PC bounds of READER and record them
- in the storage's addrmap. */
- void check_bounds (cutu_reader *reader);
-
- /* Ensure that the indicated CU exists. The cutu_reader for it is
- returned. FOR_SCANNING is true if the caller intends to scan all
- the DIEs in the CU; when false, this use is assumed to be to look
- up just a single DIE. */
- cutu_reader *ensure_cu_exists (cutu_reader *reader,
- dwarf2_per_objfile *per_objfile,
- sect_offset sect_off,
- bool is_dwz,
- bool for_scanning);
-
- /* Index DIEs in the READER starting at INFO_PTR. PARENT is
- the entry for the enclosing scope (nullptr at top level). FULLY
- is true when a full scan must be done -- in some languages,
- function scopes must be fully explored in order to find nested
- functions. This returns a pointer to just after the spot where
- reading stopped. */
- const gdb_byte *index_dies (cutu_reader *reader,
- const gdb_byte *info_ptr,
- std::variant<const cooked_index_entry *,
- parent_map::addr_type> parent,
- bool fully);
-
- /* Scan the attributes for a given DIE and update the out
- parameters. Returns a pointer to the byte after the DIE. */
- const gdb_byte *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);
-
- /* Handle DW_TAG_imported_unit, by scanning the DIE to find
- DW_AT_import, and then scanning the referenced CU. Returns a
- pointer to the byte after the DIE. */
- const gdb_byte *index_imported_unit (cutu_reader *reader,
- const gdb_byte *info_ptr,
- const abbrev_info *abbrev);
-
- /* Recursively read DIEs, recording the section offsets in
- m_die_range_map and then calling index_dies. */
- const gdb_byte *recurse (cutu_reader *reader,
- const gdb_byte *info_ptr,
- std::variant<const cooked_index_entry *,
- parent_map::addr_type> parent_entry,
- bool fully);
-
- /* The storage object, where the results are kept. */
- cooked_index_storage *m_index_storage;
- /* The CU that we are reading on behalf of. This object might be
- asked to index one CU but to treat the results as if they come
- from some including CU; in this case the including CU would be
- recorded here. */
- dwarf2_per_cu *m_per_cu;
- /* The language that we're assuming when reading. */
- enum language m_language;
-
- /* Map from DIE ranges to newly-created entries. */
- parent_map *m_die_range_map;
-};
-
/* Subroutine of dwarf2_build_psymtabs_hard to simplify it.
Process compilation unit THIS_CU for a psymtab. */
@@ -6264,7 +6128,7 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu)
&objfile->objfile_obstack);
}
-static file_and_directory &
+file_and_directory &
find_file_and_directory (struct die_info *die, struct dwarf2_cu *cu)
{
if (cu->per_cu->fnd != nullptr)
@@ -9724,12 +9588,9 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
return 1;
}
-/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
- Return 1 if the attributes are present and valid, otherwise, return 0.
- TAG is passed to dwarf2_ranges_process. If MAP is not NULL, then
- ranges in MAP are set, using DATUM as the value. */
+/* See read.h. */
-static int
+int
dwarf2_ranges_read (unsigned offset, unrelocated_addr *low_return,
unrelocated_addr *high_return, struct dwarf2_cu *cu,
addrmap_mutable *map, void *datum, dwarf_tag tag)
@@ -9900,11 +9761,9 @@ dwarf_get_pc_bounds_ranges_or_highlow_pc (die_info *die, unrelocated_addr *low,
return ret;
}
-/* Get low and high pc attributes from a die. See enum pc_bounds_kind
- definition for the return value. *LOWPC and *HIGHPC are set iff
- neither PC_BOUNDS_NOT_PRESENT nor PC_BOUNDS_INVALID are returned. */
+/* See read.h. */
-static enum pc_bounds_kind
+pc_bounds_kind
dwarf2_get_pc_bounds (struct die_info *die, unrelocated_addr *lowpc,
unrelocated_addr *highpc, struct dwarf2_cu *cu,
addrmap_mutable *map, void *datum)
@@ -14828,649 +14687,6 @@ cutu_reader::read_toplevel_die (die_info **diep, const gdb_byte *info_ptr,
return result;
}
-
-
-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->comp_unit_die (), cu);
- enum pc_bounds_kind cu_bounds_kind
- = dwarf2_get_pc_bounds (reader->comp_unit_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;
- }
-}
-
-cutu_reader *
-cooked_indexer::ensure_cu_exists (cutu_reader *reader,
- dwarf2_per_objfile *per_objfile,
- sect_offset sect_off, bool is_dwz,
- bool for_scanning)
-{
- /* Lookups for type unit references are always in the CU, and
- cross-CU references will crash. */
- if (reader->cu ()->per_cu->is_dwz == is_dwz
- && reader->cu ()->header.offset_in_cu_p (sect_off))
- return reader;
-
- dwarf2_per_cu *per_cu
- = dwarf2_find_containing_comp_unit (sect_off, is_dwz,
- per_objfile->per_bfd);
-
- /* 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)
- {
- cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false,
- language_minimal,
- &m_index_storage->get_abbrev_table_cache ());
-
- if (new_reader.is_dummy () || new_reader.comp_unit_die () == nullptr
- || !new_reader.comp_unit_die ()->has_children)
- return nullptr;
-
- auto copy = std::make_unique<cutu_reader> (std::move (new_reader));
- result = m_index_storage->preserve (std::move (copy));
- }
-
- if (result->is_dummy () || result->comp_unit_die () == nullptr
- || !result->comp_unit_die ()->has_children)
- return nullptr;
-
- if (for_scanning)
- check_bounds (result);
-
- return result;
-}
-
-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 origin_is_dwz = false;
- bool is_declaration = false;
- sect_offset origin_offset {};
-
- 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:
- if (attr.constant_value (DW_CC_normal) == 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_offset = attr.get_ref_die_offset ();
- origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
- 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
- {
- *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_offset != sect_offset (0))
- {
- cutu_reader *new_reader
- = ensure_cu_exists (reader, reader->cu ()->per_objfile, origin_offset,
- origin_is_dwz, 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)
- {
- if (m_language == language_ada
- && *linkage_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;
-}
-
-const gdb_byte *
-cooked_indexer::index_imported_unit (cutu_reader *reader,
- const gdb_byte *info_ptr,
- const abbrev_info *abbrev)
-{
- sect_offset sect_off {};
- bool is_dwz = false;
-
- 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)
- {
- sect_off = attr.get_ref_die_offset ();
- is_dwz = (attr.form == DW_FORM_GNU_ref_alt
- || reader->cu ()->per_cu->is_dwz);
- }
- }
-
- /* Did not find DW_AT_import. */
- if (sect_off == sect_offset (0))
- return info_ptr;
-
- dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile;
- cutu_reader *new_reader
- = ensure_cu_exists (reader, per_objfile, sect_off, is_dwz, 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;
-}
-
-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;
-}
-
-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
- && (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;
- }
-
- 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;
- }
-
- 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;
-}
-
-void
-cooked_indexer::make_index (cutu_reader *reader)
-{
- check_bounds (reader);
- find_file_and_directory (reader->comp_unit_die (), reader->cu ());
-
- if (!reader->comp_unit_die ()->has_children)
- return;
-
- index_dies (reader, reader->info_ptr (), nullptr, false);
-}
struct compunit_symtab *
cooked_index_functions::find_compunit_symtab_by_address
@@ -19888,33 +19104,9 @@ read_signatured_type (signatured_type *sig_type,
sig_type->tu_read = 1;
}
-/* Decode simple location descriptions.
-
- Given a pointer to a DWARF block that defines a location, compute
- the location. Returns true if the expression was computable by
- this function, false otherwise. On a true return, *RESULT is set.
-
- Note that this function does not implement a full DWARF expression
- evaluator. Instead, it is used for a few limited purposes:
-
- - Getting the address of a symbol that has a constant address. For
- example, if a symbol has a location like "DW_OP_addr", the address
- can be extracted.
-
- - Getting the offset of a virtual function in its vtable. There
- are two forms of this, one of which involves DW_OP_deref -- so this
- function handles derefs in a peculiar way to make this 'work'.
- (Probably this area should be rewritten.)
-
- - Getting the offset of a field, when it is constant.
-
- Opcodes that cannot be part of a constant expression, for example
- those involving registers, simply result in a return of
- 'false'.
-
- This function may emit a complaint. */
+/* See read.h. */
-static bool
+bool
decode_locdesc (struct dwarf_block *blk, struct dwarf2_cu *cu,
CORE_ADDR *result)
{
@@ -20434,10 +19626,9 @@ dwarf2_find_containing_comp_unit
return low;
}
-/* Locate the .debug_info compilation unit from CU's objfile which contains
- the DIE at OFFSET. Raises an error on failure. */
+/* See read.h. */
-static dwarf2_per_cu *
+dwarf2_per_cu *
dwarf2_find_containing_comp_unit (sect_offset sect_off,
unsigned int offset_in_dwz,
dwarf2_per_bfd *per_bfd)
@@ -1222,4 +1222,91 @@ extern void create_all_units (dwarf2_per_objfile *per_objfile);
extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
+/* Find the base address of the compilation unit for range lists and
+ location lists. It will normally be specified by DW_AT_low_pc.
+ In DWARF-3 draft 4, the base address could be overridden by
+ DW_AT_entry_pc. It's been removed, but GCC still uses this for
+ compilation units with discontinuous ranges. */
+
+extern void dwarf2_find_base_address (die_info *die, dwarf2_cu *cu);
+
+/* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
+ values. Keep the items ordered with increasing constraints compliance. */
+
+enum pc_bounds_kind
+{
+ /* No attribute DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges was found. */
+ PC_BOUNDS_NOT_PRESENT,
+
+ /* Some of the attributes DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges
+ were present but they do not form a valid range of PC addresses. */
+ PC_BOUNDS_INVALID,
+
+ /* Discontiguous range was found - that is DW_AT_ranges was found. */
+ PC_BOUNDS_RANGES,
+
+ /* Contiguous range was found - DW_AT_low_pc and DW_AT_high_pc were found. */
+ PC_BOUNDS_HIGH_LOW,
+};
+
+/* Get low and high pc attributes from a die. See enum pc_bounds_kind
+ definition for the return value. *LOWPC and *HIGHPC are set iff
+ neither PC_BOUNDS_NOT_PRESENT nor PC_BOUNDS_INVALID are returned. */
+
+extern pc_bounds_kind dwarf2_get_pc_bounds (die_info *die,
+ unrelocated_addr *lowpc,
+ unrelocated_addr *highpc,
+ dwarf2_cu *cu, addrmap_mutable *map,
+ void *datum);
+
+/* Locate the .debug_info compilation unit from CU's objfile which contains
+ the DIE at OFFSET. Raises an error on failure. */
+
+extern dwarf2_per_cu *dwarf2_find_containing_comp_unit (sect_offset sect_off,
+ unsigned int
+ offset_in_dwz,
+ dwarf2_per_bfd
+ *per_bfd);
+
+/* Decode simple location descriptions.
+
+ Given a pointer to a DWARF block that defines a location, compute
+ the location. Returns true if the expression was computable by
+ this function, false otherwise. On a true return, *RESULT is set.
+
+ Note that this function does not implement a full DWARF expression
+ evaluator. Instead, it is used for a few limited purposes:
+
+ - Getting the address of a symbol that has a constant address. For
+ example, if a symbol has a location like "DW_OP_addr", the address
+ can be extracted.
+
+ - Getting the offset of a virtual function in its vtable. There
+ are two forms of this, one of which involves DW_OP_deref -- so this
+ function handles derefs in a peculiar way to make this 'work'.
+ (Probably this area should be rewritten.)
+
+ - Getting the offset of a field, when it is constant.
+
+ Opcodes that cannot be part of a constant expression, for example
+ those involving registers, simply result in a return of
+ 'false'.
+
+ This function may emit a complaint. */
+
+extern bool decode_locdesc (dwarf_block *blk, dwarf2_cu *cu, CORE_ADDR *result);
+
+/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
+ Return 1 if the attributes are present and valid, otherwise, return 0.
+ TAG is passed to dwarf2_ranges_process. If MAP is not NULL, then
+ ranges in MAP are set, using DATUM as the value. */
+
+extern int dwarf2_ranges_read (unsigned offset, unrelocated_addr *low_return,
+ unrelocated_addr *high_return, dwarf2_cu *cu,
+ addrmap_mutable *map, void *datum,
+ dwarf_tag tag);
+
+extern file_and_directory &find_file_and_directory (die_info *die,
+ dwarf2_cu *cu);
+
#endif /* GDB_DWARF2_READ_H */