1658 lines
61 KiB
C
1658 lines
61 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
|
|
*/
|
|
|
|
#if 0
|
|
#pragma makedep unix
|
|
#endif
|
|
|
|
#ifdef __aarch64__
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
#ifdef HAVE_SYSCALL_H
|
|
# include <syscall.h>
|
|
#else
|
|
# ifdef HAVE_SYS_SYSCALL_H
|
|
# include <sys/syscall.h>
|
|
# endif
|
|
#endif
|
|
#ifdef HAVE_SYS_SIGNAL_H
|
|
# include <sys/signal.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_UCONTEXT_H
|
|
# include <sys/ucontext.h>
|
|
#endif
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winnt.h"
|
|
#include "winternl.h"
|
|
#include "wine/asm.h"
|
|
#include "unix_private.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(seh);
|
|
|
|
#define NTDLL_DWARF_H_NO_UNWINDER
|
|
#include "dwarf.h"
|
|
|
|
/***********************************************************************
|
|
* signal context platform-specific definitions
|
|
*/
|
|
#ifdef linux
|
|
|
|
/* All Registers access - only for local access */
|
|
# define REG_sig(reg_name, context) ((context)->uc_mcontext.reg_name)
|
|
# define REGn_sig(reg_num, context) ((context)->uc_mcontext.regs[reg_num])
|
|
|
|
/* Special Registers access */
|
|
# define SP_sig(context) REG_sig(sp, context) /* Stack pointer */
|
|
# define PC_sig(context) REG_sig(pc, context) /* Program counter */
|
|
# define PSTATE_sig(context) REG_sig(pstate, context) /* Current State Register */
|
|
# define FP_sig(context) REGn_sig(29, context) /* Frame pointer */
|
|
# define LR_sig(context) REGn_sig(30, context) /* Link Register */
|
|
|
|
static struct _aarch64_ctx *get_extended_sigcontext( const ucontext_t *sigcontext, unsigned int magic )
|
|
{
|
|
struct _aarch64_ctx *ctx = (struct _aarch64_ctx *)sigcontext->uc_mcontext.__reserved;
|
|
while ((char *)ctx < (char *)(&sigcontext->uc_mcontext + 1) && ctx->magic && ctx->size)
|
|
{
|
|
if (ctx->magic == magic) return ctx;
|
|
ctx = (struct _aarch64_ctx *)((char *)ctx + ctx->size);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct fpsimd_context *get_fpsimd_context( const ucontext_t *sigcontext )
|
|
{
|
|
return (struct fpsimd_context *)get_extended_sigcontext( sigcontext, FPSIMD_MAGIC );
|
|
}
|
|
|
|
static DWORD64 get_fault_esr( ucontext_t *sigcontext )
|
|
{
|
|
struct esr_context *esr = (struct esr_context *)get_extended_sigcontext( sigcontext, ESR_MAGIC );
|
|
if (esr) return esr->esr;
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
/* All Registers access - only for local access */
|
|
# define REG_sig(reg_name, context) ((context)->uc_mcontext->__ss.__ ## reg_name)
|
|
# define REGn_sig(reg_num, context) ((context)->uc_mcontext->__ss.__x[reg_num])
|
|
|
|
/* Special Registers access */
|
|
# define SP_sig(context) REG_sig(sp, context) /* Stack pointer */
|
|
# define PC_sig(context) REG_sig(pc, context) /* Program counter */
|
|
# define PSTATE_sig(context) REG_sig(cpsr, context) /* Current State Register */
|
|
# define FP_sig(context) REG_sig(fp, context) /* Frame pointer */
|
|
# define LR_sig(context) REG_sig(lr, context) /* Link Register */
|
|
|
|
static DWORD64 get_fault_esr( ucontext_t *sigcontext )
|
|
{
|
|
return sigcontext->uc_mcontext->__es.__esr;
|
|
}
|
|
|
|
#endif /* linux */
|
|
|
|
/* stack layout when calling KiUserExceptionDispatcher */
|
|
struct exc_stack_layout
|
|
{
|
|
CONTEXT context; /* 000 */
|
|
EXCEPTION_RECORD rec; /* 390 */
|
|
ULONG64 align; /* 428 */
|
|
ULONG64 redzone[2]; /* 430 */
|
|
};
|
|
C_ASSERT( offsetof(struct exc_stack_layout, rec) == 0x390 );
|
|
C_ASSERT( sizeof(struct exc_stack_layout) == 0x440 );
|
|
|
|
/* stack layout when calling KiUserApcDispatcher */
|
|
struct apc_stack_layout
|
|
{
|
|
void *func; /* 000 APC to call*/
|
|
ULONG64 args[3]; /* 008 function arguments */
|
|
ULONG64 alertable; /* 020 */
|
|
ULONG64 align; /* 028 */
|
|
CONTEXT context; /* 030 */
|
|
ULONG64 redzone[2]; /* 3c0 */
|
|
};
|
|
C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x30 );
|
|
C_ASSERT( sizeof(struct apc_stack_layout) == 0x3d0 );
|
|
|
|
/* stack layout when calling KiUserCallbackDispatcher */
|
|
struct callback_stack_layout
|
|
{
|
|
void *args; /* 000 arguments */
|
|
ULONG len; /* 008 arguments len */
|
|
ULONG id; /* 00c function id */
|
|
ULONG64 unknown; /* 010 */
|
|
ULONG64 lr; /* 018 */
|
|
ULONG64 sp; /* 020 sp+pc (machine frame) */
|
|
ULONG64 pc; /* 028 */
|
|
BYTE args_data[0]; /* 030 copied argument data*/
|
|
};
|
|
C_ASSERT( offsetof(struct callback_stack_layout, sp) == 0x20 );
|
|
C_ASSERT( sizeof(struct callback_stack_layout) == 0x30 );
|
|
|
|
struct syscall_frame
|
|
{
|
|
ULONG64 x[29]; /* 000 */
|
|
ULONG64 fp; /* 0e8 */
|
|
ULONG64 lr; /* 0f0 */
|
|
ULONG64 sp; /* 0f8 */
|
|
ULONG64 pc; /* 100 */
|
|
ULONG cpsr; /* 108 */
|
|
ULONG restore_flags; /* 10c */
|
|
struct syscall_frame *prev_frame; /* 110 */
|
|
void *syscall_cfa; /* 118 */
|
|
ULONG64 align; /* 120 */
|
|
ULONG fpcr; /* 128 */
|
|
ULONG fpsr; /* 12c */
|
|
NEON128 v[32]; /* 130 */
|
|
};
|
|
|
|
C_ASSERT( sizeof( struct syscall_frame ) == 0x330 );
|
|
|
|
struct arm64_thread_data
|
|
{
|
|
struct syscall_frame *syscall_frame; /* 02f0 frame pointer on syscall entry */
|
|
SYSTEM_SERVICE_TABLE *syscall_table; /* 02f8 syscall table */
|
|
};
|
|
|
|
C_ASSERT( sizeof(struct arm64_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) );
|
|
C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct arm64_thread_data, syscall_frame ) == 0x2f0 );
|
|
C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct arm64_thread_data, syscall_table ) == 0x2f8 );
|
|
|
|
static inline struct arm64_thread_data *arm64_thread_data(void)
|
|
{
|
|
return (struct arm64_thread_data *)ntdll_get_thread_data()->cpu_data;
|
|
}
|
|
|
|
static BOOL is_inside_syscall( ucontext_t *sigcontext )
|
|
{
|
|
return ((char *)SP_sig(sigcontext) >= (char *)ntdll_get_thread_data()->kernel_stack &&
|
|
(char *)SP_sig(sigcontext) <= (char *)arm64_thread_data()->syscall_frame);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* unwind_builtin_dll
|
|
*
|
|
* Equivalent of RtlVirtualUnwind for builtin modules.
|
|
*/
|
|
NTSTATUS unwind_builtin_dll( void *args )
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* syscall_frame_fixup_for_fastpath
|
|
*
|
|
* Fixes up the given syscall frame such that the syscall dispatcher
|
|
* can return via the fast path if CONTEXT_INTEGER is set in
|
|
* restore_flags.
|
|
*
|
|
* Clobbers the frame's X16 and X17 register values.
|
|
*/
|
|
static void syscall_frame_fixup_for_fastpath( struct syscall_frame *frame )
|
|
{
|
|
frame->x[16] = frame->pc;
|
|
frame->x[17] = frame->sp;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* save_fpu
|
|
*
|
|
* Set the FPU context from a sigcontext.
|
|
*/
|
|
static void save_fpu( CONTEXT *context, const ucontext_t *sigcontext )
|
|
{
|
|
#ifdef linux
|
|
struct fpsimd_context *fp = get_fpsimd_context( sigcontext );
|
|
|
|
if (!fp) return;
|
|
context->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
context->Fpcr = fp->fpcr;
|
|
context->Fpsr = fp->fpsr;
|
|
memcpy( context->V, fp->vregs, sizeof(context->V) );
|
|
#elif defined(__APPLE__)
|
|
context->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
context->Fpcr = sigcontext->uc_mcontext->__ns.__fpcr;
|
|
context->Fpsr = sigcontext->uc_mcontext->__ns.__fpsr;
|
|
memcpy( context->V, sigcontext->uc_mcontext->__ns.__v, sizeof(context->V) );
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* restore_fpu
|
|
*
|
|
* Restore the FPU context to a sigcontext.
|
|
*/
|
|
static void restore_fpu( const CONTEXT *context, ucontext_t *sigcontext )
|
|
{
|
|
#ifdef linux
|
|
struct fpsimd_context *fp = get_fpsimd_context( sigcontext );
|
|
|
|
if (!fp) return;
|
|
fp->fpcr = context->Fpcr;
|
|
fp->fpsr = context->Fpsr;
|
|
memcpy( fp->vregs, context->V, sizeof(fp->vregs) );
|
|
#elif defined(__APPLE__)
|
|
sigcontext->uc_mcontext->__ns.__fpcr = context->Fpcr;
|
|
sigcontext->uc_mcontext->__ns.__fpsr = context->Fpsr;
|
|
memcpy( sigcontext->uc_mcontext->__ns.__v, context->V, sizeof(context->V) );
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* save_context
|
|
*
|
|
* Set the register values from a sigcontext.
|
|
*/
|
|
static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
|
|
{
|
|
DWORD i;
|
|
|
|
context->ContextFlags = CONTEXT_FULL;
|
|
context->Fp = FP_sig(sigcontext); /* Frame pointer */
|
|
context->Lr = LR_sig(sigcontext); /* Link register */
|
|
context->Sp = SP_sig(sigcontext); /* Stack pointer */
|
|
context->Pc = PC_sig(sigcontext); /* Program Counter */
|
|
context->Cpsr = PSTATE_sig(sigcontext); /* Current State Register */
|
|
for (i = 0; i <= 28; i++) context->X[i] = REGn_sig( i, sigcontext );
|
|
save_fpu( context, sigcontext );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* restore_context
|
|
*
|
|
* Build a sigcontext from the register values.
|
|
*/
|
|
static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
|
|
{
|
|
DWORD i;
|
|
|
|
FP_sig(sigcontext) = context->Fp; /* Frame pointer */
|
|
LR_sig(sigcontext) = context->Lr; /* Link register */
|
|
SP_sig(sigcontext) = context->Sp; /* Stack pointer */
|
|
PC_sig(sigcontext) = context->Pc; /* Program Counter */
|
|
PSTATE_sig(sigcontext) = context->Cpsr; /* Current State Register */
|
|
for (i = 0; i <= 28; i++) REGn_sig( i, sigcontext ) = context->X[i];
|
|
restore_fpu( context, sigcontext );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* signal_set_full_context
|
|
*/
|
|
NTSTATUS signal_set_full_context( CONTEXT *context )
|
|
{
|
|
NTSTATUS status = NtSetContextThread( GetCurrentThread(), context );
|
|
|
|
if (!status && (context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
|
|
arm64_thread_data()->syscall_frame->restore_flags |= CONTEXT_INTEGER;
|
|
return status;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* get_native_context
|
|
*/
|
|
void *get_native_context( CONTEXT *context )
|
|
{
|
|
return context;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* get_wow_context
|
|
*/
|
|
void *get_wow_context( CONTEXT *context )
|
|
{
|
|
return get_cpu_area( main_image_info.Machine );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtSetContextThread (NTDLL.@)
|
|
* ZwSetContextThread (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
NTSTATUS ret = STATUS_SUCCESS;
|
|
BOOL self = (handle == GetCurrentThread());
|
|
DWORD flags = context->ContextFlags & ~CONTEXT_ARM64;
|
|
|
|
if (self && (flags & CONTEXT_DEBUG_REGISTERS)) self = FALSE;
|
|
|
|
if (!self)
|
|
{
|
|
ret = set_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARM64 );
|
|
if (ret || !self) return ret;
|
|
}
|
|
|
|
if (flags & CONTEXT_INTEGER)
|
|
{
|
|
memcpy( frame->x, context->X, sizeof(context->X[0]) * 18 );
|
|
/* skip x18 */
|
|
memcpy( frame->x + 19, context->X + 19, sizeof(context->X[0]) * 10 );
|
|
}
|
|
if (flags & CONTEXT_CONTROL)
|
|
{
|
|
frame->fp = context->Fp;
|
|
frame->lr = context->Lr;
|
|
frame->sp = context->Sp;
|
|
frame->pc = context->Pc;
|
|
frame->cpsr = context->Cpsr;
|
|
}
|
|
if (flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
frame->fpcr = context->Fpcr;
|
|
frame->fpsr = context->Fpsr;
|
|
memcpy( frame->v, context->V, sizeof(frame->v) );
|
|
}
|
|
if (flags & CONTEXT_ARM64_X18)
|
|
{
|
|
frame->x[18] = context->X[18];
|
|
}
|
|
if (flags & CONTEXT_DEBUG_REGISTERS) FIXME( "debug registers not supported\n" );
|
|
frame->restore_flags |= flags & ~CONTEXT_INTEGER;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGetContextThread (NTDLL.@)
|
|
* ZwGetContextThread (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
DWORD needed_flags = context->ContextFlags & ~CONTEXT_ARM64;
|
|
BOOL self = (handle == GetCurrentThread());
|
|
|
|
if (!self)
|
|
{
|
|
NTSTATUS ret = get_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_ARM64 );
|
|
if (ret || !self) return ret;
|
|
}
|
|
|
|
if (needed_flags & CONTEXT_INTEGER)
|
|
{
|
|
memcpy( context->X, frame->x, sizeof(context->X[0]) * 29 );
|
|
context->ContextFlags |= CONTEXT_INTEGER;
|
|
}
|
|
if (needed_flags & CONTEXT_CONTROL)
|
|
{
|
|
context->Fp = frame->fp;
|
|
context->Lr = frame->lr;
|
|
context->Sp = frame->sp;
|
|
context->Pc = frame->pc;
|
|
context->Cpsr = frame->cpsr;
|
|
context->ContextFlags |= CONTEXT_CONTROL;
|
|
}
|
|
if (needed_flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
context->Fpcr = frame->fpcr;
|
|
context->Fpsr = frame->fpsr;
|
|
memcpy( context->V, frame->v, sizeof(context->V) );
|
|
context->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
}
|
|
if (needed_flags & CONTEXT_DEBUG_REGISTERS) FIXME( "debug registers not supported\n" );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* set_thread_wow64_context
|
|
*/
|
|
NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size )
|
|
{
|
|
BOOL self = (handle == GetCurrentThread());
|
|
USHORT machine;
|
|
void *frame;
|
|
|
|
switch (size)
|
|
{
|
|
case sizeof(I386_CONTEXT): machine = IMAGE_FILE_MACHINE_I386; break;
|
|
case sizeof(ARM_CONTEXT): machine = IMAGE_FILE_MACHINE_ARMNT; break;
|
|
default: return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
if (!self)
|
|
{
|
|
NTSTATUS ret = set_thread_context( handle, ctx, &self, machine );
|
|
if (ret || !self) return ret;
|
|
}
|
|
|
|
if (!(frame = get_cpu_area( machine ))) return STATUS_INVALID_PARAMETER;
|
|
|
|
switch (machine)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
{
|
|
I386_CONTEXT *wow_frame = frame;
|
|
const I386_CONTEXT *context = ctx;
|
|
DWORD flags = context->ContextFlags & ~CONTEXT_i386;
|
|
|
|
if (flags & CONTEXT_I386_INTEGER)
|
|
{
|
|
wow_frame->Eax = context->Eax;
|
|
wow_frame->Ebx = context->Ebx;
|
|
wow_frame->Ecx = context->Ecx;
|
|
wow_frame->Edx = context->Edx;
|
|
wow_frame->Esi = context->Esi;
|
|
wow_frame->Edi = context->Edi;
|
|
}
|
|
if (flags & CONTEXT_I386_CONTROL)
|
|
{
|
|
WOW64_CPURESERVED *cpu = NtCurrentTeb()->TlsSlots[WOW64_TLS_CPURESERVED];
|
|
|
|
wow_frame->Esp = context->Esp;
|
|
wow_frame->Ebp = context->Ebp;
|
|
wow_frame->Eip = context->Eip;
|
|
wow_frame->EFlags = context->EFlags;
|
|
wow_frame->SegCs = context->SegCs;
|
|
wow_frame->SegSs = context->SegSs;
|
|
cpu->Flags |= WOW64_CPURESERVED_FLAG_RESET_STATE;
|
|
}
|
|
if (flags & CONTEXT_I386_SEGMENTS)
|
|
{
|
|
wow_frame->SegDs = context->SegDs;
|
|
wow_frame->SegEs = context->SegEs;
|
|
wow_frame->SegFs = context->SegFs;
|
|
wow_frame->SegGs = context->SegGs;
|
|
}
|
|
if (flags & CONTEXT_I386_DEBUG_REGISTERS)
|
|
{
|
|
wow_frame->Dr0 = context->Dr0;
|
|
wow_frame->Dr1 = context->Dr1;
|
|
wow_frame->Dr2 = context->Dr2;
|
|
wow_frame->Dr3 = context->Dr3;
|
|
wow_frame->Dr6 = context->Dr6;
|
|
wow_frame->Dr7 = context->Dr7;
|
|
}
|
|
if (flags & CONTEXT_I386_EXTENDED_REGISTERS)
|
|
{
|
|
memcpy( &wow_frame->ExtendedRegisters, context->ExtendedRegisters, sizeof(context->ExtendedRegisters) );
|
|
}
|
|
if (flags & CONTEXT_I386_FLOATING_POINT)
|
|
{
|
|
memcpy( &wow_frame->FloatSave, &context->FloatSave, sizeof(context->FloatSave) );
|
|
}
|
|
/* FIXME: CONTEXT_I386_XSTATE */
|
|
break;
|
|
}
|
|
|
|
case IMAGE_FILE_MACHINE_ARMNT:
|
|
{
|
|
ARM_CONTEXT *wow_frame = frame;
|
|
const ARM_CONTEXT *context = ctx;
|
|
DWORD flags = context->ContextFlags & ~CONTEXT_ARM;
|
|
|
|
if (flags & CONTEXT_INTEGER)
|
|
{
|
|
wow_frame->R0 = context->R0;
|
|
wow_frame->R1 = context->R1;
|
|
wow_frame->R2 = context->R2;
|
|
wow_frame->R3 = context->R3;
|
|
wow_frame->R4 = context->R4;
|
|
wow_frame->R5 = context->R5;
|
|
wow_frame->R6 = context->R6;
|
|
wow_frame->R7 = context->R7;
|
|
wow_frame->R8 = context->R8;
|
|
wow_frame->R9 = context->R9;
|
|
wow_frame->R10 = context->R10;
|
|
wow_frame->R11 = context->R11;
|
|
wow_frame->R12 = context->R12;
|
|
}
|
|
if (flags & CONTEXT_CONTROL)
|
|
{
|
|
wow_frame->Sp = context->Sp;
|
|
wow_frame->Lr = context->Lr;
|
|
wow_frame->Pc = context->Pc & ~1;
|
|
wow_frame->Cpsr = context->Cpsr;
|
|
if (context->Cpsr & 0x20) wow_frame->Pc |= 1; /* thumb */
|
|
}
|
|
if (flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
wow_frame->Fpscr = context->Fpscr;
|
|
memcpy( wow_frame->D, context->D, sizeof(context->D) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* get_thread_wow64_context
|
|
*/
|
|
NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size )
|
|
{
|
|
BOOL self = (handle == GetCurrentThread());
|
|
USHORT machine;
|
|
void *frame;
|
|
|
|
switch (size)
|
|
{
|
|
case sizeof(I386_CONTEXT): machine = IMAGE_FILE_MACHINE_I386; break;
|
|
case sizeof(ARM_CONTEXT): machine = IMAGE_FILE_MACHINE_ARMNT; break;
|
|
default: return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
if (!self)
|
|
{
|
|
NTSTATUS ret = get_thread_context( handle, ctx, &self, machine );
|
|
if (ret || !self) return ret;
|
|
}
|
|
|
|
if (!(frame = get_cpu_area( machine ))) return STATUS_INVALID_PARAMETER;
|
|
|
|
switch (machine)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
{
|
|
I386_CONTEXT *wow_frame = frame, *context = ctx;
|
|
DWORD needed_flags = context->ContextFlags & ~CONTEXT_i386;
|
|
|
|
if (needed_flags & CONTEXT_I386_INTEGER)
|
|
{
|
|
context->Eax = wow_frame->Eax;
|
|
context->Ebx = wow_frame->Ebx;
|
|
context->Ecx = wow_frame->Ecx;
|
|
context->Edx = wow_frame->Edx;
|
|
context->Esi = wow_frame->Esi;
|
|
context->Edi = wow_frame->Edi;
|
|
context->ContextFlags |= CONTEXT_I386_INTEGER;
|
|
}
|
|
if (needed_flags & CONTEXT_I386_CONTROL)
|
|
{
|
|
context->Esp = wow_frame->Esp;
|
|
context->Ebp = wow_frame->Ebp;
|
|
context->Eip = wow_frame->Eip;
|
|
context->EFlags = wow_frame->EFlags;
|
|
context->SegCs = wow_frame->SegCs;
|
|
context->SegSs = wow_frame->SegSs;
|
|
context->ContextFlags |= CONTEXT_I386_CONTROL;
|
|
}
|
|
if (needed_flags & CONTEXT_I386_SEGMENTS)
|
|
{
|
|
context->SegDs = wow_frame->SegDs;
|
|
context->SegEs = wow_frame->SegEs;
|
|
context->SegFs = wow_frame->SegFs;
|
|
context->SegGs = wow_frame->SegGs;
|
|
context->ContextFlags |= CONTEXT_I386_SEGMENTS;
|
|
}
|
|
if (needed_flags & CONTEXT_I386_EXTENDED_REGISTERS)
|
|
{
|
|
memcpy( context->ExtendedRegisters, &wow_frame->ExtendedRegisters, sizeof(context->ExtendedRegisters) );
|
|
context->ContextFlags |= CONTEXT_I386_EXTENDED_REGISTERS;
|
|
}
|
|
if (needed_flags & CONTEXT_I386_FLOATING_POINT)
|
|
{
|
|
memcpy( &context->FloatSave, &wow_frame->FloatSave, sizeof(context->FloatSave) );
|
|
context->ContextFlags |= CONTEXT_I386_FLOATING_POINT;
|
|
}
|
|
if (needed_flags & CONTEXT_I386_DEBUG_REGISTERS)
|
|
{
|
|
context->Dr0 = wow_frame->Dr0;
|
|
context->Dr1 = wow_frame->Dr1;
|
|
context->Dr2 = wow_frame->Dr2;
|
|
context->Dr3 = wow_frame->Dr3;
|
|
context->Dr6 = wow_frame->Dr6;
|
|
context->Dr7 = wow_frame->Dr7;
|
|
}
|
|
/* FIXME: CONTEXT_I386_XSTATE */
|
|
break;
|
|
}
|
|
|
|
case IMAGE_FILE_MACHINE_ARMNT:
|
|
{
|
|
ARM_CONTEXT *wow_frame = frame, *context = ctx;
|
|
DWORD needed_flags = context->ContextFlags & ~CONTEXT_ARM;
|
|
|
|
if (needed_flags & CONTEXT_INTEGER)
|
|
{
|
|
context->R0 = wow_frame->R0;
|
|
context->R1 = wow_frame->R1;
|
|
context->R2 = wow_frame->R2;
|
|
context->R3 = wow_frame->R3;
|
|
context->R4 = wow_frame->R4;
|
|
context->R5 = wow_frame->R5;
|
|
context->R6 = wow_frame->R6;
|
|
context->R7 = wow_frame->R7;
|
|
context->R8 = wow_frame->R8;
|
|
context->R9 = wow_frame->R9;
|
|
context->R10 = wow_frame->R10;
|
|
context->R11 = wow_frame->R11;
|
|
context->R12 = wow_frame->R12;
|
|
context->ContextFlags |= CONTEXT_INTEGER;
|
|
}
|
|
if (needed_flags & CONTEXT_CONTROL)
|
|
{
|
|
context->Sp = wow_frame->Sp;
|
|
context->Lr = wow_frame->Lr;
|
|
context->Pc = wow_frame->Pc;
|
|
context->Cpsr = wow_frame->Cpsr;
|
|
context->ContextFlags |= CONTEXT_CONTROL;
|
|
}
|
|
if (needed_flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
context->Fpscr = wow_frame->Fpscr;
|
|
memcpy( context->D, wow_frame->D, sizeof(wow_frame->D) );
|
|
context->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* setup_raise_exception
|
|
*/
|
|
static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
|
|
{
|
|
struct exc_stack_layout *stack;
|
|
void *stack_ptr = (void *)(SP_sig(sigcontext) & ~15);
|
|
NTSTATUS status;
|
|
|
|
status = send_debug_event( rec, context, TRUE );
|
|
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
|
|
{
|
|
restore_context( context, sigcontext );
|
|
return;
|
|
}
|
|
|
|
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
|
|
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Pc -= 4;
|
|
|
|
stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec );
|
|
stack->rec = *rec;
|
|
stack->context = *context;
|
|
|
|
SP_sig(sigcontext) = (ULONG_PTR)stack;
|
|
PC_sig(sigcontext) = (ULONG_PTR)pKiUserExceptionDispatcher;
|
|
REGn_sig(18, sigcontext) = (ULONG_PTR)NtCurrentTeb();
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* setup_exception
|
|
*
|
|
* Modify the signal context to call the exception raise function.
|
|
*/
|
|
static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
|
|
{
|
|
CONTEXT context;
|
|
|
|
rec->ExceptionAddress = (void *)PC_sig(sigcontext);
|
|
save_context( &context, sigcontext );
|
|
setup_raise_exception( sigcontext, rec, &context );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* call_user_apc_dispatcher
|
|
*/
|
|
NTSTATUS call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
|
|
PNTAPCFUNC func, NTSTATUS status )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
ULONG64 sp = context ? context->Sp : frame->sp;
|
|
struct apc_stack_layout *stack;
|
|
|
|
sp &= ~15;
|
|
stack = (struct apc_stack_layout *)sp - 1;
|
|
if (context)
|
|
{
|
|
memmove( &stack->context, context, sizeof(stack->context) );
|
|
NtSetContextThread( GetCurrentThread(), &stack->context );
|
|
}
|
|
else
|
|
{
|
|
stack->context.ContextFlags = CONTEXT_FULL;
|
|
NtGetContextThread( GetCurrentThread(), &stack->context );
|
|
stack->context.X0 = status;
|
|
}
|
|
stack->func = func;
|
|
stack->args[0] = arg1;
|
|
stack->args[1] = arg2;
|
|
stack->args[2] = arg3;
|
|
stack->alertable = TRUE;
|
|
|
|
frame->sp = (ULONG64)stack;
|
|
frame->pc = (ULONG64)pKiUserApcDispatcher;
|
|
frame->restore_flags |= CONTEXT_CONTROL;
|
|
syscall_frame_fixup_for_fastpath( frame );
|
|
return status;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* call_raise_user_exception_dispatcher
|
|
*/
|
|
void call_raise_user_exception_dispatcher(void)
|
|
{
|
|
arm64_thread_data()->syscall_frame->pc = (UINT64)pKiRaiseUserExceptionDispatcher;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* call_user_exception_dispatcher
|
|
*/
|
|
NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
struct exc_stack_layout *stack;
|
|
NTSTATUS status = NtSetContextThread( GetCurrentThread(), context );
|
|
|
|
if (status) return status;
|
|
stack = (struct exc_stack_layout *)(context->Sp & ~15) - 1;
|
|
memmove( &stack->context, context, sizeof(*context) );
|
|
memmove( &stack->rec, rec, sizeof(*rec) );
|
|
frame->pc = (ULONG64)pKiUserExceptionDispatcher;
|
|
frame->sp = (ULONG64)stack;
|
|
frame->restore_flags |= CONTEXT_CONTROL;
|
|
syscall_frame_fixup_for_fastpath( frame );
|
|
return status;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* call_user_mode_callback
|
|
*/
|
|
extern NTSTATUS call_user_mode_callback( ULONG64 user_sp, void **ret_ptr, ULONG *ret_len,
|
|
void *func, TEB *teb );
|
|
__ASM_GLOBAL_FUNC( call_user_mode_callback,
|
|
"stp x29, x30, [sp,#-0xc0]!\n\t"
|
|
__ASM_CFI(".cfi_def_cfa_offset 0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 29,-0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 30,-0xb8\n\t")
|
|
"mov x29, sp\n\t"
|
|
__ASM_CFI(".cfi_def_cfa_register 29\n\t")
|
|
"stp x19, x20, [x29, #0x10]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 19,0x10\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 20,0x18\n\t")
|
|
"stp x21, x22, [x29, #0x20]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 21,0x20\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 22,0x28\n\t")
|
|
"stp x23, x24, [x29, #0x30]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 23,0x30\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 24,0x38\n\t")
|
|
"stp x25, x26, [x29, #0x40]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 25,0x40\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 26,0x48\n\t")
|
|
"stp x27, x28, [x29, #0x50]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 27,0x50\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 28,0x58\n\t")
|
|
"stp d8, d9, [x29, #0x60]\n\t"
|
|
"stp d10, d11, [x29, #0x70]\n\t"
|
|
"stp d12, d13, [x29, #0x80]\n\t"
|
|
"stp d14, d15, [x29, #0x90]\n\t"
|
|
"stp x1, x2, [x29, #0xa0]\n\t" /* ret_ptr, ret_len */
|
|
"mov x18, x4\n\t" /* teb */
|
|
"mrs x1, fpcr\n\t"
|
|
"mrs x2, fpsr\n\t"
|
|
"bfi x1, x2, #0, #32\n\t"
|
|
"ldr x2, [x18]\n\t" /* teb->Tib.ExceptionList */
|
|
"stp x1, x2, [x29, #0xb0]\n\t"
|
|
|
|
"ldr x7, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"sub x1, sp, #0x330\n\t" /* sizeof(struct syscall_frame) */
|
|
"str x1, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"add x8, x29, #0xc0\n\t"
|
|
"stp x7, x8, [x1, #0x110]\n\t" /* frame->prev_frame,syscall_cfa */
|
|
/* switch to user stack */
|
|
"mov sp, x0\n\t" /* user_sp */
|
|
"br x3" )
|
|
|
|
|
|
/***********************************************************************
|
|
* user_mode_callback_return
|
|
*/
|
|
extern void DECLSPEC_NORETURN user_mode_callback_return( void *ret_ptr, ULONG ret_len,
|
|
NTSTATUS status, TEB *teb );
|
|
__ASM_GLOBAL_FUNC( user_mode_callback_return,
|
|
"ldr x4, [x3, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"ldp x5, x29, [x4,#0x110]\n\t" /* prev_frame,syscall_cfa */
|
|
"str x5, [x3, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"sub x29, x29, #0xc0\n\t"
|
|
__ASM_CFI(".cfi_def_cfa_register 29\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 29,0x00\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 30,0x08\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 19,0x10\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 20,0x18\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 21,0x20\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 22,0x28\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 23,0x30\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 24,0x38\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 25,0x40\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 26,0x48\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 27,0x50\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 28,0x58\n\t")
|
|
"ldp x5, x6, [x29, #0xb0]\n\t"
|
|
"str x6, [x3]\n\t" /* teb->Tib.ExceptionList */
|
|
"msr fpcr, x5\n\t"
|
|
"lsr x5, x5, #32\n\t"
|
|
"msr fpsr, x5\n\t"
|
|
"ldp x19, x20, [x29, #0x10]\n\t"
|
|
__ASM_CFI(".cfi_same_value 19\n\t")
|
|
__ASM_CFI(".cfi_same_value 20\n\t")
|
|
"ldp x21, x22, [x29, #0x20]\n\t"
|
|
__ASM_CFI(".cfi_same_value 21\n\t")
|
|
__ASM_CFI(".cfi_same_value 22\n\t")
|
|
"ldp x23, x24, [x29, #0x30]\n\t"
|
|
__ASM_CFI(".cfi_same_value 23\n\t")
|
|
__ASM_CFI(".cfi_same_value 24\n\t")
|
|
"ldp x25, x26, [x29, #0x40]\n\t"
|
|
__ASM_CFI(".cfi_same_value 25\n\t")
|
|
__ASM_CFI(".cfi_same_value 26\n\t")
|
|
"ldp x27, x28, [x29, #0x50]\n\t"
|
|
__ASM_CFI(".cfi_same_value 27\n\t")
|
|
__ASM_CFI(".cfi_same_value 28\n\t")
|
|
"ldp d8, d9, [x29, #0x60]\n\t"
|
|
"ldp d10, d11, [x29, #0x70]\n\t"
|
|
"ldp d12, d13, [x29, #0x80]\n\t"
|
|
"ldp d14, d15, [x29, #0x90]\n\t"
|
|
"ldp x5, x6, [x29, #0xa0]\n\t" /* ret_ptr, ret_len */
|
|
"str x0, [x5]\n\t" /* ret_ptr */
|
|
"str w1, [x6]\n\t" /* ret_len */
|
|
"mov x0, x2\n\t" /* status */
|
|
"mov sp, x29\n\t"
|
|
"ldp x29, x30, [sp], #0xc0\n\t"
|
|
"ret" )
|
|
|
|
|
|
/***********************************************************************
|
|
* user_mode_abort_thread
|
|
*/
|
|
extern void DECLSPEC_NORETURN user_mode_abort_thread( NTSTATUS status, struct syscall_frame *frame );
|
|
__ASM_GLOBAL_FUNC( user_mode_abort_thread,
|
|
"ldr x1, [x1, #0x110]\n\t" /* frame->syscall_cfa */
|
|
"sub x29, x1, #0xc0\n\t"
|
|
/* switch to kernel stack */
|
|
"mov sp, x29\n\t"
|
|
__ASM_CFI(".cfi_def_cfa 29,0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 29,-0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 30,-0xb8\n\t")
|
|
__ASM_CFI(".cfi_offset 19,-0xb0\n\t")
|
|
__ASM_CFI(".cfi_offset 20,-0xa8\n\t")
|
|
__ASM_CFI(".cfi_offset 21,-0xa0\n\t")
|
|
__ASM_CFI(".cfi_offset 22,-0x98\n\t")
|
|
__ASM_CFI(".cfi_offset 23,-0x90\n\t")
|
|
__ASM_CFI(".cfi_offset 24,-0x88\n\t")
|
|
__ASM_CFI(".cfi_offset 25,-0x80\n\t")
|
|
__ASM_CFI(".cfi_offset 26,-0x78\n\t")
|
|
__ASM_CFI(".cfi_offset 27,-0x70\n\t")
|
|
__ASM_CFI(".cfi_offset 28,-0x68\n\t")
|
|
"bl " __ASM_NAME("abort_thread") )
|
|
|
|
|
|
/***********************************************************************
|
|
* KeUserModeCallback
|
|
*/
|
|
NTSTATUS KeUserModeCallback( ULONG id, const void *args, ULONG len, void **ret_ptr, ULONG *ret_len )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
ULONG64 sp = (frame->sp - offsetof( struct callback_stack_layout, args_data[len] ) - 16) & ~15;
|
|
struct callback_stack_layout *stack = (struct callback_stack_layout *)sp;
|
|
|
|
if ((char *)ntdll_get_thread_data()->kernel_stack + min_kernel_stack > (char *)&frame)
|
|
return STATUS_STACK_OVERFLOW;
|
|
|
|
stack->args = stack->args_data;
|
|
stack->len = len;
|
|
stack->id = id;
|
|
stack->lr = frame->lr;
|
|
stack->sp = frame->sp;
|
|
stack->pc = frame->pc;
|
|
memcpy( stack->args_data, args, len );
|
|
return call_user_mode_callback( sp, ret_ptr, ret_len, pKiUserCallbackDispatcher, NtCurrentTeb() );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtCallbackReturn (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtCallbackReturn( void *ret_ptr, ULONG ret_len, NTSTATUS status )
|
|
{
|
|
if (!arm64_thread_data()->syscall_frame->prev_frame) return STATUS_NO_CALLBACK_ACTIVE;
|
|
user_mode_callback_return( ret_ptr, ret_len, status, NtCurrentTeb() );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* handle_syscall_fault
|
|
*
|
|
* Handle a page fault happening during a system call.
|
|
*/
|
|
static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
DWORD i;
|
|
|
|
if (!is_inside_syscall( context )) return FALSE;
|
|
|
|
TRACE( "code=%x flags=%x addr=%p pc=%p tid=%04x\n",
|
|
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
|
|
(void *)PC_sig(context), GetCurrentThreadId() );
|
|
for (i = 0; i < rec->NumberParameters; i++)
|
|
TRACE( " info[%d]=%016lx\n", i, rec->ExceptionInformation[i] );
|
|
|
|
TRACE(" x0=%016lx x1=%016lx x2=%016lx x3=%016lx\n",
|
|
(DWORD64)REGn_sig(0, context), (DWORD64)REGn_sig(1, context),
|
|
(DWORD64)REGn_sig(2, context), (DWORD64)REGn_sig(3, context) );
|
|
TRACE(" x4=%016lx x5=%016lx x6=%016lx x7=%016lx\n",
|
|
(DWORD64)REGn_sig(4, context), (DWORD64)REGn_sig(5, context),
|
|
(DWORD64)REGn_sig(6, context), (DWORD64)REGn_sig(7, context) );
|
|
TRACE(" x8=%016lx x9=%016lx x10=%016lx x11=%016lx\n",
|
|
(DWORD64)REGn_sig(8, context), (DWORD64)REGn_sig(9, context),
|
|
(DWORD64)REGn_sig(10, context), (DWORD64)REGn_sig(11, context) );
|
|
TRACE(" x12=%016lx x13=%016lx x14=%016lx x15=%016lx\n",
|
|
(DWORD64)REGn_sig(12, context), (DWORD64)REGn_sig(13, context),
|
|
(DWORD64)REGn_sig(14, context), (DWORD64)REGn_sig(15, context) );
|
|
TRACE(" x16=%016lx x17=%016lx x18=%016lx x19=%016lx\n",
|
|
(DWORD64)REGn_sig(16, context), (DWORD64)REGn_sig(17, context),
|
|
(DWORD64)REGn_sig(18, context), (DWORD64)REGn_sig(19, context) );
|
|
TRACE(" x20=%016lx x21=%016lx x22=%016lx x23=%016lx\n",
|
|
(DWORD64)REGn_sig(20, context), (DWORD64)REGn_sig(21, context),
|
|
(DWORD64)REGn_sig(22, context), (DWORD64)REGn_sig(23, context) );
|
|
TRACE(" x24=%016lx x25=%016lx x26=%016lx x27=%016lx\n",
|
|
(DWORD64)REGn_sig(24, context), (DWORD64)REGn_sig(25, context),
|
|
(DWORD64)REGn_sig(26, context), (DWORD64)REGn_sig(27, context) );
|
|
TRACE(" x28=%016lx fp=%016lx lr=%016lx sp=%016lx\n",
|
|
(DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context),
|
|
(DWORD64)LR_sig(context), (DWORD64)SP_sig(context) );
|
|
|
|
if (ntdll_get_thread_data()->jmp_buf)
|
|
{
|
|
TRACE( "returning to handler\n" );
|
|
REGn_sig(0, context) = (ULONG_PTR)ntdll_get_thread_data()->jmp_buf;
|
|
REGn_sig(1, context) = 1;
|
|
PC_sig(context) = (ULONG_PTR)longjmp;
|
|
ntdll_get_thread_data()->jmp_buf = NULL;
|
|
}
|
|
else
|
|
{
|
|
TRACE( "returning to user mode ip=%p ret=%08x\n", (void *)frame->pc, rec->ExceptionCode );
|
|
REGn_sig(0, context) = (ULONG_PTR)frame;
|
|
REGn_sig(1, context) = rec->ExceptionCode;
|
|
PC_sig(context) = (ULONG_PTR)__wine_syscall_dispatcher_return;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* segv_handler
|
|
*
|
|
* Handler for SIGSEGV.
|
|
*/
|
|
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { 0 };
|
|
ucontext_t *context = sigcontext;
|
|
|
|
rec.NumberParameters = 2;
|
|
rec.ExceptionInformation[0] = (get_fault_esr( context ) & 0x40) != 0;
|
|
rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr;
|
|
rec.ExceptionCode = virtual_handle_fault( siginfo->si_addr, rec.ExceptionInformation[0],
|
|
(void *)SP_sig(context) );
|
|
if (!rec.ExceptionCode) return;
|
|
if (handle_syscall_fault( context, &rec )) return;
|
|
setup_exception( context, &rec );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* ill_handler
|
|
*
|
|
* Handler for SIGILL.
|
|
*/
|
|
static void ill_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { EXCEPTION_ILLEGAL_INSTRUCTION };
|
|
|
|
setup_exception( sigcontext, &rec );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* bus_handler
|
|
*
|
|
* Handler for SIGBUS.
|
|
*/
|
|
static void bus_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { EXCEPTION_DATATYPE_MISALIGNMENT };
|
|
|
|
setup_exception( sigcontext, &rec );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* trap_handler
|
|
*
|
|
* Handler for SIGTRAP.
|
|
*/
|
|
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { 0 };
|
|
ucontext_t *context = sigcontext;
|
|
CONTEXT ctx;
|
|
|
|
rec.ExceptionAddress = (void *)PC_sig(context);
|
|
save_context( &ctx, sigcontext );
|
|
|
|
switch (siginfo->si_code)
|
|
{
|
|
case TRAP_TRACE:
|
|
rec.ExceptionCode = EXCEPTION_SINGLE_STEP;
|
|
break;
|
|
case TRAP_BRKPT:
|
|
default:
|
|
/* debug exceptions do not update ESR on Linux, so we fetch the instruction directly. */
|
|
if (!(PSTATE_sig( context ) & 0x10) && /* AArch64 (not WoW) */
|
|
!(PC_sig( context ) & 3) &&
|
|
*(ULONG *)PC_sig( context ) == 0xd43e0060UL) /* brk #0xf003 -> __fastfail */
|
|
{
|
|
rec.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
|
|
rec.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
|
rec.NumberParameters = 1;
|
|
rec.ExceptionInformation[0] = ctx.X[0];
|
|
NtRaiseException( &rec, &ctx, FALSE );
|
|
return;
|
|
}
|
|
ctx.Pc += 4; /* skip the brk instruction */
|
|
rec.ExceptionCode = EXCEPTION_BREAKPOINT;
|
|
rec.NumberParameters = 1;
|
|
break;
|
|
}
|
|
|
|
setup_raise_exception( sigcontext, &rec, &ctx );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* fpe_handler
|
|
*
|
|
* Handler for SIGFPE.
|
|
*/
|
|
static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { 0 };
|
|
|
|
switch (siginfo->si_code & 0xffff )
|
|
{
|
|
#ifdef FPE_FLTSUB
|
|
case FPE_FLTSUB:
|
|
rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_INTDIV
|
|
case FPE_INTDIV:
|
|
rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_INTOVF
|
|
case FPE_INTOVF:
|
|
rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_FLTDIV
|
|
case FPE_FLTDIV:
|
|
rec.ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_FLTOVF
|
|
case FPE_FLTOVF:
|
|
rec.ExceptionCode = EXCEPTION_FLT_OVERFLOW;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_FLTUND
|
|
case FPE_FLTUND:
|
|
rec.ExceptionCode = EXCEPTION_FLT_UNDERFLOW;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_FLTRES
|
|
case FPE_FLTRES:
|
|
rec.ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT;
|
|
break;
|
|
#endif
|
|
#ifdef FPE_FLTINV
|
|
case FPE_FLTINV:
|
|
#endif
|
|
default:
|
|
rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
|
break;
|
|
}
|
|
setup_exception( sigcontext, &rec );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* int_handler
|
|
*
|
|
* Handler for SIGINT.
|
|
*/
|
|
static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
HANDLE handle;
|
|
|
|
if (!p__wine_ctrl_routine) return;
|
|
if (!NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(),
|
|
p__wine_ctrl_routine, 0 /* CTRL_C_EVENT */, 0, 0, 0, 0, NULL ))
|
|
NtClose( handle );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* abrt_handler
|
|
*
|
|
* Handler for SIGABRT.
|
|
*/
|
|
static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec = { EXCEPTION_WINE_ASSERTION, EXCEPTION_NONCONTINUABLE };
|
|
|
|
setup_exception( sigcontext, &rec );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* quit_handler
|
|
*
|
|
* Handler for SIGQUIT.
|
|
*/
|
|
static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
if (!is_inside_syscall( sigcontext )) user_mode_abort_thread( 0, arm64_thread_data()->syscall_frame );
|
|
abort_thread(0);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* usr1_handler
|
|
*
|
|
* Handler for SIGUSR1, used to signal a thread that it got suspended.
|
|
*/
|
|
static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
CONTEXT context;
|
|
|
|
if (is_inside_syscall( sigcontext ))
|
|
{
|
|
context.ContextFlags = CONTEXT_FULL;
|
|
NtGetContextThread( GetCurrentThread(), &context );
|
|
wait_suspend( &context );
|
|
NtSetContextThread( GetCurrentThread(), &context );
|
|
}
|
|
else
|
|
{
|
|
save_context( &context, sigcontext );
|
|
wait_suspend( &context );
|
|
restore_context( &context, sigcontext );
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* usr2_handler
|
|
*
|
|
* Handler for SIGUSR2, used to set a thread context.
|
|
*/
|
|
static void usr2_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
struct syscall_frame *frame = arm64_thread_data()->syscall_frame;
|
|
ucontext_t *context = sigcontext;
|
|
DWORD i;
|
|
|
|
if (!is_inside_syscall( sigcontext )) return;
|
|
|
|
FP_sig(context) = frame->fp;
|
|
LR_sig(context) = frame->lr;
|
|
SP_sig(context) = frame->sp;
|
|
PC_sig(context) = frame->pc;
|
|
PSTATE_sig(context) = frame->cpsr;
|
|
for (i = 0; i <= 28; i++) REGn_sig( i, context ) = frame->x[i];
|
|
|
|
#ifdef linux
|
|
{
|
|
struct fpsimd_context *fp = get_fpsimd_context( sigcontext );
|
|
if (fp)
|
|
{
|
|
fp->fpcr = frame->fpcr;
|
|
fp->fpsr = frame->fpsr;
|
|
memcpy( fp->vregs, frame->v, sizeof(fp->vregs) );
|
|
}
|
|
}
|
|
#elif defined(__APPLE__)
|
|
context->uc_mcontext->__ns.__fpcr = frame->fpcr;
|
|
context->uc_mcontext->__ns.__fpsr = frame->fpsr;
|
|
memcpy( context->uc_mcontext->__ns.__v, frame->v, sizeof(frame->v) );
|
|
#endif
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* get_thread_ldt_entry
|
|
*/
|
|
NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtSetLdtEntries (NTDLL.@)
|
|
* ZwSetLdtEntries (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtSetLdtEntries( ULONG sel1, LDT_ENTRY entry1, ULONG sel2, LDT_ENTRY entry2 )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* signal_init_threading
|
|
*/
|
|
void signal_init_threading(void)
|
|
{
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* signal_alloc_thread
|
|
*/
|
|
NTSTATUS signal_alloc_thread( TEB *teb )
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* signal_free_thread
|
|
*/
|
|
void signal_free_thread( TEB *teb )
|
|
{
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* signal_init_process
|
|
*/
|
|
void signal_init_process(void)
|
|
{
|
|
struct sigaction sig_act;
|
|
void *kernel_stack = (char *)ntdll_get_thread_data()->kernel_stack + kernel_stack_size;
|
|
|
|
arm64_thread_data()->syscall_frame = (struct syscall_frame *)kernel_stack - 1;
|
|
|
|
sig_act.sa_mask = server_block_set;
|
|
sig_act.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
|
|
|
|
sig_act.sa_sigaction = int_handler;
|
|
if (sigaction( SIGINT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = fpe_handler;
|
|
if (sigaction( SIGFPE, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = abrt_handler;
|
|
if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = quit_handler;
|
|
if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = usr1_handler;
|
|
if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = usr2_handler;
|
|
if (sigaction( SIGUSR2, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = trap_handler;
|
|
if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = segv_handler;
|
|
if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = ill_handler;
|
|
if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = bus_handler;
|
|
if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error;
|
|
return;
|
|
|
|
error:
|
|
perror("sigaction");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* syscall_dispatcher_return_slowpath
|
|
*/
|
|
void syscall_dispatcher_return_slowpath(void)
|
|
{
|
|
raise( SIGUSR2 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* call_init_thunk
|
|
*/
|
|
void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB *teb,
|
|
struct syscall_frame *frame, void *syscall_cfa )
|
|
{
|
|
struct arm64_thread_data *thread_data = (struct arm64_thread_data *)&teb->GdiTebBatch;
|
|
CONTEXT *ctx, context = { CONTEXT_ALL };
|
|
I386_CONTEXT *i386_context;
|
|
ARM_CONTEXT *arm_context;
|
|
|
|
thread_data->syscall_table = KeServiceDescriptorTable;
|
|
|
|
context.X0 = (DWORD64)entry;
|
|
context.X1 = (DWORD64)arg;
|
|
context.X18 = (DWORD64)teb;
|
|
context.Sp = (DWORD64)teb->Tib.StackBase;
|
|
context.Pc = (DWORD64)pRtlUserThreadStart;
|
|
|
|
if ((i386_context = get_cpu_area( IMAGE_FILE_MACHINE_I386 )))
|
|
{
|
|
XMM_SAVE_AREA32 *fpu = (XMM_SAVE_AREA32 *)i386_context->ExtendedRegisters;
|
|
i386_context->ContextFlags = CONTEXT_I386_ALL;
|
|
i386_context->Eax = (ULONG_PTR)entry;
|
|
i386_context->Ebx = (arg == peb ? (ULONG_PTR)wow_peb : (ULONG_PTR)arg);
|
|
i386_context->Esp = get_wow_teb( teb )->Tib.StackBase - 16;
|
|
i386_context->Eip = pLdrSystemDllInitBlock->pRtlUserThreadStart;
|
|
i386_context->SegCs = 0x23;
|
|
i386_context->SegDs = 0x2b;
|
|
i386_context->SegEs = 0x2b;
|
|
i386_context->SegFs = 0x53;
|
|
i386_context->SegGs = 0x2b;
|
|
i386_context->SegSs = 0x2b;
|
|
i386_context->EFlags = 0x202;
|
|
fpu->ControlWord = 0x27f;
|
|
fpu->MxCsr = 0x1f80;
|
|
fpux_to_fpu( &i386_context->FloatSave, fpu );
|
|
}
|
|
else if ((arm_context = get_cpu_area( IMAGE_FILE_MACHINE_ARMNT )))
|
|
{
|
|
arm_context->ContextFlags = CONTEXT_ARM_ALL;
|
|
arm_context->R0 = (ULONG_PTR)entry;
|
|
arm_context->R1 = (arg == peb ? (ULONG_PTR)wow_peb : (ULONG_PTR)arg);
|
|
arm_context->Sp = get_wow_teb( teb )->Tib.StackBase;
|
|
arm_context->Pc = pLdrSystemDllInitBlock->pRtlUserThreadStart;
|
|
if (arm_context->Pc & 1) arm_context->Cpsr |= 0x20; /* thumb mode */
|
|
}
|
|
|
|
if (suspend) wait_suspend( &context );
|
|
|
|
ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1;
|
|
*ctx = context;
|
|
ctx->ContextFlags = CONTEXT_FULL;
|
|
memset( frame, 0, sizeof(*frame) );
|
|
NtSetContextThread( GetCurrentThread(), ctx );
|
|
|
|
frame->sp = (ULONG64)ctx;
|
|
frame->pc = (ULONG64)pLdrInitializeThunk;
|
|
frame->x[0] = (ULONG64)ctx;
|
|
frame->x[18] = (ULONG64)teb;
|
|
frame->restore_flags |= CONTEXT_INTEGER;
|
|
frame->syscall_cfa = syscall_cfa;
|
|
syscall_frame_fixup_for_fastpath( frame );
|
|
|
|
pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL );
|
|
__wine_syscall_dispatcher_return( frame, 0 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* signal_start_thread
|
|
*/
|
|
__ASM_GLOBAL_FUNC( signal_start_thread,
|
|
"stp x29, x30, [sp,#-0xc0]!\n\t"
|
|
__ASM_CFI(".cfi_def_cfa_offset 0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 29,-0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 30,-0xb8\n\t")
|
|
"mov x29, sp\n\t"
|
|
__ASM_CFI(".cfi_def_cfa_register 29\n\t")
|
|
"stp x19, x20, [x29, #0x10]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 19,0x10\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 20,0x18\n\t")
|
|
"stp x21, x22, [x29, #0x20]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 21,0x20\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 22,0x28\n\t")
|
|
"stp x23, x24, [x29, #0x30]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 23,0x30\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 24,0x38\n\t")
|
|
"stp x25, x26, [x29, #0x40]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 25,0x40\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 26,0x48\n\t")
|
|
"stp x27, x28, [x29, #0x50]\n\t"
|
|
__ASM_CFI(".cfi_rel_offset 27,0x50\n\t")
|
|
__ASM_CFI(".cfi_rel_offset 28,0x58\n\t")
|
|
"add x5, x29, #0xc0\n\t" /* syscall_cfa */
|
|
/* set syscall frame */
|
|
"ldr x4, [x3, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"cbnz x4, 1f\n\t"
|
|
"sub x4, sp, #0x330\n\t" /* sizeof(struct syscall_frame) */
|
|
"str x4, [x3, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
/* switch to kernel stack */
|
|
"1:\tmov sp, x4\n\t"
|
|
"bl " __ASM_NAME("call_init_thunk") )
|
|
|
|
|
|
/***********************************************************************
|
|
* __wine_syscall_dispatcher
|
|
*/
|
|
__ASM_GLOBAL_FUNC( __wine_syscall_dispatcher,
|
|
"ldr x10, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"stp x18, x19, [x10, #0x90]\n\t"
|
|
"stp x20, x21, [x10, #0xa0]\n\t"
|
|
"stp x22, x23, [x10, #0xb0]\n\t"
|
|
"stp x24, x25, [x10, #0xc0]\n\t"
|
|
"stp x26, x27, [x10, #0xd0]\n\t"
|
|
"stp x28, x29, [x10, #0xe0]\n\t"
|
|
"mov x19, sp\n\t"
|
|
"stp x9, x19, [x10, #0xf0]\n\t"
|
|
"mrs x9, NZCV\n\t"
|
|
"stp x30, x9, [x10, #0x100]\n\t"
|
|
"mrs x9, FPCR\n\t"
|
|
"str w9, [x10, #0x128]\n\t"
|
|
"mrs x9, FPSR\n\t"
|
|
"str w9, [x10, #0x12c]\n\t"
|
|
"stp q0, q1, [x10, #0x130]\n\t"
|
|
"stp q2, q3, [x10, #0x150]\n\t"
|
|
"stp q4, q5, [x10, #0x170]\n\t"
|
|
"stp q6, q7, [x10, #0x190]\n\t"
|
|
"stp q8, q9, [x10, #0x1b0]\n\t"
|
|
"stp q10, q11, [x10, #0x1d0]\n\t"
|
|
"stp q12, q13, [x10, #0x1f0]\n\t"
|
|
"stp q14, q15, [x10, #0x210]\n\t"
|
|
"stp q16, q17, [x10, #0x230]\n\t"
|
|
"stp q18, q19, [x10, #0x250]\n\t"
|
|
"stp q20, q21, [x10, #0x270]\n\t"
|
|
"stp q22, q23, [x10, #0x290]\n\t"
|
|
"stp q24, q25, [x10, #0x2b0]\n\t"
|
|
"stp q26, q27, [x10, #0x2d0]\n\t"
|
|
"stp q28, q29, [x10, #0x2f0]\n\t"
|
|
"stp q30, q31, [x10, #0x310]\n\t"
|
|
"mov x22, x10\n\t"
|
|
/* switch to kernel stack */
|
|
"mov sp, x10\n\t"
|
|
/* we're now on the kernel stack, stitch unwind info with previous frame */
|
|
__ASM_CFI_CFA_IS_AT2(x22, 0x98, 0x02) /* frame->syscall_cfa */
|
|
__ASM_CFI(".cfi_offset 29, -0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 30, -0xb8\n\t")
|
|
__ASM_CFI(".cfi_offset 19, -0xb0\n\t")
|
|
__ASM_CFI(".cfi_offset 20, -0xa8\n\t")
|
|
__ASM_CFI(".cfi_offset 21, -0xa0\n\t")
|
|
__ASM_CFI(".cfi_offset 22, -0x98\n\t")
|
|
__ASM_CFI(".cfi_offset 23, -0x90\n\t")
|
|
__ASM_CFI(".cfi_offset 24, -0x88\n\t")
|
|
__ASM_CFI(".cfi_offset 25, -0x80\n\t")
|
|
__ASM_CFI(".cfi_offset 26, -0x78\n\t")
|
|
__ASM_CFI(".cfi_offset 27, -0x70\n\t")
|
|
__ASM_CFI(".cfi_offset 28, -0x68\n\t")
|
|
"and x20, x8, #0xfff\n\t" /* syscall number */
|
|
"ubfx x21, x8, #12, #2\n\t" /* syscall table number */
|
|
"ldr x16, [x18, #0x2f8]\n\t" /* arm64_thread_data()->syscall_table */
|
|
"add x21, x16, x21, lsl #5\n\t"
|
|
"ldr x16, [x21, #16]\n\t" /* table->ServiceLimit */
|
|
"cmp x20, x16\n\t"
|
|
"bcs 4f\n\t"
|
|
"ldr x16, [x21, #24]\n\t" /* table->ArgumentTable */
|
|
"ldrb w9, [x16, x20]\n\t"
|
|
"subs x9, x9, #64\n\t"
|
|
"bls 2f\n\t"
|
|
"sub sp, sp, x9\n\t"
|
|
"tbz x9, #3, 1f\n\t"
|
|
"sub sp, sp, #8\n"
|
|
"1:\tsub x9, x9, #8\n\t"
|
|
"ldr x10, [x19, x9]\n\t"
|
|
"str x10, [sp, x9]\n\t"
|
|
"cbnz x9, 1b\n"
|
|
"2:\tldr x16, [x21]\n\t" /* table->ServiceTable */
|
|
"ldr x16, [x16, x20, lsl 3]\n\t"
|
|
"blr x16\n\t"
|
|
"mov sp, x22\n"
|
|
__ASM_CFI_CFA_IS_AT2(sp, 0x98, 0x02) /* frame->syscall_cfa */
|
|
__ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") ":\n\t"
|
|
"ldr w16, [sp, #0x10c]\n\t" /* frame->restore_flags */
|
|
"tbz x16, #1, 2f\n\t" /* CONTEXT_INTEGER */
|
|
"ldp x12, x13, [sp, #0x80]\n\t" /* frame->x[16..17] */
|
|
"ldp x14, x15, [sp, #0xf8]\n\t" /* frame->sp, frame->pc */
|
|
"cmp x12, x15\n\t" /* frame->x16 == frame->pc? */
|
|
"ccmp x13, x14, #0, eq\n\t" /* frame->x17 == frame->sp? */
|
|
"beq 1f\n\t" /* take slowpath if unequal */
|
|
"bl " __ASM_NAME("syscall_dispatcher_return_slowpath") "\n"
|
|
"1:\tldp x0, x1, [sp, #0x00]\n\t"
|
|
"ldp x2, x3, [sp, #0x10]\n\t"
|
|
"ldp x4, x5, [sp, #0x20]\n\t"
|
|
"ldp x6, x7, [sp, #0x30]\n\t"
|
|
"ldp x8, x9, [sp, #0x40]\n\t"
|
|
"ldp x10, x11, [sp, #0x50]\n\t"
|
|
"ldp x12, x13, [sp, #0x60]\n\t"
|
|
"ldp x14, x15, [sp, #0x70]\n"
|
|
"2:\tldp x18, x19, [sp, #0x90]\n\t"
|
|
"ldp x20, x21, [sp, #0xa0]\n\t"
|
|
"ldp x22, x23, [sp, #0xb0]\n\t"
|
|
"ldp x24, x25, [sp, #0xc0]\n\t"
|
|
"ldp x26, x27, [sp, #0xd0]\n\t"
|
|
"ldp x28, x29, [sp, #0xe0]\n\t"
|
|
"tbz x16, #2, 1f\n\t" /* CONTEXT_FLOATING_POINT */
|
|
"ldp q0, q1, [sp, #0x130]\n\t"
|
|
"ldp q2, q3, [sp, #0x150]\n\t"
|
|
"ldp q4, q5, [sp, #0x170]\n\t"
|
|
"ldp q6, q7, [sp, #0x190]\n\t"
|
|
"ldp q8, q9, [sp, #0x1b0]\n\t"
|
|
"ldp q10, q11, [sp, #0x1d0]\n\t"
|
|
"ldp q12, q13, [sp, #0x1f0]\n\t"
|
|
"ldp q14, q15, [sp, #0x210]\n\t"
|
|
"ldp q16, q17, [sp, #0x230]\n\t"
|
|
"ldp q18, q19, [sp, #0x250]\n\t"
|
|
"ldp q20, q21, [sp, #0x270]\n\t"
|
|
"ldp q22, q23, [sp, #0x290]\n\t"
|
|
"ldp q24, q25, [sp, #0x2b0]\n\t"
|
|
"ldp q26, q27, [sp, #0x2d0]\n\t"
|
|
"ldp q28, q29, [sp, #0x2f0]\n\t"
|
|
"ldp q30, q31, [sp, #0x310]\n\t"
|
|
"ldr w17, [sp, #0x128]\n\t"
|
|
"msr FPCR, x17\n\t"
|
|
"ldr w17, [sp, #0x12c]\n\t"
|
|
"msr FPSR, x17\n"
|
|
"1:\tldp x16, x17, [sp, #0x100]\n\t"
|
|
"msr NZCV, x17\n\t"
|
|
"ldp x30, x17, [sp, #0xf0]\n\t"
|
|
/* switch to user stack */
|
|
"mov sp, x17\n\t"
|
|
"ret x16\n"
|
|
"4:\tmov x0, #0xc0000000\n\t" /* STATUS_INVALID_PARAMETER */
|
|
"movk x0, #0x000d\n\t"
|
|
"b " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") "\n\t"
|
|
".globl " __ASM_NAME("__wine_syscall_dispatcher_return") "\n"
|
|
__ASM_NAME("__wine_syscall_dispatcher_return") ":\n\t"
|
|
"mov sp, x0\n\t"
|
|
"mov x0, x1\n\t"
|
|
"b " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") )
|
|
|
|
|
|
/***********************************************************************
|
|
* __wine_unix_call_dispatcher
|
|
*/
|
|
__ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher,
|
|
"ldr x10, [x18, #0x2f0]\n\t" /* arm64_thread_data()->syscall_frame */
|
|
"stp x18, x19, [x10, #0x90]\n\t"
|
|
"stp x20, x21, [x10, #0xa0]\n\t"
|
|
"stp x22, x23, [x10, #0xb0]\n\t"
|
|
"stp x24, x25, [x10, #0xc0]\n\t"
|
|
"stp x26, x27, [x10, #0xd0]\n\t"
|
|
"stp x28, x29, [x10, #0xe0]\n\t"
|
|
"stp q8, q9, [x10, #0x1b0]\n\t"
|
|
"stp q10, q11, [x10, #0x1d0]\n\t"
|
|
"stp q12, q13, [x10, #0x1f0]\n\t"
|
|
"stp q14, q15, [x10, #0x210]\n\t"
|
|
"mov x9, sp\n\t"
|
|
"stp x30, x9, [x10, #0xf0]\n\t"
|
|
"mrs x9, NZCV\n\t"
|
|
"stp x30, x9, [x10, #0x100]\n\t"
|
|
"mov x19, x10\n\t"
|
|
/* switch to kernel stack */
|
|
"mov sp, x10\n\t"
|
|
/* we're now on the kernel stack, stitch unwind info with previous frame */
|
|
__ASM_CFI_CFA_IS_AT2(x19, 0x98, 0x02) /* frame->syscall_cfa */
|
|
__ASM_CFI(".cfi_offset 29, -0xc0\n\t")
|
|
__ASM_CFI(".cfi_offset 30, -0xb8\n\t")
|
|
__ASM_CFI(".cfi_offset 19, -0xb0\n\t")
|
|
__ASM_CFI(".cfi_offset 20, -0xa8\n\t")
|
|
__ASM_CFI(".cfi_offset 21, -0xa0\n\t")
|
|
__ASM_CFI(".cfi_offset 22, -0x98\n\t")
|
|
__ASM_CFI(".cfi_offset 23, -0x90\n\t")
|
|
__ASM_CFI(".cfi_offset 24, -0x88\n\t")
|
|
__ASM_CFI(".cfi_offset 25, -0x80\n\t")
|
|
__ASM_CFI(".cfi_offset 26, -0x78\n\t")
|
|
__ASM_CFI(".cfi_offset 27, -0x70\n\t")
|
|
__ASM_CFI(".cfi_offset 28, -0x68\n\t")
|
|
"ldr x16, [x0, x1, lsl 3]\n\t"
|
|
"mov x0, x2\n\t" /* args */
|
|
"blr x16\n\t"
|
|
"ldr w16, [sp, #0x10c]\n\t" /* frame->restore_flags */
|
|
"cbnz w16, " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") "\n\t"
|
|
__ASM_CFI_CFA_IS_AT2(sp, 0x98, 0x02) /* frame->syscall_cfa */
|
|
"ldp x18, x19, [sp, #0x90]\n\t"
|
|
"ldp x16, x17, [sp, #0xf8]\n\t"
|
|
/* switch to user stack */
|
|
"mov sp, x16\n\t"
|
|
"ret x17" )
|
|
|
|
#endif /* __aarch64__ */
|