From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id gWugLfeQuGmykywAWB0awg (envelope-from ) for ; Mon, 16 Mar 2026 19:23:35 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=ZKE/SfsO; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id B51591E0BC; Mon, 16 Mar 2026 19:23:35 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id AF0DD1E08C for ; Mon, 16 Mar 2026 19:23:33 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 23CE04BAE7D7 for ; Mon, 16 Mar 2026 23:23:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 23CE04BAE7D7 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=ZKE/SfsO Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 455AA4BA543C for ; Mon, 16 Mar 2026 23:23:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 455AA4BA543C Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=polymtl.ca Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=polymtl.ca ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 455AA4BA543C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=132.207.4.11 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703380; cv=none; b=xrq84tpgyFSOM2EZBHJk44XNXwDAYzvDIVjiyXVFqcO8GLczGPy3UucYBz/wGhRLCfhcxyAmB0zSgMFvmCzYK3zhXzK9x3Uep+cahc7y843N3XtmtHM1ikLsA0pHhX9MOGwIt6ZCZf2XduqLIXHjJBOuBqQ4aMFIhSIxmT/bJB8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703380; c=relaxed/simple; bh=MUucq1kgrRpKpBp31l2S+RRbezVm/3cF0eKKB3MMlKc=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=CL0yNDI8plJ90kacQli7vlpV6VpYMzBwtK+sqrPCpMkZIB8twAMMYlYPmoi0EXUxSyUpR/lvYfe6tvWU837z/WqtMG5iQ2e62SSfM9Cd/+uFLb9XdV4ZECuw1AO8ECUskT7KFTBSBw7LLG42BDNqfCE5XHsBuEmmGnxJ6DMaNXY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 455AA4BA543C Received: from simark.ca (simark.ca [158.69.221.121]) (authenticated bits=0) by smtp.polymtl.ca (8.14.7/8.14.7) with ESMTP id 62GNMsW4238838 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:22:58 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNMsW4238838 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703379; bh=QZ2YeL5Oqmkcq9kcPya1q9bpDl7L6HH8ieA26oNaGPQ=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=ZKE/SfsODArwjizG/quJUurpDXyfku8OTTd0ncRV46YTP4S67YZ3NkrAfCwdTWguw urVlgEimAy3BWDIngTG7TI7+xCS3BJcRYGoVU35GgeAyuWqR+HBkGZjy89cv5Xi4Rs sot7EXIpQ2l+ruhl/24yEq/urOHf5NlsLXyn1wElFdS3Hpp12hM9ItydJgdKRVDVpN +cdMVbLwozFyut1znPrWR7iYVLr/+nypN4HZC38DZAq7vc0qQbbIoJAeahL3MATsC4 IX4liPF/czlP/BWu+pgLGDJ8JwT4HB0KPJhn+7RpAMdlvRCNQwrq53Qh2C8pI6E7B0 XGNCzIHBfoqyQ== Received: by simark.ca (Postfix) id D43F61E08C; Mon, 16 Mar 2026 19:22:53 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 8/8] gdb/dwarf: read foreign type units Date: Mon, 16 Mar 2026 19:19:26 -0400 Message-ID: <20260316232042.368080-9-simon.marchi@polymtl.ca> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260316232042.368080-1-simon.marchi@polymtl.ca> References: <20260316232042.368080-1-simon.marchi@polymtl.ca> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:22:54 +0000 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Simon Marchi 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 --- gdb/dwarf2/read-debug-names.c | 206 ++++++++++++++---- 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, 389 insertions(+), 52 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); + comp_unit = this->comp_units[0]; + } + + /* 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; + } - ++next_shard; - if (next_shard == indices.size ()) - next_shard = 0; + /* 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; + } - entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); + actual_unit = type_unit; } + else if (comp_unit != nullptr) + actual_unit = comp_unit; else - dwarf_read_debug_printf_v (" -> no valid CU/TU, skipping"); + { + 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 c58cc1731f47..39909d103cc3 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -745,6 +745,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 **); @@ -1457,6 +1464,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 (); @@ -1473,6 +1481,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); @@ -1481,6 +1490,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. @@ -1764,6 +1787,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 @@ -2266,8 +2303,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, @@ -2723,6 +2759,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; @@ -3321,6 +3361,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 ()) @@ -3402,15 +3443,9 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, /* See read.h. */ void -finalize_all_units (dwarf2_per_bfd *per_bfd) +dwarf2_per_bfd::sort_all_units () { - /* Sanity check. */ - 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 (), + 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 (), @@ -3420,6 +3455,18 @@ finalize_all_units (dwarf2_per_bfd *per_bfd) /* See read.h. */ +void +finalize_all_units (dwarf2_per_bfd *per_bfd) +{ + /* Sanity check. */ + gdb_assert (per_bfd->all_units.size () + == per_bfd->num_comp_units + per_bfd->num_type_units); + + per_bfd->sort_all_units (); +} + +/* See read.h. */ + void create_all_units (dwarf2_per_objfile *per_objfile) { @@ -3453,7 +3500,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 (); } @@ -17113,6 +17159,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. @@ -17128,6 +17245,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) @@ -17308,6 +17447,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 e4ed84b08ad9..845f50e938d3 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -108,7 +108,6 @@ struct dwarf2_per_cu m_per_bfd (per_bfd) { gdb_assert (per_bfd != nullptr); - gdb_assert (section != nullptr); } private: @@ -267,9 +266,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; } @@ -441,6 +455,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; @@ -711,6 +731,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 @@ -745,6 +769,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); @@ -1468,7 +1498,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" }] -- 2.53.0