From: Tom de Vries Date: Sat, 17 Jan 2026 07:44:57 +0000 (+0100) Subject: [gdb/tdep] Fix gdb.base/siginfo.exp on s390x-linux X-Git-Tag: binutils-2_46~217 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8fa65585b772775e8ffe14bbfa38917c5a804cac;p=thirdparty%2Fbinutils-gdb.git [gdb/tdep] Fix gdb.base/siginfo.exp on s390x-linux On s390x-linux (SLES 15 SP5), I'm running into: ... FAIL: gdb.base/siginfo.exp: backtrace for nexti (pattern 2) FAIL: gdb.base/siginfo.exp: step out of handler ... The first FAIL is caused by a failure to unwind: ... (gdb) bt^M #0 handler (sig=26, info=0x3ffffffe428, context=0x3ffffffe4a8) at \ gdb.base/siginfo.c:31^M Backtrace stopped: Cannot access memory at address 0x1a00000088^M (gdb) ... In contrast, on x86_64-linux I get instead: ... (gdb) bt^M #0 handler (sig=26, info=0x7fffffffc170, context=0x7fffffffc040) at \ gdb.base/siginfo.c:31^M #1 ^M #2 0x0000000000401201 in main () at gdb.base/siginfo.c:67^M (gdb) ... The memory access error is triggered here in s390_sigtramp_frame_unwind_cache: ... /* Restore the previous frame's SP. */ prev_sp = read_memory_unsigned_integer ( info->saved_regs[S390_SP_REGNUM].addr (), word_size, byte_order); ... while trying to read an "Old-style RT frame" (for syscall sigreturn). The problem is that we actually have a "New-style RT frame" (for syscall rt_sigreturn). [ See linux kernel source file arch/s390/kernel/signal.c for a detailed explanation of the two. ] The choice between the two is made earlier in that same function: ... /* New-style RT frame: retcode + alignment (8 bytes) siginfo (128 bytes) ucontext (contains sigregs at offset 5 words). */ if (next_ra == next_cfa) { ... } /* Old-style RT frame and all non-RT frames: old signal mask (8 bytes) pointer to sigregs. */ else ... I'm not sure why the check gives the wrong result, but I noticed that s390_sigtramp_frame_sniffer is able to distinguish between the two, so fix this by: - factoring out new function s390_sigtramp_p out of s390_sigtramp_frame_sniffer, and - using s390_sigtramp_p in s390_sigtramp_frame_unwind_cache to distinguish between the "Old-style RT frame" and "New-style RT frame". This fixes the backtrace. The second failure is: ... (gdb) step^M 32 } /* handler */^M 1: x/i $pc^M => 0x1000772 : nopr^M (gdb) step^M 0x000003fffdffe490 in __kernel_rt_sigreturn ()^M 1: x/i $pc^M => 0x3fffdffe490 <__kernel_rt_sigreturn>: svc 173^M (gdb) FAIL: gdb.base/siginfo.exp: step out of handler ... There is some code in process_event_stop_test that is supposed to trigger: ... if (ecs->event_thread->control.step_range_end != 1 && (ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE || ecs->event_thread->control.step_over_calls == STEP_OVER_ALL) && get_frame_type (frame) == SIGTRAMP_FRAME) { infrun_debug_printf ("stepped into signal trampoline"); /* The inferior, while doing a "step" or "next", has ended up in a signal trampoline (either by a signal being delivered or by the signal handler returning). Just single-step until the inferior leaves the trampoline (either by calling the handler or returning). */ keep_going (ecs); return; } ... but it doesn't because frame is a NORMAL_FRAME instead of a SIGTRAMP_FRAME. This is caused by the "dwarf2" unwinder triggering, which has higher priority than the "s390 linux sigtramp" unwinder: ... (gdb) maint info frame-unwinders Name Type Class Enabled dummy DUMMY_FRAME GDB Y dwarf2 tailcall TAILCALL_FRAME DEBUGINFO Y inline INLINE_FRAME GDB Y jit NORMAL_FRAME EXTENSION Y python NORMAL_FRAME EXTENSION Y dwarf2 NORMAL_FRAME DEBUGINFO Y dwarf2 signal SIGTRAMP_FRAME DEBUGINFO Y s390 linux sigtramp SIGTRAMP_FRAME ARCH Y s390 stub NORMAL_FRAME ARCH Y s390 prologue NORMAL_FRAME ARCH Y ... I found some code in dwarf2_frame_sniffer: ... /* On some targets, signal trampolines may have unwind information. We need to recognize them so that we set the frame type correctly. */ if (fde->cie->signal_frame || dwarf2_frame_signal_frame_p (get_frame_arch (this_frame), this_frame)) return self->type () == SIGTRAMP_FRAME; ... and an example implementation i386_linux_dwarf_signal_frame_p, and after copying this approach, indeed the stepping failure was fixed, but the backtrace broken again. Instead, fix this by giving the "s390 linux sigtramp" unwinder a higher priority: ... (gdb) maint info frame-unwinders Name Type Class Enabled dummy DUMMY_FRAME GDB Y dwarf2 tailcall TAILCALL_FRAME DEBUGINFO Y inline INLINE_FRAME GDB Y jit NORMAL_FRAME EXTENSION Y python NORMAL_FRAME EXTENSION Y s390 linux sigtramp SIGTRAMP_FRAME ARCH Y dwarf2 NORMAL_FRAME DEBUGINFO Y dwarf2 signal SIGTRAMP_FRAME DEBUGINFO Y s390 stub NORMAL_FRAME ARCH Y s390 prologue NORMAL_FRAME ARCH Y ... Also fixes test-case gdb.base/sigaltstack.exp and gdb.base/sigbpt.exp. Tested on s390x-linux. Reviewed-By: Keith Seitz PR tdep/33708 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33708 --- diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c index 3106a078621..f8ec7c99463 100644 --- a/gdb/s390-linux-tdep.c +++ b/gdb/s390-linux-tdep.c @@ -385,6 +385,30 @@ struct s390_sigtramp_unwind_cache { trad_frame_saved_reg *saved_regs; }; +/* Return true if the frame pc of THIS_FRAME points to either + __kernel_rt_sigreturn or __kernel_sigreturn. Return the corresponding + syscall number in SYSCALL_NR. */ + +static bool +s390_sigtramp_p (const frame_info_ptr &this_frame, int *syscall_nr_ptr) +{ + CORE_ADDR pc = get_frame_pc (this_frame); + bfd_byte sigreturn[2]; + + if (target_read_memory (pc, sigreturn, 2)) + return false; + + if (sigreturn[0] != op_svc) + return false; + + int syscall_nr = sigreturn[1]; + if (syscall_nr_ptr != nullptr) + *syscall_nr_ptr = syscall_nr; + + return (syscall_nr == 119 /* sigreturn */ + || syscall_nr == 173 /* rt_sigreturn */); +} + /* Unwind THIS_FRAME and return the corresponding unwind cache for s390_sigtramp_frame_unwind. */ @@ -397,7 +421,7 @@ s390_sigtramp_frame_unwind_cache (const frame_info_ptr &this_frame, int word_size = gdbarch_ptr_bit (gdbarch) / 8; enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); ULONGEST this_sp, prev_sp; - CORE_ADDR next_ra, next_cfa, sigreg_ptr, sigreg_high_off; + CORE_ADDR next_cfa, sigreg_ptr, sigreg_high_off; int i; if (*this_prologue_cache) @@ -408,14 +432,17 @@ s390_sigtramp_frame_unwind_cache (const frame_info_ptr &this_frame, info->saved_regs = trad_frame_alloc_saved_regs (this_frame); this_sp = get_frame_register_unsigned (this_frame, S390_SP_REGNUM); - next_ra = get_frame_pc (this_frame); next_cfa = this_sp + 16*word_size + 32; + int syscall_nr; + bool res = s390_sigtramp_p (this_frame, &syscall_nr); + gdb_assert (res); + /* New-style RT frame: retcode + alignment (8 bytes) siginfo (128 bytes) ucontext (contains sigregs at offset 5 words). */ - if (next_ra == next_cfa) + if (syscall_nr == 173 /* rt_sigreturn */) { sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8); /* sigregs are followed by uc_sigmask (8 bytes), then by the @@ -525,20 +552,7 @@ s390_sigtramp_frame_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame, void **this_prologue_cache) { - CORE_ADDR pc = get_frame_pc (this_frame); - bfd_byte sigreturn[2]; - - if (target_read_memory (pc, sigreturn, 2)) - return 0; - - if (sigreturn[0] != op_svc) - return 0; - - if (sigreturn[1] != 119 /* sigreturn */ - && sigreturn[1] != 173 /* rt_sigreturn */) - return 0; - - return 1; + return s390_sigtramp_p (this_frame, nullptr) ? 1 : 0; } /* S390 sigtramp frame unwinder. */ @@ -1187,7 +1201,7 @@ s390_linux_init_abi_any (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_get_syscall_number (gdbarch, s390_linux_get_syscall_number); /* Frame handling. */ - frame_unwind_append_unwinder (gdbarch, &s390_sigtramp_frame_unwind); + frame_unwind_prepend_unwinder (gdbarch, &s390_sigtramp_frame_unwind); set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); /* Enable TLS support. */