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