Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52396
-- v2: ntdll: Use .seh handler in call_unwind_handler() on x64. ntdll/tests: Add test for collided unwind. ntdll: Use .seh handler in call_handler() on x64. ntdll: Set EH_NESTED_CALL flag in call_stack_handlers() on x64.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/signal_x86_64.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index f546aa15d94..014e00ec31f 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -351,7 +351,7 @@ static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) - rec->ExceptionFlags |= EH_NESTED_CALL; + return ExceptionNestedException;
return ExceptionContinueSearch; } @@ -360,7 +360,6 @@ static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_ * call_handler * * Call a single exception handler. - * FIXME: Handle nested exceptions. */ static DWORD call_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) { @@ -447,7 +446,8 @@ static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_contex case ExceptionContinueSearch: break; case ExceptionNestedException: - FIXME_(seh)( "nested exception\n" ); + rec->ExceptionFlags |= EH_NESTED_CALL; + TRACE_(seh)( "nested exception\n" ); break; case ExceptionCollidedUnwind: { ULONG64 frame; @@ -477,7 +477,8 @@ static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_contex case ExceptionContinueSearch: break; case ExceptionNestedException: - FIXME_(seh)( "nested exception\n" ); + rec->ExceptionFlags |= EH_NESTED_CALL; + TRACE_(seh)( "nested exception\n" ); break; case ExceptionCollidedUnwind: { ULONG64 frame;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/signal_x86_64.c | 46 +++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 014e00ec31f..f2419bfeb67 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -347,8 +347,10 @@ __ASM_GLOBAL_FUNC( RtlCaptureContext, "fxsave 0x100(%rcx)\n\t" /* context->FltSave */ "ret" );
-static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, - CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) DECLSPEC_HIDDEN; +DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) return ExceptionNestedException; @@ -356,6 +358,39 @@ static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_ return ExceptionContinueSearch; }
+/*********************************************************************** + * exception_handler_call_wrapper + */ +#ifdef __ASM_SEH_SUPPORTED +DWORD WINAPI exception_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ); + +C_ASSERT( offsetof(DISPATCHER_CONTEXT, LanguageHandler) == 0x30 ); + +__ASM_GLOBAL_FUNC( exception_handler_call_wrapper, + ".seh_endprologue\n\t" + "subq $0x28, %rsp\n\t" + ".seh_stackalloc 0x28\n\t" + "callq *0x30(%r9)\n\t" /* dispatch->LanguageHandler */ + "nop\n\t" /* avoid epilogue so handler is called */ + "addq $0x28, %rsp\n\t" + "ret\n\t" + ".seh_handler " __ASM_NAME("nested_exception_handler") ", @except\n\t" ) +#else +static DWORD exception_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) +{ + EXCEPTION_REGISTRATION_RECORD wrapper_frame; + DWORD res; + + wrapper_frame.Handler = nested_exception_handler; + __wine_push_frame( &wrapper_frame ); + res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, context, dispatch ); + __wine_pop_frame( &wrapper_frame ); + return res; +} +#endif + /********************************************************************** * call_handler * @@ -363,19 +398,14 @@ static DWORD __cdecl nested_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_ */ static DWORD call_handler( EXCEPTION_RECORD *rec, CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) { - EXCEPTION_REGISTRATION_RECORD frame; DWORD res;
- frame.Handler = nested_exception_handler; - __wine_push_frame( &frame ); - TRACE_(seh)( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n", dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); - res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, context, dispatch ); + res = exception_handler_call_wrapper( rec, (void *)dispatch->EstablisherFrame, context, dispatch ); TRACE_(seh)( "handler at %p returned %lu\n", dispatch->LanguageHandler, res );
rec->ExceptionFlags &= EH_NONCONTINUABLE; - __wine_pop_frame( &frame ); return res; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 106 ++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 15 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 6164c799ba1..94dcb495f7a 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -3030,9 +3030,9 @@ static const struct exception
static int got_exception;
-static void run_exception_test(void *handler, const void* context, +static void run_exception_test_flags(void *handler, const void* context, const void *code, unsigned int code_size, - DWORD access) + DWORD access, DWORD handler_flags) { unsigned char buf[8 + 6 + 8 + 8]; RUNTIME_FUNCTION runtime_func; @@ -3045,7 +3045,7 @@ static void run_exception_test(void *handler, const void* context, runtime_func.UnwindData = 0x1000;
unwind->Version = 1; - unwind->Flags = UNW_FLAG_EHANDLER; + unwind->Flags = handler_flags; unwind->SizeOfProlog = 0; unwind->CountOfCodes = 0; unwind->FrameRegister = 0; @@ -3072,6 +3072,13 @@ static void run_exception_test(void *handler, const void* context, VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2); }
+static void run_exception_test(void *handler, const void* context, + const void *code, unsigned int code_size, + DWORD access) +{ + run_exception_test_flags(handler, context, code, code_size, access, UNW_FLAG_EHANDLER); +} + static DWORD WINAPI handler( EXCEPTION_RECORD *rec, ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) { @@ -4962,25 +4969,93 @@ static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRAT return ExceptionContinueExecution; }
-static void test_nested_exception(void) +static const BYTE nested_except_code[] = { - static const BYTE except_code[] = - { - 0xe8, 0x02, 0x00, 0x00, 0x00, /* call nest */ - 0x90, /* nop */ - 0xc3, /* ret */ - /* nest: */ - 0xcc, /* int3 */ - 0x90, /* nop */ - 0xc3, /* ret */ - }; + 0xe8, 0x02, 0x00, 0x00, 0x00, /* call nest */ + 0x90, /* nop */ + 0xc3, /* ret */ + /* nest: */ + 0xcc, /* int3 */ + 0x90, /* nop */ + 0xc3, /* ret */ +};
+static void test_nested_exception(void) +{ got_nested_exception = got_prev_frame_exception = FALSE; - run_exception_test(nested_exception_handler, NULL, except_code, ARRAY_SIZE(except_code), PAGE_EXECUTE_READ); + run_exception_test(nested_exception_handler, NULL, nested_except_code, ARRAY_SIZE(nested_except_code), PAGE_EXECUTE_READ); ok(got_nested_exception, "Did not get nested exception.\n"); ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n"); }
+static unsigned int collided_unwind_exception_count; + +static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + CONTEXT ctx; + + trace("collided_exception_handler Rip %p, Rsp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n", + (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame); + + switch(collided_unwind_exception_count++) + { + case 0: + /* Initial exception from nested_except_code. */ + ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode); + nested_exception_initial_frame = frame; + /* Start unwind. */ + pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 1: + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == EH_UNWINDING, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)context->Rip == (char *)code_mem + 7, "got %p.\n", rec->ExceptionAddress); + /* generate exception in unwind handler. */ + RaiseException(0xdeadbeef, 0, 0, 0); + ok(0, "shouldn't be reached\n"); + break; + case 2: + /* Inner call frame, continue search. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 3: + /* Top level call frame, handle exception by unwinding. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 4: + /* Collided unwind. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EH_UNWINDING | EH_COLLIDED_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 5: + /* EH_COLLIDED_UNWIND cleared for the following frames. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EH_UNWINDING | EH_TARGET_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, + (char *)nested_exception_initial_frame + 8); + break; + } + return ExceptionContinueSearch; +} + +static void test_collided_unwind(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + collided_unwind_exception_count = 0; + run_exception_test_flags(collided_exception_handler, NULL, nested_except_code, ARRAY_SIZE(nested_except_code), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER); + ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count); +} + static CONTEXT test_unwind_apc_context; static BOOL test_unwind_apc_called;
@@ -11561,6 +11636,7 @@ START_TEST(exception) test_wow64_context(); test_kiuserexceptiondispatcher(); test_nested_exception(); + test_collided_unwind();
if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind();
From: Paul Gofman pgofman@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52396 --- dlls/ntdll/signal_x86_64.c | 56 ++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index f2419bfeb67..1c8ab1d89e2 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -1032,6 +1032,8 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc, struct unwind_exception_frame { EXCEPTION_REGISTRATION_RECORD frame; + char dummy[0x10]; /* Layout 'dispatch' accessed from unwind_exception_handler() so it is above register + * save space when .seh handler is used. */ DISPATCHER_CONTEXT *dispatch; };
@@ -1040,8 +1042,10 @@ struct unwind_exception_frame * * Handler for exceptions happening while calling an unwind handler. */ -static DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, - CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) DECLSPEC_HIDDEN; +DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { struct unwind_exception_frame *unwind_frame = (struct unwind_exception_frame *)frame; DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher; @@ -1060,27 +1064,57 @@ static DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_ return ExceptionCollidedUnwind; }
+/*********************************************************************** + * unwind_handler_call_wrapper + */ +#ifdef __ASM_SEH_SUPPORTED +DWORD WINAPI unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ); + +C_ASSERT( sizeof(struct unwind_exception_frame) == 0x28 ); +C_ASSERT( offsetof(struct unwind_exception_frame, dispatch) == 0x20 ); +C_ASSERT( offsetof(DISPATCHER_CONTEXT, LanguageHandler) == 0x30 ); + +__ASM_GLOBAL_FUNC( unwind_handler_call_wrapper, + ".seh_endprologue\n\t" + "subq $0x28,%rsp\n\t" + ".seh_stackalloc 0x28\n\t" + "movq %r9,0x20(%rsp)\n\t" /* unwind_exception_frame->dispatch */ + "callq *0x30(%r9)\n\t" /* dispatch->LanguageHandler */ + "nop\n\t" /* avoid epilogue so handler is called */ + "addq $0x28, %rsp\n\t" + "ret\n\t" + ".seh_handler " __ASM_NAME("unwind_exception_handler") ", @except, @unwind\n\t" ) +#else +static DWORD unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) +{ + struct unwind_exception_frame wrapper_frame; + DWORD res; + + wrapper_frame.frame.Handler = unwind_exception_handler; + wrapper_frame.dispatch = dispatch; + __wine_push_frame( &wrapper_frame.frame ); + res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); + __wine_pop_frame( &wrapper_frame.frame ); + return res; +} +#endif + /********************************************************************** * call_unwind_handler * * Call a single unwind handler. */ -static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch ) +DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch ) { - struct unwind_exception_frame frame; DWORD res;
- frame.frame.Handler = unwind_exception_handler; - frame.dispatch = dispatch; - __wine_push_frame( &frame.frame ); - TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n", dispatch->LanguageHandler, rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); - res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); + res = unwind_handler_call_wrapper( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); TRACE( "handler %p returned %lx\n", dispatch->LanguageHandler, res );
- __wine_pop_frame( &frame.frame ); - switch (res) { case ExceptionContinueSearch: