Improve on a fixup for the difference between opengl and directx pixel
[wine] / dlls / kernel / wowthunk.c
index 9df980f..708f515 100644 (file)
 #include "winerror.h"
 #include "wownt32.h"
 #include "excpt.h"
-#include "winreg.h"
+#include "thread.h"
 #include "winternl.h"
-#include "file.h"
-#include "task.h"
-#include "miscemu.h"
-#include "selectors.h"
-#include "stackframe.h"
 #include "kernel_private.h"
+#include "kernel16_private.h"
 #include "wine/exception.h"
 #include "wine/debug.h"
 
@@ -80,20 +76,24 @@ static DWORD CALLBACK start_thread16( LPVOID threadArgs )
 /* symbols exported from relay16.s */
 extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
 extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
-extern void Call16_Ret_Start(), Call16_Ret_End();
-extern void CallTo16_Ret();
+extern void __wine_call_to_16_ret(void);
 extern void CALL32_CBClient_Ret();
 extern void CALL32_CBClientEx_Ret();
-extern DWORD CallTo16_DataSelector;
-extern SEGPTR CALL32_CBClient_RetAddr;
-extern SEGPTR CALL32_CBClientEx_RetAddr;
-extern BYTE Call16_Start;
-extern BYTE Call16_End;
+extern void DPMI_PendingEventCheck();
+extern void DPMI_PendingEventCheck_Cleanup();
+extern void DPMI_PendingEventCheck_Return();
+extern BYTE __wine_call16_start[];
+extern BYTE __wine_call16_end[];
 
 extern void RELAY16_InitDebugLists(void);
 
 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs );
-static SEGPTR call16_ret_addr;  /* segptr to CallTo16_Ret routine */
+static SEGPTR call16_ret_addr;  /* segptr to __wine_call_to_16_ret routine */
+
+static WORD  dpmi_checker_selector;
+static DWORD dpmi_checker_offset_call;
+static DWORD dpmi_checker_offset_cleanup;
+static DWORD dpmi_checker_offset_return;
 
 /***********************************************************************
  *           WOWTHUNK_Init
@@ -101,19 +101,29 @@ static SEGPTR call16_ret_addr;  /* segptr to CallTo16_Ret routine */
 BOOL WOWTHUNK_Init(void)
 {
     /* allocate the code selector for CallTo16 routines */
-    WORD codesel = SELECTOR_AllocBlock( (void *)Call16_Ret_Start,
-                                        (char *)Call16_Ret_End - (char *)Call16_Ret_Start,
-                                        WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
+    LDT_ENTRY entry;
+    WORD codesel = wine_ldt_alloc_entries(1);
+
     if (!codesel) return FALSE;
+    wine_ldt_set_base( &entry, __wine_call16_start );
+    wine_ldt_set_limit( &entry, (BYTE *)(&CallTo16_TebSelector + 1) - __wine_call16_start - 1 );
+    wine_ldt_set_flags( &entry, WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
+    wine_ldt_set_entry( codesel, &entry );
 
       /* Patch the return addresses for CallTo16 routines */
 
     CallTo16_DataSelector = wine_get_ds();
-    call16_ret_addr = MAKESEGPTR( codesel, (char*)CallTo16_Ret - (char*)Call16_Ret_Start );
+    call16_ret_addr = MAKESEGPTR( codesel, (BYTE *)__wine_call_to_16_ret - __wine_call16_start );
     CALL32_CBClient_RetAddr =
-        MAKESEGPTR( codesel, (char*)CALL32_CBClient_Ret - (char*)Call16_Ret_Start );
+        MAKESEGPTR( codesel, (BYTE *)CALL32_CBClient_Ret - __wine_call16_start );
     CALL32_CBClientEx_RetAddr =
-        MAKESEGPTR( codesel, (char*)CALL32_CBClientEx_Ret - (char*)Call16_Ret_Start );
+        MAKESEGPTR( codesel, (BYTE *)CALL32_CBClientEx_Ret - __wine_call16_start );
+
+    /* Prepare selector and offsets for DPMI event checking. */
+    dpmi_checker_selector = codesel;
+    dpmi_checker_offset_call = (BYTE *)DPMI_PendingEventCheck - __wine_call16_start;
+    dpmi_checker_offset_cleanup = (BYTE *)DPMI_PendingEventCheck_Cleanup - __wine_call16_start;
+    dpmi_checker_offset_return = (BYTE *)DPMI_PendingEventCheck_Return - __wine_call16_start;
 
     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
 
@@ -134,7 +144,7 @@ static BOOL fix_selector( CONTEXT *context )
     WORD *stack;
     BYTE *instr = (BYTE *)context->Eip;
 
-    if (instr < &Call16_Start || instr >= &Call16_End) return FALSE;
+    if (instr < __wine_call16_start || instr >= __wine_call16_end) return FALSE;
 
     /* skip prefixes */
     while (*instr == 0x66 || *instr == 0x67) instr++;
@@ -165,6 +175,79 @@ static BOOL fix_selector( CONTEXT *context )
 }
 
 
+/*************************************************************
+ *            insert_event_check
+ *
+ * Make resuming the context check for pending DPMI events
+ * before the original context is restored. This is required
+ * because DPMI events are asynchronous, they are blocked while 
+ * Wine 32-bit code is being executed and we want to prevent 
+ * a race when returning back to 16-bit or 32-bit DPMI context.
+ */
+static void insert_event_check( CONTEXT *context )
+{
+    char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
+
+    /* don't do event check while in system code */
+    if (wine_ldt_is_system(context->SegCs))
+        return;
+
+    if(context->SegCs == dpmi_checker_selector &&
+       context->Eip   >= dpmi_checker_offset_call && 
+       context->Eip   <= dpmi_checker_offset_cleanup)
+    {
+        /*
+         * Nested call. Stack will be preserved. 
+         */
+    }
+    else if(context->SegCs == dpmi_checker_selector &&
+            context->Eip   == dpmi_checker_offset_return)
+    {
+        /*
+         * Nested call. We have just finished popping the fs
+         * register, lets put it back into stack.
+         */
+
+        stack -= sizeof(WORD);
+        *(WORD*)stack = context->SegFs;
+
+        context->Esp -= 2;
+    }
+    else
+    {
+        /*
+         * Call is not nested.
+         * Push modified registers into stack.
+         * These will be popped by the assembler stub.
+         */
+
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->EFlags;
+   
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->SegCs;
+
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->Eip;
+
+        stack -= sizeof(WORD);
+        *(WORD*)stack = context->SegFs;
+
+        context->Esp  -= 14;
+    }
+
+    /*
+     * Modify the context so that we jump into assembler stub.
+     * TEB access is made easier by providing the stub
+     * with the correct fs register value.
+     */
+
+    context->SegCs = dpmi_checker_selector;
+    context->Eip   = dpmi_checker_offset_call;
+    context->SegFs = wine_get_fs();
+}
+
+
 /*************************************************************
  *            call16_handler
  *
@@ -177,7 +260,7 @@ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RE
     {
         /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
         STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame));
-        NtCurrentTeb()->cur_stack = frame32->frame16;
+        NtCurrentTeb()->WOW32Reserved = (void *)frame32->frame16;
         _LeaveWin16Lock();
     }
     else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
@@ -192,6 +275,15 @@ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RE
             SEGPTR gpHandler;
             DWORD ret = INSTR_EmulateInstruction( record, context );
 
+            /*
+             * Insert check for pending DPMI events. Note that this 
+             * check must be inserted after instructions have been 
+             * emulated because the instruction emulation requires
+             * original CS:IP and the emulation may change TEB.dpmi_vif.
+             */
+            if(NtCurrentTeb()->dpmi_vif)
+                insert_event_check( context );
+
             if (ret != ExceptionContinueSearch) return ret;
 
             /* check for Win16 __GP handler */
@@ -213,6 +305,10 @@ static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RE
             }
         }
     }
