/*********************************************************************** * MakeProcInstance (KERNEL.51) */ FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance ) { struct thunk *thunk; BYTE *lfunc; SEGPTR thunkaddr; WORD hInstanceSelector; hInstanceSelector = GlobalHandleToSel16(hInstance); TRACE("(%p, %04x);\n", func, hInstance); if (!HIWORD(func)) { /* Win95 actually protects via SEH, but this is better for debugging */ WARN("Ouch ! Called with invalid func %p !\n", func); return NULL; } if ( (GlobalHandleToSel16(CURRENT_DS) != hInstanceSelector) && (hInstance != 0) && (hInstance != 0xffff) ) { /* calling MPI with a foreign DSEG is invalid ! */ WARN("Problem with hInstance? Got %04x, using %04x instead\n", hInstance,CURRENT_DS); } /* Always use the DSEG that MPI was entered with. * We used to set hInstance to GetTaskDS16(), but this should be wrong * as CURRENT_DS provides the DSEG value we need. * ("calling" DS, *not* "task" DS !) */ hInstanceSelector = CURRENT_DS; hInstance = GlobalHandle16(hInstanceSelector); /* no thunking for DLLs */ if (NE_GetPtr(FarGetOwner16(hInstance))->ne_flags & NE_FFLAGS_LIBMODULE) return func; thunkaddr = TASK_AllocThunk(); if (!thunkaddr) return NULL; thunk = MapSL( thunkaddr ); lfunc = MapSL( (SEGPTR)func ); TRACE("(%p,%04x): got thunk %08x\n", func, hInstance, thunkaddr ); if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) || /* movw %ds, %ax */ ((lfunc[0]==0x1e) && (lfunc[1]==0x58)) /* pushw %ds, popw %ax */ ) { WARN("This was the (in)famous \"thunk useless\" warning. We thought we have to overwrite with nop;nop;, but this isn't true.\n"); } thunk->movw = 0xb8; /* movw instance, %ax */ thunk->instance = hInstanceSelector; thunk->ljmp = 0xea; /* ljmp func */ thunk->func = func; return (FARPROC16)thunkaddr; /* CX reg indicates if thunkaddr != NULL, implement if needed */ }
/********************************************************************** * GetCodeInfo (KERNEL.104) */ BOOL16 WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo ) { NE_MODULE *pModule; SEGTABLEENTRY *pSeg; int segNr; if ( !TASK_GetCodeSegment( proc, &pModule, &pSeg, &segNr ) ) return FALSE; /* Fill in segment information */ segInfo->offSegment = pSeg->filepos; segInfo->cbSegment = pSeg->size; segInfo->flags = pSeg->flags; segInfo->cbAlloc = pSeg->minsize; segInfo->h = pSeg->hSeg; segInfo->alignShift = pModule->ne_align; if ( segNr == pModule->ne_autodata ) segInfo->cbAlloc += pModule->ne_heap + pModule->ne_stack; /* Return module handle in %es */ CURRENT_STACK16->es = GlobalHandleToSel16( pModule->self ); return TRUE; }
/*********************************************************************** * GetTaskDS (KERNEL.155) * * Note: this function apparently returns a DWORD with LOWORD == HIWORD. * I don't think we need to bother with this. */ HINSTANCE16 WINAPI GetTaskDS16(void) { TDB *pTask; if (!(pTask = TASK_GetCurrent())) return 0; return GlobalHandleToSel16(pTask->hInstance); }
/*********************************************************************** * TaskNext (TOOLHELP.64) */ BOOL16 WINAPI TaskNext16( TASKENTRY *lpte ) { TDB *pTask; INSTANCEDATA *pInstData; TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext ); if (!lpte->hNext) return FALSE; /* make sure that task and hInstance are valid (skip initial Wine task !) */ while (1) { pTask = TASK_GetPtr( lpte->hNext ); if (!pTask || pTask->magic != TDB_MAGIC) return FALSE; if (pTask->hInstance) break; lpte->hNext = pTask->hNext; } pInstData = MapSL( MAKESEGPTR( GlobalHandleToSel16(pTask->hInstance), 0 ) ); lpte->hTask = lpte->hNext; lpte->hTaskParent = pTask->hParent; lpte->hInst = pTask->hInstance; lpte->hModule = pTask->hModule; lpte->wSS = SELECTOROF( pTask->teb->WOW32Reserved ); lpte->wSP = OFFSETOF( pTask->teb->WOW32Reserved ); lpte->wStackTop = pInstData->stacktop; lpte->wStackMinimum = pInstData->stackmin; lpte->wStackBottom = pInstData->stackbottom; lpte->wcEvents = pTask->nEvents; lpte->hQueue = pTask->hQueue; lstrcpynA( lpte->szModule, pTask->module_name, sizeof(lpte->szModule) ); lpte->wPSPOffset = 0x100; /*??*/ lpte->hNext = pTask->hNext; return TRUE; }
/*********************************************************************** * InitTask (KERNEL.91) * * Called by the application startup code. */ void WINAPI InitTask16( CONTEXT *context ) { TDB *pTask; INSTANCEDATA *pinstance; SEGPTR ptr; context->Eax = 0; if (!(pTask = TASK_GetCurrent())) return; /* Note: we need to trust that BX/CX contain the stack/heap sizes, as some apps, notably Visual Basic apps, *modify* the heap/stack size of the instance data segment before calling InitTask() */ /* Initialize the INSTANCEDATA structure */ pinstance = MapSL( MAKESEGPTR(CURRENT_DS, 0) ); pinstance->stackmin = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + sizeof( STACK16FRAME ); pinstance->stackbottom = pinstance->stackmin; /* yup, that's right. Confused me too. */ pinstance->stacktop = ( pinstance->stackmin > LOWORD(context->Ebx) ? pinstance->stackmin - LOWORD(context->Ebx) : 0 ) + 150; /* Initialize the local heap */ if (LOWORD(context->Ecx)) LocalInit16( GlobalHandleToSel16(pTask->hInstance), 0, LOWORD(context->Ecx) ); /* Initialize implicitly loaded DLLs */ NE_InitializeDLLs( pTask->hModule ); NE_DllProcessAttach( pTask->hModule ); /* Registers on return are: * ax 1 if OK, 0 on error * cx stack limit in bytes * dx cmdShow parameter * si instance handle of the previous instance * di instance handle of the new task * es:bx pointer to command line inside PSP * * 0 (=%bp) is pushed on the stack */ ptr = stack16_push( sizeof(WORD) ); *(WORD *)MapSL(ptr) = 0; context->Esp -= 2; context->Eax = 1; if (!pTask->pdb.cmdLine[0]) context->Ebx = 0x80; else { LPBYTE p = &pTask->pdb.cmdLine[1]; while ((*p == ' ') || (*p == '\t')) p++; context->Ebx = 0x80 + (p - pTask->pdb.cmdLine); } context->Ecx = pinstance->stacktop; context->Edx = pTask->nCmdShow; context->Esi = (DWORD)pTask->hPrevInstance; context->Edi = (DWORD)pTask->hInstance; context->SegEs = (WORD)pTask->hPDB; }
/*********************************************************************** * KERNEL thread initialisation routine */ static void thread_attach(void) { /* allocate the 16-bit stack (FIXME: should be done lazily) */ HGLOBAL16 hstack = WOWGlobalAlloc16( GMEM_FIXED, 0x10000 ); kernel_get_thread_data()->stack_sel = GlobalHandleToSel16( hstack ); NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR( kernel_get_thread_data()->stack_sel, 0x10000 - sizeof(STACK16FRAME) ); memset( (char *)GlobalLock16(hstack) + 0x10000 - sizeof(STACK16FRAME), 0, sizeof(STACK16FRAME) ); }
/*********************************************************************** * GetDummyModuleHandleDS (KERNEL.602) */ WORD WINAPI GetDummyModuleHandleDS16(void) { TDB *pTask; WORD selector; if (!(pTask = TASK_GetCurrent())) return 0; if (!(pTask->flags & TDBF_WIN32)) return 0; selector = GlobalHandleToSel16( pTask->hModule ); CURRENT_DS = selector; return selector; }
/********************************************************************** * TASK_GetCodeSegment * * Helper function for GetCodeHandle/GetCodeInfo: Retrieve the module * and logical segment number of a given code segment. * * 'proc' either *is* already a pair of module handle and segment number, * in which case there's nothing to do. Otherwise, it is a pointer to * a function, and we need to retrieve the code segment. If the pointer * happens to point to a thunk, we'll retrieve info about the code segment * where the function pointed to by the thunk resides, not the thunk itself. * * FIXME: if 'proc' is a SNOOP16 return stub, we should retrieve info about * the function the snoop code will return to ... * */ static BOOL TASK_GetCodeSegment( FARPROC16 proc, NE_MODULE **ppModule, SEGTABLEENTRY **ppSeg, int *pSegNr ) { NE_MODULE *pModule = NULL; SEGTABLEENTRY *pSeg = NULL; int segNr=0; /* Try pair of module handle / segment number */ pModule = GlobalLock16( HIWORD( proc ) ); if ( pModule && pModule->ne_magic == IMAGE_OS2_SIGNATURE ) { segNr = LOWORD( proc ); if ( segNr && segNr <= pModule->ne_cseg ) pSeg = NE_SEG_TABLE( pModule ) + segNr-1; } /* Try thunk or function */ else { BYTE *thunk = MapSL( (SEGPTR)proc ); WORD selector; if ((thunk[0] == 0xb8) && (thunk[3] == 0xea)) selector = thunk[6] + (thunk[7] << 8); else selector = HIWORD( proc ); pModule = NE_GetPtr( GlobalHandle16( selector ) ); pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL; if ( pModule ) for ( segNr = 1; segNr <= pModule->ne_cseg; segNr++, pSeg++ ) if ( GlobalHandleToSel16(pSeg->hSeg) == selector ) break; if ( pModule && segNr > pModule->ne_cseg ) pSeg = NULL; } /* Abort if segment not found */ if ( !pModule || !pSeg ) return FALSE; /* Return segment data */ if ( ppModule ) *ppModule = pModule; if ( ppSeg ) *ppSeg = pSeg; if ( pSegNr ) *pSegNr = segNr; return TRUE; }
/*********************************************************************** * TASK_Create * * NOTE: This routine might be called by a Win32 thread. Thus, we need * to be careful to protect global data structures. We do this * by entering the Win16Lock while linking the task into the * global task list. */ static TDB *TASK_Create( NE_MODULE *pModule, UINT16 cmdShow, LPCSTR cmdline, BYTE len ) { HTASK16 hTask; TDB *pTask; FARPROC16 proc; char curdir[MAX_PATH]; HMODULE16 hModule = pModule ? pModule->self : 0; /* Allocate the task structure */ hTask = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB) ); if (!hTask) return NULL; pTask = TASK_GetPtr( hTask ); FarSetOwner16( hTask, hModule ); /* Fill the task structure */ pTask->hSelf = hTask; pTask->version = pModule ? pModule->ne_expver : 0x0400; pTask->hModule = hModule; pTask->hParent = GetCurrentTask(); pTask->magic = TDB_MAGIC; pTask->nCmdShow = cmdShow; GetCurrentDirectoryA( sizeof(curdir), curdir ); GetShortPathNameA( curdir, curdir, sizeof(curdir) ); pTask->curdrive = (curdir[0] - 'A') | 0x80; lstrcpynA( pTask->curdir, curdir + 2, sizeof(pTask->curdir) ); /* Create the thunks block */ TASK_CreateThunks( hTask, (char *)pTask->thunks - (char *)pTask, 7 ); /* Copy the module name */ if (hModule) { char name[sizeof(pTask->module_name)+1]; size_t len; GetModuleName16( hModule, name, sizeof(name) ); len = strlen(name) + 1; memcpy(pTask->module_name, name, min(len,sizeof(pTask->module_name))); pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 ); } /* Allocate a selector for the PDB */ pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16), hModule, WINE_LDT_FLAGS_DATA ); /* Fill the PDB */ pTask->pdb.int20 = 0x20cd; pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */ proc = GetProcAddress16( GetModuleHandle16("KERNEL"), "DOS3Call" ); memcpy( &pTask->pdb.dispatcher[1], &proc, sizeof(proc) ); pTask->pdb.savedint22 = 0; pTask->pdb.savedint23 = 0; pTask->pdb.savedint24 = 0; pTask->pdb.fileHandlesPtr = MAKESEGPTR( GlobalHandleToSel16(pTask->hPDB), FIELD_OFFSET( PDB16, fileHandles )); pTask->pdb.hFileHandles = 0; memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) ); /* FIXME: should we make a copy of the environment? */ pTask->pdb.environment = SELECTOROF(GetDOSEnvironment16()); pTask->pdb.nbFiles = 20; /* Fill the command line */ if (!cmdline) { cmdline = GetCommandLineA(); /* remove the first word (program name) */ if (*cmdline == '"') if (!(cmdline = strchr( cmdline+1, '"' ))) cmdline = GetCommandLineA(); while (*cmdline && (*cmdline != ' ') && (*cmdline != '\t')) cmdline++; while ((*cmdline == ' ') || (*cmdline == '\t')) cmdline++; len = strlen(cmdline); } if (len >= sizeof(pTask->pdb.cmdLine)) len = sizeof(pTask->pdb.cmdLine)-1; pTask->pdb.cmdLine[0] = len; memcpy( pTask->pdb.cmdLine + 1, cmdline, len ); /* pTask->pdb.cmdLine[len+1] = 0; */ TRACE("cmdline='%.*s' task=%04x\n", len, cmdline, hTask ); /* Allocate a code segment alias for the TDB */ pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, pTask, sizeof(TDB), pTask->hPDB, WINE_LDT_FLAGS_CODE ); /* Default DTA overwrites command line */ pTask->dta = MAKESEGPTR( pTask->hPDB, FIELD_OFFSET( PDB16, cmdLine )); /* Create scheduler event for 16-bit tasks */ if ( !(pTask->flags & TDBF_WIN32) ) NtCreateEvent( &pTask->hEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ); if (!initial_task) initial_task = hTask; return pTask; }
/*********************************************************************** * GetTaskQueueES (KERNEL.119) */ void WINAPI GetTaskQueueES16(void) { CURRENT_STACK16->es = GlobalHandleToSel16( GetTaskQueue16(0) ); }
void WINAPI SNOOP16_Entry(FARPROC proc, LPBYTE args, CONTEXT86 *context) { DWORD ordinal=0; DWORD entry=(DWORD)MapSL( MAKESEGPTR(context->SegCs,LOWORD(context->Eip)) )-5; WORD xcs = context->SegCs; SNOOP16_DLL *dll = firstdll; SNOOP16_FUN *fun = NULL; SNOOP16_RETURNENTRIES **rets = &firstrets; SNOOP16_RETURNENTRY *ret; unsigned i=0; int max; while (dll) { if (xcs == dll->funhandle) { fun = (SNOOP16_FUN*)entry; ordinal = fun-dll->funs; break; } dll=dll->next; } if (!dll) { FIXME("entrypoint 0x%08x not found\n",entry); return; /* oops */ } while (*rets) { for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++) if (!(*rets)->entry[i].origreturn) break; if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0])) break; rets = &((*rets)->next); } if (!*rets) { HANDLE16 hand = GlobalHandleToSel16(GLOBAL_Alloc(GMEM_ZEROINIT,65535,0,WINE_LDT_FLAGS_CODE)); *rets = GlobalLock16(hand); (*rets)->rethandle = hand; i = 0; /* entry 0 is free */ } ret = &((*rets)->entry[i]); ret->lcall = 0x9a; ret->snr = MAKELONG(sizeof(SNOOP16_RELAY),xsnr); ret->origreturn = (FARPROC16)CALLER1REF; CALLER1REF = MAKELONG((char*)&(ret->lcall)-(char*)((*rets)->entry),(*rets)->rethandle); ret->dll = dll; ret->args = NULL; ret->ordinal = ordinal; ret->origSP = LOWORD(context->Esp); context->Eip= LOWORD(fun->origfun); context->SegCs = HIWORD(fun->origfun); DPRINTF("%04x:CALL %s.%d: %s(",GetCurrentThreadId(), dll->name,ordinal,fun->name); if (fun->nrofargs>0) { max = fun->nrofargs; if (max>16) max=16; for (i=max;i--;) DPRINTF("%04x%s",*(WORD*)((char *) MapSL( MAKESEGPTR(context->SegSs,LOWORD(context->Esp)) )+8+sizeof(WORD)*i),i?",":""); if (max!=fun->nrofargs) DPRINTF(" ..."); } else if (fun->nrofargs<0) { DPRINTF("<unknown, check return>"); ret->args = HeapAlloc(GetProcessHeap(),0,16*sizeof(WORD)); memcpy(ret->args,(LPBYTE)((char *) MapSL( MAKESEGPTR(context->SegSs,LOWORD(context->Esp)) )+8),sizeof(WORD)*16); } DPRINTF(") ret=%04x:%04x\n",HIWORD(ret->origreturn),LOWORD(ret->origreturn)); }
void SNOOP16_RegisterDLL(HMODULE16 hModule,LPCSTR name) { SNOOP16_DLL **dll = &(firstdll); char *s; if (!TRACE_ON(snoop)) return; TRACE("hmod=%x, name=%s\n", hModule, name); if (!snr) { xsnr=GLOBAL_Alloc(GMEM_ZEROINIT,2*sizeof(*snr),0,WINE_LDT_FLAGS_CODE|WINE_LDT_FLAGS_32BIT); snr = GlobalLock16(xsnr); snr[0].pushbp = 0x5566; snr[0].pusheax = 0x50; snr[0].pushax = 0x5066; snr[0].pushl = 0x68; snr[0].realfun = (DWORD)SNOOP16_Entry; snr[0].lcall = 0x9a; snr[0].callfromregs = (DWORD)__wine_call_from_16_regs; snr[0].seg = wine_get_cs(); snr[0].lret = 0xcb66; snr[1].pushbp = 0x5566; snr[1].pusheax = 0x50; snr[1].pushax = 0x5066; snr[1].pushl = 0x68; snr[1].realfun = (DWORD)SNOOP16_Return; snr[1].lcall = 0x9a; snr[1].callfromregs = (DWORD)__wine_call_from_16_regs; snr[1].seg = wine_get_cs(); snr[1].lret = 0xcb66; } while (*dll) { if ((*dll)->hmod == hModule) { /* another dll, loaded at the same address */ GlobalUnlock16((*dll)->funhandle); GlobalFree16((*dll)->funhandle); break; } dll = &((*dll)->next); } if (*dll) *dll = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *dll, sizeof(SNOOP16_DLL)+strlen(name)); else *dll = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SNOOP16_DLL)+strlen(name)); (*dll)->next = NULL; (*dll)->hmod = hModule; if ((s=strrchr(name,'\\'))) name = s+1; strcpy( (*dll)->name, name ); if ((s=strrchr((*dll)->name,'.'))) *s='\0'; (*dll)->funhandle = GlobalHandleToSel16(GLOBAL_Alloc(GMEM_ZEROINIT,65535,0,WINE_LDT_FLAGS_CODE)); (*dll)->funs = GlobalLock16((*dll)->funhandle); if (!(*dll)->funs) { HeapFree(GetProcessHeap(),0,*dll); FIXME("out of memory\n"); return; } }