ntdll: Support AVX context in fault exceptions on Linux x86_64.

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2020-08-21 02:42:12 +03:00 committed by Alexandre Julliard
parent 3f562e0b91
commit 16ed88a952
4 changed files with 136 additions and 21 deletions

View File

@ -428,6 +428,8 @@ static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_contex
NTSTATUS status;
context = *orig_context;
context.ContextFlags &= ~0x40; /* Clear xstate flag. */
dispatch.TargetIp = 0;
dispatch.ContextRecord = &context;
dispatch.HistoryTable = &table;

View File

@ -5444,7 +5444,8 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI
/* Since we got xstates enabled by OS this cpuid level should be supported. */
__cpuidex(regs, 0xd, 1);
compaction = regs[0] & 2;
todo_wine ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
todo_wine_if(sizeof(void *) == 4)
ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
"Got unexpected ContextFlags %#x.\n", context->ContextFlags);
if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE))

View File

@ -70,6 +70,7 @@
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "ddk/wdm.h"
#include "wine/exception.h"
#include "wine/list.h"
#include "wine/asm.h"
@ -86,6 +87,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh);
#include <asm/prctl.h>
static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_prctl, func, ptr ); }
#ifndef FP_XSTATE_MAGIC1
#define FP_XSTATE_MAGIC1 0x46505853
#endif
#define RAX_sig(context) ((context)->uc_mcontext.gregs[REG_RAX])
#define RBX_sig(context) ((context)->uc_mcontext.gregs[REG_RBX])
#define RCX_sig(context) ((context)->uc_mcontext.gregs[REG_RCX])
@ -110,6 +115,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.fpregs))
#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL)
#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
@ -140,6 +146,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.mc_fpstate))
#define XState_sig(context) NULL
#elif defined(__NetBSD__)
@ -170,6 +177,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.__fpregs))
#define XState_sig(context) NULL
#elif defined (__APPLE__)
@ -197,6 +205,7 @@ static inline int arch_prctl( int func, void *ptr ) { return syscall( __NR_arch_
#define TRAP_sig(context) ((context)->uc_mcontext->__es.__trapno)
#define ERROR_sig(context) ((context)->uc_mcontext->__es.__err)
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)&(context)->uc_mcontext->__fs.__fpu_fcw)
#define XState_sig(context) NULL
#else
#error You must define the signal context functions for your platform
@ -229,16 +238,21 @@ enum i386_trap_code
struct stack_layout
{
CONTEXT context;
ULONG64 unknown[4];
CONTEXT_EX context_ex;
EXCEPTION_RECORD rec;
ULONG64 rsi;
ULONG64 rdi;
ULONG64 rbp;
ULONG64 rip;
ULONG64 red_zone[16];
ULONG64 align;
char xstate[0]; /* If xstate is present it is allocated
* dynamically to provide 64 byte alignment. */
};
C_ASSERT( sizeof(struct stack_layout) == 0x630 ); /* Should match the size in call_user_exception_dispatcher(). */
C_ASSERT((offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)));
C_ASSERT( sizeof(XSTATE) == 0x140 );
C_ASSERT( sizeof(struct stack_layout) == 0x5b0 ); /* Should match the size in call_user_exception_dispatcher(). */
struct syscall_frame
{
@ -1400,8 +1414,10 @@ static inline void set_sigcontext( const CONTEXT *context, ucontext_t *sigcontex
*
* Set the register values from a sigcontext.
*/
static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
static void save_context( struct xcontext *xcontext, const ucontext_t *sigcontext )
{
CONTEXT *context = &xcontext->c;
context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_DEBUG_REGISTERS;
context->Rax = RAX_sig(sigcontext);
context->Rcx = RCX_sig(sigcontext);
@ -1450,6 +1466,11 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
context->ContextFlags |= CONTEXT_FLOATING_POINT;
context->u.FltSave = *FPU_sig(sigcontext);
context->MxCsr = context->u.FltSave.MxCsr;
xcontext->xstate = XState_sig(FPU_sig(sigcontext));
}
else
{
xcontext->xstate = NULL;
}
}
@ -1459,8 +1480,10 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
*
* Build a sigcontext from the register values.
*/
static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
static void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext )
{
const CONTEXT *context = &xcontext->c;
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
amd64_thread_data()->dr2 = context->Dr2;
@ -1805,7 +1828,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
return STATUS_SUCCESS;
}
extern void CDECL raise_func_trampoline( void *dispatcher );
__ASM_GLOBAL_FUNC( raise_func_trampoline,
@ -1814,10 +1836,12 @@ __ASM_GLOBAL_FUNC( raise_func_trampoline,
/***********************************************************************
* setup_raise_exception
*/
static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
{
void *stack_ptr = (void *)(RSP_sig(sigcontext) & ~15);
CONTEXT *context = &xcontext->c;
struct stack_layout *stack;
size_t stack_size;
NTSTATUS status;
if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP)
@ -1840,16 +1864,37 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
status = send_debug_event( rec, context, TRUE );
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
{
restore_context( context, sigcontext );
restore_context( xcontext, sigcontext );
return;
}
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Rip--;
stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec );
stack_size = sizeof(*stack);
if (xcontext->xstate)
{
stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr
- sizeof(XSTATE)) & ~(ULONG_PTR)63);
}
stack = virtual_setup_exception( stack_ptr, stack_size, rec );
stack->rec = *rec;
stack->context = *context;
if (xcontext->xstate)
{
XSTATE *dst_xs = (XSTATE *)stack->xstate;
assert( !((ULONG_PTR)dst_xs & 63) );
context_init_xstate( &stack->context, stack->xstate );
dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? 0x8000000000000004 : 0;
if (xcontext->xstate->Mask & 4)
{
dst_xs->Mask = 4;
memcpy( &dst_xs->YmmContext, &xcontext->xstate->YmmContext, sizeof(dst_xs->YmmContext) );
}
}
RIP_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline;
R8_sig(sigcontext) = (ULONG_PTR)pKiUserExceptionDispatcher;
RSP_sig(sigcontext) = (ULONG_PTR)stack;
@ -1867,7 +1912,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec
*/
static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
{
CONTEXT context;
struct xcontext context;
rec->ExceptionAddress = (void *)RIP_sig(sigcontext);
save_context( &context, sigcontext );
@ -1964,7 +2009,31 @@ void WINAPI do_call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *c
{
struct syscall_frame *frame = amd64_thread_data()->syscall_frame;
memmove(&stack->context, context, sizeof(*context));
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));
}
else
{
memmove(&stack->context, context, sizeof(*context));
}
memcpy(&stack->rec, rec, sizeof(*rec));
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
@ -1977,7 +2046,11 @@ void WINAPI do_call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *c
__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
"movq 0x98(%rdx),%r9\n\t" /* context->Rsp */
"andq $~0xf,%r9\n\t"
"subq $0x630,%r9\n\t" /* sizeof(struct stack_layout) */
"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 $0x5b0,%r9\n\t" /* sizeof(struct stack_layout) */
"cmpq %rsp,%r9\n\t"
"cmovbq %r9,%rsp\n\t"
"jmp " __ASM_NAME("do_call_user_exception_dispatcher") "\n\t")
@ -2069,8 +2142,10 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
*
* Handle an interrupt.
*/
static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
{
CONTEXT *context = &xcontext->c;
switch (ERROR_sig(sigcontext) >> 3)
{
case 0x2c:
@ -2095,11 +2170,10 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
default:
return FALSE;
}
setup_raise_exception( sigcontext, rec, context );
setup_raise_exception( sigcontext, rec, xcontext );
return TRUE;
}
/**********************************************************************
* segv_handler
*
@ -2108,7 +2182,7 @@ static inline BOOL handle_interrupt( ucontext_t *sigcontext, EXCEPTION_RECORD *r
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD rec = { 0 };
CONTEXT context;
struct xcontext context;
ucontext_t *ucontext = sigcontext;
rec.ExceptionAddress = (void *)RIP_sig(ucontext);
@ -2132,7 +2206,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
case TRAP_x86_PROTFLT: /* General protection fault */
{
WORD err = ERROR_sig(ucontext);
if (!err && (rec.ExceptionCode = is_privileged_instr( &context ))) break;
if (!err && (rec.ExceptionCode = is_privileged_instr( &context.c ))) break;
if ((err & 7) == 2 && handle_interrupt( ucontext, &rec, &context )) return;
rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
rec.NumberParameters = 2;
@ -2175,7 +2249,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD rec = { 0 };
CONTEXT context;
struct xcontext context;
ucontext_t *ucontext = sigcontext;
rec.ExceptionAddress = (void *)RIP_sig(ucontext);
@ -2294,10 +2368,10 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *ucontext )
*/
static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext )
{
CONTEXT context;
struct xcontext context;
save_context( &context, ucontext );
wait_suspend( &context );
wait_suspend( &context.c );
restore_context( &context, ucontext );
}

