mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-12-18 16:57:56 +08:00
While working on background DWARF reading, I found a race case that I tracked down to the handling of the .debug_aranges section. Currently the section data is only read in after the CUs have all been created. However, there's no real reason to do this -- it seems fine to read it a little earlier, when all the other necessary sections are read in. This patch makes this change, and updates the read_addrmap_from_aranges API to assert that the section is read in. This patch slightly changes the read_addrmap_from_aranges API as well, to reject an empty section. This seems better to me than what the current code does, which is try to read an empty section but then do no work. Regression tested on x86-64 Fedora 38. Reviewed-By: Guinevere Larsen <blarsen@redhat.com>
1054 lines
29 KiB
C
1054 lines
29 KiB
C
/* 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;
|
|
|
|
section->read (per_objfile->objfile);
|
|
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)
|
|
{
|
|
auto map = gdb::make_unique<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;
|
|
}
|