This test is supposed to show that the Eip field in the context is fixed up 
somewhere inside RtlRaiseException
or NtRaiseException (test for NtRaiseException not included) for (only) 
EXCEPTION_BREAKPOINT exception
For others context.Eip is not changed.

Since real exceptions on wine also take that path i guess it is best to 
implement context modifing in there.
The next patches will show what is the value of context.Eip in a Vectored 
exception handler
(I still have some problems with this patch)

After that i plan to wrap a debugger around the test to show that the debugger 
gets a unmodified context
The ultimate goal is to fix the "Handling of EXCEPTION_BREAKPOINT"  problem 
Vitaliy Margolen reported 
( http://bugs.winehq.org/show_bug.cgi?id=7063 )
Patch tested on XP/2k

Comments, suggestions?

Greetings Peter
---
 dlls/ntdll/tests/exception.c |   85 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 85 insertions(+), 0 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index d18f3d2..8fbfee6 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -39,6 +39,7 @@
 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
+static NTSTATUS  (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
 static void *code_mem;
 
 /* Test various instruction combinations that cause a protection fault on the i386,
@@ -184,6 +185,79 @@ static void run_exception_test(const void *handler, const void* context,
     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
 }
 
+static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+                      CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
+{
+    trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
+           rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
+
+    todo_wine {
+    ok((DWORD)rec->ExceptionAddress == (DWORD)code_mem + 0xb, "ExceptionAddress at %p instead of %x\n",
+       rec->ExceptionAddress, (DWORD)code_mem + 0xb);
+    }
+
+    /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
+     * even if raised by RtlRaiseException
+     */
+    if(rec->ExceptionCode == EXCEPTION_BREAKPOINT) 
+    {
+        todo_wine {
+        ok((DWORD)context->Eip == (DWORD)code_mem + 0xa, "Eip at %x instead of %x\n",
+           context->Eip, (DWORD)code_mem + 0xa);
+        }
+    }
+    else
+    {
+        ok((DWORD)context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
+           context->Eip, (DWORD)code_mem + 0xb);
+    }
+
+    /* Eip in context is decreased by 1
+     * Increase it again, else execution will continue in the middle of a instruction */
+    if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && ((DWORD)context->Eip == (DWORD)code_mem + 0xa))
+        context->Eip += 1;
+    return ExceptionContinueExecution;
+}
+
+
+static const BYTE call_one_arg_code[] = {
+        0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
+        0x50,                   /* push %eax */
+        0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
+        0xff, 0xd0,             /* call *%eax */
+        0x90,                   /* nop */
+        0x90,                   /* nop */
+        0x90,                   /* nop */
+        0x90,                   /* nop */
+        0xc3,                   /* ret */
+};
+
+
+static void run_rtlraiseexception_test(DWORD exceptioncode)
+{
+
+    EXCEPTION_REGISTRATION_RECORD frame;
+    EXCEPTION_RECORD record;
+
+    void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
+
+    record.ExceptionCode = exceptioncode;
+    record.ExceptionFlags = 0;
+    record.ExceptionRecord = NULL;
+    record.ExceptionAddress = NULL; // does not matter, copied return address
+    record.NumberParameters = 0;
+
+    frame.Handler = rtlraiseexception_handler;
+    frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+
+    memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
+
+    pNtCurrentTeb()->Tib.ExceptionList = &frame;
+    func(pRtlRaiseException, &record);
+    pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+}
+
+
 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
 {
@@ -391,6 +465,8 @@ static void test_exceptions(void)
 
     pNtGetContextThread = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtGetContextThread" );
     pNtSetContextThread = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtSetContextThread" );
+    pRtlRaiseException  = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "RtlRaiseException" );
+
     if (!pNtGetContextThread || !pNtSetContextThread)
     {
         trace( "NtGetContextThread/NtSetContextThread not found, skipping tests\n" );
@@ -400,6 +476,15 @@ static void test_exceptions(void)
     /* test handling of debug registers */
     run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code));
 
+    if (pRtlRaiseException)
+    {
+        run_rtlraiseexception_test(0x12345);
+        run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
+        run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
+    }
+    else
+        skip( "RtlRaiseException not found\n" );
+
     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
     res = pNtGetContextThread(GetCurrentThread(), &ctx);
     ok (res == STATUS_SUCCESS,"NtGetContextThread failed with %x\n", res);


Reply via email to