View File

@ -297,6 +297,44 @@ static inline void *get_signal_stack(void)
static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
#endif
#if defined(__i386__) || defined(__x86_64__)
struct xcontext
{
CONTEXT c;
XSTATE *xstate; /* points to xstate in sigcontext */
};
static inline XSTATE *xstate_from_context( const CONTEXT *context )
{
CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE)
return NULL;
return (XSTATE *)((char *)(context + 1) + xctx->XState.Offset);
}
static inline void context_init_xstate( CONTEXT *context, void *xstate_buffer )
{
CONTEXT_EX *xctx;
XSTATE *xs;
xctx = (CONTEXT_EX *)(context + 1);
xctx->Legacy.Length = sizeof(CONTEXT);
xctx->Legacy.Offset = -(LONG)sizeof(CONTEXT);
xctx->XState.Length = sizeof(XSTATE);
xctx->XState.Offset = xstate_buffer ? (((ULONG_PTR)xstate_buffer + 63) & ~63) - (ULONG_PTR)xctx
: (((ULONG_PTR)context + sizeof(CONTEXT) + sizeof(CONTEXT_EX) + 63) & ~63) - (ULONG_PTR)xctx;
xctx->All.Length = sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length;
xctx->All.Offset = -(LONG)sizeof(CONTEXT);
context->ContextFlags |= 0x40;
xs = xstate_from_context(context);
memset( xs, 0, offsetof(XSTATE, YmmContext) );
}
#endif
static inline size_t ntdll_wcslen( const WCHAR *str )
{
const WCHAR *s = str;