@samitouri / QOSamiQemu / commits / 9ac5aa7227

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>

Matt Turner committed May 14, 2026 at 12:55 UTC 9ac5aa72272117608482cad2430a75477263fe09
1 file changed +32 -10
linux-user/sh4/signal.c
+32 -10
@@ -329,20 +329,42 @@ badframe:
329 return -QEMU_ESIGRETURN;
330 }
331
332 +/*
333 + * "or r0,r0" nop used by the Linux kernel inline sigreturn trampolines to
334 + * avoid a hardware bug (OR_R0_R0 in arch/sh/kernel/signal_32.c). Five of
335 + * these nops follow TRAP_NOARG, placing the syscall number word 14 bytes
336 + * past the MOVW(7) instruction (at MOVW(7)'s load offset). This yields the
337 + * fixed 16-byte layout that libunwind's unw_is_signal_frame detects:
338 + * [MOVW(7), TRAP_NOARG, 5x NOP_OR, .word syscall_nr]
339 + */
340 +#define NOP_OR 0x200b
341 +
342 void setup_sigtramp(abi_ulong sigtramp_page)
343 {
334 - uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0);
344 + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 16, 0);
345 assert(tramp != NULL);
346
347 + /* sigreturn trampoline (non-RT) at offset 0 */
348 default_sigreturn = sigtramp_page;
338 - __put_user(MOVW(2), &tramp[0]);
349 + __put_user(MOVW(7), &tramp[0]);
350 __put_user(TRAP_NOARG, &tramp[1]);
340 - __put_user(TARGET_NR_sigreturn, &tramp[2]);
341 -
342 - default_rt_sigreturn = sigtramp_page + 6;
343 - __put_user(MOVW(2), &tramp[3]);
344 - __put_user(TRAP_NOARG, &tramp[4]);
345 - __put_user(TARGET_NR_rt_sigreturn, &tramp[5]);
346 -
347 - unlock_user(tramp, sigtramp_page, 2 * 6);
351 + __put_user(NOP_OR, &tramp[2]);
352 + __put_user(NOP_OR, &tramp[3]);
353 + __put_user(NOP_OR, &tramp[4]);
354 + __put_user(NOP_OR, &tramp[5]);
355 + __put_user(NOP_OR, &tramp[6]);
356 + __put_user(TARGET_NR_sigreturn, &tramp[7]);
357 +
358 + /* rt_sigreturn trampoline at offset 16 */
359 + default_rt_sigreturn = sigtramp_page + 16;
360 + __put_user(MOVW(7), &tramp[8]);
361 + __put_user(TRAP_NOARG, &tramp[9]);
362 + __put_user(NOP_OR, &tramp[10]);
363 + __put_user(NOP_OR, &tramp[11]);
364 + __put_user(NOP_OR, &tramp[12]);
365 + __put_user(NOP_OR, &tramp[13]);
366 + __put_user(NOP_OR, &tramp[14]);
367 + __put_user(TARGET_NR_rt_sigreturn, &tramp[15]);
368 +
369 + unlock_user(tramp, sigtramp_page, 2 * 16);
370 }