SetEnvironmentVariableA(NULL, NULL) crashes on Win98 -> removed.
[wine] / dlls / kernel / thunk.c
1 /*
2  * KERNEL32 thunks and other undocumented stuff
3  *
4  * Copyright 1996, 1997 Alexandre Julliard
5  * Copyright 1997, 1998 Marcus Meissner
6  * Copyright 1998       Ulrich Weigand
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26 #include <sys/types.h>
27 #include <stdio.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winerror.h"
35 #include "winternl.h"
36 #include "wine/winbase16.h"
37
38 #include "wine/debug.h"
39 #include "wine/library.h"
40 #include "flatthunk.h"
41 #include "heap.h"
42 #include "module.h"
43 #include "miscemu.h"
44 #include "selectors.h"
45 #include "stackframe.h"
46 #include "task.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(thunk);
49
50 #ifdef __i386__
51 extern void __wine_call_from_16_thunk();
52 #else
53 static void __wine_call_from_16_thunk() { }
54 #endif
55
56 /***********************************************************************
57  *                                                                     *
58  *                 Win95 internal thunks                               *
59  *                                                                     *
60  ***********************************************************************/
61
62 /***********************************************************************
63  *           LogApiThk    (KERNEL.423)
64  */
65 void WINAPI LogApiThk( LPSTR func )
66 {
67     TRACE( "%s\n", debugstr_a(func) );
68 }
69
70 /***********************************************************************
71  *           LogApiThkLSF    (KERNEL32.42)
72  *
73  * NOTE: needs to preserve all registers!
74  */
75 void WINAPI LogApiThkLSF( LPSTR func, CONTEXT86 *context )
76 {
77     TRACE( "%s\n", debugstr_a(func) );
78 }
79
80 /***********************************************************************
81  *           LogApiThkSL    (KERNEL32.44)
82  *
83  * NOTE: needs to preserve all registers!
84  */
85 void WINAPI LogApiThkSL( LPSTR func, CONTEXT86 *context )
86 {
87     TRACE( "%s\n", debugstr_a(func) );
88 }
89
90 /***********************************************************************
91  *           LogCBThkSL    (KERNEL32.47)
92  *
93  * NOTE: needs to preserve all registers!
94  */
95 void WINAPI LogCBThkSL( LPSTR func, CONTEXT86 *context )
96 {
97     TRACE( "%s\n", debugstr_a(func) );
98 }
99
100 /***********************************************************************
101  * Generates a FT_Prolog call.
102  *
103  *  0FB6D1                  movzbl edx,cl
104  *  8B1495xxxxxxxx          mov edx,[4*edx + targetTable]
105  *  68xxxxxxxx              push FT_Prolog
106  *  C3                      lret
107  */
108 static void _write_ftprolog(LPBYTE relayCode ,DWORD *targetTable) {
109         LPBYTE  x;
110
111         x       = relayCode;
112         *x++    = 0x0f;*x++=0xb6;*x++=0xd1; /* movzbl edx,cl */
113         *x++    = 0x8B;*x++=0x14;*x++=0x95;*(DWORD**)x= targetTable;
114         x+=4;   /* mov edx, [4*edx + targetTable] */
115         *x++    = 0x68; *(DWORD*)x = (DWORD)GetProcAddress(GetModuleHandleA("KERNEL32"),"FT_Prolog");
116         x+=4;   /* push FT_Prolog */
117         *x++    = 0xC3;         /* lret */
118         /* fill rest with 0xCC / int 3 */
119 }
120
121 /***********************************************************************
122  *      _write_qtthunk                                  (internal)
123  * Generates a QT_Thunk style call.
124  *
125  *  33C9                    xor ecx, ecx
126  *  8A4DFC                  mov cl , [ebp-04]
127  *  8B148Dxxxxxxxx          mov edx, [4*ecx + targetTable]
128  *  B8yyyyyyyy              mov eax, QT_Thunk
129  *  FFE0                    jmp eax
130  */
131 static void _write_qtthunk(
132         LPBYTE relayCode,       /* [in] start of QT_Thunk stub */
133         DWORD *targetTable      /* [in] start of thunk (for index lookup) */
134 ) {
135         LPBYTE  x;
136
137         x       = relayCode;
138         *x++    = 0x33;*x++=0xC9; /* xor ecx,ecx */
139         *x++    = 0x8A;*x++=0x4D;*x++=0xFC; /* movb cl,[ebp-04] */
140         *x++    = 0x8B;*x++=0x14;*x++=0x8D;*(DWORD**)x= targetTable;
141         x+=4;   /* mov edx, [4*ecx + targetTable */
142         *x++    = 0xB8; *(DWORD*)x = (DWORD)GetProcAddress(GetModuleHandleA("KERNEL32"),"QT_Thunk");
143         x+=4;   /* mov eax , QT_Thunk */
144         *x++    = 0xFF; *x++ = 0xE0;    /* jmp eax */
145         /* should fill the rest of the 32 bytes with 0xCC */
146 }
147
148 /***********************************************************************
149  *           _loadthunk
150  */
151 static LPVOID _loadthunk(LPCSTR module, LPCSTR func, LPCSTR module32,
152                          struct ThunkDataCommon *TD32, DWORD checksum)
153 {
154     struct ThunkDataCommon *TD16;
155     HMODULE16 hmod;
156     int ordinal;
157
158     if ((hmod = LoadLibrary16(module)) <= 32)
159     {
160         ERR("(%s, %s, %s): Unable to load '%s', error %d\n",
161                    module, func, module32, module, hmod);
162         return 0;
163     }
164
165     if (   !(ordinal = NE_GetOrdinal(hmod, func))
166         || !(TD16 = MapSL((SEGPTR)NE_GetEntryPointEx(hmod, ordinal, FALSE))))
167     {
168         ERR("Unable to find thunk data '%s' in %s, required by %s (conflicting/incorrect DLL versions !?).\n",
169                    func, module, module32);
170         return 0;
171     }
172
173     if (TD32 && memcmp(TD16->magic, TD32->magic, 4))
174     {
175         ERR("(%s, %s, %s): Bad magic %c%c%c%c (should be %c%c%c%c)\n",
176                    module, func, module32,
177                    TD16->magic[0], TD16->magic[1], TD16->magic[2], TD16->magic[3],
178                    TD32->magic[0], TD32->magic[1], TD32->magic[2], TD32->magic[3]);
179         return 0;
180     }
181
182     if (TD32 && TD16->checksum != TD32->checksum)
183     {
184         ERR("(%s, %s, %s): Wrong checksum %08lx (should be %08lx)\n",
185                    module, func, module32, TD16->checksum, TD32->checksum);
186         return 0;
187     }
188
189     if (!TD32 && checksum && checksum != *(LPDWORD)TD16)
190     {
191         ERR("(%s, %s, %s): Wrong checksum %08lx (should be %08lx)\n",
192                    module, func, module32, *(LPDWORD)TD16, checksum);
193         return 0;
194     }
195
196     return TD16;
197 }
198
199 /***********************************************************************
200  *           GetThunkStuff    (KERNEL32.53)
201  */
202 LPVOID WINAPI GetThunkStuff(LPSTR module, LPSTR func)
203 {
204     return _loadthunk(module, func, "<kernel>", NULL, 0L);
205 }
206
207 /***********************************************************************
208  *           GetThunkBuff    (KERNEL32.52)
209  * Returns a pointer to ThkBuf in the 16bit library SYSTHUNK.DLL.
210  */
211 LPVOID WINAPI GetThunkBuff(void)
212 {
213     return GetThunkStuff("SYSTHUNK.DLL", "ThkBuf");
214 }
215
216 /***********************************************************************
217  *              ThunkConnect32          (KERNEL32.@)
218  * Connects a 32bit and a 16bit thunkbuffer.
219  */
220 UINT WINAPI ThunkConnect32(
221         struct ThunkDataCommon *TD,  /* [in/out] thunkbuffer */
222         LPSTR thunkfun16,            /* [in] win16 thunkfunction */
223         LPSTR module16,              /* [in] name of win16 dll */
224         LPSTR module32,              /* [in] name of win32 dll */
225         HMODULE hmod32,            /* [in] hmodule of win32 dll */
226         DWORD dwReason               /* [in] initialisation argument */
227 ) {
228     BOOL directionSL;
229
230     if (!strncmp(TD->magic, "SL01", 4))
231     {
232         directionSL = TRUE;
233
234         TRACE("SL01 thunk %s (%lx) <- %s (%s), Reason: %ld\n",
235                      module32, (DWORD)TD, module16, thunkfun16, dwReason);
236     }
237     else if (!strncmp(TD->magic, "LS01", 4))
238     {
239         directionSL = FALSE;
240
241         TRACE("LS01 thunk %s (%lx) -> %s (%s), Reason: %ld\n",
242                      module32, (DWORD)TD, module16, thunkfun16, dwReason);
243     }
244     else
245     {
246         ERR("Invalid magic %c%c%c%c\n",
247                    TD->magic[0], TD->magic[1], TD->magic[2], TD->magic[3]);
248         return 0;
249     }
250
251     switch (dwReason)
252     {
253         case DLL_PROCESS_ATTACH:
254         {
255             struct ThunkDataCommon *TD16;
256             if (!(TD16 = _loadthunk(module16, thunkfun16, module32, TD, 0L)))
257                 return 0;
258
259             if (directionSL)
260             {
261                 struct ThunkDataSL32 *SL32 = (struct ThunkDataSL32 *)TD;
262                 struct ThunkDataSL16 *SL16 = (struct ThunkDataSL16 *)TD16;
263                 struct SLTargetDB *tdb;
264
265                 if (SL16->fpData == NULL)
266                 {
267                     ERR("ThunkConnect16 was not called!\n");
268                     return 0;
269                 }
270
271                 SL32->data = SL16->fpData;
272
273                 tdb = HeapAlloc(GetProcessHeap(), 0, sizeof(*tdb));
274                 tdb->process = GetCurrentProcessId();
275                 tdb->targetTable = (DWORD *)(thunkfun16 + SL32->offsetTargetTable);
276
277                 tdb->next = SL32->data->targetDB;   /* FIXME: not thread-safe! */
278                 SL32->data->targetDB = tdb;
279
280                 TRACE("Process %08lx allocated TargetDB entry for ThunkDataSL %08lx\n",
281                              GetCurrentProcessId(), (DWORD)SL32->data);
282             }
283             else
284             {
285                 struct ThunkDataLS32 *LS32 = (struct ThunkDataLS32 *)TD;
286                 struct ThunkDataLS16 *LS16 = (struct ThunkDataLS16 *)TD16;
287
288                 LS32->targetTable = MapSL(LS16->targetTable);
289
290                 /* write QT_Thunk and FT_Prolog stubs */
291                 _write_qtthunk ((LPBYTE)TD + LS32->offsetQTThunk,  LS32->targetTable);
292                 _write_ftprolog((LPBYTE)TD + LS32->offsetFTProlog, LS32->targetTable);
293             }
294             break;
295         }
296
297         case DLL_PROCESS_DETACH:
298             /* FIXME: cleanup */
299             break;
300     }
301
302     return 1;
303 }
304
305 /**********************************************************************
306  *              QT_Thunk                        (KERNEL32.@)
307  *
308  * The target address is in EDX.
309  * The 16bit arguments start at ESP.
310  * The number of 16bit argument bytes is EBP-ESP-0x40 (64 Byte thunksetup).
311  * So the stack layout is 16bit argument bytes and then the 64 byte
312  * scratch buffer.
313  * The scratch buffer is used as work space by Windows' QT_Thunk
314  * function.
315  * As the programs unfortunately don't always provide a fixed size
316  * scratch buffer (danger, stack corruption ahead !!), we simply resort
317  * to copying over the whole EBP-ESP range to the 16bit stack
318  * (as there's no way to safely figure out the param count
319  * due to this misbehaviour of some programs).
320  * [ok]
321  *
322  * See DDJ article 9614c for a very good description of QT_Thunk (also
323  * available online !).
324  *
325  * FIXME: DDJ talks of certain register usage rules; I'm not sure
326  * whether we cover this 100%.
327  */
328 void WINAPI QT_Thunk( CONTEXT86 *context )
329 {
330     CONTEXT86 context16;
331     DWORD argsize;
332
333     memcpy(&context16,context,sizeof(context16));
334
335     context16.SegCs = HIWORD(context->Edx);
336     context16.Eip   = LOWORD(context->Edx);
337     /* point EBP to the STACK16FRAME on the stack
338      * for the call_to_16 to set up the register content on calling */
339     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
340                            + (WORD)&((STACK16FRAME*)0)->bp;
341
342     /*
343      * used to be (problematic):
344      * argsize = context->Ebp - context->Esp - 0x40;
345      * due to some programs abusing the API, we better assume the full
346      * EBP - ESP range for copying instead: */
347     argsize = context->Ebp - context->Esp;
348
349     /* ok, too much is insane; let's limit param count a bit again */
350     if (argsize > 64)
351         argsize = 64; /* 32 WORDs */
352
353     memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
354             (LPBYTE)context->Esp, argsize );
355
356     /* let's hope call_to_16 won't mind getting called with such a
357      * potentially bogus large number of arguments */
358     wine_call_to_16_regs_short( &context16, argsize );
359     context->Eax = context16.Eax;
360     context->Edx = context16.Edx;
361     context->Ecx = context16.Ecx;
362
363     /* make sure to update the Win32 ESP, too, in order to throw away
364      * the number of parameters that the Win16 function
365      * accepted (that it popped from the corresponding Win16 stack) */
366     context->Esp +=   LOWORD(context16.Esp) -
367                         ( OFFSETOF( NtCurrentTeb()->cur_stack ) - argsize );
368 }
369
370
371 /**********************************************************************
372  *              FT_Prolog                       (KERNEL32.@)
373  *
374  * The set of FT_... thunk routines is used instead of QT_Thunk,
375  * if structures have to be converted from 32-bit to 16-bit
376  * (change of member alignment, conversion of members).
377  *
378  * The thunk function (as created by the thunk compiler) calls
379  * FT_Prolog at the beginning, to set up a stack frame and
380  * allocate a 64 byte buffer on the stack.
381  * The input parameters (target address and some flags) are
382  * saved for later use by FT_Thunk.
383  *
384  * Input:  EDX  16-bit target address (SEGPTR)
385  *         CX   bits  0..7   target number (in target table)
386  *              bits  8..9   some flags (unclear???)
387  *              bits 10..15  number of DWORD arguments
388  *
389  * Output: A new stackframe is created, and a 64 byte buffer
390  *         allocated on the stack. The layout of the stack
391  *         on return is as follows:
392  *
393  *  (ebp+4)  return address to caller of thunk function
394  *  (ebp)    old EBP
395  *  (ebp-4)  saved EBX register of caller
396  *  (ebp-8)  saved ESI register of caller
397  *  (ebp-12) saved EDI register of caller
398  *  (ebp-16) saved ECX register, containing flags
399  *  (ebp-20) bitmap containing parameters that are to be converted
400  *           by FT_Thunk; it is initialized to 0 by FT_Prolog and
401  *           filled in by the thunk code before calling FT_Thunk
402  *  (ebp-24)
403  *    ...    (unclear)
404  *  (ebp-44)
405  *  (ebp-48) saved EAX register of caller (unclear, never restored???)
406  *  (ebp-52) saved EDX register, containing 16-bit thunk target
407  *  (ebp-56)
408  *    ...    (unclear)
409  *  (ebp-64)
410  *
411  *  ESP is EBP-64 after return.
412  *
413  */
414
415 void WINAPI FT_Prolog( CONTEXT86 *context )
416 {
417     /* Build stack frame */
418     stack32_push(context, context->Ebp);
419     context->Ebp = context->Esp;
420
421     /* Allocate 64-byte Thunk Buffer */
422     context->Esp -= 64;
423     memset((char *)context->Esp, '\0', 64);
424
425     /* Store Flags (ECX) and Target Address (EDX) */
426     /* Save other registers to be restored later */
427     *(DWORD *)(context->Ebp -  4) = context->Ebx;
428     *(DWORD *)(context->Ebp -  8) = context->Esi;
429     *(DWORD *)(context->Ebp - 12) = context->Edi;
430     *(DWORD *)(context->Ebp - 16) = context->Ecx;
431
432     *(DWORD *)(context->Ebp - 48) = context->Eax;
433     *(DWORD *)(context->Ebp - 52) = context->Edx;
434 }
435
436 /**********************************************************************
437  *              FT_Thunk                        (KERNEL32.@)
438  *
439  * This routine performs the actual call to 16-bit code,
440  * similar to QT_Thunk. The differences are:
441  *  - The call target is taken from the buffer created by FT_Prolog
442  *  - Those arguments requested by the thunk code (by setting the
443  *    corresponding bit in the bitmap at EBP-20) are converted
444  *    from 32-bit pointers to segmented pointers (those pointers
445  *    are guaranteed to point to structures copied to the stack
446  *    by the thunk code, so we always use the 16-bit stack selector
447  *    for those addresses).
448  *
449  *    The bit #i of EBP-20 corresponds here to the DWORD starting at
450  *    ESP+4 + 2*i.
451  *
452  * FIXME: It is unclear what happens if there are more than 32 WORDs
453  *        of arguments, so that the single DWORD bitmap is no longer
454  *        sufficient ...
455  */
456
457 void WINAPI FT_Thunk( CONTEXT86 *context )
458 {
459     DWORD mapESPrelative = *(DWORD *)(context->Ebp - 20);
460     DWORD callTarget     = *(DWORD *)(context->Ebp - 52);
461
462     CONTEXT86 context16;
463     DWORD i, argsize;
464     LPBYTE newstack, oldstack;
465
466     memcpy(&context16,context,sizeof(context16));
467
468     context16.SegCs = HIWORD(callTarget);
469     context16.Eip   = LOWORD(callTarget);
470     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
471                            + (WORD)&((STACK16FRAME*)0)->bp;
472
473     argsize  = context->Ebp-context->Esp-0x40;
474     newstack = (LPBYTE)CURRENT_STACK16 - argsize;
475     oldstack = (LPBYTE)context->Esp;
476
477     memcpy( newstack, oldstack, argsize );
478
479     for (i = 0; i < 32; i++)    /* NOTE: What about > 32 arguments? */
480         if (mapESPrelative & (1 << i))
481         {
482             SEGPTR *arg = (SEGPTR *)(newstack + 2*i);
483             *arg = MAKESEGPTR(SELECTOROF(NtCurrentTeb()->cur_stack),
484                               OFFSETOF(NtCurrentTeb()->cur_stack) - argsize
485                               + (*(LPBYTE *)arg - oldstack));
486         }
487
488     wine_call_to_16_regs_short( &context16, argsize );
489     context->Eax = context16.Eax;
490     context->Edx = context16.Edx;
491     context->Ecx = context16.Ecx;
492
493     context->Esp +=   LOWORD(context16.Esp) -
494                         ( OFFSETOF( NtCurrentTeb()->cur_stack ) - argsize );
495
496     /* Copy modified buffers back to 32-bit stack */
497     memcpy( oldstack, newstack, argsize );
498 }
499
500 /**********************************************************************
501  *              FT_ExitNN               (KERNEL32.218 - 232)
502  *
503  * One of the FT_ExitNN functions is called at the end of the thunk code.
504  * It removes the stack frame created by FT_Prolog, moves the function
505  * return from EBX to EAX (yes, FT_Thunk did use EAX for the return
506  * value, but the thunk code has moved it from EAX to EBX in the
507  * meantime ... :-), restores the caller's EBX, ESI, and EDI registers,
508  * and perform a return to the CALLER of the thunk code (while removing
509  * the given number of arguments from the caller's stack).
510  */
511
512 static void FT_Exit(CONTEXT86 *context, int nPopArgs)
513 {
514     /* Return value is in EBX */
515     context->Eax = context->Ebx;
516
517     /* Restore EBX, ESI, and EDI registers */
518     context->Ebx = *(DWORD *)(context->Ebp -  4);
519     context->Esi = *(DWORD *)(context->Ebp -  8);
520     context->Edi = *(DWORD *)(context->Ebp - 12);
521
522     /* Clean up stack frame */
523     context->Esp = context->Ebp;
524     context->Ebp = stack32_pop(context);
525
526     /* Pop return address to CALLER of thunk code */
527     context->Eip = stack32_pop(context);
528     /* Remove arguments */
529     context->Esp += nPopArgs;
530 }
531
532 /***********************************************************************
533  *              FT_Exit0 (KERNEL32.@)
534  */
535 void WINAPI FT_Exit0 (CONTEXT86 *context) { FT_Exit(context,  0); }
536
537 /***********************************************************************
538  *              FT_Exit4 (KERNEL32.@)
539  */
540 void WINAPI FT_Exit4 (CONTEXT86 *context) { FT_Exit(context,  4); }
541
542 /***********************************************************************
543  *              FT_Exit8 (KERNEL32.@)
544  */
545 void WINAPI FT_Exit8 (CONTEXT86 *context) { FT_Exit(context,  8); }
546
547 /***********************************************************************
548  *              FT_Exit12 (KERNEL32.@)
549  */
550 void WINAPI FT_Exit12(CONTEXT86 *context) { FT_Exit(context, 12); }
551
552 /***********************************************************************
553  *              FT_Exit16 (KERNEL32.@)
554  */
555 void WINAPI FT_Exit16(CONTEXT86 *context) { FT_Exit(context, 16); }
556
557 /***********************************************************************
558  *              FT_Exit20 (KERNEL32.@)
559  */
560 void WINAPI FT_Exit20(CONTEXT86 *context) { FT_Exit(context, 20); }
561
562 /***********************************************************************
563  *              FT_Exit24 (KERNEL32.@)
564  */
565 void WINAPI FT_Exit24(CONTEXT86 *context) { FT_Exit(context, 24); }
566
567 /***********************************************************************
568  *              FT_Exit28 (KERNEL32.@)
569  */
570 void WINAPI FT_Exit28(CONTEXT86 *context) { FT_Exit(context, 28); }
571
572 /***********************************************************************
573  *              FT_Exit32 (KERNEL32.@)
574  */
575 void WINAPI FT_Exit32(CONTEXT86 *context) { FT_Exit(context, 32); }
576
577 /***********************************************************************
578  *              FT_Exit36 (KERNEL32.@)
579  */
580 void WINAPI FT_Exit36(CONTEXT86 *context) { FT_Exit(context, 36); }
581
582 /***********************************************************************
583  *              FT_Exit40 (KERNEL32.@)
584  */
585 void WINAPI FT_Exit40(CONTEXT86 *context) { FT_Exit(context, 40); }
586
587 /***********************************************************************
588  *              FT_Exit44 (KERNEL32.@)
589  */
590 void WINAPI FT_Exit44(CONTEXT86 *context) { FT_Exit(context, 44); }
591
592 /***********************************************************************
593  *              FT_Exit48 (KERNEL32.@)
594  */
595 void WINAPI FT_Exit48(CONTEXT86 *context) { FT_Exit(context, 48); }
596
597 /***********************************************************************
598  *              FT_Exit52 (KERNEL32.@)
599  */
600 void WINAPI FT_Exit52(CONTEXT86 *context) { FT_Exit(context, 52); }
601
602 /***********************************************************************
603  *              FT_Exit56 (KERNEL32.@)
604  */
605 void WINAPI FT_Exit56(CONTEXT86 *context) { FT_Exit(context, 56); }
606
607 /***********************************************************************
608  *              ThunkInitLS     (KERNEL32.43)
609  * A thunkbuffer link routine
610  * The thunkbuf looks like:
611  *
612  *      00: DWORD       length          ? don't know exactly
613  *      04: SEGPTR      ptr             ? where does it point to?
614  * The pointer ptr is written into the first DWORD of 'thunk'.
615  * (probably correctly implemented)
616  * [ok probably]
617  * RETURNS
618  *      segmented pointer to thunk?
619  */
620 DWORD WINAPI ThunkInitLS(
621         LPDWORD thunk,  /* [in] win32 thunk */
622         LPCSTR thkbuf,  /* [in] thkbuffer name in win16 dll */
623         DWORD len,      /* [in] thkbuffer length */
624         LPCSTR dll16,   /* [in] name of win16 dll */
625         LPCSTR dll32    /* [in] name of win32 dll (FIXME: not used?) */
626 ) {
627         LPDWORD         addr;
628
629         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
630                 return 0;
631
632         if (!addr[1])
633                 return 0;
634         *(DWORD*)thunk = addr[1];
635
636         return addr[1];
637 }
638
639 /***********************************************************************
640  *              Common32ThkLS   (KERNEL32.45)
641  *
642  * This is another 32->16 thunk, independent of the QT_Thunk/FT_Thunk
643  * style thunks. The basic difference is that the parameter conversion
644  * is done completely on the *16-bit* side here. Thus we do not call
645  * the 16-bit target directly, but call a common entry point instead.
646  * This entry function then calls the target according to the target
647  * number passed in the DI register.
648  *
649  * Input:  EAX    SEGPTR to the common 16-bit entry point
650  *         CX     offset in thunk table (target number * 4)
651  *         DX     error return value if execution fails (unclear???)
652  *         EDX.HI number of DWORD parameters
653  *
654  * (Note that we need to move the thunk table offset from CX to DI !)
655  *
656  * The called 16-bit stub expects its stack to look like this:
657  *     ...
658  *   (esp+40)  32-bit arguments
659  *     ...
660  *   (esp+8)   32 byte of stack space available as buffer
661  *   (esp)     8 byte return address for use with 0x66 lret
662  *
663  * The called 16-bit stub uses a 0x66 lret to return to 32-bit code,
664  * and uses the EAX register to return a DWORD return value.
665  * Thus we need to use a special assembly glue routine
666  * (CallRegisterLongProc instead of CallRegisterShortProc).
667  *
668  * Finally, we return to the caller, popping the arguments off
669  * the stack.  The number of arguments to be popped is returned
670  * in the BL register by the called 16-bit routine.
671  *
672  */
673 void WINAPI Common32ThkLS( CONTEXT86 *context )
674 {
675     CONTEXT86 context16;
676     DWORD argsize;
677
678     memcpy(&context16,context,sizeof(context16));
679
680     context16.Edi   = LOWORD(context->Ecx);
681     context16.SegCs = HIWORD(context->Eax);
682     context16.Eip   = LOWORD(context->Eax);
683     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
684                            + (WORD)&((STACK16FRAME*)0)->bp;
685
686     argsize = HIWORD(context->Edx) * 4;
687
688     /* FIXME: hack for stupid USER32 CallbackGlueLS routine */
689     if (context->Edx == context->Eip)
690         argsize = 6 * 4;
691
692     memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
693             (LPBYTE)context->Esp, argsize );
694
695     wine_call_to_16_regs_long(&context16, argsize + 32);
696     context->Eax = context16.Eax;
697
698     /* Clean up caller's stack frame */
699     context->Esp += BL_reg(&context16);
700 }
701
702 /***********************************************************************
703  *              OT_32ThkLSF     (KERNEL32.40)
704  *
705  * YET Another 32->16 thunk. The difference to Common32ThkLS is that
706  * argument processing is done on both the 32-bit and the 16-bit side:
707  * The 32-bit side prepares arguments, copying them onto the stack.
708  *
709  * When this routine is called, the first word on the stack is the
710  * number of argument bytes prepared by the 32-bit code, and EDX
711  * contains the 16-bit target address.
712  *
713  * The called 16-bit routine is another relaycode, doing further
714  * argument processing and then calling the real 16-bit target
715  * whose address is stored at [bp-04].
716  *
717  * The call proceeds using a normal CallRegisterShortProc.
718  * After return from the 16-bit relaycode, the arguments need
719  * to be copied *back* to the 32-bit stack, since the 32-bit
720  * relaycode processes output parameters.
721  *
722  * Note that we copy twice the number of arguments, since some of the
723  * 16-bit relaycodes in SYSTHUNK.DLL directly access the original
724  * arguments of the caller!
725  *
726  * (Note that this function seems only to be used for
727  *  OLECLI32 -> OLECLI and OLESVR32 -> OLESVR thunking.)
728  */
729 void WINAPI OT_32ThkLSF( CONTEXT86 *context )
730 {
731     CONTEXT86 context16;
732     DWORD argsize;
733
734     memcpy(&context16,context,sizeof(context16));
735
736     context16.SegCs = HIWORD(context->Edx);
737     context16.Eip   = LOWORD(context->Edx);
738     context16.Ebp   = OFFSETOF( NtCurrentTeb()->cur_stack )
739                            + (WORD)&((STACK16FRAME*)0)->bp;
740
741     argsize = 2 * *(WORD *)context->Esp + 2;
742
743     memcpy( (LPBYTE)CURRENT_STACK16 - argsize,
744             (LPBYTE)context->Esp, argsize );
745
746     wine_call_to_16_regs_short(&context16, argsize);
747     context->Eax = context16.Eax;
748     context->Edx = context16.Edx;
749
750     /* Copy modified buffers back to 32-bit stack */
751     memcpy( (LPBYTE)context->Esp,
752             (LPBYTE)CURRENT_STACK16 - argsize, argsize );
753
754     context->Esp +=   LOWORD(context16.Esp) -
755                         ( OFFSETOF( NtCurrentTeb()->cur_stack ) - argsize );
756 }
757
758 /***********************************************************************
759  *              ThunkInitLSF            (KERNEL32.41)
760  * A thunk setup routine.
761  * Expects a pointer to a preinitialized thunkbuffer in the first argument
762  * looking like:
763  *      00..03:         unknown (pointer, check _41, _43, _46)
764  *      04: EB1E                jmp +0x20
765  *
766  *      06..23:         unknown (space for replacement code, check .90)
767  *
768  *      24:>E800000000          call offset 29
769  *      29:>58                  pop eax            ( target of call )
770  *      2A: 2D25000000          sub eax,0x00000025 ( now points to offset 4 )
771  *      2F: BAxxxxxxxx          mov edx,xxxxxxxx
772  *      34: 68yyyyyyyy          push KERNEL32.90
773  *      39: C3                  ret
774  *
775  *      3A: EB1E                jmp +0x20
776  *      3E ... 59:      unknown (space for replacement code?)
777  *      5A: E8xxxxxxxx          call <32bitoffset xxxxxxxx>
778  *      5F: 5A                  pop edx
779  *      60: 81EA25xxxxxx        sub edx, 0x25xxxxxx
780  *      66: 52                  push edx
781  *      67: 68xxxxxxxx          push xxxxxxxx
782  *      6C: 68yyyyyyyy          push KERNEL32.89
783  *      71: C3                  ret
784  *      72: end?
785  * This function checks if the code is there, and replaces the yyyyyyyy entries
786  * by the functionpointers.
787  * The thunkbuf looks like:
788  *
789  *      00: DWORD       length          ? don't know exactly
790  *      04: SEGPTR      ptr             ? where does it point to?
791  * The segpointer ptr is written into the first DWORD of 'thunk'.
792  * [ok probably]
793  * RETURNS
794  *      unclear, pointer to win16 thkbuffer?
795  */
796 LPVOID WINAPI ThunkInitLSF(
797         LPBYTE thunk,   /* [in] win32 thunk */
798         LPCSTR thkbuf,  /* [in] thkbuffer name in win16 dll */
799         DWORD len,      /* [in] length of thkbuffer */
800         LPCSTR dll16,   /* [in] name of win16 dll */
801         LPCSTR dll32    /* [in] name of win32 dll */
802 ) {
803         HMODULE hkrnl32 = GetModuleHandleA("KERNEL32");
804         LPDWORD         addr,addr2;
805
806         /* FIXME: add checks for valid code ... */
807         /* write pointers to kernel32.89 and kernel32.90 (+ordinal base of 1) */
808         *(DWORD*)(thunk+0x35) = (DWORD)GetProcAddress(hkrnl32,(LPSTR)90);
809         *(DWORD*)(thunk+0x6D) = (DWORD)GetProcAddress(hkrnl32,(LPSTR)89);
810
811
812         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
813                 return 0;
814
815         addr2 = MapSL(addr[1]);
816         if (HIWORD(addr2))
817                 *(DWORD*)thunk = (DWORD)addr2;
818
819         return addr2;
820 }
821
822 /***********************************************************************
823  *              FT_PrologPrime                  (KERNEL32.89)
824  *
825  * This function is called from the relay code installed by
826  * ThunkInitLSF. It replaces the location from where it was
827  * called by a standard FT_Prolog call stub (which is 'primed'
828  * by inserting the correct target table pointer).
829  * Finally, it calls that stub.
830  *
831  * Input:  ECX    target number + flags (passed through to FT_Prolog)
832  *        (ESP)   offset of location where target table pointer
833  *                is stored, relative to the start of the relay code
834  *        (ESP+4) pointer to start of relay code
835  *                (this is where the FT_Prolog call stub gets written to)
836  *
837  * Note: The two DWORD arguments get popped off the stack.
838  *
839  */
840 void WINAPI FT_PrologPrime( CONTEXT86 *context )
841 {
842     DWORD  targetTableOffset;
843     LPBYTE relayCode;
844
845     /* Compensate for the fact that the Wine register relay code thought
846        we were being called, although we were in fact jumped to */
847     context->Esp -= 4;
848
849     /* Write FT_Prolog call stub */
850     targetTableOffset = stack32_pop(context);
851     relayCode = (LPBYTE)stack32_pop(context);
852     _write_ftprolog( relayCode, *(DWORD **)(relayCode+targetTableOffset) );
853
854     /* Jump to the call stub just created */
855     context->Eip = (DWORD)relayCode;
856 }
857
858 /***********************************************************************
859  *              QT_ThunkPrime                   (KERNEL32.90)
860  *
861  * This function corresponds to FT_PrologPrime, but installs a
862  * call stub for QT_Thunk instead.
863  *
864  * Input: (EBP-4) target number (passed through to QT_Thunk)
865  *         EDX    target table pointer location offset
866  *         EAX    start of relay code
867  *
868  */
869 void WINAPI QT_ThunkPrime( CONTEXT86 *context )
870 {
871     DWORD  targetTableOffset;
872     LPBYTE relayCode;
873
874     /* Compensate for the fact that the Wine register relay code thought
875        we were being called, although we were in fact jumped to */
876     context->Esp -= 4;
877
878     /* Write QT_Thunk call stub */
879     targetTableOffset = context->Edx;
880     relayCode = (LPBYTE)context->Eax;
881     _write_qtthunk( relayCode, *(DWORD **)(relayCode+targetTableOffset) );
882
883     /* Jump to the call stub just created */
884     context->Eip = (DWORD)relayCode;
885 }
886
887 /***********************************************************************
888  *              ThunkInitSL (KERNEL32.46)
889  * Another thunkbuf link routine.
890  * The start of the thunkbuf looks like this:
891  *      00: DWORD       length
892  *      04: SEGPTR      address for thunkbuffer pointer
893  * [ok probably]
894  */
895 VOID WINAPI ThunkInitSL(
896         LPBYTE thunk,           /* [in] start of thunkbuffer */
897         LPCSTR thkbuf,          /* [in] name/ordinal of thunkbuffer in win16 dll */
898         DWORD len,              /* [in] length of thunkbuffer */
899         LPCSTR dll16,           /* [in] name of win16 dll containing the thkbuf */
900         LPCSTR dll32            /* [in] win32 dll. FIXME: strange, unused */
901 ) {
902         LPDWORD         addr;
903
904         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
905                 return;
906
907         *(DWORD*)MapSL(addr[1]) = (DWORD)thunk;
908 }
909
910 /**********************************************************************
911  *           SSInit             (KERNEL.700)
912  * RETURNS
913  *      TRUE for success.
914  */
915 BOOL WINAPI SSInit16()
916 {
917     return TRUE;
918 }
919
920 /**********************************************************************
921  *           SSOnBigStack       (KERNEL32.87)
922  * Check if thunking is initialized (ss selector set up etc.)
923  * We do that differently, so just return TRUE.
924  * [ok]
925  * RETURNS
926  *      TRUE for success.
927  */
928 BOOL WINAPI SSOnBigStack()
929 {
930     TRACE("Yes, thunking is initialized\n");
931     return TRUE;
932 }
933
934 /**********************************************************************
935  *           SSConfirmSmallStack     (KERNEL.704)
936  *
937  * Abort if not on small stack.
938  *
939  * This must be a register routine as it has to preserve *all* registers.
940  */
941 void WINAPI SSConfirmSmallStack( CONTEXT86 *context )
942 {
943     /* We are always on the small stack while in 16-bit code ... */
944 }
945
946 /**********************************************************************
947  *           SSCall (KERNEL32.88)
948  * One of the real thunking functions. This one seems to be for 32<->32
949  * thunks. It should probably be capable of crossing processboundaries.
950  *
951  * And YES, I've seen nr=48 (somewhere in the Win95 32<->16 OLE coupling)
952  * [ok]
953  */
954 DWORD WINAPIV SSCall(
955         DWORD nr,       /* [in] number of argument bytes */
956         DWORD flags,    /* [in] FIXME: flags ? */
957         FARPROC fun,    /* [in] function to call */
958         ...             /* [in/out] arguments */
959 ) {
960     DWORD i,ret;
961     DWORD *args = ((DWORD *)&fun) + 1;
962
963     if(TRACE_ON(thunk))
964     {
965       DPRINTF("(%ld,0x%08lx,%p,[",nr,flags,fun);
966       for (i=0;i<nr/4;i++)
967           DPRINTF("0x%08lx,",args[i]);
968       DPRINTF("])\n");
969     }
970     switch (nr) {
971     case 0:     ret = fun();
972                 break;
973     case 4:     ret = fun(args[0]);
974                 break;
975     case 8:     ret = fun(args[0],args[1]);
976                 break;
977     case 12:    ret = fun(args[0],args[1],args[2]);
978                 break;
979     case 16:    ret = fun(args[0],args[1],args[2],args[3]);
980                 break;
981     case 20:    ret = fun(args[0],args[1],args[2],args[3],args[4]);
982                 break;
983     case 24:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5]);
984                 break;
985     case 28:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
986                 break;
987     case 32:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
988                 break;
989     case 36:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]);
990                 break;
991     case 40:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
992                 break;
993     case 44:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10]);
994                 break;
995     case 48:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11]);
996                 break;
997     default:
998         WARN("Unsupported nr of arguments, %ld\n",nr);
999         ret = 0;
1000         break;
1001
1002     }
1003     TRACE(" returning %ld ...\n",ret);
1004     return ret;
1005 }
1006
1007 /**********************************************************************
1008  *           W32S_BackTo32                      (KERNEL32.51)
1009  */
1010 void WINAPI W32S_BackTo32( CONTEXT86 *context )
1011 {
1012     LPDWORD stack = (LPDWORD)context->Esp;
1013     FARPROC proc = (FARPROC)context->Eip;
1014
1015     context->Eax = proc( stack[1], stack[2], stack[3], stack[4], stack[5],
1016                                stack[6], stack[7], stack[8], stack[9], stack[10] );
1017
1018     context->Eip = stack32_pop(context);
1019 }
1020
1021 /**********************************************************************
1022  *                      AllocSLCallback         (KERNEL32.@)
1023  *
1024  * Win95 uses some structchains for callbacks. It allocates them
1025  * in blocks of 100 entries, size 32 bytes each, layout:
1026  * blockstart:
1027  *      0:      PTR     nextblockstart
1028  *      4:      entry   *first;
1029  *      8:      WORD    sel ( start points to blockstart)
1030  *      A:      WORD    unknown
1031  * 100xentry:
1032  *      00..17:         Code
1033  *      18:     PDB     *owning_process;
1034  *      1C:     PTR     blockstart
1035  *
1036  * We ignore this for now. (Just a note for further developers)
1037  * FIXME: use this method, so we don't waste selectors...
1038  *
1039  * Following code is then generated by AllocSLCallback. The code is 16 bit, so
1040  * the 0x66 prefix switches from word->long registers.
1041  *
1042  *      665A            pop     edx
1043  *      6668x arg2 x    pushl   <arg2>
1044  *      6652            push    edx
1045  *      EAx arg1 x      jmpf    <arg1>
1046  *
1047  * returns the startaddress of this thunk.
1048  *
1049  * Note, that they look very similair to the ones allocates by THUNK_Alloc.
1050  * RETURNS
1051  *      segmented pointer to the start of the thunk
1052  */
1053 DWORD WINAPI
1054 AllocSLCallback(
1055         DWORD finalizer,        /* [in] finalizer function */
1056         DWORD callback          /* [in] callback function */
1057 ) {
1058         LPBYTE  x,thunk = HeapAlloc( GetProcessHeap(), 0, 32 );
1059         WORD    sel;
1060
1061         x=thunk;
1062         *x++=0x66;*x++=0x5a;                            /* popl edx */
1063         *x++=0x66;*x++=0x68;*(DWORD*)x=finalizer;x+=4;  /* pushl finalizer */
1064         *x++=0x66;*x++=0x52;                            /* pushl edx */
1065         *x++=0xea;*(DWORD*)x=callback;x+=4;             /* jmpf callback */
1066
1067         *(DWORD*)(thunk+18) = GetCurrentProcessId();
1068
1069         sel = SELECTOR_AllocBlock( thunk, 32, WINE_LDT_FLAGS_CODE );
1070         return (sel<<16)|0;
1071 }
1072
1073 /**********************************************************************
1074  *              FreeSLCallback          (KERNEL32.@)
1075  * Frees the specified 16->32 callback
1076  */
1077 void WINAPI
1078 FreeSLCallback(
1079         DWORD x /* [in] 16 bit callback (segmented pointer?) */
1080 ) {
1081         FIXME("(0x%08lx): stub\n",x);
1082 }
1083
1084
1085 /**********************************************************************
1086  *              GetTEBSelectorFS        (KERNEL.475)
1087  *      Set the 16-bit %fs to the 32-bit %fs (current TEB selector)
1088  */
1089 void WINAPI GetTEBSelectorFS16(void)
1090 {
1091     CURRENT_STACK16->fs = wine_get_fs();
1092 }
1093
1094 /**********************************************************************
1095  *              IsPeFormat              (KERNEL.431)
1096  * Checks the passed filename if it is a PE format executeable
1097  * RETURNS
1098  *  TRUE, if it is.
1099  *  FALSE if not.
1100  */
1101 BOOL16 WINAPI IsPeFormat16(
1102         LPSTR   fn,     /* [in] filename to executeable */
1103         HFILE16 hf16    /* [in] open file, if filename is NULL */
1104 ) {
1105     BOOL ret = FALSE;
1106     IMAGE_DOS_HEADER mzh;
1107     OFSTRUCT ofs;
1108     DWORD xmagic;
1109
1110     if (fn) hf16 = OpenFile16(fn,&ofs,OF_READ);
1111     if (hf16 == HFILE_ERROR16) return FALSE;
1112     _llseek16(hf16,0,SEEK_SET);
1113     if (sizeof(mzh)!=_lread16(hf16,&mzh,sizeof(mzh))) goto done;
1114     if (mzh.e_magic!=IMAGE_DOS_SIGNATURE) goto done;
1115     _llseek16(hf16,mzh.e_lfanew,SEEK_SET);
1116     if (sizeof(DWORD)!=_lread16(hf16,&xmagic,sizeof(DWORD))) goto done;
1117     ret = (xmagic == IMAGE_NT_SIGNATURE);
1118  done:
1119     _lclose16(hf16);
1120     return ret;
1121 }
1122
1123
1124 /***********************************************************************
1125  *           K32Thk1632Prolog                   (KERNEL32.@)
1126  */
1127 void WINAPI K32Thk1632Prolog( CONTEXT86 *context )
1128 {
1129    LPBYTE code = (LPBYTE)context->Eip - 5;
1130
1131    /* Arrrgh! SYSTHUNK.DLL just has to re-implement another method
1132       of 16->32 thunks instead of using one of the standard methods!
1133       This means that SYSTHUNK.DLL itself switches to a 32-bit stack,
1134       and does a far call to the 32-bit code segment of OLECLI32/OLESVR32.
1135       Unfortunately, our CallTo/CallFrom mechanism is therefore completely
1136       bypassed, which means it will crash the next time the 32-bit OLE
1137       code thunks down again to 16-bit (this *will* happen!).
1138
1139       The following hack tries to recognize this situation.
1140       This is possible since the called stubs in OLECLI32/OLESVR32 all
1141       look exactly the same:
1142         00   E8xxxxxxxx    call K32Thk1632Prolog
1143         05   FF55FC        call [ebp-04]
1144         08   E8xxxxxxxx    call K32Thk1632Epilog
1145         0D   66CB          retf
1146
1147       If we recognize this situation, we try to simulate the actions
1148       of our CallTo/CallFrom mechanism by copying the 16-bit stack
1149       to our 32-bit stack, creating a proper STACK16FRAME and
1150       updating cur_stack. */
1151
1152    if (   code[5] == 0xFF && code[6] == 0x55 && code[7] == 0xFC
1153        && code[13] == 0x66 && code[14] == 0xCB)
1154    {
1155       WORD  stackSel  = NtCurrentTeb()->stack_sel;
1156       DWORD stackBase = GetSelectorBase(stackSel);
1157
1158       DWORD argSize = context->Ebp - context->Esp;
1159       char *stack16 = (char *)context->Esp - 4;
1160       char *stack32 = (char *)NtCurrentTeb()->cur_stack - argSize;
1161       STACK16FRAME *frame16 = (STACK16FRAME *)stack16 - 1;
1162
1163       TRACE("before SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1164                    context->Ebp, context->Esp, NtCurrentTeb()->cur_stack);
1165
1166       memset(frame16, '\0', sizeof(STACK16FRAME));
1167       frame16->frame32 = (STACK32FRAME *)NtCurrentTeb()->cur_stack;
1168       frame16->ebp = context->Ebp;
1169
1170       memcpy(stack32, stack16, argSize);
1171       NtCurrentTeb()->cur_stack = MAKESEGPTR(stackSel, (DWORD)frame16 - stackBase);
1172
1173       context->Esp = (DWORD)stack32 + 4;
1174       context->Ebp = context->Esp + argSize;
1175
1176       TRACE("after  SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1177                    context->Ebp, context->Esp, NtCurrentTeb()->cur_stack);
1178    }
1179
1180     /* entry_point is never used again once the entry point has
1181        been called.  Thus we re-use it to hold the Win16Lock count */
1182    ReleaseThunkLock(&CURRENT_STACK16->entry_point);
1183 }
1184
1185 /***********************************************************************
1186  *           K32Thk1632Epilog                   (KERNEL32.@)
1187  */
1188 void WINAPI K32Thk1632Epilog( CONTEXT86 *context )
1189 {
1190    LPBYTE code = (LPBYTE)context->Eip - 13;
1191
1192    RestoreThunkLock(CURRENT_STACK16->entry_point);
1193
1194    /* We undo the SYSTHUNK hack if necessary. See K32Thk1632Prolog. */
1195
1196    if (   code[5] == 0xFF && code[6] == 0x55 && code[7] == 0xFC
1197        && code[13] == 0x66 && code[14] == 0xCB)
1198    {
1199       STACK16FRAME *frame16 = MapSL(NtCurrentTeb()->cur_stack);
1200       char *stack16 = (char *)(frame16 + 1);
1201       DWORD argSize = frame16->ebp - (DWORD)stack16;
1202       char *stack32 = (char *)frame16->frame32 - argSize;
1203
1204       DWORD nArgsPopped = context->Esp - (DWORD)stack32;
1205
1206       TRACE("before SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1207                    context->Ebp, context->Esp, NtCurrentTeb()->cur_stack);
1208
1209       NtCurrentTeb()->cur_stack = (DWORD)frame16->frame32;
1210
1211       context->Esp = (DWORD)stack16 + nArgsPopped;
1212       context->Ebp = frame16->ebp;
1213
1214       TRACE("after  SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1215                    context->Ebp, context->Esp, NtCurrentTeb()->cur_stack);
1216    }
1217 }
1218
1219 /*********************************************************************
1220  *                   PK16FNF [KERNEL32.91]
1221  *
1222  *  This routine fills in the supplied 13-byte (8.3 plus terminator)
1223  *  string buffer with the 8.3 filename of a recently loaded 16-bit
1224  *  module.  It is unknown exactly what modules trigger this
1225  *  mechanism or what purpose this serves.  Win98 Explorer (and
1226  *  probably also Win95 with IE 4 shell integration) calls this
1227  *  several times during initialization.
1228  *
1229  *  FIXME: find out what this really does and make it work.
1230  */
1231 void WINAPI PK16FNF(LPSTR strPtr)
1232 {
1233        FIXME("(%p): stub\n", strPtr);
1234
1235        /* fill in a fake filename that'll be easy to recognize */
1236        strcpy(strPtr, "WINESTUB.FIX");
1237 }
1238
1239 /***********************************************************************
1240  * 16->32 Flat Thunk routines:
1241  */
1242
1243 /***********************************************************************
1244  *              ThunkConnect16          (KERNEL.651)
1245  * Connects a 32bit and a 16bit thunkbuffer.
1246  */
1247 UINT WINAPI ThunkConnect16(
1248         LPSTR module16,              /* [in] name of win16 dll */
1249         LPSTR module32,              /* [in] name of win32 dll */
1250         HINSTANCE16 hInst16,         /* [in] hInst of win16 dll */
1251         DWORD dwReason,              /* [in] initialisation argument */
1252         struct ThunkDataCommon *TD,  /* [in/out] thunkbuffer */
1253         LPSTR thunkfun32,            /* [in] win32 thunkfunction */
1254         WORD cs                      /* [in] CS of win16 dll */
1255 ) {
1256     BOOL directionSL;
1257
1258     if (!strncmp(TD->magic, "SL01", 4))
1259     {
1260         directionSL = TRUE;
1261
1262         TRACE("SL01 thunk %s (%lx) -> %s (%s), Reason: %ld\n",
1263               module16, (DWORD)TD, module32, thunkfun32, dwReason);
1264     }
1265     else if (!strncmp(TD->magic, "LS01", 4))
1266     {
1267         directionSL = FALSE;
1268
1269         TRACE("LS01 thunk %s (%lx) <- %s (%s), Reason: %ld\n",
1270               module16, (DWORD)TD, module32, thunkfun32, dwReason);
1271     }
1272     else
1273     {
1274         ERR("Invalid magic %c%c%c%c\n",
1275             TD->magic[0], TD->magic[1], TD->magic[2], TD->magic[3]);
1276         return 0;
1277     }
1278
1279     switch (dwReason)
1280     {
1281         case DLL_PROCESS_ATTACH:
1282             if (directionSL)
1283             {
1284                 struct ThunkDataSL16 *SL16 = (struct ThunkDataSL16 *)TD;
1285                 struct ThunkDataSL   *SL   = SL16->fpData;
1286
1287                 if (SL == NULL)
1288                 {
1289                     SL = HeapAlloc(GetProcessHeap(), 0, sizeof(*SL));
1290
1291                     SL->common   = SL16->common;
1292                     SL->flags1   = SL16->flags1;
1293                     SL->flags2   = SL16->flags2;
1294
1295                     SL->apiDB    = MapSL(SL16->apiDatabase);
1296                     SL->targetDB = NULL;
1297
1298                     lstrcpynA(SL->pszDll16, module16, 255);
1299                     lstrcpynA(SL->pszDll32, module32, 255);
1300
1301                     /* We should create a SEGPTR to the ThunkDataSL,
1302                        but since the contents are not in the original format,
1303                        any access to this by 16-bit code would crash anyway. */
1304                     SL16->spData = 0;
1305                     SL16->fpData = SL;
1306                 }
1307
1308
1309                 if (SL->flags2 & 0x80000000)
1310                 {
1311                     TRACE("Preloading 32-bit library\n");
1312                     LoadLibraryA(module32);
1313                 }
1314             }
1315             else
1316             {
1317                 /* nothing to do */
1318             }
1319             break;
1320
1321         case DLL_PROCESS_DETACH:
1322             /* FIXME: cleanup */
1323             break;
1324     }
1325
1326     return 1;
1327 }
1328
1329
1330 /***********************************************************************
1331  *           C16ThkSL                           (KERNEL.630)
1332  */
1333
1334 void WINAPI C16ThkSL(CONTEXT86 *context)
1335 {
1336     LPBYTE stub = MapSL(context->Eax), x = stub;
1337     WORD cs = wine_get_cs();
1338     WORD ds = wine_get_ds();
1339
1340     /* We produce the following code:
1341      *
1342      *   mov ax, __FLATDS
1343      *   mov es, ax
1344      *   movzx ecx, cx
1345      *   mov edx, es:[ecx + $EDX]
1346      *   push bp
1347      *   push edx
1348      *   push dx
1349      *   push edx
1350      *   call __FLATCS:__wine_call_from_16_thunk
1351      */
1352
1353     *x++ = 0xB8; *((WORD *)x)++ = ds;
1354     *x++ = 0x8E; *x++ = 0xC0;
1355     *x++ = 0x66; *x++ = 0x0F; *x++ = 0xB7; *x++ = 0xC9;
1356     *x++ = 0x67; *x++ = 0x66; *x++ = 0x26; *x++ = 0x8B;
1357                  *x++ = 0x91; *((DWORD *)x)++ = context->Edx;
1358
1359     *x++ = 0x55;
1360     *x++ = 0x66; *x++ = 0x52;
1361     *x++ = 0x52;
1362     *x++ = 0x66; *x++ = 0x52;
1363     *x++ = 0x66; *x++ = 0x9A; *((DWORD *)x)++ = (DWORD)__wine_call_from_16_thunk;
1364                               *((WORD *)x)++ = cs;
1365
1366     /* Jump to the stub code just created */
1367     context->Eip = LOWORD(context->Eax);
1368     context->SegCs  = HIWORD(context->Eax);
1369
1370     /* Since C16ThkSL got called by a jmp, we need to leave the
1371        original return address on the stack */
1372     context->Esp -= 4;
1373 }
1374
1375 /***********************************************************************
1376  *           C16ThkSL01                         (KERNEL.631)
1377  */
1378
1379 void WINAPI C16ThkSL01(CONTEXT86 *context)
1380 {
1381     LPBYTE stub = MapSL(context->Eax), x = stub;
1382
1383     if (stub)
1384     {
1385         struct ThunkDataSL16 *SL16 = MapSL(context->Edx);
1386         struct ThunkDataSL *td = SL16->fpData;
1387
1388         DWORD procAddress = (DWORD)GetProcAddress16(GetModuleHandle16("KERNEL"), (LPCSTR)631);
1389         WORD cs = wine_get_cs();
1390
1391         if (!td)
1392         {
1393             ERR("ThunkConnect16 was not called!\n");
1394             return;
1395         }
1396
1397         TRACE("Creating stub for ThunkDataSL %08lx\n", (DWORD)td);
1398
1399
1400         /* We produce the following code:
1401          *
1402          *   xor eax, eax
1403          *   mov edx, $td
1404          *   call C16ThkSL01
1405          *   push bp
1406          *   push edx
1407          *   push dx
1408          *   push edx
1409          *   call __FLATCS:__wine_call_from_16_thunk
1410          */
1411
1412         *x++ = 0x66; *x++ = 0x33; *x++ = 0xC0;
1413         *x++ = 0x66; *x++ = 0xBA; *((DWORD *)x)++ = (DWORD)td;
1414         *x++ = 0x9A; *((DWORD *)x)++ = procAddress;
1415
1416         *x++ = 0x55;
1417         *x++ = 0x66; *x++ = 0x52;
1418         *x++ = 0x52;
1419         *x++ = 0x66; *x++ = 0x52;
1420         *x++ = 0x66; *x++ = 0x9A; *((DWORD *)x)++ = (DWORD)__wine_call_from_16_thunk;
1421                                   *((WORD *)x)++ = cs;
1422
1423         /* Jump to the stub code just created */
1424         context->Eip = LOWORD(context->Eax);
1425         context->SegCs  = HIWORD(context->Eax);
1426
1427         /* Since C16ThkSL01 got called by a jmp, we need to leave the
1428            orginal return address on the stack */
1429         context->Esp -= 4;
1430     }
1431     else
1432     {
1433         struct ThunkDataSL *td = (struct ThunkDataSL *)context->Edx;
1434         DWORD targetNr = CX_reg(context) / 4;
1435         struct SLTargetDB *tdb;
1436
1437         TRACE("Process %08lx calling target %ld of ThunkDataSL %08lx\n",
1438               GetCurrentProcessId(), targetNr, (DWORD)td);
1439
1440         for (tdb = td->targetDB; tdb; tdb = tdb->next)
1441             if (tdb->process == GetCurrentProcessId())
1442                 break;
1443
1444         if (!tdb)
1445         {
1446             TRACE("Loading 32-bit library %s\n", td->pszDll32);
1447             LoadLibraryA(td->pszDll32);
1448
1449             for (tdb = td->targetDB; tdb; tdb = tdb->next)
1450                 if (tdb->process == GetCurrentProcessId())
1451                     break;
1452         }
1453
1454         if (tdb)
1455         {
1456             context->Edx = tdb->targetTable[targetNr];
1457
1458             TRACE("Call target is %08lx\n", context->Edx);
1459         }
1460         else
1461         {
1462             WORD *stack = MapSL( MAKESEGPTR(context->SegSs, LOWORD(context->Esp)) );
1463             SET_DX( context, HIWORD(td->apiDB[targetNr].errorReturnValue) );
1464             SET_AX( context, LOWORD(td->apiDB[targetNr].errorReturnValue) );
1465             context->Eip = stack[2];
1466             context->SegCs  = stack[3];
1467             context->Esp += td->apiDB[targetNr].nrArgBytes + 4;
1468
1469             ERR("Process %08lx did not ThunkConnect32 %s to %s\n",
1470                 GetCurrentProcessId(), td->pszDll32, td->pszDll16);
1471         }
1472     }
1473 }
1474
1475
1476 /***********************************************************************
1477  * 16<->32 Thunklet/Callback API:
1478  */
1479
1480 #include "pshpack1.h"
1481 typedef struct _THUNKLET
1482 {
1483     BYTE        prefix_target;
1484     BYTE        pushl_target;
1485     DWORD       target;
1486
1487     BYTE        prefix_relay;
1488     BYTE        pushl_relay;
1489     DWORD       relay;
1490
1491     BYTE        jmp_glue;
1492     DWORD       glue;
1493
1494     BYTE        type;
1495     HINSTANCE16 owner;
1496     struct _THUNKLET *next;
1497 } THUNKLET;
1498 #include "poppack.h"
1499
1500 #define THUNKLET_TYPE_LS  1
1501 #define THUNKLET_TYPE_SL  2
1502
1503 static HANDLE  ThunkletHeap = 0;
1504 static WORD ThunkletCodeSel;
1505 static THUNKLET *ThunkletAnchor = NULL;
1506
1507 static FARPROC ThunkletSysthunkGlueLS = 0;
1508 static SEGPTR    ThunkletSysthunkGlueSL = 0;
1509
1510 static FARPROC ThunkletCallbackGlueLS = 0;
1511 static SEGPTR    ThunkletCallbackGlueSL = 0;
1512
1513
1514 /* map a thunk allocated on ThunkletHeap to a 16-bit pointer */
1515 inline static SEGPTR get_segptr( void *thunk )
1516 {
1517     if (!thunk) return 0;
1518     return MAKESEGPTR( ThunkletCodeSel, (char *)thunk - (char *)ThunkletHeap );
1519 }
1520
1521 /***********************************************************************
1522  *           THUNK_Init
1523  */
1524 static BOOL THUNK_Init(void)
1525 {
1526     LPBYTE thunk;
1527
1528     ThunkletHeap = HeapCreate( 0, 0x10000, 0x10000 );
1529     if (!ThunkletHeap) return FALSE;
1530
1531     ThunkletCodeSel = SELECTOR_AllocBlock( (void *)ThunkletHeap, 0x10000, WINE_LDT_FLAGS_CODE );
1532
1533     thunk = HeapAlloc( ThunkletHeap, 0, 5 );
1534     if (!thunk) return FALSE;
1535
1536     ThunkletSysthunkGlueLS = (FARPROC)thunk;
1537     *thunk++ = 0x58;                             /* popl eax */
1538     *thunk++ = 0xC3;                             /* ret      */
1539
1540     ThunkletSysthunkGlueSL = get_segptr( thunk );
1541     *thunk++ = 0x66; *thunk++ = 0x58;            /* popl eax */
1542     *thunk++ = 0xCB;                             /* lret     */
1543
1544     return TRUE;
1545 }
1546
1547 /***********************************************************************
1548  *     SetThunkletCallbackGlue             (KERNEL.560)
1549  */
1550 void WINAPI SetThunkletCallbackGlue16( FARPROC glueLS, SEGPTR glueSL )
1551 {
1552     ThunkletCallbackGlueLS = glueLS;
1553     ThunkletCallbackGlueSL = glueSL;
1554 }
1555
1556
1557 /***********************************************************************
1558  *     THUNK_FindThunklet
1559  */
1560 THUNKLET *THUNK_FindThunklet( DWORD target, DWORD relay,
1561                               DWORD glue, BYTE type )
1562 {
1563     THUNKLET *thunk;
1564
1565     for (thunk = ThunkletAnchor; thunk; thunk = thunk->next)
1566         if (    thunk->type   == type
1567              && thunk->target == target
1568              && thunk->relay  == relay
1569              && ( type == THUNKLET_TYPE_LS ?
1570                     ( thunk->glue == glue - (DWORD)&thunk->type )
1571                   : ( thunk->glue == glue ) ) )
1572             return thunk;
1573
1574      return NULL;
1575 }
1576
1577 /***********************************************************************
1578  *     THUNK_AllocLSThunklet
1579  */
1580 FARPROC THUNK_AllocLSThunklet( SEGPTR target, DWORD relay,
1581                                  FARPROC glue, HTASK16 owner )
1582 {
1583     THUNKLET *thunk = THUNK_FindThunklet( (DWORD)target, relay, (DWORD)glue,
1584                                           THUNKLET_TYPE_LS );
1585     if (!thunk)
1586     {
1587         TDB *pTask = TASK_GetPtr( owner );
1588
1589         if (!ThunkletHeap) THUNK_Init();
1590         if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) )
1591             return 0;
1592
1593         thunk->prefix_target = thunk->prefix_relay = 0x90;
1594         thunk->pushl_target  = thunk->pushl_relay  = 0x68;
1595         thunk->jmp_glue = 0xE9;
1596
1597         thunk->target  = (DWORD)target;
1598         thunk->relay   = (DWORD)relay;
1599         thunk->glue    = (DWORD)glue - (DWORD)&thunk->type;
1600
1601         thunk->type    = THUNKLET_TYPE_LS;
1602         thunk->owner   = pTask? pTask->hInstance : 0;
1603
1604         thunk->next    = ThunkletAnchor;
1605         ThunkletAnchor = thunk;
1606     }
1607
1608     return (FARPROC)thunk;
1609 }
1610
1611 /***********************************************************************
1612  *     THUNK_AllocSLThunklet
1613  */
1614 SEGPTR THUNK_AllocSLThunklet( FARPROC target, DWORD relay,
1615                               SEGPTR glue, HTASK16 owner )
1616 {
1617     THUNKLET *thunk = THUNK_FindThunklet( (DWORD)target, relay, (DWORD)glue,
1618                                           THUNKLET_TYPE_SL );
1619     if (!thunk)
1620     {
1621         TDB *pTask = TASK_GetPtr( owner );
1622
1623         if (!ThunkletHeap) THUNK_Init();
1624         if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) )
1625             return 0;
1626
1627         thunk->prefix_target = thunk->prefix_relay = 0x66;
1628         thunk->pushl_target  = thunk->pushl_relay  = 0x68;
1629         thunk->jmp_glue = 0xEA;
1630
1631         thunk->target  = (DWORD)target;
1632         thunk->relay   = (DWORD)relay;
1633         thunk->glue    = (DWORD)glue;
1634
1635         thunk->type    = THUNKLET_TYPE_SL;
1636         thunk->owner   = pTask? pTask->hInstance : 0;
1637
1638         thunk->next    = ThunkletAnchor;
1639         ThunkletAnchor = thunk;
1640     }
1641
1642     return get_segptr( thunk );
1643 }
1644
1645 /**********************************************************************
1646  *     IsLSThunklet
1647  */
1648 BOOL16 WINAPI IsLSThunklet( THUNKLET *thunk )
1649 {
1650     return    thunk->prefix_target == 0x90 && thunk->pushl_target == 0x68
1651            && thunk->prefix_relay  == 0x90 && thunk->pushl_relay  == 0x68
1652            && thunk->jmp_glue == 0xE9 && thunk->type == THUNKLET_TYPE_LS;
1653 }
1654
1655 /**********************************************************************
1656  *     IsSLThunklet                        (KERNEL.612)
1657  */
1658 BOOL16 WINAPI IsSLThunklet16( THUNKLET *thunk )
1659 {
1660     return    thunk->prefix_target == 0x66 && thunk->pushl_target == 0x68
1661            && thunk->prefix_relay  == 0x66 && thunk->pushl_relay  == 0x68
1662            && thunk->jmp_glue == 0xEA && thunk->type == THUNKLET_TYPE_SL;
1663 }
1664
1665
1666
1667 /***********************************************************************
1668  *     AllocLSThunkletSysthunk             (KERNEL.607)
1669  */
1670 FARPROC WINAPI AllocLSThunkletSysthunk16( SEGPTR target,
1671                                           FARPROC relay, DWORD dummy )
1672 {
1673     if (!ThunkletSysthunkGlueLS) THUNK_Init();
1674     return THUNK_AllocLSThunklet( (SEGPTR)relay, (DWORD)target,
1675                                   ThunkletSysthunkGlueLS, GetCurrentTask() );
1676 }
1677
1678 /***********************************************************************
1679  *     AllocSLThunkletSysthunk             (KERNEL.608)
1680  */
1681 SEGPTR WINAPI AllocSLThunkletSysthunk16( FARPROC target,
1682                                        SEGPTR relay, DWORD dummy )
1683 {
1684     if (!ThunkletSysthunkGlueSL) THUNK_Init();
1685     return THUNK_AllocSLThunklet( (FARPROC)relay, (DWORD)target,
1686                                   ThunkletSysthunkGlueSL, GetCurrentTask() );
1687 }
1688
1689
1690 /***********************************************************************
1691  *     AllocLSThunkletCallbackEx           (KERNEL.567)
1692  */
1693 FARPROC WINAPI AllocLSThunkletCallbackEx16( SEGPTR target,
1694                                             DWORD relay, HTASK16 task )
1695 {
1696     THUNKLET *thunk = MapSL( target );
1697     if ( !thunk ) return NULL;
1698
1699     if (   IsSLThunklet16( thunk ) && thunk->relay == relay
1700         && thunk->glue == (DWORD)ThunkletCallbackGlueSL )
1701         return (FARPROC)thunk->target;
1702
1703     return THUNK_AllocLSThunklet( target, relay,
1704                                   ThunkletCallbackGlueLS, task );
1705 }
1706
1707 /***********************************************************************
1708  *     AllocSLThunkletCallbackEx           (KERNEL.568)
1709  */
1710 SEGPTR WINAPI AllocSLThunkletCallbackEx16( FARPROC target,
1711                                          DWORD relay, HTASK16 task )
1712 {
1713     THUNKLET *thunk = (THUNKLET *)target;
1714     if ( !thunk ) return 0;
1715
1716     if (   IsLSThunklet( thunk ) && thunk->relay == relay
1717         && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type )
1718         return (SEGPTR)thunk->target;
1719
1720     return THUNK_AllocSLThunklet( target, relay,
1721                                   ThunkletCallbackGlueSL, task );
1722 }
1723
1724 /***********************************************************************
1725  *     AllocLSThunkletCallback             (KERNEL.561)
1726  *     AllocLSThunkletCallback_dup         (KERNEL.606)
1727  */
1728 FARPROC WINAPI AllocLSThunkletCallback16( SEGPTR target, DWORD relay )
1729 {
1730     return AllocLSThunkletCallbackEx16( target, relay, GetCurrentTask() );
1731 }
1732
1733 /***********************************************************************
1734  *     AllocSLThunkletCallback             (KERNEL.562)
1735  *     AllocSLThunkletCallback_dup         (KERNEL.605)
1736  */
1737 SEGPTR WINAPI AllocSLThunkletCallback16( FARPROC target, DWORD relay )
1738 {
1739     return AllocSLThunkletCallbackEx16( target, relay, GetCurrentTask() );
1740 }
1741
1742 /***********************************************************************
1743  *     FindLSThunkletCallback              (KERNEL.563)
1744  *     FindLSThunkletCallback_dup          (KERNEL.609)
1745  */
1746 FARPROC WINAPI FindLSThunkletCallback( SEGPTR target, DWORD relay )
1747 {
1748     THUNKLET *thunk = MapSL( target );
1749     if (   thunk && IsSLThunklet16( thunk ) && thunk->relay == relay
1750         && thunk->glue == (DWORD)ThunkletCallbackGlueSL )
1751         return (FARPROC)thunk->target;
1752
1753     thunk = THUNK_FindThunklet( (DWORD)target, relay,
1754                                 (DWORD)ThunkletCallbackGlueLS,
1755                                 THUNKLET_TYPE_LS );
1756     return (FARPROC)thunk;
1757 }
1758
1759 /***********************************************************************
1760  *     FindSLThunkletCallback              (KERNEL.564)
1761  *     FindSLThunkletCallback_dup          (KERNEL.610)
1762  */
1763 SEGPTR WINAPI FindSLThunkletCallback( FARPROC target, DWORD relay )
1764 {
1765     THUNKLET *thunk = (THUNKLET *)target;
1766     if (   thunk && IsLSThunklet( thunk ) && thunk->relay == relay
1767         && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type )
1768         return (SEGPTR)thunk->target;
1769
1770     thunk = THUNK_FindThunklet( (DWORD)target, relay,
1771                                 (DWORD)ThunkletCallbackGlueSL,
1772                                 THUNKLET_TYPE_SL );
1773     return get_segptr( thunk );
1774 }
1775
1776
1777 /***********************************************************************
1778  *     FreeThunklet            (KERNEL.611)
1779  */
1780 BOOL16 WINAPI FreeThunklet16( DWORD unused1, DWORD unused2 )
1781 {
1782     return FALSE;
1783 }
1784
1785
1786 /***********************************************************************
1787  * Callback Client API
1788  */
1789
1790 #define N_CBC_FIXED    20
1791 #define N_CBC_VARIABLE 10
1792 #define N_CBC_TOTAL    (N_CBC_FIXED + N_CBC_VARIABLE)
1793
1794 static SEGPTR CBClientRelay16[ N_CBC_TOTAL ];
1795 static FARPROC *CBClientRelay32[ N_CBC_TOTAL ];
1796
1797 /***********************************************************************
1798  *     RegisterCBClient                    (KERNEL.619)
1799  */
1800 INT16 WINAPI RegisterCBClient16( INT16 wCBCId,
1801                                  SEGPTR relay16, FARPROC *relay32 )
1802 {
1803     /* Search for free Callback ID */
1804     if ( wCBCId == -1 )
1805         for ( wCBCId = N_CBC_FIXED; wCBCId < N_CBC_TOTAL; wCBCId++ )
1806             if ( !CBClientRelay16[ wCBCId ] )
1807                 break;
1808
1809     /* Register Callback ID */
1810     if ( wCBCId > 0 && wCBCId < N_CBC_TOTAL )
1811     {
1812         CBClientRelay16[ wCBCId ] = relay16;
1813         CBClientRelay32[ wCBCId ] = relay32;
1814     }
1815     else
1816         wCBCId = 0;
1817
1818     return wCBCId;
1819 }
1820
1821 /***********************************************************************
1822  *     UnRegisterCBClient                  (KERNEL.622)
1823  */
1824 INT16 WINAPI UnRegisterCBClient16( INT16 wCBCId,
1825                                    SEGPTR relay16, FARPROC *relay32 )
1826 {
1827     if (    wCBCId >= N_CBC_FIXED && wCBCId < N_CBC_TOTAL
1828          && CBClientRelay16[ wCBCId ] == relay16
1829          && CBClientRelay32[ wCBCId ] == relay32 )
1830     {
1831         CBClientRelay16[ wCBCId ] = 0;
1832         CBClientRelay32[ wCBCId ] = 0;
1833     }
1834     else
1835         wCBCId = 0;
1836
1837     return wCBCId;
1838 }
1839
1840
1841 /***********************************************************************
1842  *     InitCBClient                        (KERNEL.623)
1843  */
1844 void WINAPI InitCBClient16( FARPROC glueLS )
1845 {
1846     HMODULE16 kernel = GetModuleHandle16( "KERNEL" );
1847     SEGPTR glueSL = (SEGPTR)GetProcAddress16( kernel, (LPCSTR)604 );
1848
1849     SetThunkletCallbackGlue16( glueLS, glueSL );
1850 }
1851
1852 /***********************************************************************
1853  *     CBClientGlueSL                      (KERNEL.604)
1854  */
1855 void WINAPI CBClientGlueSL( CONTEXT86 *context )
1856 {
1857     /* Create stack frame */
1858     SEGPTR stackSeg = stack16_push( 12 );
1859     LPWORD stackLin = MapSL( stackSeg );
1860     SEGPTR glue, *glueTab;
1861
1862     stackLin[3] = (WORD)context->Ebp;
1863     stackLin[2] = (WORD)context->Esi;
1864     stackLin[1] = (WORD)context->Edi;
1865     stackLin[0] = (WORD)context->SegDs;
1866
1867     context->Ebp = OFFSETOF( stackSeg ) + 6;
1868     context->Esp = OFFSETOF( stackSeg ) - 4;
1869     context->SegGs = 0;
1870
1871     /* Jump to 16-bit relay code */
1872     glueTab = MapSL( CBClientRelay16[ stackLin[5] ] );
1873     glue = glueTab[ stackLin[4] ];
1874     context->SegCs = SELECTOROF( glue );
1875     context->Eip   = OFFSETOF  ( glue );
1876 }
1877
1878 /***********************************************************************
1879  *     CBClientThunkSL                      (KERNEL.620)
1880  */
1881 extern DWORD CALL32_CBClient( FARPROC proc, LPWORD args, DWORD *esi );
1882 void WINAPI CBClientThunkSL( CONTEXT86 *context )
1883 {
1884     /* Call 32-bit relay code */
1885
1886     LPWORD args = MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Ebp) ) );
1887     FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ];
1888
1889     context->Eax = CALL32_CBClient( proc, args, &context->Esi );
1890 }
1891
1892 /***********************************************************************
1893  *     CBClientThunkSLEx                    (KERNEL.621)
1894  */
1895 extern DWORD CALL32_CBClientEx( FARPROC proc, LPWORD args, DWORD *esi, INT *nArgs );
1896 void WINAPI CBClientThunkSLEx( CONTEXT86 *context )
1897 {
1898     /* Call 32-bit relay code */
1899
1900     LPWORD args = MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Ebp) ) );
1901     FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ];
1902     INT nArgs;
1903     LPWORD stackLin;
1904
1905     context->Eax = CALL32_CBClientEx( proc, args, &context->Esi, &nArgs );
1906
1907     /* Restore registers saved by CBClientGlueSL */
1908     stackLin = (LPWORD)((LPBYTE)CURRENT_STACK16 + sizeof(STACK16FRAME) - 4);
1909     context->Ebp = (context->Ebp & ~0xffff) | stackLin[3];
1910     SET_SI( context, stackLin[2] );
1911     SET_DI( context, stackLin[1] );
1912     context->SegDs = stackLin[0];
1913     context->Esp += 16+nArgs;
1914
1915     /* Return to caller of CBClient thunklet */
1916     context->SegCs = stackLin[9];
1917     context->Eip   = stackLin[8];
1918 }
1919
1920
1921 /***********************************************************************
1922  *           Get16DLLAddress       (KERNEL32.@)
1923  *
1924  * This function is used by a Win32s DLL if it wants to call a Win16 function.
1925  * A 16:16 segmented pointer to the function is returned.
1926  * Written without any docu.
1927  */
1928 SEGPTR WINAPI Get16DLLAddress(HMODULE16 handle, LPSTR func_name)
1929 {
1930     static WORD code_sel32;
1931     FARPROC16 proc_16;
1932     LPBYTE thunk;
1933
1934     if (!code_sel32)
1935     {
1936         if (!ThunkletHeap) THUNK_Init();
1937         code_sel32 = SELECTOR_AllocBlock( (void *)ThunkletHeap, 0x10000,
1938                                           WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
1939         if (!code_sel32) return 0;
1940     }
1941     if (!(thunk = HeapAlloc( ThunkletHeap, 0, 32 ))) return 0;
1942
1943     if (!handle) handle = GetModuleHandle16("WIN32S16");
1944     proc_16 = GetProcAddress16(handle, func_name);
1945
1946     /* movl proc_16, $edx */
1947     *thunk++ = 0xba;
1948     *(FARPROC16 *)thunk = proc_16;
1949     thunk += sizeof(FARPROC16);
1950
1951      /* jmpl QT_Thunk */
1952     *thunk++ = 0xea;
1953     *(FARPROC *)thunk = GetProcAddress(GetModuleHandleA("KERNEL32"),"QT_Thunk");
1954     thunk += sizeof(FARPROC16);
1955     *(WORD *)thunk = wine_get_cs();
1956
1957     return MAKESEGPTR( code_sel32, (char *)thunk - (char *)ThunkletHeap );
1958 }
1959
1960
1961 /***********************************************************************
1962  *              GetWin16DOSEnv                  (KERNEL32.34)
1963  * Returns some internal value.... probably the default environment database?
1964  */
1965 DWORD WINAPI GetWin16DOSEnv()
1966 {
1967         FIXME("stub, returning 0\n");
1968         return 0;
1969 }
1970
1971 /**********************************************************************
1972  *           GetPK16SysVar    (KERNEL32.92)
1973  */
1974 LPVOID WINAPI GetPK16SysVar(void)
1975 {
1976     static BYTE PK16SysVar[128];
1977
1978     FIXME("()\n");
1979     return PK16SysVar;
1980 }
1981
1982 /**********************************************************************
1983  *           CommonUnimpStub    (KERNEL32.17)
1984  */
1985 void WINAPI CommonUnimpStub( CONTEXT86 *context )
1986 {
1987     if (context->Eax)
1988         MESSAGE( "*** Unimplemented Win32 API: %s\n", (LPSTR)context->Eax );
1989
1990     switch ((context->Ecx >> 4) & 0x0f)
1991     {
1992     case 15:  context->Eax = -1;   break;
1993     case 14:  context->Eax = 0x78; break;
1994     case 13:  context->Eax = 0x32; break;
1995     case 1:   context->Eax = 1;    break;
1996     default:  context->Eax = 0;    break;
1997     }
1998
1999     context->Esp += (context->Ecx & 0x0f) * 4;
2000 }
2001
2002 /**********************************************************************
2003  *           HouseCleanLogicallyDeadHandles    (KERNEL32.33)
2004  */
2005 void WINAPI HouseCleanLogicallyDeadHandles(void)
2006 {
2007     /* Whatever this is supposed to do, our handles probably
2008        don't need it :-) */
2009 }
2010
2011 /**********************************************************************
2012  *              @ (KERNEL32.100)
2013  */
2014 BOOL WINAPI _KERNEL32_100(HANDLE threadid,DWORD exitcode,DWORD x)
2015 {
2016         FIXME("(%p,%ld,0x%08lx): stub\n",threadid,exitcode,x);
2017         return TRUE;
2018 }
2019
2020 /**********************************************************************
2021  *              @ (KERNEL32.99)
2022  *
2023  * Checks whether the clock has to be switched from daylight
2024  * savings time to standard time or vice versa.
2025  *
2026  */
2027 DWORD WINAPI _KERNEL32_99(DWORD x)
2028 {
2029         FIXME("(0x%08lx): stub\n",x);
2030         return 1;
2031 }
2032
2033
2034 /**********************************************************************
2035  *           Catch    (KERNEL.55)
2036  *
2037  * Real prototype is:
2038  *   INT16 WINAPI Catch( LPCATCHBUF lpbuf );
2039  */
2040 void WINAPI Catch16( LPCATCHBUF lpbuf, CONTEXT86 *context )
2041 {
2042     /* Note: we don't save the current ss, as the catch buffer is */
2043     /* only 9 words long. Hopefully no one will have the silly    */
2044     /* idea to change the current stack before calling Throw()... */
2045
2046     /* Windows uses:
2047      * lpbuf[0] = ip
2048      * lpbuf[1] = cs
2049      * lpbuf[2] = sp
2050      * lpbuf[3] = bp
2051      * lpbuf[4] = si
2052      * lpbuf[5] = di
2053      * lpbuf[6] = ds
2054      * lpbuf[7] = unused
2055      * lpbuf[8] = ss
2056      */
2057
2058     lpbuf[0] = LOWORD(context->Eip);
2059     lpbuf[1] = context->SegCs;
2060     /* Windows pushes 4 more words before saving sp */
2061     lpbuf[2] = LOWORD(context->Esp) - 4 * sizeof(WORD);
2062     lpbuf[3] = LOWORD(context->Ebp);
2063     lpbuf[4] = LOWORD(context->Esi);
2064     lpbuf[5] = LOWORD(context->Edi);
2065     lpbuf[6] = context->SegDs;
2066     lpbuf[7] = 0;
2067     lpbuf[8] = context->SegSs;
2068     SET_AX( context, 0 );  /* Return 0 */
2069 }
2070
2071
2072 /**********************************************************************
2073  *           Throw    (KERNEL.56)
2074  *
2075  * Real prototype is:
2076  *   INT16 WINAPI Throw( LPCATCHBUF lpbuf, INT16 retval );
2077  */
2078 void WINAPI Throw16( LPCATCHBUF lpbuf, INT16 retval, CONTEXT86 *context )
2079 {
2080     STACK16FRAME *pFrame;
2081     STACK32FRAME *frame32;
2082     TEB *teb = NtCurrentTeb();
2083
2084     SET_AX( context, retval );
2085
2086     /* Find the frame32 corresponding to the frame16 we are jumping to */
2087     pFrame = THREAD_STACK16(teb);
2088     frame32 = pFrame->frame32;
2089     while (frame32 && frame32->frame16)
2090     {
2091         if (OFFSETOF(frame32->frame16) < OFFSETOF(teb->cur_stack))
2092             break;  /* Something strange is going on */
2093         if (OFFSETOF(frame32->frame16) > lpbuf[2])
2094         {
2095             /* We found the right frame */
2096             pFrame->frame32 = frame32;
2097             break;
2098         }
2099         frame32 = ((STACK16FRAME *)MapSL(frame32->frame16))->frame32;
2100     }
2101     RtlUnwind( &pFrame->frame32->frame, NULL, NULL, 0 );
2102
2103     context->Eip = lpbuf[0];
2104     context->SegCs  = lpbuf[1];
2105     context->Esp = lpbuf[2] + 4 * sizeof(WORD) - sizeof(WORD) /*extra arg*/;
2106     context->Ebp = lpbuf[3];
2107     context->Esi = lpbuf[4];
2108     context->Edi = lpbuf[5];
2109     context->SegDs  = lpbuf[6];
2110
2111     if (lpbuf[8] != context->SegSs)
2112         ERR("Switching stack segment with Throw() not supported; expect crash now\n" );
2113 }