Fix backtrace showing ?? or wrong name for solib frame at function entry
Checks
Commit Message
Pinging
---------- Forwarded message ---------
От: Дима <shafrnv@gmail.com>
Date: сб, 7 мар. 2026 г. в 22:48
Subject: [PATCH] Fix backtrace showing ?? or wrong name for solib
frame at function entry
To: <gdb-patches@sourceware.org>
When the resume PC is exactly at a function entry in a shared library
without debug info (e.g. a trampoline that does push return-address +
jmp), the backtrace shows "??" or the previous function name for that
frame. Reason: get_frame_address_in_block gives pc-1 for
non-innermost frames, and lookup_minimal_symbol_by_pc(pc-1) then finds
nothing or the wrong symbol.
The fix: in find_frame_funname, if lookup_minimal_symbol_by_pc(pc)
fails, try pc+1; and when a symbol is found, also check pc+1 and
prefer the symbol whose address is exactly pc+1 (the actual resume PC
at function entry).
Tested on x86_64 Linux (-m32). Added gdb.base/solib-bt-trampoline.exp.
Reproducer: gdb-solib-bt-bug-repro.tar.gz — see README inside for
build and run steps.
Patch below:
From 935853aaf1e2b4260b026ca36f5cfb9096c6b40c Mon Sep 17 00:00:00 2001
From: Dmitry Shafranov <shafrnv@gmail.com>
Date: Sat, 7 Mar 2026 22:14:22 +0300
Subject: [PATCH] Fix backtrace showing ?? or wrong name for solib frame at
function entry
For frames without full symbols (e.g. solib with no debug info),
find_frame_funname uses get_frame_address_in_block and then
lookup_minimal_symbol_by_pc. For non-innermost frames we get pc-1.
When the resume PC is exactly at a function entry (e.g. a trampoline
that does push+jmp), pc-1 is before that function, so the lookup
fails (backtrace shows "??") or finds the previous function in the
same object.
Add a fallback: if lookup_minimal_symbol_by_pc(pc) returns NULL, try
lookup_minimal_symbol_by_pc(pc+1). This recovers the correct symbol
for the "PC at function entry" case without changing unwinding or
get_frame_address_in_block.
---
gdb/stack.c | 13 ++++
.../gdb.base/solib-bt-trampoline-lib.S | 24 ++++++
gdb/testsuite/gdb.base/solib-bt-trampoline.c | 21 ++++++
.../gdb.base/solib-bt-trampoline.exp | 74 +++++++++++++++++++
4 files changed, 132 insertions(+)
create mode 100644 gdb/testsuite/gdb.base/solib-bt-trampoline-lib.S
create mode 100644 gdb/testsuite/gdb.base/solib-bt-trampoline.c
create mode 100644 gdb/testsuite/gdb.base/solib-bt-trampoline.exp
+ }
+ -re "$gdb_prompt $" {
+ fail $gdb_test_name
+ }
+}
--
2.53.0
@@ -1311,6 +1311,19 @@ find_frame_funname (const frame_info_ptr
&frame, enum language *funlang,
return funname;
bound_minimal_symbol msymbol = lookup_minimal_symbol_by_pc (pc);
+ if (msymbol.minsym == NULL)
+ {
+ /* pc may be at function entry (get_frame_address_in_block uses
pc-1); try pc+1. */
+ msymbol = lookup_minimal_symbol_by_pc (pc + 1);
+ }
+ else
+ {
+ /* Prefer symbol at pc+1 (resume PC) when it is exactly at function
entry. */
+ bound_minimal_symbol at_pc_plus_one = lookup_minimal_symbol_by_pc (pc + 1);
+ if (at_pc_plus_one.minsym != NULL
+ && at_pc_plus_one.value_address () == pc + 1)
+ msymbol = at_pc_plus_one;
+ }
if (msymbol.minsym != NULL)
{
funname = make_unique_xstrdup (msymbol.minsym->print_name ());
b/gdb/testsuite/gdb.base/solib-bt-trampoline-lib.S
new file mode 100644
@@ -0,0 +1,24 @@
+/* Trampoline: run_then_exit + run_then_exit_return in one object.
+ When return address is exactly at run_then_exit_return entry, GDB
+ looks up pc-1 and without the fix shows "run_then_exit" or "??". */
+ .text
+ .globl run_then_exit
+ .type run_then_exit,@function
+ .globl run_then_exit_return
+ .type run_then_exit_return,@function
+ .extern exit
+run_then_exit:
+ push %ebp
+ mov %esp, %ebp
+ call .Lpic
+.Lpic:
+ pop %eax
+ add $(run_then_exit_return - .Lpic), %eax
+ push 12(%ebp)
+ push %eax
+ jmp *8(%ebp)
+ .size run_then_exit, .-run_then_exit
+run_then_exit_return:
+ push $0
+ call exit@PLT
+ .size run_then_exit_return, .-run_then_exit_return
b/gdb/testsuite/gdb.base/solib-bt-trampoline.c
new file mode 100644
@@ -0,0 +1,21 @@
+/* Test program for run_then_exit trampoline. Stops in user callback so that
+ "bt" in GDB shows the trampoline return frame: frame #1 should be
+ run_then_exit_return (not "??" or "run_then_exit"). */
+
+#include <stddef.h>
+
+extern void run_then_exit (void (*fn) (void *), void *arg);
+
+static void
+inner_func (void *arg)
+{
+ (void) arg;
+ return; /* Break here and run "bt". */
+}
+
+int
+main (void)
+{
+ run_then_exit (inner_func, NULL);
+ return 0;
+}
b/gdb/testsuite/gdb.base/solib-bt-trampoline.exp
new file mode 100644
@@ -0,0 +1,74 @@
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that backtrace shows the correct function name for a solib frame
+# when the resume PC is exactly at function entry (trampoline that does
+# push return-address + jmp). Without the fix, frame #1 shows "??" or
+# the previous function (run_then_exit) instead of run_then_exit_return.
+
+require allow_shlib_tests
+require {is_any_target "i?86-*-*" "x86_64-*-*"}
+
+standard_testfile solib-bt-trampoline.c
+set libsrc ${srcdir}/${subdir}/solib-bt-trampoline-lib.S
+set libname "solib-bt-trampoline-lib"
+set libfile [standard_output_file ${libname}.so]
+
+# Shared library: assembly only, no debug info (so minimal symbols are used).
+set lib_opts [list nodebug ldflags=-nostdlib libs=-lc]
+if {![istarget "i386-*-*"]} {
+ lappend lib_opts "additional_flags=-m32"
+}
+
+if {[gdb_compile_shlib $libsrc $libfile $lib_opts] != ""} {
+ untested "failed to compile shared library (need 32-bit toolchain
on x86_64?)"
+ return -1
+}
+
+set exec_opts [list debug shlib=$libfile
"additional_flags=-fno-omit-frame-pointer"]
+if {![istarget "i386-*-*"]} {
+ lappend exec_opts "additional_flags=-m32"
+}
+
+if {[gdb_compile $srcdir/$subdir/$srcfile $binfile executable
$exec_opts] != ""} {
+ untested "failed to compile executable"
+ return -1
+}
+
+clean_restart $testfile
+gdb_load_shlib $libfile
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_breakpoint "inner_func"
+# Pattern must match to end of line (e.g. solib-bt-trampoline.c:13)
so \r\n is next.
+set location_pattern "inner_func\[^\r\n\]*|solib-bt-trampoline\[^\r\n\]*"
+gdb_test_multiple "continue" "continue to breakpoint: inner_func" {
+ -re "(?:Breakpoint|Temporary breakpoint) .* (at|in)
$location_pattern\[\r\n\]+.*$gdb_prompt $" {
+ pass $gdb_test_name
+ }
+}
+
+# Frame #1 must be run_then_exit_return (not "??" or "run_then_exit").
+gdb_test_multiple "bt" "backtrace shows run_then_exit_return in frame 1" {
+ -re "#1\[^\r\n\]*run_then_exit_return" {
+ pass $gdb_test_name