mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-07 19:08:00 +00:00
810 lines
34 KiB
C
810 lines
34 KiB
C
/*
|
|
* ARM64 signal handling routines
|
|
*
|
|
* Copyright 2010-2013 André Hentschel
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#ifdef __aarch64__
|
|
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <setjmp.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winternl.h"
|
|
#include "wine/exception.h"
|
|
#include "ntdll_misc.h"
|
|
#include "wine/debug.h"
|
|
#include "ntsyscalls.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(seh);
|
|
WINE_DECLARE_DEBUG_CHANNEL(relay);
|
|
|
|
|
|
/*******************************************************************
|
|
* syscalls
|
|
*/
|
|
#define SYSCALL_ENTRY(id,name,args) __ASM_SYSCALL_FUNC( id, name )
|
|
ALL_SYSCALLS64
|
|
#undef SYSCALL_ENTRY
|
|
|
|
|
|
/**************************************************************************
|
|
* __chkstk (NTDLL.@)
|
|
*
|
|
* Supposed to touch all the stack pages, but we shouldn't need that.
|
|
*/
|
|
__ASM_GLOBAL_FUNC( __chkstk, "ret")
|
|
|
|
|
|
/***********************************************************************
|
|
* RtlCaptureContext (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( RtlCaptureContext,
|
|
"str xzr, [x0, #0x8]\n\t" /* context->X0 */
|
|
"stp x1, x2, [x0, #0x10]\n\t" /* context->X1,X2 */
|
|
"stp x3, x4, [x0, #0x20]\n\t" /* context->X3,X4 */
|
|
"stp x5, x6, [x0, #0x30]\n\t" /* context->X5,X6 */
|
|
"stp x7, x8, [x0, #0x40]\n\t" /* context->X7,X8 */
|
|
"stp x9, x10, [x0, #0x50]\n\t" /* context->X9,X10 */
|
|
"stp x11, x12, [x0, #0x60]\n\t" /* context->X11,X12 */
|
|
"stp x13, x14, [x0, #0x70]\n\t" /* context->X13,X14 */
|
|
"stp x15, x16, [x0, #0x80]\n\t" /* context->X15,X16 */
|
|
"stp x17, x18, [x0, #0x90]\n\t" /* context->X17,X18 */
|
|
"stp x19, x20, [x0, #0xa0]\n\t" /* context->X19,X20 */
|
|
"stp x21, x22, [x0, #0xb0]\n\t" /* context->X21,X22 */
|
|
"stp x23, x24, [x0, #0xc0]\n\t" /* context->X23,X24 */
|
|
"stp x25, x26, [x0, #0xd0]\n\t" /* context->X25,X26 */
|
|
"stp x27, x28, [x0, #0xe0]\n\t" /* context->X27,X28 */
|
|
"stp x29, xzr, [x0, #0xf0]\n\t" /* context->Fp,Lr */
|
|
"mov x1, sp\n\t"
|
|
"stp x1, x30, [x0, #0x100]\n\t" /* context->Sp,Pc */
|
|
"stp q0, q1, [x0, #0x110]\n\t" /* context->V[0-1] */
|
|
"stp q2, q3, [x0, #0x130]\n\t" /* context->V[2-3] */
|
|
"stp q4, q5, [x0, #0x150]\n\t" /* context->V[4-5] */
|
|
"stp q6, q7, [x0, #0x170]\n\t" /* context->V[6-7] */
|
|
"stp q8, q9, [x0, #0x190]\n\t" /* context->V[8-9] */
|
|
"stp q10, q11, [x0, #0x1b0]\n\t" /* context->V[10-11] */
|
|
"stp q12, q13, [x0, #0x1d0]\n\t" /* context->V[12-13] */
|
|
"stp q14, q15, [x0, #0x1f0]\n\t" /* context->V[14-15] */
|
|
"stp q16, q17, [x0, #0x210]\n\t" /* context->V[16-17] */
|
|
"stp q18, q19, [x0, #0x230]\n\t" /* context->V[18-19] */
|
|
"stp q20, q21, [x0, #0x250]\n\t" /* context->V[20-21] */
|
|
"stp q22, q23, [x0, #0x270]\n\t" /* context->V[22-23] */
|
|
"stp q24, q25, [x0, #0x290]\n\t" /* context->V[24-25] */
|
|
"stp q26, q27, [x0, #0x2b0]\n\t" /* context->V[26-27] */
|
|
"stp q28, q29, [x0, #0x2d0]\n\t" /* context->V[28-29] */
|
|
"stp q30, q31, [x0, #0x2f0]\n\t" /* context->V[30-31] */
|
|
"mov w1, #0x400000\n\t" /* CONTEXT_ARM64 */
|
|
"movk w1, #0x7\n\t" /* CONTEXT_FULL */
|
|
"str w1, [x0]\n\t" /* context->ContextFlags */
|
|
"mrs x1, NZCV\n\t"
|
|
"str w1, [x0, #0x4]\n\t" /* context->Cpsr */
|
|
"mrs x1, FPCR\n\t"
|
|
"str w1, [x0, #0x310]\n\t" /* context->Fpcr */
|
|
"mrs x1, FPSR\n\t"
|
|
"str w1, [x0, #0x314]\n\t" /* context->Fpsr */
|
|
"ret" )
|
|
|
|
|
|
/**********************************************************************
|
|
* virtual_unwind
|
|
*/
|
|
static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, 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) pc -= 4;
|
|
|
|
nonvol_regs = (DISPATCHER_CONTEXT_NONVOLREG_ARM64 *)dispatch->NonVolatileRegisters;
|
|
memcpy( nonvol_regs->GpNvRegs, &context->X19, sizeof(nonvol_regs->GpNvRegs) );
|
|
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,
|
|
NULL, &dispatch->HandlerData, &dispatch->EstablisherFrame,
|
|
NULL, NULL, NULL, &dispatch->LanguageHandler, 0 ))
|
|
{
|
|
WARN( "exception data not found for pc %p, lr %p\n", (void *)pc, (void *)context->Lr );
|
|
return STATUS_INVALID_DISPOSITION;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* unwind_exception_handler
|
|
*
|
|
* Handler for exceptions happening while calling an unwind handler.
|
|
*/
|
|
EXCEPTION_DISPOSITION WINAPI unwind_exception_handler( EXCEPTION_RECORD *record, void *frame,
|
|
CONTEXT *context, DISPATCHER_CONTEXT *dispatch )
|
|
{
|
|
DISPATCHER_CONTEXT *orig_dispatch = ((DISPATCHER_CONTEXT **)frame)[-2];
|
|
|
|
/* copy the original dispatcher into the current one, except for the TargetPc */
|
|
dispatch->ControlPc = orig_dispatch->ControlPc;
|
|
dispatch->ImageBase = orig_dispatch->ImageBase;
|
|
dispatch->FunctionEntry = orig_dispatch->FunctionEntry;
|
|
dispatch->EstablisherFrame = orig_dispatch->EstablisherFrame;
|
|
dispatch->LanguageHandler = orig_dispatch->LanguageHandler;
|
|
dispatch->HandlerData = orig_dispatch->HandlerData;
|
|
dispatch->HistoryTable = orig_dispatch->HistoryTable;
|
|
dispatch->ScopeIndex = orig_dispatch->ScopeIndex;
|
|
dispatch->ControlPcIsUnwound = orig_dispatch->ControlPcIsUnwound;
|
|
*dispatch->ContextRecord = *orig_dispatch->ContextRecord;
|
|
memcpy( dispatch->NonVolatileRegisters, orig_dispatch->NonVolatileRegisters,
|
|
sizeof(DISPATCHER_CONTEXT_NONVOLREG_ARM64) );
|
|
TRACE( "detected collided unwind\n" );
|
|
return ExceptionCollidedUnwind;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* call_unwind_handler
|
|
*/
|
|
DWORD WINAPI call_unwind_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
|
|
CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler );
|
|
__ASM_GLOBAL_FUNC( call_unwind_handler,
|
|
"stp x29, x30, [sp, #-32]!\n\t"
|
|
".seh_save_fplr_x 32\n\t"
|
|
".seh_endprologue\n\t"
|
|
".seh_handler unwind_exception_handler, @except\n\t"
|
|
"str x3, [sp, #16]\n\t" /* frame[-2] = dispatch */
|
|
"blr x4\n\t"
|
|
"ldp x29, x30, [sp], #32\n\t"
|
|
"ret" )
|
|
|
|
|
|
/***********************************************************************
|
|
* call_seh_handler
|
|
*/
|
|
DWORD WINAPI call_seh_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
|
|
CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler );
|
|
__ASM_GLOBAL_FUNC( call_seh_handler,
|
|
"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"
|
|
"blr x4\n\t"
|
|
"ldp x29, x30, [sp], #16\n\t"
|
|
"ret" )
|
|
|
|
|
|
/**********************************************************************
|
|
* 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 dispatch;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
ULONG_PTR frame;
|
|
DWORD res;
|
|
|
|
context = *orig_context;
|
|
dispatch.TargetPc = 0;
|
|
dispatch.ContextRecord = &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_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, &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, &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.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
|
|
".seh_context\n\t"
|
|
".seh_endprologue\n\t"
|
|
"adr x16, " __ASM_NAME("pWow64PrepareForException") "\n\t"
|
|
"ldr x16, [x16]\n\t"
|
|
"cbz x16, 1f\n\t"
|
|
"add x0, sp, #0x390\n\t" /* rec (context + 1) */
|
|
"mov x1, sp\n\t" /* context */
|
|
"blr x16\n"
|
|
"1:\tadd x0, sp, #0x390\n\t" /* rec (context + 1) */
|
|
"mov x1, sp\n\t" /* context */
|
|
"bl " __ASM_NAME("dispatch_exception") "\n\t"
|
|
"brk #1" )
|
|
|
|
|
|
/*******************************************************************
|
|
* KiUserApcDispatcher (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( KiUserApcDispatcher,
|
|
".seh_context\n\t"
|
|
"nop\n\t"
|
|
".seh_stackalloc 0x30\n\t"
|
|
".seh_endprologue\n\t"
|
|
"ldp x16, x0, [sp]\n\t" /* func, arg1 */
|
|
"ldp x1, x2, [sp, #0x10]\n\t" /* arg2, arg3 */
|
|
"add x3, sp, #0x30\n\t" /* context (FIXME) */
|
|
"blr x16\n\t"
|
|
"add x0, sp, #0x30\n\t" /* context */
|
|
"ldr w1, [sp, #0x20]\n\t" /* alertable */
|
|
"bl " __ASM_NAME("NtContinue") "\n\t"
|
|
"brk #1" )
|
|
|
|
|
|
/*******************************************************************
|
|
* KiUserCallbackDispatcher (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
|
|
".seh_pushframe\n\t"
|
|
"nop\n\t"
|
|
".seh_stackalloc 0x20\n\t"
|
|
"nop\n\t"
|
|
".seh_save_reg lr, 0x18\n\t"
|
|
".seh_endprologue\n\t"
|
|
".seh_handler " __ASM_NAME("user_callback_handler") ", @except\n\t"
|
|
"ldr x0, [sp]\n\t" /* args */
|
|
"ldp w1, w2, [sp, #0x08]\n\t" /* len, id */
|
|
"ldr x3, [x18, 0x60]\n\t" /* peb */
|
|
"ldr x3, [x3, 0x58]\n\t" /* peb->KernelCallbackTable */
|
|
"ldr x15, [x3, x2, lsl #3]\n\t"
|
|
"blr x15\n\t"
|
|
".globl " __ASM_NAME("KiUserCallbackDispatcherReturn") "\n"
|
|
__ASM_NAME("KiUserCallbackDispatcherReturn") ":\n\t"
|
|
"mov x2, x0\n\t" /* status */
|
|
"mov x1, #0\n\t" /* ret_len */
|
|
"mov x0, x1\n\t" /* ret_ptr */
|
|
"bl " __ASM_NAME("NtCallbackReturn") "\n\t"
|
|
"bl " __ASM_NAME("RtlRaiseStatus") "\n\t"
|
|
"brk #1" )
|
|
|
|
|
|
/**********************************************************************
|
|
* consolidate_callback
|
|
*
|
|
* Wrapper function to call a consolidate callback from a fake frame.
|
|
* If the callback executes RtlUnwindEx (like for example done in C++ handlers),
|
|
* we have to skip all frames which were already processed. To do that we
|
|
* trick the unwinding functions into thinking the call came from somewhere
|
|
* else.
|
|
*/
|
|
void WINAPI DECLSPEC_NORETURN consolidate_callback( CONTEXT *context,
|
|
void *(CALLBACK *callback)(EXCEPTION_RECORD *),
|
|
EXCEPTION_RECORD *rec );
|
|
__ASM_GLOBAL_FUNC( consolidate_callback,
|
|
"stp x29, x30, [sp, #-16]!\n\t"
|
|
".seh_save_fplr_x 16\n\t"
|
|
"sub sp, sp, #0x390\n\t"
|
|
".seh_stackalloc 0x390\n\t"
|
|
".seh_endprologue\n\t"
|
|
"mov x4, sp\n\t"
|
|
/* copy the context onto the stack */
|
|
"mov x5, #0x390/16\n"
|
|
"1:\tldp x6, x7, [x0], #16\n\t"
|
|
"stp x6, x7, [x4], #16\n\t"
|
|
"subs x5, x5, #1\n\t"
|
|
"b.ne 1b\n\t"
|
|
"mov x0, x2\n\t"
|
|
"b invoke_callback" )
|
|
__ASM_GLOBAL_FUNC( invoke_callback,
|
|
".seh_context\n\t"
|
|
".seh_endprologue\n\t"
|
|
"blr x1\n\t"
|
|
"str x0, [sp, #0x108]\n\t" /* context->Pc */
|
|
"mov x0, sp\n\t"
|
|
"mov w1, #0\n\t"
|
|
"b NtContinue" )
|
|
|
|
|
|
/*******************************************************************
|
|
* RtlRestoreContext (NTDLL.@)
|
|
*/
|
|
void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
|
|
{
|
|
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
|
|
|
|
if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1)
|
|
{
|
|
struct _JUMP_BUFFER *jmp = (struct _JUMP_BUFFER *)rec->ExceptionInformation[0];
|
|
int i;
|
|
|
|
context->X19 = jmp->X19;
|
|
context->X20 = jmp->X20;
|
|
context->X21 = jmp->X21;
|
|
context->X22 = jmp->X22;
|
|
context->X23 = jmp->X23;
|
|
context->X24 = jmp->X24;
|
|
context->X25 = jmp->X25;
|
|
context->X26 = jmp->X26;
|
|
context->X27 = jmp->X27;
|
|
context->X28 = jmp->X28;
|
|
context->Fp = jmp->Fp;
|
|
context->Pc = jmp->Lr;
|
|
context->Sp = jmp->Sp;
|
|
context->Fpcr = jmp->Fpcr;
|
|
context->Fpsr = jmp->Fpsr;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
context->V[8+i].D[0] = jmp->D[i];
|
|
}
|
|
else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1)
|
|
{
|
|
PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0];
|
|
TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate, rec );
|
|
consolidate_callback( context, consolidate, rec );
|
|
}
|
|
|
|
/* hack: remove no longer accessible TEB frames */
|
|
while (is_valid_frame( (ULONG_PTR)teb_frame ) && (ULONG64)teb_frame < context->Sp)
|
|
{
|
|
TRACE( "removing TEB frame: %p\n", teb_frame );
|
|
teb_frame = __wine_pop_frame( teb_frame );
|
|
}
|
|
|
|
TRACE( "returning to %I64x stack %I64x\n", context->Pc, context->Sp );
|
|
NtContinue( context, FALSE );
|
|
}
|
|
|
|
/*******************************************************************
|
|
* RtlUnwindEx (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec,
|
|
PVOID retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table )
|
|
{
|
|
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
|
|
DISPATCHER_CONTEXT_NONVOLREG_ARM64 nonvol_regs;
|
|
EXCEPTION_RECORD record;
|
|
DISPATCHER_CONTEXT dispatch;
|
|
CONTEXT new_context;
|
|
NTSTATUS status;
|
|
ULONG_PTR frame;
|
|
DWORD i, res;
|
|
|
|
RtlCaptureContext( context );
|
|
new_context = *context;
|
|
|
|
/* build an exception record, if we do not have one */
|
|
if (!rec)
|
|
{
|
|
record.ExceptionCode = STATUS_UNWIND;
|
|
record.ExceptionFlags = 0;
|
|
record.ExceptionRecord = NULL;
|
|
record.ExceptionAddress = (void *)context->Pc;
|
|
record.NumberParameters = 0;
|
|
rec = &record;
|
|
}
|
|
|
|
rec->ExceptionFlags |= EXCEPTION_UNWINDING | (end_frame ? 0 : EXCEPTION_EXIT_UNWIND);
|
|
|
|
TRACE( "code=%lx flags=%lx end_frame=%p target_ip=%p\n",
|
|
rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip );
|
|
for (i = 0; i < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); i++)
|
|
TRACE( " info[%ld]=%016I64x\n", i, rec->ExceptionInformation[i] );
|
|
TRACE_CONTEXT( context );
|
|
|
|
dispatch.TargetPc = (ULONG64)target_ip;
|
|
dispatch.ContextRecord = context;
|
|
dispatch.HistoryTable = table;
|
|
dispatch.NonVolatileRegisters = nonvol_regs.Buffer;
|
|
|
|
for (;;)
|
|
{
|
|
status = virtual_unwind( UNW_FLAG_UHANDLER, &dispatch, &new_context );
|
|
if (status != STATUS_SUCCESS) raise_status( status, rec );
|
|
|
|
unwind_done:
|
|
if (!dispatch.EstablisherFrame) break;
|
|
|
|
if (!is_valid_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)
|
|
{
|
|
if (end_frame && (dispatch.EstablisherFrame > (ULONG64)end_frame))
|
|
{
|
|
ERR( "invalid end frame %I64x/%p\n", dispatch.EstablisherFrame, end_frame );
|
|
raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
|
|
}
|
|
if (dispatch.EstablisherFrame == (ULONG64)end_frame) rec->ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
|
|
|
|
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;
|
|
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
|
|
dispatch.ControlPc, dispatch.FunctionEntry,
|
|
&new_context, &dispatch.HandlerData, &frame,
|
|
NULL );
|
|
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
|
|
goto unwind_done;
|
|
default:
|
|
raise_status( STATUS_INVALID_DISPOSITION, rec );
|
|
break;
|
|
}
|
|
}
|
|
else /* hack: call builtin handlers registered in the tib list */
|
|
{
|
|
while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
|
|
(ULONG64)teb_frame < new_context.Sp &&
|
|
(ULONG64)teb_frame < (ULONG64)end_frame)
|
|
{
|
|
TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
|
|
teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
|
|
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)
|
|
{
|
|
case ExceptionContinueSearch:
|
|
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
|
|
break;
|
|
case ExceptionCollidedUnwind:
|
|
new_context = *context;
|
|
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
|
|
dispatch.ControlPc, dispatch.FunctionEntry,
|
|
&new_context, &dispatch.HandlerData,
|
|
&frame, NULL );
|
|
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
|
|
goto unwind_done;
|
|
default:
|
|
raise_status( STATUS_INVALID_DISPOSITION, rec );
|
|
break;
|
|
}
|
|
}
|
|
if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Sp) break;
|
|
}
|
|
|
|
if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
|
|
*context = new_context;
|
|
}
|
|
|
|
if (rec->ExceptionCode != STATUS_UNWIND_CONSOLIDATE)
|
|
context->Pc = (ULONG64)target_ip;
|
|
else if (rec->ExceptionInformation[10] == -1)
|
|
rec->ExceptionInformation[10] = (ULONG_PTR)&nonvol_regs;
|
|
|
|
context->X0 = (ULONG64)retval;
|
|
RtlRestoreContext(context, rec);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* RtlWalkFrameChain (NTDLL.@)
|
|
*/
|
|
ULONG WINAPI RtlWalkFrameChain( void **buffer, ULONG count, ULONG flags )
|
|
{
|
|
UNWIND_HISTORY_TABLE table;
|
|
RUNTIME_FUNCTION *func;
|
|
PEXCEPTION_ROUTINE handler;
|
|
ULONG_PTR pc, frame, base;
|
|
CONTEXT context;
|
|
void *data;
|
|
ULONG i, skip = flags >> 8, num_entries = 0;
|
|
|
|
RtlCaptureContext( &context );
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
pc = context.Pc;
|
|
if (context.ContextFlags & CONTEXT_UNWOUND_TO_CALL) pc -= 4;
|
|
func = RtlLookupFunctionEntry( pc, &base, &table );
|
|
if (RtlVirtualUnwind2( UNW_FLAG_NHANDLER, base, pc, func, &context, NULL,
|
|
&data, &frame, NULL, NULL, NULL, &handler, 0 ))
|
|
break;
|
|
if (!context.Pc) break;
|
|
if (!frame || !is_valid_frame( frame )) break;
|
|
if (context.Sp == (ULONG_PTR)NtCurrentTeb()->Tib.StackBase) break;
|
|
if (i >= skip) buffer[num_entries++] = (void *)context.Pc;
|
|
}
|
|
return num_entries;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* __C_ExecuteExceptionFilter
|
|
*/
|
|
__ASM_GLOBAL_FUNC( __C_ExecuteExceptionFilter,
|
|
"stp x29, x30, [sp, #-96]!\n\t"
|
|
".seh_save_fplr_x 96\n\t"
|
|
"stp x19, x20, [sp, #16]\n\t"
|
|
".seh_save_regp x19, 16\n\t"
|
|
"stp x21, x22, [sp, #32]\n\t"
|
|
".seh_save_regp x21, 32\n\t"
|
|
"stp x23, x24, [sp, #48]\n\t"
|
|
".seh_save_regp x23, 48\n\t"
|
|
"stp x25, x26, [sp, #64]\n\t"
|
|
".seh_save_regp x25, 64\n\t"
|
|
"stp x27, x28, [sp, #80]\n\t"
|
|
".seh_save_regp x27, 80\n\t"
|
|
".seh_endprologue\n\t"
|
|
"ldp x19, x20, [x3, #0]\n\t" /* nonvolatile regs */
|
|
"ldp x21, x22, [x3, #16]\n\t"
|
|
"ldp x23, x24, [x3, #32]\n\t"
|
|
"ldp x25, x26, [x3, #48]\n\t"
|
|
"ldp x27, x28, [x3, #64]\n\t"
|
|
"ldr x1, [x3, #80]\n\t" /* x29 = frame */
|
|
"blr x2\n\t" /* filter */
|
|
"ldp x19, x20, [sp, #16]\n\t"
|
|
"ldp x21, x22, [sp, #32]\n\t"
|
|
"ldp x23, x24, [sp, #48]\n\t"
|
|
"ldp x25, x26, [sp, #64]\n\t"
|
|
"ldp x27, x28, [sp, #80]\n\t"
|
|
"ldp x29, x30, [sp], #96\n\t"
|
|
"ret")
|
|
|
|
|
|
/***********************************************************************
|
|
* RtlRaiseException (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( RtlRaiseException,
|
|
"sub sp, sp, #0x3b0\n\t" /* 0x390 (context) + 0x20 */
|
|
".seh_stackalloc 0x3b0\n\t"
|
|
"stp x29, x30, [sp]\n\t"
|
|
".seh_save_fplr 0\n\t"
|
|
".seh_endprologue\n\t"
|
|
"mov x29, sp\n\t"
|
|
"str x0, [sp, #0x10]\n\t"
|
|
"add x0, sp, #0x20\n\t"
|
|
"bl " __ASM_NAME("RtlCaptureContext") "\n\t"
|
|
"add x1, sp, #0x20\n\t" /* context pointer */
|
|
"add x2, sp, #0x3b0\n\t" /* orig stack pointer */
|
|
"str x2, [x1, #0x100]\n\t" /* context->Sp */
|
|
"ldr x0, [sp, #0x10]\n\t" /* original first parameter */
|
|
"str x0, [x1, #0x08]\n\t" /* context->X0 */
|
|
"ldp x4, x5, [sp]\n\t" /* frame pointer, return address */
|
|
"stp x4, x5, [x1, #0xf0]\n\t" /* context->Fp, Lr */
|
|
"str x5, [x1, #0x108]\n\t" /* context->Pc */
|
|
"str x5, [x0, #0x10]\n\t" /* rec->ExceptionAddress */
|
|
"ldr w2, [x1]\n\t" /* context->ContextFlags */
|
|
"orr w2, w2, #0x20000000\n\t" /* CONTEXT_UNWOUND_TO_CALL */
|
|
"str w2, [x1]\n\t"
|
|
"ldr x3, [x18, #0x60]\n\t" /* peb */
|
|
"ldrb w2, [x3, #2]\n\t" /* peb->BeingDebugged */
|
|
"cbnz w2, 1f\n\t"
|
|
"bl " __ASM_NAME("dispatch_exception") "\n"
|
|
"1:\tmov x2, #1\n\t"
|
|
"bl " __ASM_NAME("NtRaiseException") "\n\t"
|
|
"bl " __ASM_NAME("RtlRaiseStatus") /* does not return */ );
|
|
|
|
|
|
/***********************************************************************
|
|
* _setjmpex (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( NTDLL__setjmpex,
|
|
".seh_endprologue\n\t"
|
|
"str x1, [x0]\n\t" /* jmp_buf->Frame */
|
|
"stp x19, x20, [x0, #0x10]\n\t" /* jmp_buf->X19, X20 */
|
|
"stp x21, x22, [x0, #0x20]\n\t" /* jmp_buf->X21, X22 */
|
|
"stp x23, x24, [x0, #0x30]\n\t" /* jmp_buf->X23, X24 */
|
|
"stp x25, x26, [x0, #0x40]\n\t" /* jmp_buf->X25, X26 */
|
|
"stp x27, x28, [x0, #0x50]\n\t" /* jmp_buf->X27, X28 */
|
|
"stp x29, x30, [x0, #0x60]\n\t" /* jmp_buf->Fp, Lr */
|
|
"mov x2, sp\n\t"
|
|
"str x2, [x0, #0x70]\n\t" /* jmp_buf->Sp */
|
|
"mrs x2, fpcr\n\t"
|
|
"mrs x3, fpsr\n\t"
|
|
"stp w2, w3, [x0, #0x78]\n\t" /* jmp_buf->Fpcr,Fpsr */
|
|
"stp d8, d9, [x0, #0x80]\n\t" /* jmp_buf->D[0-1] */
|
|
"stp d10, d11, [x0, #0x90]\n\t" /* jmp_buf->D[2-3] */
|
|
"stp d12, d13, [x0, #0xa0]\n\t" /* jmp_buf->D[4-5] */
|
|
"stp d14, d15, [x0, #0xb0]\n\t" /* jmp_buf->D[6-7] */
|
|
"mov x0, #0\n\t"
|
|
"ret" )
|
|
|
|
|
|
/*******************************************************************
|
|
* longjmp (NTDLL.@)
|
|
*/
|
|
void __cdecl NTDLL_longjmp( _JUMP_BUFFER *buf, int retval )
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
|
|
if (!retval) retval = 1;
|
|
|
|
rec.ExceptionCode = STATUS_LONGJUMP;
|
|
rec.ExceptionFlags = 0;
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionAddress = NULL;
|
|
rec.NumberParameters = 1;
|
|
rec.ExceptionInformation[0] = (DWORD_PTR)buf;
|
|
RtlUnwind( (void *)buf->Frame, (void *)buf->Lr, &rec, IntToPtr(retval) );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RtlUserThreadStart (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( RtlUserThreadStart,
|
|
"stp x29, x30, [sp, #-16]!\n\t"
|
|
".seh_save_fplr_x 16\n\t"
|
|
".seh_endprologue\n\t"
|
|
"adr x8, " __ASM_NAME("pBaseThreadInitThunk") "\n\t"
|
|
"ldr x8, [x8]\n\t"
|
|
"mov x2, x1\n\t"
|
|
"mov x1, x0\n\t"
|
|
"mov x0, #0\n\t"
|
|
"blr x8\n\t"
|
|
".seh_handler call_unhandled_exception_handler, @except" )
|
|
|
|
/******************************************************************
|
|
* LdrInitializeThunk (NTDLL.@)
|
|
*/
|
|
void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unk2, ULONG_PTR unk3, ULONG_PTR unk4 )
|
|
{
|
|
loader_init( context, (void **)&context->X0 );
|
|
TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", (void *)context->X0, (void *)context->X1 );
|
|
NtContinue( context, TRUE );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* process_breakpoint
|
|
*/
|
|
__ASM_GLOBAL_FUNC( process_breakpoint,
|
|
".seh_endprologue\n\t"
|
|
".seh_handler process_breakpoint_handler, @except\n\t"
|
|
"brk #0xf000\n\t"
|
|
"ret\n"
|
|
"process_breakpoint_handler:\n\t"
|
|
"ldr x4, [x2, #0x108]\n\t" /* context->Pc */
|
|
"add x4, x4, #4\n\t"
|
|
"str x4, [x2, #0x108]\n\t"
|
|
"mov w0, #0\n\t" /* ExceptionContinueExecution */
|
|
"ret" )
|
|
|
|
/***********************************************************************
|
|
* DbgUiRemoteBreakin (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( DbgUiRemoteBreakin,
|
|
"stp x29, x30, [sp, #-16]!\n\t"
|
|
".seh_save_fplr_x 16\n\t"
|
|
".seh_endprologue\n\t"
|
|
".seh_handler DbgUiRemoteBreakin_handler, @except\n\t"
|
|
"ldr x0, [x18, #0x60]\n\t" /* NtCurrentTeb()->Peb */
|
|
"ldrb w0, [x0, 0x02]\n\t" /* peb->BeingDebugged */
|
|
"cbz w0, 1f\n\t"
|
|
"bl " __ASM_NAME("DbgBreakPoint") "\n"
|
|
"1:\tmov w0, #0\n\t"
|
|
"bl " __ASM_NAME("RtlExitUserThread") "\n"
|
|
"DbgUiRemoteBreakin_handler:\n\t"
|
|
"mov sp, x1\n\t" /* frame */
|
|
"b 1b" )
|
|
|
|
/**********************************************************************
|
|
* DbgBreakPoint (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( DbgBreakPoint, "brk #0xf000; ret"
|
|
"\n\tnop; nop; nop; nop; nop; nop; nop; nop"
|
|
"\n\tnop; nop; nop; nop; nop; nop" );
|
|
|
|
/**********************************************************************
|
|
* DbgUserBreakPoint (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( DbgUserBreakPoint, "brk #0xf000; ret"
|
|
"\n\tnop; nop; nop; nop; nop; nop; nop; nop"
|
|
"\n\tnop; nop; nop; nop; nop; nop" );
|
|
|
|
#endif /* __aarch64__ */
|