| /* | |
| * Emulator thunks | |
| * | |
| * Copyright 1996, 1997 Alexandre Julliard | |
| * Copyright 1998 Ulrich Weigand | |
| */ | |
| #include <string.h> | |
| #include "wine/winbase16.h" | |
| #include "task.h" | |
| #include "hook.h" | |
| #include "callback.h" | |
| #include "builtin16.h" | |
| #include "user.h" | |
| #include "heap.h" | |
| #include "neexe.h" | |
| #include "process.h" | |
| #include "stackframe.h" | |
| #include "win.h" | |
| #include "flatthunk.h" | |
| #include "mouse.h" | |
| #include "selectors.h" | |
| #include "keyboard.h" | |
| #include "debugtools.h" | |
| DEFAULT_DEBUG_CHANNEL(thunk) | |
| /* List of the 16-bit callback functions. This list is used */ | |
| /* by the build program to generate the file if1632/callto16.S */ | |
| /* ### start build ### */ | |
| extern WORD CALLBACK THUNK_CallTo16_word_ (FARPROC16); | |
| extern WORD CALLBACK THUNK_CallTo16_word_w (FARPROC16,WORD); | |
| extern WORD CALLBACK THUNK_CallTo16_word_l (FARPROC16,LONG); | |
| extern LONG CALLBACK THUNK_CallTo16_long_l (FARPROC16,LONG); | |
| extern WORD CALLBACK THUNK_CallTo16_word_ww (FARPROC16,WORD,WORD); | |
| extern LONG CALLBACK THUNK_CallTo16_long_ll (FARPROC16,LONG,LONG); | |
| extern WORD CALLBACK THUNK_CallTo16_word_www (FARPROC16,WORD,WORD,WORD); | |
| extern WORD CALLBACK THUNK_CallTo16_word_lllw (FARPROC16,LONG,LONG,LONG,WORD); | |
| extern WORD CALLBACK THUNK_CallTo16_word_lwww (FARPROC16,LONG,WORD,WORD,WORD); | |
| extern WORD CALLBACK THUNK_CallTo16_word_wlww (FARPROC16,WORD,LONG,WORD,WORD); | |
| extern WORD CALLBACK THUNK_CallTo16_word_wwwl (FARPROC16,WORD,WORD,WORD,LONG); | |
| extern LONG CALLBACK THUNK_CallTo16_long_wwwl (FARPROC16,WORD,WORD,WORD,LONG); | |
| extern WORD CALLBACK THUNK_CallTo16_word_lwwww(FARPROC16,LONG,WORD,WORD,WORD,WORD); | |
| /* ### stop build ### */ | |
| static THUNK *firstThunk = NULL; | |
| static BOOL THUNK_ThunkletInit( void ); | |
| /* Callbacks function table for the emulator */ | |
| static const CALLBACKS_TABLE CALLBACK_EmulatorTable = | |
| { | |
| (void *)CallTo16RegisterShort, /* CallRegisterShortProc */ | |
| (void *)CallTo16RegisterLong, /* CallRegisterLongProc */ | |
| (void *)THUNK_CallTo16_word_w, /* CallWindowsExitProc */ | |
| (void *)THUNK_CallTo16_word_lwww, /* CallWordBreakProc */ | |
| (void *)THUNK_CallTo16_word_ww, /* CallBootAppProc */ | |
| (void *)THUNK_CallTo16_word_www, /* CallLoadAppSegProc */ | |
| (void *)THUNK_CallTo16_word_www, /* CallLocalNotifyFunc */ | |
| (void *)THUNK_CallTo16_word_www, /* CallResourceHandlerProc */ | |
| (void *)THUNK_CallTo16_long_ll, /* CallUTProc */ | |
| (void *)THUNK_CallTo16_long_l /* CallASPIPostProc */ | |
| }; | |
| const CALLBACKS_TABLE *Callbacks = &CALLBACK_EmulatorTable; | |
| CALLOUT_TABLE Callout = { 0 }; | |
| /*********************************************************************** | |
| * THUNK_Init | |
| */ | |
| BOOL THUNK_Init(void) | |
| { | |
| /* Initialize Thunklets */ | |
| return THUNK_ThunkletInit(); | |
| } | |
| /*********************************************************************** | |
| * THUNK_Alloc | |
| */ | |
| FARPROC THUNK_Alloc( FARPROC16 func, RELAY relay ) | |
| { | |
| HANDLE16 hSeg; | |
| NE_MODULE *pModule; | |
| THUNK *thunk; | |
| /* NULL maps to NULL */ | |
| if ( !func ) return NULL; | |
| /* | |
| * If we got an 16-bit built-in API entry point, retrieve the Wine | |
| * 32-bit handler for that API routine. | |
| * | |
| * NOTE: For efficiency reasons, we only check whether the selector | |
| * of 'func' points to the code segment of a built-in module. | |
| * It might be theoretically possible that the offset is such | |
| * that 'func' does not point, in fact, to an API entry point. | |
| * In this case, however, the pointer is corrupt anyway. | |
| */ | |
| hSeg = GlobalHandle16( SELECTOROF( func ) ); | |
| pModule = NE_GetPtr( FarGetOwner16( hSeg ) ); | |
| if ( pModule && (pModule->flags & NE_FFLAGS_BUILTIN) | |
| && NE_SEG_TABLE(pModule)[0].hSeg == hSeg ) | |
| { | |
| FARPROC proc = (FARPROC)((ENTRYPOINT16 *)PTR_SEG_TO_LIN( func ))->target; | |
| TRACE( "(%04x:%04x, %p) -> built-in API %p\n", | |
| SELECTOROF( func ), OFFSETOF( func ), relay, proc ); | |
| return proc; | |
| } | |
| /* Otherwise, we need to alloc a thunk */ | |
| thunk = HeapAlloc( GetProcessHeap(), 0, sizeof(*thunk) ); | |
| if (thunk) | |
| { | |
| thunk->popl_eax = 0x58; | |
| thunk->pushl_func = 0x68; | |
| thunk->proc = func; | |
| thunk->pushl_eax = 0x50; | |
| thunk->jmp = 0xe9; | |
| thunk->relay = (RELAY)((char *)relay - (char *)(&thunk->next)); | |
| thunk->magic = CALLTO16_THUNK_MAGIC; | |
| thunk->next = firstThunk; | |
| firstThunk = thunk; | |
| } | |
| TRACE( "(%04x:%04x, %p) -> allocated thunk %p\n", | |
| SELECTOROF( func ), OFFSETOF( func ), relay, thunk ); | |
| return (FARPROC)thunk; | |
| } | |
| /*********************************************************************** | |
| * THUNK_Free | |
| */ | |
| void THUNK_Free( FARPROC thunk ) | |
| { | |
| THUNK *t = (THUNK*)thunk; | |
| if ( !t || IsBadReadPtr( t, sizeof(*t) ) | |
| || t->magic != CALLTO16_THUNK_MAGIC ) | |
| return; | |
| if (HEAP_IsInsideHeap( GetProcessHeap(), 0, t )) | |
| { | |
| THUNK **prev = &firstThunk; | |
| while (*prev && (*prev != t)) prev = &(*prev)->next; | |
| if (*prev) | |
| { | |
| *prev = t->next; | |
| HeapFree( GetProcessHeap(), 0, t ); | |
| return; | |
| } | |
| } | |
| ERR("invalid thunk addr %p\n", thunk ); | |
| return; | |
| } | |
| /*********************************************************************** | |
| * THUNK_GetCalloutThunk | |
| * | |
| * Retrieve API entry point with given name from given module. | |
| * If module is builtin, return the 32-bit entry point, otherwise | |
| * create a 32->16 thunk to the 16-bit entry point, using the | |
| * given relay code. | |
| * | |
| */ | |
| static FARPROC THUNK_GetCalloutThunk( NE_MODULE *pModule, LPSTR name, RELAY relay ) | |
| { | |
| FARPROC16 proc = WIN32_GetProcAddress16( pModule->self, name ); | |
| if ( !proc ) return 0; | |
| if ( pModule->flags & NE_FFLAGS_BUILTIN ) | |
| return (FARPROC)((ENTRYPOINT16 *)PTR_SEG_TO_LIN( proc ))->target; | |
| else | |
| return (FARPROC)THUNK_Alloc( proc, relay ); | |
| } | |
| /*********************************************************************** | |
| * THUNK_InitCallout | |
| */ | |
| void THUNK_InitCallout(void) | |
| { | |
| HMODULE hModule; | |
| NE_MODULE *pModule; | |
| hModule = GetModuleHandleA( "USER32" ); | |
| if ( hModule ) | |
| { | |
| #define GETADDR( name ) \ | |
| *(FARPROC *)&Callout.##name = GetProcAddress( hModule, #name ) | |
| GETADDR( PeekMessageA ); | |
| GETADDR( PeekMessageW ); | |
| GETADDR( GetMessageA ); | |
| GETADDR( GetMessageW ); | |
| GETADDR( SendMessageA ); | |
| GETADDR( SendMessageW ); | |
| GETADDR( PostMessageA ); | |
| GETADDR( PostMessageW ); | |
| GETADDR( PostThreadMessageA ); | |
| GETADDR( PostThreadMessageW ); | |
| GETADDR( TranslateMessage ); | |
| GETADDR( DispatchMessageW ); | |
| GETADDR( DispatchMessageA ); | |
| GETADDR( RedrawWindow ); | |
| GETADDR( WaitForInputIdle ); | |
| GETADDR( MessageBoxA ); | |
| GETADDR( MessageBoxW ); | |
| #undef GETADDR | |
| } | |
| pModule = NE_GetPtr( GetModuleHandle16( "USER" ) ); | |
| if ( pModule ) | |
| { | |
| #define GETADDR( var, name, thk ) \ | |
| *(FARPROC *)&Callout.##var = THUNK_GetCalloutThunk( pModule, name, \ | |
| (RELAY)THUNK_CallTo16_##thk ) | |
| GETADDR( PeekMessage16, "PeekMessage", word_lwwww ); | |
| GETADDR( GetMessage16, "GetMessage", word_lwww ); | |
| GETADDR( SendMessage16, "SendMessage", long_wwwl ); | |
| GETADDR( PostMessage16, "PostMessage", word_wwwl ); | |
| GETADDR( PostAppMessage16, "PostAppMessage", word_wwwl ); | |
| GETADDR( TranslateMessage16, "TranslateMessage", word_l ); | |
| GETADDR( DispatchMessage16, "DispatchMessage", long_l ); | |
| GETADDR( RedrawWindow16, "RedrawWindow", word_wlww ); | |
| GETADDR( FinalUserInit16, "FinalUserInit", word_ ); | |
| GETADDR( InitApp16, "InitApp", word_w ); | |
| GETADDR( InitThreadInput16, "InitThreadInput", word_ww ); | |
| GETADDR( UserYield16, "UserYield", word_ ); | |
| GETADDR( DestroyIcon32, "DestroyIcon32", word_ww ); | |
| GETADDR( UserSignalProc, "SignalProc32", word_lllw ); | |
| #undef GETADDR | |
| } | |
| } | |
| /*********************************************************************** | |
| * 16->32 Flat Thunk routines: | |
| */ | |
| /*********************************************************************** | |
| * ThunkConnect16 (KERNEL.651) | |
| * Connects a 32bit and a 16bit thunkbuffer. | |
| */ | |
| UINT WINAPI ThunkConnect16( | |
| LPSTR module16, /* [in] name of win16 dll */ | |
| LPSTR module32, /* [in] name of win32 dll */ | |
| HINSTANCE16 hInst16, /* [in] hInst of win16 dll */ | |
| DWORD dwReason, /* [in] initialisation argument */ | |
| struct ThunkDataCommon *TD, /* [in/out] thunkbuffer */ | |
| LPSTR thunkfun32, /* [in] win32 thunkfunction */ | |
| WORD cs /* [in] CS of win16 dll */ | |
| ) { | |
| BOOL directionSL; | |
| if (!strncmp(TD->magic, "SL01", 4)) | |
| { | |
| directionSL = TRUE; | |
| TRACE("SL01 thunk %s (%lx) -> %s (%s), Reason: %ld\n", | |
| module16, (DWORD)TD, module32, thunkfun32, dwReason); | |
| } | |
| else if (!strncmp(TD->magic, "LS01", 4)) | |
| { | |
| directionSL = FALSE; | |
| TRACE("LS01 thunk %s (%lx) <- %s (%s), Reason: %ld\n", | |
| module16, (DWORD)TD, module32, thunkfun32, dwReason); | |
| } | |
| else | |
| { | |
| ERR("Invalid magic %c%c%c%c\n", | |
| TD->magic[0], TD->magic[1], TD->magic[2], TD->magic[3]); | |
| return 0; | |
| } | |
| switch (dwReason) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| if (directionSL) | |
| { | |
| struct ThunkDataSL16 *SL16 = (struct ThunkDataSL16 *)TD; | |
| struct ThunkDataSL *SL = SL16->fpData; | |
| if (SL == NULL) | |
| { | |
| SL = HeapAlloc(GetProcessHeap(), 0, sizeof(*SL)); | |
| SL->common = SL16->common; | |
| SL->flags1 = SL16->flags1; | |
| SL->flags2 = SL16->flags2; | |
| SL->apiDB = PTR_SEG_TO_LIN(SL16->apiDatabase); | |
| SL->targetDB = NULL; | |
| lstrcpynA(SL->pszDll16, module16, 255); | |
| lstrcpynA(SL->pszDll32, module32, 255); | |
| /* We should create a SEGPTR to the ThunkDataSL, | |
| but since the contents are not in the original format, | |
| any access to this by 16-bit code would crash anyway. */ | |
| SL16->spData = 0; | |
| SL16->fpData = SL; | |
| } | |
| if (SL->flags2 & 0x80000000) | |
| { | |
| TRACE("Preloading 32-bit library\n"); | |
| LoadLibraryA(module32); | |
| } | |
| } | |
| else | |
| { | |
| /* nothing to do */ | |
| } | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| /* FIXME: cleanup */ | |
| break; | |
| } | |
| return 1; | |
| } | |
| /*********************************************************************** | |
| * C16ThkSL (KERNEL.630) | |
| */ | |
| void WINAPI C16ThkSL(CONTEXT86 *context) | |
| { | |
| LPBYTE stub = PTR_SEG_TO_LIN(EAX_reg(context)), x = stub; | |
| WORD cs = __get_cs(); | |
| WORD ds = __get_ds(); | |
| /* We produce the following code: | |
| * | |
| * mov ax, __FLATDS | |
| * mov es, ax | |
| * movzx ecx, cx | |
| * mov edx, es:[ecx + $EDX] | |
| * push bp | |
| * push edx | |
| * push dx | |
| * push edx | |
| * call __FLATCS:CallFrom16Thunk | |
| */ | |
| *x++ = 0xB8; *((WORD *)x)++ = ds; | |
| *x++ = 0x8E; *x++ = 0xC0; | |
| *x++ = 0x66; *x++ = 0x0F; *x++ = 0xB7; *x++ = 0xC9; | |
| *x++ = 0x67; *x++ = 0x66; *x++ = 0x26; *x++ = 0x8B; | |
| *x++ = 0x91; *((DWORD *)x)++ = EDX_reg(context); | |
| *x++ = 0x55; | |
| *x++ = 0x66; *x++ = 0x52; | |
| *x++ = 0x52; | |
| *x++ = 0x66; *x++ = 0x52; | |
| *x++ = 0x66; *x++ = 0x9A; *((DWORD *)x)++ = (DWORD)CallFrom16Thunk; | |
| *((WORD *)x)++ = cs; | |
| /* Jump to the stub code just created */ | |
| EIP_reg(context) = LOWORD(EAX_reg(context)); | |
| CS_reg(context) = HIWORD(EAX_reg(context)); | |
| /* Since C16ThkSL got called by a jmp, we need to leave the | |
| original return address on the stack */ | |
| ESP_reg(context) -= 4; | |
| } | |
| /*********************************************************************** | |
| * C16ThkSL01 (KERNEL.631) | |
| */ | |
| void WINAPI C16ThkSL01(CONTEXT86 *context) | |
| { | |
| LPBYTE stub = PTR_SEG_TO_LIN(EAX_reg(context)), x = stub; | |
| if (stub) | |
| { | |
| struct ThunkDataSL16 *SL16 = PTR_SEG_TO_LIN(EDX_reg(context)); | |
| struct ThunkDataSL *td = SL16->fpData; | |
| DWORD procAddress = (DWORD)GetProcAddress16(GetModuleHandle16("KERNEL"), 631); | |
| WORD cs = __get_cs(); | |
| if (!td) | |
| { | |
| ERR("ThunkConnect16 was not called!\n"); | |
| return; | |
| } | |
| TRACE("Creating stub for ThunkDataSL %08lx\n", (DWORD)td); | |
| /* We produce the following code: | |
| * | |
| * xor eax, eax | |
| * mov edx, $td | |
| * call C16ThkSL01 | |
| * push bp | |
| * push edx | |
| * push dx | |
| * push edx | |
| * call __FLATCS:CallFrom16Thunk | |
| */ | |
| *x++ = 0x66; *x++ = 0x33; *x++ = 0xC0; | |
| *x++ = 0x66; *x++ = 0xBA; *((DWORD *)x)++ = (DWORD)td; | |
| *x++ = 0x9A; *((DWORD *)x)++ = procAddress; | |
| *x++ = 0x55; | |
| *x++ = 0x66; *x++ = 0x52; | |
| *x++ = 0x52; | |
| *x++ = 0x66; *x++ = 0x52; | |
| *x++ = 0x66; *x++ = 0x9A; *((DWORD *)x)++ = (DWORD)CallFrom16Thunk; | |
| *((WORD *)x)++ = cs; | |
| /* Jump to the stub code just created */ | |
| EIP_reg(context) = LOWORD(EAX_reg(context)); | |
| CS_reg(context) = HIWORD(EAX_reg(context)); | |
| /* Since C16ThkSL01 got called by a jmp, we need to leave the | |
| orginal return address on the stack */ | |
| ESP_reg(context) -= 4; | |
| } | |
| else | |
| { | |
| struct ThunkDataSL *td = (struct ThunkDataSL *)EDX_reg(context); | |
| DWORD targetNr = CX_reg(context) / 4; | |
| struct SLTargetDB *tdb; | |
| TRACE("Process %08lx calling target %ld of ThunkDataSL %08lx\n", | |
| (DWORD)PROCESS_Current(), targetNr, (DWORD)td); | |
| for (tdb = td->targetDB; tdb; tdb = tdb->next) | |
| if (tdb->process == PROCESS_Current()) | |
| break; | |
| if (!tdb) | |
| { | |
| TRACE("Loading 32-bit library %s\n", td->pszDll32); | |
| LoadLibraryA(td->pszDll32); | |
| for (tdb = td->targetDB; tdb; tdb = tdb->next) | |
| if (tdb->process == PROCESS_Current()) | |
| break; | |
| } | |
| if (tdb) | |
| { | |
| EDX_reg(context) = tdb->targetTable[targetNr]; | |
| TRACE("Call target is %08lx\n", EDX_reg(context)); | |
| } | |
| else | |
| { | |
| WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), LOWORD(ESP_reg(context))); | |
| DX_reg(context) = HIWORD(td->apiDB[targetNr].errorReturnValue); | |
| AX_reg(context) = LOWORD(td->apiDB[targetNr].errorReturnValue); | |
| EIP_reg(context) = stack[2]; | |
| CS_reg(context) = stack[3]; | |
| ESP_reg(context) += td->apiDB[targetNr].nrArgBytes + 4; | |
| ERR("Process %08lx did not ThunkConnect32 %s to %s\n", | |
| (DWORD)PROCESS_Current(), td->pszDll32, td->pszDll16); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * 16<->32 Thunklet/Callback API: | |
| */ | |
| #include "pshpack1.h" | |
| typedef struct _THUNKLET | |
| { | |
| BYTE prefix_target; | |
| BYTE pushl_target; | |
| DWORD target; | |
| BYTE prefix_relay; | |
| BYTE pushl_relay; | |
| DWORD relay; | |
| BYTE jmp_glue; | |
| DWORD glue; | |
| BYTE type; | |
| HINSTANCE16 owner; | |
| struct _THUNKLET *next; | |
| } THUNKLET; | |
| #include "poppack.h" | |
| #define THUNKLET_TYPE_LS 1 | |
| #define THUNKLET_TYPE_SL 2 | |
| static HANDLE ThunkletHeap = 0; | |
| static THUNKLET *ThunkletAnchor = NULL; | |
| static FARPROC ThunkletSysthunkGlueLS = 0; | |
| static SEGPTR ThunkletSysthunkGlueSL = 0; | |
| static FARPROC ThunkletCallbackGlueLS = 0; | |
| static SEGPTR ThunkletCallbackGlueSL = 0; | |
| /*********************************************************************** | |
| * THUNK_ThunkletInit | |
| */ | |
| static BOOL THUNK_ThunkletInit( void ) | |
| { | |
| LPBYTE thunk; | |
| ThunkletHeap = HeapCreate(HEAP_WINE_SEGPTR | HEAP_WINE_CODE16SEG, 0, 0); | |
| if (!ThunkletHeap) return FALSE; | |
| thunk = HeapAlloc( ThunkletHeap, 0, 5 ); | |
| if (!thunk) return FALSE; | |
| ThunkletSysthunkGlueLS = (FARPROC)thunk; | |
| *thunk++ = 0x58; /* popl eax */ | |
| *thunk++ = 0xC3; /* ret */ | |
| ThunkletSysthunkGlueSL = HEAP_GetSegptr( ThunkletHeap, 0, thunk ); | |
| *thunk++ = 0x66; *thunk++ = 0x58; /* popl eax */ | |
| *thunk++ = 0xCB; /* lret */ | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * SetThunkletCallbackGlue (KERNEL.560) | |
| */ | |
| void WINAPI SetThunkletCallbackGlue16( FARPROC glueLS, SEGPTR glueSL ) | |
| { | |
| ThunkletCallbackGlueLS = glueLS; | |
| ThunkletCallbackGlueSL = glueSL; | |
| } | |
| /*********************************************************************** | |
| * THUNK_FindThunklet | |
| */ | |
| THUNKLET *THUNK_FindThunklet( DWORD target, DWORD relay, | |
| DWORD glue, BYTE type ) | |
| { | |
| THUNKLET *thunk; | |
| for (thunk = ThunkletAnchor; thunk; thunk = thunk->next) | |
| if ( thunk->type == type | |
| && thunk->target == target | |
| && thunk->relay == relay | |
| && ( type == THUNKLET_TYPE_LS ? | |
| ( thunk->glue == glue - (DWORD)&thunk->type ) | |
| : ( thunk->glue == glue ) ) ) | |
| return thunk; | |
| return NULL; | |
| } | |
| /*********************************************************************** | |
| * THUNK_AllocLSThunklet | |
| */ | |
| FARPROC THUNK_AllocLSThunklet( SEGPTR target, DWORD relay, | |
| FARPROC glue, HTASK16 owner ) | |
| { | |
| THUNKLET *thunk = THUNK_FindThunklet( (DWORD)target, relay, (DWORD)glue, | |
| THUNKLET_TYPE_LS ); | |
| if (!thunk) | |
| { | |
| TDB *pTask = (TDB*)GlobalLock16( owner ); | |
| if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) ) | |
| return 0; | |
| thunk->prefix_target = thunk->prefix_relay = 0x90; | |
| thunk->pushl_target = thunk->pushl_relay = 0x68; | |
| thunk->jmp_glue = 0xE9; | |
| thunk->target = (DWORD)target; | |
| thunk->relay = (DWORD)relay; | |
| thunk->glue = (DWORD)glue - (DWORD)&thunk->type; | |
| thunk->type = THUNKLET_TYPE_LS; | |
| thunk->owner = pTask? pTask->hInstance : 0; | |
| thunk->next = ThunkletAnchor; | |
| ThunkletAnchor = thunk; | |
| } | |
| return (FARPROC)thunk; | |
| } | |
| /*********************************************************************** | |
| * THUNK_AllocSLThunklet | |
| */ | |
| SEGPTR THUNK_AllocSLThunklet( FARPROC target, DWORD relay, | |
| SEGPTR glue, HTASK16 owner ) | |
| { | |
| THUNKLET *thunk = THUNK_FindThunklet( (DWORD)target, relay, (DWORD)glue, | |
| THUNKLET_TYPE_SL ); | |
| if (!thunk) | |
| { | |
| TDB *pTask = (TDB*)GlobalLock16( owner ); | |
| if ( !(thunk = HeapAlloc( ThunkletHeap, 0, sizeof(THUNKLET) )) ) | |
| return 0; | |
| thunk->prefix_target = thunk->prefix_relay = 0x66; | |
| thunk->pushl_target = thunk->pushl_relay = 0x68; | |
| thunk->jmp_glue = 0xEA; | |
| thunk->target = (DWORD)target; | |
| thunk->relay = (DWORD)relay; | |
| thunk->glue = (DWORD)glue; | |
| thunk->type = THUNKLET_TYPE_SL; | |
| thunk->owner = pTask? pTask->hInstance : 0; | |
| thunk->next = ThunkletAnchor; | |
| ThunkletAnchor = thunk; | |
| } | |
| return HEAP_GetSegptr( ThunkletHeap, 0, thunk ); | |
| } | |
| /********************************************************************** | |
| * IsLSThunklet | |
| */ | |
| BOOL16 WINAPI IsLSThunklet( THUNKLET *thunk ) | |
| { | |
| return thunk->prefix_target == 0x90 && thunk->pushl_target == 0x68 | |
| && thunk->prefix_relay == 0x90 && thunk->pushl_relay == 0x68 | |
| && thunk->jmp_glue == 0xE9 && thunk->type == THUNKLET_TYPE_LS; | |
| } | |
| /********************************************************************** | |
| * IsSLThunklet (KERNEL.612) | |
| */ | |
| BOOL16 WINAPI IsSLThunklet16( THUNKLET *thunk ) | |
| { | |
| return thunk->prefix_target == 0x66 && thunk->pushl_target == 0x68 | |
| && thunk->prefix_relay == 0x66 && thunk->pushl_relay == 0x68 | |
| && thunk->jmp_glue == 0xEA && thunk->type == THUNKLET_TYPE_SL; | |
| } | |
| /*********************************************************************** | |
| * AllocLSThunkletSysthunk (KERNEL.607) | |
| */ | |
| FARPROC WINAPI AllocLSThunkletSysthunk16( SEGPTR target, | |
| FARPROC relay, DWORD dummy ) | |
| { | |
| return THUNK_AllocLSThunklet( (SEGPTR)relay, (DWORD)target, | |
| ThunkletSysthunkGlueLS, GetCurrentTask() ); | |
| } | |
| /*********************************************************************** | |
| * AllocSLThunkletSysthunk (KERNEL.608) | |
| */ | |
| SEGPTR WINAPI AllocSLThunkletSysthunk16( FARPROC target, | |
| SEGPTR relay, DWORD dummy ) | |
| { | |
| return THUNK_AllocSLThunklet( (FARPROC)relay, (DWORD)target, | |
| ThunkletSysthunkGlueSL, GetCurrentTask() ); | |
| } | |
| /*********************************************************************** | |
| * AllocLSThunkletCallbackEx (KERNEL.567) | |
| */ | |
| FARPROC WINAPI AllocLSThunkletCallbackEx16( SEGPTR target, | |
| DWORD relay, HTASK16 task ) | |
| { | |
| THUNKLET *thunk = (THUNKLET *)PTR_SEG_TO_LIN( target ); | |
| if ( !thunk ) return NULL; | |
| if ( IsSLThunklet16( thunk ) && thunk->relay == relay | |
| && thunk->glue == (DWORD)ThunkletCallbackGlueSL ) | |
| return (FARPROC)thunk->target; | |
| return THUNK_AllocLSThunklet( target, relay, | |
| ThunkletCallbackGlueLS, task ); | |
| } | |
| /*********************************************************************** | |
| * AllocSLThunkletCallbackEx (KERNEL.568) | |
| */ | |
| SEGPTR WINAPI AllocSLThunkletCallbackEx16( FARPROC target, | |
| DWORD relay, HTASK16 task ) | |
| { | |
| THUNKLET *thunk = (THUNKLET *)target; | |
| if ( !thunk ) return 0; | |
| if ( IsLSThunklet( thunk ) && thunk->relay == relay | |
| && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type ) | |
| return (SEGPTR)thunk->target; | |
| return THUNK_AllocSLThunklet( target, relay, | |
| ThunkletCallbackGlueSL, task ); | |
| } | |
| /*********************************************************************** | |
| * AllocLSThunkletCallback (KERNEL.561) (KERNEL.606) | |
| */ | |
| FARPROC WINAPI AllocLSThunkletCallback16( SEGPTR target, DWORD relay ) | |
| { | |
| return AllocLSThunkletCallbackEx16( target, relay, GetCurrentTask() ); | |
| } | |
| /*********************************************************************** | |
| * AllocSLThunkletCallback (KERNEL.562) (KERNEL.605) | |
| */ | |
| SEGPTR WINAPI AllocSLThunkletCallback16( FARPROC target, DWORD relay ) | |
| { | |
| return AllocSLThunkletCallbackEx16( target, relay, GetCurrentTask() ); | |
| } | |
| /*********************************************************************** | |
| * FindLSThunkletCallback (KERNEL.563) (KERNEL.609) | |
| */ | |
| FARPROC WINAPI FindLSThunkletCallback( SEGPTR target, DWORD relay ) | |
| { | |
| THUNKLET *thunk = (THUNKLET *)PTR_SEG_TO_LIN( target ); | |
| if ( thunk && IsSLThunklet16( thunk ) && thunk->relay == relay | |
| && thunk->glue == (DWORD)ThunkletCallbackGlueSL ) | |
| return (FARPROC)thunk->target; | |
| thunk = THUNK_FindThunklet( (DWORD)target, relay, | |
| (DWORD)ThunkletCallbackGlueLS, | |
| THUNKLET_TYPE_LS ); | |
| return (FARPROC)thunk; | |
| } | |
| /*********************************************************************** | |
| * FindSLThunkletCallback (KERNEL.564) (KERNEL.610) | |
| */ | |
| SEGPTR WINAPI FindSLThunkletCallback( FARPROC target, DWORD relay ) | |
| { | |
| THUNKLET *thunk = (THUNKLET *)target; | |
| if ( thunk && IsLSThunklet( thunk ) && thunk->relay == relay | |
| && thunk->glue == (DWORD)ThunkletCallbackGlueLS - (DWORD)&thunk->type ) | |
| return (SEGPTR)thunk->target; | |
| thunk = THUNK_FindThunklet( (DWORD)target, relay, | |
| (DWORD)ThunkletCallbackGlueSL, | |
| THUNKLET_TYPE_SL ); | |
| return HEAP_GetSegptr( ThunkletHeap, 0, thunk ); | |
| } | |
| /*********************************************************************** | |
| * FreeThunklet16 (KERNEL.611) | |
| */ | |
| BOOL16 WINAPI FreeThunklet16( DWORD unused1, DWORD unused2 ) | |
| { | |
| return FALSE; | |
| } | |
| /*********************************************************************** | |
| * Callback Client API | |
| */ | |
| #define N_CBC_FIXED 20 | |
| #define N_CBC_VARIABLE 10 | |
| #define N_CBC_TOTAL (N_CBC_FIXED + N_CBC_VARIABLE) | |
| static SEGPTR CBClientRelay16[ N_CBC_TOTAL ]; | |
| static FARPROC *CBClientRelay32[ N_CBC_TOTAL ]; | |
| /*********************************************************************** | |
| * RegisterCBClient (KERNEL.619) | |
| */ | |
| INT16 WINAPI RegisterCBClient16( INT16 wCBCId, | |
| SEGPTR relay16, FARPROC *relay32 ) | |
| { | |
| /* Search for free Callback ID */ | |
| if ( wCBCId == -1 ) | |
| for ( wCBCId = N_CBC_FIXED; wCBCId < N_CBC_TOTAL; wCBCId++ ) | |
| if ( !CBClientRelay16[ wCBCId ] ) | |
| break; | |
| /* Register Callback ID */ | |
| if ( wCBCId > 0 && wCBCId < N_CBC_TOTAL ) | |
| { | |
| CBClientRelay16[ wCBCId ] = relay16; | |
| CBClientRelay32[ wCBCId ] = relay32; | |
| } | |
| else | |
| wCBCId = 0; | |
| return wCBCId; | |
| } | |
| /*********************************************************************** | |
| * UnRegisterCBClient (KERNEL.622) | |
| */ | |
| INT16 WINAPI UnRegisterCBClient16( INT16 wCBCId, | |
| SEGPTR relay16, FARPROC *relay32 ) | |
| { | |
| if ( wCBCId >= N_CBC_FIXED && wCBCId < N_CBC_TOTAL | |
| && CBClientRelay16[ wCBCId ] == relay16 | |
| && CBClientRelay32[ wCBCId ] == relay32 ) | |
| { | |
| CBClientRelay16[ wCBCId ] = 0; | |
| CBClientRelay32[ wCBCId ] = 0; | |
| } | |
| else | |
| wCBCId = 0; | |
| return wCBCId; | |
| } | |
| /*********************************************************************** | |
| * InitCBClient (KERNEL.623) | |
| */ | |
| void WINAPI InitCBClient16( FARPROC glueLS ) | |
| { | |
| HMODULE16 kernel = GetModuleHandle16( "KERNEL" ); | |
| SEGPTR glueSL = (SEGPTR)WIN32_GetProcAddress16( kernel, (LPCSTR)604 ); | |
| SetThunkletCallbackGlue16( glueLS, glueSL ); | |
| } | |
| /*********************************************************************** | |
| * CBClientGlueSL (KERNEL.604) | |
| */ | |
| void WINAPI CBClientGlueSL( CONTEXT86 *context ) | |
| { | |
| /* Create stack frame */ | |
| SEGPTR stackSeg = stack16_push( 12 ); | |
| LPWORD stackLin = PTR_SEG_TO_LIN( stackSeg ); | |
| SEGPTR glue, *glueTab; | |
| stackLin[3] = BP_reg( context ); | |
| stackLin[2] = SI_reg( context ); | |
| stackLin[1] = DI_reg( context ); | |
| stackLin[0] = DS_reg( context ); | |
| EBP_reg( context ) = OFFSETOF( stackSeg ) + 6; | |
| ESP_reg( context ) = OFFSETOF( stackSeg ) - 4; | |
| GS_reg( context ) = 0; | |
| /* Jump to 16-bit relay code */ | |
| glueTab = PTR_SEG_TO_LIN( CBClientRelay16[ stackLin[5] ] ); | |
| glue = glueTab[ stackLin[4] ]; | |
| CS_reg ( context ) = SELECTOROF( glue ); | |
| EIP_reg( context ) = OFFSETOF ( glue ); | |
| } | |
| /*********************************************************************** | |
| * CBClientThunkSL (KERNEL.620) | |
| */ | |
| extern DWORD CALL32_CBClient( FARPROC proc, LPWORD args, DWORD *esi ); | |
| void WINAPI CBClientThunkSL( CONTEXT86 *context ) | |
| { | |
| /* Call 32-bit relay code */ | |
| LPWORD args = PTR_SEG_OFF_TO_LIN( SS_reg( context ), BP_reg( context ) ); | |
| FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ]; | |
| EAX_reg(context) = CALL32_CBClient( proc, args, &ESI_reg( context ) ); | |
| } | |
| /*********************************************************************** | |
| * CBClientThunkSLEx (KERNEL.621) | |
| */ | |
| extern DWORD CALL32_CBClientEx( FARPROC proc, LPWORD args, DWORD *esi, INT *nArgs ); | |
| void WINAPI CBClientThunkSLEx( CONTEXT86 *context ) | |
| { | |
| /* Call 32-bit relay code */ | |
| LPWORD args = PTR_SEG_OFF_TO_LIN( SS_reg( context ), BP_reg( context ) ); | |
| FARPROC proc = CBClientRelay32[ args[2] ][ args[1] ]; | |
| INT nArgs; | |
| LPWORD stackLin; | |
| EAX_reg(context) = CALL32_CBClientEx( proc, args, &ESI_reg( context ), &nArgs ); | |
| /* Restore registers saved by CBClientGlueSL */ | |
| stackLin = (LPWORD)((LPBYTE)CURRENT_STACK16 + sizeof(STACK16FRAME) - 4); | |
| BP_reg( context ) = stackLin[3]; | |
| SI_reg( context ) = stackLin[2]; | |
| DI_reg( context ) = stackLin[1]; | |
| DS_reg( context ) = stackLin[0]; | |
| ESP_reg( context ) += 16+nArgs; | |
| /* Return to caller of CBClient thunklet */ | |
| CS_reg ( context ) = stackLin[9]; | |
| EIP_reg( context ) = stackLin[8]; | |
| } | |