Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/signal_x86_64.c | 2 + dlls/ntdll/tests/exception.c | 3 +- dlls/ntdll/unix/signal_x86_64.c | 114 ++++++++++++++++++++++++++------ dlls/ntdll/unix/unix_private.h | 6 ++ dlls/ntdll/unixlib.h | 30 +++++++++ 5 files changed, 134 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index e1cf7f3e013..564b74b7be6 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -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; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 460f0e9a03e..cf704093fce 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5482,7 +5482,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)) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 638ad5cab76..370f1f36fd7 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -78,6 +78,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" @@ -94,6 +95,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]) @@ -118,6 +123,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__)
@@ -148,6 +154,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__)
@@ -178,6 +185,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__)
@@ -205,6 +213,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 @@ -237,16 +246,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 { @@ -1408,8 +1422,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); @@ -1458,6 +1474,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; } }
@@ -1467,8 +1488,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; @@ -1813,7 +1836,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, @@ -1822,10 +1844,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) @@ -1848,16 +1872,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; @@ -1875,7 +1920,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 ); @@ -1972,7 +2017,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 */ @@ -1985,7 +2054,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) */ + "bt $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\t" + "1:subq $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") @@ -2187,8 +2260,10 @@ static void install_bpf(struct sigaction *sig_act) * * 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: @@ -2213,11 +2288,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 * @@ -2226,7 +2300,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); @@ -2250,7 +2324,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; @@ -2293,7 +2367,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); @@ -2412,10 +2486,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 ); }
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index c9f8871fd26..68c60f78b7f 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -400,4 +400,10 @@ static inline int ntdll_wcsnicmp( const WCHAR *str1, const WCHAR *str2, int n ) #define towupper(c) ntdll_towupper(c) #define towlower(c) ntdll_towlower(c)
+struct xcontext +{ + CONTEXT c; + XSTATE *xstate; /* Points to xstate in sigcontext. */ +}; + #endif /* __NTDLL_UNIX_PRIVATE_H */ diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index 2f57873671c..70b6def530a 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -129,4 +129,34 @@ struct unix_funcs const char *function ); };
+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 /* __NTDLL_UNIXLIB_H */