From: Simon Marchi Date: Thu, 6 Mar 2025 20:30:33 +0000 (-0500) Subject: gdb/dwarf: move cooked_indexer to cooked-indexer.{h,c} X-Git-Tag: binutils-2_45~1261 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=34177f19831dc1740fcf3741f9971254b1d3eb4f;p=thirdparty%2Fbinutils-gdb.git gdb/dwarf: move cooked_indexer to cooked-indexer.{h,c} Move the cooked_indexer class declaration to a new cooked-indexer.h file, and the implementation to cooked-indexer.c. Change-Id: Ibff3b06045b2af65fa9516097acf732d7c2d9414 Approved-By: Tom Tromey --- diff --git a/gdb/Makefile.in b/gdb/Makefile.in index bf41f4142fb..b176e9cedf2 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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 \ diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c new file mode 100644 index 00000000000..40f18d1e5e5 --- /dev/null +++ b/gdb/dwarf2/cooked-indexer.c @@ -0,0 +1,693 @@ +/* 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 . */ + +#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 (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 low_pc; + std::optional 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) + { + /* 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) +{ + 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 parent, + bool fully) +{ + info_ptr = index_dies (reader, info_ptr, parent, fully); + + if (!std::holds_alternative (parent)) + return info_ptr; + const cooked_index_entry *parent_entry + = std::get (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 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)) + defer = std::get (parent); + const cooked_index_entry *parent_entry = nullptr; + if (std::holds_alternative (parent)) + parent_entry = std::get (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 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); +} diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h new file mode 100644 index 00000000000..a33f5d18c47 --- /dev/null +++ b/gdb/dwarf2/cooked-indexer.h @@ -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 . */ + +#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 + +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 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 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 */ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 4aaf5fb0bf5..202f05f7e94 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -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" @@ -93,7 +94,6 @@ #include "run-on-main-thread.h" #include "dwarf2/parent-map.h" #include "dwarf2/error.h" -#include #include "gdbsupport/unordered_set.h" #include "extract-store-integer.h" @@ -751,9 +751,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); @@ -870,31 +867,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 *); @@ -938,9 +910,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 *); @@ -1005,10 +974,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); @@ -1092,9 +1057,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, @@ -2427,15 +2389,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; @@ -3504,98 +3460,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 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 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. */ @@ -6302,7 +6166,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) @@ -9768,12 +9632,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) @@ -9944,11 +9805,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) @@ -14884,653 +14743,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 (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 low_pc; - std::optional 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) - { - /* 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; -} - -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 parent, - bool fully) -{ - info_ptr = index_dies (reader, info_ptr, parent, fully); - - if (!std::holds_alternative (parent)) - return info_ptr; - const cooked_index_entry *parent_entry - = std::get (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 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)) - defer = std::get (parent); - const cooked_index_entry *parent_entry = nullptr; - if (std::holds_alternative (parent)) - parent_entry = std::get (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 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 @@ -19959,33 +19171,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) { @@ -20505,10 +19693,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) diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index dcaea14fb49..ef83bcfaf17 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -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 */