|  | /* Reading code for .debug_names | 
|  |  | 
|  | Copyright (C) 2023 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 "defs.h" | 
|  | #include "read-debug-names.h" | 
|  |  | 
|  | #include "complaints.h" | 
|  | #include "cp-support.h" | 
|  | #include "dwz.h" | 
|  | #include "mapped-index.h" | 
|  | #include "read.h" | 
|  | #include "stringify.h" | 
|  |  | 
|  | /* A description of the mapped .debug_names. | 
|  | Uninitialized map has CU_COUNT 0.  */ | 
|  |  | 
|  | struct mapped_debug_names final : public mapped_index_base | 
|  | { | 
|  | bfd_endian dwarf5_byte_order; | 
|  | bool dwarf5_is_dwarf64; | 
|  | bool augmentation_is_gdb; | 
|  | uint8_t offset_size; | 
|  | uint32_t cu_count = 0; | 
|  | uint32_t tu_count, bucket_count, name_count; | 
|  | const gdb_byte *cu_table_reordered, *tu_table_reordered; | 
|  | const uint32_t *bucket_table_reordered, *hash_table_reordered; | 
|  | const gdb_byte *name_table_string_offs_reordered; | 
|  | const gdb_byte *name_table_entry_offs_reordered; | 
|  | const gdb_byte *entry_pool; | 
|  |  | 
|  | struct index_val | 
|  | { | 
|  | ULONGEST dwarf_tag; | 
|  | struct attr | 
|  | { | 
|  | /* Attribute name DW_IDX_*.  */ | 
|  | ULONGEST dw_idx; | 
|  |  | 
|  | /* Attribute form DW_FORM_*.  */ | 
|  | ULONGEST form; | 
|  |  | 
|  | /* Value if FORM is DW_FORM_implicit_const.  */ | 
|  | LONGEST implicit_const; | 
|  | }; | 
|  | std::vector<attr> attr_vec; | 
|  | }; | 
|  |  | 
|  | std::unordered_map<ULONGEST, index_val> abbrev_map; | 
|  |  | 
|  | const char *namei_to_name | 
|  | (uint32_t namei, dwarf2_per_objfile *per_objfile) const; | 
|  |  | 
|  | /* Implementation of the mapped_index_base virtual interface, for | 
|  | the name_components cache.  */ | 
|  |  | 
|  | const char *symbol_name_at | 
|  | (offset_type idx, dwarf2_per_objfile *per_objfile) const override | 
|  | { return namei_to_name (idx, per_objfile); } | 
|  |  | 
|  | size_t symbol_name_count () const override | 
|  | { return this->name_count; } | 
|  |  | 
|  | quick_symbol_functions_up make_quick_functions () const override; | 
|  | }; | 
|  |  | 
|  | struct dwarf2_debug_names_index : public dwarf2_base_index_functions | 
|  | { | 
|  | void dump (struct objfile *objfile) override; | 
|  |  | 
|  | void expand_matching_symbols | 
|  | (struct objfile *, | 
|  | const lookup_name_info &lookup_name, | 
|  | domain_enum domain, | 
|  | int global, | 
|  | symbol_compare_ftype *ordered_compare) override; | 
|  |  | 
|  | bool expand_symtabs_matching | 
|  | (struct objfile *objfile, | 
|  | gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher, | 
|  | const lookup_name_info *lookup_name, | 
|  | gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher, | 
|  | gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify, | 
|  | block_search_flags search_flags, | 
|  | domain_enum domain, | 
|  | enum search_domain kind) override; | 
|  | }; | 
|  |  | 
|  | quick_symbol_functions_up | 
|  | mapped_debug_names::make_quick_functions () const | 
|  | { | 
|  | return quick_symbol_functions_up (new dwarf2_debug_names_index); | 
|  | } | 
|  |  | 
|  | /* Create the signatured type hash table from .debug_names.  */ | 
|  |  | 
|  | static void | 
|  | create_signatured_type_table_from_debug_names | 
|  | (dwarf2_per_objfile *per_objfile, | 
|  | const mapped_debug_names &map, | 
|  | struct dwarf2_section_info *section, | 
|  | struct dwarf2_section_info *abbrev_section) | 
|  | { | 
|  | struct objfile *objfile = per_objfile->objfile; | 
|  |  | 
|  | section->read (objfile); | 
|  | abbrev_section->read (objfile); | 
|  |  | 
|  | htab_up sig_types_hash = allocate_signatured_type_table (); | 
|  |  | 
|  | for (uint32_t i = 0; i < map.tu_count; ++i) | 
|  | { | 
|  | signatured_type_up sig_type; | 
|  | void **slot; | 
|  |  | 
|  | sect_offset sect_off | 
|  | = (sect_offset) (extract_unsigned_integer | 
|  | (map.tu_table_reordered + i * map.offset_size, | 
|  | map.offset_size, | 
|  | map.dwarf5_byte_order)); | 
|  |  | 
|  | comp_unit_head cu_header; | 
|  | read_and_check_comp_unit_head (per_objfile, &cu_header, section, | 
|  | abbrev_section, | 
|  | section->buffer + to_underlying (sect_off), | 
|  | rcuh_kind::TYPE); | 
|  |  | 
|  | sig_type = per_objfile->per_bfd->allocate_signatured_type | 
|  | (cu_header.signature); | 
|  | sig_type->type_offset_in_tu = cu_header.type_cu_offset_in_tu; | 
|  | sig_type->section = section; | 
|  | sig_type->sect_off = sect_off; | 
|  |  | 
|  | slot = htab_find_slot (sig_types_hash.get (), sig_type.get (), INSERT); | 
|  | *slot = sig_type.get (); | 
|  |  | 
|  | per_objfile->per_bfd->all_units.emplace_back (sig_type.release ()); | 
|  | } | 
|  |  | 
|  | per_objfile->per_bfd->signatured_types = std::move (sig_types_hash); | 
|  | } | 
|  |  | 
|  | /* Read the address map data from DWARF-5 .debug_aranges, and use it to | 
|  | populate the index_addrmap.  */ | 
|  |  | 
|  | static void | 
|  | create_addrmap_from_aranges (dwarf2_per_objfile *per_objfile, | 
|  | struct dwarf2_section_info *section) | 
|  | { | 
|  | dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; | 
|  |  | 
|  | addrmap_mutable mutable_map; | 
|  |  | 
|  | if (read_addrmap_from_aranges (per_objfile, section, &mutable_map)) | 
|  | per_bfd->index_addrmap | 
|  | = new (&per_bfd->obstack) addrmap_fixed (&per_bfd->obstack, | 
|  | &mutable_map); | 
|  | } | 
|  |  | 
|  | /* DWARF-5 debug_names reader.  */ | 
|  |  | 
|  | /* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension.  */ | 
|  | static const gdb_byte dwarf5_augmentation[] = { 'G', 'D', 'B', 0 }; | 
|  |  | 
|  | /* A helper function that reads the .debug_names section in SECTION | 
|  | and fills in MAP.  FILENAME is the name of the file containing the | 
|  | section; it is used for error reporting. | 
|  |  | 
|  | Returns true if all went well, false otherwise.  */ | 
|  |  | 
|  | static bool | 
|  | read_debug_names_from_section (struct objfile *objfile, | 
|  | const char *filename, | 
|  | struct dwarf2_section_info *section, | 
|  | mapped_debug_names &map) | 
|  | { | 
|  | if (section->empty ()) | 
|  | return false; | 
|  |  | 
|  | /* Older elfutils strip versions could keep the section in the main | 
|  | executable while splitting it for the separate debug info file.  */ | 
|  | if ((section->get_flags () & SEC_HAS_CONTENTS) == 0) | 
|  | return false; | 
|  |  | 
|  | section->read (objfile); | 
|  |  | 
|  | map.dwarf5_byte_order = gdbarch_byte_order (objfile->arch ()); | 
|  |  | 
|  | const gdb_byte *addr = section->buffer; | 
|  |  | 
|  | bfd *const abfd = section->get_bfd_owner (); | 
|  |  | 
|  | unsigned int bytes_read; | 
|  | LONGEST length = read_initial_length (abfd, addr, &bytes_read); | 
|  | addr += bytes_read; | 
|  |  | 
|  | map.dwarf5_is_dwarf64 = bytes_read != 4; | 
|  | map.offset_size = map.dwarf5_is_dwarf64 ? 8 : 4; | 
|  | if (bytes_read + length != section->size) | 
|  | { | 
|  | /* There may be multiple per-CU indices.  */ | 
|  | warning (_("Section .debug_names in %s length %s does not match " | 
|  | "section length %s, ignoring .debug_names."), | 
|  | filename, plongest (bytes_read + length), | 
|  | pulongest (section->size)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* The version number.  */ | 
|  | uint16_t version = read_2_bytes (abfd, addr); | 
|  | addr += 2; | 
|  | if (version != 5) | 
|  | { | 
|  | warning (_("Section .debug_names in %s has unsupported version %d, " | 
|  | "ignoring .debug_names."), | 
|  | filename, version); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Padding.  */ | 
|  | uint16_t padding = read_2_bytes (abfd, addr); | 
|  | addr += 2; | 
|  | if (padding != 0) | 
|  | { | 
|  | warning (_("Section .debug_names in %s has unsupported padding %d, " | 
|  | "ignoring .debug_names."), | 
|  | filename, padding); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* comp_unit_count - The number of CUs in the CU list.  */ | 
|  | map.cu_count = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  |  | 
|  | /* local_type_unit_count - The number of TUs in the local TU | 
|  | list.  */ | 
|  | map.tu_count = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  |  | 
|  | /* foreign_type_unit_count - The number of TUs in the foreign TU | 
|  | list.  */ | 
|  | uint32_t foreign_tu_count = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  | if (foreign_tu_count != 0) | 
|  | { | 
|  | warning (_("Section .debug_names in %s has unsupported %lu foreign TUs, " | 
|  | "ignoring .debug_names."), | 
|  | filename, static_cast<unsigned long> (foreign_tu_count)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* bucket_count - The number of hash buckets in the hash lookup | 
|  | table.  */ | 
|  | map.bucket_count = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  |  | 
|  | /* name_count - The number of unique names in the index.  */ | 
|  | map.name_count = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  |  | 
|  | /* abbrev_table_size - The size in bytes of the abbreviations | 
|  | table.  */ | 
|  | uint32_t abbrev_table_size = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  |  | 
|  | /* augmentation_string_size - The size in bytes of the augmentation | 
|  | string.  This value is rounded up to a multiple of 4.  */ | 
|  | uint32_t augmentation_string_size = read_4_bytes (abfd, addr); | 
|  | addr += 4; | 
|  | map.augmentation_is_gdb = ((augmentation_string_size | 
|  | == sizeof (dwarf5_augmentation)) | 
|  | && memcmp (addr, dwarf5_augmentation, | 
|  | sizeof (dwarf5_augmentation)) == 0); | 
|  | augmentation_string_size += (-augmentation_string_size) & 3; | 
|  | addr += augmentation_string_size; | 
|  |  | 
|  | /* List of CUs */ | 
|  | map.cu_table_reordered = addr; | 
|  | addr += map.cu_count * map.offset_size; | 
|  |  | 
|  | /* List of Local TUs */ | 
|  | map.tu_table_reordered = addr; | 
|  | addr += map.tu_count * map.offset_size; | 
|  |  | 
|  | /* Hash Lookup Table */ | 
|  | map.bucket_table_reordered = reinterpret_cast<const uint32_t *> (addr); | 
|  | addr += map.bucket_count * 4; | 
|  | map.hash_table_reordered = reinterpret_cast<const uint32_t *> (addr); | 
|  | addr += map.name_count * 4; | 
|  |  | 
|  | /* Name Table */ | 
|  | map.name_table_string_offs_reordered = addr; | 
|  | addr += map.name_count * map.offset_size; | 
|  | map.name_table_entry_offs_reordered = addr; | 
|  | addr += map.name_count * map.offset_size; | 
|  |  | 
|  | const gdb_byte *abbrev_table_start = addr; | 
|  | for (;;) | 
|  | { | 
|  | const ULONGEST index_num = read_unsigned_leb128 (abfd, addr, &bytes_read); | 
|  | addr += bytes_read; | 
|  | if (index_num == 0) | 
|  | break; | 
|  |  | 
|  | const auto insertpair | 
|  | = map.abbrev_map.emplace (index_num, mapped_debug_names::index_val ()); | 
|  | if (!insertpair.second) | 
|  | { | 
|  | warning (_("Section .debug_names in %s has duplicate index %s, " | 
|  | "ignoring .debug_names."), | 
|  | filename, pulongest (index_num)); | 
|  | return false; | 
|  | } | 
|  | mapped_debug_names::index_val &indexval = insertpair.first->second; | 
|  | indexval.dwarf_tag = read_unsigned_leb128 (abfd, addr, &bytes_read); | 
|  | addr += bytes_read; | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | mapped_debug_names::index_val::attr attr; | 
|  | attr.dw_idx = read_unsigned_leb128 (abfd, addr, &bytes_read); | 
|  | addr += bytes_read; | 
|  | attr.form = read_unsigned_leb128 (abfd, addr, &bytes_read); | 
|  | addr += bytes_read; | 
|  | if (attr.form == DW_FORM_implicit_const) | 
|  | { | 
|  | attr.implicit_const = read_signed_leb128 (abfd, addr, | 
|  | &bytes_read); | 
|  | addr += bytes_read; | 
|  | } | 
|  | if (attr.dw_idx == 0 && attr.form == 0) | 
|  | break; | 
|  | indexval.attr_vec.push_back (std::move (attr)); | 
|  | } | 
|  | } | 
|  | if (addr != abbrev_table_start + abbrev_table_size) | 
|  | { | 
|  | warning (_("Section .debug_names in %s has abbreviation_table " | 
|  | "of size %s vs. written as %u, ignoring .debug_names."), | 
|  | filename, plongest (addr - abbrev_table_start), | 
|  | abbrev_table_size); | 
|  | return false; | 
|  | } | 
|  | map.entry_pool = addr; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* A helper for create_cus_from_debug_names that handles the MAP's CU | 
|  | list.  */ | 
|  |  | 
|  | static bool | 
|  | create_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd, | 
|  | const mapped_debug_names &map, | 
|  | dwarf2_section_info §ion, | 
|  | bool is_dwz) | 
|  | { | 
|  | if (!map.augmentation_is_gdb) | 
|  | { | 
|  | for (uint32_t i = 0; i < map.cu_count; ++i) | 
|  | { | 
|  | sect_offset sect_off | 
|  | = (sect_offset) (extract_unsigned_integer | 
|  | (map.cu_table_reordered + i * map.offset_size, | 
|  | map.offset_size, | 
|  | map.dwarf5_byte_order)); | 
|  | /* We don't know the length of the CU, because the CU list in a | 
|  | .debug_names index can be incomplete, so we can't use the start | 
|  | of the next CU as end of this CU.  We create the CUs here with | 
|  | length 0, and in cutu_reader::cutu_reader we'll fill in the | 
|  | actual length.  */ | 
|  | dwarf2_per_cu_data_up per_cu | 
|  | = create_cu_from_index_list (per_bfd, §ion, is_dwz, | 
|  | sect_off, 0); | 
|  | per_bfd->all_units.push_back (std::move (per_cu)); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | sect_offset sect_off_prev; | 
|  | for (uint32_t i = 0; i <= map.cu_count; ++i) | 
|  | { | 
|  | sect_offset sect_off_next; | 
|  | if (i < map.cu_count) | 
|  | { | 
|  | sect_off_next | 
|  | = (sect_offset) (extract_unsigned_integer | 
|  | (map.cu_table_reordered + i * map.offset_size, | 
|  | map.offset_size, | 
|  | map.dwarf5_byte_order)); | 
|  | } | 
|  | else | 
|  | sect_off_next = (sect_offset) section.size; | 
|  | if (i >= 1) | 
|  | { | 
|  | if (sect_off_next == sect_off_prev) | 
|  | { | 
|  | warning (_("Section .debug_names has duplicate entry in CU table," | 
|  | " ignoring .debug_names.")); | 
|  | return false; | 
|  | } | 
|  | if (sect_off_next < sect_off_prev) | 
|  | { | 
|  | warning (_("Section .debug_names has non-ascending CU table," | 
|  | " ignoring .debug_names.")); | 
|  | return false; | 
|  | } | 
|  | /* Note: we're not using length = sect_off_next - sect_off_prev, | 
|  | to gracefully handle an incomplete CU list.  */ | 
|  | const ULONGEST length = 0; | 
|  | dwarf2_per_cu_data_up per_cu | 
|  | = create_cu_from_index_list (per_bfd, §ion, is_dwz, | 
|  | sect_off_prev, length); | 
|  | per_bfd->all_units.push_back (std::move (per_cu)); | 
|  | } | 
|  | sect_off_prev = sect_off_next; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Read the CU list from the mapped index, and use it to create all | 
|  | the CU objects for this dwarf2_per_objfile.  */ | 
|  |  | 
|  | static bool | 
|  | create_cus_from_debug_names (dwarf2_per_bfd *per_bfd, | 
|  | const mapped_debug_names &map, | 
|  | const mapped_debug_names &dwz_map) | 
|  | { | 
|  | gdb_assert (per_bfd->all_units.empty ()); | 
|  | per_bfd->all_units.reserve (map.cu_count + dwz_map.cu_count); | 
|  |  | 
|  | if (!create_cus_from_debug_names_list (per_bfd, map, per_bfd->info, | 
|  | false /* is_dwz */)) | 
|  | return false; | 
|  |  | 
|  | if (dwz_map.cu_count == 0) | 
|  | return true; | 
|  |  | 
|  | dwz_file *dwz = dwarf2_get_dwz_file (per_bfd); | 
|  | return create_cus_from_debug_names_list (per_bfd, dwz_map, dwz->info, | 
|  | true /* is_dwz */); | 
|  | } | 
|  |  | 
|  | /* See read-debug-names.h.  */ | 
|  |  | 
|  | bool | 
|  | dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | std::unique_ptr<mapped_debug_names> map (new mapped_debug_names); | 
|  | mapped_debug_names dwz_map; | 
|  | struct objfile *objfile = per_objfile->objfile; | 
|  | dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; | 
|  |  | 
|  | if (!read_debug_names_from_section (objfile, objfile_name (objfile), | 
|  | &per_bfd->debug_names, *map)) | 
|  | return false; | 
|  |  | 
|  | /* Don't use the index if it's empty.  */ | 
|  | if (map->name_count == 0) | 
|  | return false; | 
|  |  | 
|  | /* If there is a .dwz file, read it so we can get its CU list as | 
|  | well.  */ | 
|  | dwz_file *dwz = dwarf2_get_dwz_file (per_bfd); | 
|  | if (dwz != NULL) | 
|  | { | 
|  | if (!read_debug_names_from_section (objfile, | 
|  | bfd_get_filename (dwz->dwz_bfd.get ()), | 
|  | &dwz->debug_names, dwz_map)) | 
|  | { | 
|  | warning (_("could not read '.debug_names' section from %s; skipping"), | 
|  | bfd_get_filename (dwz->dwz_bfd.get ())); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!create_cus_from_debug_names (per_bfd, *map, dwz_map)) | 
|  | { | 
|  | per_bfd->all_units.clear (); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (map->tu_count != 0) | 
|  | { | 
|  | /* We can only handle a single .debug_types when we have an | 
|  | index.  */ | 
|  | if (per_bfd->types.size () > 1) | 
|  | { | 
|  | per_bfd->all_units.clear (); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | dwarf2_section_info *section | 
|  | = (per_bfd->types.size () == 1 | 
|  | ? &per_bfd->types[0] | 
|  | : &per_bfd->info); | 
|  |  | 
|  | create_signatured_type_table_from_debug_names | 
|  | (per_objfile, *map, section, &per_bfd->abbrev); | 
|  | } | 
|  |  | 
|  | finalize_all_units (per_bfd); | 
|  |  | 
|  | create_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges); | 
|  |  | 
|  | per_bfd->index_table = std::move (map); | 
|  | per_bfd->quick_file_names_table = | 
|  | create_quick_file_names_table (per_bfd->all_units.size ()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Type used to manage iterating over all CUs looking for a symbol for | 
|  | .debug_names.  */ | 
|  |  | 
|  | class dw2_debug_names_iterator | 
|  | { | 
|  | public: | 
|  | dw2_debug_names_iterator (const mapped_debug_names &map, | 
|  | block_search_flags block_index, | 
|  | domain_enum domain, | 
|  | const char *name, dwarf2_per_objfile *per_objfile) | 
|  | : m_map (map), m_block_index (block_index), m_domain (domain), | 
|  | m_addr (find_vec_in_debug_names (map, name, per_objfile)), | 
|  | m_per_objfile (per_objfile) | 
|  | {} | 
|  |  | 
|  | dw2_debug_names_iterator (const mapped_debug_names &map, | 
|  | search_domain search, uint32_t namei, | 
|  | dwarf2_per_objfile *per_objfile, | 
|  | domain_enum domain = UNDEF_DOMAIN) | 
|  | : m_map (map), | 
|  | m_domain (domain), | 
|  | m_search (search), | 
|  | m_addr (find_vec_in_debug_names (map, namei, per_objfile)), | 
|  | m_per_objfile (per_objfile) | 
|  | {} | 
|  |  | 
|  | dw2_debug_names_iterator (const mapped_debug_names &map, | 
|  | block_search_flags block_index, domain_enum domain, | 
|  | uint32_t namei, dwarf2_per_objfile *per_objfile) | 
|  | : m_map (map), m_block_index (block_index), m_domain (domain), | 
|  | m_addr (find_vec_in_debug_names (map, namei, per_objfile)), | 
|  | m_per_objfile (per_objfile) | 
|  | {} | 
|  |  | 
|  | /* Return the next matching CU or NULL if there are no more.  */ | 
|  | dwarf2_per_cu_data *next (); | 
|  |  | 
|  | private: | 
|  | static const gdb_byte *find_vec_in_debug_names (const mapped_debug_names &map, | 
|  | const char *name, | 
|  | dwarf2_per_objfile *per_objfile); | 
|  | static const gdb_byte *find_vec_in_debug_names (const mapped_debug_names &map, | 
|  | uint32_t namei, | 
|  | dwarf2_per_objfile *per_objfile); | 
|  |  | 
|  | /* The internalized form of .debug_names.  */ | 
|  | const mapped_debug_names &m_map; | 
|  |  | 
|  | /* Restrict the search to these blocks.  */ | 
|  | block_search_flags m_block_index = (SEARCH_GLOBAL_BLOCK | 
|  | | SEARCH_STATIC_BLOCK); | 
|  |  | 
|  | /* The kind of symbol we're looking for.  */ | 
|  | const domain_enum m_domain = UNDEF_DOMAIN; | 
|  | const search_domain m_search = ALL_DOMAIN; | 
|  |  | 
|  | /* The list of CUs from the index entry of the symbol, or NULL if | 
|  | not found.  */ | 
|  | const gdb_byte *m_addr; | 
|  |  | 
|  | dwarf2_per_objfile *m_per_objfile; | 
|  | }; | 
|  |  | 
|  | const char * | 
|  | mapped_debug_names::namei_to_name | 
|  | (uint32_t namei, dwarf2_per_objfile *per_objfile) const | 
|  | { | 
|  | const ULONGEST namei_string_offs | 
|  | = extract_unsigned_integer ((name_table_string_offs_reordered | 
|  | + namei * offset_size), | 
|  | offset_size, | 
|  | dwarf5_byte_order); | 
|  | return read_indirect_string_at_offset (per_objfile, namei_string_offs); | 
|  | } | 
|  |  | 
|  | /* Find a slot in .debug_names for the object named NAME.  If NAME is | 
|  | found, return pointer to its pool data.  If NAME cannot be found, | 
|  | return NULL.  */ | 
|  |  | 
|  | const gdb_byte * | 
|  | dw2_debug_names_iterator::find_vec_in_debug_names | 
|  | (const mapped_debug_names &map, const char *name, | 
|  | dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | int (*cmp) (const char *, const char *); | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<char> without_params; | 
|  | if (current_language->la_language == language_cplus | 
|  | || current_language->la_language == language_fortran | 
|  | || current_language->la_language == language_d) | 
|  | { | 
|  | /* NAME is already canonical.  Drop any qualifiers as | 
|  | .debug_names does not contain any.  */ | 
|  |  | 
|  | if (strchr (name, '(') != NULL) | 
|  | { | 
|  | without_params = cp_remove_params (name); | 
|  | if (without_params != NULL) | 
|  | name = without_params.get (); | 
|  | } | 
|  | } | 
|  |  | 
|  | cmp = (case_sensitivity == case_sensitive_on ? strcmp : strcasecmp); | 
|  |  | 
|  | const uint32_t full_hash = dwarf5_djb_hash (name); | 
|  | uint32_t namei | 
|  | = extract_unsigned_integer (reinterpret_cast<const gdb_byte *> | 
|  | (map.bucket_table_reordered | 
|  | + (full_hash % map.bucket_count)), 4, | 
|  | map.dwarf5_byte_order); | 
|  | if (namei == 0) | 
|  | return NULL; | 
|  | --namei; | 
|  | if (namei >= map.name_count) | 
|  | { | 
|  | complaint (_("Wrong .debug_names with name index %u but name_count=%u " | 
|  | "[in module %s]"), | 
|  | namei, map.name_count, | 
|  | objfile_name (per_objfile->objfile)); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | const uint32_t namei_full_hash | 
|  | = extract_unsigned_integer (reinterpret_cast<const gdb_byte *> | 
|  | (map.hash_table_reordered + namei), 4, | 
|  | map.dwarf5_byte_order); | 
|  | if (full_hash % map.bucket_count != namei_full_hash % map.bucket_count) | 
|  | return NULL; | 
|  |  | 
|  | if (full_hash == namei_full_hash) | 
|  | { | 
|  | const char *const namei_string = map.namei_to_name (namei, per_objfile); | 
|  |  | 
|  | #if 0 /* An expensive sanity check.  */ | 
|  | if (namei_full_hash != dwarf5_djb_hash (namei_string)) | 
|  | { | 
|  | complaint (_("Wrong .debug_names hash for string at index %u " | 
|  | "[in module %s]"), | 
|  | namei, objfile_name (dwarf2_per_objfile->objfile)); | 
|  | return NULL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (cmp (namei_string, name) == 0) | 
|  | { | 
|  | const ULONGEST namei_entry_offs | 
|  | = extract_unsigned_integer ((map.name_table_entry_offs_reordered | 
|  | + namei * map.offset_size), | 
|  | map.offset_size, map.dwarf5_byte_order); | 
|  | return map.entry_pool + namei_entry_offs; | 
|  | } | 
|  | } | 
|  |  | 
|  | ++namei; | 
|  | if (namei >= map.name_count) | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | const gdb_byte * | 
|  | dw2_debug_names_iterator::find_vec_in_debug_names | 
|  | (const mapped_debug_names &map, uint32_t namei, dwarf2_per_objfile *per_objfile) | 
|  | { | 
|  | if (namei >= map.name_count) | 
|  | { | 
|  | complaint (_("Wrong .debug_names with name index %u but name_count=%u " | 
|  | "[in module %s]"), | 
|  | namei, map.name_count, | 
|  | objfile_name (per_objfile->objfile)); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | const ULONGEST namei_entry_offs | 
|  | = extract_unsigned_integer ((map.name_table_entry_offs_reordered | 
|  | + namei * map.offset_size), | 
|  | map.offset_size, map.dwarf5_byte_order); | 
|  | return map.entry_pool + namei_entry_offs; | 
|  | } | 
|  |  | 
|  | /* See dw2_debug_names_iterator.  */ | 
|  |  | 
|  | dwarf2_per_cu_data * | 
|  | dw2_debug_names_iterator::next () | 
|  | { | 
|  | if (m_addr == NULL) | 
|  | return NULL; | 
|  |  | 
|  | dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; | 
|  | struct objfile *objfile = m_per_objfile->objfile; | 
|  | bfd *const abfd = objfile->obfd.get (); | 
|  |  | 
|  | again: | 
|  |  | 
|  | unsigned int bytes_read; | 
|  | const ULONGEST abbrev = read_unsigned_leb128 (abfd, m_addr, &bytes_read); | 
|  | m_addr += bytes_read; | 
|  | if (abbrev == 0) | 
|  | return NULL; | 
|  |  | 
|  | const auto indexval_it = m_map.abbrev_map.find (abbrev); | 
|  | if (indexval_it == m_map.abbrev_map.cend ()) | 
|  | { | 
|  | complaint (_("Wrong .debug_names undefined abbrev code %s " | 
|  | "[in module %s]"), | 
|  | pulongest (abbrev), objfile_name (objfile)); | 
|  | return NULL; | 
|  | } | 
|  | const mapped_debug_names::index_val &indexval = indexval_it->second; | 
|  | enum class symbol_linkage { | 
|  | unknown, | 
|  | static_, | 
|  | extern_, | 
|  | } symbol_linkage_ = symbol_linkage::unknown; | 
|  | dwarf2_per_cu_data *per_cu = NULL; | 
|  | for (const mapped_debug_names::index_val::attr &attr : indexval.attr_vec) | 
|  | { | 
|  | ULONGEST ull; | 
|  | switch (attr.form) | 
|  | { | 
|  | case DW_FORM_implicit_const: | 
|  | ull = attr.implicit_const; | 
|  | break; | 
|  | case DW_FORM_flag_present: | 
|  | ull = 1; | 
|  | break; | 
|  | case DW_FORM_udata: | 
|  | ull = read_unsigned_leb128 (abfd, m_addr, &bytes_read); | 
|  | m_addr += bytes_read; | 
|  | break; | 
|  | case DW_FORM_ref4: | 
|  | ull = read_4_bytes (abfd, m_addr); | 
|  | m_addr += 4; | 
|  | break; | 
|  | case DW_FORM_ref8: | 
|  | ull = read_8_bytes (abfd, m_addr); | 
|  | m_addr += 8; | 
|  | break; | 
|  | case DW_FORM_ref_sig8: | 
|  | ull = read_8_bytes (abfd, m_addr); | 
|  | m_addr += 8; | 
|  | break; | 
|  | default: | 
|  | complaint (_("Unsupported .debug_names form %s [in module %s]"), | 
|  | dwarf_form_name (attr.form), | 
|  | objfile_name (objfile)); | 
|  | return NULL; | 
|  | } | 
|  | switch (attr.dw_idx) | 
|  | { | 
|  | case DW_IDX_compile_unit: | 
|  | { | 
|  | /* Don't crash on bad data.  */ | 
|  | if (ull >= per_bfd->all_comp_units.size ()) | 
|  | { | 
|  | complaint (_(".debug_names entry has bad CU index %s" | 
|  | " [in module %s]"), | 
|  | pulongest (ull), | 
|  | objfile_name (objfile)); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | per_cu = per_bfd->get_cu (ull); | 
|  | break; | 
|  | case DW_IDX_type_unit: | 
|  | /* Don't crash on bad data.  */ | 
|  | if (ull >= per_bfd->all_type_units.size ()) | 
|  | { | 
|  | complaint (_(".debug_names entry has bad TU index %s" | 
|  | " [in module %s]"), | 
|  | pulongest (ull), | 
|  | objfile_name (objfile)); | 
|  | continue; | 
|  | } | 
|  | { | 
|  | int nr_cus = per_bfd->all_comp_units.size (); | 
|  | per_cu = per_bfd->get_cu (nr_cus + ull); | 
|  | } | 
|  | break; | 
|  | case DW_IDX_die_offset: | 
|  | /* In a per-CU index (as opposed to a per-module index), index | 
|  | entries without CU attribute implicitly refer to the single CU.  */ | 
|  | if (per_cu == NULL) | 
|  | per_cu = per_bfd->get_cu (0); | 
|  | break; | 
|  | case DW_IDX_GNU_internal: | 
|  | if (!m_map.augmentation_is_gdb) | 
|  | break; | 
|  | symbol_linkage_ = symbol_linkage::static_; | 
|  | break; | 
|  | case DW_IDX_GNU_external: | 
|  | if (!m_map.augmentation_is_gdb) | 
|  | break; | 
|  | symbol_linkage_ = symbol_linkage::extern_; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Skip if we couldn't find a valid CU/TU index.  */ | 
|  | if (per_cu == nullptr) | 
|  | goto again; | 
|  |  | 
|  | /* Skip if already read in.  */ | 
|  | if (m_per_objfile->symtab_set_p (per_cu)) | 
|  | goto again; | 
|  |  | 
|  | /* Check static vs global.  */ | 
|  | if (symbol_linkage_ != symbol_linkage::unknown) | 
|  | { | 
|  | if (symbol_linkage_ == symbol_linkage::static_) | 
|  | { | 
|  | if ((m_block_index & SEARCH_STATIC_BLOCK) == 0) | 
|  | goto again; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ((m_block_index & SEARCH_GLOBAL_BLOCK) == 0) | 
|  | goto again; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Match dw2_symtab_iter_next, symbol_kind | 
|  | and debug_names::psymbol_tag.  */ | 
|  | switch (m_domain) | 
|  | { | 
|  | case VAR_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_variable: | 
|  | case DW_TAG_subprogram: | 
|  | /* Some types are also in VAR_DOMAIN.  */ | 
|  | case DW_TAG_typedef: | 
|  | case DW_TAG_structure_type: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case STRUCT_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_typedef: | 
|  | case DW_TAG_structure_type: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case LABEL_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case 0: | 
|  | case DW_TAG_variable: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case MODULE_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_module: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Match dw2_expand_symtabs_matching, symbol_kind and | 
|  | debug_names::psymbol_tag.  */ | 
|  | switch (m_search) | 
|  | { | 
|  | case VARIABLES_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_variable: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case FUNCTIONS_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_subprogram: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case TYPES_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_typedef: | 
|  | case DW_TAG_structure_type: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | break; | 
|  | case MODULES_DOMAIN: | 
|  | switch (indexval.dwarf_tag) | 
|  | { | 
|  | case DW_TAG_module: | 
|  | break; | 
|  | default: | 
|  | goto again; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return per_cu; | 
|  | } | 
|  |  | 
|  | /* This dumps minimal information about .debug_names.  It is called | 
|  | via "mt print objfiles".  The gdb.dwarf2/gdb-index.exp testcase | 
|  | uses this to verify that .debug_names has been loaded.  */ | 
|  |  | 
|  | void | 
|  | dwarf2_debug_names_index::dump (struct objfile *objfile) | 
|  | { | 
|  | gdb_printf (".debug_names: exists\n"); | 
|  | } | 
|  |  | 
|  | void | 
|  | dwarf2_debug_names_index::expand_matching_symbols | 
|  | (struct objfile *objfile, | 
|  | const lookup_name_info &name, domain_enum domain, | 
|  | int global, | 
|  | symbol_compare_ftype *ordered_compare) | 
|  | { | 
|  | dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile); | 
|  |  | 
|  | mapped_debug_names &map | 
|  | = *(gdb::checked_static_cast<mapped_debug_names *> | 
|  | (per_objfile->per_bfd->index_table.get ())); | 
|  | const block_search_flags block_flags | 
|  | = global ? SEARCH_GLOBAL_BLOCK : SEARCH_STATIC_BLOCK; | 
|  |  | 
|  | const char *match_name = name.ada ().lookup_name ().c_str (); | 
|  | auto matcher = [&] (const char *symname) | 
|  | { | 
|  | if (ordered_compare == nullptr) | 
|  | return true; | 
|  | return ordered_compare (symname, match_name) == 0; | 
|  | }; | 
|  |  | 
|  | dw2_expand_symtabs_matching_symbol (map, name, matcher, | 
|  | [&] (offset_type namei) | 
|  | { | 
|  | /* The name was matched, now expand corresponding CUs that were | 
|  | marked.  */ | 
|  | dw2_debug_names_iterator iter (map, block_flags, domain, namei, | 
|  | per_objfile); | 
|  |  | 
|  | struct dwarf2_per_cu_data *per_cu; | 
|  | while ((per_cu = iter.next ()) != NULL) | 
|  | dw2_expand_symtabs_matching_one (per_cu, per_objfile, nullptr, | 
|  | nullptr); | 
|  | return true; | 
|  | }, per_objfile); | 
|  | } | 
|  |  | 
|  | bool | 
|  | dwarf2_debug_names_index::expand_symtabs_matching | 
|  | (struct objfile *objfile, | 
|  | gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher, | 
|  | const lookup_name_info *lookup_name, | 
|  | gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher, | 
|  | gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify, | 
|  | block_search_flags search_flags, | 
|  | domain_enum domain, | 
|  | enum search_domain kind) | 
|  | { | 
|  | dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile); | 
|  |  | 
|  | dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher); | 
|  |  | 
|  | /* This invariant is documented in quick-functions.h.  */ | 
|  | gdb_assert (lookup_name != nullptr || symbol_matcher == nullptr); | 
|  | if (lookup_name == nullptr) | 
|  | { | 
|  | for (dwarf2_per_cu_data *per_cu | 
|  | : all_units_range (per_objfile->per_bfd)) | 
|  | { | 
|  | QUIT; | 
|  |  | 
|  | if (!dw2_expand_symtabs_matching_one (per_cu, per_objfile, | 
|  | file_matcher, | 
|  | expansion_notify)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | mapped_debug_names &map | 
|  | = *(gdb::checked_static_cast<mapped_debug_names *> | 
|  | (per_objfile->per_bfd->index_table.get ())); | 
|  |  | 
|  | bool result | 
|  | = dw2_expand_symtabs_matching_symbol (map, *lookup_name, | 
|  | symbol_matcher, | 
|  | [&] (offset_type namei) | 
|  | { | 
|  | /* The name was matched, now expand corresponding CUs that were | 
|  | marked.  */ | 
|  | dw2_debug_names_iterator iter (map, kind, namei, per_objfile, domain); | 
|  |  | 
|  | struct dwarf2_per_cu_data *per_cu; | 
|  | while ((per_cu = iter.next ()) != NULL) | 
|  | if (!dw2_expand_symtabs_matching_one (per_cu, per_objfile, | 
|  | file_matcher, | 
|  | expansion_notify)) | 
|  | return false; | 
|  | return true; | 
|  | }, per_objfile); | 
|  |  | 
|  | return result; | 
|  | } |