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