This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: ARM Linux EABI: unwinding through a segfault handler


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;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]