ntdll: Switch to the kernel stack for syscalls on x86-64.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-06-11 10:57:26 +02:00
parent 308a5e7c4d
commit 25b093f384
2 changed files with 162 additions and 275 deletions

View File

@ -267,18 +267,6 @@ struct apc_stack_layout
ULONG64 rip;
};
/* Should match size and offset in call_user_apc_dispatcher(). */
C_ASSERT( offsetof(struct apc_stack_layout, context) == 0x30 );
C_ASSERT( sizeof(struct apc_stack_layout) == 0x510 );
struct syscall_xsave
{
XMM_SAVE_AREA32 xsave;
XSTATE xstate;
};
C_ASSERT( sizeof(struct syscall_xsave) == 0x340 );
struct syscall_frame
{
ULONG64 rax; /* 0000 */
@ -306,9 +294,12 @@ struct syscall_frame
WORD gs; /* 0092 */
DWORD restore_flags; /* 0094 */
ULONG64 rbp; /* 0098 */
ULONG64 align[4]; /* 00a0 */
XMM_SAVE_AREA32 xsave; /* 00c0 */
XSTATE xstate; /* 02c0 */
};
C_ASSERT( sizeof( struct syscall_frame ) == 0xa0);
C_ASSERT( sizeof( struct syscall_frame ) == 0x400);
struct amd64_thread_data
{
@ -331,12 +322,13 @@ static inline struct amd64_thread_data *amd64_thread_data(void)
return (struct amd64_thread_data *)ntdll_get_thread_data()->cpu_data;
}
static struct syscall_xsave *get_syscall_xsave( struct syscall_frame *frame )
static BOOL is_inside_syscall( ucontext_t *sigcontext )
{
return (struct syscall_xsave *)((ULONG_PTR)((struct syscall_xsave *)frame - 1) & ~63);
return ((char *)RSP_sig(sigcontext) >= (char *)ntdll_get_thread_data()->kernel_stack &&
(char *)RSP_sig(sigcontext) <= (char *)amd64_thread_data()->syscall_frame);
}
/***********************************************************************
* Definitions for Dwarf unwind tables
*/
@ -1578,8 +1570,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
NTSTATUS ret = STATUS_SUCCESS;
DWORD flags = context->ContextFlags & ~CONTEXT_AMD64;
BOOL self = (handle == GetCurrentThread());
struct syscall_frame *frame;
struct syscall_xsave *xsave;
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
if ((flags & CONTEXT_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX))
{
@ -1618,8 +1609,6 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
}
}
frame = amd64_thread_data()->syscall_frame;
xsave = get_syscall_xsave( frame );
if (flags & CONTEXT_INTEGER)
{
frame->rax = context->Rax;
@ -1655,8 +1644,8 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
}
if (flags & CONTEXT_FLOATING_POINT)
{
xsave->xsave = context->u.FltSave;
xsave->xstate.Mask |= XSTATE_MASK_LEGACY;
frame->xsave = context->u.FltSave;
frame->xstate.Mask |= XSTATE_MASK_LEGACY;
}
if (flags & CONTEXT_XSTATE)
{
@ -1665,10 +1654,10 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
if (xs->Mask & XSTATE_MASK_GSSE)
{
xsave->xstate.Mask |= XSTATE_MASK_GSSE;
memcpy( &xsave->xstate.YmmContext, &xs->YmmContext, sizeof(xs->YmmContext) );
frame->xstate.Mask |= XSTATE_MASK_GSSE;
memcpy( &frame->xstate.YmmContext, &xs->YmmContext, sizeof(xs->YmmContext) );
}
else xsave->xstate.Mask &= ~XSTATE_MASK_GSSE;
else frame->xstate.Mask &= ~XSTATE_MASK_GSSE;
}
frame->restore_flags |= flags & ~CONTEXT_INTEGER;
@ -1741,13 +1730,11 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
}
if (needed_flags & CONTEXT_FLOATING_POINT)
{
struct syscall_xsave *xsave = get_syscall_xsave( frame );
if (!xstate_compaction_enabled ||
(xsave->xstate.Mask & XSTATE_MASK_LEGACY_FLOATING_POINT))
(frame->xstate.Mask & XSTATE_MASK_LEGACY_FLOATING_POINT))
{
memcpy( &context->u.FltSave, &xsave->xsave, FIELD_OFFSET( XSAVE_FORMAT, MxCsr ));
memcpy( context->u.FltSave.FloatRegisters, xsave->xsave.FloatRegisters,
memcpy( &context->u.FltSave, &frame->xsave, FIELD_OFFSET( XSAVE_FORMAT, MxCsr ));
memcpy( context->u.FltSave.FloatRegisters, frame->xsave.FloatRegisters,
sizeof( context->u.FltSave.FloatRegisters ));
}
else
@ -1758,12 +1745,12 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
context->u.FltSave.ControlWord = 0x37f;
}
if (!xstate_compaction_enabled || (xsave->xstate.Mask & XSTATE_MASK_LEGACY_SSE))
if (!xstate_compaction_enabled || (frame->xstate.Mask & XSTATE_MASK_LEGACY_SSE))
{
memcpy( context->u.FltSave.XmmRegisters, xsave->xsave.XmmRegisters,
memcpy( context->u.FltSave.XmmRegisters, frame->xsave.XmmRegisters,
sizeof( context->u.FltSave.XmmRegisters ));
context->u.FltSave.MxCsr = xsave->xsave.MxCsr;
context->u.FltSave.MxCsr_Mask = xsave->xsave.MxCsr_Mask;
context->u.FltSave.MxCsr = frame->xsave.MxCsr;
context->u.FltSave.MxCsr_Mask = frame->xsave.MxCsr_Mask;
}
else
{
@ -1788,7 +1775,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
}
if ((cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX) && (xstate = xstate_from_context( context )))
{
struct syscall_xsave *xsave = get_syscall_xsave( frame );
CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1);
unsigned int mask;
@ -1797,7 +1783,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
return STATUS_INVALID_PARAMETER;
mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE;
xstate->Mask = xsave->xstate.Mask & mask;
xstate->Mask = frame->xstate.Mask & mask;
xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0;
memset( xstate->Reserved, 0, sizeof(xstate->Reserved) );
if (xstate->Mask)
@ -1805,7 +1791,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
if (context_ex->XState.Length < sizeof(XSTATE))
return STATUS_BUFFER_OVERFLOW;
memcpy( &xstate->YmmContext, &xsave->xstate.YmmContext, sizeof(xstate->YmmContext) );
memcpy( &xstate->YmmContext, &frame->xstate.YmmContext, sizeof(xstate->YmmContext) );
}
}
}
@ -1929,76 +1915,40 @@ static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
/***********************************************************************
* call_user_apc_dispatcher
*/
struct apc_stack_layout * WINAPI setup_user_apc_dispatcher_stack( CONTEXT *context,
struct apc_stack_layout *stack,
NTSTATUS status ) DECLSPEC_HIDDEN;
struct apc_stack_layout * WINAPI setup_user_apc_dispatcher_stack( CONTEXT *context,
struct apc_stack_layout *stack,
NTSTATUS status )
NTSTATUS WINAPI call_user_apc_dispatcher( CONTEXT *context, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
PNTAPCFUNC func,
void (WINAPI *dispatcher)(CONTEXT*,ULONG_PTR,ULONG_PTR,ULONG_PTR,PNTAPCFUNC),
NTSTATUS status )
{
CONTEXT c;
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
ULONG64 rsp = context ? context->Rsp : frame->rsp;
struct apc_stack_layout *stack;
if (!context)
rsp &= ~15;
stack = (struct apc_stack_layout *)rsp - 1;
if (context)
{
c.ContextFlags = CONTEXT_FULL;
NtGetContextThread( GetCurrentThread(), &c );
c.Rax = status;
context = &c;
memmove( &stack->context, context, sizeof(stack->context) );
NtSetContextThread( GetCurrentThread(), &stack->context );
}
memmove( &stack->context, context, sizeof(stack->context) );
return stack;
else
{
stack->context.ContextFlags = CONTEXT_FULL;
NtGetContextThread( GetCurrentThread(), &stack->context );
stack->context.Rax = status;
}
frame->rbp = stack->context.Rbp;
frame->rsp = (ULONG64)stack - 8;
frame->rip = (ULONG64)dispatcher;
frame->rcx = (ULONG64)&stack->context;
frame->rdx = arg1;
frame->r8 = arg2;
frame->r9 = arg3;
stack->func = func;
frame->restore_flags |= CONTEXT_CONTROL | CONTEXT_INTEGER;
return status;
}
__ASM_GLOBAL_FUNC( call_user_apc_dispatcher,
"movq 0x28(%rsp),%rsi\n\t" /* func */
"movq 0x30(%rsp),%rdi\n\t" /* dispatcher */
"movq %gs:0x30,%rbx\n\t"
"movq %rdx,%r12\n\t" /* arg1 */
"movq %r8,%r13\n\t" /* arg2 */
"movq %r9,%r14\n\t" /* arg3 */
"movq 0x38(%rsp),%r8\n\t" /* status */
"jrcxz 1f\n\t"
"movq 0x98(%rcx),%rdx\n\t" /* context->Rsp */
"jmp 2f\n\t"
"1:\tmovq 0x328(%rbx),%rax\n\t" /* amd64_thread_data()->syscall_frame */
"movq 0x88(%rax),%rdx\n\t" /* frame->rsp */
"2:\tsubq $0x510,%rdx\n\t" /* sizeof(struct apc_stack_layout) */
"andq $~0xf,%rdx\n\t"
"addq $8,%rsp\n\t" /* pop return address */
"cmpq %rsp,%rdx\n\t"
"cmovbq %rdx,%rsp\n\t"
"subq $0x20,%rsp\n\t"
"call " __ASM_NAME("setup_user_apc_dispatcher_stack") "\n\t"
"movq %rax,%rsp\n\t"
"leaq 0x30(%rsp),%rcx\n\t" /* context */
"movq %r12,%rdx\n\t" /* arg1 */
"movq %r13,%r8\n\t" /* arg2 */
"movq %r14,%r9\n" /* arg3 */
"movq $0,0x328(%rbx)\n\t" /* amd64_thread_data()->syscall_frame */
"movq %rsi,0x20(%rsp)\n\t" /* func */
"movq %rdi,%r10\n\t"
/* Set nonvolatile regs from context. */
"movq 0xa0(%rcx),%rbp\n\t"
"movq 0x90(%rcx),%rbx\n\t"
"movq 0xa8(%rcx),%rsi\n\t"
"movq 0xb0(%rcx),%rdi\n\t"
"movq 0xd8(%rcx),%r12\n\t"
"movq 0xe0(%rcx),%r13\n\t"
"movq 0xe8(%rcx),%r14\n\t"
"movq 0xf0(%rcx),%r15\n\t"
"movdqa 0x200(%rcx),%xmm6\n\t"
"movdqa 0x210(%rcx),%xmm7\n\t"
"movdqa 0x220(%rcx),%xmm8\n\t"
"movdqa 0x230(%rcx),%xmm9\n\t"
"movdqa 0x240(%rcx),%xmm10\n\t"
"movdqa 0x250(%rcx),%xmm11\n\t"
"movdqa 0x260(%rcx),%xmm12\n\t"
"movdqa 0x270(%rcx),%xmm13\n\t"
"movdqa 0x280(%rcx),%xmm14\n\t"
"movdqa 0x290(%rcx),%xmm15\n\t"
"pushq 0xf8(%rcx)\n\t" /* context.Rip */
"jmp *%r10" )
/***********************************************************************
* call_raise_user_exception_dispatcher
@ -2012,85 +1962,38 @@ void WINAPI call_raise_user_exception_dispatcher( NTSTATUS (WINAPI *dispatcher)(
/***********************************************************************
* call_user_exception_dispatcher
*/
struct stack_layout * WINAPI setup_user_exception_dispatcher_stack( EXCEPTION_RECORD *rec, CONTEXT *context,
NTSTATUS (WINAPI *dispatcher)(EXCEPTION_RECORD*,CONTEXT*),
struct stack_layout *stack ) DECLSPEC_HIDDEN;
struct stack_layout * WINAPI setup_user_exception_dispatcher_stack( EXCEPTION_RECORD *rec, CONTEXT *context,
NTSTATUS (WINAPI *dispatcher)(EXCEPTION_RECORD*,CONTEXT*),
struct stack_layout *stack )
NTSTATUS WINAPI call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context,
NTSTATUS (WINAPI *dispatcher)(EXCEPTION_RECORD*,CONTEXT*) )
{
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
struct stack_layout *stack;
ULONG64 rsp;
NTSTATUS status = NtSetContextThread( GetCurrentThread(), context );
if (status) return status;
rsp = (context->Rsp - 0x20) & ~15;
stack = (struct stack_layout *)rsp - 1;
if ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE)
{
CONTEXT_EX *xctx = (CONTEXT_EX *)context + 1;
XSTATE *xs, *src_xs, xs_buf;
src_xs = xstate_from_context(context);
if ((CONTEXT *)src_xs >= &stack->context + 1 || src_xs + 1 <= (XSTATE *)&stack->context)
{
xs = src_xs;
}
else
{
xs = &xs_buf;
memcpy(xs, src_xs, sizeof(*xs));
}
memmove(&stack->context, context, sizeof(*context) + sizeof(*xctx));
assert(!((ULONG_PTR)stack->xstate & 63));
context_init_xstate(&stack->context, stack->xstate);
memcpy(stack->xstate, xs, sizeof(*xs));
rsp = (rsp - sizeof(XSTATE)) & ~63;
stack = (struct stack_layout *)rsp - 1;
assert( !((ULONG_PTR)stack->xstate & 63) );
context_init_xstate( &stack->context, stack->xstate );
memcpy( stack->xstate, &frame->xstate, sizeof(frame->xstate) );
}
else
{
memmove(&stack->context, context, sizeof(*context));
}
memcpy(&stack->rec, rec, sizeof(*rec));
memmove( &stack->context, context, sizeof(*context) );
stack->rec = *rec;
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
if (stack->rec.ExceptionCode == EXCEPTION_BREAKPOINT) stack->context.Rip--;
return stack;
frame->rbp = context->Rbp;
frame->rsp = (ULONG64)stack;
frame->rip = (ULONG64)dispatcher;
frame->restore_flags |= CONTEXT_CONTROL;
return status;
}
__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
"movq 0x98(%rdx),%r9\n\t" /* context->Rsp */
"subq $0x20,%r9\n\t" /* Unwind registers save space */
"andq $~0xf,%r9\n\t"
"btl $6,0x30(%rdx)\n\t" /* context->ContextFlags, CONTEXT_XSTATE bit. */
"jnc 1f\n\t"
"subq $0x140,%r9\n\t" /* sizeof(XSTATE) */
"andq $~63,%r9\n"
"1:\tsubq $0x590,%r9\n\t" /* sizeof(struct stack_layout) */
"cmpq %rsp,%r9\n\t"
"cmovbq %r9,%rsp\n\t"
"pushq %r8\n\t"
"subq $0x20,%rsp\n\t"
"call " __ASM_NAME("setup_user_exception_dispatcher_stack") "\n\t"
"addq $0x20,%rsp\n\t"
"popq %r8\n\t"
"mov %rax,%rcx\n\t"
"movq 0xa0(%rcx),%rbp\n\t"
"movq 0x90(%rcx),%rbx\n\t"
"movq 0xa8(%rcx),%rsi\n\t"
"movq 0xb0(%rcx),%rdi\n\t"
"movq 0xd8(%rcx),%r12\n\t"
"movq 0xe0(%rcx),%r13\n\t"
"movq 0xe8(%rcx),%r14\n\t"
"movq 0xf0(%rcx),%r15\n\t"
"movdqa 0x200(%rcx),%xmm6\n\t"
"movdqa 0x210(%rcx),%xmm7\n\t"
"movdqa 0x220(%rcx),%xmm8\n\t"
"movdqa 0x230(%rcx),%xmm9\n\t"
"movdqa 0x240(%rcx),%xmm10\n\t"
"movdqa 0x250(%rcx),%xmm11\n\t"
"movdqa 0x260(%rcx),%xmm12\n\t"
"movdqa 0x270(%rcx),%xmm13\n\t"
"movdqa 0x280(%rcx),%xmm14\n\t"
"movdqa 0x290(%rcx),%xmm15\n\t"
"mov %rcx,%rsp\n\t"
"movq %gs:0x30,%rax\n\t"
"movq $0,0x328(%rax)\n\t" /* amd64_thread_data()->syscall_frame */
"jmpq *%r8")
/***********************************************************************
* is_privileged_instr
@ -2222,7 +2125,7 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec,
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
DWORD i;
if (!frame) return FALSE;
if (!is_inside_syscall( sigcontext )) return FALSE;
TRACE( "code=%x flags=%x addr=%p ip=%lx tid=%04x\n",
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
@ -2262,8 +2165,7 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec,
R15_sig(sigcontext) = frame->r15;
RSP_sig(sigcontext) = frame->rsp;
RIP_sig(sigcontext) = frame->rip;
if (fpu) *fpu = get_syscall_xsave( frame )->xsave;
amd64_thread_data()->syscall_frame = NULL;
if (fpu) *fpu = frame->xsave;
}
return TRUE;
}
@ -2467,9 +2369,9 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *ucontext )
*/
static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext )
{
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
struct xcontext context;
if (frame)
if (is_inside_syscall( ucontext ))
{
DECLSPEC_ALIGN(64) XSTATE xs;
context.c.ContextFlags = CONTEXT_FULL;
@ -2628,7 +2530,9 @@ void signal_init_thread( TEB *teb )
void signal_init_process(void)
{
struct sigaction sig_act;
void *ptr;
void *ptr, *kernel_stack = (char *)ntdll_get_thread_data()->kernel_stack + kernel_stack_size;
amd64_thread_data()->syscall_frame = (struct syscall_frame *)kernel_stack - 1;
/* sneak in a syscall dispatcher pointer at a fixed address (7ffe1000) */
ptr = (char *)user_shared_data + page_size;
@ -2733,8 +2637,14 @@ __ASM_GLOBAL_FUNC( signal_start_thread,
/* store exit frame */
"movq %gs:0x30,%rax\n\t"
"movq %rsp,0x320(%rax)\n\t" /* amd64_thread_data()->exit_frame */
/* set syscall frame */
"cmpq $0,0x328(%rax)\n\t" /* amd64_thread_data()->syscall_frame */
"jnz 1f\n\t"
"leaq -0x400(%rsp),%r10\n\t" /* sizeof(struct syscall_frame) */
"andq $~63,%r10\n\t"
"movq %r10,0x328(%rax)\n" /* amd64_thread_data()->syscall_frame */
/* switch to thread stack */
"movq 8(%rax),%rax\n\t" /* NtCurrentTeb()->Tib.StackBase */
"1:\tmovq 8(%rax),%rax\n\t" /* NtCurrentTeb()->Tib.StackBase */
"movq %rcx,%rbx\n\t" /* thunk */
"leaq -0x1000(%rax),%rsp\n\t"
/* attach dlls */
@ -2756,7 +2666,7 @@ __ASM_GLOBAL_FUNC( signal_exit_thread,
"jnz 1f\n\t"
"jmp *%rsi\n"
/* switch to exit frame stack */
"1:\tmovq $0,0x330(%rax)\n\t"
"1:\tmovq $0,0x320(%rax)\n\t"
"movq %rdx,%rsp\n\t"
__ASM_CFI(".cfi_adjust_cfa_offset 56\n\t")
__ASM_CFI(".cfi_rel_offset %rbp,48\n\t")

View File

@ -1551,83 +1551,71 @@ static void output_syscall_dispatcher(void)
output( "\tjmp 5b\n" );
break;
case CPU_x86_64:
output( "\tpushq %%rbp\n" );
output_cfi( ".cfi_adjust_cfa_offset 8" );
output_cfi( ".cfi_rel_offset %%rbp,0" );
output( "\tmovq %%rsp,%%rbp\n" );
output_cfi( ".cfi_def_cfa_register %%rbp" );
output( "\tleaq -0x10(%%rbp),%%rsp\n" );
output( "\tmovq %%gs:0x30,%%rcx\n" );
output( "\tmovq 0x328(%%rcx),%%rcx\n" ); /* amd64_thread_data()->syscall_frame */
output( "\tmovq %%rax,0x00(%%rcx)\n" );
output( "\tmovq %%rbx,0x08(%%rcx)\n" );
output( "\tmovq %%rdx,0x18(%%rcx)\n" );
output( "\tmovq %%rsi,0x20(%%rcx)\n" );
output( "\tmovq %%rdi,0x28(%%rcx)\n" );
output( "\tmovq %%r12,0x50(%%rcx)\n" );
output( "\tmovq %%r13,0x58(%%rcx)\n" );
output( "\tmovq %%r14,0x60(%%rcx)\n" );
output( "\tmovq %%r15,0x68(%%rcx)\n" );
output( "\tpopq 0x70(%%rcx)\n" ); /* frame->rip */
output( "\tmovw %%cs,0x78(%%rcx)\n" );
output( "\tmovw %%ds,0x7a(%%rcx)\n" );
output( "\tmovw %%es,0x7c(%%rcx)\n" );
output( "\tmovw %%fs,0x7e(%%rcx)\n" );
output( "\tmovq %%rsp,0x88(%%rcx)\n" );
output( "\tmovw %%ss,0x90(%%rcx)\n" );
output( "\tmovw %%gs,0x92(%%rcx)\n" );
output( "\tmovl $0,0x94(%%rcx)\n" ); /* frame->restore_flags */
output( "\tmovq %%rbp,0x98(%%rcx)\n" );
output( "\tpushfq\n" );
output( "\tsubq $0x3c0,%%rsp\n" );
output( "\tandq $~63,%%rsp\n" );
output( "\tmovq %%rbx,-0x90(%%rbp)\n" );
output_cfi( ".cfi_rel_offset %%rbx,-144" );
output( "\tmovq %%rsi,-0x78(%%rbp)\n" );
output_cfi( ".cfi_rel_offset %%rsi,-120" );
output( "\tmovq %%rdi,-0x70(%%rbp)\n" );
output_cfi( ".cfi_rel_offset %%rdi,-112" );
output( "\tmovq %%r12,-0x48(%%rbp)\n" );
output_cfi( ".cfi_rel_offset %%r12,-72" );
output( "\tmovq %%r13,-0x40(%%rbp)\n" );
output( "\tmovq %%r14,-0x38(%%rbp)\n" );
output( "\tmovq %%r15,-0x30(%%rbp)\n" );
output( "\tpopq 0x80(%%rcx)\n" );
/* Legends of Runeterra hooks the first system call return instruction, and
* depends on us returning to it. Adjust the return address accordingly. */
output( "\tsubq $0xb,0x8(%%rbp)\n" );
output( "\tmovq 0x8(%%rbp),%%rbx\n" );
output( "\tmovq %%rbx,-0x28(%%rbp)\n" );
output( "\tleaq 0x10(%%rbp),%%rbx\n" );
output( "\tmovq %%rbx,-0x10(%%rbp)\n" );
output( "\tmovw %%cs,-0x20(%%rbp)\n" );
output( "\tmovw %%ds,-0x1e(%%rbp)\n" );
output( "\tmovw %%es,-0x1c(%%rbp)\n" );
output( "\tmovw %%fs,-0x1a(%%rbp)\n" );
output( "\tmovw %%ss,-0x8(%%rbp)\n" );
output( "\tmovw %%gs,-0x6(%%rbp)\n" );
output( "\tmovl $0,-0x4(%%rbp)\n" );
output( "\tmovq %%rsp,%%r12\n" );
output( "\tmovq %%rax,%%r13\n" );
output( "\tsubq $0xb,0x70(%%rcx)\n" );
output( "\tmovl %s(%%rip),%%r14d\n", asm_name("__wine_syscall_flags") );
output( "\ttestl $3,%%r14d\n" ); /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */
output( "\tjz 2f\n" );
output( "\tmovl $7,%%eax\n" );
output( "\tmovq %%rdx,%%rsi\n" );
output( "\txorq %%rdx,%%rdx\n" );
output( "\tmovq %%rdx,0x200(%%r12)\n" );
output( "\tmovq %%rdx,0x208(%%r12)\n" );
output( "\tmovq %%rdx,0x210(%%r12)\n" );
output( "\txorl %%edx,%%edx\n" );
output( "\tmovq %%rdx,0x2c0(%%rcx)\n" );
output( "\tmovq %%rdx,0x2c8(%%rcx)\n" );
output( "\tmovq %%rdx,0x2d0(%%rcx)\n" );
output( "\ttestl $2,%%r14d\n" ); /* SYSCALL_HAVE_XSAVEC */
output( "\tjz 1f\n" );
output( "\tmovq %%rdx,0x218(%%r12)\n" );
output( "\tmovq %%rdx,0x220(%%r12)\n" );
output( "\tmovq %%rdx,0x228(%%r12)\n" );
output( "\tmovq %%rdx,0x230(%%r12)\n" );
output( "\tmovq %%rdx,0x238(%%r12)\n" );
output( "\txsavec64 (%%r12)\n" );
output( "\tmovq %%rsi,%%rdx\n" );
output( "\tmovq %%rdx,0x2d8(%%rcx)\n" );
output( "\tmovq %%rdx,0x2e0(%%rcx)\n" );
output( "\tmovq %%rdx,0x2e8(%%rcx)\n" );
output( "\tmovq %%rdx,0x2f0(%%rcx)\n" );
output( "\tmovq %%rdx,0x2f8(%%rcx)\n" );
output( "\txsavec64 0xc0(%%rcx)\n" );
output( "\tjmp 3f\n" );
output( "1:\txsave64 (%%r12)\n" );
output( "\tmovq %%rsi,%%rdx\n" );
output( "1:\txsave64 0xc0(%%rcx)\n" );
output( "\tjmp 3f\n" );
output( "2:\tfxsave64 (%%r12)\n" );
output( "3:\tmovq %%gs:0x30,%%rcx\n" );
output( "\tleaq -0x98(%%rbp),%%rbx\n" );
output( "\tmovq %%rbx,0x328(%%rcx)\n" ); /* amd64_thread_data()->syscall_frame */
output( "\tmovq %%r13,%%rbx\n" );
output( "2:\tfxsave64 0xc0(%%rcx)\n" );
output( "3:\tleaq 0x98(%%rcx),%%rbp\n" );
output( "\tleaq 0x28(%%rsp),%%rsi\n" ); /* first argument */
output( "\tmovq %%rcx,%%rsp\n" );
output( "\tmovq 0x00(%%rcx),%%rax\n" );
output( "\tmovq 0x18(%%rcx),%%rdx\n" );
output( "\tmovl %%eax,%%ebx\n" );
output( "\tshrl $8,%%ebx\n" );
output( "\tandl $0x30,%%ebx\n" ); /* syscall table number */
output( "\tleaq %s(%%rip),%%rcx\n", asm_name("KeServiceDescriptorTable") );
output( "\tleaq (%%rcx,%%rbx,2),%%rbx\n" );
output( "\tandq $0xfff,%%r13\n" ); /* syscall number */
output( "\tcmpq 16(%%rbx),%%r13\n" ); /* table->ServiceLimit */
output( "\tandl $0xfff,%%eax\n" ); /* syscall number */
output( "\tcmpq 16(%%rbx),%%rax\n" ); /* table->ServiceLimit */
output( "\tjae 5f\n" );
output( "\tmovq 24(%%rbx),%%rcx\n" ); /* table->ArgumentTable */
output( "\tmovzbl (%%rcx,%%r13),%%ecx\n" );
output( "\tmovzbl (%%rcx,%%rax),%%ecx\n" );
output( "\tsubq $0x20,%%rcx\n" );
output( "\tjbe 1f\n" );
output( "\tsubq %%rcx,%%rsp\n" );
output( "\tshrq $3,%%rcx\n" );
output( "\tleaq 0x38(%%rbp),%%rsi\n" );
output( "\tandq $~15,%%rsp\n\t" );
output( "\tmovq %%rsp,%%rdi\n" );
output( "\tcld\n" );
@ -1635,55 +1623,44 @@ static void output_syscall_dispatcher(void)
output( "1:\tmovq %%r10,%%rcx\n" );
output( "\tsubq $0x20,%%rsp\n" );
output( "\tmovq (%%rbx),%%r10\n" ); /* table->ServiceTable */
output( "\tcallq *(%%r10,%%r13,8)\n" );
output( "2:\tmovq %%gs:0x30,%%rcx\n" );
output( "\tmovq $0,0x328(%%rcx)\n" );
output( "\tmovl -4(%%rbp),%%ecx\n" ); /* frame->restore_flags */
output( "\ttestl $0x48,%%ecx\n" ); /* CONTEXT_FLOATING_POINT | CONTEXT_XSTATE */
output( "\tcallq *(%%r10,%%rax,8)\n" );
output( "2:\tleaq -0x98(%%rbp),%%rcx\n" );
output( "\tmovl 0x94(%%rcx),%%edx\n" ); /* frame->restore_flags */
output( "\ttestl $0x48,%%edx\n" ); /* CONTEXT_FLOATING_POINT | CONTEXT_XSTATE */
output( "\tjz 4f\n" );
output( "\ttestl $3,%%r14d\n" ); /* SYSCALL_HAVE_XSAVE | SYSCALL_HAVE_XSAVEC */
output( "\tjz 3f\n" );
output( "\tmovq %%rax,%%r11\n" );
output( "\tmovl $7,%%eax\n" );
output( "\txorq %%rdx,%%rdx\n" );
output( "\txrstor64 (%%r12)\n" );
output( "\txorl %%edx,%%edx\n" );
output( "\txrstor64 0xc0(%%rcx)\n" );
output( "\tmovq %%r11,%%rax\n" );
output( "\tmovl 0x94(%%rcx),%%edx\n" );
output( "\tjmp 4f\n" );
output( "3:\tfxrstor64 (%%r12)\n" );
output( "4:\tmovq -0x30(%%rbp),%%r15\n" );
output( "\tmovq -0x38(%%rbp),%%r14\n" );
output( "\tmovq -0x40(%%rbp),%%r13\n" );
output( "\tmovq -0x48(%%rbp),%%r12\n" );
output_cfi( ".cfi_same_value %%r12" );
output( "\tmovq -0x70(%%rbp),%%rdi\n" );
output_cfi( ".cfi_same_value %%rdi" );
output( "\tmovq -0x78(%%rbp),%%rsi\n" );
output_cfi( ".cfi_same_value %%rsi" );
output( "\tmovq -0x90(%%rbp),%%rbx\n" );
output_cfi( ".cfi_same_value %%rbx" );
output( "\ttestl $0x3,%%ecx\n" ); /* CONTEXT_CONTROL | CONTEXT_INTEGER */
output( "3:\tfxrstor64 0xc0(%%rcx)\n" );
output( "4:\tmovq 0x98(%%rcx),%%rbp\n" );
output( "\tmovq 0x68(%%rcx),%%r15\n" );
output( "\tmovq 0x60(%%rcx),%%r14\n" );
output( "\tmovq 0x58(%%rcx),%%r13\n" );
output( "\tmovq 0x50(%%rcx),%%r12\n" );
output( "\tmovq 0x28(%%rcx),%%rdi\n" );
output( "\tmovq 0x20(%%rcx),%%rsi\n" );
output( "\tmovq 0x08(%%rcx),%%rbx\n" );
output( "\ttestl $0x3,%%edx\n" ); /* CONTEXT_CONTROL | CONTEXT_INTEGER */
output( "\tjnz 1f\n" );
output( "\tmovq -0x28(%%rbp),%%rcx\n" ); /* frame->rip */
output( "\tleaq -0x10(%%rbp),%%rsp\n" ); /* frame->rsp */
output( "\tmovq (%%rbp),%%rbp\n" );
output_cfi( ".cfi_same_value %%rbp" );
output( "\tpopq %%rsp\n" );
output( "\tjmpq *%%rcx\n" );
output( "1:\ttestl $0x2,%%ecx\n" ); /* CONTEXT_INTEGER */
output( "\tmovq 0x88(%%rcx),%%rsp\n" );
output( "\tjmpq *0x70(%%rcx)\n" ); /* frame->rip */
output( "1:\tleaq 0x70(%%rcx),%%rsp\n" );
output( "\ttestl $0x2,%%edx\n" ); /* CONTEXT_INTEGER */
output( "\tjz 1f\n" );
output( "\tmovq -0x98(%%rbp),%%rax\n" );
output( "\tmovq -0x88(%%rbp),%%rcx\n" );
output( "\tmovq -0x80(%%rbp),%%rdx\n" );
output( "\tmovq -0x68(%%rbp),%%r8\n" );
output( "\tmovq -0x60(%%rbp),%%r9\n" );
output( "\tmovq -0x58(%%rbp),%%r10\n" );
output( "\tmovq -0x50(%%rbp),%%r11\n" );
output( "1:\tleaq -0x28(%%rbp),%%rsp\n" );
output_cfi( ".cfi_def_cfa_register %%rsp" );
output_cfi( ".cfi_adjust_cfa_offset 40" );
output( "\tmovq (%%rbp),%%rbp\n" );
output_cfi( ".cfi_same_value %%rbp" );
output( "\tiretq\n" );
output( "\tmovq 0x00(%%rcx),%%rax\n" );
output( "\tmovq 0x18(%%rcx),%%rdx\n" );
output( "\tmovq 0x30(%%rcx),%%r8\n" );
output( "\tmovq 0x38(%%rcx),%%r9\n" );
output( "\tmovq 0x40(%%rcx),%%r10\n" );
output( "\tmovq 0x48(%%rcx),%%r11\n" );
output( "\tmovq 0x10(%%rcx),%%rcx\n" );
output( "1:\tiretq\n" );
output( "5:\tmovl $0x%x,%%eax\n", invalid_param );
output( "\tjmp 2b\n" );
break;