From: simon.marchi@polymtl.ca
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@efficios.com>
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 [thread overview]
Message-ID: <20260316232042.368080-2-simon.marchi@polymtl.ca> (raw)
In-Reply-To: <20260316232042.368080-1-simon.marchi@polymtl.ca>
From: Simon Marchi <simon.marchi@efficios.com>
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><b>: 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: <unknown>
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
+ <http://www.gnu.org/licenses/>. */
+
+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 <http://www.gnu.org/licenses/>.
+
+# 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
next prev parent reply other threads:[~2026-03-16 23:21 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-16 23:19 [PATCH 0/8] Handle foreign type units in .debug_names simon.marchi
2026-03-16 23:19 ` simon.marchi [this message]
2026-03-17 12:57 ` [PATCH 1/8] gdb/dwarf: refuse to produce .gdb_index when skeletonless type units are present Eli Zaretskii
2026-03-16 23:19 ` [PATCH 2/8] gdb/dwarf: move dwo_unit and dwo_file to read.h simon.marchi
2026-03-16 23:19 ` [PATCH 3/8] gdb/dwarf: move dwarf2_cu::section to cu.c simon.marchi
2026-03-16 23:19 ` [PATCH 4/8] gdb/dwarf: add dwo_file::find_tus simon.marchi
2026-03-16 23:19 ` [PATCH 5/8] gdb/dwarf: generate foreign type units in .debug_names simon.marchi
2026-03-16 23:19 ` [PATCH 6/8] gdb/dwarf: add debug output in read-debug-names.c simon.marchi
2026-03-16 23:19 ` [PATCH 7/8] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry simon.marchi
2026-03-16 23:19 ` [PATCH 8/8] gdb/dwarf: read foreign type units simon.marchi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260316232042.368080-2-simon.marchi@polymtl.ca \
--to=simon.marchi@polymtl.ca \
--cc=gdb-patches@sourceware.org \
--cc=simon.marchi@efficios.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox