[binutils-gdb] [gdb/tdep] Fix gdb.base/siginfo.exp on s390x-linux

Tom de Vries vries@sourceware.org
Sat Jan 17 07:45:01 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=8fa65585b772775e8ffe14bbfa38917c5a804cac

commit 8fa65585b772775e8ffe14bbfa38917c5a804cac
Author: Tom de Vries <tdevries@suse.de>
Date:   Sat Jan 17 08:44:57 2026 +0100

    [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  <signal handler called>^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 <handler+50>:      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 <keiths@redhat.com>
    
    PR tdep/33708
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33708

Diff:
---
 gdb/s390-linux-tdep.c | 50 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 18 deletions(-)

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.  */


More information about the Gdb-cvs mailing list