Commit 9ac5aa7227 for qemu.org

commit 9ac5aa72272117608482cad2430a75477263fe09
Author: Matt Turner <mattst88@gmail.com>
Date:   Thu May 14 12:55:26 2026 -0400

    linux-user/sh4: Fix setup_sigtramp to match Linux kernel trampoline pattern

    QEMU used MOVW(2) (0x9300), which loads the syscall number from PC+4,
    instead of the kernel's MOVW(7) (0x9305), which loads from PC+14.  The
    kernel uses five "or r0,r0" nop pads between TRAP_NOARG and the syscall
    number word to reach that offset.  libunwind's unw_is_signal_frame checks
    for the exact kernel byte pattern 0xc3109305 at the frame PC, so QEMU's
    compact layout was not detected, breaking unwinding through signal frames.

    Expand each trampoline from 6 to 16 bytes matching the kernel layout
    defined in arch/sh/kernel/signal_32.c:

      #define MOVW(n)    (0x9300|((n)-2))  /* Move mem word at PC+n to R3 */
      #define TRAP_NOARG 0xc310            /* Syscall w/no args (NR in R3) */
      #define OR_R0_R0   0x200b            /* or r0,r0 (insert to avoid hardware bug) */

      __put_user(MOVW(7),          &frame->retcode[0]);  /* 0x9305 */
      __put_user(TRAP_NOARG,       &frame->retcode[1]);  /* 0xc310 */
      __put_user(OR_R0_R0,         &frame->retcode[2]);  /* 0x200b */
      __put_user(OR_R0_R0,         &frame->retcode[3]);  /* 0x200b */
      __put_user(OR_R0_R0,         &frame->retcode[4]);  /* 0x200b */
      __put_user(OR_R0_R0,         &frame->retcode[5]);  /* 0x200b */
      __put_user(OR_R0_R0,         &frame->retcode[6]);  /* 0x200b */
      __put_user((__NR_sigreturn), &frame->retcode[7]);

    The first two halfwords (MOVW(7) || TRAP_NOARG = 0xc3109305) form the
    32-bit value libunwind checks at the frame PC, followed by two
    OR_R0_R0 halfwords (0x200b200b) at PC+4.  The same layout applies to
    the rt_sigreturn trampoline (lines 366-373 of signal_32.c).

    Neither this fix nor the companion tuc_link fix is independently
    sufficient: this fix makes signal frames detectable but register reads
    remain garbage without the correct ucontext layout; that fix corrects the
    ucontext layout but libunwind still cannot detect the frame without the
    correct trampoline pattern.  Together they fix the following libunwind
    tests on a 64-bit host:
      Gtest-sig-context, Gtest-trace, Ltest-init-local-signal,
      Ltest-sig-context, Ltest-trace

    Signed-off-by: Matt Turner <mattst88@gmail.com>
    Cc: qemu-stable@nongnu.org
    Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
    Signed-off-by: Helge Deller <deller@gmx.de>

diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c
index 20d2bc8b2c..d70be24c38 100644
--- a/linux-user/sh4/signal.c
+++ b/linux-user/sh4/signal.c
@@ -329,20 +329,42 @@ badframe:
     return -QEMU_ESIGRETURN;
 }

+/*
+ * "or r0,r0" nop used by the Linux kernel inline sigreturn trampolines to
+ * avoid a hardware bug (OR_R0_R0 in arch/sh/kernel/signal_32.c).  Five of
+ * these nops follow TRAP_NOARG, placing the syscall number word 14 bytes
+ * past the MOVW(7) instruction (at MOVW(7)'s load offset).  This yields the
+ * fixed 16-byte layout that libunwind's unw_is_signal_frame detects:
+ *   [MOVW(7), TRAP_NOARG, 5x NOP_OR, .word syscall_nr]
+ */
+#define NOP_OR 0x200b
+
 void setup_sigtramp(abi_ulong sigtramp_page)
 {
-    uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0);
+    uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 16, 0);
     assert(tramp != NULL);

+    /* sigreturn trampoline (non-RT) at offset 0 */
     default_sigreturn = sigtramp_page;
-    __put_user(MOVW(2), &tramp[0]);
+    __put_user(MOVW(7), &tramp[0]);
     __put_user(TRAP_NOARG, &tramp[1]);
-    __put_user(TARGET_NR_sigreturn, &tramp[2]);
-
-    default_rt_sigreturn = sigtramp_page + 6;
-    __put_user(MOVW(2), &tramp[3]);
-    __put_user(TRAP_NOARG, &tramp[4]);
-    __put_user(TARGET_NR_rt_sigreturn, &tramp[5]);
-
-    unlock_user(tramp, sigtramp_page, 2 * 6);
+    __put_user(NOP_OR, &tramp[2]);
+    __put_user(NOP_OR, &tramp[3]);
+    __put_user(NOP_OR, &tramp[4]);
+    __put_user(NOP_OR, &tramp[5]);
+    __put_user(NOP_OR, &tramp[6]);
+    __put_user(TARGET_NR_sigreturn, &tramp[7]);
+
+    /* rt_sigreturn trampoline at offset 16 */
+    default_rt_sigreturn = sigtramp_page + 16;
+    __put_user(MOVW(7), &tramp[8]);
+    __put_user(TRAP_NOARG, &tramp[9]);
+    __put_user(NOP_OR, &tramp[10]);
+    __put_user(NOP_OR, &tramp[11]);
+    __put_user(NOP_OR, &tramp[12]);
+    __put_user(NOP_OR, &tramp[13]);
+    __put_user(NOP_OR, &tramp[14]);
+    __put_user(TARGET_NR_rt_sigreturn, &tramp[15]);
+
+    unlock_user(tramp, sigtramp_page, 2 * 16);
 }