* [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile
@ 2026-10-05 5:25 simon.marchi
2026-10-05 5:25 ` [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp simon.marchi
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: simon.marchi @ 2026-10-05 5:25 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
The "set compiler" line looks like a copy-paste error from the if just
above. If we compare this to the Go handler, then it seems clear that the
intent was to write:
if {[board_info $dest exists rustcompiler]} {
set compiler [target_info rustcompiler]
} else {
set compiler [find_rustc]
}
The name rustcompiler follows the convention used by other languages (e.g.
"gocompiler", "dcompiler", etc).
This also matches what was sent upstream to DejaGnu in commit c8beff4a0843
("Add Rust support to default_target_compile").
Change-Id: Ic21b397ff6a2afbcda72901203ff7834c752ff3f
---
gdb/testsuite/lib/future.exp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp
index 44fc17b3f004..a5444df66c87 100644
--- a/gdb/testsuite/lib/future.exp
+++ b/gdb/testsuite/lib/future.exp
@@ -308,8 +308,8 @@ proc gdb_default_target_compile_1 {source destfile type options} {
if {[board_info $dest exists rustflags]} {
append add_flags " [target_info rustflags]"
}
- if {[board_info $dest exists rustflags]} {
- set compiler [target_info rustflags]
+ if {[board_info $dest exists rustcompiler]} {
+ set compiler [target_info rustcompiler]
} else {
set compiler [find_rustc]
}
base-commit: 2ce6c3190773eb22f467446c4fdc7edb123ce82f
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp 2026-10-05 5:25 [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile simon.marchi @ 2026-10-05 5:25 ` simon.marchi 2026-10-06 10:56 ` Andrew Burgess 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi 2026-10-06 10:56 ` [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile Andrew Burgess 2 siblings, 1 reply; 20+ messages in thread From: simon.marchi @ 2026-10-05 5:25 UTC (permalink / raw) To: gdb-patches; +Cc: Simon Marchi From: Simon Marchi <simon.marchi@polymtl.ca> I noticed that the gdb.rust/* executables did not contain a .gdb_index when run with the cc-with-gdb-index board. This is because cc-with-tweaks.exp does not wrap RUSTC_FOR_TARGET yet. Wrap RUSTC_FOR_TARGET too, the same way as for the other compilers. I can now see a .gdb_index in the produced executable. I do not see a difference in the test results, which I guess is a good sign. Change-Id: If950c2c4f851b93142e6e7c068444043feff5612 --- gdb/testsuite/boards/cc-with-tweaks.exp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gdb/testsuite/boards/cc-with-tweaks.exp b/gdb/testsuite/boards/cc-with-tweaks.exp index 1f981414b396..08b4a9e4da48 100644 --- a/gdb/testsuite/boards/cc-with-tweaks.exp +++ b/gdb/testsuite/boards/cc-with-tweaks.exp @@ -44,6 +44,7 @@ set found_gcc [find_gcc] set found_gxx [find_g++] set found_gnatmake [find_gnatmake] set found_f90 [find_gfortran] +set found_rustc [find_rustc] set_board_info compiler "$found_gcc" set contrib_dir [file normalize $srcdir/../contrib] @@ -63,6 +64,10 @@ if ![info exists F90_FOR_TARGET] { set F90_FOR_TARGET "$found_f90" } set F90_FOR_TARGET "$contrib_dir/cc-with-tweaks.sh $CC_WITH_TWEAKS_FLAGS $F90_FOR_TARGET" +if ![info exists RUSTC_FOR_TARGET] { + set RUSTC_FOR_TARGET "$found_rustc" +} +set RUSTC_FOR_TARGET "$contrib_dir/cc-with-tweaks.sh $CC_WITH_TWEAKS_FLAGS $RUSTC_FOR_TARGET" set env(GDB) \ [cached_file gdb.sh "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS \"\$@\"" 1] -- 2.55.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp 2026-10-05 5:25 ` [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp simon.marchi @ 2026-10-06 10:56 ` Andrew Burgess 0 siblings, 0 replies; 20+ messages in thread From: Andrew Burgess @ 2026-10-06 10:56 UTC (permalink / raw) To: simon.marchi, gdb-patches; +Cc: Simon Marchi simon.marchi@polymtl.ca writes: > From: Simon Marchi <simon.marchi@polymtl.ca> > > I noticed that the gdb.rust/* executables did not contain a .gdb_index when > run with the cc-with-gdb-index board. This is because cc-with-tweaks.exp > does not wrap RUSTC_FOR_TARGET yet. > > Wrap RUSTC_FOR_TARGET too, the same way as for the other compilers. > > I can now see a .gdb_index in the produced executable. I do not see a > difference in the test results, which I guess is a good sign. LGTM. Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > > Change-Id: If950c2c4f851b93142e6e7c068444043feff5612 > --- > gdb/testsuite/boards/cc-with-tweaks.exp | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/gdb/testsuite/boards/cc-with-tweaks.exp b/gdb/testsuite/boards/cc-with-tweaks.exp > index 1f981414b396..08b4a9e4da48 100644 > --- a/gdb/testsuite/boards/cc-with-tweaks.exp > +++ b/gdb/testsuite/boards/cc-with-tweaks.exp > @@ -44,6 +44,7 @@ set found_gcc [find_gcc] > set found_gxx [find_g++] > set found_gnatmake [find_gnatmake] > set found_f90 [find_gfortran] > +set found_rustc [find_rustc] > set_board_info compiler "$found_gcc" > > set contrib_dir [file normalize $srcdir/../contrib] > @@ -63,6 +64,10 @@ if ![info exists F90_FOR_TARGET] { > set F90_FOR_TARGET "$found_f90" > } > set F90_FOR_TARGET "$contrib_dir/cc-with-tweaks.sh $CC_WITH_TWEAKS_FLAGS $F90_FOR_TARGET" > +if ![info exists RUSTC_FOR_TARGET] { > + set RUSTC_FOR_TARGET "$found_rustc" > +} > +set RUSTC_FOR_TARGET "$contrib_dir/cc-with-tweaks.sh $CC_WITH_TWEAKS_FLAGS $RUSTC_FOR_TARGET" > > set env(GDB) \ > [cached_file gdb.sh "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS \"\$@\"" 1] > -- > 2.55.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-05 5:25 [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile simon.marchi 2026-10-05 5:25 ` [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp simon.marchi @ 2026-10-05 5:25 ` simon.marchi 2026-10-06 11:01 ` Andrew Burgess ` (4 more replies) 2026-10-06 10:56 ` [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile Andrew Burgess 2 siblings, 5 replies; 20+ messages in thread From: simon.marchi @ 2026-10-05 5:25 UTC (permalink / raw) To: gdb-patches; +Cc: Simon Marchi From: Simon Marchi <simon.marchi@polymtl.ca> PR 34707 reports that loading the separate debug file of /usr/lib/libglycin-2.so from Arch Linux crashes GDB (when built with -D_GLIBCXX_DEBUG): $ ./gdb -q -nx --data-directory data-directory $(DEBUGINFOD_URLS=https://debuginfod.archlinux.org debuginfod-find debuginfo /usr/lib/libglycin-2.so) /usr/include/c++/16/string_view:291: ... Assertion 'this->_M_len > 0' failed. This catches a case of calling string_view::back() when the string_view is empty. The file in question contains a .gdb_index with some Rust symbol names that are mangled with the legacy mangling style. The legacy mangling style was the default one until the Rust 1.97 release in July 2026, so it's still very much in use, even though it's called "legacy". Source: https://blog.rust-lang.org/2026/07/09/Rust-1.97.0/ An example of a problematic symbol is: _ZN4core3ptr271drop_in_place$LT$core..iter..adapters..filter..Filter$LT$alloc..vec..into_iter..IntoIter$LT$core..net..socket_addr..SocketAddr$GT$$C$zbus..address..transport..tcp..Tcp..connect..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$$GT$$GT$17h57c03bb201e55682E Method mapped_gdb_index::build_name_components attempts to split the demangled fully-qualified symbol names into parts (it does not know in advance whether a symbol name is a mangled (linkage) name or a demangled one). Since this name does not match the C++ and Ada special cases, it falls into the DOT_STYLE else: components = split_name (name, split_style::DOT_STYLE); Because of the consecutive dots in the symbol name, `components` (an std::vector<std::string_view>) contains some empty components. Some code later calls .back() on one of those empty string_views, which is invalid. It seems like it's only caught when gdb is built with _GLIBCXX_DEBUG / _GLIBCXX_ASSERTIONS, which apparently is the case for the official Arch Linux packages. I can't think of a pretty way to fix this. Since we don't have a flag that tells us whether a name is a linkage name or not, we can only make some educated guesses. This patch fixes the problem by looking for empty components in the vector. If there are, we assume that we are probably looking at a Rust legacy mangled symbol name, or at the very least not a fully qualified demangled Go (or whatever) symbol. The name is then treated like other names without separators (a single component with an unknown language). This is a regression introduced by commit 486bc5ac812f ("Rewrite the .gdb_index reader"). Before that, the reader only split names on "::" and "__". This is new in the GDB 18.1 release, so this patch would be a candidate for the gdb-18-branch. Add a gdb.dwarf2 test that builds a Rust CU with such a linkage name, adds an index using gdb-add-index, and verifies that GDB can load it and find the function. The test is done with both .gdb_index and .debug_names, although only the .gdb_index reader was affected. Like the original crash, the test only fails without the fix if GDB is built with libstdc++ assertions. One last thing, I don't know if it's expected for Rust mangled names to appear in the index. For C++, index-write.c does not write them: else if (entry->lang == language_cplus && (entry->flags & IS_LINKAGE) != 0) { /* GDB never put C++ linkage names into .gdb_index. The theory here is that a linkage name will normally be in the minimal symbols anyway, so including it in the index is usually redundant -- and the cases where it would not be redundant are rare and not worth supporting. */ continue; } Perhaps this is the real root of the problem: maybe those names should not have been in the index in the first place, if the same rationale applies to Rust. We could make that change, but even if we do, there are already indexes out there with those mangled names, so we have to handle them. Change-Id: I2c346dfba25edecda970af0726f9d77216838cbd Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34707 --- gdb/dwarf2/read-gdb-index.c | 21 ++++ .../gdb-index-rust-legacy-linkage-name.c | 31 ++++++ .../gdb-index-rust-legacy-linkage-name.exp | 98 +++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index 324984c0de2d..cb2678d4ea22 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -203,6 +203,15 @@ struct mapped_gdb_index void build_name_components (dwarf2_per_objfile *per_objfile); }; +/* Return true if any of COMPONENTS is empty. */ + +static bool +has_empty_components (const std::vector<std::string_view> &components) +{ + return std::any_of (components.begin (), components.end (), + [] (std::string_view c) { return c.empty (); }); +} + /* See declaration. */ void @@ -251,6 +260,18 @@ mapped_gdb_index::build_name_components (dwarf2_per_objfile *per_objfile) else { components = split_name (name, split_style::DOT_STYLE); + + /* Some names contain dots but are not qualified names, for + example Rust "legacy" mangled names such as + "_ZN4core3ptr80drop_in_place$LT$alloc..vec..drain..Drain...". + If splitting produced an empty component, assume it's not a + qualified name and don't split it. */ + if (has_empty_components (components)) + { + components.clear (); + components.emplace_back (name); + } + /* Mark ordinary names as having an unknown language. This is a hack to avoid problems with some Ada names. */ lang = (components.size () == 1) ? language_unknown : language_go; diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c new file mode 100644 index 000000000000..0d687040a800 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c @@ -0,0 +1,31 @@ +/* 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/>. */ + +void +func (void) +{ + asm ("func_label: .globl func_label"); +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + + func (); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp new file mode 100644 index 000000000000..b995e986355f --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp @@ -0,0 +1,98 @@ +# 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 reading an index containing a Rust "legacy" mangled linkage +# name. These names contain ".." sequences, which the .gdb_index +# reader used to split into empty components, leading to a crash. +# The .debug_names index is tested too, for completeness. +# +# Note that the crash only happened with libstdc++ assertions enabled +# (e.g. with -D_GLIBCXX_DEBUG). Otherwise, it was silent undefined +# behavior. + +load_lib dwarf.exp + +require dwarf2_support +require !readnow + +standard_testfile .c -dw.S + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + cu {} { + DW_TAG_compile_unit { + DW_AT_language @DW_LANG_Rust + DW_AT_name lib.rs + } { + DW_TAG_namespace { + DW_AT_name foo + } { + # The linkage name demangles to foo::bar<foo::Baz>. + DW_TAG_subprogram { + DW_AT_name "bar<foo::Baz>" + DW_AT_linkage_name \ + "_ZN3foo19bar\$LT\$foo..Baz\$GT\$17h0123456789abcdefE" + DW_AT_external 1 DW_FORM_flag + MACRO_AT_range {func} + } + } + } + } +} + +if { [build_executable "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] == -1 } { + return +} + +# For simplicity, skip the test if the executable already has an index (e.g. +# with the cc-with-gdb-index board). The test will be covered when running +# with the unix board. +clean_restart $testfile +if { [get_index_type $binfile "check index type"] ne "cooked" } { + unsupported "executable already has an index" + return +} + +foreach_with_prefix index_type { gdb_index debug_names } { + set testfile_with_index ${testfile}-${index_type} + set binfile_with_index [standard_output_file $testfile_with_index] + remote_exec build "cp $binfile $binfile_with_index" + + if { $index_type eq "gdb_index" } { + set style "" + } else { + set style "-dwarf-5" + } + + if { [add_gdb_index $binfile_with_index $style] != 1 } { + untested "error adding index" + continue + } + + clean_restart $testfile_with_index + gdb_assert { [have_index $binfile_with_index] eq $index_type } \ + "index used" + + # Check that the function can be found. + set re "^foo::bar<foo::Baz>$" + gdb_test "info functions $re" \ + [multi_line \ + [string_to_regexp \ + "All functions matching regular expression \"$re\":"] \ + "" \ + "File lib.rs:" \ + "\[^\r\n\]*[string_to_regexp "fn foo::bar<foo::Baz>();"]"] +} -- 2.55.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi @ 2026-10-06 11:01 ` Andrew Burgess 2026-10-06 13:39 ` Simon Marchi 2026-10-14 13:01 ` Tom Tromey ` (3 subsequent siblings) 4 siblings, 1 reply; 20+ messages in thread From: Andrew Burgess @ 2026-10-06 11:01 UTC (permalink / raw) To: simon.marchi, gdb-patches; +Cc: Simon Marchi simon.marchi@polymtl.ca writes: > From: Simon Marchi <simon.marchi@polymtl.ca> > > PR 34707 reports that loading the separate debug file of > /usr/lib/libglycin-2.so from Arch Linux crashes GDB (when built with > -D_GLIBCXX_DEBUG): > > $ ./gdb -q -nx --data-directory data-directory $(DEBUGINFOD_URLS=https://debuginfod.archlinux.org debuginfod-find debuginfo /usr/lib/libglycin-2.so) > /usr/include/c++/16/string_view:291: ... Assertion 'this->_M_len > 0' failed. > > This catches a case of calling string_view::back() when the string_view is > empty. > > The file in question contains a .gdb_index with some Rust symbol names that are > mangled with the legacy mangling style. > > The legacy mangling style was the default one until the Rust 1.97 release > in July 2026, so it's still very much in use, even though it's called > "legacy". > > Source: https://blog.rust-lang.org/2026/07/09/Rust-1.97.0/ > > An example of a problematic symbol is: > > _ZN4core3ptr271drop_in_place$LT$core..iter..adapters..filter..Filter$LT$alloc..vec..into_iter..IntoIter$LT$core..net..socket_addr..SocketAddr$GT$$C$zbus..address..transport..tcp..Tcp..connect..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$$GT$$GT$17h57c03bb201e55682E > > Method mapped_gdb_index::build_name_components attempts to split the > demangled fully-qualified symbol names into parts (it does not know in > advance whether a symbol name is a mangled (linkage) name or a demangled > one). > > Since this name does not match the C++ and Ada special cases, it falls into > the DOT_STYLE else: > > components = split_name (name, split_style::DOT_STYLE); > > Because of the consecutive dots in the symbol name, `components` (an > std::vector<std::string_view>) contains some empty components. Some code > later calls .back() on one of those empty string_views, which is invalid. > > It seems like it's only caught when gdb is built with _GLIBCXX_DEBUG / > _GLIBCXX_ASSERTIONS, which apparently is the case for the official Arch > Linux packages. > > I can't think of a pretty way to fix this. Since we don't have a flag that > tells us whether a name is a linkage name or not, we can only make some > educated guesses. > > This patch fixes the problem by looking for empty components in the vector. > If there are, we assume that we are probably looking at a Rust legacy > mangled symbol name, or at the very least not a fully qualified demangled > Go (or whatever) symbol. The name is then treated like other names without > separators (a single component with an unknown language). > > This is a regression introduced by commit 486bc5ac812f ("Rewrite the > .gdb_index reader"). Before that, the reader only split names on "::" and > "__". This is new in the GDB 18.1 release, so this patch would be a > candidate for the gdb-18-branch. > > Add a gdb.dwarf2 test that builds a Rust CU with such a linkage name, adds > an index using gdb-add-index, and verifies that GDB can load it and find > the function. The test is done with both .gdb_index and .debug_names, > although only the .gdb_index reader was affected. Like the original > crash, the test only fails without the fix if GDB is built with libstdc++ > assertions. > > One last thing, I don't know if it's expected for Rust mangled names to > appear in the index. For C++, index-write.c does not write them: > > else if (entry->lang == language_cplus > && (entry->flags & IS_LINKAGE) != 0) > { > /* GDB never put C++ linkage names into .gdb_index. The > theory here is that a linkage name will normally be in > the minimal symbols anyway, so including it in the index > is usually redundant -- and the cases where it would not > be redundant are rare and not worth supporting. */ > continue; > } > > Perhaps this is the real root of the problem: maybe those names should not > have been in the index in the first place, if the same rationale applies to > Rust. We could make that change, but even if we do, there are already > indexes out there with those mangled names, so we have to handle them. I agree being defensive on the read side is a good idea. Even if these names were not intended to be in the index, and GDB had never emitted them, there's nothing to stop some other tool generating an index, and that tool might emit these names. Conclusion: GDB should treat the index as untrusted input. I actually started looking at making some fixes in this area -- not this particular fix, but others in this area -- the fixes were easy, but writing tests was taking some time. It's a work in progress. I agree that this could be merged to master and gdb-18-branch. Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-06 11:01 ` Andrew Burgess @ 2026-10-06 13:39 ` Simon Marchi 0 siblings, 0 replies; 20+ messages in thread From: Simon Marchi @ 2026-10-06 13:39 UTC (permalink / raw) To: Andrew Burgess, gdb-patches On 10/6/26 7:01 AM, Andrew Burgess wrote: > simon.marchi@polymtl.ca writes: > >> From: Simon Marchi <simon.marchi@polymtl.ca> >> >> PR 34707 reports that loading the separate debug file of >> /usr/lib/libglycin-2.so from Arch Linux crashes GDB (when built with >> -D_GLIBCXX_DEBUG): >> >> $ ./gdb -q -nx --data-directory data-directory $(DEBUGINFOD_URLS=https://debuginfod.archlinux.org debuginfod-find debuginfo /usr/lib/libglycin-2.so) >> /usr/include/c++/16/string_view:291: ... Assertion 'this->_M_len > 0' failed. >> >> This catches a case of calling string_view::back() when the string_view is >> empty. >> >> The file in question contains a .gdb_index with some Rust symbol names that are >> mangled with the legacy mangling style. >> >> The legacy mangling style was the default one until the Rust 1.97 release >> in July 2026, so it's still very much in use, even though it's called >> "legacy". >> >> Source: https://blog.rust-lang.org/2026/07/09/Rust-1.97.0/ >> >> An example of a problematic symbol is: >> >> _ZN4core3ptr271drop_in_place$LT$core..iter..adapters..filter..Filter$LT$alloc..vec..into_iter..IntoIter$LT$core..net..socket_addr..SocketAddr$GT$$C$zbus..address..transport..tcp..Tcp..connect..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$$GT$$GT$17h57c03bb201e55682E >> >> Method mapped_gdb_index::build_name_components attempts to split the >> demangled fully-qualified symbol names into parts (it does not know in >> advance whether a symbol name is a mangled (linkage) name or a demangled >> one). >> >> Since this name does not match the C++ and Ada special cases, it falls into >> the DOT_STYLE else: >> >> components = split_name (name, split_style::DOT_STYLE); >> >> Because of the consecutive dots in the symbol name, `components` (an >> std::vector<std::string_view>) contains some empty components. Some code >> later calls .back() on one of those empty string_views, which is invalid. >> >> It seems like it's only caught when gdb is built with _GLIBCXX_DEBUG / >> _GLIBCXX_ASSERTIONS, which apparently is the case for the official Arch >> Linux packages. >> >> I can't think of a pretty way to fix this. Since we don't have a flag that >> tells us whether a name is a linkage name or not, we can only make some >> educated guesses. >> >> This patch fixes the problem by looking for empty components in the vector. >> If there are, we assume that we are probably looking at a Rust legacy >> mangled symbol name, or at the very least not a fully qualified demangled >> Go (or whatever) symbol. The name is then treated like other names without >> separators (a single component with an unknown language). >> >> This is a regression introduced by commit 486bc5ac812f ("Rewrite the >> .gdb_index reader"). Before that, the reader only split names on "::" and >> "__". This is new in the GDB 18.1 release, so this patch would be a >> candidate for the gdb-18-branch. >> >> Add a gdb.dwarf2 test that builds a Rust CU with such a linkage name, adds >> an index using gdb-add-index, and verifies that GDB can load it and find >> the function. The test is done with both .gdb_index and .debug_names, >> although only the .gdb_index reader was affected. Like the original >> crash, the test only fails without the fix if GDB is built with libstdc++ >> assertions. >> >> One last thing, I don't know if it's expected for Rust mangled names to >> appear in the index. For C++, index-write.c does not write them: >> >> else if (entry->lang == language_cplus >> && (entry->flags & IS_LINKAGE) != 0) >> { >> /* GDB never put C++ linkage names into .gdb_index. The >> theory here is that a linkage name will normally be in >> the minimal symbols anyway, so including it in the index >> is usually redundant -- and the cases where it would not >> be redundant are rare and not worth supporting. */ >> continue; >> } >> >> Perhaps this is the real root of the problem: maybe those names should not >> have been in the index in the first place, if the same rationale applies to >> Rust. We could make that change, but even if we do, there are already >> indexes out there with those mangled names, so we have to handle them. > > I agree being defensive on the read side is a good idea. Even if these > names were not intended to be in the index, and GDB had never emitted > them, there's nothing to stop some other tool generating an index, and > that tool might emit these names. Conclusion: GDB should treat the > index as untrusted input. > > I actually started looking at making some fixes in this area -- not this > particular fix, but others in this area -- the fixes were easy, but > writing tests was taking some time. It's a work in progress. > > I agree that this could be merged to master and gdb-18-branch. > > Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks a lot for the reviews. I will give Tom Tromey some time if he wants to look at this, since DWARF and Rust are some of his areas of expertise. Simon ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi 2026-10-06 11:01 ` Andrew Burgess @ 2026-10-14 13:01 ` Tom Tromey 2026-10-14 13:22 ` Simon Marchi 2026-10-14 16:46 ` [PATCH v2 0/2] Fix " Simon Marchi ` (2 subsequent siblings) 4 siblings, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-10-14 13:01 UTC (permalink / raw) To: simon.marchi; +Cc: gdb-patches >>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes: Simon> Since this name does not match the C++ and Ada special cases, it falls into Simon> the DOT_STYLE else: Simon> components = split_name (name, split_style::DOT_STYLE); Simon> Because of the consecutive dots in the symbol name, `components` (an Simon> std::vector<std::string_view>) contains some empty components. Some code Simon> later calls .back() on one of those empty string_views, which is invalid. It seems to me that a "dot-style" name could never have two consecutive dots. So, how about changing split_name to bail out in this case? I see I didn't write any unit tests for split-name.c, which seems like a real oversight. Simon> I can't think of a pretty way to fix this. Since we don't have a flag that Simon> tells us whether a name is a linkage name or not, we can only make some Simon> educated guesses. Yeah. I think we've resisted doing this. But maybe we should just give in and look for a leading _Z/_R. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-14 13:01 ` Tom Tromey @ 2026-10-14 13:22 ` Simon Marchi 2026-10-14 14:59 ` Tom Tromey 2026-10-14 16:13 ` Tom Tromey 0 siblings, 2 replies; 20+ messages in thread From: Simon Marchi @ 2026-10-14 13:22 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 10/14/26 9:01 AM, Tom Tromey wrote: >>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes: > > Simon> Since this name does not match the C++ and Ada special cases, it falls into > Simon> the DOT_STYLE else: > Simon> components = split_name (name, split_style::DOT_STYLE); > Simon> Because of the consecutive dots in the symbol name, `components` (an > Simon> std::vector<std::string_view>) contains some empty components. Some code > Simon> later calls .back() on one of those empty string_views, which is invalid. > > It seems to me that a "dot-style" name could never have two consecutive > dots. So, how about changing split_name to bail out in this case? Yes it would make sense to do the check directly in split_name, instead of doing it in just one caller. I'll make a v2 for that, but do you think patches 1 and 2 could go in as-is? > I see I didn't write any unit tests for split-name.c, which seems like a > real oversight. > > Simon> I can't think of a pretty way to fix this. Since we don't have a flag that > Simon> tells us whether a name is a linkage name or not, we can only make some > Simon> educated guesses. > > Yeah. I think we've resisted doing this. But maybe we should just give > in and look for a leading _Z/_R. But that would could false positives, right? Like if I have classes _Zebra or _Random. Simon ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-14 13:22 ` Simon Marchi @ 2026-10-14 14:59 ` Tom Tromey 2026-10-14 16:13 ` Tom Tromey 1 sibling, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-10-14 14:59 UTC (permalink / raw) To: Simon Marchi; +Cc: Tom Tromey, gdb-patches >> Yeah. I think we've resisted doing this. But maybe we should just give >> in and look for a leading _Z/_R. Simon> But that would could false positives, right? Yeah, it's easy to write a confounding case in C. It may even happen in the wild if you're trying to interface C and C++. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-14 13:22 ` Simon Marchi 2026-10-14 14:59 ` Tom Tromey @ 2026-10-14 16:13 ` Tom Tromey 2026-10-14 16:26 ` Simon Marchi 1 sibling, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-10-14 16:13 UTC (permalink / raw) To: Simon Marchi; +Cc: Tom Tromey, gdb-patches Simon> I'll make a v2 for that, but do you think patches 1 and 2 could go in Simon> as-is? Yes. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-14 16:13 ` Tom Tromey @ 2026-10-14 16:26 ` Simon Marchi 0 siblings, 0 replies; 20+ messages in thread From: Simon Marchi @ 2026-10-14 16:26 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 10/14/26 12:13 PM, Tom Tromey wrote: > Simon> I'll make a v2 for that, but do you think patches 1 and 2 could go in > Simon> as-is? > > Yes. > > Tom Thanks, pushed both. Simon ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/2] Fix crash when .gdb_index contains Rust legacy mangled names 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi 2026-10-06 11:01 ` Andrew Burgess 2026-10-14 13:01 ` Tom Tromey @ 2026-10-14 16:46 ` Simon Marchi 2026-10-15 15:47 ` Tom Tromey 2026-10-14 16:46 ` [PATCH v2 1/2] gdb: add selftests for split_name Simon Marchi 2026-10-14 16:46 ` [PATCH v2 2/2] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names Simon Marchi 4 siblings, 1 reply; 20+ messages in thread From: Simon Marchi @ 2026-10-14 16:46 UTC (permalink / raw) To: gcc-patches, gdb-patches; +Cc: Simon Marchi This is v2 of: https://inbox.sourceware.org/gdb-patches/878q4bumgh.fsf@redhat.com/T/#mf4974aff377310db2cf24c34224fb8e5a174a905 The changes are: - do the fix in split_name: in DOT_STYLE mode, refuse to split a name that would lead to an empty component - add a preparatory patch that adds selftests for split_name Simon Marchi (2): gdb: add selftests for split_name gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names gdb/split-name.c | 199 ++++++++++++++++++ .../gdb-index-rust-legacy-linkage-name.c | 29 +++ .../gdb-index-rust-legacy-linkage-name.exp | 98 +++++++++ 3 files changed, 326 insertions(+) create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp base-commit: 91a47f3ae9d6d494b0c794a3ccd81b242bd15b7d -- 2.56.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 0/2] Fix crash when .gdb_index contains Rust legacy mangled names 2026-10-14 16:46 ` [PATCH v2 0/2] Fix " Simon Marchi @ 2026-10-15 15:47 ` Tom Tromey 2026-10-18 3:45 ` Simon Marchi 0 siblings, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-10-15 15:47 UTC (permalink / raw) To: Simon Marchi; +Cc: gcc-patches, gdb-patches >>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes: Simon> This is v2 of: Simon> https://inbox.sourceware.org/gdb-patches/878q4bumgh.fsf@redhat.com/T/#mf4974aff377310db2cf24c34224fb8e5a174a905 Simon> The changes are: Simon> - do the fix in split_name: in DOT_STYLE mode, refuse to split a name Simon> that would lead to an empty component Simon> - add a preparatory patch that adds selftests for split_name Thank you. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 0/2] Fix crash when .gdb_index contains Rust legacy mangled names 2026-10-15 15:47 ` Tom Tromey @ 2026-10-18 3:45 ` Simon Marchi 0 siblings, 0 replies; 20+ messages in thread From: Simon Marchi @ 2026-10-18 3:45 UTC (permalink / raw) To: Tom Tromey, Simon Marchi; +Cc: gcc-patches, gdb-patches On 2026-10-15 11:47, Tom Tromey wrote: >>>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes: > > Simon> This is v2 of: > Simon> https://inbox.sourceware.org/gdb-patches/878q4bumgh.fsf@redhat.com/T/#mf4974aff377310db2cf24c34224fb8e5a174a905 > > Simon> The changes are: > > Simon> - do the fix in split_name: in DOT_STYLE mode, refuse to split a name > Simon> that would lead to an empty component > Simon> - add a preparatory patch that adds selftests for split_name > > Thank you. > Approved-By: Tom Tromey <tom@tromey.com> Thanks, pushed to both master and gdb-18-branch. Simon ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/2] gdb: add selftests for split_name 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi ` (2 preceding siblings ...) 2026-10-14 16:46 ` [PATCH v2 0/2] Fix " Simon Marchi @ 2026-10-14 16:46 ` Simon Marchi 2026-10-15 15:48 ` Tom Tromey 2026-10-14 16:46 ` [PATCH v2 2/2] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names Simon Marchi 4 siblings, 1 reply; 20+ messages in thread From: Simon Marchi @ 2026-10-14 16:46 UTC (permalink / raw) To: gcc-patches, gdb-patches; +Cc: Simon Marchi Add selftests for split_name, covering all splitting styles. Some output is questionable, but the goal is to document the current behavior. Change-Id: Ib31a6f01762e84eefff647e0c141975ea23d469f --- gdb/split-name.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/gdb/split-name.c b/gdb/split-name.c index bc145909c2e8..cb740a968d49 100644 --- a/gdb/split-name.c +++ b/gdb/split-name.c @@ -19,6 +19,8 @@ #include "split-name.h" #include "cp-support.h" +#include "gdbsupport/enumerate.h" +#include "gdbsupport/selftest.h" /* See split-name.h. */ @@ -64,3 +66,190 @@ split_name (const char *name, split_style style) result.emplace_back (&name[previous_len]); return result; } + +#if GDB_SELF_TEST + +namespace selftests { + +/* Return a string representation of COMPONENTS, for debugging. */ + +static std::string +components_to_string (const std::vector<std::string_view> &components) +{ + std::string str = "{"; + + for (auto [i, component] : gdb::ranges::views::enumerate (components)) + { + if (i > 0) + str += ","; + + str += " \""; + str += component; + str += "\""; + } + + str += " }"; + return str; +} + +/* Check that splitting NAME using STYLE gives EXPECTED. */ + +static void +check_split_name (const char *name, split_style style, + const std::vector<std::string_view> &expected) +{ + std::vector<std::string_view> result = split_name (name, style); + + if (result != expected) + debug_printf ("split_name (\"%s\"): got %s, expected %s\n", name, + components_to_string (result).c_str (), + components_to_string (expected).c_str ()); + + SELF_CHECK (result == expected); +} + +static void +test_split_name_none () +{ + /* No splitting is ever done. */ + check_split_name ("", split_style::NONE, { "" }); + check_split_name ("foo", split_style::NONE, { "foo" }); + check_split_name ("a::b", split_style::NONE, { "a::b" }); + check_split_name ("a.b", split_style::NONE, { "a.b" }); + check_split_name ("a..b", split_style::NONE, { "a..b" }); + check_split_name ("::", split_style::NONE, { "::" }); + check_split_name (".", split_style::NONE, { "." }); +} + +static void +test_split_name_cxx () +{ + /* Simple names. */ + check_split_name ("", split_style::CXX, { "" }); + check_split_name ("foo", split_style::CXX, { "foo" }); + check_split_name ("a::b", split_style::CXX, { "a", "b" }); + check_split_name ("a::b::c", split_style::CXX, { "a", "b", "c" }); + + /* Dots are not separators. */ + check_split_name ("a.b", split_style::CXX, { "a.b" }); + check_split_name ("a..b::c", split_style::CXX, { "a..b", "c" }); + + /* A single colon is not a separator. */ + check_split_name ("a:b", split_style::CXX, { "a:b" }); + check_split_name ("a:::b", split_style::CXX, { "a", ":b" }); + + /* Templates. */ + check_split_name ("a<b::c>", split_style::CXX, { "a<b::c>" }); + check_split_name ("a<b::c>::d", split_style::CXX, { "a<b::c>", "d" }); + check_split_name ("a::b<c::d, e::f>::g", split_style::CXX, + { "a", "b<c::d, e::f>", "g" }); + check_split_name ("a<b::c<d::e>>::f", split_style::CXX, + { "a<b::c<d::e>>", "f" }); + check_split_name ("std::vector<int, std::allocator<int> >::size", + split_style::CXX, + { "std", "vector<int, std::allocator<int> >", "size" }); + + /* Parentheses: function parameters, function-local names, + anonymous namespaces. */ + check_split_name ("a::b(c::d)", split_style::CXX, { "a", "b(c::d)" }); + check_split_name ("a::b(c::d) const", split_style::CXX, + { "a", "b(c::d) const" }); + check_split_name ("foo(int)::bar", split_style::CXX, + { "foo(int)", "bar" }); + check_split_name ("(anonymous namespace)::foo", split_style::CXX, + { "(anonymous namespace)", "foo" }); + check_split_name ("a::(anonymous namespace)::foo", split_style::CXX, + { "a", "(anonymous namespace)", "foo" }); + + /* Operators, which can contain characters that would otherwise + be treated specially. */ + check_split_name ("A::operator<", split_style::CXX, + { "A", "operator<" }); + check_split_name ("A::operator<<", split_style::CXX, + { "A", "operator<<" }); + check_split_name ("A::operator>", split_style::CXX, + { "A", "operator>" }); + check_split_name ("A::operator>>", split_style::CXX, + { "A", "operator>>" }); + check_split_name ("A::operator->", split_style::CXX, + { "A", "operator->" }); + check_split_name ("A::operator()", split_style::CXX, + { "A", "operator()" }); + check_split_name ("A::operator< <int>", split_style::CXX, + { "A", "operator< <int>" }); + check_split_name ("A::operator<(A const&, A const&)", split_style::CXX, + { "A", "operator<(A const&, A const&)" }); + + /* "operator" as part of an identifier is not an operator. */ + check_split_name ("cooperator::x", split_style::CXX, + { "cooperator", "x" }); + check_split_name ("operator_foo::x", split_style::CXX, + { "operator_foo", "x" }); + + /* Malformed names are returned as a single component. */ + check_split_name ("a<b::c", split_style::CXX, { "a<b::c" }); + check_split_name ("a(b::c", split_style::CXX, { "a(b::c" }); + check_split_name ("a>b::c", split_style::CXX, { "a>b::c" }); + check_split_name ("a)b::c", split_style::CXX, { "a)b::c" }); + + /* Leading, trailing or consecutive separators produce empty + components. */ + check_split_name ("::", split_style::CXX, { "", "" }); + check_split_name ("::a", split_style::CXX, { "", "a" }); + check_split_name ("a::", split_style::CXX, { "a", "" }); + check_split_name ("a::::b", split_style::CXX, { "a", "", "b" }); +} + +static void +test_split_name_dot_style () +{ + /* Simple names. */ + check_split_name ("", split_style::DOT_STYLE, { "" }); + check_split_name ("foo", split_style::DOT_STYLE, { "foo" }); + check_split_name ("a.b", split_style::DOT_STYLE, { "a", "b" }); + check_split_name ("a.b.c", split_style::DOT_STYLE, { "a", "b", "c" }); + check_split_name ("pkg.sub.func", split_style::DOT_STYLE, + { "pkg", "sub", "func" }); + + /* "::" is not a separator. */ + check_split_name ("a::b", split_style::DOT_STYLE, { "a::b" }); + check_split_name ("a::b.c", split_style::DOT_STYLE, { "a::b", "c" }); + + /* Go method names. */ + check_split_name ("main.(*T).Method", split_style::DOT_STYLE, + { "main", "(*T)", "Method" }); + + /* Ada operator names. */ + check_split_name ("pkg.\"+\"", split_style::DOT_STYLE, + { "pkg", "\"+\"" }); + + /* Leading, trailing or consecutive dots produce empty components. */ + check_split_name (".", split_style::DOT_STYLE, { "", "" }); + check_split_name ("..", split_style::DOT_STYLE, { "", "", "" }); + check_split_name (".a", split_style::DOT_STYLE, { "", "a" }); + check_split_name ("a.", split_style::DOT_STYLE, { "a", "" }); + check_split_name ("a..b", split_style::DOT_STYLE, { "a", "", "b" }); + check_split_name (".a.b", split_style::DOT_STYLE, { "", "a", "b" }); + check_split_name ("a.b.", split_style::DOT_STYLE, { "a", "b", "" }); + check_split_name ("a.b..c", split_style::DOT_STYLE, + { "a", "b", "", "c" }); + check_split_name ("a..b.c", split_style::DOT_STYLE, + { "a", "", "b", "c" }); + check_split_name ("a...b", split_style::DOT_STYLE, { "a", "", "", "b" }); +} + +} /* namespace selftests */ + +#endif /* GDB_SELF_TEST */ + +INIT_GDB_FILE (split_name) +{ +#if GDB_SELF_TEST + selftests::register_test ("split_name_none", + selftests::test_split_name_none); + selftests::register_test ("split_name_cxx", + selftests::test_split_name_cxx); + selftests::register_test ("split_name_dot_style", + selftests::test_split_name_dot_style); +#endif +} -- 2.56.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/2] gdb: add selftests for split_name 2026-10-14 16:46 ` [PATCH v2 1/2] gdb: add selftests for split_name Simon Marchi @ 2026-10-15 15:48 ` Tom Tromey 2026-10-15 18:33 ` Simon Marchi 0 siblings, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-10-15 15:48 UTC (permalink / raw) To: Simon Marchi; +Cc: gcc-patches, gdb-patches >>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes: Simon> + if (result != expected) Simon> + debug_printf ("split_name (\"%s\"): got %s, expected %s\n", name, Simon> + components_to_string (result).c_str (), Simon> + components_to_string (expected).c_str ()); Simon> + Simon> + SELF_CHECK (result == expected); I've often regretted not adding a SELF_CHECK variant that takes two arguments, and then prints them both when the comparison fails. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/2] gdb: add selftests for split_name 2026-10-15 15:48 ` Tom Tromey @ 2026-10-15 18:33 ` Simon Marchi 2026-10-16 3:15 ` Tom Tromey 0 siblings, 1 reply; 20+ messages in thread From: Simon Marchi @ 2026-10-15 18:33 UTC (permalink / raw) To: Tom Tromey, Simon Marchi; +Cc: gcc-patches, gdb-patches On 10/15/26 11:48 AM, Tom Tromey wrote: >>>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes: > > Simon> + if (result != expected) > Simon> + debug_printf ("split_name (\"%s\"): got %s, expected %s\n", name, > Simon> + components_to_string (result).c_str (), > Simon> + components_to_string (expected).c_str ()); > Simon> + > Simon> + SELF_CHECK (result == expected); > > I've often regretted not adding a SELF_CHECK variant that takes two > arguments, and then prints them both when the comparison fails. It's funny that you say that because yesterday I was thinking how our SELF_CHECK macro could do like catch2's CHECK/REQUIRE macro and automatically do that. I don't know if you have used catch2 before, but the output of a failing test is: catch2.cpp:26: FAILED: CHECK( reverse_string("hello") == "bonjour" ) with expansion: "olleh" == "bonjour" I dismissed the idea because I thought people would find it's too much complexity for nothing, but if you are interested I'd be happy to look into it. Simon ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/2] gdb: add selftests for split_name 2026-10-15 18:33 ` Simon Marchi @ 2026-10-16 3:15 ` Tom Tromey 0 siblings, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-10-16 3:15 UTC (permalink / raw) To: Simon Marchi; +Cc: Tom Tromey, Simon Marchi, gcc-patches, gdb-patches >> I've often regretted not adding a SELF_CHECK variant that takes two >> arguments, and then prints them both when the comparison fails. Simon> It's funny that you say that because yesterday I was thinking how our Simon> SELF_CHECK macro could do like catch2's CHECK/REQUIRE macro and automatically do Simon> that. I don't know if you have used catch2 before, but the output of a Simon> failing test is: I haven't used it but yeah, something like that would be good. Though IIUC check2 uses some hairy stuff to dissect the expression, not sure if we really need all that. Most of the time I think we're just comparing == or !=, or anyway just handling those would be a step forward. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 2/2] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi ` (3 preceding siblings ...) 2026-10-14 16:46 ` [PATCH v2 1/2] gdb: add selftests for split_name Simon Marchi @ 2026-10-14 16:46 ` Simon Marchi 4 siblings, 0 replies; 20+ messages in thread From: Simon Marchi @ 2026-10-14 16:46 UTC (permalink / raw) To: gcc-patches, gdb-patches; +Cc: Simon Marchi From: Simon Marchi <simon.marchi@polymtl.ca> PR 34707 reports that loading the separate debug file of /usr/lib/libglycin-2.so from Arch Linux crashes GDB (when built with -D_GLIBCXX_DEBUG): $ ./gdb -q -nx --data-directory data-directory $(DEBUGINFOD_URLS=https://debuginfod.archlinux.org debuginfod-find debuginfo /usr/lib/libglycin-2.so) /usr/include/c++/16/string_view:291: ... Assertion 'this->_M_len > 0' failed. This catches a case of calling string_view::back() when the string_view is empty. The file in question contains a .gdb_index with some Rust symbol names that are mangled with the legacy mangling style. The legacy mangling style was the default one until the Rust 1.97 release in July 2026, so it's still very much in use, even though it's called "legacy". Source: https://blog.rust-lang.org/2026/07/09/Rust-1.97.0/ An example of a problematic symbol is: _ZN4core3ptr271drop_in_place$LT$core..iter..adapters..filter..Filter$LT$alloc..vec..into_iter..IntoIter$LT$core..net..socket_addr..SocketAddr$GT$$C$zbus..address..transport..tcp..Tcp..connect..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$..$u7b$$u7b$closure$u7d$$u7d$$GT$$GT$17h57c03bb201e55682E Method mapped_gdb_index::build_name_components attempts to split the demangled fully-qualified symbol names into parts (it does not know in advance whether a symbol name is a mangled (linkage) name or a demangled one). Since this name does not match the C++ and Ada special cases, it falls into the DOT_STYLE else: components = split_name (name, split_style::DOT_STYLE); Because of the consecutive dots in the symbol name, `components` (an std::vector<std::string_view>) contains some empty components. Some code later calls .back() on one of those empty string_views, which is invalid. It seems like it's only caught when gdb is built with _GLIBCXX_DEBUG / _GLIBCXX_ASSERTIONS, which apparently is the case for the official Arch Linux packages. This patch fixes the problem in split_name: with DOT_STYLE, if splitting would produce an empty component (because of leading, trailing or consecutive dots), we assume that we are probably not looking at a fully qualified dot-separated demangled symbol name. split_name then bails out and returns the whole name as a single component. In build_name_components, the name is therefore treated like other names without separators (a single component with an unknown language). Update the split_name selftests accordingly. This is a regression introduced by commit 486bc5ac812f ("Rewrite the .gdb_index reader", 2025-03-06). Before that, the reader only split names on "::" and "__". This is new in the GDB 18.1 release, so this patch would be a candidate for the gdb-18-branch. Add a gdb.dwarf2 test that builds a Rust CU with such a linkage name, adds an index using gdb-add-index, and verifies that GDB can load it and find the function. The test is done with both .gdb_index and .debug_names, although only the .gdb_index reader was affected. Like the original crash, the test only fails without the fix if GDB is built with libstdc++ assertions. One last thing, I don't know if it's expected for Rust mangled names to appear in the index. For C++, index-write.c does not write them: else if (entry->lang == language_cplus && (entry->flags & IS_LINKAGE) != 0) { /* GDB never put C++ linkage names into .gdb_index. The theory here is that a linkage name will normally be in the minimal symbols anyway, so including it in the index is usually redundant -- and the cases where it would not be redundant are rare and not worth supporting. */ continue; } Perhaps this is the real root of the problem: maybe those names should not have been in the index in the first place, if the same rationale applies to Rust. We could make that change, but even if we do, there are already indexes out there with those mangled names, so we have to handle them. Change-Id: I2c346dfba25edecda970af0726f9d77216838cbd Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34707 --- gdb/split-name.c | 36 ++++--- .../gdb-index-rust-legacy-linkage-name.c | 29 ++++++ .../gdb-index-rust-legacy-linkage-name.exp | 98 +++++++++++++++++++ 3 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c create mode 100644 gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp diff --git a/gdb/split-name.c b/gdb/split-name.c index cb740a968d49..b8605c4040f4 100644 --- a/gdb/split-name.c +++ b/gdb/split-name.c @@ -52,11 +52,22 @@ split_name (const char *name, split_style style) iter != nullptr; iter = strchr (iter, '.')) { + /* If the name contains a leading dot or consecutive dots, it's not a + valid qualified dot-separated name. */ + if (iter == &name[previous_len]) + return { name }; + result.emplace_back (&name[previous_len], iter - &name[previous_len]); ++iter; previous_len = iter - name; } + + /* If there is a trailing dot, it's not a valid qualified dot-separated + name either. */ + if (name[previous_len] == '\0') + return { name }; + break; default: @@ -223,19 +234,18 @@ test_split_name_dot_style () check_split_name ("pkg.\"+\"", split_style::DOT_STYLE, { "pkg", "\"+\"" }); - /* Leading, trailing or consecutive dots produce empty components. */ - check_split_name (".", split_style::DOT_STYLE, { "", "" }); - check_split_name ("..", split_style::DOT_STYLE, { "", "", "" }); - check_split_name (".a", split_style::DOT_STYLE, { "", "a" }); - check_split_name ("a.", split_style::DOT_STYLE, { "a", "" }); - check_split_name ("a..b", split_style::DOT_STYLE, { "a", "", "b" }); - check_split_name (".a.b", split_style::DOT_STYLE, { "", "a", "b" }); - check_split_name ("a.b.", split_style::DOT_STYLE, { "a", "b", "" }); - check_split_name ("a.b..c", split_style::DOT_STYLE, - { "a", "b", "", "c" }); - check_split_name ("a..b.c", split_style::DOT_STYLE, - { "a", "", "b", "c" }); - check_split_name ("a...b", split_style::DOT_STYLE, { "a", "", "", "b" }); + /* Names with leading, trailing or consecutive dots are not recognized as + qualified names are are returned as-is. */ + check_split_name (".", split_style::DOT_STYLE, { "." }); + check_split_name ("..", split_style::DOT_STYLE, { ".." }); + check_split_name (".a", split_style::DOT_STYLE, { ".a" }); + check_split_name ("a.", split_style::DOT_STYLE, { "a." }); + check_split_name ("a..b", split_style::DOT_STYLE, { "a..b" }); + check_split_name (".a.b", split_style::DOT_STYLE, { ".a.b" }); + check_split_name ("a.b.", split_style::DOT_STYLE, { "a.b." }); + check_split_name ("a.b..c", split_style::DOT_STYLE, { "a.b..c" }); + check_split_name ("a..b.c", split_style::DOT_STYLE, { "a..b.c" }); + check_split_name ("a...b", split_style::DOT_STYLE, { "a...b" }); } } /* namespace selftests */ diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c new file mode 100644 index 000000000000..ea6d12575233 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.c @@ -0,0 +1,29 @@ +/* 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/>. */ + +void +func (void) +{ + asm ("func_label: .globl func_label"); +} + +int +main (void) +{ + func (); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp new file mode 100644 index 000000000000..b995e986355f --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-rust-legacy-linkage-name.exp @@ -0,0 +1,98 @@ +# 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 reading an index containing a Rust "legacy" mangled linkage +# name. These names contain ".." sequences, which the .gdb_index +# reader used to split into empty components, leading to a crash. +# The .debug_names index is tested too, for completeness. +# +# Note that the crash only happened with libstdc++ assertions enabled +# (e.g. with -D_GLIBCXX_DEBUG). Otherwise, it was silent undefined +# behavior. + +load_lib dwarf.exp + +require dwarf2_support +require !readnow + +standard_testfile .c -dw.S + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + cu {} { + DW_TAG_compile_unit { + DW_AT_language @DW_LANG_Rust + DW_AT_name lib.rs + } { + DW_TAG_namespace { + DW_AT_name foo + } { + # The linkage name demangles to foo::bar<foo::Baz>. + DW_TAG_subprogram { + DW_AT_name "bar<foo::Baz>" + DW_AT_linkage_name \ + "_ZN3foo19bar\$LT\$foo..Baz\$GT\$17h0123456789abcdefE" + DW_AT_external 1 DW_FORM_flag + MACRO_AT_range {func} + } + } + } + } +} + +if { [build_executable "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] == -1 } { + return +} + +# For simplicity, skip the test if the executable already has an index (e.g. +# with the cc-with-gdb-index board). The test will be covered when running +# with the unix board. +clean_restart $testfile +if { [get_index_type $binfile "check index type"] ne "cooked" } { + unsupported "executable already has an index" + return +} + +foreach_with_prefix index_type { gdb_index debug_names } { + set testfile_with_index ${testfile}-${index_type} + set binfile_with_index [standard_output_file $testfile_with_index] + remote_exec build "cp $binfile $binfile_with_index" + + if { $index_type eq "gdb_index" } { + set style "" + } else { + set style "-dwarf-5" + } + + if { [add_gdb_index $binfile_with_index $style] != 1 } { + untested "error adding index" + continue + } + + clean_restart $testfile_with_index + gdb_assert { [have_index $binfile_with_index] eq $index_type } \ + "index used" + + # Check that the function can be found. + set re "^foo::bar<foo::Baz>$" + gdb_test "info functions $re" \ + [multi_line \ + [string_to_regexp \ + "All functions matching regular expression \"$re\":"] \ + "" \ + "File lib.rs:" \ + "\[^\r\n\]*[string_to_regexp "fn foo::bar<foo::Baz>();"]"] +} -- 2.56.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile 2026-10-05 5:25 [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile simon.marchi 2026-10-05 5:25 ` [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp simon.marchi 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi @ 2026-10-06 10:56 ` Andrew Burgess 2 siblings, 0 replies; 20+ messages in thread From: Andrew Burgess @ 2026-10-06 10:56 UTC (permalink / raw) To: simon.marchi, gdb-patches; +Cc: Simon Marchi simon.marchi@polymtl.ca writes: > From: Simon Marchi <simon.marchi@polymtl.ca> > > The "set compiler" line looks like a copy-paste error from the if just > above. If we compare this to the Go handler, then it seems clear that the > intent was to write: > > if {[board_info $dest exists rustcompiler]} { > set compiler [target_info rustcompiler] > } else { > set compiler [find_rustc] > } > > The name rustcompiler follows the convention used by other languages (e.g. > "gocompiler", "dcompiler", etc). > > This also matches what was sent upstream to DejaGnu in commit c8beff4a0843 > ("Add Rust support to default_target_compile"). LGTM. Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > > Change-Id: Ic21b397ff6a2afbcda72901203ff7834c752ff3f > --- > gdb/testsuite/lib/future.exp | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp > index 44fc17b3f004..a5444df66c87 100644 > --- a/gdb/testsuite/lib/future.exp > +++ b/gdb/testsuite/lib/future.exp > @@ -308,8 +308,8 @@ proc gdb_default_target_compile_1 {source destfile type options} { > if {[board_info $dest exists rustflags]} { > append add_flags " [target_info rustflags]" > } > - if {[board_info $dest exists rustflags]} { > - set compiler [target_info rustflags] > + if {[board_info $dest exists rustcompiler]} { > + set compiler [target_info rustcompiler] > } else { > set compiler [find_rustc] > } > > base-commit: 2ce6c3190773eb22f467446c4fdc7edb123ce82f > -- > 2.55.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-10-18 3:46 UTC | newest] Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-05 5:25 [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile simon.marchi 2026-10-05 5:25 ` [PATCH 2/3] gdb/testsuite: wrap rustc in cc-with-tweaks.exp simon.marchi 2026-10-06 10:56 ` Andrew Burgess 2026-10-05 5:25 ` [PATCH 3/3] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names simon.marchi 2026-10-06 11:01 ` Andrew Burgess 2026-10-06 13:39 ` Simon Marchi 2026-10-14 13:01 ` Tom Tromey 2026-10-14 13:22 ` Simon Marchi 2026-10-14 14:59 ` Tom Tromey 2026-10-14 16:13 ` Tom Tromey 2026-10-14 16:26 ` Simon Marchi 2026-10-14 16:46 ` [PATCH v2 0/2] Fix " Simon Marchi 2026-10-15 15:47 ` Tom Tromey 2026-10-18 3:45 ` Simon Marchi 2026-10-14 16:46 ` [PATCH v2 1/2] gdb: add selftests for split_name Simon Marchi 2026-10-15 15:48 ` Tom Tromey 2026-10-15 18:33 ` Simon Marchi 2026-10-16 3:15 ` Tom Tromey 2026-10-14 16:46 ` [PATCH v2 2/2] gdb/dwarf: fix crash when .gdb_index contains Rust legacy mangled names Simon Marchi 2026-10-06 10:56 ` [PATCH 1/3] gdb/testsuite: fix rust compiler selection in gdb_default_target_compile Andrew Burgess
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox