blob: 0d60b01f408cc427c0f654c0e3f89216ce8a54f2 [file] [log] [blame]
/* Reading code for .debug_names
Copyright (C) 2023-2024 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 "dwarf2/aranges.h"
#include "dwarf2/cooked-index.h"
#include "complaints.h"
#include "cp-support.h"
#include "dwz.h"
#include "mapped-index.h"
#include "read.h"
#include "stringify.h"
/* This is just like cooked_index_functions, but overrides a single
method so the test suite can distinguish the .debug_names case from
the ordinary case. */
struct dwarf2_debug_names_index : public cooked_index_functions
{
/* 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 dump (struct objfile *objfile) override
{
gdb_printf (".debug_names: exists\n");
/* This could call the superclass method if that's useful. */
}
};
/* This is like a cooked index, but as it has been ingested from
.debug_names, it can't be used to write out an index. */
class debug_names_index : public cooked_index
{
public:
using cooked_index::cooked_index;
cooked_index *index_for_writing () override
{ return nullptr; }
quick_symbol_functions_up make_quick_functions () const override
{ return quick_symbol_functions_up (new dwarf2_debug_names_index); }
};
/* A description of the mapped .debug_names. */
struct mapped_debug_names_reader
{
const gdb_byte *scan_one_entry (const char *name,
const gdb_byte *entry,
cooked_index_entry **result,
std::optional<ULONGEST> &parent);
void scan_entries (uint32_t index, const char *name, const gdb_byte *entry);
void scan_all_names ();
dwarf2_per_objfile *per_objfile = nullptr;
bfd *abfd = nullptr;
bfd_endian dwarf5_byte_order {};
bool dwarf5_is_dwarf64 = false;
bool augmentation_is_gdb = false;
uint8_t offset_size = 0;
uint32_t cu_count = 0;
uint32_t tu_count = 0, bucket_count = 0, name_count = 0;
const gdb_byte *cu_table_reordered = nullptr;
const gdb_byte *tu_table_reordered = nullptr;
const uint32_t *bucket_table_reordered = nullptr;
const uint32_t *hash_table_reordered = nullptr;
const gdb_byte *name_table_string_offs_reordered = nullptr;
const gdb_byte *name_table_entry_offs_reordered = nullptr;
const gdb_byte *entry_pool = nullptr;
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;
std::unique_ptr<cooked_index_shard> shard;
std::vector<std::pair<cooked_index_entry *, ULONGEST>> needs_parent;
std::vector<std::vector<cooked_index_entry *>> all_entries;
};
/* Scan a single entry from the entries table. Set *RESULT and PARENT
(if needed) and return the updated pointer on success, or return
nullptr on error, or at the end of the table. */
const gdb_byte *
mapped_debug_names_reader::scan_one_entry (const char *name,
const gdb_byte *entry,
cooked_index_entry **result,
std::optional<ULONGEST> &parent)
{
unsigned int bytes_read;
const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read);
entry += bytes_read;
if (abbrev == 0)
return nullptr;
const auto indexval_it = abbrev_map.find (abbrev);
if (indexval_it == abbrev_map.cend ())
{
complaint (_("Wrong .debug_names undefined abbrev code %s "
"[in module %s]"),
pulongest (abbrev), bfd_get_filename (abfd));
return nullptr;
}
const auto &indexval = indexval_it->second;
cooked_index_flag flags = 0;
sect_offset die_offset {};
enum language lang = language_unknown;
dwarf2_per_cu_data *per_cu = nullptr;
for (const auto &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, entry, &bytes_read);
entry += bytes_read;
break;
case DW_FORM_ref_addr:
ull = read_offset (abfd, entry, offset_size);
entry += offset_size;
break;
case DW_FORM_ref4:
ull = read_4_bytes (abfd, entry);
entry += 4;
break;
case DW_FORM_ref8:
ull = read_8_bytes (abfd, entry);
entry += 8;
break;
case DW_FORM_ref_sig8:
ull = read_8_bytes (abfd, entry);
entry += 8;
break;
default:
complaint (_("Unsupported .debug_names form %s [in module %s]"),
dwarf_form_name (attr.form),
bfd_get_filename (abfd));
return nullptr;
}
switch (attr.dw_idx)
{
case DW_IDX_compile_unit:
{
/* Don't crash on bad data. */
if (ull >= per_objfile->per_bfd->all_comp_units.size ())
{
complaint (_(".debug_names entry has bad CU index %s"
" [in module %s]"),
pulongest (ull),
bfd_get_filename (abfd));
continue;
}
}
per_cu = per_objfile->per_bfd->get_cu (ull);
break;
case DW_IDX_type_unit:
/* Don't crash on bad data. */
if (ull >= per_objfile->per_bfd->all_type_units.size ())
{
complaint (_(".debug_names entry has bad TU index %s"
" [in module %s]"),
pulongest (ull),
bfd_get_filename (abfd));
continue;
}
{
int nr_cus = per_objfile->per_bfd->all_comp_units.size ();
per_cu = per_objfile->per_bfd->get_cu (nr_cus + ull);
}
break;
case DW_IDX_die_offset:
die_offset = sect_offset (ull);
/* 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_objfile->per_bfd->get_cu (0);
break;
case DW_IDX_parent:
parent = ull;
break;
case DW_IDX_GNU_internal:
if (augmentation_is_gdb && ull != 0)
flags |= IS_STATIC;
break;
case DW_IDX_GNU_main:
if (augmentation_is_gdb && ull != 0)
flags |= IS_MAIN;
break;
case DW_IDX_GNU_language:
if (augmentation_is_gdb)
lang = dwarf_lang_to_enum_language (ull);
break;
case DW_IDX_GNU_linkage_name:
if (augmentation_is_gdb && ull != 0)
flags |= IS_LINKAGE;
break;
}
}
/* Skip if we couldn't find a valid CU/TU index. */
if (per_cu != nullptr)
*result = shard->add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags,
lang, name, nullptr, per_cu);
return entry;
}
/* Scan all the entries for NAME, at name slot INDEX. */
void
mapped_debug_names_reader::scan_entries (uint32_t index,
const char *name,
const gdb_byte *entry)
{
std::vector<cooked_index_entry *> these_entries;
while (true)
{
std::optional<ULONGEST> parent;
cooked_index_entry *this_entry;
entry = scan_one_entry (name, entry, &this_entry, parent);
if (entry == nullptr)
break;
these_entries.push_back (this_entry);
if (parent.has_value ())
needs_parent.emplace_back (this_entry, *parent);
}
all_entries[index] = std::move (these_entries);
}
/* Scan the name table and create all the entries. */
void
mapped_debug_names_reader::scan_all_names ()
{
all_entries.resize (name_count);
/* In the first pass, create all the entries. */
for (uint32_t i = 0; i < name_count; ++i)
{
const ULONGEST namei_string_offs
= extract_unsigned_integer ((name_table_string_offs_reordered
+ i * offset_size),
offset_size, dwarf5_byte_order);
const char *name = read_indirect_string_at_offset (per_objfile,
namei_string_offs);
const ULONGEST namei_entry_offs
= extract_unsigned_integer ((name_table_entry_offs_reordered
+ i * offset_size),
offset_size, dwarf5_byte_order);
const gdb_byte *entry = entry_pool + namei_entry_offs;
scan_entries (i, name, entry);
}
/* Now update the parent pointers for all entries. This has to be
done in a funny way because DWARF specifies the parent entry to
point to a name -- but we don't know which specific one. */
for (auto [entry, parent_idx] : needs_parent)
{
/* Name entries are indexed from 1 in DWARF. */
std::vector<cooked_index_entry *> &entries = all_entries[parent_idx - 1];
for (const auto &parent : entries)
if (parent->lang == entry->lang)
{
entry->set_parent (parent);
break;
}
}
}
/* A reader for .debug_names. */
struct cooked_index_debug_names : public cooked_index_worker
{
cooked_index_debug_names (dwarf2_per_objfile *per_objfile,
mapped_debug_names_reader &&map)
: cooked_index_worker (per_objfile),
m_map (std::move (map))
{ }
void do_reading () override;
mapped_debug_names_reader m_map;
};
void
cooked_index_debug_names::do_reading ()
{
complaint_interceptor complaint_handler;
std::vector<gdb_exception> exceptions;
try
{
m_map.scan_all_names ();
}
catch (const gdb_exception &exc)
{
exceptions.push_back (std::move (exc));
}
dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
per_bfd->quick_file_names_table
= create_quick_file_names_table (per_bfd->all_units.size ());
m_results.emplace_back (nullptr,
complaint_handler.release (),
std::move (exceptions));
std::vector<std::unique_ptr<cooked_index_shard>> indexes;
indexes.push_back (std::move (m_map.shard));
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
(per_bfd->index_table.get ()));
table->set_contents (std::move (indexes), &m_warnings);
bfd_thread_cleanup ();
}
/* Check the signatured type hash table from .debug_names. */
static bool
check_signatured_type_table_from_debug_names
(dwarf2_per_objfile *per_objfile,
const mapped_debug_names_reader &map,
struct dwarf2_section_info *section)
{
struct objfile *objfile = per_objfile->objfile;
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
int nr_cus = per_bfd->all_comp_units.size ();
int nr_cus_tus = per_bfd->all_units.size ();
section->read (objfile);
uint32_t j = nr_cus;
for (uint32_t i = 0; i < map.tu_count; ++i)
{
sect_offset sect_off
= (sect_offset) (extract_unsigned_integer
(map.tu_table_reordered + i * map.offset_size,
map.offset_size,
map.dwarf5_byte_order));
bool found = false;
for (; j < nr_cus_tus; j++)
if (per_bfd->get_cu (j)->sect_off == sect_off)
{
found = true;
break;
}
if (!found)
{
warning (_("Section .debug_names has incorrect entry in TU table,"
" ignoring .debug_names."));
return false;
}
per_bfd->all_comp_units_index_tus.push_back (per_bfd->get_cu (j));
}
return true;
}
/* DWARF-5 debug_names reader. */
/* The old, no-longer-supported GDB augmentation. */
static const gdb_byte old_gdb_augmentation[]
= { 'G', 'D', 'B', 0 };
static_assert (sizeof (old_gdb_augmentation) % 4 == 0);
/* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension. This
must have a size that is a multiple of 4. */
const gdb_byte dwarf5_augmentation[8] = { 'G', 'D', 'B', '2', 0, 0, 0, 0 };
static_assert (sizeof (dwarf5_augmentation) % 4 == 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 (dwarf2_per_objfile *per_objfile,
const char *filename,
struct dwarf2_section_info *section,
mapped_debug_names_reader &map)
{
struct objfile *objfile = per_objfile->objfile;
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.per_objfile = per_objfile;
map.dwarf5_byte_order = gdbarch_byte_order (objfile->arch ());
const gdb_byte *addr = section->buffer;
bfd *abfd = section->get_bfd_owner ();
map.abfd = abfd;
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;
augmentation_string_size += (-augmentation_string_size) & 3;
if (augmentation_string_size == sizeof (old_gdb_augmentation)
&& memcmp (addr, old_gdb_augmentation,
sizeof (old_gdb_augmentation)) == 0)
{
warning (_(".debug_names created by an old version of gdb; ignoring"));
return false;
}
map.augmentation_is_gdb = ((augmentation_string_size
== sizeof (dwarf5_augmentation))
&& memcmp (addr, dwarf5_augmentation,
sizeof (dwarf5_augmentation)) == 0);
if (!map.augmentation_is_gdb)
{
warning (_(".debug_names not created by gdb; ignoring"));
return false;
}
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);
if (map.bucket_count != 0)
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_reader::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_reader::index_val &indexval = insertpair.first->second;
indexval.dwarf_tag = read_unsigned_leb128 (abfd, addr, &bytes_read);
addr += bytes_read;
for (;;)
{
mapped_debug_names_reader::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 check_cus_from_debug_names that handles the MAP's CU
list. */
static bool
check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd,
const mapped_debug_names_reader &map,
dwarf2_section_info &section,
bool is_dwz)
{
int nr_cus = per_bfd->all_comp_units.size ();
if (!map.augmentation_is_gdb)
{
uint32_t j = 0;
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));
bool found = false;
for (; j < nr_cus; j++)
if (per_bfd->get_cu (j)->sect_off == sect_off)
{
found = true;
break;
}
if (!found)
{
warning (_("Section .debug_names has incorrect entry in CU table,"
" ignoring .debug_names."));
return false;
}
per_bfd->all_comp_units_index_cus.push_back (per_bfd->get_cu (j));
}
return true;
}
if (map.cu_count != nr_cus)
{
warning (_("Section .debug_names has incorrect number of CUs in CU table,"
" ignoring .debug_names."));
return false;
}
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));
if (sect_off != per_bfd->get_cu (i)->sect_off)
{
warning (_("Section .debug_names has incorrect entry in CU table,"
" ignoring .debug_names."));
return false;
}
}
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
check_cus_from_debug_names (dwarf2_per_bfd *per_bfd,
const mapped_debug_names_reader &map,
const mapped_debug_names_reader &dwz_map)
{
if (!check_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 check_cus_from_debug_names_list (per_bfd, dwz_map, dwz->info,
true /* is_dwz */);
}
/* This does all the work for dwarf2_read_debug_names, but putting it
into a separate function makes some cleanup a bit simpler. */
static bool
do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
{
mapped_debug_names_reader map;
mapped_debug_names_reader dwz_map;
struct objfile *objfile = per_objfile->objfile;
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
if (!read_debug_names_from_section (per_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 (per_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;
}
}
create_all_units (per_objfile);
if (!check_cus_from_debug_names (per_bfd, map, dwz_map))
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)
return false;
dwarf2_section_info *section
= (per_bfd->types.size () == 1
? &per_bfd->types[0]
: &per_bfd->info);
if (!check_signatured_type_table_from_debug_names (per_objfile,
map, section))
return false;
}
per_bfd->debug_aranges.read (per_objfile->objfile);
addrmap_mutable addrmap;
deferred_warnings warnings;
read_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges,
&addrmap, &warnings);
warnings.emit ();
map.shard = std::make_unique<cooked_index_shard> ();
map.shard->install_addrmap (&addrmap);
cooked_index *idx
= new debug_names_index (per_objfile,
(std::make_unique<cooked_index_debug_names>
(per_objfile, std::move (map))));
per_bfd->index_table.reset (idx);
idx->start_reading ();
return true;
}
/* See read-debug-names.h. */
bool
dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
{
bool result = do_dwarf2_read_debug_names (per_objfile);
if (!result)
per_objfile->per_bfd->all_units.clear ();
return result;
}