+    else if (record->ExceptionCode == EXCEPTION_VM86_STI)
+    {
+        insert_event_check( context );
+    }
     return ExceptionContinueSearch;
 }
 
@@ -500,7 +596,7 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
      * (both for PASCAL and CDECL calling convention), so we simply
      * copy them to the 16-bit stack ...
      */
-    WORD *stack = (WORD *)CURRENT_STACK16 - cbArgs / sizeof(WORD);
+    char *stack = (char *)CURRENT_STACK16 - cbArgs;
 
     memcpy( stack, pArgs, cbArgs );
 
@@ -511,13 +607,14 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
         if (TRACE_ON(relay))
         {
             DWORD count = cbArgs / sizeof(WORD);
+            WORD * wstack = (WORD *)stack;
 
             DPRINTF("%04lx:CallTo16(func=%04lx:%04x,ds=%04lx",
                     GetCurrentThreadId(),
                     context->SegCs, LOWORD(context->Eip), context->SegDs );
-            while (count) DPRINTF( ",%04x", stack[--count] );
+            while (count) DPRINTF( ",%04x", wstack[--count] );
             DPRINTF(") ss:sp=%04x:%04x",
-                    SELECTOROF(NtCurrentTeb()->cur_stack), OFFSETOF(NtCurrentTeb()->cur_stack) );
+                    SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x es=%04x fs=%04x\n",
                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
                     (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
@@ -538,16 +635,32 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
             /* push return address */
             if (dwFlags & WCB16_REGS_LONG)
             {
-                *((DWORD *)stack - 1) = HIWORD(call16_ret_addr);
-                *((DWORD *)stack - 2) = LOWORD(call16_ret_addr);
+                stack -= sizeof(DWORD);
+                *((DWORD *)stack) = HIWORD(call16_ret_addr);
+                stack -= sizeof(DWORD);
+                *((DWORD *)stack) = LOWORD(call16_ret_addr);
                 cbArgs += 2 * sizeof(DWORD);
             }
             else
             {
-                *((SEGPTR *)stack - 1) = call16_ret_addr;
+                stack -= sizeof(SEGPTR);
+                *((SEGPTR *)stack) = call16_ret_addr;
                 cbArgs += sizeof(SEGPTR);
             }
 
+            /*
+             * Start call by checking for pending events.
+             * Note that wine_call_to_16_regs overwrites context stack
+             * pointer so we may modify it here without a problem.
+             */
+            if (NtCurrentTeb()->dpmi_vif)
+            {
+                context->SegSs = wine_get_ds();
+                context->Esp   = (DWORD)stack;
+                insert_event_check( context );
+                cbArgs += (DWORD)stack - context->Esp;
+            }
+
             _EnterWin16Lock();
             wine_call_to_16_regs( context, cbArgs, call16_handler );
             _LeaveWin16Lock();
@@ -556,8 +669,8 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
         if (TRACE_ON(relay))
         {
             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x ",
-                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->cur_stack),
-                    OFFSETOF(NtCurrentTeb()->cur_stack));
+                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
+                    OFFSETOF(NtCurrentTeb()->WOW32Reserved));
             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
                     (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
@@ -571,18 +684,20 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
         if (TRACE_ON(relay))
         {
             DWORD count = cbArgs / sizeof(WORD);
+            WORD * wstack = (WORD *)stack;
 
             DPRINTF("%04lx:CallTo16(func=%04x:%04x,ds=%04x",
                     GetCurrentThreadId(), HIWORD(vpfn16), LOWORD(vpfn16),
-                    SELECTOROF(NtCurrentTeb()->cur_stack) );
-            while (count) DPRINTF( ",%04x", stack[--count] );
+                    SELECTOROF(NtCurrentTeb()->WOW32Reserved) );
+            while (count) DPRINTF( ",%04x", wstack[--count] );
             DPRINTF(") ss:sp=%04x:%04x\n",
-                    SELECTOROF(NtCurrentTeb()->cur_stack), OFFSETOF(NtCurrentTeb()->cur_stack) );
+                    SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
             SYSLEVEL_CheckNotLevel( 2 );
         }
 
         /* push return address */
