New stubs PrivateExtractIconEx[AW], PrivateExtractIconsW,
[wine] / win32 / kernel32.c
1 /*
2  * KERNEL32 thunks and other undocumented stuff
3  *
4  * Copyright 1997-1998 Marcus Meissner
5  * Copyright 1998      Ulrich Weigand
6  */
7
8 #include "windows.h"
9 #include "callback.h"
10 #include "resource.h"
11 #include "task.h"
12 #include "user.h"
13 #include "heap.h"
14 #include "module.h"
15 #include "process.h"
16 #include "stackframe.h"
17 #include "heap.h"
18 #include "selectors.h"
19 #include "task.h"
20 #include "win.h"
21 #include "file.h"
22 #include "debug.h"
23 #include "flatthunk.h"
24 #include "syslevel.h"
25
26
27 /***********************************************************************
28  *                                                                     *
29  *                 Win95 internal thunks                               *
30  *                                                                     *
31  ***********************************************************************/
32
33 /***********************************************************************
34  * Generates a FT_Prolog call.
35  *      
36  *  0FB6D1                  movzbl edx,cl
37  *  8B1495xxxxxxxx          mov edx,[4*edx + targetTable]
38  *  68xxxxxxxx              push FT_Prolog
39  *  C3                      lret
40  */
41 static void _write_ftprolog(LPBYTE relayCode ,DWORD *targetTable) {
42         LPBYTE  x;
43
44         x       = relayCode;
45         *x++    = 0x0f;*x++=0xb6;*x++=0xd1; /* movzbl edx,cl */
46         *x++    = 0x8B;*x++=0x14;*x++=0x95;*(DWORD**)x= targetTable;
47         x+=4;   /* mov edx, [4*edx + targetTable] */
48         *x++    = 0x68; *(DWORD*)x = (DWORD)GetProcAddress32(GetModuleHandle32A("KERNEL32"),"FT_Prolog");
49         x+=4;   /* push FT_Prolog */
50         *x++    = 0xC3;         /* lret */
51         /* fill rest with 0xCC / int 3 */
52 }
53
54 /***********************************************************************
55  *      _write_qtthunk                                  (internal)
56  * Generates a QT_Thunk style call.
57  *
58  *  33C9                    xor ecx, ecx
59  *  8A4DFC                  mov cl , [ebp-04]
60  *  8B148Dxxxxxxxx          mov edx, [4*ecx + targetTable]
61  *  B8yyyyyyyy              mov eax, QT_Thunk
62  *  FFE0                    jmp eax
63  */
64 static void _write_qtthunk(
65         LPBYTE relayCode,       /* [in] start of QT_Thunk stub */
66         DWORD *targetTable      /* [in] start of thunk (for index lookup) */
67 ) {
68         LPBYTE  x;
69
70         x       = relayCode;
71         *x++    = 0x33;*x++=0xC9; /* xor ecx,ecx */
72         *x++    = 0x8A;*x++=0x4D;*x++=0xFC; /* movb cl,[ebp-04] */
73         *x++    = 0x8B;*x++=0x14;*x++=0x8D;*(DWORD**)x= targetTable;
74         x+=4;   /* mov edx, [4*ecx + targetTable */
75         *x++    = 0xB8; *(DWORD*)x = (DWORD)GetProcAddress32(GetModuleHandle32A("KERNEL32"),"QT_Thunk");
76         x+=4;   /* mov eax , QT_Thunk */
77         *x++    = 0xFF; *x++ = 0xE0;    /* jmp eax */
78         /* should fill the rest of the 32 bytes with 0xCC */
79 }
80
81 /***********************************************************************
82  *           _loadthunk
83  */
84 static LPVOID _loadthunk(LPCSTR module, LPCSTR func, LPCSTR module32, 
85                          struct ThunkDataCommon *TD32, DWORD checksum)
86 {
87     struct ThunkDataCommon *TD16;
88     HMODULE32 hmod;
89     int ordinal;
90
91     if ((hmod = LoadLibrary16(module)) <= 32) 
92     {
93         ERR(thunk, "(%s, %s, %s): Unable to load '%s', error %d\n",
94                    module, func, module32, module, hmod);
95         return 0;
96     }
97
98     if (   !(ordinal = NE_GetOrdinal(hmod, func))
99         || !(TD16 = PTR_SEG_TO_LIN(NE_GetEntryPointEx(hmod, ordinal, FALSE))))
100     {
101         ERR(thunk, "(%s, %s, %s): Unable to find '%s'\n",
102                    module, func, module32, func);
103         return 0;
104     }
105
106     if (TD32 && memcmp(TD16->magic, TD32->magic, 4))
107     {
108         ERR(thunk, "(%s, %s, %s): Bad magic %c%c%c%c (should be %c%c%c%c)\n",
109                    module, func, module32, 
110                    TD16->magic[0], TD16->magic[1], TD16->magic[2], TD16->magic[3],
111                    TD32->magic[0], TD32->magic[1], TD32->magic[2], TD32->magic[3]);
112         return 0;
113     }
114
115     if (TD32 && TD16->checksum != TD32->checksum)
116     {
117         ERR(thunk, "(%s, %s, %s): Wrong checksum %08lx (should be %08lx)\n",
118                    module, func, module32, TD16->checksum, TD32->checksum);
119         return 0;
120     }
121
122     if (!TD32 && checksum && checksum != *(LPDWORD)TD16)
123     {
124         ERR(thunk, "(%s, %s, %s): Wrong checksum %08lx (should be %08lx)\n",
125                    module, func, module32, *(LPDWORD)TD16, checksum);
126         return 0;
127     }
128
129     return TD16;
130 }
131
132 /***********************************************************************
133  *           GetThunkStuff    (KERNEL32.53)
134  */
135 LPVOID WINAPI GetThunkStuff(LPSTR module, LPSTR func)
136 {
137     return _loadthunk(module, func, "<kernel>", NULL, 0L);
138 }
139
140 /***********************************************************************
141  *           GetThunkBuff    (KERNEL32.52)
142  * Returns a pointer to ThkBuf in the 16bit library SYSTHUNK.DLL.
143  */
144 LPVOID WINAPI GetThunkBuff(void)
145 {
146     return GetThunkStuff("SYSTHUNK.DLL", "ThkBuf");
147 }
148
149 /***********************************************************************
150  *              ThunkConnect32          (KERNEL32)
151  * Connects a 32bit and a 16bit thunkbuffer.
152  */
153 UINT32 WINAPI ThunkConnect32( 
154         struct ThunkDataCommon *TD,  /* [in/out] thunkbuffer */
155         LPSTR thunkfun16,            /* [in] win16 thunkfunction */
156         LPSTR module16,              /* [in] name of win16 dll */
157         LPSTR module32,              /* [in] name of win32 dll */
158         HMODULE32 hmod32,            /* [in] hmodule of win32 dll */
159         DWORD dwReason               /* [in] initialisation argument */
160 ) {
161     BOOL32 directionSL;
162
163     if (!lstrncmp32A(TD->magic, "SL01", 4))
164     {
165         directionSL = TRUE;
166
167         TRACE(thunk, "SL01 thunk %s (%lx) <- %s (%s), Reason: %ld\n",
168                      module32, (DWORD)TD, module16, thunkfun16, dwReason);
169     }
170     else if (!lstrncmp32A(TD->magic, "LS01", 4))
171     {
172         directionSL = FALSE;
173
174         TRACE(thunk, "LS01 thunk %s (%lx) -> %s (%s), Reason: %ld\n",
175                      module32, (DWORD)TD, module16, thunkfun16, dwReason);
176     }
177     else
178     {
179         ERR(thunk, "Invalid magic %c%c%c%c\n", 
180                    TD->magic[0], TD->magic[1], TD->magic[2], TD->magic[3]);
181         return 0;
182     }
183     
184     switch (dwReason)
185     {
186         case DLL_PROCESS_ATTACH:
187         {
188             struct ThunkDataCommon *TD16;
189             if (!(TD16 = _loadthunk(module16, thunkfun16, module32, TD, 0L)))
190                 return 0;
191
192             if (directionSL)
193             {
194                 struct ThunkDataSL32 *SL32 = (struct ThunkDataSL32 *)TD;
195                 struct ThunkDataSL16 *SL16 = (struct ThunkDataSL16 *)TD16;
196                 struct SLTargetDB *tdb;
197
198                 if (SL16->fpData == NULL)
199                 {
200                     ERR(thunk, "ThunkConnect16 was not called!\n");
201                     return 0;
202                 }
203
204                 SL32->data = SL16->fpData;
205
206                 tdb = HeapAlloc(GetProcessHeap(), 0, sizeof(*tdb));
207                 tdb->process = PROCESS_Current();
208                 tdb->targetTable = (DWORD *)(thunkfun16 + SL32->offsetTargetTable);
209
210                 tdb->next = SL32->data->targetDB;   /* FIXME: not thread-safe! */
211                 SL32->data->targetDB = tdb;
212
213                 TRACE(thunk, "Process %08lx allocated TargetDB entry for ThunkDataSL %08lx\n", 
214                              (DWORD)PROCESS_Current(), (DWORD)SL32->data);
215             }
216             else
217             {
218                 struct ThunkDataLS32 *LS32 = (struct ThunkDataLS32 *)TD;
219                 struct ThunkDataLS16 *LS16 = (struct ThunkDataLS16 *)TD16;
220
221                 LS32->targetTable = PTR_SEG_TO_LIN(LS16->targetTable);
222
223                 /* write QT_Thunk and FT_Prolog stubs */
224                 _write_qtthunk ((LPBYTE)TD + LS32->offsetQTThunk,  LS32->targetTable);
225                 _write_ftprolog((LPBYTE)TD + LS32->offsetFTProlog, LS32->targetTable);
226             }
227             break;
228         }
229
230         case DLL_PROCESS_DETACH:
231             /* FIXME: cleanup */
232             break;
233     }
234
235     return 1;
236 }
237
238 /**********************************************************************
239  *              QT_Thunk                        (KERNEL32)
240  *
241  * The target address is in EDX.
242  * The 16 bit arguments start at ESP+4.
243  * The number of 16bit argumentbytes is EBP-ESP-0x44 (68 Byte thunksetup).
244  * [ok]
245  */
246 REGS_ENTRYPOINT(QT_Thunk)
247 {
248     CONTEXT context16;
249     DWORD argsize;
250     THDB *thdb = THREAD_Current();
251
252     memcpy(&context16,context,sizeof(context16));
253
254     CS_reg(&context16)  = HIWORD(EDX_reg(context));
255     IP_reg(&context16)  = LOWORD(EDX_reg(context));
256     EBP_reg(&context16) = OFFSETOF( thdb->cur_stack )
257                            + (WORD)&((STACK16FRAME*)0)->bp;
258
259     argsize = EBP_reg(context)-ESP_reg(context)-0x44;
260
261     memcpy( ((LPBYTE)THREAD_STACK16(thdb))-argsize,
262             (LPBYTE)ESP_reg(context)+4, argsize );
263
264     EAX_reg(context) = Callbacks->CallRegisterShortProc( &context16, argsize );
265     EDX_reg(context) = HIWORD(EAX_reg(context));
266 }
267
268
269 /**********************************************************************
270  *              FT_Prolog                       (KERNEL32.233)
271  * 
272  * The set of FT_... thunk routines is used instead of QT_Thunk,
273  * if structures have to be converted from 32-bit to 16-bit
274  * (change of member alignment, conversion of members).
275  *
276  * The thunk function (as created by the thunk compiler) calls
277  * FT_Prolog at the beginning, to set up a stack frame and
278  * allocate a 64 byte buffer on the stack.
279  * The input parameters (target address and some flags) are
280  * saved for later use by FT_Thunk.
281  *
282  * Input:  EDX  16-bit target address (SEGPTR)
283  *         CX   bits  0..7   target number (in target table)
284  *              bits  8..9   some flags (unclear???)
285  *              bits 10..15  number of DWORD arguments
286  *
287  * Output: A new stackframe is created, and a 64 byte buffer
288  *         allocated on the stack. The layout of the stack 
289  *         on return is as follows:
290  *
291  *  (ebp+4)  return address to caller of thunk function
292  *  (ebp)    old EBP
293  *  (ebp-4)  saved EBX register of caller
294  *  (ebp-8)  saved ESI register of caller
295  *  (ebp-12) saved EDI register of caller
296  *  (ebp-16) saved ECX register, containing flags
297  *  (ebp-20) bitmap containing parameters that are to be converted
298  *           by FT_Thunk; it is initialized to 0 by FT_Prolog and
299  *           filled in by the thunk code before calling FT_Thunk
300  *  (ebp-24)
301  *    ...    (unclear)
302  *  (ebp-44)
303  *  (ebp-48) saved EAX register of caller (unclear, never restored???)
304  *  (ebp-52) saved EDX register, containing 16-bit thunk target
305  *  (ebp-56)
306  *    ...    (unclear)
307  *  (ebp-64)
308  *
309  *  ESP is EBP-68 on return.
310  *         
311  */
312
313 REGS_ENTRYPOINT(FT_Prolog)
314 {
315     /* Pop return address to thunk code */
316     EIP_reg(context) = STACK32_POP(context);
317
318     /* Build stack frame */
319     STACK32_PUSH(context, EBP_reg(context));
320     EBP_reg(context) = ESP_reg(context);
321
322     /* Allocate 64-byte Thunk Buffer */
323     ESP_reg(context) -= 64;
324     memset((char *)ESP_reg(context), '\0', 64);
325
326     /* Store Flags (ECX) and Target Address (EDX) */
327     /* Save other registers to be restored later */
328     *(DWORD *)(EBP_reg(context) -  4) = EBX_reg(context);
329     *(DWORD *)(EBP_reg(context) -  8) = ESI_reg(context);
330     *(DWORD *)(EBP_reg(context) - 12) = EDI_reg(context);
331     *(DWORD *)(EBP_reg(context) - 16) = ECX_reg(context);
332
333     *(DWORD *)(EBP_reg(context) - 48) = EAX_reg(context);
334     *(DWORD *)(EBP_reg(context) - 52) = EDX_reg(context);
335     
336     /* Push return address back onto stack */
337     STACK32_PUSH(context, EIP_reg(context));
338 }
339
340 /**********************************************************************
341  *              FT_Thunk                        (KERNEL32.234)
342  *
343  * This routine performs the actual call to 16-bit code, 
344  * similar to QT_Thunk. The differences are:
345  *  - The call target is taken from the buffer created by FT_Prolog
346  *  - Those arguments requested by the thunk code (by setting the
347  *    corresponding bit in the bitmap at EBP-20) are converted
348  *    from 32-bit pointers to segmented pointers (those pointers
349  *    are guaranteed to point to structures copied to the stack
350  *    by the thunk code, so we always use the 16-bit stack selector
351  *    for those addresses).
352  * 
353  *    The bit #i of EBP-20 corresponds here to the DWORD starting at
354  *    ESP+4 + 2*i.
355  * 
356  * FIXME: It is unclear what happens if there are more than 32 WORDs 
357  *        of arguments, so that the single DWORD bitmap is no longer
358  *        sufficient ...
359  */
360
361 REGS_ENTRYPOINT(FT_Thunk)
362 {
363     DWORD mapESPrelative = *(DWORD *)(EBP_reg(context) - 20);
364     DWORD callTarget     = *(DWORD *)(EBP_reg(context) - 52);
365
366     CONTEXT context16;
367     DWORD i, argsize;
368     LPBYTE newstack, oldstack;
369     THDB *thdb = THREAD_Current();
370
371     memcpy(&context16,context,sizeof(context16));
372
373     CS_reg(&context16)  = HIWORD(callTarget);
374     IP_reg(&context16)  = LOWORD(callTarget);
375     EBP_reg(&context16) = OFFSETOF( thdb->cur_stack )
376                            + (WORD)&((STACK16FRAME*)0)->bp;
377
378     argsize  = EBP_reg(context)-ESP_reg(context)-0x44;
379     newstack = ((LPBYTE)THREAD_STACK16(thdb))-argsize;
380     oldstack = (LPBYTE)ESP_reg(context)+4;
381
382     memcpy( newstack, oldstack, argsize );
383
384     for (i = 0; i < 32; i++)    /* NOTE: What about > 32 arguments? */
385         if (mapESPrelative & (1 << i))
386         {
387             SEGPTR *arg = (SEGPTR *)(newstack + 2*i);
388             *arg = PTR_SEG_OFF_TO_SEGPTR(SELECTOROF(thdb->cur_stack), 
389                                          OFFSETOF(thdb->cur_stack) - argsize
390                                          + (*(LPBYTE *)arg - oldstack));
391         }
392
393     EAX_reg(context) = Callbacks->CallRegisterShortProc( &context16, argsize );
394     EDX_reg(context) = HIWORD(EAX_reg(context));
395 }
396
397 /**********************************************************************
398  *              FT_ExitNN               (KERNEL32.218 - 232)
399  *
400  * One of the FT_ExitNN functions is called at the end of the thunk code.
401  * It removes the stack frame created by FT_Prolog, moves the function
402  * return from EBX to EAX (yes, FT_Thunk did use EAX for the return 
403  * value, but the thunk code has moved it from EAX to EBX in the 
404  * meantime ... :-), restores the caller's EBX, ESI, and EDI registers,
405  * and perform a return to the CALLER of the thunk code (while removing
406  * the given number of arguments from the caller's stack).
407  */
408
409 static void FT_Exit(CONTEXT *context, int nPopArgs)
410 {
411     /* Return value is in EBX */
412     EAX_reg(context) = EBX_reg(context);
413
414     /* Restore EBX, ESI, and EDI registers */
415     EBX_reg(context) = *(DWORD *)(EBP_reg(context) -  4);
416     ESI_reg(context) = *(DWORD *)(EBP_reg(context) -  8);
417     EDI_reg(context) = *(DWORD *)(EBP_reg(context) - 12);
418
419     /* Clean up stack frame */
420     ESP_reg(context) = EBP_reg(context);
421     EBP_reg(context) = STACK32_POP(context);
422
423     /* Pop return address to CALLER of thunk code */
424     EIP_reg(context) = STACK32_POP(context);
425     /* Remove arguments */
426     ESP_reg(context) += nPopArgs;
427     /* Push return address back onto stack */
428     STACK32_PUSH(context, EIP_reg(context));
429 }
430
431 REGS_ENTRYPOINT(FT_Exit0)  { FT_Exit(context,  0); }
432 REGS_ENTRYPOINT(FT_Exit4)  { FT_Exit(context,  4); }
433 REGS_ENTRYPOINT(FT_Exit8)  { FT_Exit(context,  8); }
434 REGS_ENTRYPOINT(FT_Exit12) { FT_Exit(context, 12); }
435 REGS_ENTRYPOINT(FT_Exit16) { FT_Exit(context, 16); }
436 REGS_ENTRYPOINT(FT_Exit20) { FT_Exit(context, 20); }
437 REGS_ENTRYPOINT(FT_Exit24) { FT_Exit(context, 24); }
438 REGS_ENTRYPOINT(FT_Exit28) { FT_Exit(context, 28); }
439 REGS_ENTRYPOINT(FT_Exit32) { FT_Exit(context, 32); }
440 REGS_ENTRYPOINT(FT_Exit36) { FT_Exit(context, 36); }
441 REGS_ENTRYPOINT(FT_Exit40) { FT_Exit(context, 40); }
442 REGS_ENTRYPOINT(FT_Exit44) { FT_Exit(context, 44); }
443 REGS_ENTRYPOINT(FT_Exit48) { FT_Exit(context, 48); }
444 REGS_ENTRYPOINT(FT_Exit52) { FT_Exit(context, 52); }
445 REGS_ENTRYPOINT(FT_Exit56) { FT_Exit(context, 56); }
446
447
448 /**********************************************************************
449  *           WOWCallback16 (KERNEL32.62)(WOW32.2)
450  * Calls a win16 function with a single DWORD argument.
451  * RETURNS
452  *      the return value
453  */
454 DWORD WINAPI WOWCallback16(
455         FARPROC16 fproc,        /* [in] win16 function to call */
456         DWORD arg               /* [in] single DWORD argument to function */
457 ) {
458         DWORD   ret;
459         TRACE(thunk,"(%p,0x%08lx)...\n",fproc,arg);
460         ret =  Callbacks->CallWOWCallbackProc(fproc,arg);
461         TRACE(thunk,"... returns %ld\n",ret);
462         return ret;
463 }
464
465 /**********************************************************************
466  *           WOWCallback16Ex (KERNEL32.55)(WOW32.3)
467  * Calls a function in 16bit code.
468  * RETURNS
469  *      TRUE for success
470  */
471 BOOL32 WINAPI WOWCallback16Ex(
472         FARPROC16 vpfn16,       /* [in] win16 function to call */
473         DWORD dwFlags,          /* [in] flags */
474         DWORD cbArgs,           /* [in] nr of arguments */
475         LPVOID pArgs,           /* [in] pointer to arguments (LPDWORD) */
476         LPDWORD pdwRetCode      /* [out] return value of win16 function */
477 ) {
478         return Callbacks->CallWOWCallback16Ex(vpfn16,dwFlags,cbArgs,pArgs,pdwRetCode);
479 }
480
481 /***********************************************************************
482  *              ThunkInitLS     (KERNEL32.43)
483  * A thunkbuffer link routine 
484  * The thunkbuf looks like:
485  *
486  *      00: DWORD       length          ? don't know exactly
487  *      04: SEGPTR      ptr             ? where does it point to?
488  * The pointer ptr is written into the first DWORD of 'thunk'.
489  * (probably correct implemented)
490  * [ok probably]
491  * RETURNS
492  *      segmented pointer to thunk?
493  */
494 DWORD WINAPI ThunkInitLS(
495         LPDWORD thunk,  /* [in] win32 thunk */
496         LPCSTR thkbuf,  /* [in] thkbuffer name in win16 dll */
497         DWORD len,      /* [in] thkbuffer length */
498         LPCSTR dll16,   /* [in] name of win16 dll */
499         LPCSTR dll32    /* [in] name of win32 dll (FIXME: not used?) */
500 ) {
501         LPDWORD         addr;
502
503         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
504                 return 0;
505
506         if (!addr[1])
507                 return 0;
508         *(DWORD*)thunk = addr[1];
509
510         return addr[1];
511 }
512
513 /***********************************************************************
514  *              Common32ThkLS   (KERNEL32.45)
515  * 
516  * This is another 32->16 thunk, independent of the QT_Thunk/FT_Thunk
517  * style thunks. The basic difference is that the parameter conversion 
518  * is done completely on the *16-bit* side here. Thus we do not call
519  * the 16-bit target directly, but call a common entry point instead.
520  * This entry function then calls the target according to the target
521  * number passed in the DI register.
522  * 
523  * Input:  EAX    SEGPTR to the common 16-bit entry point
524  *         CX     offset in thunk table (target number * 4)
525  *         DX     error return value if execution fails (unclear???)
526  *         EDX.HI number of DWORD parameters
527  *
528  * (Note that we need to move the thunk table offset from CX to DI !)
529  *
530  * The called 16-bit stub expects its stack to look like this:
531  *     ...
532  *   (esp+40)  32-bit arguments
533  *     ...
534  *   (esp+8)   32 byte of stack space available as buffer
535  *   (esp)     8 byte return address for use with 0x66 lret 
536  * 
537  * The called 16-bit stub uses a 0x66 lret to return to 32-bit code,
538  * and uses the EAX register to return a DWORD return value.
539  * Thus we need to use a special assembly glue routine 
540  * (CallRegisterLongProc instead of CallRegisterShortProc).
541  *
542  * Finally, we return to the caller, popping the arguments off 
543  * the stack.
544  *
545  * FIXME: The called function uses EBX to return the number of 
546  *        arguments that are to be popped off the caller's stack.
547  *        This is clobbered by the assembly glue, so we simply use
548  *        the original EDX.HI to get the number of arguments.
549  *        (Those two values should be equal anyway ...?)
550  * 
551  */
552 REGS_ENTRYPOINT(Common32ThkLS)
553 {
554     CONTEXT context16;
555     DWORD argsize;
556     THDB *thdb = THREAD_Current();
557
558     memcpy(&context16,context,sizeof(context16));
559
560     DI_reg(&context16)  = CX_reg(context);
561     CS_reg(&context16)  = HIWORD(EAX_reg(context));
562     IP_reg(&context16)  = LOWORD(EAX_reg(context));
563     EBP_reg(&context16) = OFFSETOF( thdb->cur_stack )
564                            + (WORD)&((STACK16FRAME*)0)->bp;
565
566     argsize = HIWORD(EDX_reg(context)) * 4;
567
568     memcpy( ((LPBYTE)THREAD_STACK16(thdb))-argsize,
569             (LPBYTE)ESP_reg(context)+4, argsize );
570
571     EAX_reg(context) = Callbacks->CallRegisterLongProc(&context16, argsize + 32);
572
573     /* Clean up caller's stack frame */
574
575     EIP_reg(context) = STACK32_POP(context);
576     ESP_reg(context) += argsize;
577     STACK32_PUSH(context, EIP_reg(context));
578 }
579
580 /***********************************************************************
581  *              OT_32ThkLSF     (KERNEL32.40)
582  *
583  * YET Another 32->16 thunk. The difference to Common32ThkLS is that
584  * argument processing is done on both the 32-bit and the 16-bit side:
585  * The 32-bit side prepares arguments, copying them onto the stack.
586  * 
587  * When this routine is called, the first word on the stack is the 
588  * number of argument bytes prepared by the 32-bit code, and EDX
589  * contains the 16-bit target address.
590  *
591  * The called 16-bit routine is another relaycode, doing further 
592  * argument processing and then calling the real 16-bit target
593  * whose address is stored at [bp-04].
594  *
595  * The call proceeds using a normal CallRegisterShortProc.
596  * After return from the 16-bit relaycode, the arguments need
597  * to be copied *back* to the 32-bit stack, since the 32-bit
598  * relaycode processes output parameters.
599  * 
600  * Note that we copy twice the number of arguments, since some of the
601  * 16-bit relaycodes in SYSTHUNK.DLL directly access the original
602  * arguments of the caller!
603  *
604  * (Note that this function seems only to be used for
605  *  OLECLI32 -> OLECLI and OLESVR32 -> OLESVR thunking.)
606  */
607 REGS_ENTRYPOINT(OT_32ThkLSF)
608 {
609     CONTEXT context16;
610     DWORD argsize;
611     THDB *thdb = THREAD_Current();
612
613     memcpy(&context16,context,sizeof(context16));
614
615     CS_reg(&context16)  = HIWORD(EDX_reg(context));
616     IP_reg(&context16)  = LOWORD(EDX_reg(context));
617     EBP_reg(&context16) = OFFSETOF( thdb->cur_stack )
618                            + (WORD)&((STACK16FRAME*)0)->bp;
619
620     argsize = 2 * *(WORD *)(ESP_reg(context) + 4) + 2;
621
622     memcpy( ((LPBYTE)THREAD_STACK16(thdb))-argsize,
623             (LPBYTE)ESP_reg(context)+4, argsize );
624
625     EAX_reg(context) = Callbacks->CallRegisterShortProc(&context16, argsize);
626
627     memcpy( (LPBYTE)ESP_reg(context)+4, 
628             ((LPBYTE)THREAD_STACK16(thdb))-argsize, argsize );
629 }
630
631 /***********************************************************************
632  *              ThunkInitLSF            (KERNEL32.41)
633  * A thunk setup routine.
634  * Expects a pointer to a preinitialized thunkbuffer in the first argument
635  * looking like:
636  *      00..03:         unknown (pointer, check _41, _43, _46)
637  *      04: EB1E                jmp +0x20
638  *
639  *      06..23:         unknown (space for replacement code, check .90)
640  *
641  *      24:>E800000000          call offset 29
642  *      29:>58                  pop eax            ( target of call )
643  *      2A: 2D25000000          sub eax,0x00000025 ( now points to offset 4 )
644  *      2F: BAxxxxxxxx          mov edx,xxxxxxxx
645  *      34: 68yyyyyyyy          push KERNEL32.90
646  *      39: C3                  ret
647  *
648  *      3A: EB1E                jmp +0x20
649  *      3E ... 59:      unknown (space for replacement code?)
650  *      5A: E8xxxxxxxx          call <32bitoffset xxxxxxxx>
651  *      5F: 5A                  pop edx
652  *      60: 81EA25xxxxxx        sub edx, 0x25xxxxxx
653  *      66: 52                  push edx
654  *      67: 68xxxxxxxx          push xxxxxxxx
655  *      6C: 68yyyyyyyy          push KERNEL32.89
656  *      71: C3                  ret
657  *      72: end?
658  * This function checks if the code is there, and replaces the yyyyyyyy entries
659  * by the functionpointers.
660  * The thunkbuf looks like:
661  *
662  *      00: DWORD       length          ? don't know exactly
663  *      04: SEGPTR      ptr             ? where does it point to?
664  * The segpointer ptr is written into the first DWORD of 'thunk'.
665  * [ok probably]
666  * RETURNS
667  *      unclear, pointer to win16 thkbuffer?
668  */
669 LPVOID WINAPI ThunkInitLSF(
670         LPBYTE thunk,   /* [in] win32 thunk */
671         LPCSTR thkbuf,  /* [in] thkbuffer name in win16 dll */
672         DWORD len,      /* [in] length of thkbuffer */
673         LPCSTR dll16,   /* [in] name of win16 dll */
674         LPCSTR dll32    /* [in] name of win32 dll */
675 ) {
676         HMODULE32       hkrnl32 = GetModuleHandle32A("KERNEL32");
677         LPDWORD         addr,addr2;
678
679         /* FIXME: add checks for valid code ... */
680         /* write pointers to kernel32.89 and kernel32.90 (+ordinal base of 1) */
681         *(DWORD*)(thunk+0x35) = (DWORD)GetProcAddress32(hkrnl32,(LPSTR)90);
682         *(DWORD*)(thunk+0x6D) = (DWORD)GetProcAddress32(hkrnl32,(LPSTR)89);
683
684         
685         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
686                 return 0;
687
688         addr2 = PTR_SEG_TO_LIN(addr[1]);
689         if (HIWORD(addr2))
690                 *(DWORD*)thunk = (DWORD)addr2;
691
692         return addr2;
693 }
694
695 /***********************************************************************
696  *              FT_PrologPrime                  (KERNEL32.89)
697  * 
698  * This function is called from the relay code installed by
699  * ThunkInitLSF. It replaces the location from where it was 
700  * called by a standard FT_Prolog call stub (which is 'primed'
701  * by inserting the correct target table pointer).
702  * Finally, it calls that stub.
703  * 
704  * Input:  ECX    target number + flags (passed through to FT_Prolog)
705  *        (ESP)   offset of location where target table pointer 
706  *                is stored, relative to the start of the relay code
707  *        (ESP+4) pointer to start of relay code
708  *                (this is where the FT_Prolog call stub gets written to)
709  * 
710  * Note: The two DWORD arguments get popped from the stack.
711  *        
712  */
713 REGS_ENTRYPOINT(FT_PrologPrime)
714 {
715     DWORD  targetTableOffset = STACK32_POP(context);
716     LPBYTE relayCode = (LPBYTE)STACK32_POP(context);
717     DWORD *targetTable = *(DWORD **)(relayCode+targetTableOffset);
718     DWORD  targetNr = LOBYTE(ECX_reg(context));
719
720     _write_ftprolog(relayCode, targetTable);
721
722     /* We should actually call the relay code now, */
723     /* but we skip it and go directly to FT_Prolog */
724     EDX_reg(context) = targetTable[targetNr];
725     __regs_FT_Prolog(context);
726 }
727
728 /***********************************************************************
729  *              QT_ThunkPrime                   (KERNEL32.90)
730  *
731  * This function corresponds to FT_PrologPrime, but installs a 
732  * call stub for QT_Thunk instead.
733  *
734  * Input: (EBP-4) target number (passed through to QT_Thunk)
735  *         EDX    target table pointer location offset
736  *         EAX    start of relay code
737  *      
738  */
739 REGS_ENTRYPOINT(QT_ThunkPrime)
740 {
741     DWORD  targetTableOffset = EDX_reg(context);
742     LPBYTE relayCode = (LPBYTE)EAX_reg(context);
743     DWORD *targetTable = *(DWORD **)(relayCode+targetTableOffset);
744     DWORD  targetNr = LOBYTE(*(DWORD *)(EBP_reg(context) - 4));
745
746     _write_qtthunk(relayCode, targetTable);
747
748     /* We should actually call the relay code now, */
749     /* but we skip it and go directly to QT_Thunk */
750     EDX_reg(context) = targetTable[targetNr];
751     __regs_QT_Thunk(context);
752 }
753
754 /***********************************************************************
755  *                                                      (KERNEL32.46)
756  * Another thunkbuf link routine.
757  * The start of the thunkbuf looks like this:
758  *      00: DWORD       length
759  *      04: SEGPTR      address for thunkbuffer pointer
760  * [ok probably]
761  */
762 VOID WINAPI ThunkInitSL(
763         LPBYTE thunk,           /* [in] start of thunkbuffer */
764         LPCSTR thkbuf,          /* [in] name/ordinal of thunkbuffer in win16 dll */
765         DWORD len,              /* [in] length of thunkbuffer */
766         LPCSTR dll16,           /* [in] name of win16 dll containing the thkbuf */
767         LPCSTR dll32            /* [in] win32 dll. FIXME: strange, unused */
768 ) {
769         LPDWORD         addr;
770
771         if (!(addr = _loadthunk( dll16, thkbuf, dll32, NULL, len )))
772                 return;
773
774         *(DWORD*)PTR_SEG_TO_LIN(addr[1]) = (DWORD)thunk;
775 }
776
777 /**********************************************************************
778  *           SSInit             KERNEL.700
779  * RETURNS
780  *      TRUE for success.
781  */
782 BOOL32 WINAPI SSInit()
783 {
784     return TRUE;
785 }
786
787 /**********************************************************************
788  *           SSOnBigStack       KERNEL32.87
789  * Check if thunking is initialized (ss selector set up etc.)
790  * We do that differently, so just return TRUE.
791  * [ok]
792  * RETURNS
793  *      TRUE for success.
794  */
795 BOOL32 WINAPI SSOnBigStack()
796 {
797     TRACE(thunk, "Yes, thunking is initialized\n");
798     return TRUE;
799 }
800
801 /**********************************************************************
802  *           SSCall
803  * One of the real thunking functions. This one seems to be for 32<->32
804  * thunks. It should probably be capable of crossing processboundaries.
805  *
806  * And YES, I've seen nr=48 (somewhere in the Win95 32<->16 OLE coupling)
807  * [ok]
808  */
809 DWORD WINAPIV SSCall(
810         DWORD nr,       /* [in] number of argument bytes */
811         DWORD flags,    /* [in] FIXME: flags ? */
812         FARPROC32 fun,  /* [in] function to call */
813         ...             /* [in/out] arguments */
814 ) {
815     DWORD i,ret;
816     DWORD *args = ((DWORD *)&fun) + 1;
817
818     if(TRACE_ON(thunk)){
819       dbg_decl_str(thunk, 256);
820       for (i=0;i<nr/4;i++) 
821         dsprintf(thunk,"0x%08lx,",args[i]);
822       TRACE(thunk,"(%ld,0x%08lx,%p,[%s])\n",
823                     nr,flags,fun,dbg_str(thunk));
824     }
825     switch (nr) {
826     case 0:     ret = fun();
827                 break;
828     case 4:     ret = fun(args[0]);
829                 break;
830     case 8:     ret = fun(args[0],args[1]);
831                 break;
832     case 12:    ret = fun(args[0],args[1],args[2]);
833                 break;
834     case 16:    ret = fun(args[0],args[1],args[2],args[3]);
835                 break;
836     case 20:    ret = fun(args[0],args[1],args[2],args[3],args[4]);
837                 break;
838     case 24:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5]);
839                 break;
840     case 28:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
841                 break;
842     case 32:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
843                 break;
844     case 36:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]);
845                 break;
846     case 40:    ret = fun(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
847                 break;
848     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]);
849                 break;
850     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]);
851                 break;
852     default:
853         WARN(thunk,"Unsupported nr of arguments, %ld\n",nr);
854         ret = 0;
855         break;
856
857     }
858     TRACE(thunk," returning %ld ...\n",ret);
859     return ret;
860 }
861
862 /**********************************************************************
863  *           W32S_BackTo32                      (KERNEL32.51)
864  */
865 REGS_ENTRYPOINT(W32S_BackTo32)
866 {
867     LPDWORD stack = (LPDWORD)ESP_reg( context );
868     FARPROC32 proc = (FARPROC32) stack[0];
869
870     EAX_reg( context ) = proc( stack[2], stack[3], stack[4], stack[5], stack[6],
871                                stack[7], stack[8], stack[9], stack[10], stack[11] );
872
873     EIP_reg( context ) = stack[1];
874 }
875
876 /**********************************************************************
877  *                      AllocSLCallback         (KERNEL32)
878  *
879  * Win95 uses some structchains for callbacks. It allocates them
880  * in blocks of 100 entries, size 32 bytes each, layout:
881  * blockstart:
882  *      0:      PTR     nextblockstart
883  *      4:      entry   *first;
884  *      8:      WORD    sel ( start points to blockstart)
885  *      A:      WORD    unknown
886  * 100xentry:
887  *      00..17:         Code
888  *      18:     PDB     *owning_process;
889  *      1C:     PTR     blockstart
890  *
891  * We ignore this for now. (Just a note for further developers)
892  * FIXME: use this method, so we don't waste selectors...
893  *
894  * Following code is then generated by AllocSLCallback. The code is 16 bit, so
895  * the 0x66 prefix switches from word->long registers.
896  *
897  *      665A            pop     edx 
898  *      6668x arg2 x    pushl   <arg2>
899  *      6652            push    edx
900  *      EAx arg1 x      jmpf    <arg1>
901  *
902  * returns the startaddress of this thunk.
903  *
904  * Note, that they look very similair to the ones allocates by THUNK_Alloc.
905  * RETURNS
906  *      segmented pointer to the start of the thunk
907  */
908 DWORD WINAPI
909 AllocSLCallback(
910         DWORD finalizer,        /* [in] finalizer function */
911         DWORD callback          /* [in] callback function */
912 ) {
913         LPBYTE  x,thunk = HeapAlloc( GetProcessHeap(), 0, 32 );
914         WORD    sel;
915
916         x=thunk;
917         *x++=0x66;*x++=0x5a;                            /* popl edx */
918         *x++=0x66;*x++=0x68;*(DWORD*)x=finalizer;x+=4;  /* pushl finalizer */
919         *x++=0x66;*x++=0x52;                            /* pushl edx */
920         *x++=0xea;*(DWORD*)x=callback;x+=4;             /* jmpf callback */
921
922         *(PDB32**)(thunk+18) = PROCESS_Current();
923
924         sel = SELECTOR_AllocBlock( thunk , 32, SEGMENT_CODE, FALSE, FALSE );
925         return (sel<<16)|0;
926 }
927
928 /**********************************************************************
929  *              FreeSLCallback          (KERNEL32.274)
930  * Frees the specified 16->32 callback
931  */
932 void WINAPI
933 FreeSLCallback(
934         DWORD x /* [in] 16 bit callback (segmented pointer?) */
935 ) {
936         FIXME(win32,"(0x%08lx): stub\n",x);
937 }
938
939
940 /**********************************************************************
941  *              GetTEBSelectorFS        (KERNEL.475)
942  *      Set the 16-bit %fs to the 32-bit %fs (current TEB selector)
943  */
944 VOID WINAPI GetTEBSelectorFS( CONTEXT *context ) 
945 {
946     GET_FS( FS_reg(context) );
947 }
948
949 /**********************************************************************
950  *              KERNEL_431              (KERNEL.431)
951  *              IsPeFormat              (W32SYS.2)
952  * Checks the passed filename if it is a PE format executeable
953  * RETURNS
954  *  TRUE, if it is.
955  *  FALSE if not.
956  */
957 BOOL16 WINAPI IsPeFormat(
958         LPSTR   fn,     /* [in] filename to executeable */
959         HFILE16 hf16    /* [in] open file, if filename is NULL */
960 ) {
961         IMAGE_DOS_HEADER        mzh;
962         HFILE32                 hf=HFILE16_TO_HFILE32(hf16);
963         OFSTRUCT                ofs;
964         DWORD                   xmagic;
965
966         if (fn) {
967                 hf = OpenFile32(fn,&ofs,OF_READ);
968                 if (hf==HFILE_ERROR32)
969                         return FALSE;
970         }
971         _llseek32(hf,0,SEEK_SET);
972         if (sizeof(mzh)!=_lread32(hf,&mzh,sizeof(mzh))) {
973                 _lclose32(hf);
974                 return FALSE;
975         }
976         if (mzh.e_magic!=IMAGE_DOS_SIGNATURE) {
977                 WARN(dosmem,"File has not got dos signature!\n");
978                 _lclose32(hf);
979                 return FALSE;
980         }
981         _llseek32(hf,mzh.e_lfanew,SEEK_SET);
982         if (sizeof(DWORD)!=_lread32(hf,&xmagic,sizeof(DWORD))) {
983                 _lclose32(hf);
984                 return FALSE;
985         }
986         _lclose32(hf);
987         return (xmagic == IMAGE_NT_SIGNATURE);
988 }
989
990 /***********************************************************************
991  *           WOWHandle32                        (KERNEL32.57)(WOW32.16)
992  * Converts a win16 handle of type into the respective win32 handle.
993  * We currently just return this handle, since most handles are the same
994  * for win16 and win32.
995  * RETURNS
996  *      The new handle
997  */
998 HANDLE32 WINAPI WOWHandle32(
999         WORD handle,            /* [in] win16 handle */
1000         WOW_HANDLE_TYPE type    /* [in] handle type */
1001 ) {
1002         TRACE(win32,"(0x%04x,%d)\n",handle,type);
1003         return (HANDLE32)handle;
1004 }
1005
1006 /***********************************************************************
1007  *           K32Thk1632Prolog                   (KERNEL32.492)
1008  */
1009 REGS_ENTRYPOINT(K32Thk1632Prolog)
1010 {
1011    LPBYTE code = (LPBYTE)EIP_reg(context) - 5;
1012
1013    /* Arrrgh! SYSTHUNK.DLL just has to re-implement another method
1014       of 16->32 thunks instead of using one of the standard methods!
1015       This means that SYSTHUNK.DLL itself switches to a 32-bit stack,
1016       and does a far call to the 32-bit code segment of OLECLI32/OLESVR32.
1017       Unfortunately, our CallTo/CallFrom mechanism is therefore completely
1018       bypassed, which means it will crash the next time the 32-bit OLE 
1019       code thunks down again to 16-bit (this *will* happen!).
1020
1021       The following hack tries to recognize this situation.
1022       This is possible since the called stubs in OLECLI32/OLESVR32 all
1023       look exactly the same:
1024         00   E8xxxxxxxx    call K32Thk1632Prolog
1025         05   FF55FC        call [ebp-04]
1026         08   E8xxxxxxxx    call K32Thk1632Epilog
1027         0D   66CB          retf
1028
1029       If we recognize this situation, we try to simulate the actions
1030       of our CallTo/CallFrom mechanism by copying the 16-bit stack
1031       to our 32-bit stack, creating a proper STACK16FRAME and 
1032       updating thdb->cur_stack. */ 
1033
1034    if (   code[5] == 0xFF && code[6] == 0x55 && code[7] == 0xFC
1035        && code[13] == 0x66 && code[14] == 0xCB)
1036    {
1037       WORD  stackSel  = NtCurrentTeb()->stack_sel;
1038       DWORD stackBase = GetSelectorBase(stackSel);
1039
1040       THDB *thdb = THREAD_Current();
1041       DWORD argSize = EBP_reg(context) - ESP_reg(context);
1042       char *stack16 = (char *)ESP_reg(context);
1043       char *stack32 = (char *)thdb->cur_stack - argSize;
1044       STACK16FRAME *frame16 = (STACK16FRAME *)stack16 - 1;
1045
1046       TRACE(thunk, "before SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1047                    EBP_reg(context), ESP_reg(context), thdb->cur_stack);
1048
1049       memset(frame16, '\0', sizeof(STACK16FRAME));
1050       frame16->frame32 = (STACK32FRAME *)thdb->cur_stack;
1051       frame16->ebp = EBP_reg(context);
1052
1053       memcpy(stack32, stack16, argSize);
1054       thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR(stackSel, (DWORD)frame16 - stackBase);
1055
1056       ESP_reg(context) = (DWORD)stack32;
1057       EBP_reg(context) = ESP_reg(context) + argSize;
1058
1059       TRACE(thunk, "after  SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1060                    EBP_reg(context), ESP_reg(context), thdb->cur_stack);
1061    }
1062
1063    SYSLEVEL_ReleaseWin16Lock();
1064 }
1065
1066 /***********************************************************************
1067  *           K32Thk1632Epilog                   (KERNEL32.491)
1068  */
1069 REGS_ENTRYPOINT(K32Thk1632Epilog)
1070 {
1071    LPBYTE code = (LPBYTE)EIP_reg(context) - 13;
1072
1073    SYSLEVEL_RestoreWin16Lock();
1074
1075    /* We undo the SYSTHUNK hack if necessary. See K32Thk1632Prolog. */
1076
1077    if (   code[5] == 0xFF && code[6] == 0x55 && code[7] == 0xFC
1078        && code[13] == 0x66 && code[14] == 0xCB)
1079    {
1080       THDB *thdb = THREAD_Current();
1081       STACK16FRAME *frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN(thdb->cur_stack);
1082       char *stack16 = (char *)(frame16 + 1);
1083       DWORD argSize = frame16->ebp - (DWORD)stack16;
1084       char *stack32 = (char *)frame16->frame32 - argSize;
1085
1086       DWORD nArgsPopped = ESP_reg(context) - (DWORD)stack32;
1087
1088       TRACE(thunk, "before SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1089                    EBP_reg(context), ESP_reg(context), thdb->cur_stack);
1090
1091       thdb->cur_stack = (DWORD)frame16->frame32;
1092
1093       ESP_reg(context) = (DWORD)stack16 + nArgsPopped;
1094       EBP_reg(context) = frame16->ebp;
1095
1096       TRACE(thunk, "after  SYSTHUNK hack: EBP: %08lx ESP: %08lx cur_stack: %08lx\n",
1097                    EBP_reg(context), ESP_reg(context), thdb->cur_stack);
1098    }
1099 }
1100