ntdll: Implement exception dispatching on ARM64EC.
This commit is contained in:
parent
daea0b9b57
commit
e6ce5d5df7
2 changed files with 219 additions and 1 deletions
|
@ -37,6 +37,20 @@
|
|||
WINE_DEFAULT_DEBUG_CHANNEL(seh);
|
||||
WINE_DECLARE_DEBUG_CHANNEL(relay);
|
||||
|
||||
|
||||
static inline CHPE_V2_CPU_AREA_INFO *get_arm64ec_cpu_area(void)
|
||||
{
|
||||
return NtCurrentTeb()->ChpeV2CpuAreaInfo;
|
||||
}
|
||||
|
||||
static inline BOOL is_valid_arm64ec_frame( ULONG_PTR frame )
|
||||
{
|
||||
if (frame & (sizeof(void*) - 1)) return FALSE;
|
||||
if (is_valid_frame( frame )) return TRUE;
|
||||
return (frame >= get_arm64ec_cpu_area()->EmulatorStackLimit &&
|
||||
frame <= get_arm64ec_cpu_area()->EmulatorStackBase);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
* syscalls
|
||||
*/
|
||||
|
@ -1485,6 +1499,178 @@ static NTSTATUS WINAPI LdrpSetX64Information( ULONG type, ULONG_PTR input, void
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* virtual_unwind
|
||||
*/
|
||||
static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT_ARM64EC *dispatch,
|
||||
ARM64EC_NT_CONTEXT *context )
|
||||
{
|
||||
DISPATCHER_CONTEXT_NONVOLREG_ARM64 *nonvol_regs;
|
||||
DWORD64 pc = context->Pc;
|
||||
int i;
|
||||
|
||||
dispatch->ScopeIndex = 0;
|
||||
dispatch->ControlPc = pc;
|
||||
dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0;
|
||||
if (dispatch->ControlPcIsUnwound && RtlIsEcCode( pc )) pc -= 4;
|
||||
|
||||
nonvol_regs = (DISPATCHER_CONTEXT_NONVOLREG_ARM64 *)dispatch->NonVolatileRegisters;
|
||||
nonvol_regs->GpNvRegs[0] = context->X19;
|
||||
nonvol_regs->GpNvRegs[1] = context->X20;
|
||||
nonvol_regs->GpNvRegs[2] = context->X21;
|
||||
nonvol_regs->GpNvRegs[3] = context->X22;
|
||||
nonvol_regs->GpNvRegs[4] = 0;
|
||||
nonvol_regs->GpNvRegs[5] = 0;
|
||||
nonvol_regs->GpNvRegs[6] = context->X25;
|
||||
nonvol_regs->GpNvRegs[7] = context->X26;
|
||||
nonvol_regs->GpNvRegs[8] = context->X27;
|
||||
nonvol_regs->GpNvRegs[9] = 0;
|
||||
nonvol_regs->GpNvRegs[10] = context->Fp;
|
||||
for (i = 0; i < 8; i++) nonvol_regs->FpNvRegs[i] = context->V[i + 8].D[0];
|
||||
|
||||
dispatch->FunctionEntry = RtlLookupFunctionEntry( pc, &dispatch->ImageBase, dispatch->HistoryTable );
|
||||
|
||||
if (RtlVirtualUnwind2( type, dispatch->ImageBase, pc, dispatch->FunctionEntry, &context->AMD64_Context,
|
||||
NULL, &dispatch->HandlerData, &dispatch->EstablisherFrame,
|
||||
NULL, NULL, NULL, &dispatch->LanguageHandler, 0 ))
|
||||
{
|
||||
WARN( "exception data not found for pc %p\n", (void *)pc );
|
||||
return STATUS_INVALID_DISPOSITION;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* call_seh_handler
|
||||
*/
|
||||
static DWORD __attribute__((naked)) call_seh_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
|
||||
CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler )
|
||||
{
|
||||
asm( ".seh_proc call_seh_handler\n\t"
|
||||
"stp x29, x30, [sp, #-16]!\n\t"
|
||||
".seh_save_fplr_x 16\n\t"
|
||||
".seh_endprologue\n\t"
|
||||
".seh_handler nested_exception_handler, @except\n\t"
|
||||
"mov x11, x4\n\t" /* handler */
|
||||
"adr x10, $iexit_thunk$cdecl$i8$i8i8i8i8\n\t"
|
||||
"adrp x16, __os_arm64x_dispatch_icall\n\t"
|
||||
"ldr x16, [x16, #:lo12:__os_arm64x_dispatch_icall]\n\t"
|
||||
"blr x16\n\t"
|
||||
"blr x11\n\t"
|
||||
"ldp x29, x30, [sp], #16\n\t"
|
||||
"ret\n\t"
|
||||
".seh_endproc" );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* call_seh_handlers
|
||||
*
|
||||
* Call the SEH handlers.
|
||||
*/
|
||||
NTSTATUS call_seh_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context )
|
||||
{
|
||||
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
|
||||
DISPATCHER_CONTEXT_NONVOLREG_ARM64 nonvol_regs;
|
||||
UNWIND_HISTORY_TABLE table;
|
||||
DISPATCHER_CONTEXT_ARM64EC dispatch;
|
||||
ARM64EC_NT_CONTEXT context;
|
||||
NTSTATUS status;
|
||||
ULONG_PTR frame;
|
||||
DWORD res;
|
||||
|
||||
context.AMD64_Context = *orig_context;
|
||||
context.ContextFlags &= ~0x40; /* Clear xstate flag. */
|
||||
|
||||
dispatch.TargetPc = 0;
|
||||
dispatch.ContextRecord = &context.AMD64_Context;
|
||||
dispatch.HistoryTable = &table;
|
||||
dispatch.NonVolatileRegisters = nonvol_regs.Buffer;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context );
|
||||
if (status != STATUS_SUCCESS) return status;
|
||||
|
||||
unwind_done:
|
||||
if (!dispatch.EstablisherFrame) break;
|
||||
|
||||
if (!is_valid_arm64ec_frame( dispatch.EstablisherFrame ))
|
||||
{
|
||||
ERR( "invalid frame %I64x (%p-%p)\n", dispatch.EstablisherFrame,
|
||||
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
|
||||
rec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dispatch.LanguageHandler)
|
||||
{
|
||||
TRACE( "calling handler %p (rec=%p, frame=%I64x context=%p, dispatch=%p)\n",
|
||||
dispatch.LanguageHandler, rec, dispatch.EstablisherFrame, orig_context, &dispatch );
|
||||
res = call_seh_handler( rec, dispatch.EstablisherFrame, orig_context,
|
||||
&dispatch, dispatch.LanguageHandler );
|
||||
rec->ExceptionFlags &= EXCEPTION_NONCONTINUABLE;
|
||||
TRACE( "handler at %p returned %lu\n", dispatch.LanguageHandler, res );
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case ExceptionContinueExecution:
|
||||
if (rec->ExceptionFlags & EXCEPTION_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
|
||||
return STATUS_SUCCESS;
|
||||
case ExceptionContinueSearch:
|
||||
break;
|
||||
case ExceptionNestedException:
|
||||
rec->ExceptionFlags |= EXCEPTION_NESTED_CALL;
|
||||
TRACE( "nested exception\n" );
|
||||
break;
|
||||
case ExceptionCollidedUnwind:
|
||||
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
|
||||
dispatch.ControlPc, dispatch.FunctionEntry,
|
||||
&context.AMD64_Context, &dispatch.HandlerData, &frame, NULL );
|
||||
goto unwind_done;
|
||||
default:
|
||||
return STATUS_INVALID_DISPOSITION;
|
||||
}
|
||||
}
|
||||
/* hack: call wine handlers registered in the tib list */
|
||||
else while (is_valid_frame( (ULONG_PTR)teb_frame ) && (ULONG64)teb_frame < context.Sp)
|
||||
{
|
||||
TRACE( "calling TEB handler %p (rec=%p frame=%p context=%p dispatch=%p) sp=%I64x\n",
|
||||
teb_frame->Handler, rec, teb_frame, orig_context, &dispatch, context.Sp );
|
||||
res = call_seh_handler( rec, (ULONG_PTR)teb_frame, orig_context,
|
||||
&dispatch, (PEXCEPTION_ROUTINE)teb_frame->Handler );
|
||||
TRACE( "TEB handler at %p returned %lu\n", teb_frame->Handler, res );
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case ExceptionContinueExecution:
|
||||
if (rec->ExceptionFlags & EXCEPTION_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
|
||||
return STATUS_SUCCESS;
|
||||
case ExceptionContinueSearch:
|
||||
break;
|
||||
case ExceptionNestedException:
|
||||
rec->ExceptionFlags |= EXCEPTION_NESTED_CALL;
|
||||
TRACE( "nested exception\n" );
|
||||
break;
|
||||
case ExceptionCollidedUnwind:
|
||||
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
|
||||
dispatch.ControlPc, dispatch.FunctionEntry,
|
||||
&context.AMD64_Context, &dispatch.HandlerData, &frame, NULL );
|
||||
teb_frame = teb_frame->Prev;
|
||||
goto unwind_done;
|
||||
default:
|
||||
return STATUS_INVALID_DISPOSITION;
|
||||
}
|
||||
teb_frame = teb_frame->Prev;
|
||||
}
|
||||
|
||||
if (context.Sp == (ULONG64)NtCurrentTeb()->Tib.StackBase) break;
|
||||
}
|
||||
return STATUS_UNHANDLED_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* KiUserExceptionDispatcher (NTDLL.@)
|
||||
*/
|
||||
|
|
|
@ -3275,8 +3275,40 @@ static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec,
|
|||
}
|
||||
|
||||
static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame,
|
||||
CONTEXT *context, void *dispatcher )
|
||||
CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
|
||||
{
|
||||
if (is_arm64ec)
|
||||
{
|
||||
ARM64EC_NT_CONTEXT *ec_ctx = (ARM64EC_NT_CONTEXT *)context;
|
||||
DISPATCHER_CONTEXT_NONVOLREG_ARM64 *nonvol_regs;
|
||||
int i;
|
||||
|
||||
nonvol_regs = (void *)((DISPATCHER_CONTEXT_ARM64 *)dispatcher)->NonVolatileRegisters;
|
||||
ok( nonvol_regs->GpNvRegs[0] == ec_ctx->X19,
|
||||
"wrong non volatile reg x19 %I64x / %I64x\n", nonvol_regs->GpNvRegs[0], ec_ctx->X19 );
|
||||
ok( nonvol_regs->GpNvRegs[1] == ec_ctx->X20,
|
||||
"wrong non volatile reg x20 %I64x / %I64x\n", nonvol_regs->GpNvRegs[1], ec_ctx->X20 );
|
||||
ok( nonvol_regs->GpNvRegs[2] == ec_ctx->X21,
|
||||
"wrong non volatile reg x21 %I64x / %I64x\n", nonvol_regs->GpNvRegs[2], ec_ctx->X21 );
|
||||
ok( nonvol_regs->GpNvRegs[3] == ec_ctx->X22,
|
||||
"wrong non volatile reg x22 %I64x / %I64x\n", nonvol_regs->GpNvRegs[3], ec_ctx->X22 );
|
||||
ok( nonvol_regs->GpNvRegs[4] == 0, "wrong non volatile reg x23 %I64x\n", nonvol_regs->GpNvRegs[4] );
|
||||
ok( nonvol_regs->GpNvRegs[5] == 0, "wrong non volatile reg x24 %I64x\n", nonvol_regs->GpNvRegs[5] );
|
||||
ok( nonvol_regs->GpNvRegs[6] == ec_ctx->X25,
|
||||
"wrong non volatile reg x25 %I64x / %I64x\n", nonvol_regs->GpNvRegs[6], ec_ctx->X25 );
|
||||
ok( nonvol_regs->GpNvRegs[7] == ec_ctx->X26,
|
||||
"wrong non volatile reg x26 %I64x / %I64x\n", nonvol_regs->GpNvRegs[7], ec_ctx->X26 );
|
||||
ok( nonvol_regs->GpNvRegs[8] == ec_ctx->X27,
|
||||
"wrong non volatile reg x27 %I64x / %I64x\n", nonvol_regs->GpNvRegs[8], ec_ctx->X27 );
|
||||
ok( nonvol_regs->GpNvRegs[9] == 0, "wrong non volatile reg x28 %I64x\n", nonvol_regs->GpNvRegs[9] );
|
||||
ok( nonvol_regs->GpNvRegs[10] > ec_ctx->Fp, /* previous frame */
|
||||
"wrong non volatile reg x29 %I64x / %I64x\n", nonvol_regs->GpNvRegs[10], ec_ctx->Fp );
|
||||
|
||||
for (i = 0; i < NONVOL_FP_NUMREG_ARM64; i++)
|
||||
ok( nonvol_regs->FpNvRegs[i] == ec_ctx->V[i + 8].D[0],
|
||||
"wrong non volatile reg d%u %g / %g\n", i + 8,
|
||||
nonvol_regs->FpNvRegs[i] , ec_ctx->V[i + 8].D[0] );
|
||||
}
|
||||
rtlraiseexception_handler_called = 1;
|
||||
rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
|
||||
return ExceptionContinueSearch;
|
||||
|
|
Loading…
Add table
Reference in a new issue