From 78ee3e0a09735f835303b7ddd458c8852b4774fc Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Tue, 11 Nov 2014 03:11:33 +0100 Subject: ntdll: Implement emulation of SIDT instruction when using Exagear. --- configure.ac | 8 ++ dlls/ntdll/signal_i386.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/configure.ac b/configure.ac index c9445e7..8a5b2d8 100644 --- a/configure.ac +++ b/configure.ac @@ -32,6 +32,7 @@ AC_ARG_ENABLE(win16, AS_HELP_STRING([--disable-win16],[do not include Win16 supp AC_ARG_ENABLE(win64, AS_HELP_STRING([--enable-win64],[build a Win64 emulator on AMD64 (won't run Win32 binaries)])) AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not build the regression tests])) AC_ARG_ENABLE(maintainer-mode, AS_HELP_STRING([--enable-maintainer-mode],[enable maintainer-specific build rules])) +AC_ARG_ENABLE(exagear-compat, AS_HELP_STRING([--enable-exagear-compat],[use workarounds for known problems in the Exagear emulator])) AC_ARG_WITH(alsa, AS_HELP_STRING([--without-alsa],[do not use the Alsa sound support]), [if test "x$withval" = "xno"; then ac_cv_header_sys_asoundlib_h=no; ac_cv_header_alsa_asoundlib_h=no; fi]) @@ -372,6 +373,13 @@ fi WINE_WARNING_WITH(gettext,[test "$MSGFMT" = false], [gettext tools not found (or too old), translations won't be built.]) +dnl **** Enable Exagear workarounds **** + +if test "x$enable_exagear_compat" = "xyes" +then + AC_DEFINE(EXAGEAR_COMPAT, 1, [Define if you want to enable Exagear emulator workarounds]) +fi + dnl **** Check for some libraries **** dnl Check for -li386 for NetBSD and OpenBSD diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 7121267..6976709 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -96,6 +96,14 @@ typedef struct BYTE Reserved4[96]; } XMM_SAVE_AREA32; +#include "pshpack1.h" +struct idtr +{ + WORD limit; + BYTE *base; +}; +#include "poppack.h" + /*********************************************************************** * signal context platform-specific definitions */ @@ -1898,6 +1906,213 @@ static inline DWORD get_fpu_code( const CONTEXT *context ) } +#ifdef EXAGEAR_COMPAT + +/*********************************************************************** + * INSTR_GetOperandAddr + * + * Return the address of an instruction operand (from the mod/rm byte). + */ +static BYTE *INSTR_GetOperandAddr( CONTEXT *context, const BYTE *instr, + int long_addr, int segprefix, int *len ) +{ + int mod, rm, base = 0, index = 0, ss = 0, off; + +#define GET_VAL(val,type) \ + { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); } + + *len = 0; + GET_VAL( &mod, BYTE ); + rm = mod & 7; + mod >>= 6; + + if (mod == 3) + { + switch(rm) + { + case 0: return (BYTE *)&context->Eax; + case 1: return (BYTE *)&context->Ecx; + case 2: return (BYTE *)&context->Edx; + case 3: return (BYTE *)&context->Ebx; + case 4: return (BYTE *)&context->Esp; + case 5: return (BYTE *)&context->Ebp; + case 6: return (BYTE *)&context->Esi; + case 7: return (BYTE *)&context->Edi; + } + } + + if (long_addr) + { + if (rm == 4) + { + BYTE sib; + GET_VAL( &sib, BYTE ); + rm = sib & 7; + ss = sib >> 6; + switch((sib >> 3) & 7) + { + case 0: index = context->Eax; break; + case 1: index = context->Ecx; break; + case 2: index = context->Edx; break; + case 3: index = context->Ebx; break; + case 4: index = 0; break; + case 5: index = context->Ebp; break; + case 6: index = context->Esi; break; + case 7: index = context->Edi; break; + } + } + + switch(rm) + { + case 0: base = context->Eax; break; + case 1: base = context->Ecx; break; + case 2: base = context->Edx; break; + case 3: base = context->Ebx; break; + case 4: base = context->Esp; break; + case 5: base = context->Ebp; break; + case 6: base = context->Esi; break; + case 7: base = context->Edi; break; + } + switch (mod) + { + case 0: + if (rm == 5) /* special case: ds:(disp32) */ + { + GET_VAL( &base, DWORD ); + } + 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; + } + } + else /* short address */ + { + switch(rm) + { + case 0: /* ds:(bx,si) */ + base = LOWORD(context->Ebx) + LOWORD(context->Esi); + break; + case 1: /* ds:(bx,di) */ + base = LOWORD(context->Ebx) + LOWORD(context->Edi); + break; + case 2: /* ss:(bp,si) */ + base = LOWORD(context->Ebp) + LOWORD(context->Esi); + break; + case 3: /* ss:(bp,di) */ + base = LOWORD(context->Ebp) + LOWORD(context->Edi); + break; + case 4: /* ds:(si) */ + base = LOWORD(context->Esi); + break; + case 5: /* ds:(di) */ + base = LOWORD(context->Edi); + break; + case 6: /* ss:(bp) */ + base = LOWORD(context->Ebp); + break; + case 7: /* ds:(bx) */ + base = LOWORD(context->Ebx); + break; + } + + switch(mod) + { + case 0: + if (rm == 6) /* special case: ds:(disp16) */ + { + GET_VAL( &base, WORD ); + } + break; + + case 1: /* 8-bit disp */ + GET_VAL( &off, BYTE ); + base += (signed char)off; + break; + + case 2: /* 16-bit disp */ + GET_VAL( &off, WORD ); + base += (signed short)off; + break; + } + base &= 0xffff; + } + /* FIXME: we assume that all segments have a base of 0 */ + return (BYTE *)(base + (index << ss)); +#undef GET_VAL +} + + +/*********************************************************************** + * check_invalid_instr + * + * Support for instructions not implemented by Exagear. + */ +static inline BOOL check_invalid_instr( CONTEXT *context ) +{ + const BYTE *instr; + unsigned int prefix_count = 0; + int len, long_addr = 1; + + if (!wine_ldt_is_system( context->SegCs )) return FALSE; + instr = (BYTE *)context->Eip; + + for (;;) switch (*instr) + { + /* instruction prefixes */ + case 0x2e: /* %cs: */ + case 0x36: /* %ss: */ + case 0x3e: /* %ds: */ + case 0x26: /* %es: */ + case 0x64: /* %fs: */ + case 0x65: /* %gs: */ + case 0x66: /* opcode size */ + case 0x67: /* addr size */ + case 0xf0: /* lock */ + case 0xf2: /* repne */ + case 0xf3: /* repe */ + if (++prefix_count >= 15) return FALSE; + if (*instr == 0x67) long_addr = !long_addr; /* addr size */ + instr++; + continue; + case 0x0f: /* extended instruction */ + switch (instr[1]) + { + case 0x01: + if (((instr[2] >> 3) & 7) == 1) /* sidt m */ + { + struct idtr ret; + BYTE *addr; + + if ((instr[2] >> 6) == 3) return FALSE; /* loading to register not allowed */ + addr = INSTR_GetOperandAddr( context, instr + 2, long_addr, 0, &len ); + + /* fake IDT structure */ + ret.limit = 0xfff; + ret.base = (void *)0xff000000; + memcpy(addr, &ret, sizeof(ret)); + + context->Eip += prefix_count + len + 2; + return TRUE; + } + break; + } + return FALSE; + default: + return FALSE; + } +} + +#endif /* EXAGEAR_COMPAT */ + + /********************************************************************** * raise_segv_exception */ @@ -1907,6 +2122,14 @@ static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context switch(rec->ExceptionCode) { +#ifdef EXAGEAR_COMPAT + case EXCEPTION_ILLEGAL_INSTRUCTION: + { + if (check_invalid_instr( context )) + goto done; + } + break; +#endif case EXCEPTION_ACCESS_VIOLATION: if (rec->NumberParameters == 2) { -- 2.7.1