-        *((SEGPTR *)stack - 1) = call16_ret_addr;
+        stack -= sizeof(SEGPTR);
+        *((SEGPTR *)stack) = call16_ret_addr;
         cbArgs += sizeof(SEGPTR);
 
         /*
@@ -599,8 +714,8 @@ BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
         if (TRACE_ON(relay))
         {
             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x retval=%08lx\n",
-                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->cur_stack),
-                    OFFSETOF(NtCurrentTeb()->cur_stack), ret);
+                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
+                    OFFSETOF(NtCurrentTeb()->WOW32Reserved), ret);
             SYSLEVEL_CheckNotLevel( 2 );
         }
     }
@@ -645,11 +760,9 @@ DWORD WINAPI GetVDMPointer32W16( SEGPTR vp, UINT16 fMode )
 DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags )
 {
     HMODULE hModule;
-    DOS_FULL_NAME full_name;
     DWORD mutex_count;
-    UNICODE_STRING libfileW;
-    LPCWSTR filenameW;
-    static const WCHAR dllW[] = {'.','D','L','L',0};
+    OFSTRUCT ofs;
+    const char *p;
 
     if (!lpszLibFile)
     {
@@ -657,25 +770,27 @@ DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags
         return 0;
     }
 
-    if (!RtlCreateUnicodeStringFromAsciiz(&libfileW, lpszLibFile))
+    /* if the file cannot be found, call LoadLibraryExA anyway, since it might be
+       a builtin module. This case is handled in MODULE_LoadLibraryExA */
+
+    if ((p = strrchr( lpszLibFile, '.' )) && !strchr( p, '\\' ))  /* got an extension */
     {
-        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return 0;
+        if (OpenFile16( lpszLibFile, &ofs, OF_EXIST ) != HFILE_ERROR16)
+            lpszLibFile = ofs.szPathName;
+    }
+    else
+    {
+        char buffer[MAX_PATH+4];
+        strcpy( buffer, lpszLibFile );
+        strcat( buffer, ".dll" );
+        if (OpenFile16( buffer, &ofs, OF_EXIST ) != HFILE_ERROR16)
+            lpszLibFile = ofs.szPathName;
     }
-
-    /* if the file can not be found, call LoadLibraryExA anyway, since it might be
-       a buildin module. This case is handled in MODULE_LoadLibraryExA */
-
-    filenameW = libfileW.Buffer;
-    if ( DIR_SearchPath( NULL, filenameW, dllW, &full_name, FALSE ) )
-        filenameW = full_name.short_name;
 
     ReleaseThunkLock( &mutex_count );
-    hModule = LoadLibraryExW( filenameW, (HANDLE)hFile, dwFlags );
+    hModule = LoadLibraryExA( lpszLibFile, (HANDLE)hFile, dwFlags );
     RestoreThunkLock( mutex_count );
 
-    RtlFreeUnicodeString(&libfileW);
-
     return (DWORD)hModule;
 }