ntdll/signal_arm.c: Implement segv_handler
chris.nousco at gmail.com
chris.nousco at gmail.com
Thu Feb 17 02:29:22 CST 2011
From: JinSung Kang <chris.nousco at gmail.com>
Improve that segv_handler function check page_fault and setting up exception handler.
It is possbible whether read fault or write fault.
---
dlls/ntdll/signal_arm.c | 224 ++++++++++++++++++++++++++++++++++++++++------
1 files changed, 194 insertions(+), 30 deletions(-)
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c
index 992b63c..ae6ff5a 100644
--- a/dlls/ntdll/signal_arm.c
+++ b/dlls/ntdll/signal_arm.c
@@ -66,6 +66,8 @@ static pthread_key_t teb_key;
*/
#ifdef linux
+typedef ucontext_t SIGCONTEXT;
+
/* All Registers access - only for local access */
# define REG_sig(reg_name, context) ((context)->uc_mcontext.reg_name)
# define REGn_sig(reg_num, context) ((context)->uc_mcontext.arm_r##reg_num)
@@ -75,11 +77,44 @@ static pthread_key_t teb_key;
# define LR_sig(context) REG_sig(arm_lr, context) /* Link register */
# define PC_sig(context) REG_sig(arm_pc, context) /* Program counter */
# define CPSR_sig(context) REG_sig(arm_cpsr, context) /* Current State Register */
-# define IP_sig(context) REG_sig(arm_ip, context) /* Program counter (2?) */
+# define IP_sig(context) REG_sig(arm_ip, context) /* Scratch Register */
# define FP_sig(context) REG_sig(arm_fp, context) /* Frame pointer */
+/* Exception Registers access */
+# define ERROR_sig(context) REG_sig(error_code, context)
+# define FAULT_sig(context) REG_sig(fault_address, context)
+# define TRAP_sig(context) REG_sig(trap_no, context)
+
#endif /* linux */
+/*make sure that trap_no in arm*/
+enum arm_trap_code
+{
+ TRAP_ARM_UNKNOWN = -1, /* Unknown fault (TRAP_sig not define) */
+ TRAP_ARM_PAGEFLT = 14 /* Page fault */
+};
+
+/***********************************************************************
+ * get_trap_code
+ *
+ * Get the trap code for a signal.
+ */
+static inline enum arm_trap_code get_trap_code( const ucontext_t *sigcontext )
+{
+ return TRAP_sig(sigcontext);
+}
+
+/***********************************************************************
+ * get_error_code
+ *
+ * Get the error code for a signal.
+ */
+static inline WORD get_error_code( const ucontext_t *sigcontext )
+{
+ return ERROR_sig(sigcontext);
+}
+
+
typedef int (*wine_signal_handler)(unsigned int sig);
static wine_signal_handler handlers[256];
@@ -299,15 +334,54 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
*/
static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
{
- EXCEPTION_POINTERS ptrs;
- FIXME( "not implemented on ARM, exceptioncode: %x\n", rec->ExceptionCode );
+ EXCEPTION_REGISTRATION_RECORD *frame, *dispatch, *nested_frame;
+ DWORD res;
- /* hack: call unhandled exception filter directly */
- ptrs.ExceptionRecord = rec;
- ptrs.ContextRecord = context;
- unhandled_exception_filter( &ptrs );
- return STATUS_UNHANDLED_EXCEPTION;
+ frame = NtCurrentTeb()->Tib.ExceptionList;
+ nested_frame = NULL;
+ while (frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL)
+ {
+ /* Check frame address */
+ if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
+ ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
+ (ULONG_PTR)frame & 3)
+ {
+ rec->ExceptionFlags |= EH_STACK_INVALID;
+ break;
+ }
+
+ /* Call handler */
+ TRACE( "calling handler at %p code=%x flags=%x\n",
+ frame->Handler, rec->ExceptionCode, rec->ExceptionFlags );
+ res = frame->Handler(rec,frame,context,&dispatch);
+
+ TRACE( "handler at %p returned %x\n", frame->Handler, res );
+
+ if (frame == nested_frame)
+ {
+ /* no longer nested */
+ nested_frame = NULL;
+ rec->ExceptionFlags &= ~EH_NESTED_CALL;
+ }
+
+ switch(res)
+ {
+ case ExceptionContinueExecution:
+ if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return STATUS_SUCCESS;
+ return STATUS_NONCONTINUABLE_EXCEPTION;
+ case ExceptionContinueSearch:
+ break;
+ case ExceptionNestedException:
+ if (nested_frame < dispatch) nested_frame = dispatch;
+ rec->ExceptionFlags |= EH_NESTED_CALL;
+ break;
+ default:
+ return STATUS_INVALID_DISPOSITION;
+ }
+ frame = frame->Prev;
+ }
+ return STATUS_UNHANDLED_EXCEPTION;
}
@@ -339,7 +413,12 @@ static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL f
}
else
{
- /* FIXME: dump context */
+ TRACE (" r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x r5=%08x"
+ "r6=%08x r7=%08x r8=%08x r9=%08x r10=%08x fp=%08x ip=%08x\n",
+ context->R0,context->R1,context->R2,context->R3,context->R4,
+ context->R5,context->R6,context->R7,context->R8,context->R9,
+ context->R10,context->Fp,context->Ip);
+ TRACE(" sp=%08x lr =%08x pc=%08x\n", context->Sp, context->Lr, context->Pc);
}
status = send_debug_event( rec, TRUE, context );
@@ -370,35 +449,120 @@ static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL f
return STATUS_SUCCESS;
}
+/***********************************************************************
+ * setup_exception_record
+ *
+ * Setup the exception record and context on the thread stack.
+ */
+typedef void (WINAPI *raise_func)( EXCEPTION_RECORD *rec, CONTEXT *context );
+
+static EXCEPTION_RECORD *setup_exception_record( ucontext_t *sigcontext, void *stack_ptr, raise_func func )
+{
+ struct stack_layout
+ {
+ CONTEXT context;
+ EXCEPTION_RECORD rec;
+ } *stack = stack_ptr;
+ DWORD exception_code = 0;
+
+ stack--; /* push the stack_layout structure */
+
+ stack->rec.ExceptionRecord = NULL;
+ stack->rec.ExceptionCode = exception_code;
+ stack->rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
+ stack->rec.ExceptionAddress = (LPVOID)PC_sig(sigcontext);
+ stack->rec.NumberParameters = 0;
+
+ save_context( &stack->context, sigcontext);
+
+ /* now modify the sigcontext to return to the raise function */
+ SP_sig(sigcontext) = (DWORD)stack;
+ LR_sig(sigcontext) = (DWORD)(PC_sig(sigcontext)) ;
+ PC_sig(sigcontext) = (DWORD)func;
+ REGn_sig(0,sigcontext) = (DWORD)&stack->rec; /* first arg for raise_func */
+ REGn_sig(1,sigcontext) = (DWORD)&stack->context; /* second arg for raise_func */
+
+ return &stack->rec;
+}
/**********************************************************************
- * segv_handler
- *
- * Handler for SIGSEGV and related errors.
+ * raise_segv_exception
*/
-static void segv_handler( int signal, siginfo_t *info, void *ucontext )
+static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
{
- EXCEPTION_RECORD rec;
- CONTEXT context;
NTSTATUS status;
- rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+ switch(rec->ExceptionCode)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ if (rec->NumberParameters == 2)
+ {
+ if (!(rec->ExceptionCode = virtual_handle_fault( (void *)rec->ExceptionInformation[1],
+ rec->ExceptionInformation[0] )))
+ goto done;
+ /* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
+ if (rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
+ {
+ ULONG flags;
+ NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
+ &flags, sizeof(flags), NULL );
+ if (!(flags & MEM_EXECUTE_OPTION_DISABLE))
+ rec->ExceptionInformation[0] = EXCEPTION_READ_FAULT;
+ }
+ }
+ break;
+ }
+ status = NtRaiseException( rec, context, TRUE );
+ if (status) raise_status( status, rec );
+done:
+ NtSetContextThread( GetCurrentThread(), context );
- /* we want the page-fault case to be fast */
- if ( info->si_code == SEGV_ACCERR )
- if (!(rec.ExceptionCode = virtual_handle_fault( info->si_addr, 0 ))) return;
+}
- save_context( &context, ucontext );
- rec.ExceptionRecord = NULL;
- rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
- rec.ExceptionAddress = (LPVOID)context.Pc;
- rec.NumberParameters = 2;
- rec.ExceptionInformation[0] = 0; /* FIXME: read/write access ? */
- rec.ExceptionInformation[1] = (ULONG_PTR)info->si_addr;
- status = raise_exception( &rec, &context, TRUE );
- if (status) raise_status( status, &rec );
- restore_context( &context, ucontext );
+/**********************************************************************
+ * segv_handler
+ *
+ * Handler for SIGSEGV and related errors.
+ */
+static void segv_handler( int signal, siginfo_t *info, void *ucontext )
+{
+ EXCEPTION_RECORD *rec;
+ ucontext_t *context = ucontext;
+ void *stack = (void *) (SP_sig(context) & ~3);
+
+ /* check for page fault inside the thread stack */
+ if (get_trap_code(context) == TRAP_ARM_PAGEFLT &&
+ (char *)info->si_addr >= (char *)NtCurrentTeb()->DeallocationStack &&
+ (char *)info->si_addr < (char *)NtCurrentTeb()->Tib.StackBase &&
+ virtual_handle_stack_fault( info->si_addr ))
+ {
+ /* check if this was the last guard page */
+ if ((char *)info->si_addr < (char *)NtCurrentTeb()->DeallocationStack + 2*4096)
+ {
+ rec = setup_exception_record( context, stack, raise_segv_exception );
+ rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW;
+ }
+ return;
+ }
+
+ rec = setup_exception_record( context, stack, raise_segv_exception );
+ if (rec->ExceptionCode == EXCEPTION_STACK_OVERFLOW) return;
+
+ switch(get_trap_code(context))
+ {
+ /* FIXME : add trap code.*/
+ case TRAP_ARM_PAGEFLT: /* Page fault */
+ rec->ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+ rec->NumberParameters = 2;
+ //rec->ExceptionInformation[0] = (get_error_code(context) >> 1) & 0x09;
+ rec->ExceptionInformation[0] = (get_error_code(context)==31)?0:1;
+ rec->ExceptionInformation[1] = (ULONG_PTR)info->si_addr;
+ break;
+ default:
+ rec->ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+ break;
+ }
}
/**********************************************************************
--
1.5.6.1
More information about the wine-patches
mailing list