Sebastian Lackner : ntoskrnl: Add initial code to emulate memory access to USER_SHARED_DATA on x86_64.

Alexandre Julliard julliard at wine.codeweavers.com
Fri Jun 19 07:19:14 CDT 2015


Module: wine
Branch: master
Commit: 0b06d4c4a36e34b314c1886a30423aa9faa3faa2
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=0b06d4c4a36e34b314c1886a30423aa9faa3faa2

Author: Sebastian Lackner <sebastian at fds-team.de>
Date:   Fri Jun 19 03:58:41 2015 +0200

ntoskrnl: Add initial code to emulate memory access to USER_SHARED_DATA on x86_64.

---

 dlls/ntoskrnl.exe/instr.c    | 263 ++++++++++++++++++++++++++++++++++++++++++-
 dlls/ntoskrnl.exe/ntoskrnl.c |   2 +-
 2 files changed, 261 insertions(+), 4 deletions(-)

diff --git a/dlls/ntoskrnl.exe/instr.c b/dlls/ntoskrnl.exe/instr.c
index 45021c6..5a469cf 100644
--- a/dlls/ntoskrnl.exe/instr.c
+++ b/dlls/ntoskrnl.exe/instr.c
@@ -4,6 +4,7 @@
  * Copyright 1995 Alexandre Julliard
  * Copyright 2005 Ivan Leo Puoti
  * Copyright 2005 Laurent Pinchart
+ * Copyright 2014-2015 Sebastian Lackner
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -23,17 +24,19 @@
 #include "config.h"
 #include "wine/port.h"
 
-#ifdef __i386__
-
 #include <stdarg.h>
 
 #include "windef.h"
 #include "winbase.h"
 #include "winternl.h"
+#define WIN32_NO_STATUS
+#include "ddk/wdm.h"
 #include "excpt.h"
 #include "wine/debug.h"
 #include "wine/exception.h"
 
+#ifdef __i386__
+
 WINE_DEFAULT_DEBUG_CHANNEL(int);
 
 #include "pshpack1.h"
@@ -475,4 +478,258 @@ LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs )
     return EXCEPTION_CONTINUE_SEARCH;
 }
 
