[PATCH 4/5] ntdll: Support AVX context in fault exceptions on Linux x86.

Paul Gofman pgofman at codeweavers.com
Thu Aug 20 18:42:14 CDT 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/tests/exception.c  |   2 +-
 dlls/ntdll/unix/signal_i386.c | 146 ++++++++++++++++++++++++----------
 2 files changed, 106 insertions(+), 42 deletions(-)

diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 1bbf9f4f912..860221366ce 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -5482,7 +5482,7 @@ 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_if(sizeof(void *) == 4)
+
     ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
             "Got unexpected ContextFlags %#x.\n", context->ContextFlags);
 
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c
index bc30dd34df8..791ffe90940 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -33,6 +33,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/types.h>
+#include <assert.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
@@ -56,6 +57,8 @@
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
+#include "winternl.h"
+#include "ddk/wdm.h"
 #include "wine/asm.h"
 #include "wine/exception.h"
 #include "unix_private.h"
@@ -122,6 +125,10 @@ typedef struct ucontext
 } ucontext_t;
 #endif /* HAVE_SYS_UCONTEXT_H */
 
+#ifndef FP_XSTATE_MAGIC1
+#define FP_XSTATE_MAGIC1 0x46505853
+#endif
+
 #define EAX_sig(context)     ((context)->uc_mcontext.gregs[REG_EAX])
 #define EBX_sig(context)     ((context)->uc_mcontext.gregs[REG_EBX])
 #define ECX_sig(context)     ((context)->uc_mcontext.gregs[REG_ECX])
@@ -145,6 +152,7 @@ typedef struct ucontext
 
 #define FPU_sig(context)     ((FLOATING_SAVE_AREA*)((context)->uc_mcontext.fpregs))
 #define FPUX_sig(context)    (FPU_sig(context) && !((context)->uc_mcontext.fpregs->status >> 16) ? (XSAVE_FORMAT *)(FPU_sig(context) + 1) : NULL)
+#define XState_sig(fpu)      (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL)
 
 #ifdef __ANDROID__
 /* custom signal restorer since we may have unmapped the one in vdso, and bionic doesn't check for that */
@@ -208,6 +216,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 
 #define FPU_sig(context)     NULL  /* FIXME */
 #define FPUX_sig(context)    NULL  /* FIXME */
+#define XState_sig(context)  NULL  /* FIXME */
 
 #elif defined (__OpenBSD__)
 
@@ -238,6 +247,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 
 #define FPU_sig(context)     NULL  /* FIXME */
 #define FPUX_sig(context)    NULL  /* FIXME */
+#define XState_sig(context)  NULL  /* FIXME */
 
 #define T_MCHK T_MACHK
 #define T_XMMFLT T_XFTRAP
@@ -283,6 +293,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 
 #define FPU_sig(context)     NULL  /* FIXME */
 #define FPUX_sig(context)    NULL  /* FIXME */
+#define XState_sig(context)  NULL  /* FIXME */
 
 #elif defined (__APPLE__)
 
@@ -310,6 +321,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 #define ERROR_sig(context)   ((context)->uc_mcontext->__es.__err)
 #define FPU_sig(context)     NULL
 #define FPUX_sig(context)    ((XSAVE_FORMAT *)&(context)->uc_mcontext->__fs.__fpu_fcw)
+#define XState_sig(context)  NULL  /* FIXME */
 #else
 #define EAX_sig(context)     ((context)->uc_mcontext->ss.eax)
 #define EBX_sig(context)     ((context)->uc_mcontext->ss.ebx)
@@ -331,6 +343,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 #define ERROR_sig(context)   ((context)->uc_mcontext->es.err)
 #define FPU_sig(context)     NULL
 #define FPUX_sig(context)    ((XSAVE_FORMAT *)&(context)->uc_mcontext->fs.fpu_fcw)
+#define XState_sig(context)  NULL  /* FIXME */
 #endif
 
 #elif defined(__NetBSD__)
@@ -361,6 +374,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 
 #define FPU_sig(context)     NULL
 #define FPUX_sig(context)    ((XSAVE_FORMAT *)&((context)->uc_mcontext.__fpregs))
+#define XState_sig(context)  NULL  /* FIXME */
 
 #define T_MCHK T_MCA
 #define T_XMMFLT T_XMM
@@ -393,6 +407,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
 
 #define FPU_sig(context)     ((FLOATING_SAVE_AREA *)&(context)->uc_mcontext.fpregs.fp_reg_set.fpchip_state)
 #define FPUX_sig(context)    NULL
+#define XState_sig(context)  NULL  /* FIXME */
 
 #else
 #error You must define the signal context functions for your platform
@@ -752,10 +767,11 @@ static void fpux_to_fpu( FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux )
  *
  * Build a context structure from the signal info.
  */
