2 * Win32 WOW Generic Thunk API
4 * Copyright 1999 Ulrich Weigand
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/winbase16.h"
31 #include "kernel16_private.h"
32 #include "wine/exception.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(thunk
);
36 WINE_DECLARE_DEBUG_CHANNEL(relay
);
37 WINE_DECLARE_DEBUG_CHANNEL(snoop
);
39 /* symbols exported from relay16.s */
40 extern DWORD WINAPI
wine_call_to_16( FARPROC16 target
, DWORD cbArgs
, PEXCEPTION_HANDLER handler
);
41 extern void WINAPI
wine_call_to_16_regs( CONTEXT
*context
, DWORD cbArgs
, PEXCEPTION_HANDLER handler
);
42 extern void __wine_call_to_16_ret(void);
43 extern void CALL32_CBClient_Ret(void);
44 extern void CALL32_CBClientEx_Ret(void);
45 extern BYTE __wine_call16_start
[];
46 extern BYTE __wine_call16_end
[];
48 static SEGPTR call16_ret_addr
; /* segptr to __wine_call_to_16_ret routine */
50 /***********************************************************************
53 BOOL
WOWTHUNK_Init(void)
55 /* allocate the code selector for CallTo16 routines */
56 WORD codesel
= SELECTOR_AllocBlock( __wine_call16_start
,
57 (BYTE
*)(&CallTo16_TebSelector
+ 1) - __wine_call16_start
,
58 LDT_FLAGS_CODE
| LDT_FLAGS_32BIT
);
59 if (!codesel
) return FALSE
;
61 /* Patch the return addresses for CallTo16 routines */
63 CallTo16_DataSelector
= get_ds();
64 call16_ret_addr
= MAKESEGPTR( codesel
, (BYTE
*)__wine_call_to_16_ret
- __wine_call16_start
);
65 CALL32_CBClient_RetAddr
=
66 MAKESEGPTR( codesel
, (BYTE
*)CALL32_CBClient_Ret
- __wine_call16_start
);
67 CALL32_CBClientEx_RetAddr
=
68 MAKESEGPTR( codesel
, (BYTE
*)CALL32_CBClientEx_Ret
- __wine_call16_start
);
70 if (TRACE_ON(relay
) || TRACE_ON(snoop
)) RELAY16_InitDebugLists();
76 /*************************************************************
79 * Fix a selector load that caused an exception if it's in the
82 static BOOL
fix_selector( CONTEXT
*context
)
85 BYTE
*instr
= (BYTE
*)context
->Eip
;
87 if (instr
< __wine_call16_start
|| instr
>= __wine_call16_end
) return FALSE
;
90 while (*instr
== 0x66 || *instr
== 0x67) instr
++;
94 case 0x07: /* pop es */
95 case 0x17: /* pop ss */
96 case 0x1f: /* pop ds */
98 case 0x0f: /* extended instruction */
101 case 0xa1: /* pop fs */
102 case 0xa9: /* pop gs */
111 stack
= ldt_get_ptr( context
->SegSs
, context
->Esp
);
112 TRACE( "fixing up selector %x for pop instruction\n", *stack
);
118 /*************************************************************
121 * Handler for exceptions occurring in 16-bit code.
123 static DWORD
call16_handler( EXCEPTION_RECORD
*record
, EXCEPTION_REGISTRATION_RECORD
*frame
,
124 CONTEXT
*context
, EXCEPTION_REGISTRATION_RECORD
**pdispatcher
)
126 if (record
->ExceptionFlags
& (EH_UNWINDING
| EH_EXIT_UNWIND
))
128 /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
129 STACK32FRAME
*frame32
= CONTAINING_RECORD(frame
, STACK32FRAME
, frame
);
130 NtCurrentTeb()->WOW32Reserved
= (void *)frame32
->frame16
;
133 else if (record
->ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
||
134 record
->ExceptionCode
== EXCEPTION_PRIV_INSTRUCTION
)
136 if (ldt_is_system(context
->SegCs
))
138 if (fix_selector( context
)) return ExceptionContinueExecution
;
143 DWORD ret
= __wine_emulate_instruction( record
, context
);
145 if (ret
!= ExceptionContinueSearch
) return ret
;
147 /* check for Win16 __GP handler */
148 if ((gpHandler
= HasGPHandler16( MAKESEGPTR( context
->SegCs
, context
->Eip
) )))
150 WORD
*stack
= ldt_get_ptr( context
->SegSs
, context
->Esp
);
151 *--stack
= context
->SegCs
;
152 *--stack
= context
->Eip
;
154 if (!IS_SELECTOR_32BIT(context
->SegSs
))
155 context
->Esp
= MAKELONG( LOWORD(context
->Esp
- 2*sizeof(WORD
)),
156 HIWORD(context
->Esp
) );
158 context
->Esp
-= 2*sizeof(WORD
);
160 context
->SegCs
= SELECTOROF( gpHandler
);
161 context
->Eip
= OFFSETOF( gpHandler
);
162 return ExceptionContinueExecution
;
166 return ExceptionContinueSearch
;
171 * 32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
174 /**********************************************************************
175 * K32WOWGetDescriptor (KERNEL32.70)
177 BOOL WINAPI
K32WOWGetDescriptor( SEGPTR segptr
, LPLDT_ENTRY ldtent
)
179 return GetThreadSelectorEntry( GetCurrentThread(),
180 segptr
>> 16, ldtent
);
183 /**********************************************************************
184 * K32WOWGetVDMPointer (KERNEL32.56)
186 LPVOID WINAPI
K32WOWGetVDMPointer( DWORD vp
, DWORD dwBytes
, BOOL fProtectedMode
)
188 /* FIXME: add size check too */
190 if ( fProtectedMode
)
193 return DOSMEM_MapRealToLinear( vp
);
196 /**********************************************************************
197 * K32WOWGetVDMPointerFix (KERNEL32.68)
199 LPVOID WINAPI
K32WOWGetVDMPointerFix( DWORD vp
, DWORD dwBytes
, BOOL fProtectedMode
)
202 * Hmmm. According to the docu, we should call:
204 * GlobalFix16( SELECTOROF(vp) );
206 * But this is unnecessary under Wine, as we never move global
207 * memory segments in linear memory anyway.
209 * (I'm not so sure what we are *supposed* to do if
210 * fProtectedMode is TRUE, anyway ...)
213 return K32WOWGetVDMPointer( vp
, dwBytes
, fProtectedMode
);
216 /**********************************************************************
217 * K32WOWGetVDMPointerUnfix (KERNEL32.69)
219 VOID WINAPI
K32WOWGetVDMPointerUnfix( DWORD vp
)
222 * See above why we don't call:
224 * GlobalUnfix16( SELECTOROF(vp) );
229 /**********************************************************************
230 * K32WOWGlobalAlloc16 (KERNEL32.59)
232 WORD WINAPI
K32WOWGlobalAlloc16( WORD wFlags
, DWORD cb
)
234 return (WORD
)GlobalAlloc16( wFlags
, cb
);
237 /**********************************************************************
238 * K32WOWGlobalFree16 (KERNEL32.62)
240 WORD WINAPI
K32WOWGlobalFree16( WORD hMem
)
242 return (WORD
)GlobalFree16( (HGLOBAL16
)hMem
);
245 /**********************************************************************
246 * K32WOWGlobalUnlock16 (KERNEL32.61)
248 BOOL WINAPI
K32WOWGlobalUnlock16( WORD hMem
)
250 return (BOOL
)GlobalUnlock16( (HGLOBAL16
)hMem
);
253 /**********************************************************************
254 * K32WOWGlobalAllocLock16 (KERNEL32.63)
256 DWORD WINAPI
K32WOWGlobalAllocLock16( WORD wFlags
, DWORD cb
, WORD
*phMem
)
258 WORD hMem
= K32WOWGlobalAlloc16( wFlags
, cb
);
259 if (phMem
) *phMem
= hMem
;
261 return K32WOWGlobalLock16( hMem
);
264 /**********************************************************************
265 * K32WOWGlobalLockSize16 (KERNEL32.65)
267 DWORD WINAPI
K32WOWGlobalLockSize16( WORD hMem
, PDWORD pcb
)
270 *pcb
= GlobalSize16( (HGLOBAL16
)hMem
);
272 return K32WOWGlobalLock16( hMem
);
275 /**********************************************************************
276 * K32WOWGlobalUnlockFree16 (KERNEL32.64)
278 WORD WINAPI
K32WOWGlobalUnlockFree16( DWORD vpMem
)
280 if ( !K32WOWGlobalUnlock16( HIWORD(vpMem
) ) )
283 return K32WOWGlobalFree16( HIWORD(vpMem
) );
287 /**********************************************************************
288 * K32WOWYield16 (KERNEL32.66)
290 VOID WINAPI
K32WOWYield16( void )
293 * This does the right thing for both Win16 and Win32 tasks.
294 * More or less, at least :-/
299 /**********************************************************************
300 * K32WOWDirectedYield16 (KERNEL32.67)
302 VOID WINAPI
K32WOWDirectedYield16( WORD htask16
)
305 * Argh. Our scheduler doesn't like DirectedYield by Win32
306 * tasks at all. So we do hope that this routine is indeed
307 * only ever called by Win16 tasks that have thunked up ...
309 DirectedYield16( (HTASK16
)htask16
);
313 /***********************************************************************
314 * K32WOWHandle32 (KERNEL32.57)
316 HANDLE WINAPI
K32WOWHandle32( WORD handle
, WOW_HANDLE_TYPE type
)
327 case WOW_TYPE_HBITMAP
:
328 case WOW_TYPE_HBRUSH
:
329 case WOW_TYPE_HPALETTE
:
331 case WOW_TYPE_HACCEL
:
332 return (HANDLE
)(ULONG_PTR
)handle
;
334 case WOW_TYPE_HMETAFILE
:
335 FIXME( "conversion of metafile handles not supported yet\n" );
336 return (HANDLE
)(ULONG_PTR
)handle
;
339 return ((TDB
*)GlobalLock16(handle
))->teb
->ClientId
.UniqueThread
;
341 case WOW_TYPE_FULLHWND
:
342 FIXME( "conversion of full window handles not supported yet\n" );
343 return (HANDLE
)(ULONG_PTR
)handle
;
346 ERR( "handle 0x%04x of unknown type %d\n", handle
, type
);
347 return (HANDLE
)(ULONG_PTR
)handle
;
351 /***********************************************************************
352 * K32WOWHandle16 (KERNEL32.58)
354 WORD WINAPI
K32WOWHandle16( HANDLE handle
, WOW_HANDLE_TYPE type
)
365 case WOW_TYPE_HBITMAP
:
366 case WOW_TYPE_HBRUSH
:
367 case WOW_TYPE_HPALETTE
:
369 case WOW_TYPE_HACCEL
:
370 case WOW_TYPE_FULLHWND
:
371 if ( HIWORD(handle
) )
372 ERR( "handle %p of type %d has non-zero HIWORD\n", handle
, type
);
373 return LOWORD(handle
);
375 case WOW_TYPE_HMETAFILE
:
376 FIXME( "conversion of metafile handles not supported yet\n" );
377 return LOWORD(handle
);
380 return TASK_GetTaskFromThread( (DWORD
)handle
);
383 ERR( "handle %p of unknown type %d\n", handle
, type
);
384 return LOWORD(handle
);
388 /**********************************************************************
389 * K32WOWCallback16Ex (KERNEL32.55)
391 BOOL WINAPI
K32WOWCallback16Ex( DWORD vpfn16
, DWORD dwFlags
,
392 DWORD cbArgs
, LPVOID pArgs
, LPDWORD pdwRetCode
)
395 * Arguments must be prepared in the correct order by the caller
396 * (both for PASCAL and CDECL calling convention), so we simply
397 * copy them to the 16-bit stack ...
399 char *stack
= (char *)CURRENT_STACK16
- cbArgs
;
401 memcpy( stack
, pArgs
, cbArgs
);
403 if (dwFlags
& (WCB16_REGS
|WCB16_REGS_LONG
))
405 CONTEXT
*context
= (CONTEXT
*)pdwRetCode
;
409 DWORD count
= cbArgs
/ sizeof(WORD
);
410 WORD
* wstack
= (WORD
*)stack
;
412 TRACE_(relay
)( "\1CallTo16(func=%04x:%04x", context
->SegCs
, LOWORD(context
->Eip
) );
413 while (count
) TRACE_(relay
)( ",%04x", wstack
[--count
] );
414 TRACE_(relay
)( ") ss:sp=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x ds=%04x es=%04x\n",
415 SELECTOROF(NtCurrentTeb()->WOW32Reserved
),
416 OFFSETOF(NtCurrentTeb()->WOW32Reserved
),
417 (WORD
)context
->Eax
, (WORD
)context
->Ebx
, (WORD
)context
->Ecx
,
418 (WORD
)context
->Edx
, (WORD
)context
->Esi
, (WORD
)context
->Edi
,
419 (WORD
)context
->Ebp
, (WORD
)context
->SegDs
, (WORD
)context
->SegEs
);
420 SYSLEVEL_CheckNotLevel( 2 );
423 /* push return address */
424 if (dwFlags
& WCB16_REGS_LONG
)
426 stack
-= sizeof(DWORD
);
427 *((DWORD
*)stack
) = HIWORD(call16_ret_addr
);
428 stack
-= sizeof(DWORD
);
429 *((DWORD
*)stack
) = LOWORD(call16_ret_addr
);
430 cbArgs
+= 2 * sizeof(DWORD
);
434 stack
-= sizeof(SEGPTR
);
435 *((SEGPTR
*)stack
) = call16_ret_addr
;
436 cbArgs
+= sizeof(SEGPTR
);
440 wine_call_to_16_regs( context
, cbArgs
, call16_handler
);
445 TRACE_(relay
)( "\1RetFrom16() ss:sp=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
446 SELECTOROF(NtCurrentTeb()->WOW32Reserved
),
447 OFFSETOF(NtCurrentTeb()->WOW32Reserved
),
448 (WORD
)context
->Eax
, (WORD
)context
->Ebx
, (WORD
)context
->Ecx
,
449 (WORD
)context
->Edx
, (WORD
)context
->Ebp
, (WORD
)context
->Esp
);
450 SYSLEVEL_CheckNotLevel( 2 );
459 DWORD count
= cbArgs
/ sizeof(WORD
);
460 WORD
* wstack
= (WORD
*)stack
;
462 TRACE_(relay
)( "\1CallTo16(func=%04x:%04x,ds=%04x",
463 HIWORD(vpfn16
), LOWORD(vpfn16
), SELECTOROF(NtCurrentTeb()->WOW32Reserved
) );
464 while (count
) TRACE_(relay
)( ",%04x", wstack
[--count
] );
465 TRACE_(relay
)( ") ss:sp=%04x:%04x\n", SELECTOROF(NtCurrentTeb()->WOW32Reserved
),
466 OFFSETOF(NtCurrentTeb()->WOW32Reserved
) );
467 SYSLEVEL_CheckNotLevel( 2 );
470 /* push return address */
471 stack
-= sizeof(SEGPTR
);
472 *((SEGPTR
*)stack
) = call16_ret_addr
;
473 cbArgs
+= sizeof(SEGPTR
);
476 * Actually, we should take care whether the called routine cleans up
477 * its stack or not. Fortunately, our wine_call_to_16 core doesn't rely on
478 * the callee to do so; after the routine has returned, the 16-bit
479 * stack pointer is always reset to the position it had before.
482 ret
= wine_call_to_16( (FARPROC16
)vpfn16
, cbArgs
, call16_handler
);
483 if (pdwRetCode
) *pdwRetCode
= ret
;
488 TRACE_(relay
)( "\1RetFrom16() ss:sp=%04x:%04x retval=%08x\n",
489 SELECTOROF(NtCurrentTeb()->WOW32Reserved
),
490 OFFSETOF(NtCurrentTeb()->WOW32Reserved
), ret
);
491 SYSLEVEL_CheckNotLevel( 2 );
495 return TRUE
; /* success */
498 /**********************************************************************
499 * K32WOWCallback16 (KERNEL32.54)
501 DWORD WINAPI
K32WOWCallback16( DWORD vpfn16
, DWORD dwParam
)
505 if ( !K32WOWCallback16Ex( vpfn16
, WCB16_PASCAL
,
506 sizeof(DWORD
), &dwParam
, &ret
) )