I've discussed this issue with H. Peter Anvin via the kernel list, beginning at https://lore.kernel.org/all/f3412cc3e8f66d1853cc9d572c0f2fab076872b1.camel@xry111.site/. The code current reads: fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1, &context->bases); if (fde == NULL) { #ifdef MD_FALLBACK_FRAME_STATE_FOR /* Couldn't find frame unwind info for this function. Try a target-specific fallback mechanism. This will necessarily not provide a personality routine or LSDA. */ return MD_FALLBACK_FRAME_STATE_FOR (context, fs); #else return _URC_END_OF_STACK; #endif } fs->pc = context->bases.func; cie = get_cie (fde); insn = extract_cie_info (cie, context, fs); It indeed attempts to avoid subtracting 1 for a signal frame, but _Unwind_IsSignalFrame (context) actually extracts a flag in context which will only be raised up by extract_cie_info. So the logic never worked. On many existing Linux targets, the unwinding from signal handler actually works by using MD_FALLBACK_FRAME_STATE_FOR (sometimes only coincidentally). For example, on AArch64 and LoongArch it's simply the unwind info isn't emitted for vDSO as at now, if the kernel developers want to add the unwind info for them, they still need a hack (inserting a NOP before the sigreturn trampoline, x86 is already doing this) for unwinding from signal frame.
This very likely worked by means to MD_FROB_UPDATE_CONTEXT?
Is there any kind of tool we can use to test?
I thought these days most of the time signal frames work through glibc using the right unwind info for the trampolines. sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c on x86_64 etc. MD_FALLBACK_FRAME_STATE_FOR just kicks in when that isn't provided.
That's what SHOULD happen. The big question is if that is what DOES happen.
It isn't just glibc, either; on legacy platforms that used to use the stack it is handled by the vdso these days. The real question is: is the signal frame flag actually picked up, or not?
> The real question is: is the signal frame flag actually picked up, or not? Yes, we would need a testcase here (hence the WAITING status).
At the very least the situation is not so good on RISC-V. With a simplified glibc test case: #include <execinfo.h> #include <inttypes.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> static void handler (int signal, siginfo_t *info, void *ctx) { void *callstack[10]; int callstack_count = backtrace (callstack, 10); bool found = false; for (int i = 0; i < callstack_count; ++i) printf ("info: call stack entry %d: 0x%" PRIxPTR "\n", i, (uintptr_t) callstack[i]); } int main (void) { struct sigaction sa = { .sa_sigaction = &handler, .sa_flags = SA_SIGINFO }; sigaction (SIGUSR1, &sa, NULL); raise (SIGUSR1); return 0; } Debugging it on RISC-V shows: (gdb) b _Unwind_Find_FDE ❌️ Function "_Unwind_Find_FDE" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (_Unwind_Find_FDE) pending. (gdb) r Starting program: /home/xry111/a.out [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Program received signal SIGUSR1, User defined signal 1. __pthread_kill_implementation (threadid=<optimized out>, signo=-877095168, no_tid=0) at pthread_kill.c:44 ⚠️ warning: 44 pthread_kill.c: no such file or directory (gdb) c Continuing. Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7e1b323 <_Unwind_Backtrace+131>, bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541 ⚠️ warning: 541 ../../../libgcc/unwind-dw2-fde-dip.c: no such file or directory (gdb) c Continuing. Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7f20251 <__GI___backtrace+81>, bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541 541 in ../../../libgcc/unwind-dw2-fde-dip.c (gdb) c Continuing. Breakpoint 1, _Unwind_Find_FDE (pc=0x2aaaaaa87d <handler+49>, bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541 541 in ../../../libgcc/unwind-dw2-fde-dip.c (gdb) c Continuing. Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7fd358f, bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541 541 in ../../../libgcc/unwind-dw2-fde-dip.c (gdb) p pc+1 $1 = (void *) 0x3ff7fd3590 <__vdso_rt_sigreturn> (gdb) finish Run till exit from #0 _Unwind_Find_FDE (pc=0x3ff7fd358f, bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541 0x0000003ff7e18af2 in uw_frame_state_for (context=context@entry=0x3fffffe090, fs=fs@entry=0x3fffffe570) at ../../../libgcc/unwind-dw2.c:1008 ⚠️ warning: 1008 ../../../libgcc/unwind-dw2.c: no such file or directory Value returned is $2 = (const fde *) 0x0 We can see the pc passed to _Unwind_Find_FDE is subtracted with 1, and _Unwind_Find_FDE cannot find the FDE. Thus it relies on MD_FALLBACK_FRAME_STATE_FOR. To make the situation more fragile: the fall back only works because on RISC-V __vdso_rt_sigreturn happens to be the first function in the text section. Otherwise using the subtracted pc will cause _Unwind_Find_FDE to return the FDE of the previous function and break unwinding completely.