-static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext )
+static inline void save_context( struct xcontext *xcontext, const ucontext_t *sigcontext )
 {
     FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
     XSAVE_FORMAT *fpux = FPUX_sig(sigcontext);
+    CONTEXT *context = &xcontext->c;
 
     memset(context, 0, sizeof(*context));
     context->ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
@@ -792,6 +808,11 @@ static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext
         context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
         memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) );
         if (!fpu) fpux_to_fpu( &context->FloatSave, fpux );
+        xcontext->xstate = XState_sig(fpux);
+    }
+    else
+    {
+        xcontext->xstate = NULL;
     }
     if (!fpu && !fpux) save_fpu( context );
 }
@@ -802,10 +823,11 @@ static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext
  *
  * Restore the signal info from the context.
  */
-static inline void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
+static inline void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext )
 {
     FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
     XSAVE_FORMAT *fpux = FPUX_sig(sigcontext);
+    const CONTEXT *context = &xcontext->c;
 
     x86_thread_data()->dr0 = context->Dr0;
     x86_thread_data()->dr1 = context->Dr1;
@@ -831,7 +853,18 @@ static inline void restore_context( const CONTEXT *context, ucontext_t *sigconte
     SS_sig(sigcontext)  = context->SegSs;
 
     if (fpu) *fpu = context->FloatSave;
-    if (fpux) memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) );
+    if (fpux)
+    {
+        XSTATE *src_xs, *dst_xs;
+
+        memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) );
+
+        if ((dst_xs = XState_sig(fpux)) && (src_xs = xstate_from_context( context )))
+        {
+            memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
+            dst_xs->Mask |= src_xs->Mask;
+        }
+    }
     if (!fpu && !fpux) restore_fpu( context );
 }
 
@@ -1442,48 +1475,76 @@ static BOOL check_atl_thunk( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONT
  *
  * Setup the exception record and context on the thread stack.
  */
-static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
+static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
 {
     void *stack = init_handler( sigcontext );
 
     rec->ExceptionAddress = (void *)EIP_sig( sigcontext );
-    save_context( context, sigcontext );
+    save_context( xcontext, sigcontext );
     return stack;
 }
 
-
 /***********************************************************************
  *           setup_raise_exception
  *
  * Change context to setup a call to a raise exception function.
  */
 static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
-                                   EXCEPTION_RECORD *rec, CONTEXT *context )
+                                   EXCEPTION_RECORD *rec, struct xcontext *xcontext )
 {
-    struct
+    CONTEXT *context = &xcontext->c;
+    size_t stack_size;
+
+    struct stack_layout
     {
         EXCEPTION_RECORD *rec_ptr;       /* first arg for KiUserExceptionDispatcher */
         CONTEXT          *context_ptr;   /* second arg for KiUserExceptionDispatcher */
         CONTEXT           context;
+        CONTEXT_EX        context_ex;
         EXCEPTION_RECORD  rec;
         DWORD             ebp;
         DWORD             eip;
+        char              xstate[0];
     } *stack;
 
+C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) );
+
     NTSTATUS 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->Eip--;
 
-    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) );
+        }
+    }
+
     stack->rec_ptr      = &stack->rec;
     stack->context_ptr  = &stack->context;
     ESP_sig(sigcontext) = (DWORD)stack;
@@ -1506,9 +1567,10 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
  */
 static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
 {
-    CONTEXT context;
-    void *stack = setup_exception_record( sigcontext, rec, &context );
-    setup_raise_exception( sigcontext, stack, rec, &context );
+    struct xcontext xcontext;
+    void *stack = setup_exception_record( sigcontext, rec, &xcontext );
+
+    setup_raise_exception( sigcontext, stack, rec, &xcontext );
 }
 
 
@@ -1623,8 +1685,10 @@ static inline DWORD get_fpu_code( const CONTEXT *context )
  * Handle an interrupt.
  */
 static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, void *stack,
-                              EXCEPTION_RECORD *rec, CONTEXT *context )
+                              EXCEPTION_RECORD *rec, struct xcontext *xcontext )
 {
+    CONTEXT *context = &xcontext->c;
+
     switch(interrupt)
     {
     case 0x2d:
@@ -1649,7 +1713,7 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
         rec->ExceptionInformation[0] = context->Eax;
         rec->ExceptionInformation[1] = context->Ecx;
         rec->ExceptionInformation[2] = context->Edx;
-        setup_raise_exception( sigcontext, stack, rec, context );
+        setup_raise_exception( sigcontext, stack, rec, xcontext );
         return TRUE;
     default:
         return FALSE;
@@ -1665,9 +1729,9 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
 static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
 {
     EXCEPTION_RECORD rec = { 0 };
-    CONTEXT context;
+    struct xcontext xcontext;
     ucontext_t *ucontext = sigcontext;
-    void *stack = setup_exception_record( sigcontext, &rec, &context );
+    void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
 
     switch (TRAP_sig(ucontext))
     {
@@ -1687,8 +1751,8 @@ 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 & 7) == 2 && handle_interrupt( err >> 3, ucontext, stack, &rec, &context )) return;
+            if (!err && (rec.ExceptionCode = is_privileged_instr( &xcontext.c ))) break;
+            if ((err & 7) == 2 && handle_interrupt( err >> 3, ucontext, stack, &rec, &xcontext )) return;
             rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
             rec.NumberParameters = 2;
             rec.ExceptionInformation[0] = 0;
@@ -1697,7 +1761,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
             else
             {
                 rec.ExceptionInformation[1] = 0xffffffff;
-                if (check_invalid_gs( ucontext, &context )) return;
+                if (check_invalid_gs( ucontext, &xcontext.c )) return;
             }
         }
         break;
@@ -1714,7 +1778,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
             NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
                                        &flags, sizeof(flags), NULL );
             if (!(flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) &&
-                check_atl_thunk( ucontext, &rec, &context ))
+                check_atl_thunk( ucontext, &rec, &xcontext.c ))
                 return;
 
             /* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
@@ -1723,7 +1787,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         break;
     case TRAP_x86_ALIGNFLT:  /* Alignment check exception */
         /* FIXME: pass through exception handler first? */
