Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [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

* [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 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

* 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

* 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

* [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

* [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 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 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

* 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

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