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