-        if (context.EFlags & 0x00040000)
+        if (xcontext.c.EFlags & 0x00040000)
         {
             EFL_sig(ucontext) &= ~0x00040000;  /* disable AC flag */
             return;
@@ -1742,7 +1806,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
         break;
     }
-    setup_raise_exception( ucontext, stack, &rec, &context );
+    setup_raise_exception( ucontext, stack, &rec, &xcontext );
 }
 
 
@@ -1754,9 +1818,9 @@ 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 xcontext;
     ucontext_t *ucontext = sigcontext;
-    void *stack = setup_exception_record( sigcontext, &rec, &context );
+    void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
 
     switch (TRAP_sig(ucontext))
     {
@@ -1765,15 +1829,15 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         /* when single stepping can't tell whether this is a hw bp or a
          * single step interrupt. try to avoid as much overhead as possible
          * and only do a server call if there is any hw bp enabled. */
-        if (!(context.EFlags & 0x100) || (context.Dr7 & 0xff))
+        if (!(xcontext.c.EFlags & 0x100) || (xcontext.c.Dr7 & 0xff))
         {
             /* (possible) hardware breakpoint, fetch the debug registers */
-            DWORD saved_flags = context.ContextFlags;
-            context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
-            NtGetContextThread( GetCurrentThread(), &context );
-            context.ContextFlags |= saved_flags;  /* restore flags */
+            DWORD saved_flags = xcontext.c.ContextFlags;
+            xcontext.c.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+            NtGetContextThread( GetCurrentThread(), &xcontext.c );
+            xcontext.c.ContextFlags |= saved_flags;  /* restore flags */
         }
-        context.EFlags &= ~0x100;  /* clear single-step flag */
+        xcontext.c.EFlags &= ~0x100;  /* clear single-step flag */
         break;
     case TRAP_x86_BPTFLT:   /* Breakpoint exception */
         rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1;  /* back up over the int3 instruction */
@@ -1786,7 +1850,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         rec.ExceptionInformation[2] = 0; /* FIXME */
         break;
     }
-    setup_raise_exception( sigcontext, stack, &rec, &context );
+    setup_raise_exception( sigcontext, stack, &rec, &xcontext );
 }
 
 
@@ -1798,9 +1862,9 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
 static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
 {
     EXCEPTION_RECORD rec = { 0 };
-    CONTEXT context;
+    struct xcontext xcontext;
     ucontext_t *ucontext = sigcontext;
-    void *stack = setup_exception_record( sigcontext, &rec, &context );
+    void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
 
     switch (TRAP_sig(ucontext))
     {
@@ -1811,8 +1875,8 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
         break;
     case TRAP_x86_ARITHTRAP:  /* Floating point exception */
-        rec.ExceptionCode = get_fpu_code( &context );
-        rec.ExceptionAddress = (void *)context.FloatSave.ErrorOffset;
+        rec.ExceptionCode = get_fpu_code( &xcontext.c );
+        rec.ExceptionAddress = (void *)xcontext.c.FloatSave.ErrorOffset;
         break;
     case TRAP_x86_CACHEFLT:  /* SIMD exception */
         /* TODO:
@@ -1832,7 +1896,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
         rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
         break;
     }
-    setup_raise_exception( sigcontext, stack, &rec, &context );
+    setup_raise_exception( sigcontext, stack, &rec, &xcontext );
 }
 
 
@@ -1880,12 +1944,12 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext )
  */
 static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
 {
-    CONTEXT context;
+    struct xcontext xcontext;
 
     init_handler( sigcontext );
-    save_context( &context, sigcontext );
-    wait_suspend( &context );
-    restore_context( &context, sigcontext );
+    save_context( &xcontext, sigcontext );
+    wait_suspend( &xcontext.c );
+    restore_context( &xcontext, sigcontext );
 }
 
 
-- 
2.26.2




More information about the wine-devel mailing list