Write section access also assumes read access.
[wine] / dlls / kernel / thunk.c
index f4f101c..8b3fcd0 100644 (file)
  * Copyright 1997, 1998 Marcus Meissner
  * Copyright 1998       Ulrich Weigand
  *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #include <string.h>
 #include <sys/types.h>
-#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "wownt32.h"
 #include "wine/winbase16.h"
 
-#include "builtin16.h"
-#include "callback.h"
-#include "debugtools.h"
-#include "flatthunk.h"
-#include "heap.h"
+#include "wine/debug.h"
+#include "wine/library.h"
 #include "module.h"
-#include "selectors.h"
 #include "stackframe.h"
-#include "task.h"
+#include "kernel_private.h"
 
-DEFAULT_DEBUG_CHANNEL(thunk);
+WINE_DEFAULT_DEBUG_CHANNEL(thunk);
+
+struct ThunkDataCommon
+{
+    char                   magic[4];         /* 00 */
+    DWORD                  checksum;         /* 04 */
+};
+
+struct ThunkDataLS16
+{
+    struct ThunkDataCommon common;           /* 00 */
+    SEGPTR                 targetTable;      /* 08 */
+    DWORD                  firstTime;        /* 0C */
+};
+
+struct ThunkDataLS32
+{
+    struct ThunkDataCommon common;           /* 00 */
+    DWORD *                targetTable;      /* 08 */
+    char                   lateBinding[4];   /* 0C */
+    DWORD                  flags;            /* 10 */
+    DWORD                  reserved1;        /* 14 */
+    DWORD                  reserved2;        /* 18 */
+    DWORD                  offsetQTThunk;    /* 1C */
+    DWORD                  offsetFTProlog;   /* 20 */
+};
+
+struct ThunkDataSL16
+{
+    struct ThunkDataCommon common;            /* 00 */
+    DWORD                  flags1;            /* 08 */
+    DWORD                  reserved1;         /* 0C */
+    struct ThunkDataSL *   fpData;            /* 10 */
+    SEGPTR                 spData;            /* 14 */
+    DWORD                  reserved2;         /* 18 */
+    char                   lateBinding[4];    /* 1C */
+    DWORD                  flags2;            /* 20 */
+    DWORD                  reserved3;         /* 20 */
+    SEGPTR                 apiDatabase;       /* 28 */
+};
+
+struct ThunkDataSL32
+{
+    struct ThunkDataCommon common;            /* 00 */
+    DWORD                  reserved1;         /* 08 */
+    struct ThunkDataSL *   data;              /* 0C */
+    char                   lateBinding[4];    /* 10 */
+    DWORD                  flags;             /* 14 */
+    DWORD                  reserved2;         /* 18 */
+    DWORD                  reserved3;         /* 1C */
+    DWORD                  offsetTargetTable; /* 20 */
+};
+
+struct ThunkDataSL
+{
+#if 0
+    This structure differs from the Win95 original,
+    but this should not matter since it is strictly internal to
+    the thunk handling routines in KRNL386 / KERNEL32.
+
+    For reference, here is the Win95 layout:
+
+    struct ThunkDataCommon common;            /* 00 */
+    DWORD                  flags1;            /* 08 */
+    SEGPTR                 apiDatabase;       /* 0C */
+    WORD                   exePtr;            /* 10 */
+    WORD                   segMBA;            /* 12 */
+    DWORD                  lenMBATotal;       /* 14 */
+    DWORD                  lenMBAUsed;        /* 18 */
+    DWORD                  flags2;            /* 1C */
+    char                   pszDll16[256];     /* 20 */
+    char                   pszDll32[256];     /*120 */
+
+    We do it differently since all our thunk handling is done
+    by 32-bit code. Therefore we do not need do provide
+    easy access to this data, especially the process target
+    table database, for 16-bit code.
+#endif
+
+    struct ThunkDataCommon common;
+    DWORD                  flags1;
+    struct SLApiDB *       apiDB;
+    struct SLTargetDB *    targetDB;
+    DWORD                  flags2;
+    char                   pszDll16[256];
+    char                   pszDll32[256];
+};
+
+struct SLTargetDB
+{
+     struct SLTargetDB *   next;
+     DWORD                 process;
+     DWORD *               targetTable;
+};
 
+struct SLApiDB
+{
+    DWORD                  nrArgBytes;
+    DWORD                  errorReturnValue;
+};
+
+#ifdef __i386__
+extern void __wine_call_from_16_thunk();
+#else
+static void __wine_call_from_16_thunk() { }
+#endif
+
+/* Push a DWORD on the 32-bit stack */
+static inline void stack32_push( CONTEXT86 *context, DWORD val )
+{
+    context->Esp -= sizeof(DWORD);
+    *(DWORD *)context->Esp = val;
+}
+
+/* Pop a DWORD from the 32-bit stack */
+static inline DWORD stack32_pop( CONTEXT86 *context )
+{
+    DWORD ret = *(DWORD *)context->Esp;
+    context->Esp += sizeof(DWORD);
+    return ret;
+}
 
 /***********************************************************************
  *                                                                     *
@@ -45,7 +182,7 @@ void WINAPI LogApiThk( LPSTR func )
 
 /***********************************************************************
  *           LogApiThkLSF    (KERNEL32.42)
- * 
+ *
  * NOTE: needs to preserve all registers!
  */
 void WINAPI LogApiThkLSF( LPSTR func, CONTEXT86 *context )
@@ -55,7 +192,7 @@ void WINAPI LogApiThkLSF( LPSTR func, CONTEXT86 *context )
 
 /***********************************************************************
  *           LogApiThkSL    (KERNEL32.44)
- * 
+ *
  * NOTE: needs to preserve all registers!
  */
 void WINAPI LogApiThkSL( LPSTR func, CONTEXT86 *context )
@@ -65,7 +202,7 @@ void WINAPI LogApiThkSL( LPSTR func, CONTEXT86 *context )
 
 /***********************************************************************
  *           LogCBThkSL    (KERNEL32.47)
- * 
+ *
  * NOTE: needs to preserve all registers!
  */
 void WINAPI LogCBThkSL( LPSTR func, CONTEXT86 *context )
@@ -75,7 +212,7 @@ void WINAPI LogCBThkSL( LPSTR func, CONTEXT86 *context )
 
 /***********************************************************************
  * Generates a FT_Prolog call.
- *     
+ *
  *  0FB6D1                  movzbl edx,cl
  *  8B1495xxxxxxxx         mov edx,[4*edx + targetTable]
  *  68xxxxxxxx             push FT_Prolog
@@ -88,7 +225,7 @@ static void _write_ftprolog(LPBYTE relayCode ,DWORD *targetTable) {
        *x++    = 0x0f;*x++=0xb6;*x++=0xd1; /* movzbl edx,cl */
        *x++    = 0x8B;*x++=0x14;*x++=0x95;*(DWORD**)x= targetTable;
        x+=4;   /* mov edx, [4*edx + targetTable] */
-       *x++    = 0x68; *(DWORD*)x = (DWORD)GetProcAddress(GetModuleHandleA("KERNEL32"),"FT_Prolog");
+       *x++    = 0x68; *(DWORD*)x = (DWORD)GetProcAddress(kernel32_handle,"FT_Prolog");
        x+=4;   /* push FT_Prolog */
        *x++    = 0xC3;         /* lret */
        /* fill rest with 0xCC / int 3 */
