I've managed to expand Paul's outline into working code. It's still incomplete but I can report success throwing an exception in a signal handler and catching it outside it.
I didn't rebuild glibc but just linked some extra code to my executable. The <unwind.h> I grabbed from glibc, the rest are system headers. Note that this version doesn't cover the use of SA_SIGINFO nor the old v1 format, and currently doesn't restore VFP registers yet. It also needs to restore the signal mask. I'm hoping it's safe to do that in the location where I've marked the TODO in my code, but I have the feeling this is the sort of code that can easily have ugly corner cases or race conditions... manually restoring the signal mask after catching the exception might be safer. // sigrestorer.S .syntax unified .global __default_sa_restorer_v2 .fnstart .personality __gnu_personality_sigframe nop __default_sa_restorer_v2: mov r7, 139 // sigreturn svc 0 .fnend // gnu_personality_sigframe.c #include <unwind.h> #include <ucontext.h> #include <signal.h> #include <stdbool.h> struct sigframe { struct ucontext uc; unsigned long retcode[2]; }; static inline _uw get_core_reg( _Unwind_Context *context, _uw reg ) { _uw val; _Unwind_VRS_Get( context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val ); return val; } static inline void set_core_reg( _Unwind_Context *context, _uw reg, _uw val ) { _Unwind_VRS_Set( context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val ); } #define R_SP 13 #define R_LR 14 #define R_PC 15 #define T_BIT ( 1 << 5 ) _Unwind_Reason_Code __gnu_personality_sigframe( _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context ) { bool for_realzies; switch( state ) { case _US_VIRTUAL_UNWIND_FRAME: for_realzies = false; break; case _US_UNWIND_FRAME_STARTING: for_realzies = true; break; default: return _URC_FAILURE; } struct sigframe *sf = (struct sigframe *) get_core_reg( context, R_SP ); // basically replicate restore_sigframe() in arch/arm/kernel/signal.c _uw *reg = &sf->uc.uc_mcontext.arm_r0; for( int i = 0; i < 15; i++ ) set_core_reg( context, i, reg[i] ); _uw pc = sf->uc.uc_mcontext.arm_pc; _uw psr = sf->uc.uc_mcontext.arm_cpsr; // advance PC and set bit 0 to indicate thumb state if( psr & T_BIT ) { bool thumb32 = *(_uw16 *) pc >= 0xe800; pc += thumb32 ? 4 : 2; pc |= 1; } else { pc += 4; } set_core_reg( context, R_PC, pc ); // TODO vfp if( for_realzies ) { // XXX restore sigmask, vfp control registers, etc? } return _URC_CONTINUE_UNWIND; }