From patchwork Mon Mar 16 23:19:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131835 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id C0C504BA23E8 for ; Mon, 16 Mar 2026 23:22:02 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C0C504BA23E8 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=RRBrhydU X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 6BC704B1A36F for ; Mon, 16 Mar 2026 23:20:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6BC704B1A36F 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 6BC704B1A36F 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=1773703257; cv=none; b=CZylDm2QkcypuUlvwbwcQGuAeyXcVYTz4GY90S/1BQoFUls7dmMI4S627tOZtUK3RuGZj2vGZ6Y9C9dmL10KVapClN5cNBIV/sXslBtKo2j2kJXJWLySvQEUBoWxCNG9iaWorRkVUyeuzAkRShqB85Zjw76Q53VbuWjw77buTJY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703257; c=relaxed/simple; bh=izzm5yZF0HCfkyOuWliJetVnr39maDafVWpUVjSkTzI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=eMZ4zhD/TDrG+71xvufXecn/1O8y7hh9hA+nMrbYO/6vF4gQfd0nXs9Fo7C133UDCIcHEB4y7kqOfh6nwZudvNjrh0fzCtXP+TQbcST+pIBGCy+2NGh6qzYg7dOpEJKdbaCgh4P3yb3iUAIw+o139E1AHlmGEqAwzAedQfS7144= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6BC704B1A36F 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 62GNKpQh238295 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:20:55 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNKpQh238295 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703256; bh=gNCXOq835GsRSOJ+C/UCRiOWwEqbbJQlfOGcpot3btU=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=RRBrhydUkxFJau0Yh9Ui9R33YcM0sqPZRfTRt3qVv+mpR6p3iP7TebRgLCMRCX5j3 BSkl/Z8bq0Uf1NGtSBlMf1HB9SlcNhe5/r2YV8F+x3TzjH0v/AxOVBnAnloBvnLasC xrG11spi6AbX0I6QZOOAco7B2cLdwKDkVwH73heFopeFY1totorBjbQ1clNYNqgseo 5/au/v1Sf1D0mX6aZLybynKooe/VVlroCA2CDa1YX/b5TvrwPIhV1R2mOvV9JrnBth eC29qWxWUpNRK7ApNaCT1EjpblCG7hAxw+owKirCQAtY2ghu3NPgsdrJZGIs/blCZV 5P6VFCrz6K5wg== Received: by simark.ca (Postfix) id 70B231E0A2; Mon, 16 Mar 2026 19:20:49 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 1/8] gdb/dwarf: refuse to produce .gdb_index when skeletonless type units are present Date: Mon, 16 Mar 2026 19:19:19 -0400 Message-ID: <20260316232042.368080-2-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:20:51 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi Running test gdb.dwarf2/fission-with-type-unit.exp with the cc-with-gdb-index target board fails with: (gdb) maint expand-symtabs /home/simark/src/binutils-gdb/gdb/dwarf2/read.c:3064: internal-error: cutu_reader: Assertion `sig_type->signature == cu->header.signature' failed. A problem internal to GDB has been detected, further debugging may prove unreliable. ----- Backtrace ----- FAIL: gdb.dwarf2/fission-with-type-unit.exp: maint expand-symtabs (GDB internal error) This is a consequence of .gdb_index not supporting skeletonless type units in .dwo files. That is, type units in .dwo files that don't have a corresponding skeleton (or stub) in the main file. For context: in DWARF 4, gcc 4.x used to create skeletons for type units in .dwo files, but subsequent versions don't. DWARF 5 doesn't have support for type unit skeletons at all. So skeletons for type units are mostly a historical curiosity at this point, the norm is to not have them. Here's what leads up to the crash. First, this is what is in the main file's .debug_info section (the first and last CUs are dummy CUs added by the testsuite): Compilation Unit @ offset 0: Length: 0x8 (32-bit) Version: 4 Abbrev Offset: 0 Pointer Size: 8 <0>: Abbrev Number: 1 (DW_TAG_compile_unit) Compilation Unit @ offset 0xc: Length: 0x15 (32-bit) Version: 5 Unit Type: DW_UT_skeleton (4) Abbrev Offset: 0x6 Pointer Size: 8 DWO ID: 0xf00d <0><20>: Abbrev Number: 1 (DW_TAG_compile_unit) <21> DW_AT_dwo_name : (strp) (offset: 0): fission-with-type-unit-dw.dwo Compilation Unit @ offset 0x25: Length: 0x8 (32-bit) Version: 4 Abbrev Offset: 0xe Pointer Size: 8 <0><30>: Abbrev Number: 1 (DW_TAG_compile_unit) And here is what is in the fission-with-type-unit-dw.dwo file (one TU and the CU): Contents of the .debug_info.dwo section: Compilation Unit @ offset 0: Length: 0x1d (32-bit) Version: 5 Unit Type: DW_UT_type (2) Abbrev Offset: 0 Pointer Size: 8 Signature: 0xcafe Type Offset: 0x19 <0><18>: Abbrev Number: 1 (DW_TAG_type_unit) <1><19>: Abbrev Number: 2 (DW_TAG_base_type) <1a> DW_AT_byte_size : (sdata) 4 <1b> DW_AT_encoding : (sdata) 5 (signed) <1c> DW_AT_name : (string) int <1><20>: Abbrev Number: 0 Contents of the .debug_info.dwo section: Compilation Unit @ offset 0: Length: 0x2d (32-bit) Version: 5 Unit Type: DW_UT_split_compile (5) Abbrev Offset: 0 Pointer Size: 8 DWO ID: 0xf00d <0><14>: Abbrev Number: 3 (DW_TAG_compile_unit) <1><15>: Abbrev Number: 4 (DW_TAG_base_type) <16> DW_AT_byte_size : (sdata) 4 <17> DW_AT_encoding : (sdata) 5 (signed) <18> DW_AT_name : (string) int <1><1c>: Abbrev Number: 5 (DW_TAG_variable) <1d> DW_AT_name : (string) global_var <28> DW_AT_type : (ref4) <0x15>, int <2c> DW_AT_location : (exprloc) 3 byte block: 8 c 9f (DW_OP_const1u: 12; DW_OP_stack_value) <1><30>: Abbrev Number: 0 After loading the above in GDB, here is what is in GDB's mind (contents of dwarf2_per_bfd::all_units): - CU at offset 0x0 of .debug_info in fission-with-type-unit -- dummy - CU at offset 0xc of .debug_info in fission-with-type-unit - CU at offset 0x25 of .debug_info in fission-with-type-unit -- dummy - TU at offset 0x0 of .debug_info.dwo in fission-with-type-unit-dw.dwo This is correct. Then, this is the generated .gdb_index: Contents of the .gdb_index section: Version 9 CU table: [ 0] 0 - 0xb -- dummy [ 1] 0xc - 0x24 [ 2] 0x25 - 0x30 -- dummy TU table: [ 0] 0 0x19 000000000000cafe Address table: Symbol table: [ 3] global_var: 1 [static, variable] [754] int: 1 [static, type] Shortcut table: Language of main: unknown: 0 Name of main: The TU table says that there exists a TU at offset 0. Unfortunately, there is no way for a reader of that index to know that this TU is really in a .dwo file, not in the main file. So when GDB loads this index back (creating dwarf2_per_bfd::all_units from .gdb_index this time, rather than walking the debug info), this is what is in its mind: - CU at offset 0x0 of .debug_info in fission-with-type-unit -- dummy - TU at offset 0x0 of .debug_info in fission-with-type-unit - CU at offset 0xc of .debug_info in fission-with-type-unit - CU at offset 0x25 of .debug_info in fission-with-type-unit -- dummy GDB now incorrectly believes there's a TU at offset 0 of .debug_info in the main file, which is wrong. When trying to expand that TU with "maint expand-symtabs", we're not really reading the TU, so we hit the assert checking that the signature in the TU header matches what was given by the index. The .debug_names format has a way to list the TUs found in the .dwo files, called the "foreign TU list" (see section 6.1.1.2 "Structure of the Name Index" of DWARF 5). That list only includes the signature of the type, and there is some capability to figure out which .dwo file contains that type unit. The .gdb_index format does not have something like that. We could try to retrofit such a feature in the .gdb_index format, but I think we prefer to put our efforts on the standard .debug_names format. To avoid producing a misleading index like shown above, I propose to make GDB refuse to produce an index if there exists a skeletonless type unit. This patch implements that by looking at section of all signatured_types. If the containing section ends with .dwo, then this is a skeletonless type unit. As a reminder: if a unit has a skeleton, the dwarf2_per_cu section will point at the skeleton section, in the main file. If the unit does not have a skeleton, the dwarf2_per_cu section will point at the section in the .dwo file. All .dwo section names end with ".dwo". Add a "endswith" utils function to help with that. With this patch, running the gdb.dwarf/fission-with-type-unit.exp leads to a compilation failure: gdb compile failed, Error while writing index for `/home/smarchi/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/.tmp/fission-with-type-unit': Found skeletonless type units, unable to produce .gdb_index. Consider using .debug_names instead. ... which makes the test "untested". Add a new test, gdb.dwarf2/gdb-index-skeletonless-tu.exp, to verify the new error path. Change-Id: I1e2e0204c9c2b48763aa99ce63521ae4a5262b22 Reviewed-By: Eli Zaretskii --- gdb/doc/gdb.texinfo | 6 + gdb/dwarf2/index-write.c | 22 ++++ .../gdb.dwarf2/gdb-index-skeletonless-tu.c | 23 ++++ .../gdb.dwarf2/gdb-index-skeletonless-tu.exp | 103 ++++++++++++++++++ gdbsupport/common-utils.h | 12 ++ 5 files changed, 166 insertions(+) create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.c create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.exp diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index ceb69669ea60..cacbdede50db 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -51095,6 +51095,12 @@ gdb-index} (@pxref{Index Files}). The index section is DWARF-specific; some knowledge of DWARF is assumed in this description. +Note that the @code{.gdb_index} format does not support describing +skeletonless type units, that is, type units in @file{.dwo} files that +don't have a corresponding skeleton in the main file. @value{GDBN} +will refuse to generate a @code{.gdb_index} index for such executables. +Consider using the @code{.debug_names} format instead. + The mapped index file format is designed to be directly @code{mmap}able on any architecture. In most cases, a datum is represented using a little-endian 32-bit integer value, called an diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index 3a70787355cc..0c8474b3e4ed 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -633,6 +633,18 @@ write_address_map (const addrmap *addrmap, data_buf &addr_vec, addrmap_index_data.previous_cu_index); } +/* Return true if TU is a foreign type unit, that is a type unit defined in a + .dwo file without a corresponding skeleton in the main file. */ + +static bool +is_foreign_tu (const signatured_type *tu) +{ + /* If a type unit has a skeleton, then `tu->section ()` will be the section + of the skeleton, in the main file. If it's foreign, it will point to the + section in the .dwo file. */ + return endswith (tu->section ()->get_name (), ".dwo"); +} + /* DWARF-5 .debug_names builder. */ class debug_names { @@ -1375,6 +1387,16 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table, cu_index_htab.reserve (per_bfd->all_units.size ()); unit_lists units = get_unit_lists (*per_bfd); + + /* .gdb_index doesn't have a way to describe skeletonless type units, the way + that DWARF 5's .debug_names does with "foreign type units". If the + executable has such skeletonless type units, refuse to produce an index, + instead of producing a bogus one. */ + for (const signatured_type *tu : units.type) + if (is_foreign_tu (tu)) + error (_("Found foreign (skeletonless) type unit, unable to produce " + ".gdb_index. Consider using .debug_names instead.")); + int counter = 0; /* Write comp units. */ diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.c b/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.c new file mode 100644 index 000000000000..c86a4322de82 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.c @@ -0,0 +1,23 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 + . */ + +int +main (int argc, char **argv) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.exp b/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.exp new file mode 100644 index 000000000000..31a45e224cad --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-skeletonless-tu.exp @@ -0,0 +1,103 @@ +# 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 . + +# Test that GDB refuses to produce a .gdb_index when skeletonless type units +# are present. A skeletonless type unit is a type unit in a .dwo file that +# doesn't have a corresponding skeleton in the main file. The .gdb_index +# format cannot represent these, so GDB must refuse to produce one rather than +# produce a bogus index. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +# Can't produce an index with readnow. +require !readnow + +standard_testfile .c -dw.S + +set asm_file [standard_output_file $srcfile2] + +Dwarf::assemble $asm_file { + # In the main file: a skeleton CU pointing to the .dwo file. + cu { + version 5 + dwo_id 0xF00D + } { + compile_unit { + DW_AT_dwo_name ${::gdb_test_file_name}-dw.dwo DW_FORM_strp + } {} + } + + # In the .dwo file: a type unit (skeletonless, no corresponding skeleton + # in main file). + tu { + fission 1 + version 5 + } 0xCAFE "the_type" { + type_unit {} { + the_type: base_type { + DW_AT_byte_size 4 DW_FORM_sdata + DW_AT_encoding @DW_ATE_signed + DW_AT_name int + } + } + } + + # In the .dwo file: the split compile unit. + cu { + fission 1 + version 5 + dwo_id 0xF00D + } { + compile_unit {} { + DW_TAG_variable { + DW_AT_name global_var + DW_AT_type 0xCAFE DW_FORM_ref_sig8 + DW_AT_location { + DW_OP_const1u 12 + DW_OP_stack_value + } SPECIAL_expr + } + } + } +} + +set obj [standard_output_file "${testfile}-dw.o"] +if {[build_executable_and_dwo_files "$testfile.exp" "${binfile}" {} \ + [list $asm_file {nodebug split-dwo} $obj] \ + [list $srcfile {nodebug}]]} { + return +} + +clean_restart ${testfile} + +# Sanity check, verify that the executable works correctly. +gdb_test "print global_var" " = 12" + +# Verify that saving a .gdb_index index fails. +set output_dir [standard_output_file ""] +gdb_test "save gdb-index ${output_dir}" \ + "Found foreign \\(skeletonless\\) type unit, unable to produce \\.gdb_index\\. Consider using \\.debug_names instead\\." \ + "save gdb-index fails" + +# Verify that saving a .debug_names index works. +gdb_test_no_output "save gdb-index -dwarf-5 ${output_dir}" \ + "save gdb-index -dwarf-5 succeeds" + +# Verify that the .debug_names file was created. +set debug_names_file "${output_dir}/${testfile}.debug_names" +gdb_assert {[file exists $debug_names_file]} ".debug_names file exists" diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h index fb4b8ea28ced..de83a715ac45 100644 --- a/gdbsupport/common-utils.h +++ b/gdbsupport/common-utils.h @@ -115,6 +115,18 @@ startswith (const char *str, const std::string_view &prefix) return strncmp (str, prefix.data (), prefix.length ()) == 0; } +/* Return true if the end of STR matches PATTERN, false otherwise. + + This can be replaced with std::string_view::ends_with when we require + C++20. */ + +static inline bool +endswith (std::string_view str, std::string_view pattern) +{ + return (str.length () >= pattern.length () + && str.substr (str.length () - pattern.length ()) == pattern); +} + /* Return true if the strings are equal. */ static inline bool From patchwork Mon Mar 16 23:19:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131836 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 3AAD44BAE7D7 for ; Mon, 16 Mar 2026 23:22:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3AAD44BAE7D7 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=iZUs4Z7N X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 5B80D4C515F8 for ; Mon, 16 Mar 2026 23:20:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5B80D4C515F8 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 5B80D4C515F8 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=1773703259; cv=none; b=GuZgy9/ubrNiGtE8u9HfoBdNyiXzDudsRa8qXlA6vdWdf02qkncn32pl5nwDJTc3OnDi4GFW3/BTJipgsUVeBOgHdRrt8AAG/qAFz6od7eopq1GOPqYzqyL1afDLRwY3ElOcZRU3Nq9bZ5WNsGCVvKbSkjYKrbPhlBUkxznGr1A= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703259; c=relaxed/simple; bh=1OCWVPQgmCGflcQdXzo8wyCV6lmP6NchhjJtzWHHKKM=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=OuM599qmGxu/70K4vtVNqvNrbmXQfjDp5nGJl/i1xFhm3zodNIHAsjEEPZXzKgGUYxmBD28Th4HOqreUdIyifVzLe26BdE7oFAVST8j2uW57fnIQfcIcGkXC+WDg6Oqs4rVzCFaggFCRLTT843HJrUsKN/5JkpL0GmHouesomU0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5B80D4C515F8 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 62GNKpT0238298 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:20:56 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNKpT0238298 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703256; bh=l/sw64pJm9KPx/yL2QFfXggPSQTvqDXobjIn9sJW43U=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=iZUs4Z7NYo+u3ER07wsY9kqQ36YosPzS/OdSZ+rivD4cPb6elatmOl91QpPa1fZub fxcGMCjSMvRjzn5f9p5Q0YBTdZf3K3UnxYFO1WBy9wvCFPT3C4u58Ozm8GG6H7Xq6y EOVCJAGNdk809m9lPY+5qGO1rWL5OI2MdZNOZCxJU9yXWTzsnp6BFK4ixBLYyI2YrT iUK9IBMnhtyDMB+4UNhhbtC21HkaHxAqrmDufA4N3jE6+wF/f01VX+Bp/Y8EdAdrzn VrmKhgOvTAPBQMhkAGSZSpj219SXv6mn0JKm9gyiyWqltqUPqT8S2T6A3pFnNcCvKF NhjhVzMeLymIw== Received: by simark.ca (Postfix) id 949DC1E0BC; Mon, 16 Mar 2026 19:20:50 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 2/8] gdb/dwarf: move dwo_unit and dwo_file to read.h Date: Mon, 16 Mar 2026 19:19:20 -0400 Message-ID: <20260316232042.368080-3-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:20:51 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi This is to allow index-write.c to see these types, in a later patch. Change-Id: Ia32e0643f95561d3a1bfb67d501c8e20f5682f0e --- gdb/dwarf2/read.c | 125 ---------------------------------------------- gdb/dwarf2/read.h | 125 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 126 deletions(-) diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 8b87d58dd9c5..c13ea6c1622f 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -266,86 +266,6 @@ struct loclists_rnglists_header unsigned int offset_entry_count; }; -/* These sections are what may appear in a (real or virtual) DWO file. */ - -struct dwo_sections -{ - struct dwarf2_section_info abbrev; - struct dwarf2_section_info line; - struct dwarf2_section_info loc; - struct dwarf2_section_info loclists; - struct dwarf2_section_info macinfo; - struct dwarf2_section_info macro; - struct dwarf2_section_info rnglists; - struct dwarf2_section_info str; - struct dwarf2_section_info str_offsets; - /* In the case of a virtual DWO file, these two are unused. */ - std::vector infos; - std::vector types; -}; - -/* CUs/TUs in DWP/DWO files. */ - -struct dwo_unit -{ - /* Backlink to the containing struct dwo_file. */ - struct dwo_file *dwo_file = nullptr; - - /* The "id" that distinguishes this CU/TU. - .debug_info calls this "dwo_id", .debug_types calls this "signature". - Since signatures came first, we stick with it for consistency. */ - ULONGEST signature = 0; - - /* The section this CU/TU lives in, in the DWO file. */ - dwarf2_section_info *section = nullptr; - - /* This is set if SECTION is owned by this dwo_unit. */ - dwarf2_section_info_up section_holder; - - /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section. */ - sect_offset sect_off {}; - unsigned int length = 0; - - /* For types, offset in the type's DIE of the type defined by this TU. */ - cu_offset type_offset_in_tu; -}; - -using dwo_unit_up = std::unique_ptr; - -/* Hash function for dwo_unit objects, based on the signature. */ - -struct dwo_unit_hash -{ - using is_transparent = void; - - std::size_t operator() (ULONGEST signature) const noexcept - { return signature; } - - std::size_t operator() (const dwo_unit_up &unit) const noexcept - { return (*this) (unit->signature); } -}; - -/* Equal function for dwo_unit objects, based on the signature. - - The signature is assumed to be unique within the DWO file. So while object - file CU dwo_id's always have the value zero, that's OK, assuming each object - file DWO file has only one CU, and that's the rule for now. */ - -struct dwo_unit_eq -{ - using is_transparent = void; - - bool operator() (ULONGEST sig, const dwo_unit_up &unit) const noexcept - { return sig == unit->signature; } - - bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept - { return (*this) (a->signature, b); } -}; - -/* Set of dwo_unit object, using their signature as identity. */ - -using dwo_unit_set = gdb::unordered_set; - /* include/dwarf2.h defines the DWP section codes. It defines a max value but it doesn't define a min value, which we use for error checking, so provide one. */ @@ -355,51 +275,6 @@ enum dwp_v2_section_ids DW_SECT_MIN = 1 }; -/* Data for one DWO file. - - This includes virtual DWO files (a virtual DWO file is a DWO file as it - appears in a DWP file). DWP files don't really have DWO files per se - - comdat folding of types "loses" the DWO file they came from, and from - a high level view DWP files appear to contain a mass of random types. - However, to maintain consistency with the non-DWP case we pretend DWP - files contain virtual DWO files, and we assign each TU with one virtual - DWO file (generally based on the line and abbrev section offsets - - a heuristic that seems to work in practice). */ - -struct dwo_file -{ - dwo_file () = default; - DISABLE_COPY_AND_ASSIGN (dwo_file); - - /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute. - For virtual DWO files the name is constructed from the section offsets - of abbrev,line,loc,str_offsets so that we combine virtual DWO files - from related CU+TUs. */ - std::string dwo_name; - - /* The DW_AT_comp_dir attribute. */ - const char *comp_dir = nullptr; - - /* The bfd, when the file is open. Otherwise this is NULL. - This is unused(NULL) for virtual DWO files where we use dwp_file.dbfd. */ - gdb_bfd_ref_ptr dbfd; - - /* The sections that make up this DWO file. - Remember that for virtual DWO files in DWP V2 or DWP V5, these are virtual - sections (for lack of a better name). */ - struct dwo_sections sections {}; - - /* The CUs in the file. - - Multiple CUs per DWO are supported as an extension to handle LLVM's Link - Time Optimization output (where multiple source files may be compiled into - a single object/dwo pair). */ - dwo_unit_set cus; - - /* Table of TUs in the file. */ - dwo_unit_set tus; -}; - /* See dwarf2/read.h. */ std::size_t diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 86f97e7ccf4a..5a46786e4f3f 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -489,7 +489,130 @@ using signatured_type_set = gdb::unordered_set; -struct dwo_file; +/* CUs/TUs in DWP/DWO files. */ + +struct dwo_unit +{ + /* Backlink to the containing struct dwo_file. */ + struct dwo_file *dwo_file = nullptr; + + /* The "id" that distinguishes this CU/TU. + .debug_info calls this "dwo_id", .debug_types calls this "signature". + Since signatures came first, we stick with it for consistency. */ + ULONGEST signature = 0; + + /* The section this CU/TU lives in, in the DWO file. */ + dwarf2_section_info *section = nullptr; + + /* This is set if SECTION is owned by this dwo_unit. */ + dwarf2_section_info_up section_holder; + + /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section. */ + sect_offset sect_off {}; + unsigned int length = 0; + + /* For types, offset in the type's DIE of the type defined by this TU. */ + cu_offset type_offset_in_tu; +}; + +using dwo_unit_up = std::unique_ptr; + +/* These sections are what may appear in a (real or virtual) DWO file. */ + +struct dwo_sections +{ + struct dwarf2_section_info abbrev; + struct dwarf2_section_info line; + struct dwarf2_section_info loc; + struct dwarf2_section_info loclists; + struct dwarf2_section_info macinfo; + struct dwarf2_section_info macro; + struct dwarf2_section_info rnglists; + struct dwarf2_section_info str; + struct dwarf2_section_info str_offsets; + /* In the case of a virtual DWO file, these two are unused. */ + std::vector infos; + std::vector types; +}; + +/* Hash function for dwo_unit objects, based on the signature. */ + +struct dwo_unit_hash +{ + using is_transparent = void; + + std::size_t operator() (ULONGEST signature) const noexcept + { return signature; } + + std::size_t operator() (const dwo_unit_up &unit) const noexcept + { return (*this) (unit->signature); } +}; + +/* Equal function for dwo_unit objects, based on the signature. + + The signature is assumed to be unique within the DWO file. So while object + file CU dwo_id's always have the value zero, that's OK, assuming each object + file DWO file has only one CU, and that's the rule for now. */ + +struct dwo_unit_eq +{ + using is_transparent = void; + + bool operator() (ULONGEST sig, const dwo_unit_up &unit) const noexcept + { return sig == unit->signature; } + + bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept + { return (*this) (a->signature, b); } +}; + +/* Set of dwo_unit object, using their signature as identity. */ + +using dwo_unit_set = gdb::unordered_set; + +/* Data for one DWO file. + + This includes virtual DWO files (a virtual DWO file is a DWO file as it + appears in a DWP file). DWP files don't really have DWO files per se - + comdat folding of types "loses" the DWO file they came from, and from + a high level view DWP files appear to contain a mass of random types. + However, to maintain consistency with the non-DWP case we pretend DWP + files contain virtual DWO files, and we assign each TU with one virtual + DWO file (generally based on the line and abbrev section offsets - + a heuristic that seems to work in practice). */ + +struct dwo_file +{ + dwo_file () = default; + DISABLE_COPY_AND_ASSIGN (dwo_file); + + /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute. + For virtual DWO files the name is constructed from the section offsets + of abbrev,line,loc,str_offsets so that we combine virtual DWO files + from related CU+TUs. */ + std::string dwo_name; + + /* The DW_AT_comp_dir attribute. */ + const char *comp_dir = nullptr; + + /* The bfd, when the file is open. Otherwise this is NULL. + This is unused(NULL) for virtual DWO files where we use dwp_file.dbfd. */ + gdb_bfd_ref_ptr dbfd; + + /* The sections that make up this DWO file. + Remember that for virtual DWO files in DWP V2 or DWP V5, these are virtual + sections (for lack of a better name). */ + struct dwo_sections sections {}; + + /* The CUs in the file. + + Multiple CUs per DWO are supported as an extension to handle LLVM's Link + Time Optimization output (where multiple source files may be compiled into + a single object/dwo pair). */ + dwo_unit_set cus; + + /* Table of TUs in the file. */ + dwo_unit_set tus; +}; using dwo_file_up = std::unique_ptr; From patchwork Mon Mar 16 23:19:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131834 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id BE42B4BA23E8 for ; Mon, 16 Mar 2026 23:21:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BE42B4BA23E8 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=lnW6f7yU X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 702E64BBCDCA for ; Mon, 16 Mar 2026 23:20:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 702E64BBCDCA 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 702E64BBCDCA 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=1773703258; cv=none; b=gECj5FJoRlIgucLsqgvds5+oEelVqnv+WHarkn3+CtlaX+4sVOI5UX+SGP9NJjsFQjgnZul4/VlIpoZlWUAgC7pMju66N/OMWonvjs9lL4DPOJ6A0SNvSEriDCetVuSy+BJxlxoUmeVNIighPheLUnZEnT3UDXD2ECIqLrx0tM4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703258; c=relaxed/simple; bh=JJoW427m3Df0iqDpytr5vmnMkO0C1a8s1AMKZImH6Ds=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=npv4SBxfEnENBEPsHFcEynABjeJ74f680SEAYBg+RaG4SpEnkszTvUrTTzAIHK4uNhGdWXhVIjtOvkwH8qoIrDg68AlNuQ7KhyXocnRKzxPk7iF7ySSGCb9nvXINqSdVaNjprDEGp7O8fN5kxg/irZYhEeruiGJ7cSU4kIvNLM4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 702E64BBCDCA 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 62GNKqZr238301 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:20:56 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNKqZr238301 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703257; bh=oxs5I76Hk2cq4YeGhU2g2cIPX/yOuFkwzMUCslfy2ck=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=lnW6f7yUa1ddCt58QLvwEKdG0smJFpFfR2/FIZn+gQMTSdkRKTwI6iZtLTy47nJzH ESAUy0sDBpAMHwtQUQ0HJZQOgh68cx/yFChlUR44ucIaMrhs6haFNsSQisFbDCfdCi WiTA4l5SzbJX+dmEeq0piYiKuxtkfVqCGtVBOWEgL92k4VXnuKpNgIeEqlBKnnhdN0 fycmh70thhonW6C4g2ffVhYtgPqh172f9h34fu5XpoHlkt5H34ILbIyJmleK6fBAT8 gDhFmHluwJEAPTfPGBYkdqPgZw6D5zJ8aYoDquZmYbH8cpXzxVHCrsUv6z8wf5+xAq CAWbwGSvagyCA== Received: by simark.ca (Postfix) id D1A2D1E0C3; Mon, 16 Mar 2026 19:20:51 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 3/8] gdb/dwarf: move dwarf2_cu::section to cu.c Date: Mon, 16 Mar 2026 19:19:21 -0400 Message-ID: <20260316232042.368080-4-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:20:52 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi Following the previous patch that moves the dwo_unit structure from dwarf2/read.c to dwarf2/read.h, dwarf2_cu::section has no reason to be implemented in dwarf2/read.c anymore. Move it to dwarf2/cu.c. Change-Id: I67e2bb42d878ac18e4bf3460d75f1394477a46ce --- gdb/dwarf2/cu.c | 11 +++++++++++ gdb/dwarf2/read.c | 14 +------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/gdb/dwarf2/cu.c b/gdb/dwarf2/cu.c index c3bfd15f14a1..3c89bd960d56 100644 --- a/gdb/dwarf2/cu.c +++ b/gdb/dwarf2/cu.c @@ -58,6 +58,17 @@ dwarf2_cu::dwarf2_cu (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile) /* See cu.h. */ +const dwarf2_section_info & +dwarf2_cu::section () const +{ + if (this->dwo_unit != nullptr) + return *this->dwo_unit->section; + else + return *this->per_cu->section (); +} + +/* See cu.h. */ + struct type * dwarf2_cu::addr_sized_int_type (bool unsigned_p) const { diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index c13ea6c1622f..3a0602b4ac08 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -5806,19 +5806,7 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) } } -/* See cu.h. - - This function is defined in this file (instead of cu.c) because it needs - to see the definition of struct dwo_unit. */ - -const dwarf2_section_info & -dwarf2_cu::section () const -{ - if (this->dwo_unit != nullptr) - return *this->dwo_unit->section; - else - return *this->per_cu->section (); -} +/* See cu.h. */ /* See cu.h. From patchwork Mon Mar 16 23:19:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131840 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 58C7D4BB3B83 for ; Mon, 16 Mar 2026 23:30:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 58C7D4BB3B83 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=HZwQ76Ue X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id A326B4BA23D0 for ; Mon, 16 Mar 2026 23:29:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A326B4BA23D0 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 A326B4BA23D0 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=1773703769; cv=none; b=ckHQR69+73T4dS4EQUOcBuJmlPgGfnvELgiibyPCN4pPdTn7pkyacCPtCzpB6mpKPfKQvmNBlXozRgGqmQt0cgB4rUY/iD9xr2oPOwHX7R7QJcBinqVc78np9+kaoAk7DU/oum6Sqdq2etz+3AJC75aYPEyO11akluDKcAM7iyw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703769; c=relaxed/simple; bh=sG7+FsTsCWrkWTZlAr+nKhiz7V9bhBQOciEam1Tc7q0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=sMS1Qw8/JcOAjxcaTi3K6mKv0SGr9bicK6M6nAJEx09WcbUR7tVxZTYX+bBJpnjDjvoZC9mEnFODIDYEyqc5uhT0IljtHMEPV6RGw9XAJPwaRSK/TFKKdgddVIHSvA7W5lZYx3EtEhJgzLuf5XqC3ROS6HJEuTIKaAK3PJAWoSw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A326B4BA23D0 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 62GNTN10240303 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:29:27 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNTN10240303 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703768; bh=QEFXSbL8ftRKpVxhMHoTykEeBYsySFKd3GytKLdRZ0M=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=HZwQ76UeMdE/Tkwtbpr6uShI+HpF7WtTsYGHyNof6TpvMksrtnZgMfqjcPGNziqqY auPb/n/9Ibi3fwa5oBcvXNBgbzpTJBTGA5N6nE5VNqEkfbJsXztKP3nPQ27R0mpU7B Sczy8sNAnYH1iDAuKeUXXU67aoRXUgCh38G40g6iXDRhxZUJYicLhzahpwDgz8yd4i AQ4vhRSa+9nB0u6qz8rzA5CewBMLIVoeuspM/i9fxif+dagPiPJZzs+T67cYrecqrJ sIE8uQsj8qDdCWgMt5LgoaiDz5c3gmSQNWwk1ACwjCXkAZ/5fQ+Fr4FHaeRLDizPnm rqpmVF9jHDahw== Received: by simark.ca (Postfix) id 819D81E0CA; Mon, 16 Mar 2026 19:20:52 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 4/8] gdb/dwarf: add dwo_file::find_tus Date: Mon, 16 Mar 2026 19:19:22 -0400 Message-ID: <20260316232042.368080-5-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:29:23 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi Add this little helper to make finding a TU a bit simpler. I always find the STL way of doing things cryptic, so I think that hiding it in small helper methods makes the code clearer. A subsequent patch will add more uses of it Change-Id: Ibfb20d0e44c65d2ff729f3e0980ec4435f223aef --- gdb/dwarf2/read.c | 18 ++++++++++++++---- gdb/dwarf2/read.h | 5 +++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 3a0602b4ac08..fd1c37ad8e89 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -266,6 +266,18 @@ struct loclists_rnglists_header unsigned int offset_entry_count; }; +/* See read.h. */ + +dwo_unit * +dwo_file::find_tu (ULONGEST signature) const +{ + auto it = this->tus.find (signature); + if (it == this->tus.end ()) + return nullptr; + + return it->get (); +} + /* include/dwarf2.h defines the DWP section codes. It defines a max value but it doesn't define a min value, which we use for error checking, so provide one. */ @@ -2342,12 +2354,10 @@ lookup_dwo_signatured_type (struct dwarf2_cu *cu, ULONGEST sig) /* Note: cu->dwo_unit is the dwo_unit that references this TU, not the dwo_unit of the TU itself. */ dwo_file *dwo_file = cu->dwo_unit->dwo_file; - auto it = dwo_file->tus.find (sig); - if (it == dwo_file->tus.end ()) + dwo_unit *dwo_entry = dwo_file->find_tu (sig); + if (dwo_entry == nullptr) return nullptr; - dwo_unit *dwo_entry = it->get (); - /* If the global table doesn't have an entry for this TU, add one. */ if (sig_type_it == per_bfd->signatured_types.end ()) { diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 5a46786e4f3f..5c61e91870b4 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -585,6 +585,11 @@ struct dwo_file dwo_file () = default; DISABLE_COPY_AND_ASSIGN (dwo_file); + /* Look for a type unit with signature SIGNATURE in this dwo_file. + + Return nullptr if not found. */ + dwo_unit *find_tu (ULONGEST signature) const; + /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute. For virtual DWO files the name is constructed from the section offsets of abbrev,line,loc,str_offsets so that we combine virtual DWO files From patchwork Mon Mar 16 23:19:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131841 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 6E85C4BB3BCF for ; Mon, 16 Mar 2026 23:31:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6E85C4BB3BCF 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=sS9e9GG9 X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 603794BAD165 for ; Mon, 16 Mar 2026 23:29:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 603794BAD165 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 603794BAD165 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=1773703770; cv=none; b=S6we0NbXZSU1zC8j8h2G7+MQ5yGMllnymDdVyT5gDCAfK7oTLOH4Fk0/E6lmUyLJsFkW57qd43Vmh5vq3BK7mlRYFYdnoGUPNb519be0kFJ2pt/ow6jfmXpPebSxwhUvM/lpdqUXZ1uPdS0+w3AYgcHrV9YYQkMXMvxO7or84Mw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703770; c=relaxed/simple; bh=usszWYeEkEhdGEN85I6+aETsIPugCJ3qX6/aED0aVFQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=wDyZmY3t19/6lU1D4SA0xOb2sQbQIUJ4dQMopxRb3HK+mEPIZzIs9vssw23hetiJqwHnQg5o/o8Xr+A/o04UfzWdjYy2XD89cZuhatfTFPtyWB/kqPry4YJJ0iQsQFtFa0YTVTxHgXq/KVp9gZcggQ1d1n6zgzIYX2f9xe0h/5c= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 603794BAD165 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 62GNTNMS240302 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:29:27 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNTNMS240302 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703768; bh=oBytp61X76Wu7V7IG1FzTU5rs0IltFUVW17lNiUYnhA=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=sS9e9GG9eg7Pn+qn3JYPDtwhWgmJ3mxfPF89VQHViRv0zd61UvwDyXISL1XmbZJaR P1Oy0NI15xXQOIATrLWeJVQe4619cunz7dcXf22yaOZ149pPhZblQR9mU4u3NUn4HV Bn7pFq3vc42n3HC38DmzwrZ7Tb9hgh03ojr84hb+566QMgD8rp/HkUG/HfH++jyNJ2 qEgCLuiOLTCqC/BmP3yNa6R5ifGUUswa+Wsea5brKTkzQZn2KoK1whESIHZluxWrxR kMS24cVEkQiUIpZz4bBDKQv4BByIA6ZjQE9p/YCxvshwaGf3BrDbYzlcO8C/r+1/ty 3JWRbsPiGc6ig== Received: by simark.ca (Postfix) id 97ED01E0D1; Mon, 16 Mar 2026 19:20:52 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 5/8] gdb/dwarf: generate foreign type units in .debug_names Date: Mon, 16 Mar 2026 19:19:23 -0400 Message-ID: <20260316232042.368080-6-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:29:23 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi The DWARF 5 .debug_names index makes the distinction between local type units and foreign type units. Local type units are those present directly in the main file. Foreign type units are those present in .dwo files. GDB only knows how to produce local type units today, which leads to invalid indexes whenever there are type units in .dwo files. To observe this, run test gdb.dwarf2/fission-with-type-unit.exp with board cc-with-debug-names. The test passes, but we see this in the log: (gdb) file /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit^M Reading symbols from /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit...^M warning: Section .debug_names has incorrect entry in TU table, ignoring .debug_names.^M These are the units involved in this test. The first and last CUs are dummy CUs added by the DWARF assembler, they are not important. - CU at offset 0x0 of .debug_info in fission-with-type-unit -- dummy - CU at offset 0xc of .debug_info in fission-with-type-unit - CU at offset 0x25 of .debug_info in fission-with-type-unit -- dummy - TU at offset 0x0 of .debug_info.dwo in fission-with-type-unit-dw.dwo This is the content of the produced .debug_names: Contents of the .debug_names section: $ readelf --debug-dump=gdb_index testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit ... Version 5 Augmentation string: 47 44 42 33 00 00 00 00 ("GDB3") CU table: [ 0] 0 [ 1] 0xc [ 2] 0x25 TU table: [ 0] 0 Foreign TU table: Used 0 of 0 buckets. Symbol table: [ 1] global_var: <0><1> DW_TAG_variable DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x1c> DW_IDX_GNU_language=(udata)=0 DW_IDX_GNU_internal=(flag_present)=1 [ 2] int: <0x8><2> DW_TAG_base_type DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x15> DW_IDX_GNU_language=(udata)=0 <0xf><3> DW_TAG_base_type DW_IDX_type_unit=(udata)=0 DW_IDX_die_offset=(ref_addr)=<0x19> DW_IDX_GNU_language=(udata)=0 The TU table claims that there is a TU at offset 0 in the main file (fission-with-type-unit). This is wrong: the TU is in fission-with-type-unit-dw.dwo. It should be listed in the Foreign TU table instead. This patch therefore teaches GDB to use the foreign TU table for TUs in .dwo files. A note about foreign type units and skeletons: in the early history of split DWARF and type units, gcc 4.x used to create skeletons for type units in .dwo files, but subsequent versions don't. DWARF 5 doesn't have support for type unit skeletons at all. So skeletons for type units are mostly a historical curiosity at this point, the norm is to not have them. But if for some reason a type unit in a .dwo file had a matching skeleton in the main file, then it would be ok for that TU to be listed in the "TU table". The offset would be that of the skeleton. While the list of CUs and local TUs contain the offset within the .debug_info section where to find the unit, the foreign TU list only contains the 8-byte signature of the types. With just that, a reader wouldn't be able to easily locate a .dwo that contain the type with a given signature. To help with this, index entries for foreign type units may also include a reference to a compilation unit that can be followed in order to find a .dwo file containing the type. This patch implements it. Implementation details ---------------------- The first change is the addition of the dwo_unit::per_cu field, which allows going from the dwo_unit to the dwarf2_per_cu structure (which describes the skeleton) that was used to lookup this dwo_unit. This fields starts at nullptr, and it gets set in lookup_dwo_cutu whenever we look up the dwo_unit for a given dwarf2_per_cu. This will come handy later. I made this field an std::atomic, because I think it would be possible to craft a weird test case that would make two indexer threads try to set the field on the same dwo_unit. During normal operation, we expect the field for each dwo_unit representing a CU to be written exactly once. In index-write.c, change the get_unit_lists function in dwarf2/index-write.c to segregate local and foreign type units. Then, update write_debug_names to emit the list of foreign TUs in the .debug_names header. This consists of a list of type signatures. In debug_names::build, for foreign type units, emit a DW_IDX_compile_unit field. This is the reference to the CU that can be used to locate the .dwo file containing that type unit. To obtain the value for this field, look up a CU in the same dwo_file that has its dwo_unit::per_cu field set (typically there will be exactly one CU, and the field will be set). With this patch, the index for the test case above looks like: $ readelf --debug-dump=gdb_index testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit ... Version 5 Augmentation string: 47 44 42 33 00 00 00 00 ("GDB3") CU table: [ 0] 0 [ 1] 0xc [ 2] 0x25 TU table: Foreign TU table: [ 0] 000000000000cafe Used 0 of 0 buckets. Symbol table: [ 1] global_var: <0><1> DW_TAG_variable DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x1c> DW_IDX_GNU_language=(udata)=0 DW_IDX_GNU_internal=(flag_present)=1 [ 2] int: <0x8><2> DW_TAG_base_type DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x15> DW_IDX_GNU_language=(udata)=0 <0xf><3> DW_TAG_base_type DW_IDX_type_unit=(udata)=0 DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x19> DW_IDX_GNU_language=(udata)=0 ... We can see that the TU is correctly placed in the foreign TU list, and that the index entry (the last line) points to the TU at index 0, but also to the CU at index 1, which is indeed the CU that the reader can follow to find the type unit. With this patch, GDB still rejects the index: (gdb) file /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit Reading symbols from /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit... warning: Section .debug_names in /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit has unsupported 1 foreign TUs, ignoring .debug_names. But at least, we don't produce a bogus index anymore, that's already an improvement. A following patch in this series implements the reading side Change-Id: I311fd7b4ca57d9ff6d64ae08df805c6635961eed --- gdb/dwarf2/index-write.c | 160 +++++++++++++++++++++++++++++++++------ gdb/dwarf2/read.c | 9 +++ gdb/dwarf2/read.h | 17 +++++ 3 files changed, 162 insertions(+), 24 deletions(-) diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index 0c8474b3e4ed..5edada5d16c3 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -633,6 +633,10 @@ write_address_map (const addrmap *addrmap, data_buf &addr_vec, addrmap_index_data.previous_cu_index); } +/* Is this symbol a compile unit, local type unit (type unit in the main file) + or foreign type unit (type unit in a .dwo file)? */ +enum class unit_kind { cu, local_tu, foreign_tu }; + /* Return true if TU is a foreign type unit, that is a type unit defined in a .dwo file without a corresponding skeleton in the main file. */ @@ -645,6 +649,22 @@ is_foreign_tu (const signatured_type *tu) return endswith (tu->section ()->get_name (), ".dwo"); } +/* Determine the unit_kind for PER_CU. */ + +static unit_kind +classify_unit (const dwarf2_per_cu &per_cu) +{ + if (auto sig_type = per_cu.as_signatured_type (); sig_type != nullptr) + { + if (is_foreign_tu (sig_type)) + return unit_kind::foreign_tu; + else + return unit_kind::local_tu; + } + else + return unit_kind::cu; +} + /* DWARF-5 .debug_names builder. */ class debug_names { @@ -668,9 +688,6 @@ class debug_names return dwarf5_is_dwarf64 ? 8 : 4; } - /* Is this symbol from DW_TAG_compile_unit or DW_TAG_type_unit? */ - enum class unit_kind { cu, tu }; - /* Insert one symbol. */ void insert (const cooked_index_entry *entry) { @@ -723,9 +740,8 @@ class debug_names for (const cooked_index_entry *entry : these_entries) { - unit_kind kind = (entry->per_cu->is_debug_types () - ? unit_kind::tu - : unit_kind::cu); + unit_kind kind = classify_unit (*entry->per_cu); + /* Some Ada parentage is synthesized by the reader and so must be ignored here. */ const cooked_index_entry *parent = entry->get_parent (); @@ -752,6 +768,14 @@ class debug_names : DW_IDX_type_unit); m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + /* For foreign TUs only: the index of a CU that can be used to + locate the .dwo file containing that TU. */ + if (kind == unit_kind::foreign_tu) + { + m_abbrev_table.append_unsigned_leb128 (DW_IDX_compile_unit); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + } + /* DIE offset. */ m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset); m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr); @@ -809,6 +833,67 @@ class debug_names gdb_assert (it != m_cu_index_htab.cend ()); m_entry_pool.append_unsigned_leb128 (it->second); + /* For foreign TUs only: the index of a CU that can be used to + locate the .dwo file containing that TU. + + This code implements this snippet of the DWARF 5 standard, from + 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 + + So, the idea here is to find a CU in the same .dwo file as the + type unit (there will typically be one) and write the offset to + its skeleton. */ + if (kind == unit_kind::foreign_tu) + { + /* If we know about this TU that is only listed in some .dwo + file, it means that we looked up the .dwo file from a CU + skeleton at some point during indexing, and recorded it in + ENTRY->PER_CU. */ + gdb_assert (entry->per_cu != nullptr); + + signatured_type *sig_type + = entry->per_cu->as_signatured_type (); + + /* We know it's a TU, because it has been classified as + foreign_tu. */ + gdb_assert (sig_type != nullptr); + + /* We know it has a dwo_unit, because it's foreign. */ + gdb_assert (sig_type->dwo_unit != nullptr); + + /* Find a CU in the same .dwo file as the TU. */ + dwarf2_per_cu *per_cu = nullptr; + for (auto &dwo_cu : sig_type->dwo_unit->dwo_file->cus) + if (dwo_cu->per_cu != nullptr) + { + per_cu = dwo_cu->per_cu; + break; + } + + /* It would be really weird to not find a CU that we looked up + in this .dwo file. Otherwise, how would we know about this + foreign TU? But it might be possible to craft a bad faith + .dwo file that does this by having a one TU with a skeleton + and one TU without a skeleton in the same .dwo file (i.e. + non-standard DWARF 5). So, just in case, we error out + gracefully. */ + if (per_cu == nullptr) + error (_("Could not find a CU for foreign type unit in DWO " + "file %s"), + sig_type->dwo_unit->dwo_file->dwo_name.c_str ()); + + /* This is the CU that can be used to find the .dwo file + containing the type unit, write its offset. */ + const auto cu_it = m_cu_index_htab.find (per_cu); + gdb_assert (cu_it != m_cu_index_htab.cend ()); + m_entry_pool.append_unsigned_leb128 (cu_it->second); + } + /* DIE offset. */ m_entry_pool.append_uint (dwarf5_offset_size (), m_dwarf5_byte_order, @@ -1333,8 +1418,9 @@ struct unit_lists /* Compilation units. */ std::vector comp; - /* Type units. */ - std::vector type; + /* Type units, local and foreign. */ + std::vector local_type; + std::vector foreign_type; }; /* Get sorted (by section offset) lists of comp units and type units. */ @@ -1345,16 +1431,28 @@ get_unit_lists (const dwarf2_per_bfd &per_bfd) unit_lists lists; for (const auto &unit : per_bfd.all_units) - if (const signatured_type *sig_type = unit->as_signatured_type (); - sig_type != nullptr) - lists.type.emplace_back (sig_type); - else - lists.comp.emplace_back (unit.get ()); + switch (classify_unit (*unit)) + { + case unit_kind::cu: + lists.comp.emplace_back (unit.get ()); + break; + + case unit_kind::local_tu: + lists.local_type.emplace_back (unit->as_signatured_type ()); + break; + + case unit_kind::foreign_tu: + lists.foreign_type.emplace_back (unit->as_signatured_type ()); + break; + } auto by_sect_off = [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) { return lhs->sect_off () < rhs->sect_off (); }; - /* Sort both lists, even though it is technically not always required: + auto by_sig = [] (const signatured_type *lhs, const signatured_type *rhs) + { return lhs->signature < rhs->signature; }; + + /* Sort the lists, even though it is technically not always required: - while .gdb_index requires the CU list to be sorted, DWARF 5 doesn't say anything about the order of CUs in .debug_names. @@ -1364,7 +1462,8 @@ get_unit_lists (const dwarf2_per_bfd &per_bfd) However, it helps make sure that GDB produce a stable and predictable output, which is nice. */ std::sort (lists.comp.begin (), lists.comp.end (), by_sect_off); - std::sort (lists.type.begin (), lists.type.end (), by_sect_off); + std::sort (lists.local_type.begin (), lists.local_type.end (), by_sect_off); + std::sort (lists.foreign_type.begin (), lists.foreign_type.end (), by_sig); return lists; } @@ -1392,10 +1491,9 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table, that DWARF 5's .debug_names does with "foreign type units". If the executable has such skeletonless type units, refuse to produce an index, instead of producing a bogus one. */ - for (const signatured_type *tu : units.type) - if (is_foreign_tu (tu)) - error (_("Found foreign (skeletonless) type unit, unable to produce " - ".gdb_index. Consider using .debug_names instead.")); + if (!units.foreign_type.empty ()) + error (_("Found foreign (skeletonless) type unit, unable to produce " + ".gdb_index. Consider using .debug_names instead.")); int counter = 0; @@ -1421,7 +1519,7 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table, /* Write type units. */ data_buf types_cu_list; - for (const signatured_type *sig_type : units.type) + for (const signatured_type *sig_type : units.local_type) { const auto insertpair = cu_index_htab.emplace (sig_type, counter); gdb_assert (insertpair.second); @@ -1494,7 +1592,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, data_buf type_unit_list; - for (auto [i, per_cu] : gdb::enumerate (units.type)) + for (auto [i, per_cu] : gdb::enumerate (units.local_type)) { nametable.add_cu (per_cu, i); type_unit_list.append_uint (nametable.dwarf5_offset_size (), @@ -1502,9 +1600,21 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, to_underlying (per_cu->sect_off ())); } + data_buf foreign_type_unit_list; + + for (auto [i, sig_type] : gdb::enumerate (units.foreign_type)) + { + /* The numbering of foreign type units follows the numbering of local type + units. */ + nametable.add_cu (sig_type, units.local_type.size () + i); + foreign_type_unit_list.append_uint (8, dwarf5_byte_order, + sig_type->signature); + } + /* Verify that all units are represented. */ gdb_assert (units.comp.size () == per_bfd->num_comp_units); - gdb_assert (units.type.size () == per_bfd->num_type_units); + gdb_assert (units.local_type.size () + units.foreign_type.size () + == per_bfd->num_type_units); for (const cooked_index_entry *entry : table->all_entries ()) nametable.insert (entry); @@ -1521,6 +1631,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, expected_bytes += bytes_of_header; expected_bytes += comp_unit_list.size (); expected_bytes += type_unit_list.size (); + expected_bytes += foreign_type_unit_list.size (); expected_bytes += nametable.bytes (); data_buf header; @@ -1547,11 +1658,11 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, /* local_type_unit_count - The number of TUs in the local TU list. */ - header.append_uint (4, dwarf5_byte_order, units.type.size ()); + header.append_uint (4, dwarf5_byte_order, units.local_type.size ()); /* foreign_type_unit_count - The number of TUs in the foreign TU list. */ - header.append_uint (4, dwarf5_byte_order, 0); + header.append_uint (4, dwarf5_byte_order, units.foreign_type.size ()); /* bucket_count - The number of hash buckets in the hash lookup table. GDB does not use the hash table, so there's also no need @@ -1577,6 +1688,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, header.file_write (out_file); comp_unit_list.file_write (out_file); type_unit_list.file_write (out_file); + foreign_type_unit_list.file_write (out_file); nametable.file_write (out_file, out_file_str); assert_file_size (out_file, expected_bytes); diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index fd1c37ad8e89..af7ef8d8f3db 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -7232,6 +7232,15 @@ cutu_reader::lookup_dwo_cutu (dwarf2_cu *cu, const char *dwo_name, ("DWO %s %s(%s) found: @%s", kind, dwo_name, hex_string (signature), host_address_to_string (dwo_unit_it->get ())); + + /* Record the dwarf2_per_cu that was used to look up this + dwo_unit. There will typically be exactly one skeleton + pointing to each DWO CU. But if for some mysterious reason + there are multiple skeletons pointing to the same DWO CU, it's + fine, we just need to remember one. This will keep the last + one seen. */ + (*dwo_unit_it)->per_cu = cu->per_cu; + return dwo_unit_it->get (); } } diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 5c61e91870b4..1687bc52e432 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -259,6 +259,7 @@ struct dwarf2_per_cu /* If this dwarf2_per_cu is a signatured_type, return "this" cast to signatured_type. Otherwise, return nullptr. */ signatured_type *as_signatured_type (); + const signatured_type *as_signatured_type () const; dwarf2_per_bfd *per_bfd () const { return m_per_bfd; } @@ -455,6 +456,17 @@ dwarf2_per_cu::as_signatured_type () return nullptr; } +/* See dwarf2_per_cu declaration. */ + +inline const signatured_type * +dwarf2_per_cu::as_signatured_type () const +{ + if (m_is_debug_types) + return static_cast (this); + + return nullptr; +} + /* Hash a signatured_type object based on its signature. */ struct signatured_type_hash @@ -496,6 +508,11 @@ struct dwo_unit /* Backlink to the containing struct dwo_file. */ struct dwo_file *dwo_file = nullptr; + /* For compile units only, the per-CU structure that represents the skeleton + that we used to reach this dwo_unit. It starts as nullptr, and gets set + in cutu_reader::lookup_dwo_cutu. */ + std::atomic per_cu = nullptr; + /* The "id" that distinguishes this CU/TU. .debug_info calls this "dwo_id", .debug_types calls this "signature". Since signatures came first, we stick with it for consistency. */ From patchwork Mon Mar 16 23:19:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131842 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 67B4A4C900F3 for ; Mon, 16 Mar 2026 23:31:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 67B4A4C900F3 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=EqG8NHXR X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id CA3EF4BA23E8 for ; Mon, 16 Mar 2026 23:29:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CA3EF4BA23E8 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 CA3EF4BA23E8 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=1773703770; cv=none; b=OW+L+RI7R3HDp3Ic25NswIapFges3udGyurQhkwFsxiGCqNy1Jgg1Wz+9SYSeh2lN7AYey4XGOBY7oH2v2ZQ8Mtquqf3iF49UmRshLkQBW5yPvK8M5BBua2o1wCt1ZiJS4oEeFgUlYKm1UZpy855a4ULMyegfilamHmPBLojhok= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703770; c=relaxed/simple; bh=YSSglbdcheU/d9uCe7NcQIqnPTuwFkDOuIcIQL3Jo84=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=H2pRJrVbEP1b3z0ELseQUMfvDYyG1bdIY7OSGRXAmKSIuH0Dk9+TP4DDsu7IX4BNKPJ6fIgvGON3EUBtK7NzZQ/3dvq6cg/05Mi1kv194VBi9lfcxmZSIEu61zFk6hpqOs+Zj4fEEUx8HYCttY8X1/3A6eAC4mrJXNc3WhULRx4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CA3EF4BA23E8 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 62GNTNdk240304 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:29:27 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNTNdk240304 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703768; bh=0c6nON6yjYbCWVpRuPisDdam3FbxXRLHQSYXNbtWJsE=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=EqG8NHXRb9Dm5EVnU6DFRA00/Nb1f9F5lJM/H0E0zhheacc99j5iJAV/ajeVE6EEh PmSNhlh83dV328j7M2ydC963MdtYQ1iZ5pM/6ubOZZM9984bBxVu8z38tfbBmS4hks szNOiLaWnsI9PCQY/r9STH4eTriAurXN1PTjGZjiQMJSCnLjI3R+U5kldzlYhpyGMR uXu/HN7ZA9xNb6Dq3jp4vQoCi2IsLULHS/i9PZfvNsZG1/iO2b/9ReTt59siPhX6zT wpjaGsF8ed14OU6Nb3uCwBHjQPvL6zqGj2y1Mlc01OR8kmuAJx6/Z7Jr9As6Sixzhw g+LPGceVOwf3Q== Received: by simark.ca (Postfix) id DD5421E0DA; Mon, 16 Mar 2026 19:20:52 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 6/8] gdb/dwarf: add debug output in read-debug-names.c Date: Mon, 16 Mar 2026 19:19:24 -0400 Message-ID: <20260316232042.368080-7-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:29:23 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi Add a bunch of debug prints in read-debug-names.c to dump what we are reading. Some of if was helpful to me when debugging my changes to the .debug_names reader and writer. The debug prints are activated by "set debug dwarf-read" (I didn't feel like we needed a separate knob for that). There are two levels of verbosity. At level 1, we print everything that happens once, like the header, counts, offsets of important parts of the index, etc. At levels > 1, we print information about each abbrev, CU/TU and index entry. The output is made such that it is easy look at the output of readelf of llvm-dwarfdump on the side and confirm that GDB is reading the index right. Here are some examples of the output. Header and some abbrevs: [dwarf-read] read_debug_names_from_section: start: reading .debug_names from /home/simark/build/babeltrace/src/lib/.libs/libbabeltrace2.so.0.0.0 [dwarf-read] read_debug_names_from_section: section size: 0x19d4e, initial length: 0x19d4a, dwarf64: 0, offset_size: 4 [dwarf-read] read_debug_names_from_section: version: 5 [dwarf-read] read_debug_names_from_section: cu_count: 56, tu_count: 0, foreign_tu_count: 0 [dwarf-read] read_debug_names_from_section: bucket_count: 0, name_count: 3418, abbrev_table_size: 374, augmentation_string_size: 8 [dwarf-read] read_debug_names_from_section: augmentation string: "GDB3\x00\x00\x00\x00" [dwarf-read] read_debug_names_from_section: abbrev 1: tag DW_TAG_namespace, 4 attributes [dwarf-read] read_debug_names_from_section: DW_IDX_compile_unit DW_FORM_udata [dwarf-read] read_debug_names_from_section: DW_IDX_die_offset DW_FORM_ref_addr [dwarf-read] read_debug_names_from_section: DW_IDX_GNU_language DW_FORM_udata [dwarf-read] read_debug_names_from_section: DW_IDX_parent DW_FORM_data4 [dwarf-read] read_debug_names_from_section: abbrev 2: tag DW_TAG_enumerator, 4 attributes [dwarf-read] read_debug_names_from_section: DW_IDX_compile_unit DW_FORM_udata [dwarf-read] read_debug_names_from_section: DW_IDX_die_offset DW_FORM_ref_addr [dwarf-read] read_debug_names_from_section: DW_IDX_GNU_language DW_FORM_udata [dwarf-read] read_debug_names_from_section: DW_IDX_GNU_internal DW_FORM_flag_present Excerpt of the CU list: [dwarf-read] build_and_check_cu_list_from_debug_names: building CU list from .debug_names (56 CUs) [dwarf-read] build_and_check_cu_list_from_debug_names: CU 0: offset 0x0 [dwarf-read] build_and_check_cu_list_from_debug_names: CU 1: offset 0x1bac [dwarf-read] build_and_check_cu_list_from_debug_names: CU 2: offset 0x4262 [dwarf-read] build_and_check_cu_list_from_debug_names: CU 3: offset 0x4ff0 [dwarf-read] build_and_check_cu_list_from_debug_names: CU 4: offset 0x59e9 [dwarf-read] build_and_check_cu_list_from_debug_names: CU 5: offset 0x67af [dwarf-read] build_and_check_cu_list_from_debug_names: CU 6: offset 0x8e1a [dwarf-read] build_and_check_cu_list_from_debug_names: CU 7: offset 0xa5cc [dwarf-read] build_and_check_cu_list_from_debug_names: CU 8: offset 0xcd39 Some entries: [dwarf-read] scan_all_names: start: scanning 3418 names from .debug_names [dwarf-read] scan_entries: scanning entries for name 1: "(anonymous namespace)" (entry pool offset 0x0) [dwarf-read] scan_one_entry: entry pool offset 0x0: abbrev 1, tag DW_TAG_namespace [dwarf-read] scan_one_entry: DW_IDX_compile_unit (DW_FORM_udata): 55 [dwarf-read] scan_one_entry: DW_IDX_die_offset (DW_FORM_ref_addr): 0x7d06b [dwarf-read] scan_one_entry: DW_IDX_GNU_language (DW_FORM_udata): 33 [dwarf-read] scan_one_entry: DW_IDX_parent (DW_FORM_data4): 38201 [dwarf-read] scan_one_entry: -> die_offset 0x7d06b, per_cu offset 0x5af0e [dwarf-read] scan_one_entry: entry pool offset 0xb: end of entries (abbrev 0) [dwarf-read] scan_entries: scanning entries for name 2: "AUTO_SEEK_STREAM_STATE_PACKET_BEGAN" (entry pool offset 0xc) [dwarf-read] scan_one_entry: entry pool offset 0xc: abbrev 2, tag DW_TAG_enumerator [dwarf-read] scan_one_entry: DW_IDX_compile_unit (DW_FORM_udata): 17 [dwarf-read] scan_one_entry: DW_IDX_die_offset (DW_FORM_ref_addr): 0x1beea [dwarf-read] scan_one_entry: DW_IDX_GNU_language (DW_FORM_udata): 29 [dwarf-read] scan_one_entry: DW_IDX_GNU_internal (DW_FORM_flag_present): 1 [dwarf-read] scan_one_entry: -> die_offset 0x1beea, per_cu offset 0x1a28e [dwarf-read] scan_one_entry: entry pool offset 0x13: end of entries (abbrev 0) Change-Id: I4a40bfb73fa7feccb5038814a7d1a1bcd4f6231b --- gdb/dwarf2/read-debug-names.c | 120 +++++++++++++++++++++++++++++++++- gdb/dwarf2/read.c | 24 +------ gdb/dwarf2/read.h | 24 +++++++ 3 files changed, 143 insertions(+), 25 deletions(-) diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index 8e488377439b..6ac65d101b16 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -170,7 +170,12 @@ mapped_debug_names_reader::scan_one_entry (const char *name, const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read); entry += bytes_read; if (abbrev == 0) - return nullptr; + { + dwarf_read_debug_printf_v + (" entry pool offset 0x%tx: end of entries (abbrev 0)", + offset_in_entry_pool); + return nullptr; + } const auto indexval_it = abbrev_map.find (abbrev); if (indexval_it == abbrev_map.cend ()) @@ -182,6 +187,12 @@ mapped_debug_names_reader::scan_one_entry (const char *name, } const auto &indexval = indexval_it->second; + + dwarf_read_debug_printf_v + (" entry pool offset 0x%tx: abbrev %s, tag %s", + offset_in_entry_pool, pulongest (abbrev), + dwarf_tag_name (indexval.dwarf_tag)); + cooked_index_flag flags = 0; sect_offset die_offset {}; enum language lang = language_unknown; @@ -238,6 +249,14 @@ mapped_debug_names_reader::scan_one_entry (const char *name, bfd_get_filename (abfd))); return nullptr; } + + dwarf_read_debug_printf_v (" %s (%s): %s", + get_DW_IDX_name (attr.dw_idx), + dwarf_form_name (attr.form), + (attr.form == DW_FORM_ref_addr + ? hex_string (ull) + : pulongest (ull))); + switch (attr.dw_idx) { case DW_IDX_compile_unit: @@ -302,6 +321,11 @@ 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) { + dwarf_read_debug_printf_v + (" -> die_offset %s, per_cu offset %s", + sect_offset_str (die_offset), + sect_offset_str (per_cu->sect_off ())); + *result = indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags, lang, name, nullptr, per_cu); @@ -312,6 +336,8 @@ mapped_debug_names_reader::scan_one_entry (const char *name, entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); } + else + dwarf_read_debug_printf_v (" -> no valid CU/TU, skipping"); return entry; } @@ -323,6 +349,12 @@ mapped_debug_names_reader::scan_entries (uint32_t index, const char *name, const gdb_byte *entry) { + /* Print a 1-based index, because this is what readelf and llvm-dwarfdump + do. This makes it easier to compare output side-by-side. */ + dwarf_read_debug_printf_v + ("scanning entries for name %u: \"%s\" (entry pool offset 0x%tx)", + index + 1, name, entry - entry_pool); + std::vector these_entries; while (true) @@ -347,6 +379,9 @@ mapped_debug_names_reader::scan_entries (uint32_t index, void mapped_debug_names_reader::scan_all_names () { + DWARF_READ_SCOPED_DEBUG_START_END + ("scanning %u names from .debug_names", name_count); + all_entries.resize (name_count); /* In the first pass, create all the entries. */ @@ -377,6 +412,9 @@ mapped_debug_names_reader::scan_all_names () Otherwise, the DW_IDX_parent value is an offset into the entry pool, which is not ambiguous. */ + dwarf_read_debug_printf ("resolving %zu parent pointers", + needs_parent.size ()); + for (auto &[entry, parent_val] : needs_parent) { if (augmentation_is_gdb && gdb_augmentation_version == 2) @@ -463,6 +501,9 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile, mapped_debug_names_reader &map, dwarf2_section_info *section) { + dwarf_read_debug_printf ("building TU list from .debug_names (%u TUs)", + map.tu_count); + struct objfile *objfile = per_objfile->objfile; dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; @@ -477,6 +518,9 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile, map.offset_size, map.dwarf5_byte_order)); + dwarf_read_debug_printf_v (" TU %u: offset %s", i, + sect_offset_str (sect_off)); + /* Find the matching dwarf2_per_cu. */ dwarf2_per_cu *per_cu = dwarf2_find_unit ({ section, sect_off }, per_bfd); @@ -508,15 +552,24 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, struct dwarf2_section_info *section, mapped_debug_names_reader &map) { + DWARF_READ_SCOPED_DEBUG_START_END + ("reading .debug_names from %s", filename); + struct objfile *objfile = per_objfile->objfile; if (section->empty ()) - return false; + { + dwarf_read_debug_printf ("section is 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; + { + dwarf_read_debug_printf ("section has no contents"); + return false; + } section->read (objfile); @@ -534,6 +587,11 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, map.dwarf5_is_dwarf64 = bytes_read != 4; map.offset_size = map.dwarf5_is_dwarf64 ? 8 : 4; + + dwarf_read_debug_printf ("section size: %s, initial length: %s, " + "dwarf64: %d, offset_size: %d", + hex_string (section->size), hex_string (length), + map.dwarf5_is_dwarf64, map.offset_size); if (bytes_read + length != section->size) { /* There may be multiple per-CU indices. */ @@ -549,6 +607,9 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, /* The version number. */ uint16_t version = read_2_bytes (abfd, addr); addr += 2; + + dwarf_read_debug_printf ("version: %d", version); + if (version != 5) { warning (_("Section .debug_names in %ps has unsupported version %d, " @@ -585,6 +646,11 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, list. */ uint32_t 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, " @@ -613,11 +679,34 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, string. This value is rounded up to a multiple of 4. */ uint32_t augmentation_string_size = read_4_bytes (abfd, addr); addr += 4; + + dwarf_read_debug_printf ("bucket_count: %u, name_count: %u, " + "abbrev_table_size: %u, " + "augmentation_string_size: %u", + map.bucket_count, map.name_count, + abbrev_table_size, augmentation_string_size); + augmentation_string_size += (-augmentation_string_size) & 3; const auto augmentation_string = gdb::make_array_view (addr, augmentation_string_size); + if (dwarf_read_debug >= 1) + { + std::string aug_repr; + for (size_t i = 0; i < augmentation_string_size; i++) + { + gdb_byte b = addr[i]; + if (c_isprint (b)) + aug_repr += b; + else + aug_repr += string_printf ("\\x%02x", b); + } + + dwarf_read_debug_printf ("augmentation string: \"%s\"", + aug_repr.c_str ()); + } + if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_1)) { warning (_(".debug_names created by an old version of gdb; ignoring")); @@ -704,7 +793,26 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, break; indexval.attr_vec.push_back (std::move (attr)); } + + dwarf_read_debug_printf_v + (" abbrev %s: tag %s, %zu attributes", + pulongest (index_num), dwarf_tag_name (indexval.dwarf_tag), + indexval.attr_vec.size ()); + + for (const auto &attr : indexval.attr_vec) + dwarf_read_debug_printf_v + (" %s %s%s", + get_DW_IDX_name (attr.dw_idx), + dwarf_form_name (attr.form), + (attr.form == DW_FORM_implicit_const + ? string_printf (" (%s)", + plongest (attr.implicit_const)).c_str () + : "")); } + + dwarf_read_debug_printf ("%zu abbreviations read", + map.abbrev_map.size ()); + if (addr != abbrev_table_start + abbrev_table_size) { warning (_("Section .debug_names in %ps has abbreviation_table " @@ -728,6 +836,9 @@ build_and_check_cu_list_from_debug_names (dwarf2_per_bfd *per_bfd, mapped_debug_names_reader &map, dwarf2_section_info §ion) { + dwarf_read_debug_printf ("building CU list from .debug_names (%u CUs)", + map.cu_count); + int nr_cus = per_bfd->num_comp_units; if (map.cu_count != nr_cus) @@ -745,6 +856,9 @@ build_and_check_cu_list_from_debug_names (dwarf2_per_bfd *per_bfd, map.offset_size, map.dwarf5_byte_order)); + dwarf_read_debug_printf_v (" CU %u: offset %s", i, + sect_offset_str (sect_off)); + /* Find the matching dwarf2_per_cu. */ dwarf2_per_cu *per_cu = dwarf2_find_unit ({ §ion, sect_off }, per_bfd); diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index af7ef8d8f3db..c58cc1731f47 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -99,28 +99,8 @@ #include "extract-store-integer.h" #include "cli/cli-style.h" -/* When == 1, print basic high level tracing messages. - When > 1, be more verbose. - This is in contrast to the low level DIE reading of dwarf_die_debug. */ -static unsigned int dwarf_read_debug = 0; - -/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 1. */ - -#define dwarf_read_debug_printf(fmt, ...) \ - debug_prefixed_printf_cond (dwarf_read_debug >= 1, "dwarf-read", fmt, \ - ##__VA_ARGS__) - -/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 2. */ - -#define dwarf_read_debug_printf_v(fmt, ...) \ - debug_prefixed_printf_cond (dwarf_read_debug >= 2, "dwarf-read", fmt, \ - ##__VA_ARGS__) - -/* Print "dwarf-read" start/end debug statements. */ - -#define DWARF_READ_SCOPED_DEBUG_START_END(fmt, ...) \ - scoped_debug_start_end ([] { return dwarf_read_debug >= 1; }, "dwarf-read", \ - fmt, ##__VA_ARGS__) +/* See read.h. */ +unsigned int dwarf_read_debug = 0; /* When non-zero, dump DIEs after they are read in. */ static unsigned int dwarf_die_debug = 0; diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 1687bc52e432..e4ed84b08ad9 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -1344,6 +1344,30 @@ type *dwarf2_fetch_die_type_sect_off (sect_offset sect_off, dwarf2_per_objfile *per_objfile, const char **var_name = nullptr); +/* When == 1, print basic high level tracing messages. + When > 1, be more verbose. + This is in contrast to the low level DIE reading of dwarf_die_debug. */ + +extern unsigned int dwarf_read_debug; + +/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 1. */ + +#define dwarf_read_debug_printf(fmt, ...) \ + debug_prefixed_printf_cond (dwarf_read_debug >= 1, "dwarf-read", fmt, \ + ##__VA_ARGS__) + +/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 2. */ + +#define dwarf_read_debug_printf_v(fmt, ...) \ + debug_prefixed_printf_cond (dwarf_read_debug >= 2, "dwarf-read", fmt, \ + ##__VA_ARGS__) + +/* Print "dwarf-read" start/end debug statements. */ + +#define DWARF_READ_SCOPED_DEBUG_START_END(fmt, ...) \ + scoped_debug_start_end ([] { return dwarf_read_debug >= 1; }, "dwarf-read", \ + fmt, ##__VA_ARGS__) + /* When non-zero, dump line number entries as they are read in. */ extern unsigned int dwarf_line_debug; From patchwork Mon Mar 16 23:19:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131839 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 8C9E54BAD165 for ; Mon, 16 Mar 2026 23:30:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8C9E54BAD165 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=cijZyUSV X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 0045C4BA2E18 for ; Mon, 16 Mar 2026 23:29:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0045C4BA2E18 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 0045C4BA2E18 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=1773703769; cv=none; b=B0loz+yVBFlxhlXdPVpKpkzR4Xt8aFHPEXNXCoIm3C5uT+j4mB56aeeUvFEPz7YcTbwv9Jumksmi4dEO2dFNAQFn9QZFaHAkZRrGWHWjl/H/3hcknx8c5U9yXjmQoKyhPFMSDeYupGlwV73t96BG9jwJ+R9vnMB2xuXNXaYf/ww= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773703769; c=relaxed/simple; bh=gV7NiVS5N/43cJVMkl6RMriRlOFuvTBMd+/52R6PlCo=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=XG51df+wy+caOW3CtYAEmbE8oA8FzF++lyIxD+US25pxONbhXlepjV5RfvpwqHGGykHftyI4I9KAk4aeJFJRgLRX9OdnUVav+dnVHUAS/aIsDhbX2HmL/aDvGGbsNxq9nGxyr2aqUrzIM6rssPgnfjEfAgpBdbyDnYZzEjZUSOg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0045C4BA2E18 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 62GNTNPZ240311 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 16 Mar 2026 19:29:27 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 62GNTNPZ240311 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1773703768; bh=kF4/tnzon0rmPs32XM3Yd7GaT9o0yWQC7xGNRxni6uM=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=cijZyUSVbVf71jc3SYviV/4pHd3A/9DndwS8cOjmp3gi726xFHe511BSelo0gOcjs t1VWW1sBHtOIQiuFdBIKly9/t6cdkDqAbzLoYTBSafWyZJARNeBuicslCC/pI2vuYA f0Kar7ElASxB0owj8BLOkmH5U8i6+HxTECEjFojGC8OPvC9TvjYEkZz9ogK5mO+RUL Y3nOW20gVlkR2ws1BAlWS+riEjK1B5GqQxbwbdkcEJTAye+BjqQs1sM/ZqBVIm7ooi t+PukBLT6Zso7Z5tQ5JhYpDiMIV3fz6sz01TUbHkCiO9XO9kNvEPT5dDxJFTRPWTng NOrFYILxM4aSw== Received: by simark.ca (Postfix) id 348951E0E2; Mon, 16 Mar 2026 19:20:53 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 7/8] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry Date: Mon, 16 Mar 2026 19:19:25 -0400 Message-ID: <20260316232042.368080-8-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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:29:23 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org From: Simon Marchi I realize that complaints are not often enabled and shown, but if we emit them, I think they should include enough context to help a user who wishes to dig in the problem to pinpoint where the problem happened exactly. For this reason, change the complaints in mapped_debug_names_reader::scan_one_entry to include precisions regarding which index entry is problematic exactly. I am not attached to the particular format. I thought that because this is extra contextual information, I would put it in the square brackets at the end (which only shows the module name currently). However, it would be nice to converge towards a format that we could replicate elsewhere in the DWARF reader, so feel free to chime in. I factored out the formatting to a separate function, because I will be adding more of these in a subsequent patch, and it's easier to have the formatting centralized. I have not touched the "Unsupported .debug_names form" warning. If that comes up, we probably don't care about the specific index entry, we just need to implement support for that form. Change-Id: Id68a0ae406a3ca620408576b81f893f05c7e3df2 --- gdb/dwarf2/read-debug-names.c | 43 ++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index 6ac65d101b16..487e2ea87edb 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -155,6 +155,25 @@ struct mapped_debug_names_reader std::vector> all_entries; }; +/* Emit a complaint about a specific index entry. */ + +static void ATTRIBUTE_PRINTF (4, 5) +complain_about_index_entry (bfd *abfd, const char *name, + ptrdiff_t offset_in_entry_pool, const char *fmt, + ...) +{ + va_list ap; + va_start (ap, fmt); + std::string msg = string_vprintf (fmt, ap); + va_end (ap); + + msg += string_printf (_(" [in module %s, index entry for name %s," + " entry pool offset 0x%tx]"), + bfd_get_filename (abfd), name, offset_in_entry_pool); + + complaint ("%s", msg.c_str ()); +} + /* 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. */ @@ -180,9 +199,11 @@ mapped_debug_names_reader::scan_one_entry (const char *name, 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)); + complain_about_index_entry (abfd, name, offset_in_entry_pool, + _("Wrong .debug_names abbrev code %s"), + pulongest (abbrev)); + /* We can't go past this entry because we don't know its size, stop + reading this entry chain. */ return nullptr; } @@ -264,10 +285,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name, /* Don't crash on bad data. */ if (ull >= this->comp_units.size ()) { - complaint (_(".debug_names entry has bad CU index %s" - " [in module %s]"), - pulongest (ull), - bfd_get_filename (abfd)); + complain_about_index_entry + (abfd, name, offset_in_entry_pool, + _(".debug_names entry has bad CU index %s"), + pulongest (ull)); continue; } @@ -279,10 +300,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name, /* Don't crash on bad data. */ if (ull >= this->type_units.size ()) { - complaint (_(".debug_names entry has bad TU index %s" - " [in module %s]"), - pulongest (ull), - bfd_get_filename (abfd)); + complain_about_index_entry + (abfd, name, offset_in_entry_pool, + _(".debug_names entry has bad TU index %s"), + pulongest (ull)); continue; } From patchwork Mon Mar 16 23:19:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 131837 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 1A3B44B9DB74 for ; Mon, 16 Mar 2026 23:23:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1A3B44B9DB74 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 X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:22:54 +0000 X-Spam-Status: No, score=-3034.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@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" }]