@@ -115,7 +252,7 @@ static void _write_qtthunk(
        *x++    = 0x8A;*x++=0x4D;*x++=0xFC; /* movb cl,[ebp-04] */
        *x++    = 0x8B;*x++=0x14;*x++=0x8D;*(DWORD**)x= targetTable;
        x+=4;   /* mov edx, [4*ecx + targetTable */
-       *x++    = 0xB8; *(DWORD*)x = (DWORD)GetProcAddress(GetModuleHandleA("KERNEL32"),"QT_Thunk");
+       *x++    = 0xB8; *(DWORD*)x = (DWORD)GetProcAddress(kernel32_handle,"QT_Thunk");
        x+=4;   /* mov eax , QT_Thunk */
        *x++    = 0xFF; *x++ = 0xE0;    /* jmp eax */
        /* should fill the rest of the 32 bytes with 0xCC */
@@ -124,14 +261,14 @@ static void _write_qtthunk(
 /***********************************************************************
  *           _loadthunk
  */
-static LPVOID _loadthunk(LPCSTR module, LPCSTR func, LPCSTR module32, 
+static LPVOID _loadthunk(LPCSTR module, LPCSTR func, LPCSTR module32,
                          struct ThunkDataCommon *TD32, DWORD checksum)
 {
     struct ThunkDataCommon *TD16;
-    HMODULE hmod;
+    HMODULE16 hmod;
     int ordinal;
 
-    if ((hmod = LoadLibrary16(module)) <= 32) 
+    if ((hmod = LoadLibrary16(module)) <= 32)
     {
         ERR("(%s, %s, %s): Unable to load '%s', error %d\n",
                    module, func, module32, module, hmod);
@@ -149,7 +286,7 @@ static LPVOID _loadthunk(LPCSTR module, LPCSTR func, LPCSTR module32,
     if (TD32 && memcmp(TD16->magic, TD32->magic, 4))
     {
         ERR("(%s, %s, %s): Bad magic %c%c%c%c (should be %c%c%c%c)\n",
-                   module, func, module32, 
+                   module, func, module32,
                    TD16->magic[0], TD16->magic[1], TD16->magic[2], TD16->magic[3],
                    TD32->magic[0], TD32->magic[1], TD32->magic[2], TD32->magic[3]);
         return 0;
@@ -190,10 +327,10 @@ LPVOID WINAPI GetThunkBuff(void)
 }
 
 /***********************************************************************
- *             ThunkConnect32          (KERNEL32)
+ *             ThunkConnect32          (KERNEL32.@)
  * Connects a 32bit and a 16bit thunkbuffer.
  */
-UINT WINAPI ThunkConnect32( 
+UINT WINAPI ThunkConnect32(
        struct ThunkDataCommon *TD,  /* [in/out] thunkbuffer */
        LPSTR thunkfun16,            /* [in] win16 thunkfunction */
        LPSTR module16,              /* [in] name of win16 dll */
@@ -219,11 +356,11 @@ UINT WINAPI ThunkConnect32(
     }
     else
     {
-        ERR("Invalid magic %c%c%c%c\n", 
+        ERR("Invalid magic %c%c%c%c\n",
                    TD->magic[0], TD->magic[1], TD->magic[2], TD->magic[3]);
         return 0;
     }
-    
+
     switch (dwReason)
     {
         case DLL_PROCESS_ATTACH:
@@ -253,7 +390,7 @@ UINT WINAPI ThunkConnect32(
                 tdb->next = SL32->data->targetDB;   /* FIXME: not thread-safe! */
                 SL32->data->targetDB = tdb;
 
-                TRACE("Process %08lx allocated TargetDB entry for ThunkDataSL %08lx\n", 
+                TRACE("Process %08lx allocated TargetDB entry for ThunkDataSL %08lx\n",
                              GetCurrentProcessId(), (DWORD)SL32->data);
             }
             else
@@ -279,12 +416,27 @@ UINT WINAPI ThunkConnect32(
 }
 
 /**********************************************************************
- *             QT_Thunk                        (KERNEL32)
+ *             QT_Thunk                        (KERNEL32.@)
  *
  * The target address is in EDX.
- * The 16 bit arguments start at ESP.
+ * The 16bit arguments start at ESP.
  * The number of 16bit argument bytes is EBP-ESP-0x40 (64 Byte thunksetup).
+ * So the stack layout is 16bit argument bytes and then the 64 byte
+ * scratch buffer.
+ * The scratch buffer is used as work space by Windows' QT_Thunk
+ * function.
+ * As the programs unfortunately don't always provide a fixed size
+ * scratch buffer (danger, stack corruption ahead !!), we simply resort
+ * to copying over the whole EBP-ESP range to the 16bit stack
+ * (as there's no way to safely figure out the param count
+ * due to this misbehaviour of some programs).
  * [ok]
+ *
+ * See DDJ article 9614c for a very good description of QT_Thunk (also
+ * available online !).
+ *
+ * FIXME: DDJ talks of certain register usage rules; I'm not sure
+ * whether we cover this 100%.
  */
 void WINAPI QT_Thunk( CONTEXT86 *context )
 {
@@ -293,29 +445,42 @@ void WINAPI QT_Thunk( CONTEXT86 *context )
 
     memcpy(&context16,context,sizeof(context16));
 
+    context16.SegFs = wine_get_fs();
+    context16.SegGs = wine_get_gs();
     context16.SegCs = HIWORD(context->Edx);
     context16.Eip   = LOWORD(context->Edx);
+    /* point EBP to the STACK16FRAME on the stack
+     * for the call_to_16 to set up the register content on calling */
     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
                            + (WORD)&((STACK16FRAME*)0)->bp;
 
-    argsize = context->Ebp-context->Esp-0x40;
+    /*
+     * used to be (problematic):
+     * argsize = context->Ebp - context->Esp - 0x40;
+     * due to some programs abusing the API, we better assume the full
+     * EBP - ESP range for copying instead: */
+    argsize = context->Ebp - context->Esp;
 
-    memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
-            (LPBYTE)context->Esp, argsize );
+    /* ok, too much is insane; let's limit param count a bit again */
+    if (argsize > 64)
+       argsize = 64; /* 32 WORDs */
 
-    wine_call_to_16_regs_short( &context16, argsize );
+    WOWCallback16Ex( 0, WCB16_REGS, argsize, (void *)context->Esp, (DWORD *)&context16 );
     context->Eax = context16.Eax;
     context->Edx = context16.Edx;
     context->Ecx = context16.Ecx;
 
+    /* make sure to update the Win32 ESP, too, in order to throw away
+     * the number of parameters that the Win16 function
+     * accepted (that it popped from the corresponding Win16 stack) */
     context->Esp +=   LOWORD(context16.Esp) -
                         ( OFFSETOF( NtCurrentTeb()->cur_stack ) - argsize );
 }
 
 
 /**********************************************************************
- *             FT_Prolog                       (KERNEL32.233)
- * 
+ *             FT_Prolog                       (KERNEL32.@)
+ *
  * The set of FT_... thunk routines is used instead of QT_Thunk,
  * if structures have to be converted from 32-bit to 16-bit
  * (change of member alignment, conversion of members).
@@ -332,7 +497,7 @@ void WINAPI QT_Thunk( CONTEXT86 *context )
  *              bits 10..15  number of DWORD arguments
  *
  * Output: A new stackframe is created, and a 64 byte buffer
- *         allocated on the stack. The layout of the stack 
+ *         allocated on the stack. The layout of the stack
  *         on return is as follows:
  *
  *  (ebp+4)  return address to caller of thunk function
@@ -354,7 +519,7 @@ void WINAPI QT_Thunk( CONTEXT86 *context )
  *  (ebp-64)
  *
  *  ESP is EBP-64 after return.
- *         
+ *
  */
 
 void WINAPI FT_Prolog( CONTEXT86 *context )
@@ -379,9 +544,9 @@ void WINAPI FT_Prolog( CONTEXT86 *context )
 }
 
 /**********************************************************************
- *             FT_Thunk                        (KERNEL32.234)
+ *             FT_Thunk                        (KERNEL32.@)
  *
- * This routine performs the actual call to 16-bit code, 
+ * This routine performs the actual call to 16-bit code,
  * similar to QT_Thunk. The differences are:
  *  - The call target is taken from the buffer created by FT_Prolog
  *  - Those arguments requested by the thunk code (by setting the
@@ -390,11 +555,11 @@ void WINAPI FT_Prolog( CONTEXT86 *context )
  *    are guaranteed to point to structures copied to the stack
  *    by the thunk code, so we always use the 16-bit stack selector
  *    for those addresses).
- * 
+ *
  *    The bit #i of EBP-20 corresponds here to the DWORD starting at
  *    ESP+4 + 2*i.
- * 
- * FIXME: It is unclear what happens if there are more than 32 WORDs 
+ *
+ * FIXME: It is unclear what happens if there are more than 32 WORDs
  *        of arguments, so that the single DWORD bitmap is no longer
  *        sufficient ...
  */
@@ -406,17 +571,20 @@ void WINAPI FT_Thunk( CONTEXT86 *context )
 
     CONTEXT86 context16;
     DWORD i, argsize;
-    LPBYTE newstack, oldstack;
+    DWORD newstack[32];
+    LPBYTE oldstack;
 
     memcpy(&context16,context,sizeof(context16));
 
+    context16.SegFs = wine_get_fs();
+    context16.SegGs = wine_get_gs();
     context16.SegCs = HIWORD(callTarget);
     context16.Eip   = LOWORD(callTarget);
     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
                            + (WORD)&((STACK16FRAME*)0)->bp;
 
     argsize  = context->Ebp-context->Esp-0x40;
-    newstack = (LPBYTE)CURRENT_STACK16 - argsize;
+    if (argsize > sizeof(newstack)) argsize = sizeof(newstack);
     oldstack = (LPBYTE)context->Esp;
 
     memcpy( newstack, oldstack, argsize );
@@ -424,13 +592,13 @@ void WINAPI FT_Thunk( CONTEXT86 *context )
     for (i = 0; i < 32; i++)   /* NOTE: What about > 32 arguments? */
        if (mapESPrelative & (1 << i))
        {
-           SEGPTR *arg = (SEGPTR *)(newstack + 2*i);
+           SEGPTR *arg = (SEGPTR *)newstack[i];
            *arg = MAKESEGPTR(SELECTOROF(NtCurrentTeb()->cur_stack),
                               OFFSETOF(NtCurrentTeb()->cur_stack) - argsize
                               + (*(LPBYTE *)arg - oldstack));
        }
 
-    wine_call_to_16_regs_short( &context16, argsize );
+    WOWCallback16Ex( 0, WCB16_REGS, argsize, newstack, (DWORD *)&context16 );
     context->Eax = context16.Eax;
     context->Edx = context16.Edx;
     context->Ecx = context16.Ecx;
@@ -447,8 +615,8 @@ void WINAPI FT_Thunk( CONTEXT86 *context )
  *
  * One of the FT_ExitNN functions is called at the end of the thunk code.
  * It removes the stack frame created by FT_Prolog, moves the function
- * return from EBX to EAX (yes, FT_Thunk did use EAX for the return 
- * value, but the thunk code has moved it from EAX to EBX in the 
+ * return from EBX to EAX (yes, FT_Thunk did use EAX for the return
+ * value, but the thunk code has moved it from EAX to EBX in the
  * meantime ... :-), restores the caller's EBX, ESI, and EDI registers,
  * and perform a return to the CALLER of the thunk code (while removing
  * the given number of arguments from the caller's stack).
@@ -475,83 +643,83 @@ static void FT_Exit(CONTEXT86 *context, int nPopArgs)
 }
 
 /***********************************************************************
- *             FT_Exit0 (KERNEL32.218)
+ *             FT_Exit0 (KERNEL32.@)
  */
 void WINAPI FT_Exit0 (CONTEXT86 *context) { FT_Exit(context,  0); }
 
 /***********************************************************************
- *             FT_Exit4 (KERNEL32.219)
+ *             FT_Exit4 (KERNEL32.@)
  */
 void WINAPI FT_Exit4 (CONTEXT86 *context) { FT_Exit(context,  4); }
 
 /***********************************************************************
- *             FT_Exit8 (KERNEL32.220)
+ *             FT_Exit8 (KERNEL32.@)
  */
 void WINAPI FT_Exit8 (CONTEXT86 *context) { FT_Exit(context,  8); }
 
 /***********************************************************************
- *             FT_Exit12 (KERNEL32.221)
+ *             FT_Exit12 (KERNEL32.@)
  */
 void WINAPI FT_Exit12(CONTEXT86 *context) { FT_Exit(context, 12); }
 
 /***********************************************************************
- *             FT_Exit16 (KERNEL32.222)
+ *             FT_Exit16 (KERNEL32.@)
  */
 void WINAPI FT_Exit16(CONTEXT86 *context) { FT_Exit(context, 16); }
 
 /***********************************************************************
- *             FT_Exit20 (KERNEL32.223)
+ *             FT_Exit20 (KERNEL32.@)
  */
 void WINAPI FT_Exit20(CONTEXT86 *context) { FT_Exit(context, 20); }
 
 /***********************************************************************
- *             FT_Exit24 (KERNEL32.224)
+ *             FT_Exit24 (KERNEL32.@)
  */
 void WINAPI FT_Exit24(CONTEXT86 *context) { FT_Exit(context, 24); }
 
 /***********************************************************************
- *             FT_Exit28 (KERNEL32.225)
+ *             FT_Exit28 (KERNEL32.@)
  */
 void WINAPI FT_Exit28(CONTEXT86 *context) { FT_Exit(context, 28); }
 
 /***********************************************************************
- *             FT_Exit32 (KERNEL32.226)
+ *             FT_Exit32 (KERNEL32.@)
  */
 void WINAPI FT_Exit32(CONTEXT86 *context) { FT_Exit(context, 32); }
 
 /***********************************************************************
- *             FT_Exit36 (KERNEL32.227)
+ *             FT_Exit36 (KERNEL32.@)
  */
 void WINAPI FT_Exit36(CONTEXT86 *context) { FT_Exit(context, 36); }
 
 /***********************************************************************
- *             FT_Exit40 (KERNEL32.228)
+ *             FT_Exit40 (KERNEL32.@)
  */
 void WINAPI FT_Exit40(CONTEXT86 *context) { FT_Exit(context, 40); }
 
 /***********************************************************************
- *             FT_Exit44 (KERNEL32.229)
+ *             FT_Exit44 (KERNEL32.@)
  */
 void WINAPI FT_Exit44(CONTEXT86 *context) { FT_Exit(context, 44); }
 
 /***********************************************************************
- *             FT_Exit48 (KERNEL32.230)
+ *             FT_Exit48 (KERNEL32.@)
  */
 void WINAPI FT_Exit48(CONTEXT86 *context) { FT_Exit(context, 48); }
 
 /***********************************************************************
- *             FT_Exit52 (KERNEL32.231)
+ *             FT_Exit52 (KERNEL32.@)
  */
 void WINAPI FT_Exit52(CONTEXT86 *context) { FT_Exit(context, 52); }
 
 /***********************************************************************
- *             FT_Exit56 (KERNEL32.232)
+ *             FT_Exit56 (KERNEL32.@)
  */
 void WINAPI FT_Exit56(CONTEXT86 *context) { FT_Exit(context, 56); }
 
 /***********************************************************************
  *             ThunkInitLS     (KERNEL32.43)
- * A thunkbuffer link routine 
+ * A thunkbuffer link routine
  * The thunkbuf looks like:
  *
  *     00: DWORD       length          ? don't know exactly
@@ -583,14 +751,14 @@ DWORD WINAPI ThunkInitLS(
 
 /***********************************************************************
  *             Common32ThkLS   (KERNEL32.45)
- * 
+ *
  * This is another 32->16 thunk, independent of the QT_Thunk/FT_Thunk
- * style thunks. The basic difference is that the parameter conversion 
+ * style thunks. The basic difference is that the parameter conversion
  * is done completely on the *16-bit* side here. Thus we do not call
  * the 16-bit target directly, but call a common entry point instead.
  * This entry function then calls the target according to the target
  * number passed in the DI register.
- * 
+ *
  * Input:  EAX    SEGPTR to the common 16-bit entry point
  *         CX     offset in thunk table (target number * 4)
  *         DX     error return value if execution fails (unclear???)
@@ -603,14 +771,14 @@ DWORD WINAPI ThunkInitLS(
  *   (esp+40)  32-bit arguments
  *     ...
  *   (esp+8)   32 byte of stack space available as buffer
- *   (esp)     8 byte return address for use with 0x66 lret 
- * 
+ *   (esp)     8 byte return address for use with 0x66 lret
+ *
  * The called 16-bit stub uses a 0x66 lret to return to 32-bit code,
  * and uses the EAX register to return a DWORD return value.
- * Thus we need to use a special assembly glue routine 
+ * Thus we need to use a special assembly glue routine
  * (CallRegisterLongProc instead of CallRegisterShortProc).
  *
- * Finally, we return to the caller, popping the arguments off 
+ * Finally, we return to the caller, popping the arguments off
  * the stack.  The number of arguments to be popped is returned
  * in the BL register by the called 16-bit routine.
  *
@@ -622,6 +790,8 @@ void WINAPI Common32ThkLS( CONTEXT86 *context )
 
     memcpy(&context16,context,sizeof(context16));
 
+    context16.SegFs = wine_get_fs();
+    context16.SegGs = wine_get_gs();
     context16.Edi   = LOWORD(context->Ecx);
     context16.SegCs = HIWORD(context->Eax);
     context16.Eip   = LOWORD(context->Eax);
@@ -634,14 +804,15 @@ void WINAPI Common32ThkLS( CONTEXT86 *context )
     if (context->Edx == context->Eip)
         argsize = 6 * 4;
 
-    memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
-            (LPBYTE)context->Esp, argsize );
-
-    wine_call_to_16_regs_long(&context16, argsize + 32);
+    /* Note: the first 32 bytes we copy are just garbage from the 32-bit stack, in order to reserve
+     *       the space. It is safe to do that since the register function prefix has reserved
+     *       a lot more space than that below context->Esp.
+     */
+    WOWCallback16Ex( 0, WCB16_REGS, argsize + 32, (LPBYTE)context->Esp - 32, (DWORD *)&context16 );
     context->Eax = context16.Eax;
 
     /* Clean up caller's stack frame */
-    context->Esp += BL_reg(&context16);
+    context->Esp += LOBYTE(context16.Ebx);
 }
 
 /***********************************************************************
@@ -650,12 +821,12 @@ void WINAPI Common32ThkLS( CONTEXT86 *context )
  * YET Another 32->16 thunk. The difference to Common32ThkLS is that
  * argument processing is done on both the 32-bit and the 16-bit side:
  * The 32-bit side prepares arguments, copying them onto the stack.
- * 
- * When this routine is called, the first word on the stack is the 
+ *
+ * When this routine is called, the first word on the stack is the
  * number of argument bytes prepared by the 32-bit code, and EDX
  * contains the 16-bit target address.
  *
- * The called 16-bit routine is another relaycode, doing further 
+ * The called 16-bit routine is another relaycode, doing further
  * argument processing and then calling the real 16-bit target
  * whose address is stored at [bp-04].
  *
@@ -663,7 +834,7 @@ void WINAPI Common32ThkLS( CONTEXT86 *context )
  * After return from the 16-bit relaycode, the arguments need
  * to be copied *back* to the 32-bit stack, since the 32-bit
  * relaycode processes output parameters.
- * 
+ *
  * Note that we copy twice the number of arguments, since some of the
  * 16-bit relaycodes in SYSTHUNK.DLL directly access the original
  * arguments of the caller!
@@ -678,6 +849,8 @@ void WINAPI OT_32ThkLSF( CONTEXT86 *context )
 
     memcpy(&context16,context,sizeof(context16));
 
+    context16.SegFs = wine_get_fs();
+    context16.SegGs = wine_get_gs();
     context16.SegCs = HIWORD(context->Edx);
     context16.Eip   = LOWORD(context->Edx);
     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
@@ -685,15 +858,12 @@ void WINAPI OT_32ThkLSF( CONTEXT86 *context )
 
     argsize = 2 * *(WORD *)context->Esp + 2;
 
-    memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
-            (LPBYTE)context->Esp, argsize );
-
-    wine_call_to_16_regs_short(&context16, argsize);
+    WOWCallback16Ex( 0, WCB16_REGS, argsize, (void *)context->Esp, (DWORD *)&context16 );
     context->Eax = context16.Eax;
     context->Edx = context16.Edx;
 
     /* Copy modified buffers back to 32-bit stack */
-    memcpy( (LPBYTE)context->Esp, 
+    memcpy( (LPBYTE)context->Esp,
             (LPBYTE)CURRENT_STACK16 - argsize, argsize );
 
     context->Esp +=   LOWORD(context16.Esp) -
@@ -705,34 +875,34 @@ void WINAPI OT_32ThkLSF( CONTEXT86 *context )
  * A thunk setup routine.
  * Expects a pointer to a preinitialized thunkbuffer in the first argument
  * looking like:
- *     00..03:         unknown (pointer, check _41, _43, _46)
- *     04: EB1E                jmp +0x20
- *
- *     06..23:         unknown (space for replacement code, check .90)
- *
- *     24:>E800000000          call offset 29
- *     29:>58                  pop eax            ( target of call )
- *     2A: 2D25000000          sub eax,0x00000025 ( now points to offset 4 )
- *     2F: BAxxxxxxxx          mov edx,xxxxxxxx
- *     34: 68yyyyyyyy          push KERNEL32.90
- *     39: C3                  ret
- *
- *     3A: EB1E                jmp +0x20
- *     3E ... 59:      unknown (space for replacement code?)
- *     5A: E8xxxxxxxx          call <32bitoffset xxxxxxxx>
- *     5F: 5A                  pop edx
- *     60: 81EA25xxxxxx        sub edx, 0x25xxxxxx
- *     66: 52                  push edx
- *     67: 68xxxxxxxx          push xxxxxxxx
- *     6C: 68yyyyyyyy          push KERNEL32.89
- *     71: C3                  ret
- *     72: end?
+ *|    00..03:         unknown (pointer, check _41, _43, _46)
+ *|    04: EB1E                jmp +0x20
+ *|
+ *|    06..23:         unknown (space for replacement code, check .90)
+ *|
+ *|    24:>E800000000          call offset 29
+ *|    29:>58                  pop eax            ( target of call )
+ *|    2A: 2D25000000          sub eax,0x00000025 ( now points to offset 4 )
+ *|    2F: BAxxxxxxxx          mov edx,xxxxxxxx
+ *|    34: 68yyyyyyyy          push KERNEL32.90
+ *|    39: C3                  ret
+ *|
+ *|    3A: EB1E                jmp +0x20
+ *|    3E ... 59:      unknown (space for replacement code?)
+ *|    5A: E8xxxxxxxx          call <32bitoffset xxxxxxxx>
+ *|    5F: 5A                  pop edx
+ *|    60: 81EA25xxxxxx        sub edx, 0x25xxxxxx
+ *|    66: 52                  push edx
+ *|    67: 68xxxxxxxx          push xxxxxxxx
+ *|    6C: 68yyyyyyyy          push KERNEL32.89
+ *|    71: C3                  ret
+ *|    72: end?
  * This function checks if the code is there, and replaces the yyyyyyyy entries
  * by the functionpointers.
  * The thunkbuf looks like:
  *
- *     00: DWORD       length          ? don't know exactly
- *     04: SEGPTR      ptr             ? where does it point to?
+ *|    00: DWORD       length          ? don't know exactly
+ *|    04: SEGPTR      ptr             ? where does it point to?
  * The segpointer ptr is written into the first DWORD of 'thunk'.
  * [ok probably]
  * RETURNS
@@ -745,15 +915,14 @@ LPVOID WINAPI ThunkInitLSF(
        LPCSTR dll16,   /* [in] name of win16 dll */
        LPCSTR dll32    /* [in] name of win32 dll */
 ) {
-       HMODULE hkrnl32 = GetModuleHandleA("KERNEL32");
        LPDWORD         addr,addr2;
 
        /* FIXME: add checks for valid code ... */
        /* write pointers to kernel32.89 and kernel32.90 (+ordinal base of 1) */
-       *(DWORD*)(thunk+0x35) = (DWORD)GetProcAddress(hkrnl32,(LPSTR)90);
-       *(DWORD*)(thunk+0x6D) = (DWORD)GetProcAddress(hkrnl32,(LPSTR)89);
+       *(DWORD*)(thunk+0x35) = (DWORD)GetProcAddress(kernel32_handle,(LPSTR)90);
+       *(DWORD*)(thunk+0x6D) = (DWORD)GetProcAddress(kernel32_handle,(LPSTR)89);
+
 
-       
        if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
                return 0;
 
@@ -766,21 +935,21 @@ LPVOID WINAPI ThunkInitLSF(
 
 /***********************************************************************
  *             FT_PrologPrime                  (KERNEL32.89)
- * 
+ *
  * This function is called from the relay code installed by
- * ThunkInitLSF. It replaces the location from where it was 
+ * ThunkInitLSF. It replaces the location from where it was
  * called by a standard FT_Prolog call stub (which is 'primed'
  * by inserting the correct target table pointer).
  * Finally, it calls that stub.
- * 
+ *
  * Input:  ECX    target number + flags (passed through to FT_Prolog)
- *        (ESP)   offset of location where target table pointer 
+ *        (ESP)   offset of location where target table pointer
  *                is stored, relative to the start of the relay code
  *        (ESP+4) pointer to start of relay code
  *                (this is where the FT_Prolog call stub gets written to)
- * 
+ *
  * Note: The two DWORD arguments get popped off the stack.
- *        
+ *
  */
 void WINAPI FT_PrologPrime( CONTEXT86 *context )
 {
@@ -803,13 +972,13 @@ void WINAPI FT_PrologPrime( CONTEXT86 *context )
 /***********************************************************************
  *             QT_ThunkPrime                   (KERNEL32.90)
  *
- * This function corresponds to FT_PrologPrime, but installs a 
+ * This function corresponds to FT_PrologPrime, but installs a
  * call stub for QT_Thunk instead.
  *
  * Input: (EBP-4) target number (passed through to QT_Thunk)
  *         EDX    target table pointer location offset
  *         EAX    start of relay code
- *      
+ *
  */
 void WINAPI QT_ThunkPrime( CONTEXT86 *context )
 {
@@ -853,7 +1022,7 @@ VOID WINAPI ThunkInitSL(
 }
 
 /**********************************************************************
- *           SSInit            KERNEL.700
+ *           SSInit            (KERNEL.700)
  * RETURNS
  *     TRUE for success.
  */
@@ -863,7 +1032,7 @@ BOOL WINAPI SSInit16()
 }
 
 /**********************************************************************
- *           SSOnBigStack      KERNEL32.87
+ *           SSOnBigStack      (KERNEL32.87)
  * Check if thunking is initialized (ss selector set up etc.)
  * We do that differently, so just return TRUE.
  * [ok]
@@ -877,7 +1046,7 @@ BOOL WINAPI SSOnBigStack()
 }
 
 /**********************************************************************
- *           SSConfirmSmallStack     KERNEL.704
+ *           SSConfirmSmallStack     (KERNEL.704)
  *
  * Abort if not on small stack.
  *
@@ -889,7 +1058,7 @@ void WINAPI SSConfirmSmallStack( CONTEXT86 *context )
 }
 
 /**********************************************************************
- *           SSCall
+ *           SSCall (KERNEL32.88)
  * One of the real thunking functions. This one seems to be for 32<->32
  * thunks. It should probably be capable of crossing processboundaries.
  *
@@ -908,7 +1077,7 @@ DWORD WINAPIV SSCall(
     if(TRACE_ON(thunk))
     {
       DPRINTF("(%ld,0x%08lx,%p,[",nr,flags,fun);
-      for (i=0;i<nr/4;i++) 
+      for (i=0;i<nr/4;i++)
           DPRINTF("0x%08lx,",args[i]);
       DPRINTF("])\n");
     }
@@ -964,19 +1133,20 @@ void WINAPI W32S_BackTo32( CONTEXT86 *context )
 }
 
 /**********************************************************************
- *                     AllocSLCallback         (KERNEL32)
+ *                     AllocSLCallback         (KERNEL32.@)
  *
+ * NOTES
  * Win95 uses some structchains for callbacks. It allocates them
  * in blocks of 100 entries, size 32 bytes each, layout:
  * blockstart:
- *     0:      PTR     nextblockstart
- *     4:      entry   *first;
- *     8:      WORD    sel ( start points to blockstart)
- *     A:      WORD    unknown
+ *|    0:      PTR     nextblockstart
+ *|    4:      entry   *first;
+ *|    8:      WORD    sel ( start points to blockstart)
+ *|    A:      WORD    unknown
  * 100xentry:
- *     00..17:         Code
- *     18:     PDB     *owning_process;
- *     1C:     PTR     blockstart
+ *|    00..17:         Code
+ *|    18:     PDB     *owning_process;
+ *|    1C:     PTR     blockstart
  *
  * We ignore this for now. (Just a note for further developers)
  * FIXME: use this method, so we don't waste selectors...
@@ -984,21 +1154,21 @@ void WINAPI W32S_BackTo32( CONTEXT86 *context )
  * Following code is then generated by AllocSLCallback. The code is 16 bit, so
  * the 0x66 prefix switches from word->long registers.
  *
- *     665A            pop     edx 
- *     6668x arg2 x    pushl   <arg2>
- *     6652            push    edx
- *     EAx arg1 x      jmpf    <arg1>
+ *|    665A            pop     edx
+ *|    6668x arg2 x    pushl   <arg2>
+ *|    6652            push    edx
+ *|    EAx arg1 x      jmpf    <arg1>
  *
  * returns the startaddress of this thunk.
  *
- * Note, that they look very similair to the ones allocates by THUNK_Alloc.
+ * Note, that they look very similar to the ones allocates by THUNK_Alloc.
  * RETURNS
- *     segmented pointer to the start of the thunk
+ *     segmented pointer to the start of the thunk
  */
 DWORD WINAPI
 AllocSLCallback(
-       DWORD finalizer,        /* [in] finalizer function */
-       DWORD callback          /* [in] callback function */
+       DWORD finalizer,        /* [in] Finalizer function */
+       DWORD callback          /* [in] Callback function */
 ) {
        LPBYTE  x,thunk = HeapAlloc( GetProcessHeap(), 0, 32 );
        WORD    sel;
@@ -1016,7 +1186,7 @@ AllocSLCallback(
 }
 
 /**********************************************************************
- *             FreeSLCallback          (KERNEL32.274)
+ *             FreeSLCallback          (KERNEL32.@)
  * Frees the specified 16->32 callback
  */
 void WINAPI
@@ -1031,22 +1201,27 @@ FreeSLCallback(
  *             GetTEBSelectorFS        (KERNEL.475)
  *     Set the 16-bit %fs to the 32-bit %fs (current TEB selector)
  */
-void WINAPI GetTEBSelectorFS16(void) 
+void WINAPI GetTEBSelectorFS16(void)
 {
-    CURRENT_STACK16->fs = __get_fs();
+    CURRENT_STACK16->fs = wine_get_fs();
 }
 
 /**********************************************************************
  *             IsPeFormat              (KERNEL.431)
- * Checks the passed filename if it is a PE format executeable
+ *
+ * Determine if a file is a PE format executable.
+ *
  * RETURNS
  *  TRUE, if it is.
- *  FALSE if not.
+ *  FALSE if the file could not be opened or is not a PE file.
+ *
+ * NOTES
+ *  If fn is given as NULL then the function expects hf16 to be valid.
  */
 BOOL16 WINAPI IsPeFormat16(
-       LPSTR   fn,     /* [in] filename to executeable */
-       HFILE16 hf16    /* [in] open file, if filename is NULL */
-{
+       LPSTR   fn,     /* [in] Filename to the executeable */
+       HFILE16 hf16)   /* [in] An open file handle */
+{
     BOOL ret = FALSE;
     IMAGE_DOS_HEADER mzh;
     OFSTRUCT ofs;
@@ -1067,7 +1242,7 @@ BOOL16 WINAPI IsPeFormat16(
 
 
 /***********************************************************************
- *           K32Thk1632Prolog                  (KERNEL32.492)
+ *           K32Thk1632Prolog                  (KERNEL32.@)
  */
 void WINAPI K32Thk1632Prolog( CONTEXT86 *context )
 {
@@ -1078,7 +1253,7 @@ void WINAPI K32Thk1632Prolog( CONTEXT86 *context )
       This means that SYSTHUNK.DLL itself switches to a 32-bit stack,
       and does a far call to the 32-bit code segment of OLECLI32/OLESVR32.
       Unfortunately, our CallTo/CallFrom mechanism is therefore completely
-      bypassed, which means it will crash the next time the 32-bit OLE 
+      bypassed, which means it will crash the next time the 32-bit OLE
       code thunks down again to 16-bit (this *will* happen!).
 
       The following hack tries to recognize this situation.
@@ -1091,8 +1266,8 @@ void WINAPI K32Thk1632Prolog( CONTEXT86 *context )
 
       If we recognize this situation, we try to simulate the actions
       of our CallTo/CallFrom mechanism by copying the 16-bit stack
-      to our 32-bit stack, creating a proper STACK16FRAME and 
-      updating cur_stack. */ 
+      to our 32-bit stack, creating a proper STACK16FRAME and
+      updating cur_stack. */
 
    if (   code[5] == 0xFF && code[6] == 0x55 && code[7] == 0xFC
        && code[13] == 0x66 && code[14] == 0xCB)
@@ -1128,7 +1303,7 @@ void WINAPI K32Thk1632Prolog( CONTEXT86 *context )
 }
 
 /***********************************************************************
- *           K32Thk1632Epilog                  (KERNEL32.491)
+ *           K32Thk1632Epilog                  (KERNEL32.@)
  */
 void WINAPI K32Thk1632Epilog( CONTEXT86 *context )
 {
@@ -1279,8 +1454,8 @@ UINT WINAPI ThunkConnect16(
 void WINAPI C16ThkSL(CONTEXT86 *context)
 {
     LPBYTE stub = MapSL(context->Eax), x = stub;
-    WORD cs = __get_cs();
-    WORD ds = __get_ds();
+    WORD cs = wine_get_cs();
+    WORD ds = wine_get_ds();
 
     /* We produce the following code:
      *
@@ -1331,7 +1506,7 @@ void WINAPI C16ThkSL01(CONTEXT86 *context)
         struct ThunkDataSL *td = SL16->fpData;
 
         DWORD procAddress = (DWORD)GetProcAddress16(GetModuleHandle16("KERNEL"), (LPCSTR)631);
-        WORD cs = __get_cs();
+        WORD cs = wine_get_cs();
 
         if (!td)
         {
@@ -1376,7 +1551,7 @@ void WINAPI C16ThkSL01(CONTEXT86 *context)
     else
     {
         struct ThunkDataSL *td = (struct ThunkDataSL *)context->Edx;
-        DWORD targetNr = CX_reg(context) / 4;
+        DWORD targetNr = LOWORD(context->Ecx) / 4;
         struct SLTargetDB *tdb;
 
         TRACE("Process %08lx calling target %ld of ThunkDataSL %08lx\n",
@@ -1405,8 +1580,8 @@ void WINAPI C16ThkSL01(CONTEXT86 *context)
         else
         {
             WORD *stack = MapSL( MAKESEGPTR(context->SegSs, LOWORD(context->Esp)) );
-            DX_reg(context) = HIWORD(td->apiDB[targetNr].errorReturnValue);
-            AX_reg(context) = LOWORD(td->apiDB[targetNr].errorReturnValue);
+            context->Edx = (context->Edx & ~0xffff) | HIWORD(td->apiDB[targetNr].errorReturnValue);
+            context->Eax = (context->Eax & ~0xffff) | LOWORD(td->apiDB[targetNr].errorReturnValue);
             context->Eip = stack[2];
             context->SegCs  = stack[3];
             context->Esp += td->apiDB[targetNr].nrArgBytes + 4;
@@ -1446,6 +1621,7 @@ typedef struct _THUNKLET
 #define THUNKLET_TYPE_SL  2
 
 static HANDLE  ThunkletHeap = 0;
+static WORD ThunkletCodeSel;
 static THUNKLET *ThunkletAnchor = NULL;
 
 static FARPROC ThunkletSysthunkGlueLS = 0;
@@ -1454,24 +1630,34 @@ static SEGPTR    ThunkletSysthunkGlueSL = 0;
 static FARPROC ThunkletCallbackGlueLS = 0;
 static SEGPTR    ThunkletCallbackGlueSL = 0;
 
+
+/* map a thunk allocated on ThunkletHeap to a 16-bit pointer */
+inline static SEGPTR get_segptr( void *thunk )
+{
+    if (!thunk) return 0;
+    return MAKESEGPTR( ThunkletCodeSel, (char *)thunk - (char *)ThunkletHeap );
+}
+
 /***********************************************************************
  *           THUNK_Init
  */
-BOOL THUNK_Init(void)
+static BOOL THUNK_Init(void)
 {
     LPBYTE thunk;
 
-    ThunkletHeap = HeapCreate(HEAP_WINE_SEGPTR | HEAP_WINE_CODE16SEG, 0, 0);
+    ThunkletHeap = HeapCreate( 0, 0x10000, 0x10000 );
     if (!ThunkletHeap) return FALSE;
 
+    ThunkletCodeSel = SELECTOR_AllocBlock( (void *)ThunkletHeap, 0x10000, WINE_LDT_FLAGS_CODE );
+
     thunk = HeapAlloc( ThunkletHeap, 0, 5 );
     if (!thunk) return FALSE;
-    
+
     ThunkletSysthunkGlueLS = (FARPROC)thunk;
     *thunk++ = 0x58;                             /* popl eax */
     *thunk++ = 0xC3;                             /* ret      */
 
-    ThunkletSysthunkGlueSL = HEAP_GetSegptr( ThunkletHeap, 0, thunk );
+    ThunkletSysthunkGlueSL = get_segptr( thunk );
     *thunk++ = 0x66; *thunk++ = 0x58;            /* popl eax */
     *thunk++ = 0xCB;                             /* lret     */
 
@@ -1491,15 +1677,15 @@ void WINAPI SetThunkletCallbackGlue16( FARPROC glueLS, SEGPTR glueSL )
 /***********************************************************************
  *     THUNK_FindThunklet
  */
-THUNKLET *THUNK_FindThunklet( DWORD target, DWORD relay, 
-                              DWORD glue, BYTE type ) 
+THUNKLET *THUNK_FindThunklet( DWORD target, DWORD relay,
+                              DWORD glue, BYTE type )
 {
-    THUNKLET *thunk; 
+    THUNKLET *thunk;
 
     for (thunk = ThunkletAnchor; thunk; thunk = thunk->next)
         if (    thunk->type   == type
              && thunk->target == target
-             && thunk->relay  == relay 
+             && thunk->relay  == relay
              && ( type == THUNKLET_TYPE_LS ?
                     ( thunk->glue == glue - (DWORD)&thunk->type )
                   : ( thunk->glue == glue ) ) )
@@ -1511,15 +1697,16 @@ THUNKLET *THUNK_FindThunklet( DWORD target, DWORD relay,
 /***********************************************************************
  *     THUNK_AllocLSThunklet
  */
-FARPROC THUNK_AllocLSThunklet( SEGPTR target, DWORD relay, 
-                                 FARPROC glue, HTASK16 owner ) 
+FARPROC THUNK_AllocLSThunklet( SEGPTR target, DWORD relay,
+                                 FARPROC glue, HTASK16 owner )
 {
     THUNKLET *thunk = THUNK_FindThunklet( (DWORD)target, relay, (DWORD)glue,
                                           THUNKLET_TYPE_LS );
     if (!thunk)
     {
-        TDB *pTask = (TDB*)GlobalLock16( owner );
+        TDB *pTask = GlobalLock16( owner );
 
+        if (!ThunkletHeap) THUNK_Init();
         if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) )
             return 0;
 
@@ -1551,8 +1738,9 @@ SEGPTR THUNK_AllocSLThunklet( FARPROC target, DWORD relay,
                                           THUNKLET_TYPE_SL );
     if (!thunk)
     {
-        TDB *pTask = (TDB*)GlobalLock16( owner );
+        TDB *pTask = GlobalLock16( owner );
 
+        if (!ThunkletHeap) THUNK_Init();
         if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) )
             return 0;
 
@@ -1571,7 +1759,7 @@ SEGPTR THUNK_AllocSLThunklet( FARPROC target, DWORD relay,
         ThunkletAnchor = thunk;
     }
 
-    return HEAP_GetSegptr( ThunkletHeap, 0, thunk );
+    return get_segptr( thunk );
 }
 
 /**********************************************************************
@@ -1599,20 +1787,22 @@ BOOL16 WINAPI IsSLThunklet16( THUNKLET *thunk )
 /***********************************************************************
  *     AllocLSThunkletSysthunk             (KERNEL.607)
  */
-FARPROC WINAPI AllocLSThunkletSysthunk16( SEGPTR target, 
+FARPROC WINAPI AllocLSThunkletSysthunk16( SEGPTR target,
                                           FARPROC relay, DWORD dummy )
 {
-    return THUNK_AllocLSThunklet( (SEGPTR)relay, (DWORD)target, 
+    if (!ThunkletSysthunkGlueLS) THUNK_Init();
+    return THUNK_AllocLSThunklet( (SEGPTR)relay, (DWORD)target,
                                   ThunkletSysthunkGlueLS, GetCurrentTask() );
 }
 
 /***********************************************************************
  *     AllocSLThunkletSysthunk             (KERNEL.608)
  */
-SEGPTR WINAPI AllocSLThunkletSysthunk16( FARPROC target, 
+SEGPTR WINAPI AllocSLThunkletSysthunk16( FARPROC target,
                                        SEGPTR relay, DWORD dummy )
 {
-    return THUNK_AllocSLThunklet( (FARPROC)relay, (DWORD)target, 
+    if (!ThunkletSysthunkGlueSL) THUNK_Init();
+    return THUNK_AllocSLThunklet( (FARPROC)relay, (DWORD)target,
                                   ThunkletSysthunkGlueSL, GetCurrentTask() );
 }
 
@@ -1620,39 +1810,40 @@ SEGPTR WINAPI AllocSLThunkletSysthunk16( FARPROC target,
 /***********************************************************************
  *     AllocLSThunkletCallbackEx           (KERNEL.567)
  */
-FARPROC WINAPI AllocLSThunkletCallbackEx16( SEGPTR target, 
+FARPROC WINAPI AllocLSThunkletCallbackEx16( SEGPTR target,
                                             DWORD relay, HTASK16 task )
 {
     THUNKLET *thunk = MapSL( target );
     if ( !thunk ) return NULL;
 
-    if (   IsSLThunklet16( thunk ) && thunk->relay == relay 
+    if (   IsSLThunklet16( thunk ) && thunk->relay == relay
         && thunk->glue == (DWORD)ThunkletCallbackGlueSL )
         return (FARPROC)thunk->target;
 
-    return THUNK_AllocLSThunklet( target, relay, 
+    return THUNK_AllocLSThunklet( target, relay,
                                   ThunkletCallbackGlueLS, task );
 }
 
 /***********************************************************************
  *     AllocSLThunkletCallbackEx           (KERNEL.568)
  */
-SEGPTR WINAPI AllocSLThunkletCallbackEx16( FARPROC target, 
+SEGPTR WINAPI AllocSLThunkletCallbackEx16( FARPROC target,
                                          DWORD relay, HTASK16 task )
 {
     THUNKLET *thunk = (THUNKLET *)target;
     if ( !thunk ) return 0;
 
-    if (   IsLSThunklet( thunk ) && thunk->relay == relay 
+    if (   IsLSThunklet( thunk ) && thunk->relay == relay
         && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type )
         return (SEGPTR)thunk->target;
 
-    return THUNK_AllocSLThunklet( target, relay, 
+    return THUNK_AllocSLThunklet( target, relay,
                                   ThunkletCallbackGlueSL, task );
 }
 
 /***********************************************************************
- *     AllocLSThunkletCallback             (KERNEL.561) (KERNEL.606)
+ *     AllocLSThunkletCallback             (KERNEL.561)
+ *     AllocLSThunkletCallback_dup         (KERNEL.606)
  */
 FARPROC WINAPI AllocLSThunkletCallback16( SEGPTR target, DWORD relay )
 {
@@ -1660,7 +1851,8 @@ FARPROC WINAPI AllocLSThunkletCallback16( SEGPTR target, DWORD relay )
 }
 
 /***********************************************************************
- *     AllocSLThunkletCallback             (KERNEL.562) (KERNEL.605)
+ *     AllocSLThunkletCallback             (KERNEL.562)
+ *     AllocSLThunkletCallback_dup         (KERNEL.605)
  */
 SEGPTR WINAPI AllocSLThunkletCallback16( FARPROC target, DWORD relay )
 {
@@ -1668,40 +1860,42 @@ SEGPTR WINAPI AllocSLThunkletCallback16( FARPROC target, DWORD relay )
 }
 
 /***********************************************************************
- *     FindLSThunkletCallback              (KERNEL.563) (KERNEL.609)
+ *     FindLSThunkletCallback              (KERNEL.563)
+ *     FindLSThunkletCallback_dup          (KERNEL.609)
  */
 FARPROC WINAPI FindLSThunkletCallback( SEGPTR target, DWORD relay )
 {
     THUNKLET *thunk = MapSL( target );
-    if (   thunk && IsSLThunklet16( thunk ) && thunk->relay == relay 
+    if (   thunk && IsSLThunklet16( thunk ) && thunk->relay == relay
         && thunk->glue == (DWORD)ThunkletCallbackGlueSL )
         return (FARPROC)thunk->target;
 
-    thunk = THUNK_FindThunklet( (DWORD)target, relay, 
-                                (DWORD)ThunkletCallbackGlueLS, 
+    thunk = THUNK_FindThunklet( (DWORD)target, relay,
+                                (DWORD)ThunkletCallbackGlueLS,
                                 THUNKLET_TYPE_LS );
     return (FARPROC)thunk;
 }
 
 /***********************************************************************
- *     FindSLThunkletCallback              (KERNEL.564) (KERNEL.610)
+ *     FindSLThunkletCallback              (KERNEL.564)
+ *     FindSLThunkletCallback_dup          (KERNEL.610)
  */
 SEGPTR WINAPI FindSLThunkletCallback( FARPROC target, DWORD relay )
 {
     THUNKLET *thunk = (THUNKLET *)target;
-    if (   thunk && IsLSThunklet( thunk ) && thunk->relay == relay 
+    if (   thunk && IsLSThunklet( thunk ) && thunk->relay == relay
         && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type )
         return (SEGPTR)thunk->target;
 
-    thunk = THUNK_FindThunklet( (DWORD)target, relay, 
-                                (DWORD)ThunkletCallbackGlueSL, 
+    thunk = THUNK_FindThunklet( (DWORD)target, relay,
+                                (DWORD)ThunkletCallbackGlueSL,
                                 THUNKLET_TYPE_SL );
-    return HEAP_GetSegptr( ThunkletHeap, 0, thunk );
+    return get_segptr( thunk );
 }
 
 
 /***********************************************************************
- *     FreeThunklet16            (KERNEL.611)
+ *     FreeThunklet            (KERNEL.611)
  */
 BOOL16 WINAPI FreeThunklet16( DWORD unused1, DWORD unused2 )
 {
@@ -1723,7 +1917,7 @@ static FARPROC *CBClientRelay32[ N_CBC_TOTAL ];
 /***********************************************************************
  *     RegisterCBClient                    (KERNEL.619)
  */
-INT16 WINAPI RegisterCBClient16( INT16 wCBCId, 
+INT16 WINAPI RegisterCBClient16( INT16 wCBCId,
                                  SEGPTR relay16, FARPROC *relay32 )
 {
     /* Search for free Callback ID */
@@ -1747,11 +1941,11 @@ INT16 WINAPI RegisterCBClient16( INT16 wCBCId,
 /***********************************************************************
  *     UnRegisterCBClient                  (KERNEL.622)
  */
-INT16 WINAPI UnRegisterCBClient16( INT16 wCBCId, 
+INT16 WINAPI UnRegisterCBClient16( INT16 wCBCId,
                                    SEGPTR relay16, FARPROC *relay32 )
 {
-    if (    wCBCId >= N_CBC_FIXED && wCBCId < N_CBC_TOTAL 
-         && CBClientRelay16[ wCBCId ] == relay16 
+    if (    wCBCId >= N_CBC_FIXED && wCBCId < N_CBC_TOTAL
+         && CBClientRelay16[ wCBCId ] == relay16
          && CBClientRelay32[ wCBCId ] == relay32 )
     {
         CBClientRelay16[ wCBCId ] = 0;
@@ -1784,11 +1978,11 @@ void WINAPI CBClientGlueSL( CONTEXT86 *context )
     SEGPTR stackSeg = stack16_push( 12 );
     LPWORD stackLin = MapSL( stackSeg );
     SEGPTR glue, *glueTab;
-    
-    stackLin[3] = BP_reg( context );
-    stackLin[2] = SI_reg( context );
-    stackLin[1] = DI_reg( context );
-    stackLin[0] = context->SegDs;
+
+    stackLin[3] = (WORD)context->Ebp;
+    stackLin[2] = (WORD)context->Esi;
+    stackLin[1] = (WORD)context->Edi;
+    stackLin[0] = (WORD)context->SegDs;
 
     context->Ebp = OFFSETOF( stackSeg ) + 6;
     context->Esp = OFFSETOF( stackSeg ) - 4;
@@ -1809,7 +2003,7 @@ void WINAPI CBClientThunkSL( CONTEXT86 *context )
 {
     /* Call 32-bit relay code */
 
-    LPWORD args = MapSL( MAKESEGPTR( context->SegSs, BP_reg( context ) ) );
+    LPWORD args = MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Ebp) ) );
     FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ];
 
     context->Eax = CALL32_CBClient( proc, args, &context->Esi );
@@ -1823,7 +2017,7 @@ void WINAPI CBClientThunkSLEx( CONTEXT86 *context )
 {
     /* Call 32-bit relay code */
 
-    LPWORD args = MapSL( MAKESEGPTR( context->SegSs, BP_reg( context ) ) );
+    LPWORD args = MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Ebp) ) );
     FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ];
     INT nArgs;
     LPWORD stackLin;
@@ -1832,9 +2026,9 @@ void WINAPI CBClientThunkSLEx( CONTEXT86 *context )
 
     /* Restore registers saved by CBClientGlueSL */
     stackLin = (LPWORD)((LPBYTE)CURRENT_STACK16 + sizeof(STACK16FRAME) - 4);
-    BP_reg( context ) = stackLin[3];
-    SI_reg( context ) = stackLin[2];
-    DI_reg( context ) = stackLin[1];
+    context->Ebp = (context->Ebp & ~0xffff) | stackLin[3];
+    context->Esi = (context->Esi & ~0xffff) | stackLin[2];
+    context->Edi = (context->Edi & ~0xffff) | stackLin[1];
     context->SegDs = stackLin[0];
     context->Esp += 16+nArgs;
 
@@ -1845,27 +2039,42 @@ void WINAPI CBClientThunkSLEx( CONTEXT86 *context )
 
 
 /***********************************************************************
- *           Get16DLLAddress       (KERNEL32)
+ *           Get16DLLAddress       (KERNEL32.@)
  *
  * This function is used by a Win32s DLL if it wants to call a Win16 function.
  * A 16:16 segmented pointer to the function is returned.
  * Written without any docu.
  */
-SEGPTR WINAPI Get16DLLAddress(HMODULE handle, LPSTR func_name) {
-       HANDLE ThunkHeap = HeapCreate(HEAP_WINE_SEGPTR | HEAP_WINE_CODESEG, 0, 64);
-        LPBYTE x;
-       LPVOID tmpheap = HeapAlloc(ThunkHeap, 0, 32);
-       SEGPTR thunk = HEAP_GetSegptr(ThunkHeap, 0, tmpheap);
-       DWORD proc_16;
+SEGPTR WINAPI Get16DLLAddress(HMODULE16 handle, LPSTR func_name)
+{
+    static WORD code_sel32;
+    FARPROC16 proc_16;
+    LPBYTE thunk;
+
+    if (!code_sel32)
+    {
+        if (!ThunkletHeap) THUNK_Init();
+        code_sel32 = SELECTOR_AllocBlock( (void *)ThunkletHeap, 0x10000,
+                                          WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
+        if (!code_sel32) return 0;
+    }
+    if (!(thunk = HeapAlloc( ThunkletHeap, 0, 32 ))) return 0;
 
-        if (!handle) handle=GetModuleHandle16("WIN32S16");
-        proc_16 = (DWORD)GetProcAddress16(handle, func_name);
+    if (!handle) handle = GetModuleHandle16("WIN32S16");
+    proc_16 = GetProcAddress16(handle, func_name);
 
-        x=MapSL(thunk);
-        *x++=0xba; *(DWORD*)x=proc_16;x+=4;             /* movl proc_16, $edx */
-        *x++=0xea; *(DWORD*)x=(DWORD)GetProcAddress(GetModuleHandleA("KERNEL32"),"QT_Thunk");x+=4;     /* jmpl QT_Thunk */
-       *(WORD*)x=__get_cs();
-        return thunk;
+    /* movl proc_16, $edx */
+    *thunk++ = 0xba;
+    *(FARPROC16 *)thunk = proc_16;
+    thunk += sizeof(FARPROC16);
+
+     /* jmpl QT_Thunk */
+    *thunk++ = 0xea;
+    *(FARPROC *)thunk = GetProcAddress(kernel32_handle,"QT_Thunk");
+    thunk += sizeof(FARPROC16);
+    *(WORD *)thunk = wine_get_cs();
+
+    return MAKESEGPTR( code_sel32, (char *)thunk - (char *)ThunkletHeap );
 }
 
 
@@ -1895,8 +2104,7 @@ LPVOID WINAPI GetPK16SysVar(void)
  */
 void WINAPI CommonUnimpStub( CONTEXT86 *context )
 {
-    if (context->Eax)
-        MESSAGE( "*** Unimplemented Win32 API: %s\n", (LPSTR)context->Eax );
+    FIXME("generic stub: %s\n", ((LPSTR)context->Eax ? (LPSTR)context->Eax : "?"));
 
     switch ((context->Ecx >> 4) & 0x0f)
     {
@@ -1920,16 +2128,20 @@ void WINAPI HouseCleanLogicallyDeadHandles(void)
 }
 
 /**********************************************************************
- *             _KERNEL32_100
+ *             @ (KERNEL32.100)
  */
 BOOL WINAPI _KERNEL32_100(HANDLE threadid,DWORD exitcode,DWORD x)
 {
-       FIXME("(%d,%ld,0x%08lx): stub\n",threadid,exitcode,x);
+       FIXME("(%p,%ld,0x%08lx): stub\n",threadid,exitcode,x);
        return TRUE;
 }
 
 /**********************************************************************
- *             _KERNEL32_99
+ *             @ (KERNEL32.99)
+ *
+ * Checks whether the clock has to be switched from daylight
+ * savings time to standard time or vice versa.
+ *
  */
 DWORD WINAPI _KERNEL32_99(DWORD x)
 {
@@ -1972,7 +2184,7 @@ void WINAPI Catch16( LPCATCHBUF lpbuf, CONTEXT86 *context )
     lpbuf[6] = context->SegDs;
     lpbuf[7] = 0;
     lpbuf[8] = context->SegSs;
-    AX_reg(context) = 0;  /* Return 0 */
+    context->Eax &= ~0xffff;  /* Return 0 */
 }
 
 
@@ -1986,16 +2198,15 @@ void WINAPI Throw16( LPCATCHBUF lpbuf, INT16 retval, CONTEXT86 *context )
 {
     STACK16FRAME *pFrame;
     STACK32FRAME *frame32;
-    TEB *teb = NtCurrentTeb();
 
-    AX_reg(context) = retval;
+    context->Eax = (context->Eax & ~0xffff) | (WORD)retval;
 
     /* Find the frame32 corresponding to the frame16 we are jumping to */
-    pFrame = THREAD_STACK16(teb);
+    pFrame = CURRENT_STACK16;
     frame32 = pFrame->frame32;
     while (frame32 && frame32->frame16)
     {
-        if (OFFSETOF(frame32->frame16) < OFFSETOF(teb->cur_stack))
+        if (OFFSETOF(frame32->frame16) < OFFSETOF(NtCurrentTeb()->cur_stack))
             break;  /* Something strange is going on */
         if (OFFSETOF(frame32->frame16) > lpbuf[2])
         {
@@ -2005,6 +2216,7 @@ void WINAPI Throw16( LPCATCHBUF lpbuf, INT16 retval, CONTEXT86 *context )
         }
         frame32 = ((STACK16FRAME *)MapSL(frame32->frame16))->frame32;
     }
+    RtlUnwind( &pFrame->frame32->frame, NULL, NULL, 0 );
 
     context->Eip = lpbuf[0];
     context->SegCs  = lpbuf[1];