Hi Simon! This commit seems to have made the gdb.dwarf2/debug-names-bad-cu-index.exp have some racy cases. I've attached a log of the failed run in this email to help you figure out what could be going on. On 5/11/26 12:40 PM, Simon Marchi wrote: > In DWARF 5, foreign type units are type units present in .dwo files. > Type units in .dwo files don't have a matching skeleton in the main > file. When an .debug_names index is present, it can't include those > type units in the regular type unit list, since that list specifies > offsets in the main file. They are instead listed in the foreign TU > list, which is basically just a list of type signatures. > > In order to help the debugger locate these units (i.e. find the .dwo > file containing them), individual index entries referencing foreign type > units may also include a reference to a compile unit that the debugger > can follow to find the appropriate .dwo file. > > This patch implements reading the .debug_names foreign TU list and using > these "hint" CUs to locate foreign type units. I use the term "hint" > throughout the code, but I don't mind another name if someone has a > better idea. > > The first part is to read the actual foreign TU list from the > .debug_names index and create signatured_type objects out of them. This > is done in the new function create_foreign_type_units_from_debug_names. > Append the newly created signatured_type to the > mapped_debug_names_reader::foreign_type_units vector, which will be used > later to resolve DW_IDX_type_unit indices. Populate the > dwarf2_per_bfd::signatured_types set, which contains signatured_types > indexed by signature. And finally, transfer ownership of the object to > the dwarf2_per_bfd::all_units vector. > > Previously, all dwarf2_per_cu (including signatured_type) objects were > created with a non-nullptr section. With foreign type units, we don't > know the section at creation time. We also don't know the offset into > section nor the size of the unit. Therefore, add a > dwarf2_per_bfd::allocate_signatured_type overload that takes just the > signature. Remove the "section != nullptr" assert from the > dwarf2_per_cu constructor, but add it to the other allocate_* methods. > > Since the new create_foreign_type_units_from_debug_names function adds > items to the dwarf2_per_bfd::all_units vector, the vector needs to be > sorted after create_foreign_type_units_from_debug_names runs. Remove > the finalize_all_units call from create_all_units, making the callers > responsible to call it. > > The next step is to read the hint CU attributes when scanning the index > entries. Rework mapped_debug_names_reader::scan_one_entry to remember > which kind of unit references it saw for the entry (comp, type and/or > foreign type) and then figure out what this means. The logic is: > > - Did the entry reference a foreign type unit? If so, it's a foreign > type unit. Does it also reference a hint CU? If not, drop the > entry, there's nothing useful we can do with it. > > - Otherwise, did the entry reference a (non-foreign) type unit? Then > it's a regular type unit. If so, it shouldn't also have a > DW_IDX_compile_unit. > > - Otherwise, did the entry reference a comp unit? If so, it's a comp > unit. > > - Otherwise, we don't know what unit the entry references, it's an > error. > > Since the .debug_name index attaches hint CU attributes to individual > index entries, my initial implementation added the hint CU information > to the cooked_index_entry structure. I am not sure why DWARF 5 chose to > do it this way, as opposed to attaching one hint per foreign TU. Does > this mean that two type units with the same signature could be different > and, for a specific index entry, it would be important to find one > specific instance of the type unit over the others? I have no idea. > > However, I know that the current GDB DWARF reader is not able to load > multiple type units with the same signature but different content. Once > it loads one type unit with a given signature, all subsequent references > to that signature will use that loaded type unit. I therefore chose to > have the .debug_names reader record just one hint CU per foreign TU. > This avoids growing the cooked_index_entry structure for nothing, and > having to pass through this information through multiple layers. > > The next step is to locate the .dwo file containing the foreign TUs when > we need them. This sometimes makes use of the hint CU, but not always. > I identified these 3 code paths: > > 1. When the type unit gets expanded directly, for instance if you use > "ptype" and there is a direct match into the type unit. This case > is handled in load_full_type_unit, calling a new function > fill_in_sig_entry_from_per_cu_hint. This one uses the hint recorded > by the .debug_names reader. When a cooked index entry exists and > refers to a foreign TU for which the section is not yet known, we > know that there exists a hint, otherwise we wouldn't have created > the entry in the first place. > > 2. The second one is when the type unit is referenced by some other > unit. This case is handled in follow_die_sig_1, calling another new > function fill_in_sig_entry_from_dwo_file. In this case, we know > which unit is referring to the TU, so we use that unit's dwo file to > fill in the details. As explained in the comment, this is sometimes > just an optimization, but sometimes also necessary, if the TU > does not have a hint, due to it not containing any indexed name. > > 3. Similarly, in dwarf2_base_index_functions::expand_all_symtabs, we > might have to handle foreign type units for which we don't have a > hint. I initially implemented something in two passes, to go dig in > the dwo_file structures to find those TUs, but ended up choosing to > just skip them, for the reasons explained in the comment there. > > Setting a dwarf2_per_cu's section a posteriori breaks the assumed > ordering of the dwarf2_per_bfd::all_units vector. After setting the > section, re-sort the vector. > > Add a target board to exercise this new code. This board builds with: > > - type units (-fdebug-types-section) > - split DWARF (-gsplit-dwarf) > - .debug_names index (created by GDB) > > I ran the whole testsuite with this board file and it's not perfect, but > the results are comparable to the dwarf5-fission-debug-types board, for > instance. > > There is one known failure that I am unable to get to the bottom of. It > seems orthogonal to my change though, more like an indexer or symbol > reader issue. There are maybe more of this kind, but this is one > example: > > FAIL: gdb.ada/tick_length_array_enum_idx.exp: ptype variable_table'length (GDB internal error) > > /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:1839: internal-error: search_one: Assertion `symtab != nullptr' failed. > > The problem appears to be that a cooked index lookup for symbol > variable_table says that a given TU should contain a match. But then > trying to expand the TU makes dw2_instantiate_symtab yield a nullptr > compunit_symtab, I think because the symbol reader found nothing > interesting symbol-wise. And then the assert in search_one triggers. > > The issue seems sensitive to some aspects of the environment (gnat > version?). I am able to reproduce the issue on Arch Linux (gnat 15) > with: > > $ make check TESTS="gdb.ada/tick_length_array_enum_idx.exp" RUNTESTFLAGS="--target_board=dwarf5-fission-debug-types-debug-names" > > But it doesn't reproduce on Debian 13 (gnat 14), Ubuntu 24.04 (gnat > 13) or Fedora Rawhide (gnat 16). > > Change-Id: I0d4ccc1cbbce3a337794341744d24091e8549d7f > Approved-By: Tom Tromey > --- > gdb/dwarf2/read-debug-names.c | 212 ++++++++++++++---- > gdb/dwarf2/read.c | 167 +++++++++++++- > gdb/dwarf2/read.h | 37 ++- > gdb/dwarf2/types.h | 2 + > ...dwarf5-fission-debug-types-debug-names.exp | 29 +++ > 5 files changed, 392 insertions(+), 55 deletions(-) > create mode 100644 gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp > > diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c > index 487e2ea87edb..a062c1ad2562 100644 > --- a/gdb/dwarf2/read-debug-names.c > +++ b/gdb/dwarf2/read-debug-names.c > @@ -88,9 +88,13 @@ struct mapped_debug_names_reader > > uint8_t offset_size = 0; > uint32_t cu_count = 0; > - uint32_t tu_count = 0, bucket_count = 0, name_count = 0; > + uint32_t tu_count = 0; > + uint32_t foreign_tu_count = 0; > + uint32_t bucket_count = 0; > + uint32_t name_count = 0; > const gdb_byte *cu_table_reordered = nullptr; > const gdb_byte *tu_table_reordered = nullptr; > + const gdb_byte *foreign_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; > @@ -122,7 +126,11 @@ struct mapped_debug_names_reader > > /* List of local TUs in the same order as found in the index (DWARF 5 section > 6.1.1.4.3). */ > - std::vector type_units; > + std::vector type_units; > + > + /* List of foreign TUs in the same order as found in the index (DWARF 5 > + section 6.1.1.4.4). */ > + std::vector foreign_type_units; > > /* Even though the scanning of .debug_names and creation of the > cooked index entries is done serially, we create multiple shards > @@ -217,7 +225,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name, > cooked_index_flag flags = 0; > sect_offset die_offset {}; > enum language lang = language_unknown; > - dwarf2_per_cu *per_cu = nullptr; > + dwarf2_per_cu *comp_unit = nullptr; > + signatured_type *type_unit = nullptr; > + signatured_type *foreign_type_unit = nullptr; > + > for (const auto &attr : indexval.attr_vec) > { > ULONGEST ull; > @@ -282,7 +293,6 @@ mapped_debug_names_reader::scan_one_entry (const char *name, > { > case DW_IDX_compile_unit: > { > - /* Don't crash on bad data. */ > if (ull >= this->comp_units.size ()) > { > complain_about_index_entry > @@ -292,13 +302,17 @@ mapped_debug_names_reader::scan_one_entry (const char *name, > continue; > } > > - per_cu = this->comp_units[ull]; > + comp_unit = this->comp_units[ull]; > break; > } > case DW_IDX_type_unit: > { > - /* Don't crash on bad data. */ > - if (ull >= this->type_units.size ()) > + if (ull < this->type_units.size ()) > + type_unit = this->type_units[ull]; > + else if (auto foreign_idx = ull - this->type_units.size (); > + foreign_idx < this->foreign_type_units.size ()) > + foreign_type_unit = this->foreign_type_units[foreign_idx]; > + else > { > complain_about_index_entry > (abfd, name, offset_in_entry_pool, > @@ -307,15 +321,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name, > continue; > } > > - per_cu = this->type_units[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 == nullptr) > - per_cu = this->comp_units[0]; > break; > case DW_IDX_parent: > parent = ull; > @@ -339,27 +348,117 @@ mapped_debug_names_reader::scan_one_entry (const char *name, > } > } > > - /* Skip if we couldn't find a valid CU/TU index. */ > - if (per_cu != nullptr) > + /* From DWARF 5 section 6.1.1.3 ("Per-CU versus Per-Module Indexes"): > + > + In a per-CU index, the CU list may have only a single entry, and index > + entries may omit the CU attribute. */ > + if (comp_unit == nullptr > + && type_unit == nullptr > + && foreign_type_unit == nullptr) > { > - dwarf_read_debug_printf_v > - (" -> die_offset %s, per_cu offset %s", > - sect_offset_str (die_offset), > - sect_offset_str (per_cu->sect_off ())); > + if (this->comp_units.size () != 1) > + { > + complain_about_index_entry > + (abfd, name, offset_in_entry_pool, > + _(".debug_names entry is missing CU index, but index has %zu CUs " > + "(expecting 1)"), > + this->comp_units.size ()); > + return entry; > + } > > - *result > - = indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag, > - flags, lang, name, nullptr, per_cu); > - > - ++next_shard; > - if (next_shard == indices.size ()) > - next_shard = 0; > - > - entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); > + comp_unit = this->comp_units[0]; > } > - else > - dwarf_read_debug_printf_v (" -> no valid CU/TU, skipping"); > > + /* Figure out which unit this entry refers to. */ > + dwarf2_per_cu *actual_unit; > + > + if (foreign_type_unit != nullptr) > + { > + if (type_unit != nullptr) > + { > + complain_about_index_entry > + (abfd, name, offset_in_entry_pool, > + _(".debug_names entry refers to two TUs")); > + return entry; > + } > + > + /* Implement this part of DWARF 5 section 6.1.1.2 ("Structure of the Name > + Index"): > + > + When an index entry refers to a foreign type unit, it may have > + attributes for both CU and (foreign) TU. For such entries, the CU > + attribute gives the consumer a reference to the CU that may be used > + to locate a split DWARF object file that contains the type unit. */ > + if (comp_unit == nullptr) > + { > + /* If a foreign type unit does not say which compile unit to follow > + to find it, it's pretty much useless. */ > + complain_about_index_entry > + (abfd, name, offset_in_entry_pool, > + _(".debug_names entry refers to foreign type unit but " > + "no compile unit")); > + return entry; > + } > + > + actual_unit = foreign_type_unit; > + > + /* .debug_names attaches one "hint" CU per index entry, insinuating that > + for some names / index entries, it is important which .dwo we choose to > + locate the TU. Even if it's important, the DWARF reader is not > + currently able to load multiple versions of the same TU. So just > + record one hint CU for each foreign TU. */ > + if (foreign_type_unit->hint_per_cu == nullptr) > + { > + foreign_type_unit->hint_per_cu = comp_unit; > + dwarf_read_debug_printf_v > + (" hint CU for foreign type unit with signature %s: " > + "unit offset %s", > + hex_string (foreign_type_unit->signature), > + sect_offset_str (comp_unit->sect_off ())); > + } > + } > + else if (type_unit != nullptr) > + { > + if (comp_unit != nullptr) > + { > + complain_about_index_entry > + (abfd, name, offset_in_entry_pool, > + _(".debug_names entry refers to both a CU and a TU")); > + return entry; > + } > + > + actual_unit = type_unit; > + } > + else if (comp_unit != nullptr) > + actual_unit = comp_unit; > + else > + { > + complain_about_index_entry > + (abfd, name, offset_in_entry_pool, > + _(".debug_names entry is missing a CU or TU reference")); > + return entry; > + } > + > + gdb_assert (actual_unit != nullptr); > + > + if (foreign_type_unit != nullptr) > + dwarf_read_debug_printf_v (" -> signature %s, DIE offset: %s", > + hex_string (foreign_type_unit->signature), > + sect_offset_str (die_offset)); > + else > + dwarf_read_debug_printf_v (" -> unit offset %s, DIE offset %s", > + sect_offset_str (actual_unit->sect_off ()), > + sect_offset_str (die_offset)); > + > + *result = indices[next_shard].add (die_offset, > + (dwarf_tag) indexval.dwarf_tag, flags, > + lang, name, nullptr, actual_unit); > + > + ++next_shard; > + if (next_shard == indices.size ()) > + next_shard = 0; > + > + entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); > return entry; > } > > @@ -553,7 +652,7 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile, > return false; > } > > - map.type_units.emplace_back (per_cu); > + map.type_units.emplace_back (per_cu->as_signatured_type ()); > } > > return true; > @@ -665,22 +764,12 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, > > /* foreign_type_unit_count - The number of TUs in the foreign TU > list. */ > - uint32_t foreign_tu_count = read_4_bytes (abfd, addr); > + map.foreign_tu_count = read_4_bytes (abfd, addr); > addr += 4; > > dwarf_read_debug_printf ("cu_count: %u, tu_count: %u, " > "foreign_tu_count: %u", > - map.cu_count, map.tu_count, foreign_tu_count); > - > - if (foreign_tu_count != 0) > - { > - warning (_("Section .debug_names in %ps has unsupported %lu foreign TUs, " > - "ignoring .debug_names."), > - styled_string (file_name_style.style (), > - filename), > - static_cast (foreign_tu_count)); > - return false; > - } > + map.cu_count, map.tu_count, map.foreign_tu_count); > > /* bucket_count - The number of hash buckets in the hash lookup > table. */ > @@ -761,6 +850,10 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, > map.tu_table_reordered = addr; > addr += map.tu_count * map.offset_size; > > + /* List of foreign TUs */ > + map.foreign_tu_table_reordered = addr; > + addr += map.foreign_tu_count * sizeof (std::uint64_t); > + > /* Hash Lookup Table */ > map.bucket_table_reordered = reinterpret_cast (addr); > addr += map.bucket_count * 4; > @@ -918,6 +1011,35 @@ build_and_check_cu_lists_from_debug_names (dwarf2_per_bfd *per_bfd, > return build_and_check_cu_list_from_debug_names (per_bfd, dwz_map, dwz->info); > } > > +/* Create signatured_type objects for the foreign TU list in MAP. */ > + > +static void > +create_foreign_type_units_from_debug_names (dwarf2_per_bfd *per_bfd, > + mapped_debug_names_reader &map) > +{ > + for (uint32_t i = 0; i < map.foreign_tu_count; ++i) > + { > + /* The list of foreign TUs is a list of 64-bit (DW_FORM_ref_sig8) type > + signatures representing type units placed in .dwo files. All we know > + about them for now is the signature. */ > + const gdb_byte *ptr > + = map.foreign_tu_table_reordered + i * sizeof (std::uint64_t); > + std::uint64_t sig > + = extract_unsigned_integer (ptr, sizeof (std::uint64_t), > + map.dwarf5_byte_order); > + > + dwarf_read_debug_printf_v (" Foreign TU %u (%u): signature %s", i, > + i + map.tu_count, hex_string (sig)); > + > + signatured_type_up sig_type > + = per_bfd->allocate_signatured_type (sig); > + > + map.foreign_type_units.emplace_back (sig_type.get ()); > + per_bfd->signatured_types.emplace (sig_type.get ()); > + per_bfd->all_units.emplace_back (sig_type.release ()); > + } > +} > + > /* See read-debug-names.h. */ > > bool > @@ -975,6 +1097,12 @@ dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) > return false; > } > > + create_foreign_type_units_from_debug_names (per_objfile->per_bfd, map); > + > + /* create_foreign_type_units_from_debug_names may add more entries to the > + ALL_UNITS vector, so it must be called before finalize_all_units. */ > + finalize_all_units (per_objfile->per_bfd); > + > per_bfd->debug_aranges.read (per_objfile->objfile); > > /* There is a single address map for the whole index (coming from > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c > index d061bc22b12d..4d8923d3ca7a 100644 > --- a/gdb/dwarf2/read.c > +++ b/gdb/dwarf2/read.c > @@ -695,6 +695,13 @@ static struct die_info *follow_die_ref (struct die_info *, > const struct attribute *, > struct dwarf2_cu **); > > +static void fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile, > + signatured_type *sig_entry, > + dwo_unit *dwo_entry); > + > +static void fill_in_sig_entry_from_per_cu_hint > + (dwarf2_per_objfile &per_objfile, signatured_type &sig_type); > + > static struct die_info *follow_die_sig (struct die_info *, > const struct attribute *, > struct dwarf2_cu **); > @@ -1415,6 +1422,7 @@ dwarf2_per_bfd::allocate_per_cu (dwarf2_section_info *section, > sect_offset sect_off, unsigned int length, > bool is_dwz) > { > + gdb_assert (section != nullptr); > dwarf2_per_cu_up result (new dwarf2_per_cu (this, section, sect_off, > length, is_dwz)); > result->index = all_units.size (); > @@ -1431,6 +1439,7 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section, > bool is_dwz, > ULONGEST signature) > { > + gdb_assert (section != nullptr); > auto result > = std::make_unique (this, section, sect_off, length, > is_dwz, signature); > @@ -1439,6 +1448,20 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section, > return result; > } > > +/* See read.h. */ > + > +signatured_type_up > +dwarf2_per_bfd::allocate_signatured_type (ULONGEST signature) > +{ > + auto result > + = std::make_unique (this, nullptr, > + invalid_sect_offset, > + 0, false, signature); > + result->index = all_units.size (); > + this->num_type_units++; > + return result; > +} > + > /* Subroutine of dw2_get_file_names_reader to simplify it. > Return the file name for the given file_entry. > CU_INFO describes the CU's DW_AT_name and DW_AT_comp_dir. > @@ -1722,6 +1745,20 @@ dwarf2_base_index_functions::expand_all_symtabs (struct objfile *objfile) > > for (dwarf2_per_cu *per_cu : all_units_range (per_objfile->per_bfd)) > { > + /* If a .debug_names index contains a foreign TU but no index entry > + references it, the TU won't have a hint CU. This is a problem, because > + we won't be able to locate it. Skip them, for the following reasons: > + > + - If they don't contain anything worthy of a named index entry, they > + are unlikely to contain anything interesting, symbol-wise. > + - They are likely to be referred to by some other unit (otherwise, > + why does it exist?), so will get expanded anyway. */ > + if (signatured_type *sig_type = per_cu->as_signatured_type (); > + (sig_type != nullptr > + && sig_type->section () == nullptr > + && sig_type->hint_per_cu == nullptr)) > + continue; > + > /* We don't want to directly expand a partial CU, because if we > read it with the wrong language, then assertion failures can > be triggered later on. See PR symtab/23010. So, tell > @@ -2223,8 +2260,7 @@ add_type_unit (dwarf2_per_bfd *per_bfd, dwarf2_section_info *section, > return emplace_ret.first; > } > > -/* Subroutine of lookup_dwo_signatured_type and lookup_dwp_signatured_type. > - Fill in SIG_ENTRY with DWO_ENTRY. */ > +/* Fill in the missing details in SIG_ENTRY from DWO_ENTRY. */ > > static void > fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile, > @@ -2680,6 +2716,10 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, > { > struct objfile *objfile = per_objfile.objfile; > struct dwarf2_section_info *section = this_cu.section (); > + > + /* Any foreign TU must have been located before getting here. */ > + gdb_assert (section != nullptr); > + > bfd *abfd = section->get_bfd_owner (); > const gdb_byte *begin_info_ptr; > struct dwarf2_section_info *abbrev_section; > @@ -3278,6 +3318,7 @@ cooked_index_worker_debug_info::do_reading () > dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; > > create_all_units (m_per_objfile); > + finalize_all_units (m_per_objfile->per_bfd); > process_type_units (m_per_objfile, &m_index_storage); > > if (!per_bfd->debug_aranges.empty ()) > @@ -3358,6 +3399,19 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, > > /* See read.h. */ > > +void > +dwarf2_per_bfd::sort_all_units () > +{ > + std::sort (this->all_units.begin (), this->all_units.end (), > + [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b) > + { > + return all_units_less_than (*a, { b->section (), > + b->sect_off () }); > + }); > +} > + > +/* See read.h. */ > + > void > finalize_all_units (dwarf2_per_bfd *per_bfd) > { > @@ -3365,14 +3419,7 @@ finalize_all_units (dwarf2_per_bfd *per_bfd) > gdb_assert (per_bfd->all_units.size () > == per_bfd->num_comp_units + per_bfd->num_type_units); > > - /* Ensure that the all_units vector is in the expected order for > - dwarf2_find_containing_unit to be able to perform a binary search. */ > - std::sort (per_bfd->all_units.begin (), per_bfd->all_units.end (), > - [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b) > - { > - return all_units_less_than (*a, { b->section (), > - b->sect_off () }); > - }); > + per_bfd->sort_all_units (); > } > > /* See read.h. */ > @@ -3410,7 +3457,6 @@ create_all_units (dwarf2_per_objfile *per_objfile) > > per_objfile->per_bfd->signatured_types = std::move (sig_types); > > - finalize_all_units (per_objfile->per_bfd); > remove_all_units.disable (); > } > > @@ -17104,6 +17150,77 @@ dwarf2_get_die_type (cu_offset die_offset, dwarf2_per_cu *per_cu, > return get_die_type_at_offset (die_offset_sect, per_cu, per_objfile); > } > > +/* Fill in the missing details in SIG_TYPE from DWO_FILE. > + > + Error out if there isn't a type unit with the appropriate signature in > + DWO_FILE. */ > + > +static void > +fill_in_sig_entry_from_dwo_file (dwarf2_per_objfile &per_objfile, > + signatured_type &sig_type, dwo_file &dwo_file) > +{ > + gdb_assert (sig_type.section () == nullptr); > + > + dwo_unit *dwo_tu = dwo_file.find_tu (sig_type.signature); > + if (dwo_tu == nullptr) > + error (_(DWARF_ERROR_PREFIX > + "Unable to locate type unit with signature %s in DWO file %s " > + "[in module %s]"), > + hex_string (sig_type.signature), dwo_file.dwo_name.c_str (), > + objfile_name (per_objfile.objfile)); > + > + fill_in_sig_entry_from_dwo_entry (&per_objfile, &sig_type, dwo_tu); > + sig_type.set_section (dwo_tu->section); > + sig_type.set_sect_off (dwo_tu->sect_off); > + sig_type.set_length (dwo_tu->length); > + > + /* Setting SIG_TYPE's section invalidates the ALL_UNITS vector order, > + re-sort it. */ > + per_objfile.per_bfd->sort_all_units (); > +} > + > +/* Fill in the missing details in SIG_TYPE from its hint CU. > + > + Error out if we're unable to locate the .dwo file using the hint CU. */ > + > +static void > +fill_in_sig_entry_from_per_cu_hint (dwarf2_per_objfile &per_objfile, > + signatured_type &sig_type) > +{ > + gdb_assert (sig_type.section () == nullptr); > + gdb_assert (sig_type.hint_per_cu != nullptr); > + > + dwarf2_per_cu *hint_per_cu = sig_type.hint_per_cu; > + dwo_unit *dwo_unit; > + > + /* If a dwarf2_cu already exists for HINT_PER_CU, no need to build another > + one. */ > + if (dwarf2_cu *hint_cu = per_objfile.get_cu (hint_per_cu); > + hint_cu != nullptr) > + dwo_unit = hint_cu->dwo_unit; > + else > + { > + /* Constructing this cutu_reader will look for the .dwo file, creating > + the dwo_file if necessary. */ > + abbrev_table_cache abbrev_table_cache; > + cutu_reader reader (*hint_per_cu, per_objfile, nullptr, true, > + std::nullopt, abbrev_table_cache); > + > + /* dwo_unit is owned by the per_bfd, which outlives the reader. */ > + dwo_unit = reader.cu ()->dwo_unit; > + } > + > + if (dwo_unit == nullptr) > + error (_(DWARF_ERROR_PREFIX > + "Hint compilation unit for foreign type unit with signature %s is " > + "not in a DWO file [in module %s]"), > + hex_string (sig_type.signature), > + objfile_name (per_objfile.objfile)); > + > + fill_in_sig_entry_from_dwo_file (per_objfile, sig_type, > + *dwo_unit->dwo_file); > +} > + > /* Follow type unit SIG_TYPE referenced by SRC_DIE. > On entry *REF_CU is the CU of SRC_DIE. > On exit *REF_CU is the CU of the result. > @@ -17119,6 +17236,28 @@ follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type, > we can get here for DW_AT_imported_declaration where we need > the DIE not the type. */ > > + /* If SIG_TYPE's section is not set, it means it's a .debug_names foreign > + type unit for which we don't know the containing file or section yet. > + Use the referencing CU as the hint: we know there must exist a TU with > + the correct signature in its .dwo file, and that .dwo is already open, > + might as well use it. > + > + But this is not only an optimization. If a .debug_names foreign unit > + does not have any index entry referencing it, then we don't have any > + "hint CU" for it. The only hint we have is the referencing CU. */ > + if (sig_type->section () == nullptr) > + { > + if ((*ref_cu)->dwo_unit == nullptr) > + error (_(DWARF_ERROR_PREFIX > + "Unit referencing foreign type unit with signature %s is " > + "not from a DWO file [in module %s]"), > + hex_string (sig_type->signature), > + objfile_name (per_objfile->objfile)); > + > + fill_in_sig_entry_from_dwo_file (*per_objfile, *sig_type, > + *(*ref_cu)->dwo_unit->dwo_file); > + } > + > dwarf2_cu *sig_cu = ensure_loaded_type_unit (sig_type, per_objfile); > > if (sig_cu == nullptr) > @@ -17299,6 +17438,12 @@ load_full_type_unit (signatured_type *sig_type, > gdb_assert (sig_type->is_debug_types ()); > gdb_assert (per_objfile->get_cu (sig_type) == nullptr); > > + /* If the section is not set, this is a .debug_names foreign type unit for > + which we don't know the containing file nor section yet. For these, we > + must have a "hint" CU to follow to find the file and section. */ > + if (sig_type->section () == nullptr) > + fill_in_sig_entry_from_per_cu_hint (*per_objfile, *sig_type); > + > abbrev_table_cache abbrev_table_cache; > cutu_reader reader (*sig_type, *per_objfile, nullptr, false, > std::nullopt, abbrev_table_cache); > diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h > index a0811fe0d377..12dc6224d8c0 100644 > --- a/gdb/dwarf2/read.h > +++ b/gdb/dwarf2/read.h > @@ -110,7 +110,6 @@ struct dwarf2_per_cu > m_per_bfd (per_bfd) > { > gdb_assert (per_bfd != nullptr); > - gdb_assert (section != nullptr); > } > > private: > @@ -285,9 +284,24 @@ struct dwarf2_per_cu > dwarf2_section_info *section () const > { return m_section; } > > + /* Set the section of this unit. */ > + void set_section (dwarf2_section_info *section) > + { > + gdb_assert (section != nullptr); > + gdb_assert (m_section == nullptr); > + m_section = section; > + } > + > sect_offset sect_off () const > { return m_sect_off; } > > + /* Set the section offset of this unit. */ > + void set_sect_off (sect_offset sect_off) > + { > + gdb_assert (m_sect_off == invalid_sect_offset); > + m_sect_off = sect_off; > + } > + > bool is_dwz () const > { return m_is_dwz; } > > @@ -482,6 +496,12 @@ struct signatured_type : public dwarf2_per_cu > /* Containing DWO unit. > This field is valid iff per_cu.reading_dwo_directly. */ > struct dwo_unit *dwo_unit = nullptr; > + > + /* When using a .debug_names index, the section is not initially known for > + foreign type units (aka skeletonless type units). This is a reference to > + a CU that can be used to locate the .dwo file and section containing the > + type unit. */ > + dwarf2_per_cu *hint_per_cu = nullptr; > }; > > using signatured_type_up = std::unique_ptr; > @@ -576,6 +596,10 @@ struct dwarf2_per_bfd > return this->all_units[index].get (); > } > > + /* Ensure that the all_units vector is in the expected order for > + dwarf2_find_containing_unit to be able to perform a binary search. */ > + void sort_all_units (); > + > /* Return the separate '.dwz' debug file. If there is no > .gnu_debugaltlink or .debug_sup section in the file, then the > result depends on REQUIRE: if REQUIRE is true, error out; if > @@ -610,6 +634,12 @@ struct dwarf2_per_bfd > bool is_dwz, > ULONGEST signature); > > + /* A convenience function to allocate a signatured_type. The > + returned object has its "index" field set properly. > + > + This one is used when only the signature is known at creation time. */ > + signatured_type_up allocate_signatured_type (ULONGEST signature); > + > /* Map all the DWARF section data needed when scanning > .debug_info. */ > void map_info_sections (struct objfile *objfile); > @@ -1334,7 +1364,10 @@ extern const char *read_indirect_string_at_offset > > extern void finalize_all_units (dwarf2_per_bfd *per_bfd); > > -/* Create a list of all compilation units in OBJFILE. */ > +/* Create a list of all compilation units in OBJFILE. > + > + After it is done creating all units, the caller is responsible for calling > + finalize_all_units. */ > > extern void create_all_units (dwarf2_per_objfile *per_objfile); > > diff --git a/gdb/dwarf2/types.h b/gdb/dwarf2/types.h > index 169b0fd08678..cb8ce33940ca 100644 > --- a/gdb/dwarf2/types.h > +++ b/gdb/dwarf2/types.h > @@ -31,6 +31,8 @@ DEFINE_OFFSET_TYPE (cu_offset, unsigned int); > section. */ > DEFINE_OFFSET_TYPE (sect_offset, uint64_t); > > +constexpr auto invalid_sect_offset = static_cast (-1); > + > static inline const char * > sect_offset_str (sect_offset offset) > { > diff --git a/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp > new file mode 100644 > index 000000000000..be8bebed1a43 > --- /dev/null > +++ b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp > @@ -0,0 +1,29 @@ > +# Copyright 2026 Free Software Foundation, Inc. > + > +# 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 . > + > +# A board that compiles with DWARF 5, split DWARF (fission) and type units, > +# and generates a .debug_names index using GDB. > +# > +# Example usage: > +# bash$ make check \ > +# RUNTESTFLAGS='--target_board=dwarf5-fission-debug-types-debug-names' > + > +set CC_WITH_TWEAKS_FLAGS "-n" > +load_board_description "cc-with-tweaks" > + > +set_board_info debug_flags \ > + [join { "-gdwarf-5" \ > + "-gsplit-dwarf" \ > + "-fdebug-types-section" }] -- Cheers, Guinevere Larsen It/she