From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id oXN8LpGQuGmykywAWB0awg (envelope-from ) for ; Mon, 16 Mar 2026 19:21:53 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=RRBrhydU; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id B694A1E0BC; Mon, 16 Mar 2026 19:21:53 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id C04961E0A2 for ; Mon, 16 Mar 2026 19:21:51 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 4EF294C900FC for ; Mon, 16 Mar 2026 23:21:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4EF294C900FC 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 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 Content-Transfer-Encoding: 8bit X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Mon, 16 Mar 2026 23:20:51 +0000 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Simon Marchi 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 --- 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 -- 2.53.0