Commit cf08bbaa authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Use a common wrapper to call unwind handlers on x86-64.

parent 24e9fcac
...@@ -391,6 +391,9 @@ struct unwind_exception_frame ...@@ -391,6 +391,9 @@ struct unwind_exception_frame
* save space when .seh handler is used. */ * save space when .seh handler is used. */
DISPATCHER_CONTEXT *dispatch; DISPATCHER_CONTEXT *dispatch;
}; };
C_ASSERT( sizeof(struct unwind_exception_frame) == 0x28 );
C_ASSERT( offsetof(struct unwind_exception_frame, dispatch) == 0x20 );
/********************************************************************** /**********************************************************************
* unwind_exception_handler * unwind_exception_handler
...@@ -418,30 +421,26 @@ DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR ...@@ -418,30 +421,26 @@ DWORD __cdecl unwind_exception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR
return ExceptionCollidedUnwind; return ExceptionCollidedUnwind;
} }
/***********************************************************************
* unwind_handler_call_wrapper /**********************************************************************
* call_unwind_handler
*/ */
#ifdef __WINE_PE_BUILD #ifdef __WINE_PE_BUILD
DWORD WINAPI unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, DWORD WINAPI call_unwind_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
CONTEXT *context, DISPATCHER_CONTEXT *dispatch ); CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler );
__ASM_GLOBAL_FUNC( call_unwind_handler,
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,
"subq $0x28,%rsp\n\t" "subq $0x28,%rsp\n\t"
".seh_stackalloc 0x28\n\t" ".seh_stackalloc 0x28\n\t"
".seh_endprologue\n\t" ".seh_endprologue\n\t"
".seh_handler unwind_exception_handler, @except, @unwind\n\t"
"movq %r9,0x20(%rsp)\n\t" /* unwind_exception_frame->dispatch */ "movq %r9,0x20(%rsp)\n\t" /* unwind_exception_frame->dispatch */
"callq *0x30(%r9)\n\t" /* dispatch->LanguageHandler */ "callq *0x50(%rsp)\n\t" /* handler */
"nop\n\t" /* avoid epilogue so handler is called */ "nop\n\t" /* avoid epilogue so handler is called */
"addq $0x28, %rsp\n\t" "addq $0x28, %rsp\n\t"
"ret\n\t" "ret" )
".seh_handler " __ASM_NAME("unwind_exception_handler") ", @except, @unwind\n\t" )
#else #else
static DWORD unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, static DWORD call_unwind_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler )
{ {
struct unwind_exception_frame wrapper_frame; struct unwind_exception_frame wrapper_frame;
DWORD res; DWORD res;
...@@ -449,68 +448,12 @@ static DWORD unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame, ...@@ -449,68 +448,12 @@ static DWORD unwind_handler_call_wrapper( EXCEPTION_RECORD *rec, void *frame,
wrapper_frame.frame.Handler = unwind_exception_handler; wrapper_frame.frame.Handler = unwind_exception_handler;
wrapper_frame.dispatch = dispatch; wrapper_frame.dispatch = dispatch;
__wine_push_frame( &wrapper_frame.frame ); __wine_push_frame( &wrapper_frame.frame );
res = dispatch->LanguageHandler( rec, (void *)dispatch->EstablisherFrame, dispatch->ContextRecord, dispatch ); res = handler( rec, (void *)frame, context, dispatch );
__wine_pop_frame( &wrapper_frame.frame ); __wine_pop_frame( &wrapper_frame.frame );
return res; return res;
} }
#endif #endif
/**********************************************************************
* call_unwind_handler
*
* Call a single unwind handler.
*/
DWORD call_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch )
{
DWORD res;
TRACE( "calling handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
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 );
switch (res)
{
case ExceptionContinueSearch:
case ExceptionCollidedUnwind:
break;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
}
return res;
}
/**********************************************************************
* call_teb_unwind_handler
*
* Call a single unwind handler from the TEB chain.
*/
static DWORD call_teb_unwind_handler( EXCEPTION_RECORD *rec, DISPATCHER_CONTEXT *dispatch,
EXCEPTION_REGISTRATION_RECORD *teb_frame )
{
DWORD res;
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 %lu\n", teb_frame->Handler, res );
switch (res)
{
case ExceptionContinueSearch:
case ExceptionCollidedUnwind:
break;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
}
return res;
}
/********************************************************************** /**********************************************************************
* call_consolidate_callback * call_consolidate_callback
...@@ -691,7 +634,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec ...@@ -691,7 +634,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
CONTEXT new_context; CONTEXT new_context;
NTSTATUS status; NTSTATUS status;
ULONG_PTR frame; ULONG_PTR frame;
DWORD i; DWORD i, res;
RtlCaptureContext( context ); RtlCaptureContext( context );
new_context = *context; new_context = *context;
...@@ -744,40 +687,62 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec ...@@ -744,40 +687,62 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
raise_status( STATUS_INVALID_UNWIND_TARGET, rec ); raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
} }
if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EXCEPTION_TARGET_UNWIND; if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
if (call_unwind_handler( rec, &dispatch ) == ExceptionCollidedUnwind) TRACE( "calling handler %p (rec=%p, frame=%I64x context=%p, dispatch=%p)\n",
dispatch.LanguageHandler, rec, dispatch.EstablisherFrame,
dispatch.ContextRecord, &dispatch );
res = call_unwind_handler( rec, dispatch.EstablisherFrame, dispatch.ContextRecord,
&dispatch, dispatch.LanguageHandler );
TRACE( "handler %p returned %lx\n", dispatch.LanguageHandler, res );
switch (res)
{ {
case ExceptionContinueSearch:
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
break;
case ExceptionCollidedUnwind:
new_context = *context; new_context = *context;
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry, dispatch.ControlPc, dispatch.FunctionEntry,
&new_context, NULL, &frame, NULL ); &new_context, NULL, &frame, NULL );
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
goto unwind_done; goto unwind_done;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
} }
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
} }
else /* hack: call builtin handlers registered in the tib list */ else /* hack: call builtin handlers registered in the tib list */
{ {
DWORD64 backup_frame = dispatch.EstablisherFrame;
while (is_valid_frame( (ULONG_PTR)teb_frame ) && while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
(ULONG64)teb_frame < new_context.Rsp && (ULONG64)teb_frame < new_context.Rsp &&
(ULONG64)teb_frame < (ULONG64)end_frame) (ULONG64)teb_frame < (ULONG64)end_frame)
{ {
TRACE( "found builtin frame %p handler %p\n", teb_frame, teb_frame->Handler ); TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
dispatch.EstablisherFrame = (ULONG64)teb_frame; teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
if (call_teb_unwind_handler( rec, &dispatch, teb_frame ) == ExceptionCollidedUnwind) res = call_unwind_handler( rec, (ULONG_PTR)teb_frame, dispatch.ContextRecord, &dispatch,
(PEXCEPTION_ROUTINE)teb_frame->Handler );
TRACE( "handler at %p returned %lu\n", teb_frame->Handler, res );
teb_frame = __wine_pop_frame( teb_frame );
switch (res)
{ {
teb_frame = __wine_pop_frame( teb_frame ); case ExceptionContinueSearch:
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
break;
case ExceptionCollidedUnwind:
new_context = *context; new_context = *context;
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase, RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry, dispatch.ControlPc, dispatch.FunctionEntry,
&new_context, NULL, &frame, NULL ); &new_context, &dispatch.HandlerData,
&frame, NULL );
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
goto unwind_done; goto unwind_done;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
} }
teb_frame = __wine_pop_frame( teb_frame );
} }
if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break; if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break;
dispatch.EstablisherFrame = backup_frame;
} }
if (dispatch.EstablisherFrame == (ULONG64)end_frame) break; if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment