Module: wine Branch: master Commit: 66567dbdf183d8a08453a3efb2fd87527ec0d9de URL: http://source.winehq.org/git/wine.git/?a=commit;h=66567dbdf183d8a08453a3efb2...
Author: Piotr Caban piotr@codeweavers.com Date: Wed Aug 17 12:48:00 2016 +0200
ntdll: Add support for collided unwinds.
Based on work by Alexandre Julliard.
Signed-off-by: Piotr Caban piotr@codeweavers.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/ntdll/signal_x86_64.c | 90 +++++++++++++++++++++++++++++++++++++--------- include/wine/exception.h | 1 + 2 files changed, 75 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 4c88536..79ca846 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -3376,35 +3376,69 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc, return (char *)base + handler_data->handler; }
+struct unwind_exception_frame +{ + EXCEPTION_REGISTRATION_RECORD frame; + DISPATCHER_CONTEXT *dispatch; +}; + +/********************************************************************** + * unwind_exception_handler + * + * Handler for exceptions happening while calling an unwind handler. + */ +static DWORD 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; + + /* copy the original dispatcher into the current one, except for the TargetIp */ + dispatch->ControlPc = unwind_frame->dispatch->ControlPc; + dispatch->ImageBase = unwind_frame->dispatch->ImageBase; + dispatch->FunctionEntry = unwind_frame->dispatch->FunctionEntry; + dispatch->EstablisherFrame = unwind_frame->dispatch->EstablisherFrame; + dispatch->ContextRecord = unwind_frame->dispatch->ContextRecord; + dispatch->LanguageHandler = unwind_frame->dispatch->LanguageHandler; + dispatch->HandlerData = unwind_frame->dispatch->HandlerData; + dispatch->HistoryTable = unwind_frame->dispatch->HistoryTable; + dispatch->ScopeIndex = unwind_frame->dispatch->ScopeIndex; + TRACE( "detected collided unwind\n" ); + return ExceptionCollidedUnwind; +}
/********************************************************************** * call_unwind_handler * * Call a single unwind handler. - * FIXME: Handle nested exceptions. */ -static void call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch ) +static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch ) { + struct unwind_exception_frame frame; DWORD res;
- dispatch->ControlPc = dispatch->ContextRecord->Rip; + frame.frame.Handler = unwind_exception_handler; + frame.dispatch = dispatch; + __wine_push_frame( &frame.frame );
TRACE( "calling handler %p (rec=%p, frame=0x%lx context=%p, dispatch=%p)\n", dispatch->LanguageHandler, rec, dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); res = dispatch->LanguageHandler( rec, dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); TRACE( "handler %p returned %x\n", dispatch->LanguageHandler, res );
+ __wine_pop_frame( &frame.frame ); + switch (res) { case ExceptionContinueSearch: - break; case ExceptionCollidedUnwind: - FIXME( "ExceptionCollidedUnwind not supported yet\n" ); break; default: raise_status( STATUS_INVALID_DISPOSITION, rec ); break; } + + return res; }
@@ -3412,30 +3446,28 @@ static void call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *disp * call_teb_unwind_handler * * Call a single unwind handler from the TEB chain. - * FIXME: Handle nested exceptions. */ -static void call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch, +static DWORD call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch, EXCEPTION_REGISTRATION_RECORD *teb_frame ) { - EXCEPTION_REGISTRATION_RECORD *dispatcher; DWORD res;
- TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatcher=%p)\n", - teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, &dispatcher ); - res = teb_frame->Handler( rec, teb_frame, dispatch->ContextRecord, &dispatcher ); + TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n", + teb_frame->Handler, rec, teb_frame, dispatch->ContextRecord, dispatch ); + res = teb_frame->Handler( rec, teb_frame, dispatch->ContextRecord, (EXCEPTION_REGISTRATION_RECORD**)dispatch ); TRACE( "handler at %p returned %u\n", teb_frame->Handler, res );
switch (res) { case ExceptionContinueSearch: - break; case ExceptionCollidedUnwind: - FIXME( "ExceptionCollidedUnwind not supported yet\n" ); break; default: raise_status( STATUS_INVALID_DISPOSITION, rec ); break; } + + return res; }
@@ -3588,7 +3620,8 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec /* FIXME: should use the history table to make things faster */
dispatch.ImageBase = 0; - dispatch.ScopeIndex = 0; /* FIXME */ + dispatch.ScopeIndex = 0; + dispatch.ControlPc = context->Rip;
/* first look for PE exception information */
@@ -3674,7 +3707,19 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec raise_status( STATUS_INVALID_UNWIND_TARGET, rec ); } if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EH_TARGET_UNWIND; - call_unwind_handler( rec, &dispatch ); + if (call_unwind_handler( rec, &dispatch ) == ExceptionCollidedUnwind) + { + ULONG64 frame; + + *context = new_context = *dispatch.ContextRecord; + dispatch.ContextRecord = context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &new_context, NULL, &frame, NULL ); + rec->ExceptionFlags |= EH_COLLIDED_UNWIND; + goto unwind_done; + } + rec->ExceptionFlags &= ~EH_COLLIDED_UNWIND; } else /* hack: call builtin handlers registered in the tib list */ { @@ -3683,7 +3728,20 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec { TRACE( "found builtin frame %p handler %p\n", teb_frame, teb_frame->Handler ); dispatch.EstablisherFrame = (ULONG64)teb_frame; - call_teb_unwind_handler( rec, &dispatch, teb_frame ); + if (call_teb_unwind_handler( rec, &dispatch, teb_frame ) == ExceptionCollidedUnwind) + { + ULONG64 frame; + + teb_frame = __wine_pop_frame( teb_frame ); + + *context = new_context = *dispatch.ContextRecord; + dispatch.ContextRecord = context; + RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, + dispatch.ControlPc, dispatch.FunctionEntry, + &new_context, NULL, &frame, NULL ); + rec->ExceptionFlags |= EH_COLLIDED_UNWIND; + goto unwind_done; + } teb_frame = __wine_pop_frame( teb_frame ); } if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break; diff --git a/include/wine/exception.h b/include/wine/exception.h index 8b807c1..f275568 100644 --- a/include/wine/exception.h +++ b/include/wine/exception.h @@ -253,6 +253,7 @@ static inline EXCEPTION_REGISTRATION_RECORD *__wine_get_frame(void) #define EH_STACK_INVALID 0x08 #define EH_NESTED_CALL 0x10 #define EH_TARGET_UNWIND 0x20 +#define EH_COLLIDED_UNWIND 0x40
/* Wine-specific exceptions codes */