| /* 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; |
| } |