-#endif  /* __i386__ */
+#elif defined(__x86_64__)  /* __i386__ */
+
+WINE_DEFAULT_DEBUG_CHANNEL(int);
+
+#define REX_B   1
+#define REX_X   2
+#define REX_R   4
+#define REX_W   8
+
+#define REGMODRM_MOD( regmodrm, rex )   ((regmodrm) >> 6)
+#define REGMODRM_REG( regmodrm, rex )   (((regmodrm) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0)
+#define REGMODRM_RM( regmodrm, rex )    (((regmodrm) & 7) | (((rex) & REX_B) ? 8 : 0))
+
+#define SIB_SS( sib, rex )      ((sib) >> 6)
+#define SIB_INDEX( sib, rex )   (((sib) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0)
+#define SIB_BASE( sib, rex )    (((sib) & 7) | (((rex) & REX_B) ? 8 : 0))
+
+/* keep in sync with dlls/ntdll/thread.c:thread_init */
+static const BYTE *wine_user_shared_data = (BYTE *)0x7ffe0000;
+static const BYTE *user_shared_data      = (BYTE *)0xfffff78000000000;
+
+static inline DWORD64 *get_int_reg( CONTEXT *context, int index )
+{
+    return &context->Rax + index; /* index should be in range 0 .. 15 */
+}
+
+static inline int get_op_size( int long_op, int rex )
+{
+    if (rex & REX_W)
+        return sizeof(DWORD64);
+    else if (long_op)
+        return sizeof(DWORD);
+    else
+        return sizeof(WORD);
+}
+
+/* store an operand into a register */
+static void store_reg_word( CONTEXT *context, BYTE regmodrm, const BYTE *addr, int long_op, int rex )
+{
+    int index = REGMODRM_REG( regmodrm, rex );
+    BYTE *reg = (BYTE *)get_int_reg( context, index );
+    memcpy( reg, addr, get_op_size( long_op, rex ) );
+}
+
+/* store an operand into a byte register */
+static void store_reg_byte( CONTEXT *context, BYTE regmodrm, const BYTE *addr, int rex )
+{
+    int index = REGMODRM_REG( regmodrm, rex );
+    BYTE *reg = (BYTE *)get_int_reg( context, index );
+    if (!rex && index >= 4 && index < 8) reg -= (4 * sizeof(DWORD64) - 1); /* special case: ah, ch, dh, bh */
+    *reg = *addr;
+}
+
+/***********************************************************************
+ *           INSTR_GetOperandAddr
+ *
+ * Return the address of an instruction operand (from the mod/rm byte).
+ */
+static BYTE *INSTR_GetOperandAddr( CONTEXT *context, BYTE *instr,
+                                   int long_addr, int rex, int segprefix, int *len )
+{
+    int mod, rm, ss = 0, off, have_sib = 0;
+    DWORD64 base = 0, index = 0;
+
+#define GET_VAL( val, type ) \
+    { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
+
+    *len = 0;
+    GET_VAL( &mod, BYTE );
+    rm  = REGMODRM_RM( mod, rex );
+    mod = REGMODRM_MOD( mod, rex );
+
+    if (mod == 3)
+        return (BYTE *)get_int_reg( context, rm );
+
+    if ((rm & 7) == 4)
+    {
+        BYTE sib;
+        int id;
+
+        GET_VAL( &sib, BYTE );
+        rm = SIB_BASE( sib, rex );
+        id = SIB_INDEX( sib, rex );
+        ss = SIB_SS( sib, rex );
+
+        index = (id != 4) ? *get_int_reg( context, id ) : 0;
+        if (!long_addr) index &= 0xffffffff;
+        have_sib = 1;
+    }
+
+    base = *get_int_reg( context, rm );
+    if (!long_addr) base &= 0xffffffff;
+
+    switch (mod)
+    {
+    case 0:
+        if (rm == 5)  /* special case */
+        {
+            base = have_sib ? 0 : context->Rip;
+            if (!long_addr) base &= 0xffffffff;
+            GET_VAL( &off, DWORD );
+            base += (signed long)off;
+        }
+        break;
+
+    case 1:  /* 8-bit disp */
+        GET_VAL( &off, BYTE );
+        base += (signed char)off;
+        break;
+
+    case 2:  /* 32-bit disp */
+        GET_VAL( &off, DWORD );
+        base += (signed long)off;
+        break;
+    }
+
+    /* FIXME: we assume that all segments have a base of 0 */
+    return (BYTE *)(base + (index << ss));
+#undef GET_VAL
+}
+
+
+/***********************************************************************
+ *           emulate_instruction
+ *
+ * Emulate a privileged instruction.
+ * Returns exception continuation status.
+ */
+static DWORD emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context )
+{
+    int prefix, segprefix, prefixlen, len, long_op, long_addr, rex;
+    BYTE *instr;
+
+    long_op = long_addr = 1;
+    instr = (BYTE *)context->Rip;
+    if (!instr) return ExceptionContinueSearch;
+
+    /* First handle any possible prefix */
+
+    segprefix = -1;  /* no seg prefix */
+    rex = 0;  /* no rex prefix */
+    prefix = 1;
+    prefixlen = 0;
+    while(prefix)
+    {
+        switch(*instr)
+        {
+        case 0x2e:
+            segprefix = context->SegCs;
+            break;
+        case 0x36:
+            segprefix = context->SegSs;
+            break;
+        case 0x3e:
+            segprefix = context->SegDs;
+            break;
+        case 0x26:
+            segprefix = context->SegEs;
+            break;
+        case 0x64:
+            segprefix = context->SegFs;
+            break;
+        case 0x65:
+            segprefix = context->SegGs;
+            break;
+        case 0x66:
+            long_op = !long_op;  /* opcode size prefix */
+            break;
+        case 0x67:
+            long_addr = !long_addr;  /* addr size prefix */
+            break;
+        case 0xf0:  /* lock */
+        break;
+        case 0xf2:  /* repne */
+        break;
+        case 0xf3:  /* repe */
+            break;
+        default:
+            prefix = 0;  /* no more prefixes */
+            break;
+        }
+        if (*instr >= 0x40 && *instr < 0x50)  /* rex */
+        {
+            rex = *instr;
+            prefix = TRUE;
+        }
+        if (prefix)
+        {
+            instr++;
+            prefixlen++;
+        }
+    }
+
+    /* Now look at the actual instruction */
+
+    switch(*instr)
+    {
+    case 0x8a: /* mov Eb, Gb */
+    case 0x8b: /* mov Ev, Gv */
+    {
+        BYTE *data = INSTR_GetOperandAddr( context, instr + 1, long_addr,
+                                           rex, segprefix, &len );
+        unsigned int data_size = (*instr == 0x8b) ? get_op_size( long_op, rex ) : 1;
+        unsigned int offset = data - user_shared_data;
+
+        if (offset <= sizeof(KSHARED_USER_DATA) - data_size)
+        {
+            switch (*instr)
+            {
+            case 0x8a: store_reg_byte( context, instr[1], wine_user_shared_data + offset, rex ); break;
+            case 0x8b: store_reg_word( context, instr[1], wine_user_shared_data + offset, long_op, rex ); break;
+            }
+            context->Rip += prefixlen + len + 1;
+            return ExceptionContinueExecution;
+        }
+        break;  /* Unable to emulate it */
+    }
+    }
+    return ExceptionContinueSearch;  /* Unable to emulate it */
+}
+
+
+/***********************************************************************
+ *           vectored_handler
+ *
+ * Vectored exception handler used to emulate protected instructions
+ * from 64-bit code.
+ */
+LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs )
+{
+    EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
+    CONTEXT *context = ptrs->ContextRecord;
+
+    if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
+        record->ExceptionInformation[0] == EXCEPTION_READ_FAULT)
+    {
+        if (emulate_instruction( record, context ) == ExceptionContinueExecution)
+        {
+            TRACE( "next instruction rip=%lx\n", context->Rip );
+            TRACE( "  rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
+                   context->Rax, context->Rbx, context->Rcx, context->Rdx );
+            TRACE( "  rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
+                   context->Rsi, context->Rdi, context->Rbp, context->Rsp );
+            TRACE( "   r8=%016lx  r9=%016lx r10=%016lx r11=%016lx\n",
+                   context->R8, context->R9, context->R10, context->R11 );
+            TRACE( "  r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
+                   context->R12, context->R13, context->R14, context->R15 );
+
+            return EXCEPTION_CONTINUE_EXECUTION;
+        }
+    }
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#endif  /* __x86_64__ */
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 2051939..35454a5 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -2069,7 +2069,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
     {
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls( inst );
-#ifdef __i386__
+#if defined(__i386__) || defined(__x86_64__)
         handler = RtlAddVectoredExceptionHandler( TRUE, vectored_handler );
 #endif
         KeQueryTickCount( &count );  /* initialize the global KeTickCount */




More information about the wine-cvs mailing list