diff options
Diffstat (limited to 'private/mvdm/wow16/toolhelp')
35 files changed, 8441 insertions, 0 deletions
diff --git a/private/mvdm/wow16/toolhelp/class1.c b/private/mvdm/wow16/toolhelp/class1.c new file mode 100644 index 000000000..4b44d010e --- /dev/null +++ b/private/mvdm/wow16/toolhelp/class1.c @@ -0,0 +1,88 @@ +/************************************************************************* + * CLASS1.C + * + * Routines used to enumerate window classes + * + *************************************************************************/ + +#include "toolpriv.h" +#include <testing.h> + +/* ----- Types ----- */ + +/* The following was stolen from the 3.1 USER but is the same as 3.0. + * Note that the only fielda we use (for now) are the atomClassName + * and the pclsNext fields. + * Oops. We're going to use the hInstance field also. + */ +typedef struct tagCLS +{ + struct tagCLS *pclsNext; + unsigned clsMagic; + unsigned atomClassName; + char *pdce; /* DCE * to DC associated with class */ + int cWndReferenceCount; /* Windows registered with this class */ + unsigned style; + long (far *lpfnWndProc)(); + int cbclsExtra; + int cbwndExtra; + HANDLE hInstance; + HANDLE hIcon; + HANDLE hCursor; + HANDLE hbrBackground; + char far *lpszMenuName; + char far *lpszClassName; +} CLS; +typedef CLS FAR *LPCLS; + +/* ----- Functions ----- */ + +/* ClassFirst + * Returns information about the first task in the task chain. + */ + +BOOL TOOLHELPAPI ClassFirst( + CLASSENTRY FAR *lpClass) +{ + WORD wClassHead; + + /* Check for errors */ + if (!wLibInstalled || !lpClass || lpClass->dwSize != sizeof (CLASSENTRY)) + return FALSE; + + /* If we're in Win3.1, call the special entry point to get the head */ + if (!(wTHFlags & TH_WIN30)) + wClassHead = (WORD)(*lpfnUserSeeUserDo)(SD_GETCLASSHEADPTR, 0, 0L); + + /* In 3.0 (and 3.0a) we're forced to use a fixed offset. Unfortunately, + * this offset is different in debug and nondebug versions. + */ + else + { + if (GetSystemMetrics(SM_DEBUG)) + wClassHead = 0x1cc; + else + wClassHead = 0x1b8; + wClassHead = *(WORD FAR *)MAKEFARPTR(hUserHeap, wClassHead); + } + + /* Now get the stuff */ + return ClassInfo(lpClass, wClassHead); +} + + +/* ClassNext + * Returns information about the next task in the task chain. + */ + +BOOL TOOLHELPAPI ClassNext( + CLASSENTRY FAR *lpClass) +{ + /* Check for errors */ + if (!wLibInstalled || !lpClass || !lpClass->wNext || + lpClass->dwSize != sizeof (CLASSENTRY)) + return FALSE; + + return ClassInfo(lpClass, lpClass->wNext); +} + diff --git a/private/mvdm/wow16/toolhelp/class2.asm b/private/mvdm/wow16/toolhelp/class2.asm new file mode 100644 index 000000000..b22a42526 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/class2.asm @@ -0,0 +1,92 @@ +;************************************************************************** +;* CLASS2.ASM +;* +;* Assembly support for the class enumeration routines. +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + +PMODE32 = 0 +PMODE = 0 +SWAPPRO = 0 + INCLUDE TDB.INC + +;** Class structure +CLS STRUC +cls_pclsNext DW ? +cls_clsMagic DW ? +cls_atom DW ? +cls_pdce DW ? +cls_RefCount DW ? +cls_style DW ? +cls_lpfnWndProc DD ? +cls_cbclsExtra DW ? +cls_cbwndExtra DW ? +cls_hInstance DW ? +cls_hIcon DW ? +cls_hCursor DW ? +cls_hbrBackgr DW ? +cls_lpszMnName DW ? +cls_lpszClsName DW ? +CLS ENDS + +;** External functions +externNP HelperVerifySeg +externFP GetAtomName + +;** Functions + +sBegin CODE + assumes CS,CODE + +; ClassInfo +; +; Returns information about the class with the given block handle + +cProc ClassInfo, <PUBLIC,NEAR>, <si,di,ds> + parmD lpClass + parmW wOffset +cBegin + ;** Start by verifying that we can read the segment here + mov ax,hUserHeap ;Get the selector + mov bx,wOffset ; and the desired offset + cCall HelperVerifySeg, <ax,bx> + or ax,ax ;FALSE return? + jnz CI_SelOk ;We're OK + xor ax,ax ;Return FALSE + jmp CI_End +CI_SelOk: + + ;** Point to the CLS structure with DS:SI. Note that using DS to + ;** point to USER's DS is useful to get USER's local atoms + mov ax,hUserHeap ;User's heap is User's DGROUP + mov ds,ax + mov si,wOffset ;Get a pointer to the CLS structure + + ;** Copy the hInstance + les di,lpClass ;Get the structure + mov ax,[si].cls_hInstance ;Get the hInst of the class owner + mov es:[di].ce_hInst,ax ;Save in the CLASSENTRY struct + + ;** Get the string from the atom and copy the next pointer + mov ax,[si].cls_atom ;Get the desired atom number + lea bx,[di].ce_szClassName ;Get the offset to copy string to + push es ;Save ES (GetAtomName may trash) + mov cx,MAX_CLASSNAME ;Get max classname length + cCall GetAtomName, <ax,es,bx,cx> ;Copy the atom string + pop es + or ax,ax ;OK? + jnz CI_20 ;Yes + mov es:[di].ce_szClassName,0 ;No. Clear the string +CI_20: mov ax,[si].cls_pclsNext ;Get the next pointer + mov es:[di].ce_wNext,ax ;Save it + + ;** Return TRUE on success + mov ax,TRUE +CI_End: +cEnd + +sEnd + + END diff --git a/private/mvdm/wow16/toolhelp/dllentry.asm b/private/mvdm/wow16/toolhelp/dllentry.asm new file mode 100644 index 000000000..be77125d0 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/dllentry.asm @@ -0,0 +1,117 @@ +PAGE,132 +;*************************************************************************** +;* +;* DLLENTRY.ASM +;* +;* TOOLHELP.DLL Entry code +;* +;* This module generates a code segment called INIT_TEXT. +;* It initializes the local heap if one exists and then calls +;* the C routine LibMain() which should have the form: +;* BOOL FAR PASCAL LibMain(HANDLE hInstance, +;* WORD wDataSeg, +;* WORD cbHeap, +;* LPSTR lpszCmdLine); +;* +;* The result of the call to LibMain is returned to Windows. +;* The C routine should return TRUE if it completes initialization +;* successfully, FALSE if some error occurs. +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + +extrn LocalInit:FAR +extrn GlobalUnwire:FAR + +sBegin CODE + assumes CS,CODE + +externNP ToolHelpLibMain +externNP HelperReleaseSelector +externNP NotifyUnInit +externNP InterruptUnInit + +?PLM=0 +externA <_acrtused> ;Ensures that Win DLL startup code is linked +?PLM=1 + + +; LibEntry +; +; KERNEL calls this when the TOOLHELP is loaded the first time + +cProc LibEntry, <PUBLIC,FAR> +cBegin + push di ;Handle of the module instance + push ds ;Library data segment + push cx ;Heap size + push es ;Command line segment + push si ;Command line offset + + ;** If we have some heap then initialize it + jcxz callc ;Jump if no heap specified + + ;** Call the Windows function LocalInit() to set up the heap + ;** LocalInit((LPSTR)start, WORD cbHeap); + xor ax,ax + cCall LocalInit <ds, ax, cx> + or ax,ax ;Did it do it ok ? + jz error ;Quit if it failed + + ;** Invoke our initialization routine +callc: + call ToolHelpLibMain ;Invoke the 'C' routine (result in AX) + jmp SHORT exit + +error: + pop si ;Clean up stack on a LocalInit error + pop es + pop cx + pop ds + pop di +exit: + +cEnd + +; WEP +; Windows Exit Procedure + +cProc WEP, <FAR,PUBLIC>, <si,di,ds> + parmW wState +cBegin + ;** Make sure our DS is safe + mov ax,_DATA ;Get the DS value + lar cx,ax ;Is it OK? + jz @F + jmp SHORT WEP_Bad ;No +@@: and cx,8a00h ;Clear all but P, Code/Data, R/W bits + cmp cx,8200h ;Is it P, R/W, Code/Data? + jne WEP_Bad ;No + mov ax,_DATA ;Get our DS now + mov ds,ax + + ;** Uninstall the Register PTrace notifications if necessary + cmp wNotifyInstalled,0 + jz @F + cCall NotifyUnInit +@@: + ;** Release fault handlers + cmp wIntInstalled,0 + jz @F + cCall InterruptUnInit +@@: + ;** Release our roving selector + test wTHFlags, TH_WIN30STDMODE + jz @F + cCall HelperReleaseSelector, <wSel> +@@: + +WEP_Bad: + mov ax,1 +cEnd + +sEnd + + END LibEntry + diff --git a/private/mvdm/wow16/toolhelp/global.c b/private/mvdm/wow16/toolhelp/global.c new file mode 100644 index 000000000..65346714b --- /dev/null +++ b/private/mvdm/wow16/toolhelp/global.c @@ -0,0 +1,246 @@ +/************************************************************************** + * GLOBAL.C + * + * Routines used to walk the global heap. + * + **************************************************************************/ + +#include "toolpriv.h" +#include <newexe.h> +#include <string.h> + +/* GlobalInfo + * Reports information about the state of the global heap, + * specifically, the number of elements that will be returned by + * a global heap walk. + */ + +BOOL TOOLHELPAPI GlobalInfo( + GLOBALINFO FAR *lpGlobalInfo) +{ + /* Check the structure size and verify proper installation */ + if (!wLibInstalled || lpGlobalInfo->dwSize != sizeof (GLOBALINFO)) + return FALSE; + + /* Get the item counts */ + if (wTHFlags & TH_KERNEL_386) + { + lpGlobalInfo->wcItems = Walk386Count(GLOBAL_ALL); + lpGlobalInfo->wcItemsFree = Walk386Count(GLOBAL_FREE); + lpGlobalInfo->wcItemsLRU = Walk386Count(GLOBAL_LRU); + } + else + { + lpGlobalInfo->wcItems = Walk286Count(GLOBAL_ALL); + lpGlobalInfo->wcItemsFree = Walk286Count(GLOBAL_FREE); + lpGlobalInfo->wcItemsLRU = Walk286Count(GLOBAL_LRU); + } + + return TRUE; +} + +/* GlobalFirst + * Finds the first element in the global heap. This is modified by + * wFlags which modifies which list (GLOBAL_ALL, GLOBAL_FREE, + * GLOBAL_LRU) should be walked + */ + +BOOL TOOLHELPAPI GlobalFirst( + GLOBALENTRY FAR *lpGlobal, + WORD wFlags) +{ + DWORD dwFirst; + + /* Check the structure size and verify proper installation */ + if (!wLibInstalled || !lpGlobal || + lpGlobal->dwSize != sizeof (GLOBALENTRY)) + return FALSE; + + /* Call the appropriate low-level routine to find the first block */ + if (wTHFlags & TH_KERNEL_386) + { + /* Get the first item. Return false if no items in this list */ + if (!(dwFirst = Walk386First(wFlags))) + return FALSE; + + /* Return information about this first item */ + Walk386(dwFirst, lpGlobal, wFlags); + } + else + { + /* Get the first item. Return false if no items in this list */ + if (!(dwFirst = Walk286First(wFlags))) + return FALSE; + + /* Return information about this first item */ + Walk286(dwFirst, lpGlobal, wFlags); + } + + /* Guess at the type of the object */ + HelperGlobalType(lpGlobal); + + return TRUE; +} + + +/* GlobalNext + * Returns the next item in the chain pointed to by lpGlobal and + * in the list indicated by wFlags (same choices as for GlobalFirst(). + */ + +BOOL TOOLHELPAPI GlobalNext( + GLOBALENTRY FAR *lpGlobal, + WORD wFlags) +{ + DWORD dwNext; + + /* Check the structure size and verify proper installation */ + if (!wLibInstalled || !lpGlobal || + lpGlobal->dwSize != sizeof (GLOBALENTRY)) + return FALSE; + + /* Check to see if we're at the end of the list */ + dwNext = wFlags & 3 ? lpGlobal->dwNextAlt : lpGlobal->dwNext; + if (!dwNext) + return FALSE; + + /* If we're using the 386 kernel, call the 386 heap walk routine with + * a pointer to the appropriate heap item + * (Note that this depends on GLOBAL_ALL being zero) + */ + if (wTHFlags & TH_KERNEL_386) + Walk386(dwNext, lpGlobal, wFlags); + else + Walk286(dwNext, lpGlobal, wFlags); + + /* Guess at the type of the object */ + HelperGlobalType(lpGlobal); + + return TRUE; +} + + +/* GlobalEntryHandle + * Used to find information about a global heap entry. Information + * about this entry is returned in the structure. + */ + +BOOL TOOLHELPAPI GlobalEntryHandle( + GLOBALENTRY FAR *lpGlobal, + HANDLE hItem) +{ + DWORD dwBlock; + + /* Check the structure size and verify proper installation */ + if (!wLibInstalled || !lpGlobal || + lpGlobal->dwSize != sizeof (GLOBALENTRY)) + return FALSE; + + /* Make sure this is a valid block */ + if (wTHFlags & TH_KERNEL_386) + { + if (!(dwBlock = Walk386Handle(hItem))) + return FALSE; + } + else + { + if (!(dwBlock = Walk286Handle(hItem))) + return FALSE; + } + + /* Return information about this item */ + if (wTHFlags & TH_KERNEL_386) + Walk386(dwBlock, lpGlobal, GLOBAL_ALL); + else + Walk286(dwBlock, lpGlobal, GLOBAL_ALL); + + /* Guess at the type of the object */ + HelperGlobalType(lpGlobal); + + return TRUE; +} + + +/* GlobalEntryModule + * Returns global information about the block with the given module + * handle and segment number. + */ + +BOOL TOOLHELPAPI GlobalEntryModule( + GLOBALENTRY FAR *lpGlobal, + HANDLE hModule, + WORD wSeg) +{ + struct new_exe FAR *lpNewExe; + struct new_seg1 FAR *lpSeg; + DWORD dwBlock; + + /* Check the structure size and verify proper installation */ + if (!wLibInstalled || !lpGlobal || + lpGlobal->dwSize != sizeof (GLOBALENTRY)) + return FALSE; + + /* Grunge in the module database to find the proper selector. Start + * by first verifying the module database pointer + */ + if (!HelperVerifySeg(hModule, sizeof (struct new_exe))) + return FALSE; + + /* Get a pointer to the module database */ + lpNewExe = MAKEFARPTR(hModule, 0); + + /* Make sure this is a module database */ + if (lpNewExe->ne_magic != NEMAGIC) + return FALSE; + + /* See if the number requested is past the end of the segment table. + * Note that the first segment is segment 1. + */ + --wSeg; + if (lpNewExe->ne_cseg <= wSeg) + return FALSE; + + /* Get a pointer to the segment table */ + lpSeg = MAKEFARPTR(hModule, lpNewExe->ne_segtab); + + /* Jump to the right spot in the segment table */ + lpSeg += wSeg; + + /* Make sure this is a valid block and get its arena pointer */ + if (wTHFlags & TH_KERNEL_386) + { + if (!(dwBlock = Walk386Handle(lpSeg->ns_handle))) + return FALSE; + } + else + { + if (!(dwBlock = Walk286Handle(lpSeg->ns_handle))) + return FALSE; + } + + /* Return information about this item */ + if (wTHFlags & TH_KERNEL_386) + Walk386(dwBlock, lpGlobal, GLOBAL_ALL); + else + Walk286(dwBlock, lpGlobal, GLOBAL_ALL); + + /* Guess at the type of the object */ + HelperGlobalType(lpGlobal); + + /* If we've gotten to here, it must be OK */ + return TRUE; +} + + +/* GlobalHandleToSel + * Provides a generic method of converting a handle to a selector. + * This works across Windows versions as well as working when the + * value is already a selector. + */ + +WORD TOOLHELPAPI GlobalHandleToSel( + HANDLE hMem) +{ + return HelperHandleToSel(hMem); +} + diff --git a/private/mvdm/wow16/toolhelp/helper.asm b/private/mvdm/wow16/toolhelp/helper.asm new file mode 100644 index 000000000..b8ff65962 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/helper.asm @@ -0,0 +1,555 @@ +;************************************************************************** +;* HELPER.ASM +;* +;* Assembly routines used by more than one module +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE WINDOWS.INC +PMODE32 = 0 +PMODE = 1 +SWAPPRO = 0 + INCLUDE WINKERN.INC + INCLUDE NEWEXE.INC + INCLUDE TDB.INC + +;** External functions +externNP Walk386VerifyLocHeap +externNP Walk286VerifyLocHeap +externFP GetCurrentTask +externFP InterruptUnRegister +externFP NotifyUnRegister +externNP SignalUnRegister +externFP TaskFirst +externFP TaskNext + +;** Functions + +sBegin CODE + assumes CS,CODE + +.286p + +; HelperVerifySeg +; +; Verifies that a selector is valid and that the segment it points +; to is safe for reading out to wcb bytes offset +; Returns 0 if too short or the length of the segment. +; Preserves all used registers except the return value, AX + +cProc HelperVerifySeg, <PUBLIC,NEAR>, <dx> + parmW wSeg + parmW wcb +cBegin + ;** Verify that this is a valid selector and that it is long enough + cCall HelperSegLen, <wSeg> ;Check the segment + or dx,dx ;>64K? If so, always return OK + jnz HVS_End + cmp ax,wcb ;Long enough? + ja HVS_End ;Yes, return the length +HVS_Bad: + xor ax,ax ;No. Return FALSE +HVS_End: +cEnd + + +; HelperHandleToSel +; Converts a handle to a selector. This routine knows how to +; handle 3.0 and 3.1 differences as well as 286 & 386 differences. + +cProc HelperHandleToSel, <NEAR,PUBLIC>, <ds> + parmW h ;Handle +cBegin + mov ax,_DATA ;Get the data segment + mov ds,ax ;Point with DS + mov ax,h ;Get the handle + test wTHFlags,TH_WIN30 ;Win3.0? + jz HTS_Win31 ;No + test ax,1 ;Check the low bit + jnz HTS_End ;It's already a selector + dec ax ;Decrement for proper selector + jmp SHORT HTS_End ;Out of here + +HTS_Win31: + or ax,1 ;Set the bit + +HTS_End: + +cEnd + + +; HelperVerifyLocHeap +; +; Uses the processor-specific local heap verify routine to check the +; validity of a local heap. +; +; Call: +; AX = Block handle or selector +; DS must point to TOOLHELP's DGROUP +; Return: +; Carry flag set iff NOT a local heap segment +; +; Destroys all registers except AX, ESI, EDI, DS, and ES + +HelperVerifyLocHeap PROC Near + PUBLIC HelperVerifyLocHeap + + test wTHFlags,TH_KERNEL_386 ;Are we using KRNL386? + jz HVL_286 ;No + jmp Walk386VerifyLocHeap ;Jump to the 386 routine + +HVL_286: + jmp Walk286VerifyLocHeap ;Jump to the 286 routine + +HVL_End: + ret + +HelperVerifyLocHeap ENDP + + +; HelperGlobalType +; +; Given data about a block, gropes around trying to decipher the +; block type. Parameters are passed and returned in the GLOBALENTRY +; structure. + +cProc HelperGlobalType, <PUBLIC,NEAR>, <si,di,ds> + parmD lpGlobal + localV Task,<SIZE TASKENTRY> +cBegin + lds si,lpGlobal ;Get the pointer + mov [si].ge_wData,0 ;Clear the wData field + ; Zero's not a valid seg # or type # + + ;** Check for internal block types + mov bx,[si].ge_hOwner ;Get the owner handle + mov ax,GT_SENTINEL ;Just in case... + cmp bx,GA_SENTINAL ;Is this a sentinel? + jz HGT_JmpEnd ;Yes, get out + mov ax,GT_BURGERMASTER ;Just in case... + cmp bx,GT_BURGERMASTER ;Burgermaster? + jz HGT_JmpEnd ;Yes, get out + cmp bx,-7 ;Lowest number reserved + jb HGT_0 ;Not an internal block + mov ax,GT_INTERNAL ;Internal KERNEL block type +HGT_JmpEnd: + jmp HGT_End ;Get out +HGT_0: + + ;** Check for a free block + or bx,bx ;Check for 0: Free block + jnz HGT_2 ;Not zero + mov ax,GT_FREE ;Free blocks have zero owner + jmp HGT_End ;Unknown type +HGT_2: + + ;** Check for DGROUP and other data segments + mov ax,[si].ge_wFlags ;Get the block flags + test ax,GAH_DGROUP ;Is this a DGROUP segment + jnz @F + jmp HGT_10 ;Didn't find it so continue +@@: + + ;** Save the segment number of the segment + mov ax,[si].ge_hOwner ;Get the module database + push ax ;Save for later + mov bx,[si].ge_hBlock ;Get the handle + cCall HelperGetSegNumber ;Get the segment number + mov [si].ge_wData,ax ;Save the segment number + pop bx ;Get hExe back in BX + + ;** Try two methods: First, see if it is the hInst of the FIRST + ;** instance of its module + lsl cx, bx ;Is this segment OK? + jnz HGT_5 ;No, punt and call it unknown data + cmp cx, ne_pautodata ;Long enough? + jbe HGT_5 ;No, get out + mov es,bx ;Point with ES + cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database + jnz HGT_5 ;It isn't so get out + mov bx,es:[ne_pautodata] ;Point to the segment table entry + or bx,bx ;Is there a DGROUP segment? + jz HGT_5 ;No, flag as unknown data + mov ax,es:[bx].ns_handle ;Get the handle from the table + cmp ax,[si].ge_hBlock ;Does the DGROUP handle point here? + jnz HGT_3 ;No, might be multiple instance + mov ax,GT_DGROUP ;Matches, must be DGROUP + jmp HGT_End ;Get out +HGT_3: + ;** It's not the first instance of this module. + ;** All multiple instance things will be on the task list + ;** so try to find it there. + mov bx,[si].ge_hBlock ;Get the handle + cCall HelperHandleToSel,<bx> ;Get the selector for this + mov di,ax ;Save in DI + mov ax,SIZE TASKENTRY ;Get the struct size + mov WORD PTR Task.te_dwSize[0],ax ;Put in struct + mov WORD PTR Task.te_dwSize[2],0 ;Clear high word + lea ax,Task ;Get the structure + cCall TaskFirst, <ss,ax> ;Get the first task's info + or ax,ax ;No tasks? + jz HGT_5 ;Just call it data (not DGROUP) +HGT_TaskLoop: + mov ax,Task.te_hInst ;Get this task's hInst + cCall HelperHandleToSel, <ax> ;Convert to selector + cmp ax,di ;Is this a match? + je HGT_TaskFound ;Yes, do it + lea ax,Task ;Point to the struct + cCall TaskNext, <ss,ax> ;Get the next one + or ax,ax ;End of the line? + jnz HGT_TaskLoop ;Nope, do the next one +HGT_5: mov ax,GT_DATA ;Unknown data segment + jmp HGT_End ;Get out +HGT_TaskFound: + mov ax,GT_DGROUP ;Matches, must be DGROUP + jmp HGT_End ;Get out +HGT_10: + + ;** Check for a task database + mov ax,[si].ge_hBlock ;Get the segment + mov bx,TDBSize ;Get the limit to verify + push ax ;Save the segment + cCall HelperVerifySeg <ax,bx> ;Make sure we can check signature + pop bx ;Retrieve the segment value + or ax,ax ;Zero return means bad + jz HGT_20 ;Not a task database + mov es,bx ;Point to the segment + cmp es:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB? + jnz HGT_20 ;Nope, go on + mov ax,GT_TASK ;Set the task flag + jmp HGT_End ;Get out +HGT_20: + + ;** Now check for Module database + mov ax,[si].ge_hOwner ;Get the owner handle + cCall HelperHandleToSel, <ax> ;Convert to selector for compare + mov cx,ax ;Save in CX + mov ax,[si].ge_hBlock ;Does this block own itself? + cCall HelperHandleToSel, <ax> ;Convert to selector for compare + cmp ax,cx ;Do the pointers match? + jnz HGT_24 ;No, so it's not a module database + mov ax,GT_MODULE ;Set type + jmp HGT_End ;Get out +HGT_24: + + ;** Check for a code segment. If found, return segment number + mov ax,[si].ge_hOwner ;Get the module database + push ax ;Save the selector + cCall HelperVerifySeg <ax,2> ;Make sure this is OK to put in ES + pop bx ;Retrieve in BX + or ax,ax ;Zero means bad + jnz @F + jmp SHORT HGT_Unknown +@@: mov es,bx ;Point with ES + xor dx,dx ;Use DX to count segments + cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database + jz HGT_25 ;Looks good + jmp SHORT HGT_40 ;Not code or resource, try next +HGT_25: mov cx,es:[ne_cseg] ;Get max number of segments + jcxz HGT_30 ;No segments + mov di,es:[ne_segtab] ;Point to the segment table + mov bx,[si].ge_hBlock ;Get the block we're looking for +HGT_SegLoop: + inc dx ;Bump the segment number + cmp bx,es:[di].ns_handle ;Is this the correct segment entry? + jz HGT_27 ;Yes, get out + add di,SIZE new_seg1 ;Bump to next entry + loop HGT_SegLoop ;Loop back to check next entry + jmp SHORT HGT_30 ;Now check resources +HGT_27: + mov [si].ge_wData,dx ;Save the segment count + mov ax,GT_CODE ;Flag that it's a code segment + jmp SHORT HGT_End ;Get out + + ;** Check to see if it's a resource. If so, return resource type # +HGT_30: mov di,es:[ne_rsrctab] ;Point to the resource table + cmp di,es:[ne_restab] ;If both point to same place, no rsrc + jz HGT_40 ;No resource table -- unknown type + add di,2 ;Skip past alignment count +HGT_TypeLoop: + mov dx,es:[di].rt_id ;DX holds current type number + or dx,dx ;Zero type means end of res table + jz HGT_40 ;Not found so get out! + mov cx,es:[di].rt_nres ;Get the number of resources + add di,SIZE RSRC_TYPEINFO ;Bump past the structure +HGT_ResLoop: + cmp bx,es:[di].rn_handle ;Is it this resource? + jz HGT_FoundRes ;Yep. This is the one + add di,SIZE RSRC_NAMEINFO ;Bump past this structure + loop HGT_ResLoop ;Loop for next resource structure + jmp HGT_TypeLoop ;Try the next type + + ;** Found the resource, now compute the resource type +HGT_FoundRes: + test dx,RSORDID ;If this bit set, must be ordinal type + jnz HGT_32 ;Yep. Ordinal + mov dx,GD_USERDEFINED ;Named resources are all user-def +HGT_32: and dx,NOT RSORDID ;Clear the flag bit + cmp dx,GD_MAX_RESOURCE ;If the type is too big, it's user-def + jbe HGT_34 ;Standard type + mov dx,GD_USERDEFINED ;User-defined resource type +HGT_34: mov [si].ge_wData,dx ;Save the type + mov ax,GT_RESOURCE ;Return that it's a resource + jmp SHORT HGT_End ;Get out + +HGT_40: +HGT_Unknown: + mov ax,GT_UNKNOWN ;Unknown type +HGT_End: + mov [si].ge_wType,ax ;Save the type and exit +cEnd + + +; HelperGrabSelector +; +; Allocates a selector from DPMI. + +cProc HelperGrabSelector, <NEAR,PUBLIC> +cBegin + xor ax,ax ;DPMI Function 0, allocate LDT sels + mov cx,1 ;Just 1 sel + int 31h ;Call DPMI. Selector in AX +cEnd + + +; HelperReleaseSelector +; +; Frees a selector to DPMI + +cProc HelperReleaseSelector, <NEAR,PUBLIC> + parmW wSelector +cBegin + mov ax,1 ;DPMI function 1, free LDT sels + mov bx,wSelector ;Get the sel + int 31h ;Free it +cEnd + +; HelperSetSignalProc +; Puts a signal proc in a task's TDB so that it gets called in place +; of USER's proc. Returns the old USER proc. + +cProc HelperSetSignalProc, <NEAR,PUBLIC>, <si,di> + parmW hTask, + parmD lpfn +cBegin + ;** Point to the TDB + mov es,hTask ;Point with ES + + ;** Swap the new with the old and return the old one + mov ax,WORD PTR lpfn ;Get the new signal proc + xchg ax,WORD PTR es:[TDB_USignalProc] ;Switch with the old one + mov dx,WORD PTR lpfn + 2 ;Get HIWORD + xchg dx,WORD PTR es:[TDB_USignalProc + 2] ;Switch with old one +cEnd + + +; HelperSignalProc +; Cleans up when a TOOLHELP-using app is terminated. This proc +; MUST chain on to USER's signal proc. Note that action is only taken +; on the death signal (BX = 0666h) + +cProc HelperSignalProc, <FAR,PUBLIC> +cBegin NOGEN + + ;** Save all registers + sub sp,4 + push bp + mov bp,sp ;Make a stack frame + pusha + push ds + push es + + ;** Get a pointer to the correct SIGNAL structure + mov ax,_DATA ;Get the TOOLHELP.DLL DS + mov ds,ax ;Point with DS + cCall GetCurrentTask ;Get the current task in AX + mov di,ax ;Save task in DI + mov si,npSignalHead ;Get the first struct +HSP_SigLoop: + or si,si ;End of the list? + jz HSP_Return ;Yes -- This is bad!! + cmp di,[si].si_hTask ;Task match? + je HSP_FoundIt ;Yes + mov si,[si].si_pNext ;Get the next one + jmp HSP_SigLoop ;Loop around + + ;** Compute the fake return address (old signal proc) +HSP_FoundIt: + mov ax,WORD PTR [si].si_lpfnOld ;Get LOWORD of old proc + mov [bp + 2],ax ;Put on stack frame + mov dx,WORD PTR [si].si_lpfnOld + 2 ;Get HIWORD of old proc + mov [bp + 4],dx ;Put on stack frame + + ;** See if we have the death signal. If not, don't do anything + ;** but just chain on. 20h is the signal for task exit + cmp bx, 20h ;Is this the death signal? + jne HSP_Done ;No. Don't cleanup + + ;** Since we have a death signal, use it to clean up everything + push ax ;Save the return address + push dx + cCall InterruptUnRegister, <di> ;Unregister any interrupt callbacks + cCall NotifyUnRegister, <di> ;Unregister any notification callbacks + cCall SignalUnRegister, <di> ;Unregister any signal callbacks + + ;** If we have fooled with the LRU lock (we only do this on 286 + ;** machines), we must force it unlocked. + cmp wLRUCount, 0 ;Is it set? + je HSP_NoLRUFoolingAround ;No, don't mess with this + mov es, hMaster ;Point to GlobalInfo struct + mov ax, es:[gi_lrulock] ;Get current lock count + sub ax, wLRUCount ;Get rid of the amount we messed it up + jns @F ;Result is OK--no underflow + xor ax, ax ;We don't like negative, so zero it +@@: mov es:[gi_lrulock], ax ;Save the result + mov wLRUCount, 0 ;No more LRU count +HSP_NoLRUFoolingAround: + pop dx + pop ax + + ;** Make sure we have a proc to chain to +HSP_Done: + or ax,dx ;NULL pointer? + jz HSP_Return ;Yes, don't chain to this one + +HSP_ChainOn: + pop es + pop ds + popa + pop bp + retf ;Jump to next signal proc + +HSP_Return: + pop es + pop ds + popa + pop bp + add sp,4 ;Clear fake return address + retf 10 ;Return to signal caller + +cEnd NOGEN + + +; HelperSegLen +; Gets the length of a segment, regardless whether it is a 286 or +; 386 segment. +; Returns the DWORD length of the segment or zero on error. +; Doesn't trash registers except DX:AX + +cProc HelperSegLen, <NEAR,PUBLIC>, <si,di,cx> + parmW wSeg +cBegin + ;** Make sure the segment is present + mov cx,wSeg ;Get the selector + lar ax,cx ;Get the access rights + jnz HSL_Bad ;If LAR fails, this is bad + test ax,8000h ;Is this segment present? + jz HSL_Bad ;No, call it bad + + ;** Do different stuff on 286 and 386/486 + mov ax,__WinFlags ;Get the flags + test ax,WF_CPU286 ;286? + jnz HSL_Do286 ;Yes, do 16 bit stuff + + ;** Get the 32 bit length +.386p + lsl eax,ecx ;Get the limit + jnz SHORT HSL_Bad ;We have an error + mov edx,eax ;Get HIWORD in DX + shr edx,16 + jmp SHORT HSL_End ;Done +.286p + + ;** Get the 16 bit length +HSL_Do286: + xor dx,dx ;286 never has >64K segs + lsl ax,cx ;Get the limit + jnz HSL_Bad ;Bad if LSL fails + jmp SHORT HSL_End ;Done + +HSL_Bad: + xor ax,ax ;Zero return value + xor dx,dx + +HSL_End: + +cEnd + +; HelperGetSegNumber +; +; Returns the segment number corresponding to a selector given the +; hExe. +; +; Caller: AX=hExe, BX=Handle +; Exit: AX=Seg Number or 0 + +cProc HelperGetSegNumber, <NEAR,PUBLIC>, <di> +cBegin + lsl cx, ax ;Is the segment OK to load? + jnz HGSN_Error ;No, don't do it + cmp cx, ne_segtab ;Long enough? + jbe HGSN_Error ;No + mov es,ax ;Point with ES + xor dx,dx ;Use DX to count segments + cmp es:[ne_magic],NEMAGIC ;Make sure we have an hExe + jnz HGSN_Error ;Nope, get out + mov cx,es:[ne_cseg] ;Get max number of segments + jcxz HGSN_Error ;No segments + mov di,es:[ne_segtab] ;Point to the segment table +HGSN_SegLoop: + inc dx ;Bump the segment number + cmp bx,es:[di].ns_handle ;Is this the correct segment entry? + je HGSN_FoundIt ;Yes, get out + add di,SIZE new_seg1 ;Bump to next entry + loop HGSN_SegLoop ;Loop back to check next entry + jmp SHORT HGSN_Error ;Not found + +HGSN_FoundIt: + mov ax,dx ;Get segment number + jmp SHORT HGSN_End + +HGSN_Error: + xor ax,ax ;Error return + +HGSN_End: +cEnd + +;** Internal helper functions + +; HelperPDBtoTDB +; +; Takes a PDB handle and finds the task handle associated with it. +; Caller: AX = PDB Handle +; Return: AX = TDB handle or zero if no TDB exists for it + +cProc HelperPDBtoTDB, <NEAR,PUBLIC> +cBegin + ;** Point to the first TDB + mov dx,_DATA ;Get the library static segment + mov es,dx ;Point with ES + mov bx,es:[npwTDBHead] ;Get pointer to first TDB + mov dx,es:[segKernel] ;Get the KERNEL data segment + mov es,dx ;Point with ES + mov dx,es:[bx] ;Get the first TDB + + ;** Check this TDB's PDB to see if it matches +PT_Loop: + mov es,dx ;Get the TDB segment + cmp ax,es:[TDB_PDB] ;Compare PDB pointers + jz PT_Found ;This is it + mov dx,es:[TDB_next] ;Get the next TDB + or dx,dx ;End of the line? + jnz PT_Loop ;Nope, loop back + xor ax,ax ;Return NULL' + jmp SHORT PT_End ;Outta here + +PT_Found: + mov ax,es ;Save the found value +PT_End: + +cEnd + +sEnd + + END diff --git a/private/mvdm/wow16/toolhelp/int1.c b/private/mvdm/wow16/toolhelp/int1.c new file mode 100644 index 000000000..3acd50307 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/int1.c @@ -0,0 +1,153 @@ +/************************************************************************** + * INT1.C + * + * Routines used to implement the interrupt trapping API in + * TOOLHELP.DLL + * + **************************************************************************/ + +#include <string.h> +#include "toolpriv.h" + +/* ----- Global variables ----- */ + WORD wIntInstalled; + INTERRUPT NEAR *npIntHead; + +/* InterruptRegister + * Registers an interrupt callback. + */ + +BOOL TOOLHELPAPI InterruptRegister( + HANDLE hTask, + FARPROC lpfnCallback) +{ + INTERRUPT *pInt; + INTERRUPT *pTemp; + + /* Make sure TOOLHELP.DLL is installed */ + if (!wLibInstalled) + return FALSE; + + /* If the interrupt hook has not yet been installed, install it */ + if (!wIntInstalled) + { + /* Make sure we can hook! */ + if (!InterruptInit()) + return FALSE; + wIntInstalled = TRUE; + } + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* Register a death signal handler for this task (does nothing if one + * is already installed. + */ + SignalRegister(hTask); + + /* Check to see if this task is already registered */ + for (pInt = npIntHead ; pInt ; pInt = pInt->pNext) + if (pInt->hTask == hTask) + return FALSE; + + /* Allocate a new INTERRUPT structure */ + pInt = (INTERRUPT *)LocalAlloc(LMEM_FIXED, sizeof (INTERRUPT)); + if (!pInt) + return FALSE; + + /* Fill in the useful fields */ + pInt->hTask = hTask; + pInt->lpfn = lpfnCallback; + + /* If this is the only handler, just insert it */ + if (!npIntHead) + { + pInt->pNext = npIntHead; + npIntHead = pInt; + } + + /* Otherwise, insert at the end of the list */ + else + { + for (pTemp = npIntHead ; pTemp->pNext ; pTemp = pTemp->pNext) + ; + pInt->pNext = pTemp->pNext; + pTemp->pNext = pInt; + } + + return TRUE; +} + + +/* InterruptUnRegister + * Called by an app whose callback is no longer to be used. + * NULL hTask uses current task. + */ + +BOOL TOOLHELPAPI InterruptUnRegister( + HANDLE hTask) +{ + INTERRUPT *pInt; + INTERRUPT *pBefore; + + /* Make sure we have interrupt installed and that TOOLHELP is OK */ + if (!wLibInstalled || !wIntInstalled) + return FALSE; + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* First try to find the task */ + pBefore = NULL; + for (pInt = npIntHead ; pInt ; pInt = pInt->pNext) + if (pInt->hTask == hTask) + break; + else + pBefore = pInt; + if (!pInt) + return FALSE; + + /* Unhook the death signal proc only if there is no interrupt handler */ + if (!NotifyIsHooked(hTask)) + SignalUnRegister(hTask); + + /* Remove it from the list */ + if (!pBefore) + npIntHead = pInt->pNext; + else + pBefore->pNext = pInt->pNext; + + /* Free the structure */ + LocalFree((HANDLE)pInt); + + /* If there are no more handlers, unhook the callback */ + if (!npIntHead) + { + InterruptUnInit(); + wIntInstalled = FALSE; + } + + return TRUE; +} + +/* ----- Helper functions ----- */ + +/* InterruptIsHooked + * Returns TRUE iff the parameter task already has a interrupt hook. + */ + +BOOL PASCAL InterruptIsHooked( + HANDLE hTask) +{ + INTERRUPT *pInt; + + /* Loop thorugh all interrupts */ + for (pInt = npIntHead ; pInt ; pInt = pInt->pNext) + if (pInt->hTask == hTask) + break; + + /* Return found/not found */ + return (BOOL)pInt; +} diff --git a/private/mvdm/wow16/toolhelp/int2.asm b/private/mvdm/wow16/toolhelp/int2.asm new file mode 100644 index 000000000..25e52ffea --- /dev/null +++ b/private/mvdm/wow16/toolhelp/int2.asm @@ -0,0 +1,1088 @@ + PAGE 60,150 +;*************************************************************************** +;* INT2.ASM +;* +;* Assembly code support routines used for the TOOLHELP.DLL interrupt +;* trapping API +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE WINDOWS.INC + include vint.inc +.286p + +;** Symbols +I_EXCEPTION EQU 0 +I_INTERRUPT EQU 1 +MAX_INTERRUPT EQU 7 +GIVE_WDEB386 EQU 8000h +BAD_STACK_FLAG EQU 8000h +MIN_STACK_ALLOWED EQU 128 + +;** Local types + +INT_INFO STRUC +ii_wNumber DW ? ;INT nn +ii_wType DW ? ;I_EXCEPTION or I_INTERRUPT +ii_dwChain DD ? +ii_wHandler DW ? ;Note that this is CS relative +INT_INFO ENDS + +;** Data +sBegin DATA + +IntInfo LABEL BYTE + public IntInfo +UD_Info DW 6 ;Undefined opcode + DW I_EXCEPTION ;This should be DPMI-hooked + DD 0 ;Chain address (will be initialized) + DW OFFSET _TEXT:UD_Handler +Div0_Info DW 0 ;Divide by zero + DW I_EXCEPTION ;Hook with DPMI + DW OFFSET _TEXT:Div0_Handler + DW 0 + DW OFFSET _TEXT:Div0_Handler +Int1_Info DW 1 ;Single step + debug register + DW I_INTERRUPT ;Hook with DOS + DD 0 ;Chain address + DW OFFSET _TEXT:Int1_Handler +Int3_Info DW 3 ;Software debug int + DW I_INTERRUPT ;Hook with DOS + DD 0 ;Chain address + DW OFFSET _TEXT:Int3_Handler +GP_Info DW 13 ;GP Fault + DW I_EXCEPTION ;This should be DPMI-hooked + ;** This entry is a special case entry for the Win30 std mode + ;* handler. This is a separate entry point into the + ;** interrupt handler routine + DW OFFSET _TEXT:GP_StdModeHandler + DW 0 + DW OFFSET _TEXT:GP_Handler +SF_Info DW 12 ;Stack fault + DW I_EXCEPTION ;This should be DPMI-hooked + ;** This entry is a special case entry for the Win30 std mode + ;* handler. This is a separate entry point into the + ;** interrupt handler routine + DW OFFSET _TEXT:SF_StdModeHandler + DW 0 + DW OFFSET _TEXT:SF_Handler +PF_Info DW 14 ;Page fault + DW I_EXCEPTION ;This should be DPMI-hooked + ;** This entry is a special case entry for the Win30 std mode + ;* handler. This is a separate entry point into the + ;** interrupt handler routine + DW OFFSET _TEXT:PF_StdModeHandler + DW 0 + DW OFFSET _TEXT:PF_Handler +CASRq_Info DW 256 ;CtlAltSysRq (fake interrupt) + DW I_INTERRUPT ;Hook with DOS + DD 0 ;Chain address + DW OFFSET _TEXT:CASRq_Handler + + ;** The following data is used to see if GDI wants the + ;** Div0 we have trapped +lpGDIFlag DD 0 +hGDI DW 0 +szGDI DB 'GDI', 0 + public lpGDIFlag, hGDI + + ;** Points to a KERNEL routine to see if it wants the + ;** GP fault first +lpfnPV DD 0 ;Call to see if PV GP fault + public lpfnPV + + ;** Globals used for DPMI emulation +lpOldHandler DD 0 ;Previous DPMI exception handler +lpChainCSIP DD 0 ;Next exception handler on chain +wException DW 0 + public lpOldHandler, lpChainCSIP + +externW wCASRqFlag ;Set when an CASRq INT3 has been set +externD dwCASRqCSIP ;Holds the CS:IP of the CASRq INT3 +sEnd + +;** Imports +externNP TerminateApp +externNP HelperHandleToSel +externNP HelperVerifySeg +externFP AllocCStoDSAlias +externFP FreeSelector +externFP GetModuleHandle +externFP GetProcAddress +externFP GlobalEntryHandle +externFP _wsprintf +externFP OutputDebugString +externA __WinFlags + +;** Functions + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +;** Interrupt trapping API + +; InterruptInit +; Hooks all necessary interrupts and exceptions to allow an API +; for app-level interrupt hooks. + +cProc InterruptInit, <NEAR,PUBLIC>, <si,di> +cBegin + ;** Loop through all possible handlers + mov cx,MAX_INTERRUPT ;Number of ints to hook + lea si,IntInfo ;Get the address of the array +DII_HandlerLoop: + push cx ;Save loop counter + cmp [si].ii_wNumber,256 ;Fake exception? + jae DII_Continue ;Yes, don't hook anything! + cmp [si].ii_wType,I_EXCEPTION ;Exception? + jnz DII_Interrupt ;Nope, hook as interrupt + + ;** Do a special case for 3.0 Std Mode + test wTHFlags,TH_WIN30STDMODE ;Are we in Win30 std mode? + jz DII_NotStdMode ;No. + mov ax,WORD PTR [si].ii_dwChain ;Get the secondary handler + mov [si].ii_wHandler,ax ;Make sure we use it instead! +DII_NotStdMode: + + ;** Hook as an exception (DPMI) + mov ax,0202h ;Get exception handler - DPMI + mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number + int 31h ;Call DPMI + mov WORD PTR [si].ii_dwChain,dx ;Save the old offset + mov WORD PTR [si].ii_dwChain + 2,cx ;Save the old selector + mov ax,0203h ;Set exception handler - DPMI + mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number + mov dx,[si].ii_wHandler ;Address of exception handler + mov cx,cs ;Selector value of handler + int 31h ;Call DPMI + jmp SHORT DII_Continue + + ;** Hook as an interrupt (DOS) +DII_Interrupt: + mov ah,35h ;Get interrrupt handler - DOS + mov al,BYTE PTR [si].ii_wNumber ;Interrupt number + int 21h ;Call DOS + mov WORD PTR [si].ii_dwChain,bx ;Save the old offset + mov WORD PTR [si].ii_dwChain + 2,es ;Save the old selector + mov ah,25h ;Set interrupt handler - DOS + mov al,BYTE PTR [si].ii_wNumber ;Interrupt number + mov dx,[si].ii_wHandler ;Address of exception handler + push ds ;Save static DS for later + push cs ;DS = CS + pop ds + int 21h ;Call DOS + pop ds ;Get segment back + + ;** Prepare for next in table +DII_Continue: + add si,SIZE INT_INFO ;Bump to next entry + pop cx ;Get loop counter back + loop DII_HandlerLoop ;Loop back + + ;** Prepare the linked list + mov npIntHead,0 ;Put a NULL in the list head + + ;** Get information so we can check the GDI flag + lea ax,szGDI ;Get the string + cCall GetModuleHandle, <ds,ax> ;Get GDI's module handle + cCall HelperHandleToSel, <ax> ;Convert the owner to a selector + mov hGDI,ax ;Save it for later + cCall GetProcAddress, <ax,0,355> ;The flag is ordinal 355 + mov WORD PTR lpGDIFlag[0],ax ;Save it for later + mov WORD PTR lpGDIFlag[2],dx + +DII_End: + ;** Return TRUE + mov ax,1 +cEnd + + +; InterruptUnInit +; Unhooks all interrupts and exceptions hooked by DebugInterruptUnInit. + +cProc InterruptUnInit, <NEAR,PUBLIC>, <si,di> +cBegin + + ;** Loop through all possible handlers + mov cx,MAX_INTERRUPT ;Number of ints to hook + lea si,IntInfo ;Get the address of the array +DIU_HandlerLoop: + push cx ;Save loop counter + cmp [si].ii_wNumber,256 ;Fake exception? + jae DIU_Continue ;Yes, don't unhook anything! + cmp [si].ii_wType,I_EXCEPTION ;Exception? + jnz DIU_Interrupt ;Nope, hook as interrupt + + ;** Unhook exception (DPMI) + mov ax,0203h ;Set exception handler - DPMI + mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number + mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset + mov cx,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector + int 31h ;Call DPMI + jmp SHORT DIU_Continue + + ;** Unhook interrupt (DOS) +DIU_Interrupt: + mov ah,35h ;Get interrrupt handler - DOS + mov al,BYTE PTR [si].ii_wNumber ;Interrupt number + int 21h ;Call DOS + mov ah,25h ;Set interrupt handler - DOS + mov al,BYTE PTR [si].ii_wNumber ;Interrupt number + mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset + push ds + mov ds,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector + int 21h ;Call DOS + pop ds + + ;** Prepare for next in table +DIU_Continue: + add si,SIZE INT_INFO ;Bump to next entry + pop cx ;Get loop counter back + loop DIU_HandlerLoop ;Loop back + + ;** Prepare the linked list + mov npIntHead,0 ;Put a NULL in the list head + +DIU_End: + +cEnd + +InterruptEntry MACRO Name, wBytes + labelFP Name ;;Start at this address + PUBLIC Name + sub sp,wBytes ;;Leave room on stack for return val + push bx ;;Save for the info pointer +ENDM + +InterruptJump MACRO pInfo + mov bx,OFFSET pInfo ;;Point to interrupt info + jmp DIH_Main +ENDM + +; InterruptHandler +; This routine is used to handle interrupts as they come in. This +; routine has multiple entry points; a seperate one for each int/ +; exception trapped. Because interrupts and exceptions have +; different stack frames, they are handled by two different code +; sections. + +cProc InterruptHandler, <FAR,PUBLIC> +cBegin NOGEN + + ;** All interrupt entry points here + + InterruptEntry GP_Handler, 14 ;Normal GP fault + InterruptJump GP_Info + InterruptEntry GP_StdModeHandler, 12 ;3.0 Std mode GP fault + InterruptJump GP_Info + InterruptEntry SF_Handler, 14 ;Normal Stack Fault + InterruptJump SF_Info + InterruptEntry SF_StdModeHandler, 12 ;3.0 Std mode Stack Fault + InterruptJump SF_Info + InterruptEntry PF_Handler, 14 ;Page fault + InterruptJump PF_Info + InterruptEntry PF_StdModeHandler, 10 ;3.0 Std mode Page fault + InterruptJump PF_Info + InterruptEntry UD_Handler, 14 ;Undefined opcode + InterruptJump UD_Info + InterruptEntry Int1_Handler, 14 ;Int 1 + InterruptJump Int1_Info + InterruptEntry Int3_Handler, 14 ;Int 3 + InterruptJump Int3_Info + InterruptEntry CASRq_Handler, 14 ;Ctrl-Alt-SysRq (not really an int) + InterruptJump CASRq_Info + + ;** The divide by zero case has to include checking to make sure + ;** that this isn't GDI's divide by zero. + + InterruptEntry Div0_Handler, 14 + + ;** Check to see if GDI wants this Div0 + push ds ;Save some registers + push es + mov bx,_DATA ;Point to our data + mov ds,bx ; with DS + mov bx,WORD PTR lpGDIFlag[0];Get the low word + push bx + or bx,WORD PTR lpGDIFlag[2];Do we have a flag to look at? + pop bx + jz DIH_NoFlag ;No. Do this the hard way + + ;** Since we have a pointer to GDI's flag to look at, use it + mov es,WORD PTR lpGDIFlag[2];Get the seg value + cmp WORD PTR es:[bx],0 ;The flag is nonzero if GDI wants it + je DIH_NormalDiv0 ;GDI doesn't want it + + ;** GDI wants the Div0 so chain to it +DIH_ChainToGDI: + pop es ;Restore registers + pop ds ; (Doesn't trash flags) + push bp ;Make the same stack frame for compat + mov bp,sp + pusha ;Save all registers + push ds + push es + mov ax,_DATA ;Get the data segment + mov ds,ax ;Point with DS + mov bx,OFFSET Div0_Info ;This fault's info + jmp DIH_DPMIChainOn ;Chain on (ignore the int) + +DIH_NormalDiv0: + pop es ;Restore registers + pop ds + InterruptJump Div0_Info + + ;** We didn't get a GDI flag (only present in 3.1) so instead, we + ;* check the owner of the CS where the fault occurred. If + ;** the owner is GDI, we ignore the Div0. +DIH_NoFlag: + push bp ;Make a stack frame + mov bp,sp + sub sp,SIZE GLOBALENTRY ;Make room for a structure +Global EQU [bp - SIZE GLOBALENTRY] ;Point to our structure + pusha ;Save all registers + mov WORD PTR Global.ge_dwSize[0],SIZE GLOBALENTRY ;Size of struct + mov WORD PTR Global.ge_dwSize[2],0 + lea bx,Global ;Point to the structure + test wTHFlags,TH_WIN30STDMODE ;3.0 std mode? + jnz DIH_Div0_StdMode ;Yes + mov ax,[bp + 1ah + 4] ;Get the CS value (4 is extra BP,BX + jmp SHORT @F ; pushed by InterruptEntry macro) +DIH_Div0_StdMode: + mov ax,[bp + 14h + 4] ;Get the CS value +@@: cCall GlobalEntryHandle, <ss,bx,ax> ;Get info about the CS + or ax,ax ;Did the call succeed? + jne @F ;Yes, go on + popa ;No, clear stack frame and do normal + mov sp,bp + pop bp + jmp DIH_NormalDiv0 ;Jump to normal processing +@@: mov ax,Global.ge_hOwner ;Get the owner + cCall HelperHandleToSel, <ax> ;Make it a selector + cmp hGDI,ax ;Is this owned by GDI? + popa ;Restore the registers + mov sp,bp ;Clear stack frame + pop bp + je DIH_ChainToGDI ;Yes, so give the interrupt to it + jmp DIH_NormalDiv0 ;No, do normal stuff + + ;** We now have to first find the item on the block to see if we + ;** want to handle the interrupt. +PubLabel CommonInterruptEntry +DIH_Main: + push bp ;Make a stack frame + mov bp,sp + pusha ;Save all registers + push ds + push es + + ;** We check first to see if this was a GP fault received from the + ;* parameter validation code. If it was, we just chain on + ;* just as if we don't find any handlers. + ;** + mov ax,_DATA ;Get our data segment + mov ds,ax + FSTI ;Must have interrupts on + cmp bx,OFFSET GP_Info ;GP Fault? + jnz DIH_NotPVGPFault ;No. + mov cx,WORD PTR lpfnPV[0] ;Get the low word + or cx,WORD PTR lpfnPV[2] ;Param Validation stuff present? + jz DIH_NotPVGPFault ;No, skip this + + ;** Check to see if the parameter validation code wants the fault + push ds + push bx + push [bp + 1Ah] ;Push faulting CS:IP + push [bp + 18h] + call [lpfnPV] ;Call it + pop bx + pop ds + or ax,ax ;Non-zero means this was PV fault + je DIH_NotPVGPFault ;Not a PV GP fault + + ;** It is a parameter validation fault so ignore it + jmp DIH_DPMIChainOn ;Chain the fault on--we don't want it + + ;** We check here to see if the INT3 we received is from the CASRq + ;* handler. If it was, we have to replace the INT3 with the + ;* previous byte and tell the user this was actually a CASRq + ;** event (not an INT3). +PubLabel DIH_NotPVGPFault + cmp bx,OFFSET Int3_Info ;INT3? + jnz DIH_NotCASRq ;Nope, ignore all this + cmp wCASRqFlag,0 ;Was this because of CASRq? + je DIH_NotCASRq ;No. + mov ax,[bp + 12h] ;INT3 is an IRET frame. Get bkpt IP + dec ax ;Breaks AFTER instruction + cmp WORD PTR dwCASRqCSIP[0],ax ;Is this the right CASRq address? + jne DIH_NotCASRq ;Nope + mov dx,[bp + 14h] ;Get the breakpoint CS + cmp WORD PTR dwCASRqCSIP[2],dx ;Is this correct? + jne DIH_NotCASRq ;Nope + push ax ;Save the IP value + cCall AllocCStoDSAlias, <dx> ;Get a data alias to the CS + mov es,ax ;Point with ES + pop si ;Restore the IP value + mov [bp + 12h],si ;Back to instr where INT3 was + mov al,BYTE PTR wCASRqFlag ;Get the saved byte + mov es:[si],al ;Put it back in the code + mov wCASRqFlag,0 ;Clear the flag + cCall FreeSelector, <es> ;Get rid of the alias + mov bx,OFFSET CASRq_Info ;Point to the CASRq information + + ;** See if we have at least one handler. We should always have one. +PubLabel DIH_NotCASRq + mov si,npIntHead ;Get the list start + or si,si ;Are there any routines hooked? + jnz DIH_Found ;There should ALWAYS be at least one + ; routine hooked (otherwise, the + ; interrupt hooks should have + ; already been removed) + + ;** Return the stack to its prior state and chain on. + ;* We only get here in an erroneous state. We keep the code in + ;* to avoid GP faulting if things get wierd. + ;* The stack looks like this: + ;* ------------ + ;* | ES | + ;* | DS | + ;* | PUSHA | + ;* BP-->| Old BP | [BP + 00h] + ;* | BX | [BP + 02h] + ;* | Empty | [BP + 04h] + ;* | Empty | [BP + 06h] + ;* | Empty | [BP + 08h] + ;* | Empty | [BP + 0Ah] + ;* | Empty | [BP + 0Ch] + ;* |Our Ret IP| [BP + 0Eh] + ;* |Our Ret CS| [BP + 10h] + ;* |Original | + ;* | Frame | + ;* | .... | + ;* ------------ + ;** +PubLabel DIH_DPMIChainOn + mov ax,WORD PTR [bx].ii_dwChain ;Get the LOWORD + mov [bp + 0eh],ax ;Put into the frame we created + mov ax,WORD PTR [bx].ii_dwChain + 2 ;Get the HIWORD + mov [bp + 10h],ax ;Put into the frame + pop es ;Clear the stack + pop ds + popa + pop bp + pop bx + add sp,10 ;Clear extra space + retf ;This uses our own "return" frame + ; to chain on + + ;** Since we found the entry, we have to call the user callback. + ;* Because we must be reentrant at this state, we have to make + ;* sure that we're safe. To do so, we must do different + ;** actions for DPMI and for DOS frames. +PubLabel DIH_Found + cmp [bx].ii_wType,I_EXCEPTION ;DPMI Exception frame? + je @F + jmp DIH_SkipDPMI ;No. Skip DPMI processing +@@: + + ;** If we are in Win3.0 Std Mode, the DPMI frame was broken. It + ;* simply left the normal IRET frame on the stack *AND* the + ;** error code. + test wTHFlags,TH_Win30StdMode ;3.0 Std mode? + jz @F + jmp DIH_SkipDPMI ;Yes +@@: + + ;** Tell DPMI that the exception is over. Before we do this, + ;* however, save information we'll need later on the user stack. + ;* The stack currently looks like this: + ;* ------------ + ;* | ES | + ;* | DS | + ;* | PUSHA | + ;* BP-->| Old BP | [BP + 00h] + ;* | BX | [BP + 02h] + ;* | Empty | [BP + 04h] + ;* | Empty | [BP + 06h] + ;* | Empty | [BP + 08h] + ;* | Empty | [BP + 0Ah] + ;* | Empty | [BP + 0Ch] + ;* | Empty | [BP + 0Eh] + ;* | Empty | [BP + 10h] + ;* | Ret IP | [BP + 12h] <- + ;* | Ret CS | [BP + 14h] | + ;* |Error Code| [BP + 16h] | Pushed by DPMI + ;* | IP | [BP + 18h] | + ;* | CS | [BP + 1Ah] | (Locked stack) + ;* | Flags | [BP + 1Ch] | + ;* | SP | [BP + 1Eh] | + ;* | SS | [BP + 20h] <- + ;* ------------ + ;* + ;* Before returning to DPMI, however, we want to create a + ;* stack frame on the user's stack that we will be returning + ;* to so we can preserve information in a reentrant fashion. + ;* The user's stack will appear like this: + ;* ------------ + ;* BP---->| Old BP | [BP + 00h] + ;* | BX | [BP + 02h] + ;* |Our Ret IP| [BP + 04h] + ;* |Our Ret CS| [BP + 06h] + ;* | Ret IP | [BP + 08h] + ;* | Ret CS | [BP + 0Ah] + ;* | AX | [BP + 0Ch] + ;* |Exception#| [BP + 0Eh] + ;* | Handle | [BP + 10h] + ;* | IP | [BP + 12h] + ;* | CS | [BP + 14h] + ;* | Flags | [BP + 16h] + ;* ------------ + ;** + +PubLabel DIH_Exception + + ;** Check to see if we're already on the faulting stack. If we are, + ;** we want to shift everything up on this stack so that we + ;** have room for the TOOLHELP user frame + mov ax,ss ;Get the current SS + cmp ax,WORD PTR ss:[bp + 20h] ;Is it the same as the user frame? + jne DIH_EnoughRoomOnStack ;No, ignore all of this + + ;** Move everything up by copy everything that's on stack now to + ;** above where SP starts at. This actually uses too much + ;** stack, but it's safe and easy. + push bp ;We use BP to do copy + lea bp,[bp + 20h] ;Point to lowest WORD to copy + mov ax,sp ;Point to position to copy to + dec ax + dec ax +DIH_CopyLoop: + push WORD PTR [bp] ;Copy a WORD + dec bp ;Point to next WORD to copy + dec bp + cmp bp,ax ;Done yet? + jne DIH_CopyLoop ;No + pop bp ;Yes, compute new BP value + sub bp,56 ;Point BP to right place + + ;** Put stuff on DPMI's stack +PubLabel DIH_EnoughRoomOnStack + mov di,[bp + 1Eh] ;Get the old SP value + mov cx,[bp + 20h] ; and the SS value + cmp di,MIN_STACK_ALLOWED ;Are we going to stack fault? + jb DIH_BadStack ;Yes, so swich + mov ax,__WinFlags ;Make sure we have a 386 or higher + test ax,WF_CPU286 + jnz DIH_SkipBigCheck ;No need to check big bit +.386p + push eax ;Make sure we don't trash EAX + lar eax,ecx ;Get the access rights DWORD + test eax,00400000h ;Check the BIG bit + pop eax + jnz DIH_BadStack ;Don't use this stack if BIG +.286p +DIH_SkipBigCheck: + mov ax,di ;Get the stack pointer + add ax,2 ;Point just beyond + cCall HelperVerifySeg, <cx,ax> ;Is this seg OK? + or ax,ax ;Check for success + jz DIH_BadStack ;Stack is bogus, don't change to it + +PubLabel DIH_StackOK + sub di,20 ;Reserve space for the user frame + mov ds,cx ;Get stack value in DS + mov dx,[bp + 1Ah] ;Get the old CS value + mov cx,[bp + 18h] ;Get the old IP value + mov ax,[bp + 1Ch] ;Get the old flags + mov [bp + 1Eh],di ;Save as new SP value + sub di,4 ;Make DI equal to what BP will be + mov [bp + 1Ah],cs ;Prepare to IRET to ourself + mov [bp + 18h],OFFSET _TEXT:DIH_DPMIRestart + + ;** Save some things on the user's stack before returning + mov [di + 16h],ax ;Save the flags + mov [di + 14h],dx ;Save the old CS value + mov [di + 12h],cx ;Save the old IP value + mov [di + 0Eh],bx ;INT_INFO pointer to new stack + mov [di + 10h],si ;Handle to new stack + + ;** Clear the Trace and Ints Enabled flags + and [bp + 1Ch],NOT 0100h ;Clear TF. We don't want to trace here + pop es ;Clear the DPMI stack + pop ds + popa + pop bp + pop bx + add sp,14 ;Clear extra allocated space + retf + + ;** The user stack is bad, so we want to stay on the fault handler + ;** stack. In order to do this, we have to build a frame for + ;** the callback directly on the fault handler stack. + ;** We build the frame here and jump to it. + ;** ------------ + ;** | ES | + ;** | DS | + ;** | PUSHA | + ;** BP-->| Old BP | [BP + 00h] + ;** | BX | [BP + 02h] + ;** | Empty | [BP + 04h] + ;** |Our Ret IP| [BP + 06h] ; Client callback addr + ;** |Our Ret CS| [BP + 08h] + ;** | Ret IP | [BP + 0Ah] ; TOOLHELP restart addr + ;** | Ret CS | [BP + 0Ch] + ;** | AX | [BP + 0Eh] ; Saved AX for MPI + ;** |Exception#| [BP + 10h] ; Exception number + ;** | Handle | [BP + 12h] ; TOOLHELP handle + ;** | IP | [BP + 14h] ; IRET frame of fault + ;** | CS | [BP + 16h] + ;** | Flags | [BP + 18h] + ;** | SP | [BP + 1Ah] ; Faulting SS:SP + ;** | SS | [BP + 1Ch] + ;** | Ret IP | [BP + 1Eh] ; DPMI return address + ;** | Ret CS | [BP + 20h] + ;** ------------ +PubLabel DIH_BadStack + mov dx,[bp + 12h] ;DPMI return CS:IP + mov cx,[bp + 14h] ; stored in CX:DX + mov ax,[bp + 18h] ;Faulting IP + mov [bp + 14h],ax + mov ax,[bp + 1Ah] ;Faulting CS + mov [bp + 16h],ax + mov ax,[bp + 1Ch] ;Flags + mov [bp + 18h],ax + mov ax,[bp + 1Eh] ;Faulting SP + mov [bp + 1Ah],ax + mov ax,[bp + 20h] ;Faulting SS + mov [bp + 1Ch],ax + mov [bp + 1Eh],dx ;DPMI ret IP + mov [bp + 20h],cx ;DPMI ret CS + mov [bp + 12h],si ;Point to INTERRUPT struct + mov ax,[bx].ii_wNumber ;Get the interrupt number + or ax,BAD_STACK_FLAG ;Flag the client that stack is bad + mov [bp + 10h],ax + mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame + mov [bp + 0Eh],ax + mov [bp + 0Ch],cs ;Point to callback return address + mov [bp + 0Ah],OFFSET _TEXT:DIH_CallbackRestart + mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET + mov [bp + 06h],ax + mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment + mov [bp + 08h],ax + pop es ;Clear the stack + pop ds + popa + pop bp + pop bx + add sp,2 + retf ;Jump to the user callback + + ;** At this point, DPMI IRETs back to us instead of to the faulting + ;** app. We have to now create a stack frame identical to the + ;** frame used by interrupt-style hooks. Note that we have + ;** already allocated the frame space (but not initialized it) + ;** before returning to DPMI. + ;** It will look like this: + ;** ------------ + ;** | ES | [BP - 14h] + ;** | DS | [BP - 12h] + ;** | DI | [BP - 10h] + ;** | SI | [BP - 0Eh] + ;** | BP | [BP - 0Ch] + ;** | SP | [BP - 0Ah] + ;** | BX | [BP - 08h] + ;** | DX | [BP - 06h] + ;** | CX | [BP - 04h] + ;** | AX | [BP - 02h] + ;** BP---->| Old BP | [BP + 00h] + ;** | BX | [BP + 02h] + ;** |Our Ret IP| [BP + 04h] + ;** |Our Ret CS| [BP + 06h] + ;** | Ret IP | [BP + 08h] + ;** | Ret CS | [BP + 0Ah] + ;** | AX | [BP + 0Ch] + ;** |Exception#| [BP + 0Eh] + ;** | Handle | [BP + 10h] + ;** | IP | [BP + 12h] + ;** | CS | [BP + 14h] + ;** | Flags | [BP + 16h] + ;** ------------ + ;** +PubLabel DIH_DPMIRestart + push bx ;Save this register we're using + push bp ;Make a stack frame + mov bp,sp + pusha ;Save all the registers + push ds + push es + mov bx,[bp + 0Eh] ;Get the INT_INFO pointer back + mov si,[bp + 10h] ;Get the INTERRUPT structure back + mov ax,_DATA ;Get our data segment + mov ds,ax + + ;** We can now proceed with joint processing as we've matched the + ;** DOS interrupt frame +PubLabel DIH_SkipDPMI + + ;** Build our return frame and jump to the user callback + mov [bp + 10h],si ;Point to INTERRUPT struct + mov ax,[bx].ii_wNumber ;Get the interrupt number + mov [bp + 0Eh],ax ;Put on frame + mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame + mov [bp + 0Ch],ax ;Put on frame + mov [bp + 0Ah],cs ;Point to callback return address + mov [bp + 08h],OFFSET _TEXT:DIH_CallbackRestart + mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET + mov [bp + 04h],ax + mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment + mov [bp + 06h],ax + pop es ;Clear the stack + pop ds + popa + pop bp + pop bx + retf ;Jump to the user callback + + ;** When the callback returns, we have to know how to call the + ;* next matching callback or to chain on the interrupt list. + ;* We have to do a raft of special stuff if this was an + ;* exception so that the chained on handlers think it was + ;** DPMI that called them. +PubLabel DIH_CallbackRestart + sub sp,8 ;Leave room for ret addresses + push bx ;For compat. with the above code + push bp ;Make the same stack frame + mov bp,sp + pusha + push ds + push es + + ;** Get the next matching item on the list + mov ax,_DATA ;Get our data segment + mov ds,ax + mov ax,[bp + 0Ch] ;Get the saved AX value + mov [bp - 02h],ax ;Put in PUSHA frame + mov si,[bp + 10h] ;Get the last handle used + or si,si ;If NULL, app has messed with it + jz DIH_NukeIt ;Nuke the app--it did a no-no + mov si,[si].i_pNext ;Get the next item in the list + or si,si ;End of the line? + jz DIH_NextNotFound ;Yes, chain on + mov ax,[bp + 0Eh] ;Get the exception number + cCall InterruptInfo ;Get the INT_INFO structure + or ax,ax ;If NULL return, user messed with # + jz DIH_NukeIt ; so nuke it + mov bx,ax ;Point with BX + jmp DIH_SkipDPMI ;Process this one + + ;** If we don't find a match, we pass on to previous handler +PubLabel DIH_NextNotFound + mov ax,[bp + 0Eh] ;Get the exception number + and ax,7fffh ;Clear the new stack bit + cCall InterruptInfo ;Find the INT_INFO structure + or ax,ax ;If the user messed with it, + jz DIH_NukeIt ; nuke the app. + mov si,ax ;Get the pointer in AX + test wTHFlags,TH_Win30StdMode ;3.0 Std mode? + jnz DIH_30StdChainOn ;Always do normal chain on in 3.0sm + cmp [si].ii_wType,I_INTERRUPT ;Was this an interrupt? + je DIH_ChainOn ;Yes, do normal chain on + jmp DIH_EmulateDPMI ;No, do the DPMI chain on + +PubLabel DIH_NukeIt + push [bp + 16h] ;Copy the IRET frame for KERNEL + push [bp + 14h] + push [bp + 12h] + + push 0 ;Nuke current task + push UAE_BOX OR GIVE_WDEB386 ;UAE box + give to wdeb + push cs ;Simulate a far jump + call NEAR PTR TerminateApp ;Nuke the app + + ;** We only get here if WDEB386 is installed. We tell it to set + ;* a breakpoint, then restart the app, in effect giving + ;* control to WDEB386. Unfortunately, at this point, all + ;** fault handlers for this task have been removed + add sp,6 ;Clear fake IRET frame + mov cx,[bp + 14h] ;Faulting CS + mov bx,[bp + 12h] ;Faulting IP + mov ax, 40h ;16 bit forced go command + int 41h ;Call debugger + pop es ;Restore registers and clear stack + pop ds + popa + pop bp + pop bx + add sp,14 ;Clear extra words + ; all that remains is IRET frame + iret ;WDEB386 will get control + +PubLabel DIH_NukeApp + push 0 ;Nuke current task + push UAE_BOX ;Draw the UAE box + push cs ;Simulate a far jump + call NEAR PTR TerminateApp ;Nuke the app + int 1 ;Should never return + jmp SHORT DIH_ChainOn + + ;** In 3.0 standard mode we have to put an error code on the stack + ;** if it's a GP fault or. If not, we just chain on normally +PubLabel DIH_30StdChainOn + cmp si,OFFSET GP_Info ;Is this a GP fault? + jne DIH_ChainOn ;No, handle normally + mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD + mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD + mov bx,ax ;Save the LOWORD + or ax,dx ;Is there a chain on address? + jz DIH_NoChainAddr ;No, just restart the instruction + mov [bp + 0Ch],bx ;Put on stack so we can retf to it + mov [bp + 0Eh],dx + mov WORD PTR [bp + 10h],0 ;Zero the error code + pop es ;Restore registers and clear stack + pop ds + popa + pop bp + pop bx + add sp,8 ;Clear extra words + retf + +PubLabel DIH_ChainOn + mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD + mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD + mov bx,ax ;Save the LOWORD + or ax,dx ;Is there a chain on address? + jz DIH_NoChainAddr ;No, just restart the instruction + mov [bp + 0Eh],bx ;Put on stack so we can retf to it + mov [bp + 10h],dx + pop es ;Restore registers and clear stack + pop ds + popa + pop bp + pop bx + add sp,10 ;Clear extra words + retf + + ;** No chain on address was recorded so just restart the instruction +PubLabel DIH_NoChainAddr + pop es + pop ds + popa + pop bp + pop bx + add sp,14 ;Clear all extra words + iret ; and restart instruction + + ;** Chain on a DPMI-style exception: + ;** + ;** The goal here is to make a fault frame that appears that DPMI + ;** has passed the next exception handler the interrupt. We + ;** have only two important cases here: + ;** 1) We have already told DPMI the int was finished. + ;** 2) We have not told DPMI the int was finished and + ;** have not switched off the fault handler stack + ;** We handle the cases differently: + ;** -If we have already told DPMI that the fault was handled, + ;** we have to make a new fault so that the next handler can see + ;** the frame. This can be best accomplished by restarting the + ;** faulting instruction. This will cause the same fault to + ;** happen and will make the same frame. + ;** -In the case of us still being on the fh stack, we have to + ;** rebuild the frame and chain on. + ;** The stack we're given looks like this: + ;** ------------ + ;** | ES | [BP - 14h] + ;** | DS | [BP - 12h] + ;** | DI | [BP - 10h] + ;** | SI | [BP - 0Eh] + ;** | BP | [BP - 0Ch] + ;** | SP | [BP - 0Ah] + ;** | BX | [BP - 08h] + ;** | DX | [BP - 06h] + ;** | CX | [BP - 04h] + ;** | AX | [BP - 02h] + ;** BP---->| Old BP | [BP + 00h] + ;** | BX | [BP + 02h] + ;** |Our Ret IP| [BP + 04h] + ;** |Our Ret CS| [BP + 06h] + ;** | Ret IP | [BP + 08h] + ;** | Ret CS | [BP + 0Ah] + ;** | AX | [BP + 0Ch] + ;** |Exception#| [BP + 0Eh] + ;** | Handle | [BP + 10h] + ;** | IP | [BP + 12h] + ;** | CS | [BP + 14h] + ;** | Flags | [BP + 16h] + ;** | SP | [BP + 18h] ;Only here if on fh stack + ;** | SS | [BP + 1Ah] + ;** | Ret IP | [BP + 1Ch] ;DPMI return address + ;** | Ret CS | [BP + 1Eh] + ;** ------------ +PubLabel DIH_EmulateDPMI + mov ax,[bp + 0Eh] ;Get the exception number + test ax,BAD_STACK_FLAG ;Still on fh stack? + jnz DIH_RebuildDPMIFrame ;Yes, rebuild the frame + + ;** Rehook the exception so we're sure to get it first + push si ;Preserve handle + mov bx,ax ;Fault number in bx + mov wException,bx ;Save as a static also + mov ax,0202h ;Get exception handler - DPMI + int 31h ;Call DPMI + mov WORD PTR lpOldHandler[0],dx ;Save the old exception handler + mov WORD PTR lpOldHandler[2],cx + mov ax,0203h ;Set exception handler - DPMI + mov dx,OFFSET DIH_EmDPMIRestart + mov cx,cs ;Selector value of handler + int 31h ;Call DPMI + pop si + + ;** Save the address of the next exception handler + mov ax,WORD PTR [si].ii_dwChain[0] ;Address to chain fault to + mov WORD PTR lpChainCSIP[0],ax + mov ax,WORD PTR [si].ii_dwChain[2] + mov WORD PTR lpChainCSIP[2],ax + + ;** Restart the instruction. This will fault and jump to our + ;** newly-established handler at DIH_EmDPMIRestart + pop es + pop ds + popa + pop bp + pop bx + add sp,14 ;Clear all extra words + iret ; and restart instruction + + ;** Now we're on the fault handler stack with a DPMI frame. Throw + ;** it to the next handler on the chain +PubLabel DIH_EmDPMIRestart + sub sp,4 ;Enough room for a RETF frame + push bp + mov bp,sp + pusha + push ds + push es + mov ax,_DATA ;Point to TOOLHELP's DS + mov ds,ax + + ;** Restore the exception handler + mov ax,0203h ;Set exception handler - DPMI + mov bx,wException ;Get exception number + mov dx,WORD PTR lpOldHandler[0] ;Get the exception handler address + mov cx,WORD PTR lpOldHandler[2] + int 31h ;Call DPMI + + ;** Put the chain address on the stack so we can return to it + mov ax,WORD PTR lpChainCSIP[0] ;Get chain address + mov [bp + 02h],ax + mov ax,WORD PTR lpChainCSIP[2] + mov [bp + 04h],ax + + ;** Restore registers and jump to the handler + pop es + pop ds + popa + pop bp + retf + + ;** Since we are already on the fault handler stack, there is no + ;** need to fault again. All we have to do here is recreate the + ;** DPMI fault stack as if the fault had just occurred. It would + ;** be nice to clear the exception and then make it fault again, + ;** but since we only get here in potentially stack-faulting + ;** conditions, we cannot do this. We just build a reasonable + ;** facsimile of the frame and chain on. This frame should + ;** look as follows when we're done: + ;** ------------ + ;** | ES | [BP - 14h] + ;** | DS | [BP - 12h] + ;** | DI | [BP - 10h] + ;** | SI | [BP - 0Eh] + ;** | BP | [BP - 0Ch] + ;** | SP | [BP - 0Ah] + ;** | BX | [BP - 08h] + ;** | DX | [BP - 06h] + ;** | CX | [BP - 04h] + ;** | AX | [BP - 02h] + ;** BP---->| Old BP | [BP + 00h] + ;** | BX | [BP + 02h] + ;** | Empty | [BP + 04h] + ;** | Empty | [BP + 06h] + ;** | Empty | [BP + 08h] + ;** | Empty | [BP + 0Ah] + ;** | Chain IP | [BP + 0Ch] + ;** | Chain CS | [BP + 0Eh] + ;** | Ret IP | [BP + 10h] + ;** | Ret CS | [BP + 12h] + ;** |Error Code| [BP + 14h] ;Always return zero + ;** | IP | [BP + 16h] + ;** | CS | [BP + 18h] ;Only here if on fh stack + ;** | Flags | [BP + 1Ah] + ;** | SP | [BP + 1Ch] ;DPMI return address + ;** | SS | [BP + 1Eh] + ;** ------------ + +PubLabel DIH_RebuildDPMIFrame + mov dx,[bp + 1Ch] ;DPMI return CS:IP + mov cx,[bp + 1Eh] ; stored in CX:DX + mov ax,[bp + 1Ah] ;Faulting SS + mov [bp + 1Eh],ax + mov ax,[bp + 18h] ;Faulting SP + mov [bp + 1Ch],ax + mov ax,[bp + 16h] ;Flags + mov [bp + 1Ah],ax + mov ax,[bp + 14h] ;Faulting CS + mov [bp + 18h],ax + mov ax,[bp + 12h] ;Faulting IP + mov [bp + 16h],ax + xor ax,ax ;Error code + mov [bp + 14h],ax + mov [bp + 12h],cx ;DPMI ret CS + mov [bp + 10h],dx ;DPMI ret IP + mov ax,WORD PTR [si].ii_dwChain[2] ;Address to chain fault to + mov [bp + 0Eh],ax + mov ax,WORD PTR [si].ii_dwChain[0] + mov [bp + 0Ch],ax + pop es + pop ds + popa + pop bp + pop bx + add sp,8 ;Clear all extra words + retf + +cEnd NOGEN + + +;** Helper functions + +; InterruptInfo +; Gets a pointer to the INT_INFO structure given the interrupt +; number. Accepts the int number in AX and returns the pointer in AX. +; Preserves all other registers + +cProc InterruptInfo, <NEAR,PUBLIC>, <si,cx> +cBegin + ;** Loop through all possible handlers + mov cx,MAX_INTERRUPT + 1 ;Number of ints to hook + lea si,IntInfo ;Get the address of the array + + ;** Is this a match? +II_HandlerLoop: + cmp [si].ii_wNumber,ax ;Match? + jz II_End ;Yes, return the pointer + + ;** Prepare for next in table +II_Continue: + add si,SIZE INT_INFO ;Bump to next entry + loop II_HandlerLoop ;Loop back + xor si,si ;Return NULL for not found + +II_End: + mov ax,si ;Get return value +cEnd + +sEnd + + END diff --git a/private/mvdm/wow16/toolhelp/krnlpeek.asm b/private/mvdm/wow16/toolhelp/krnlpeek.asm new file mode 100644 index 000000000..0e34099f9 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/krnlpeek.asm @@ -0,0 +1,110 @@ +;*************************************************************************** +;* KRNLPEEK.ASM +;* +;* Assembly code used to peer into the heart of KERNEL and return +;* information in global variables. +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC ;Include the TOOLHELP values +PMODE32 = 0 ;This should work either way +PMODE = 0 + INCLUDE WINKERN.INC + INCLUDE WINDOWS.INC + +;** Functions +externFP GlobalMasterHandle +externFP GlobalLock +externFP GetVersion +externFP GetProcAddress +externFP GetModuleHandle +externNP HelperHandleToSel + +sBegin DATA +externB _szKernel +sEnd DATA +;** Functions + +sBegin CODE + assumes CS,CODE + +; void KernelType(void) +; +; Returns information from KERNEL in global variables + +cProc KernelType, <PUBLIC>, <si,di> +cBegin + ;** Make sure we're in PMODE. TOOLHELP does not run in non-PMODE + ;** Windows. + mov ax,__WinFlags ;Get WinFlags + test ax,1 ;In PMODE? + mov wTHFlags,0 ;Zero flags indicates error + jnz @F ;Yes, go on + jmp KT_End ;No, not in PMODE, return error +@@: +.286 + ;** Call the undocumented function GlobalMasterHandle to get + ;* a pointer to the global HeapInfo structure. + ;** This is the means we can use to detect the kernel types. + + cCall GlobalMasterHandle + cCall HelperHandleToSel, <dx> ;Convert it to a selector + mov hMaster,ax ;Save the handle + mov es,ax ;Use ES to point to the block + mov ax,es:[hi_first] ;Get low word of first heap entry + test ax,01fh ;Mask out lowest 5 bits + jz KT_Krnl386 ;Must be Krnl386 so leave the flags + mov wTHFlags,TH_KERNEL_286 + jmp SHORT KT_BothPModes ;Skip TH_KERNEL_386 stuff +KT_Krnl386: + mov wTHFlags,TH_KERNEL_386 +KT_BothPModes: + + ;** Now get pmode KERNEL information + cCall GetVersion ;Which Windows version are we on + mov bx,SEG GlobalLock ;KERNEL code segment selector + cmp ax,0003h ;Win 3.0 or Win 3.0a? + je KT_Win30 ;Yes + cmp ax,0103h ;Beta releas of Win 3.0a? + je KT_Win30 ;Yes + cmp ax,0a03h ;Win 3.1? + je KT_Win31 ;Yes + mov wTHFlags,0 ;Zero wTHFlags indicates error + jmp SHORT KT_End ;Unknown Windows version +KT_Win31: + mov ax,seg _DATA + mov dx,offset _DATA:_szKernel + cCall GetModuleHandle,<ax,dx> + cCall GetProcAddress,<ax,0,332> ; DX:AX -> hGlobalHeap + mov segKernel,dx ;Save for later + mov es,dx ;Point with ES + add ax,4 + mov npwExeHead,ax + add ax,10 + mov npwTDBHead,ax + add ax,2 + mov npwTDBCur,ax + add ax,6 + mov npwSelTableLen,ax + add ax,2 + mov npdwSelTableStart,ax + jmp SHORT KT_End ;Skip the 3.0 std mode check +KT_Win30: + or wTHFlags,TH_WIN30 ;Flag we're in Win30 + mov npwSelTableLen,0324h ;Correct values for 3.0 + mov npdwSelTableStart,0326h + mov ax,__WinFlags ;Get the WinFlags variable + mov segKernel,bx ;Save for later + mov npwExeHead,0014h ;Save the offsets of these vars + mov npwTDBHead,001eh + mov npwTDBCur,0020h + test ax,WF_STANDARD ;3.0 Standard mode? + jz KT_End ;No. + or wTHFlags,TH_WIN30STDMODE ;Yes +.8086 +KT_End: + +cEnd + +sEnd + END diff --git a/private/mvdm/wow16/toolhelp/local.c b/private/mvdm/wow16/toolhelp/local.c new file mode 100644 index 000000000..34285cee1 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/local.c @@ -0,0 +1,138 @@ +/************************************************************************** + * LOCAL.C + * + * Routines used to walk local heaps + * + **************************************************************************/ + +#include "toolpriv.h" + +/* ----- Function prototypes ----- */ + + NOEXPORT void NEAR PASCAL ComputeType( + LOCALENTRY FAR *lpLocal); + +/* LocalInfo + * Reports information about the state of the indicated heap + */ + +BOOL TOOLHELPAPI LocalInfo( + LOCALINFO FAR *lpLocalInfo, + HANDLE hHeap) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpLocalInfo || + lpLocalInfo->dwSize != sizeof (LOCALINFO)) + return FALSE; + + /* Get the item counts */ + if (wTHFlags & TH_KERNEL_386) + lpLocalInfo->wcItems = WalkLoc386Count(hHeap); + else + lpLocalInfo->wcItems = WalkLoc286Count(hHeap); + + return TRUE; +} + +/* LocalFirst + * Finds the first block on a local heap. + */ + +BOOL TOOLHELPAPI LocalFirst( + LOCALENTRY FAR *lpLocal, + HANDLE hHeap) +{ + WORD wFirst; + + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpLocal || lpLocal->dwSize != sizeof (LOCALENTRY)) + return FALSE; + + /* Convert the heap variable to a selector */ + hHeap = HelperHandleToSel(hHeap); + + /* Get the first item from the heap */ + if (wTHFlags & TH_KERNEL_386) + { + if (!(wFirst = WalkLoc386First(hHeap))) + return FALSE; + } + else + { + if (!(wFirst = WalkLoc286First(hHeap))) + return FALSE; + } + + + /* Fill in other miscellaneous stuff */ + lpLocal->hHeap = hHeap; + + /* Get information about this item */ + if (wTHFlags & TH_KERNEL_386) + WalkLoc386(wFirst, lpLocal, hHeap); + else + WalkLoc286(wFirst, lpLocal, hHeap); + + /* Guess at the type of the object */ + ComputeType(lpLocal); + + return TRUE; +} + + +/* LocalNext + * Continues a local heap walk by getting information about the + * next item. + */ + +BOOL TOOLHELPAPI LocalNext( + LOCALENTRY FAR *lpLocal) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpLocal || lpLocal->dwSize != sizeof (LOCALENTRY)) + return FALSE; + + if (wTHFlags & TH_KERNEL_386) + WalkLoc386(lpLocal->wNext, lpLocal, lpLocal->hHeap); + else + WalkLoc286(lpLocal->wNext, lpLocal, lpLocal->hHeap); + + /* See if this item is the last one. If so, return done because this + * last item is NOT useful. + */ + if (!lpLocal->wNext) + return FALSE; + + /* Guess at the type of the object */ + ComputeType(lpLocal); + + return TRUE; +} + + +/* ComputeType + * Computes the object type of an object + */ + +NOEXPORT void NEAR PASCAL ComputeType( + LOCALENTRY FAR *lpLocal) +{ + /* Decode the free/fixed/moveable bits */ + if (lpLocal->wFlags & 2) + lpLocal->wFlags = LF_MOVEABLE; + else if (lpLocal->wFlags & 1) + lpLocal->wFlags = LF_FIXED; + else + { + /* Free blocks never have a unique type so return */ + lpLocal->wFlags = LF_FREE; + lpLocal->wType = LT_FREE; + lpLocal->hHandle = NULL; + return; + } + + /* Decode the heap type if possible */ + UserGdiType(lpLocal); +} + + diff --git a/private/mvdm/wow16/toolhelp/makefile b/private/mvdm/wow16/toolhelp/makefile new file mode 100644 index 000000000..7f99d2f88 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/makefile @@ -0,0 +1,168 @@ +# TOOLHELP.DLL for WOW makefile +# +# Copyright (c) 1992, Microsoft Corporation +# +# History: +# 4-Nov-1992 Dave Hart (davehart) +# Created. +# + +# +# Macros for build utilities +# + +#RC16 = .\rc +RC16 = rc16 # use after rc16 is Win 3.1 flavor, and delfile rc* +CL16 = cl16 +MASM16 = masm +LINK16 = link16 +MAPSYM16 = mapsym +IMPLIB16 = implib +MKPUB16 = mkpublic + +# +# Command line options common to C compiler and assembler +# + +DEFINES = -DWOW $(MVDMFLAGS) +INCLUDES = -I..\inc -I..\..\inc -I..\kernel31 + +# +# C, MASM, Link16, and RC16 options +# + +########## Path definition so we find 16 bit tools ########## +# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be +# in a directory that is greater than 128 chars down the path, even if +# rc 3.1 is running as an OS/2 app. + +PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH) + +!if "$(NTDEBUG)"!="" && "$(NTDEBUG)"!="retail" && "$(NTDEBUG)" != "ntsdnodbg" +ADEBUG = -Zd +CDEBUG = /Od /Oi /Zd +LDEBUG = /LI +!endif + +CFLAGS = -c -ASw -G2s -Oas -W3 -Zpe $(DEFINES) $(INCLUDES) $(CDEBUG) +AFLAGS = -DmemS=1 -w2 $(DEFINES) $(INCLUDES) $(ADEBUG) +LFLAGS = /ALIGN:16 $(LDEBUG) +RFLAGS = $(INCLUDES) + +# +# Libraries to link with. +# + +W16LIBS = ..\lib\sdllcew.LIB ..\lib\LIBW.LIB + +# +# Target objects (keep in sync with dependencies below) +# + +OBJS = toolhelp.obj dllentry.obj global.obj krnlpeek.obj \ + walk386.obj local.obj module.obj task1.obj task2.obj stack1.obj \ + stack2.obj usergdi1.obj usergdi2.obj memman.obj helper.obj \ + walk286.obj notify1.obj notify2.obj int1.obj int2.obj terminat.obj \ + signal.obj memory.obj timer.obj ththunks.obj + + +# +# Common build pseudotargets: +# all builds everything - must be be first target in file +# cleanup deletes everything +# clean deletes & then builds everything +# + +all: toolhelp.dll toolhelp.sym toolhelp.map toolhelp.lib + -binplace toolhelp.dll + -binplace toolhelp.map + -binplace toolhelp.sym + +clean: cleanup all + +cleanup: + if exist *.obj del *.obj + if exist *.dll del *.dll + if exist *.map del *.map + if exist *.sym del *.sym + if exist *.res del *.res + if exist *.lib del *.lib + if exist stripped.def del stripped.def + +# +# Default build rules. +# + +.c.obj: + $(CL16) $(CFLAGS) $*.c + +.asm.obj: + $(MASM16) $(AFLAGS) $*.asm; + +.asm.lst: + $(MASM16) $(AFLAGS) -l $*.asm,nul,$*.lst; + +.rc.res: + $(RC16) $(RFLAGS) -r $*.rc + +.def.lib: + $(IMPLIB16) $@ $** + +# +# Dependencies that use default build rules. +# + +# NOTE: Class1.c and Class2.asm are checked in for reference but are not +# ---- built for WOW. Instead THTHUNKS.ASM contains thunks to WOW32. +# +# class1.obj: class1.c toolpriv.h toolhelp.h +# class2.obj: class2.asm toolpriv.inc toolhelp.inc +dllentry.obj: dllentry.asm +global.obj: global.c toolpriv.h toolhelp.h string.h +helper.obj: helper.asm toolpriv.inc toolhelp.inc +int1.obj: int1.c toolpriv.h toolhelp.h string.h +int2.obj: int2.asm toolpriv.inc toolhelp.inc +krnlpeek.obj: krnlpeek.asm toolpriv.inc toolhelp.inc +local.obj: local.c toolpriv.h toolhelp.h +memman.obj: memman.asm toolpriv.inc toolhelp.inc +memory.obj: memory.asm toolpriv.inc toolhelp.inc +module.obj: module.c toolpriv.h toolhelp.h string.h +notify1.obj: notify1.c toolpriv.h toolhelp.h string.h +notify2.obj: notify2.asm toolpriv.inc toolhelp.inc +signal.obj: signal.c toolpriv.h toolhelp.h string.h +stack1.obj: stack1.c toolpriv.h toolhelp.h string.h +stack2.obj: stack2.asm toolpriv.inc toolhelp.inc +task1.obj: task1.c toolpriv.h toolhelp.h string.h +task2.obj: task2.asm toolpriv.inc toolhelp.inc +terminat.obj: terminat.asm toolpriv.inc toolhelp.inc +ththunks.obj: ththunks.asm ..\..\inc\wow.inc ..\..\inc\wowth.inc +timer.obj: timer.asm toolpriv.inc toolhelp.inc +toolhelp.obj: toolhelp.c toolpriv.h toolhelp.h +usergdi1.obj: usergdi1.c toolpriv.h toolhelp.h +usergdi2.obj: usergdi2.asm toolpriv.inc toolhelp.inc +walk286.obj: walk286.asm toolpriv.inc toolhelp.inc +walk386.obj: walk386.asm toolpriv.inc toolhelp.inc + +# +# Targets with specialized build rules +# + +toolhelp.dll: $(OBJS) toolhelp.def + $(LINK16) $(LFLAGS) @<< + toolhelp + dllentry + krnlpeek + global + walk386 + + local + module + task1 + task2 + stack1 + stack2 + + usergdi1 + usergdi2 + memman + helper + walk286 + + notify1 + notify2 + int1 + int2 + terminat + signal + + memory + timer + ththunks + toolhelp.dll + toolhelp.map/map + $(W16LIBS) /NOE/NOD + toolhelp.def; + +<< + $(MAPSYM16) toolhelp.map + $(RC16) $(RFLAGS) -t -30 toolhelp.rcv toolhelp.dll + +toolhelp.lib: toolhelp.def + $(MKPUB16) toolhelp.def stripped.def + $(IMPLIB16) toolhelp.lib stripped.def diff --git a/private/mvdm/wow16/toolhelp/memman.asm b/private/mvdm/wow16/toolhelp/memman.asm new file mode 100644 index 000000000..0c75f04b5 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/memman.asm @@ -0,0 +1,88 @@ +;************************************************************************** +;* MEMMAN.ASM +;* +;* Returns information about the VMM. +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + + +sBegin CODE + assumes CS,CODE + +; MemManInfo +; +; Returns information through DPMI about the VMM + +cProc MemManInfo, <PUBLIC,FAR>, <si,di,ds> + parmD lpMemMan + localV DPMIBuffer,30h ;30h byte buffer for DPMI info +cBegin + mov ax,_DATA ;Get our data segment + mov ds,ax + + ;** Fill the buffer with -1 so if the call messes up like + ;** in 3.0 std mode we get the correct results + push ss ;Point to the local variable block + pop es + lea di,DPMIBuffer ;Get offset of buffer + mov cx,30h ;Max len of DPMI buffer + mov al,0ffh ;-1 + rep stosb ;Fill buffer + + ;** Prepare to build public structure + mov ax,0500h ;DPMI -- Get Free Memory Info + lea di,DPMIBuffer ;Get offset of buffer + int 31h ;Call DPMI + jnc MMI_10 ;Success + xor ax,ax ;Return FALSE + jmp MMI_End ;Get out because of DPMI error +MMI_10: lds si,lpMemMan ;Point to the MEMMANINFO structure + + ;** Fill MEMMANINFO structure + mov ax,es:[di+0] ;Loword of largest free block + mov WORD PTR [si].vmm_dwLargestFreeBlock,ax + mov ax,es:[di+2] ;High word + mov WORD PTR [si].vmm_dwLargestFreeBlock + 2,ax + mov ax,es:[di+4] ;Loword of largest unlockable block + mov WORD PTR [si].vmm_dwMaxPagesAvailable,ax + mov ax,es:[di+6] ;Hiword + mov WORD PTR [si].vmm_dwMaxPagesAvailable + 2,ax + mov ax,es:[di+8] ;Loword of largest lockable page + mov WORD PTR [si].vmm_dwMaxPagesLockable,ax + mov ax,es:[di+0ah] ;Hiword + mov WORD PTR [si].vmm_dwMaxPagesLockable + 2,ax + mov ax,es:[di+0ch] ;Loword of linear address space + mov WORD PTR [si].vmm_dwTotalLinearSpace,ax + mov ax,es:[di+0eh] ;Hiword + mov WORD PTR [si].vmm_dwTotalLinearSpace + 2,ax + mov ax,es:[di+10h] ;Loword of number of unlocked pages + mov WORD PTR [si].vmm_dwTotalUnlockedPages,ax + mov ax,es:[di+12h] ;Hiword + mov WORD PTR [si].vmm_dwTotalUnlockedPages + 2,ax + mov ax,es:[di+14h] ;Loword of number of free pages + mov WORD PTR [si].vmm_dwFreePages,ax + mov ax,es:[di+16h] ;Hiword + mov WORD PTR [si].vmm_dwFreePages + 2,ax + mov ax,es:[di+18h] ;Loword of total physical pages + mov WORD PTR [si].vmm_dwTotalPages,ax + mov ax,es:[di+1ah] ;Hiword + mov WORD PTR [si].vmm_dwTotalPages + 2,ax + mov ax,es:[di+1ch] ;Loword of free lin addr space (pages) + mov WORD PTR [si].vmm_dwFreeLinearSpace,ax + mov ax,es:[di+1eh] ;Hiword + mov WORD PTR [si].vmm_dwFreeLinearSpace + 2,ax + mov ax,es:[di+20h] ;Loword of size of paging file (pages) + mov WORD PTR [si].vmm_dwSwapFilePages,ax + mov ax,es:[di+22h] ;Hiword + mov WORD PTR [si].vmm_dwSwapFilePages + 2,ax + mov [si].vmm_wPageSize,4096 ;Safe to hard code this for 386/486 + mov ax,TRUE ;Return TRUE + +MMI_End: + +cEnd + +sEnd + END diff --git a/private/mvdm/wow16/toolhelp/memory.asm b/private/mvdm/wow16/toolhelp/memory.asm new file mode 100644 index 000000000..1227218f6 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/memory.asm @@ -0,0 +1,608 @@ + PAGE 60,150 +;*************************************************************************** +;* MEMORY.ASM +;* +;* Routines used to handle the read/write random memory API +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE WINDOWS.INC + +;** Symbols +SI_CRITICAL EQU 1 +DI_CRITICAL EQU 2 + +;** Imports +externA __AHINCR +externFP GlobalEntryHandle +externNP HelperHandleToSel + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +; MemoryRead +; Uses the passed in selector and offset to read memory into a user- +; specified buffer. This works for >64K segments and, if code, may +; have been discarded. +; +; This function is normally used for heap selectors. However, if +; a non-global heap selector is used, it must be less than 64K on +; a 286. +; +; Prototype: +; DWORD MemoryRead( +; WORD wSel, /* Selector to read from */ +; DWORD dwOffset, /* Offset to read at */ +; LPSTR lpBuffer, /* Buffer to put data into */ +; DWORD dwcb) /* Number of characters to read */ +; Returns number of characters read (ends at segment limit) + +cProc MemoryRead, <FAR,PUBLIC>, <si,di,ds> + parmW wSelector + parmD dwOffset + parmD lpBuffer + parmD dwcb + localD dwcbCopied + localV Global,<SIZE GLOBALENTRY> +cBegin + ;** Make sure the segment is present. We only will fault the + ;** segment in if it is a code segment + cCall HelperHandleToSel, <wSelector> ;Convert to sel from handle + mov wSelector, ax ;Save it so we have a good sel + mov cx, ax + push WORD PTR lpBuffer[2] ;Convert handle to selector + cCall HelperHandleToSel + mov WORD PTR lpBuffer[2], ax ;Save converted handle + lar ax,cx ;Get the access rights + jnz MR_ShortBad ;Failed. It's bad + test ax,8000h ;Is it present? + jnz MR_Present ;Yes + test ax,0800h ;This bit set for code segments + jnz MR_FaultIn ;Code segment, fault it in +MR_ShortBad: + jmp MR_Bad ;Return error +MR_FaultIn: + mov es,wSelector ;Get the selector in ES. + mov al,es:[0] ;Must be at least one byte long +MR_Present: + + ;** Check this block's length. We use the global heap functions + ;* to do this because they check in the arena for the length. + ;* This is the only way to get lengths of 286 heap blocks + ;** beyond 64K. + mov ax,SIZE GLOBALENTRY ;Get the size of the structure + mov WORD PTR Global.ge_dwSize[0],ax ;Save in the structure + mov WORD PTR Global.ge_dwSize[2],0 ;Clear the HIWORD + lea ax,Global ;Point to the structure + cCall GlobalEntryHandle, <ss,ax,wSelector> + or ax,ax ;Was this a valid selector? + jnz MR_HeapSel ;Yes, this is a heap selector + + ;** If this wasn't a heap selector, we get the length with an LSL. + ;** When used like this, 64K is the max on a 286 +MR_NonHeap: + mov bx,wSelector ;Get the selector + mov ax,__WinFlags ;Get the flags + test ax,WF_CPU286 ;286? + jz MR_32BitSize ;No, do 32 bit size stuff + lsl dx,bx ;Get length in DX + mov WORD PTR Global.ge_dwBlockSize[0],dx ;Put in GLOBALENTRY struct + mov WORD PTR Global.ge_dwBlockSize[2],0 + jmp SHORT MR_HeapSel +MR_32BitSize: +.386p + lsl edx,ebx + mov Global.ge_dwBlockSize,edx ;Put in GLOBALENTRY struct for later +.286p + +MR_HeapSel: + mov dx,WORD PTR dwOffset[2] ;Get the HIWORD of segment offset + cmp dx,WORD PTR Global.ge_dwBlockSize[2] ;Check HIWORD of size + jb MR_OK ;Offset should be OK + je @F ;Equal. Must check LOWORD + jmp MR_Bad ;Offset is not inside segment +@@: mov ax,WORD PTR dwOffset[0] ;Get the LOWORD of segment offset + cmp ax,WORD PTR Global.ge_dwBlockSize[0] ;Check LOWORD of size + jb MR_OK ;It's inside segment + jmp MR_Bad ;Not inside segment +MR_OK: + + ;** Do different stuff on 286 and 386/486 + mov ax,__WinFlags ;Get the flags + test ax,WF_CPU286 ;286? + jnz MR_Do16Bit ;Yes, do 16 bit stuff + + ;** Do this the 386 way (easy) +.386p + mov ax,wSelector ;Point with DS + mov ds,ax ; (keep copy in AX) + mov esi,dwOffset ;Point into the big segment + mov ecx,dwcb ;Get the copy length + lsl edx,eax ;Get the segment limit + sub edx,esi ;Get distance from offset to limit + inc edx ;Make this the real length + cmp ecx,edx ;Are we within the limit? + jbe SHORT MR_LimitOK ;Yes + mov ecx,edx ;No, so make this the copy amount +MR_LimitOK: + mov edx,ecx ;Get the # of bytes to read for ret + xor edi,edi ;Clear the high word + les di,lpBuffer ;Point to the dest. buffer + mov ax,cx ;Save the low bits of ECX + shr ecx,2 ;Prepare for DWORD move + jecxz @F ;No zero-length DWORD moves! + rep movs DWORD PTR [edi],DWORD PTR [esi] + db 67h ;Handles 386 bug + db 90h +@@: mov cx,ax ;Get a copy + jecxz @F ;Don't do zero! + and cx,03 ;Do the remaining 3,2, or 1 + rep movs BYTE PTR [edi], BYTE PTR [esi] + db 67h ;Handles 386 bug + db 90h +@@: mov ax,dx ;Bytes copied returned in DX:AX + shr edx,16 + jmp MR_End ;Get out +.286p + + ;** Do this the 286 way (hard) +MR_Do16Bit: + + ;** Compute the actual copy length + mov ax,WORD PTR Global.ge_dwBlockSize[0] ;Get the segment size + mov dx,WORD PTR Global.ge_dwBlockSize[2] + sub ax,WORD PTR dwOffset[0] ;Get distance from offset to limit + sbb dx,WORD PTR dwOffset[2] + cmp dx,WORD PTR dwcb[2] ;Off end of heap block? + ja MR_LimOk ;No, just do it + jb MR_Truncate ;Yes, must truncate our length + cmp ax,WORD PTR dwcb[0] ;Are we off the end? + jae MR_LimOk ;No, just do it +MR_Truncate: + mov WORD PTR dwcb[0],ax ;Force this to be the new length + mov WORD PTR dwcb[2],dx +MR_LimOk: + + ;** Save the number of bytes to be copied for the return value + mov ax,WORD PTR dwcb[0] ;Get the LOWORD + mov WORD PTR dwcbCopied[0],ax ;Save it + mov ax,WORD PTR dwcb[2] ;Get the HIWORD + mov WORD PTR dwcbCopied[2],ax ;Save it + + ;** Position the initial copying selectors + mov al,BYTE PTR dwOffset[2] ;Grab the HIWORD (286 is only 24 bits) + mov ah,__AHINCR ;Get the selector inc value + mul ah ;Multiply to get sel offset + add ax,wSelector ;AX = sel in sel array + mov ds,ax ;Point to this with DS + mov si,WORD PTR dwOffset[0] ;Get the current pointers + les di,lpBuffer + + ;** This is the main copying loop +MR_CopyLoop: + + ;** Compute the size of this block copy. This is done by finding the + ;* smaller of the following quantities: + ;* - Distance to end of source segment + ;* - Distance to end of dest. segment + ;** - Distance to end of copy + xor bx,bx ;Flags start at zero + xor cx,cx ;Get the highest segment value (64K) + cmp di,si ;The bigger of the two will win + je MR_Equal ;They're the same + ja MR_DIBigger ;DI is bigger + sub cx,si ;SI bigger, compute dist to end + or bx,SI_CRITICAL ;Flag set for SI-critical + jmp SHORT MR_CheckEndCopy ;Go on +MR_Equal: + sub cx,di ;Use DI (SI and DI are the same) + or bx,SI_CRITICAL OR DI_CRITICAL ;Both will come true + jmp SHORT MR_CheckEndCopy ;Go on +MR_DIBigger: + sub cx,di ;SI is bigger + or bx,DI_CRITICAL ;Flag clear for DI-critical +MR_CheckEndCopy: + cmp WORD PTR dwcb[2],0 ;Check for less than 64K left + ja MR_DoCopy ;Nope. More than 64K left + jcxz MR_GetSize ;CX = 0 is full 64K segment + cmp WORD PTR dwcb[0],cx ;Less than in either segment left? + ja MR_DoCopy ;No. Do it +MR_GetSize: + mov cx,WORD PTR dwcb[0] ;Get in CX +MR_DoCopy: + + ;** Do this block of 64K or less. + mov dx,cx ;Save the number of bytes we did + jcxz @F ;Do 64K + shr cx,1 ;Do WORDS + jmp SHORT MR_10 ;Skip over +@@: mov cx,8000h ;32K WORDS +MR_10: jcxz @F ;No zero length WORD moves! + rep movsw ;Do the copy +@@: mov cx,dx ;Get any remaining bits + and cx,1 ;Any more to do? + jcxz @F ;No, don't do it + movsb ;Do the odd byte if necessary +@@: mov cx,dx ;Get back in CX + + ;** Bump the loop pointers + jcxz MR_BigCount ;We did 64K + sub WORD PTR dwcb[0],cx ;Subtract the bytes done + sbb WORD PTR dwcb[2],0 ; and don't forget the HIWORD + jmp SHORT MR_20 ;Continue +MR_BigCount: + sub WORD PTR dwcb[2],1 ;Subtract 64K +MR_20: mov ax,WORD PTR dwcb[0] ;We're done if the count of bytes + or ax,WORD PTR dwcb[2] ; is zero + jnz @F ;Not zero, go on + mov dx,WORD PTR dwcbCopied[2] ;Get the return count + mov ax,WORD PTR dwcbCopied[0] + jmp SHORT MR_End ;Get out +@@: test bx,SI_CRITICAL ;Does SI need incrementing? + jz MR_TestDI ;No, try DI + mov ax,ds ;Get the segment value + add ax,__AHINCR ;Bump to next selector + mov ds,ax ;Point with DS still + xor si,si ;Point to start of this segment +MR_TestDI: + test bx,DI_CRITICAL ;Does SI need incrementing? + jz MR_Continue ;No, try DI + mov ax,es ;Get the segment value + add ax,__AHINCR ;Bump to next selector + mov es,ax ;Point with DS still + xor di,di ;Point to start of this segment +MR_Continue: + jmp MR_CopyLoop ;Do it again + +MR_Bad: + xor ax,ax ;Return DWORD 0 + cwd + +MR_End: + +cEnd + + +; MemoryWrite +; Uses the passed in selector and offset to write memory from a user- +; specified buffer. This works for >64K segments and, if code, may +; have been discarded. The selector may be a selector or a handle +; but MUST be on the global heap (no aliases or selector array +; members). If worried about low memory conditions, lpBuffer should +; be in a (temporarily) fixed segment. +; +; Prototype: +; DWORD MemoryWrite( +; WORD wSel, /* Selector to read from */ +; DWORD dwOffset, /* Offset to read at */ +; LPSTR lpBuffer, /* Buffer to put data into */ +; DWORD dwcb) /* Number of characters to read */ +; Returns number of characters read (ends at segment limit) + +cProc MemoryWrite, <FAR,PUBLIC>, <si,di,ds> + parmW wSelector + parmD dwOffset + parmD lpBuffer + parmD dwcb + localW wSelFlag + localD dwcbCopied + localV DPMISelBuf,8 + localV Global,<SIZE GLOBALENTRY> +cBegin + ;** Make sure the segment is present. We only will fault the + ;** segment in if it is a code segment + cCall HelperHandleToSel, <wSelector> ;Convert to sel from handle + mov wSelector, ax ;Save it + mov cx,ax ;Get the selector + push WORD PTR lpBuffer[2] ;Convert handle to selector + cCall HelperHandleToSel + mov WORD PTR lpBuffer[2], ax ;Save converted handle + mov wSelFlag,0 ;Clear the flag + lar ax,cx ;Get the access rights + jnz MW_ShortBad ;Failed + test ax,8000h ;Is it present? + jnz MW_Present ;Yes + test ax,0800h ;This bit set for code segments + jnz MW_FaultIn ;Code segment, fault it in +MW_ShortBad: + jmp MW_Bad ;Return error +MW_FaultIn: + mov es,wSelector ;Get the selector in ES. + mov al,es:[0] ;Must be at least one byte long +MW_Present: + + ;** Check this block's length. We use the global heap functions + ;* to do this because they check in the arena for the length. + ;* This is the only way to get lengths of 286 heap blocks + ;** beyond 64K. + mov ax,SIZE GLOBALENTRY ;Get the size of the structure + mov WORD PTR Global.ge_dwSize[0],ax ;Save in the structure + mov WORD PTR Global.ge_dwSize[2],0 ;Clear the HIWORD + lea ax,Global ;Point to the structure + cCall GlobalEntryHandle, <ss,ax,wSelector> + or ax,ax ;Was this a valid selector? + jnz MW_HeapSel ;Yes, this is a heap selector + + ;** If this wasn't a heap selector, we get the length with an LSL. + ;** When used like this, 64K is the max on a 286 +MW_NonHeap: + mov bx,wSelector ;Get the selector + mov ax,__WinFlags ;Get the flags + test ax,WF_CPU286 ;286? + jz MW_32BitSize ;No, do 32 bit size stuff + lsl dx,bx ;Get length in DX + mov WORD PTR Global.ge_dwBlockSize[0],dx ;Put in GLOBALENTRY struct + mov WORD PTR Global.ge_dwBlockSize[2],0 + jmp SHORT MW_HeapSel +MW_32BitSize: +.386p + lsl edx,ebx + mov Global.ge_dwBlockSize,edx ;Put in GLOBALENTRY struct for later +.286p + +MW_HeapSel: + mov dx,WORD PTR dwOffset[2] ;Get the HIWORD of segment offset + cmp dx,WORD PTR Global.ge_dwBlockSize[2] ;Check HIWORD of size + jb MW_OK ;Offset should be OK + je @F ;Equal. Must check LOWORD + jmp MW_Bad ;Offset is not inside segment +@@: mov ax,WORD PTR dwOffset[0] ;Get the LOWORD of segment offset + cmp ax,WORD PTR Global.ge_dwBlockSize[0] ;Check LOWORD of size + jb MW_OK ;It's inside segment + jmp MW_Bad ;Not inside segment +MW_OK: + ;** Do different stuff on 286 and 386/486 + mov ax,__WinFlags ;Get the flags + test ax,WF_CPU286 ;286? + jnz MW_Do16Bit ;Yes, do 16 bit stuff + + ;** Do this the 386 way (easy) +.386p + ;** Get an alias selector if necessary + mov ax,wSelector ;Get the source selector + push ss ;Get ES = SS + pop es + lea di,DPMISelBuf ;Point to our descriptor buffer + cCall MakeAlias ;Make the alias selector + jnc SHORT @F ;No error + jmp MW_Bad ;Must be error +@@: mov wSelFlag,bx ;Set the selector flag + mov wSelector,ax ;Save the new selector + + ;** Do the copying + mov ax,wSelector ;Point with DS + mov es,ax ; (keep copy in AX) + mov edi,dwOffset ;Point into the big segment + mov ecx,dwcb ;Get the copy length + lsl edx,eax ;Get the segment limit + sub edx,edi ;Get distance from offset to limit + inc edx ;Make this the real length + cmp ecx,edx ;Are we within the limit? + jbe SHORT MW_LimitOK ;Yes + mov ecx,edx ;No, so make this the copy amount +MW_LimitOK: + xor esi,esi ;Clear the high word + lds si,lpBuffer ;Point to the dest. buffer + mov eax,ecx ;Save ECX + shr ecx,2 ;Prepare for DWORD move + jecxz @F ;No zero-length DWORD moves! + rep movs DWORD PTR [edi],DWORD PTR [esi] + db 67h ;Handles 386 bug + db 90h +@@: mov ecx,eax ;Get a copy + jecxz @F ;Don't do zero! + and ecx,03 ;Do the remaining 3,2, or 1 + rep movs BYTE PTR [edi], BYTE PTR [esi] + db 67h ;Handles 386 bug + db 90h +@@: mov edx,eax ;Bytes copied returned in DX:AX + shr edx,16 + + ;** Free alias if necessary + push ax ;Save return value + push dx + cmp wSelFlag,0 ;Selector flag set? + je SHORT @F ;Nope + mov ax,1 ;DPMI function - Free Selector + mov bx,wSelector ;Selector to free + int 31h ;Call DPMI +@@: pop dx + pop ax + jmp MW_End ;Get out +.286p + + ;** Do this the 286 way (hard) +MW_Do16Bit: + + ;** Compute the actual copy length + mov ax,WORD PTR Global.ge_dwBlockSize[0] ;Get the segment size + mov dx,WORD PTR Global.ge_dwBlockSize[2] + sub ax,WORD PTR dwOffset[0] ;Get distance from offset to limit + sbb dx,WORD PTR dwOffset[2] + cmp dx,WORD PTR dwcb[2] ;Off end of heap block? + ja MW_LimOk ;No, just do it + jb MW_Truncate ;Yes, must truncate our length + cmp ax,WORD PTR dwcb[0] ;Are we off the end? + jae MW_LimOk ;No, just do it +MW_Truncate: + mov WORD PTR dwcb[0],ax ;Force this to be the new length + mov WORD PTR dwcb[2],dx +MW_LimOk: + + ;** Save the number of bytes to be copied for the return value + mov ax,WORD PTR dwcb[0] ;Get the LOWORD + mov WORD PTR dwcbCopied[0],ax ;Save it + mov ax,WORD PTR dwcb[2] ;Get the HIWORD + mov WORD PTR dwcbCopied[2],ax ;Save it + + ;** Position the initial copying selectors + mov al,BYTE PTR dwOffset[2] ;Grab the HIWORD (286 is only 24 bits) + mov ah,__AHINCR ;Get the selector inc value + mul ah ;Multiply to get sel offset + add ax,wSelector ;AX = sel in sel array + mov es,ax ;Point to this with DS + mov di,WORD PTR dwOffset[0] ;Get the current pointers + lds si,lpBuffer + + ;** This is the main copying loop +MW_CopyLoop: + + ;** Get an alias selector if necessary + push si ;Save regs + push di + mov ax,es ;Get the source selector + push ss ;Get ES = SS + pop es + lea di,DPMISelBuf ;Point to our descriptor buffer + cCall MakeAlias ;Make the alias selector + pop di ;Restore regs + pop si + jnc @F ;No error + jmp MW_Bad ;Must be error +@@: mov wSelFlag,bx ;Set the selector flag + mov es,ax ;Save the new selector + + ;** Compute the size of this block copy. This is done by finding the + ;* smaller of the following quantities: + ;* - Distance to end of source segment + ;* - Distance to end of dest. segment + ;** - Distance to end of copy + xor bx,bx ;Flags start at zero + xor cx,cx ;Get the highest segment value (64K) + cmp di,si ;The bigger of the two will win + je MW_Equal ;They're the same + ja MW_DIBigger ;DI is bigger + sub cx,si ;SI bigger, compute dist to end + or bx,SI_CRITICAL ;Flag set for SI-critical + jmp SHORT MW_CheckEndCopy ;Go on +MW_Equal: + sub cx,di ;Use DI (SI and DI are the same) + or bx,SI_CRITICAL OR DI_CRITICAL ;Both will come true + jmp SHORT MW_CheckEndCopy ;Go on +MW_DIBigger: + sub cx,di ;SI is bigger + or bx,DI_CRITICAL ;Flag clear for DI-critical +MW_CheckEndCopy: + cmp WORD PTR dwcb[2],0 ;Check for less than 64K left + ja MW_DoCopy ;Nope. More than 64K left + jcxz MW_GetSize ;CX = 0 is full 64K segment + cmp WORD PTR dwcb[0],cx ;Less than in either segment left? + ja MW_DoCopy ;No. Do it +MW_GetSize: + mov cx,WORD PTR dwcb[0] ;Get in CX +MW_DoCopy: + + ;** Do this block of 64K or less. + mov dx,cx ;Save the number of bytes we did + jcxz @F ;Do 64K + shr cx,1 ;Do WORDS + jmp SHORT MW_10 ;Skip over +@@: mov cx,8000h ;32K WORDS +MW_10: jcxz @F ;No zero-length WORD moves + rep movsw ;Do the copy +@@: mov cx,dx ;Get any remaining bits + and cx,1 ;Any more to do? + jcxz @F ;No, don't do it + movsb ;Do the odd byte if necessary +@@: mov cx,dx ;Get back in CX + + ;** Bump the loop pointers + jcxz MW_BigCount ;We did 64K + sub WORD PTR dwcb[0],cx ;Subtract the bytes done + sbb WORD PTR dwcb[2],0 ; and don't forget the HIWORD + jmp SHORT MW_20 ;Continue +MW_BigCount: + sub WORD PTR dwcb[2],1 ;Subtract 64K +MW_20: mov ax,WORD PTR dwcb[0] ;We're done if the count of bytes + or ax,WORD PTR dwcb[2] ; is zero + jnz @F ;Not zero, go on + mov dx,WORD PTR dwcbCopied[2] ;Get the return count + mov ax,WORD PTR dwcbCopied[0] + jmp SHORT MW_End ;Get out +@@: test bx,SI_CRITICAL ;Does SI need incrementing? + jz MW_TestDI ;No, try DI + mov ax,ds ;Get the segment value + add ax,__AHINCR ;Bump to next selector + mov ds,ax ;Point with DS still + xor si,si ;Point to start of this segment +MW_TestDI: + test bx,DI_CRITICAL ;Does SI need incrementing? + jz MW_Continue ;No, try DI + mov ax,es ;Get the segment value + add ax,__AHINCR ;Bump to next selector + mov es,ax ;Point with DS still + xor di,di ;Point to start of this segment +MW_Continue: + + ;** Free alias if necessary + cmp wSelFlag,0 ;Selector flag set? + je @F ;Nope + mov ax,1 ;DPMI function - Free Selector + mov bx,wSelector ;Selector to free + int 31h ;Call DPMI +@@: jmp MW_CopyLoop ;Do it again + +MW_Bad: + xor ax,ax ;Return DWORD 0 + cwd + +MW_End: + +cEnd + + +;** Helper functions + +; MakeAlias +; Makes an alias selector for the selector in AX. The new selector +; is returned in AX. Carry is set on exit if error. +; Returns nonzero in BX if an alias was made, zero if not +; ES:DI points to an 8-byte descriptor buffer + +cProc MakeAlias, <NEAR, PUBLIC>, <si,di> +cBegin + + ;** If this is not a read/write selector, we must create an alias. + ;* In order to be able to free up the selector, we set a flag + ;** so we know to free it. + xor si,si ;No alias made, just in case + lar cx,ax ;Get its access rights + jnz MA_Bad ;Failed + test cx,800h ;Is this a code segment? + jnz MA_MakeAlias ;Yes. Always make an alias + test cx,200h ;Is it read/write? + jnz MA_End ;Yes, no need for alias +MA_MakeAlias: + mov bx,ax ;Get the selector + mov ax,0bh ;DPMI function - Get Descriptor + ;ES:DI already point to buffer + int 31h ;Call DPMI + jc MA_Bad ;Error + xor ax,ax ;DPMI Function - Alloc selector + mov cx,1 ;Alloc 1 selector + int 31h ;Call DPMI + jc MA_Bad ;Error + mov si,1 ;Set flag to say alias made + and BYTE PTR DPMISelBuf[5],0f0h ;Mask out unwanted bits + or BYTE PTR DPMISelBuf[5],2 ;Make it a R/W Data segment + mov bx,ax ;Selector in BX + mov ax,0ch ;DPMI function - Set Descriptor + int 31h ;Call DPMI + jc MA_Bad ;Error + mov ax,bx ;Get the new selector in AX + jmp SHORT MA_End ;Get out + +MA_Bad: + stc ;Error + +MA_End: + mov bx,si ;Get flag in BX +cEnd + + +sEnd + + END + diff --git a/private/mvdm/wow16/toolhelp/module.c b/private/mvdm/wow16/toolhelp/module.c new file mode 100644 index 000000000..72270feba --- /dev/null +++ b/private/mvdm/wow16/toolhelp/module.c @@ -0,0 +1,156 @@ +/************************************************************************* + * MODULE.C + * + * Routines to enumerate the various module headers on the module + * chain. + * + *************************************************************************/ + +#include "toolpriv.h" +#include <newexe.h> +#include <string.h> + +/* ----- Function prototypes ----- */ + + NOEXPORT BOOL PASCAL ModuleGetInfo( + WORD wModule, + MODULEENTRY FAR *lpModule); + +/* ModuleFirst + * Finds the first module in the module list and returns information + * about this module. + */ + +BOOL TOOLHELPAPI ModuleFirst( + MODULEENTRY FAR *lpModule) +{ + WORD FAR *lpwExeHead; + + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpModule || + lpModule->dwSize != sizeof (MODULEENTRY)) + return FALSE; + + /* Get a pointer to the module head */ + lpwExeHead = MAKEFARPTR(segKernel, npwExeHead); + + /* Use this pointer to get information about this module */ + return ModuleGetInfo(*lpwExeHead, lpModule); +} + + +/* ModuleNext + * Finds the next module in the module list. + */ + +BOOL TOOLHELPAPI ModuleNext( + MODULEENTRY FAR *lpModule) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpModule || + lpModule->dwSize != sizeof (MODULEENTRY)) + return FALSE; + + /* Use the next handle to get information about this module */ + return ModuleGetInfo(lpModule->wNext, lpModule); +} + + +/* ModuleFindName + * Finds a module with the given module name and returns information + * about it. + */ + +HANDLE TOOLHELPAPI ModuleFindName( + MODULEENTRY FAR *lpModule, + LPCSTR lpstrName) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpModule || !lpstrName || + lpModule->dwSize != sizeof (MODULEENTRY)) + return NULL; + + /* Loop through module chain until we find the name (or maybe we don't) */ + if (ModuleFirst(lpModule)) + do + { + /* Is this the name? If so, we have the info, so return */ + if (!lstrcmp(lpstrName, lpModule->szModule)) + return lpModule->hModule; + } + while (ModuleNext(lpModule)); + + /* If we get here, we didn't find it or there was an error */ + return NULL; +} + + +/* ModuleFindHandle + * Returns information about a module with the given handle. + */ + +HANDLE TOOLHELPAPI ModuleFindHandle( + MODULEENTRY FAR *lpModule, + HANDLE hModule) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpModule || !hModule || + lpModule->dwSize != sizeof (MODULEENTRY)) + return NULL; + + /* Use the helper function to find out about this module */ + if (!ModuleGetInfo(hModule, lpModule)) + return NULL; + + return lpModule->hModule; +} + + +/* ----- Helper functions ----- */ + +NOEXPORT BOOL PASCAL ModuleGetInfo( + WORD wModule, + MODULEENTRY FAR *lpModule) +{ + struct new_exe FAR *lpNewExe; + BYTE FAR *lpb; + + /* Verify the segment so we don't GP fault */ + if (!HelperVerifySeg(wModule, 2)) + return FALSE; + + /* Get a pointer to the module database */ + lpNewExe = MAKEFARPTR(wModule, 0); + + /* Make sure this is a module database */ + if (lpNewExe->ne_magic != NEMAGIC) + return FALSE; + + /* Get the module name (it's the first name in the resident names + * table + */ + lpb = ((BYTE FAR *)lpNewExe) + lpNewExe->ne_restab; + _fstrncpy(lpModule->szModule, lpb + 1, *lpb); + lpModule->szModule[*lpb] = '\0'; + + /* Get the EXE file path. A pointer is stored in the same place as + * the high word of the CRC was in the EXE file. (6th word in new_exe) + * This pointer points to the length of a PASCAL string whose first + * eight characters are meaningless to us. + */ + lpb = MAKEFARPTR(wModule, *(((WORD FAR *)lpNewExe) + 5)); + _fstrncpy(lpModule->szExePath, lpb + 8, *lpb - 8); + lpModule->szExePath[*lpb - 8] = '\0'; + + /* Get other information from the EXE Header + * The usage count is stored in the second word of the EXE header + * The handle of the next module in the chain is stored in the + * ne_cbenttab structure member. + */ + lpModule->hModule = wModule; + lpModule->wcUsage = *(((WORD FAR *)lpNewExe) + 1); + lpModule->wNext = lpNewExe->ne_cbenttab; + + return TRUE; +} + diff --git a/private/mvdm/wow16/toolhelp/notify1.c b/private/mvdm/wow16/toolhelp/notify1.c new file mode 100644 index 000000000..bf101ad4d --- /dev/null +++ b/private/mvdm/wow16/toolhelp/notify1.c @@ -0,0 +1,165 @@ +/************************************************************************** + * NOTIFY1.C + * + * Routines used to implement the Debugger Notification API in + * TOOLHELP.DLL + * + **************************************************************************/ + +#include <string.h> +#include "toolpriv.h" + +/* ----- Global variables ----- */ + WORD wNotifyInstalled; + NOTIFYSTRUCT NEAR *npNotifyHead; + NOTIFYSTRUCT NEAR *npNotifyNext; + +/* NotifyRegister + * Registers a debugger notification callback. This callback will + * be called whenever KERNEL has a notification to be sent. + * The format of the call to the callback function is documented + * elsewhere. + */ + +BOOL TOOLHELPAPI NotifyRegister( + HANDLE hTask, + LPFNNOTIFYCALLBACK lpfn, + WORD wFlags) +{ + NOTIFYSTRUCT *pInfo; + NOTIFYSTRUCT *pTemp; + + /* Make sure TOOLHELP.DLL is installed */ + if (!wLibInstalled) + return FALSE; + + /* If the notification hook has not yet been installed, install it */ + if (!wNotifyInstalled) + { + /* Make sure we can get a hook! */ + if (!NotifyInit()) + return FALSE; + wNotifyInstalled = TRUE; + } + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* Register a death signal handler for this task (does nothing if one + * is already installed. + */ + SignalRegister(hTask); + + /* Check to see if this task is already registered */ + for (pInfo = npNotifyHead ; pInfo ; pInfo = pInfo->pNext) + if (pInfo->hTask == hTask) + return FALSE; + + /* Allocate a new NOTIFYSTRUCT structure */ + pInfo = (NOTIFYSTRUCT *)LocalAlloc(LMEM_FIXED, sizeof (NOTIFYSTRUCT)); + if (!pInfo) + return FALSE; + + /* Fill in the useful fields */ + pInfo->hTask = hTask; + pInfo->wFlags = wFlags; + pInfo->lpfn = lpfn; + + /* If this is the only handler, just insert it */ + if (!npNotifyHead) + { + pInfo->pNext = npNotifyHead; + npNotifyHead = pInfo; + } + + /* Otherwise, insert at the end of the list */ + else + { + for (pTemp = npNotifyHead ; pTemp->pNext ; pTemp = pTemp->pNext) + ; + pInfo->pNext = pTemp->pNext; + pTemp->pNext = pInfo; + } + + return TRUE; +} + + +/* NotifyUnRegister + * Called by an app whose callback is no longer to be used. + * NULL hTask uses current task. + */ + +BOOL TOOLHELPAPI NotifyUnRegister( + HANDLE hTask) +{ + NOTIFYSTRUCT *pNotify; + NOTIFYSTRUCT *pBefore; + + /* Make sure we have notifications installed and that TOOLHELP is OK */ + if (!wLibInstalled || !wNotifyInstalled) + return FALSE; + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* First try to find the task */ + pBefore = NULL; + for (pNotify = npNotifyHead ; pNotify ; pNotify = pNotify->pNext) + if (pNotify->hTask == hTask) + break; + else + pBefore = pNotify; + if (!pNotify) + return FALSE; + + /* Unhook the death signal proc only if there is no interrupt handler */ + if (!InterruptIsHooked(hTask)) + SignalUnRegister(hTask); + + /* Check to see if the notification handler is about to use this entry. + * If it is, we point it to the next one, if any. + */ + if (npNotifyNext == pNotify) + npNotifyNext = pNotify->pNext; + + /* Remove it from the list */ + if (!pBefore) + npNotifyHead = pNotify->pNext; + else + pBefore->pNext = pNotify->pNext; + + /* Free the structure */ + LocalFree((HANDLE)pNotify); + + /* If there are no more handlers, unhook the callback */ + if (!npNotifyHead) + { + NotifyUnInit(); + wNotifyInstalled = FALSE; + } + + return TRUE; +} + +/* ----- Helper functions ----- */ + +/* NotifyIsHooked + * Returns TRUE iff the parameter task already has a notification hook. + */ + +BOOL PASCAL NotifyIsHooked( + HANDLE hTask) +{ + NOTIFYSTRUCT *pNotify; + + /* Loop thorugh all notifications */ + for (pNotify = npNotifyHead ; pNotify ; pNotify = pNotify->pNext) + if (pNotify->hTask == hTask) + break; + + /* Return found/not found */ + return (BOOL)pNotify; +} diff --git a/private/mvdm/wow16/toolhelp/notify2.asm b/private/mvdm/wow16/toolhelp/notify2.asm new file mode 100644 index 000000000..974c192b1 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/notify2.asm @@ -0,0 +1,643 @@ + PAGE 60,150 +;*************************************************************************** +;* NOTIFY2.ASM +;* +;* Assembly code support routines used for the TOOLHELP.DLL +;* notification API +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC +.286p + +;** Data +sBegin DATA + +globalW wCASRqFlag,0 ;Set when an CASRq INT3 has been set +globalD dwCASRqCSIP,0 ;Holds the CS:IP of the CASRq INT3 +globalD lpfnOldProc,0 ;Old hook from new PTrace hook +szWinDebug DB 'WINDEBUG', 0 + +;** WARNING!! +;** This structure is set to the size of the largest notification +;** structure. This is currently NFYLOADSEG which is 16 bytes long. +;** If a structure is added that is longer than this or if any other +;** structure is added, this space must be increased to match!! +ReturnStruct DB 16 DUP (?) + +sEnd + +;** Imports +externFP GetModuleHandle +externFP RegisterPTrace +externFP OutputDebugString +externFP AllocCStoDSAlias +externFP FreeSelector +externNP HelperHandleToSel + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +; NotifyInit +; Called when the first app registers a notification handler. +; Hooks the Register PTrace notification. +; Returns FALSE if we couldn't initialize, TRUE otherwise + +cProc NotifyInit, <NEAR,PUBLIC>, <si,di,ds> +cBegin + ;** In the Windows 3.1 KERNEL, there is a special hook just for + ;* TOOLHELP that lets us get PTrace stuff and still coexist + ;* with old-fashioned debuggers. We can check to see if the + ;* hook exists by simply checking the TOOLHELP flags + ;** + test wTHFlags,TH_GOODPTRACEHOOK ;Good hook around? + jz DNI_UseRegPTrace ;Nope, use the old one + lea si,NotifyHandler ;Point to the routine + push cs ;Parameter is lpfn to callback + push si + call lpfnNotifyHook ;Hook it + mov WORD PTR lpfnOldProc[0],ax ;Save old proc + mov WORD PTR lpfnOldProc[2],dx + jmp SHORT DNI_10 ;We're in + + ;** Since there's no way we can see if someone else has Register + ;* PTrace, we just connect and hope for the best! + ;** We do check, however, to see if WINDEBUG.DLL is installed. +DNI_UseRegPTrace: + lea si,szWinDebug ;Get the name of the module + cCall GetModuleHandle, <ds,si> ;Is WINDEBUG present? + or ax,ax ;Check the handle + jnz DNI_Fail ;It's here so fail + or wTHFlags,TH_GOTOLDPTRACE ;Flag that we made the hook + lea si,NotifyHandler ;Point to our routine + cCall RegisterPTrace, <cs,si> ;Tell KERNEL to use it + + ;** Connect to the FatalExit hook. We currently ignore + ;** the return value, thus unhooking anyone else +DNI_10: cmp WORD PTR lpfnFatalExitHook + 2,0 ;Can we hook it? + jz DNI_20 ;Can't do it + push cs ;Get the CS:IP of RIP handler + push OFFSET NotifyRIPHandler + call DWORD PTR lpfnFatalExitHook ;Tell KERNEL to insert the hook +DNI_20: + + ;** Return OK + mov ax,TRUE ;Return TRUE + jmp SHORT DNI_End ;Get out + +DNI_Fail: + xor ax,ax ;FALSE + +DNI_End: +cEnd + + +; NotifyUnInit +; Called when the no more apps have hooked notification handlers +; so the hook to the Register PTrace notification is no longer needed. + +cProc NotifyUnInit, <NEAR,PUBLIC>, <si,di,ds> +cBegin + ;** Did we have a new hook to undo? + test wTHFlags,TH_GOODPTRACEHOOK ;Do we have a new hook? + jz DNU_TryOldPTrace ;No + push WORD PTR lpfnOldProc[0] ;Get the old proc + push WORD PTR lpfnOldProc[2] + call lpfnNotifyHook ;Unhook ourself + jmp SHORT DNU_NoOldPTrace + + ;** Unhook the old-style hook if necessary +DNU_TryOldPTrace: + test wTHFlags,TH_GOTOLDPTRACE ;Did we have a hook? + jz DNU_NoOldPTrace ;No + push 0 + push 0 + call RegisterPTrace ;Call KERNEL's routine to unhook +DNU_NoOldPTrace: + + ;** Unhook alternate hooks + cmp WORD PTR lpfnFatalExitHook + 2,0 ;Can we unhook it? + jz DNU_NoRIP ;Can't do it + xor ax,ax ;Remove any other hooks + push ax ;NULL procedure + push ax + call DWORD PTR lpfnFatalExitHook +DNU_NoRIP: + +cEnd + + +; NotifyHandler +; This routine is called directly by PTrace and is used to +; dispatch the notifications to all registered callbacks. + +cProc NotifyHandler, <FAR,PUBLIC> +cBegin NOGEN + + ;** Push a register frame + ;* When done, it should look like this: + ;* ------------ + ;* | ES | [BP - 14h] + ;* | DS | [BP - 12h] + ;* | DI | [BP - 10h] + ;* | SI | [BP - 0Eh] + ;* | BP | [BP - 0Ch] + ;* | SP | [BP - 0Ah] + ;* | BX | [BP - 08h] + ;* | DX | [BP - 06h] + ;* | CX | [BP - 04h] + ;* | AX | [BP - 02h] + ;* BP-->| Old BP | [BP - 00h] + ;* | IP | [BP + 02h] + ;* | CS | [BP + 04h] + ;* ------------ + ;** + push bp ;Make a stack frame + mov bp,sp + pusha ;Save all registers + push ds ;Save segment registers, too + push es + + ;** Get the data segment + mov bx,_DATA ;Get TOOLHELP data segment + mov ds,bx + + ;** If in 3.0 std mode and we get this wild notification 69h, + ;** translate it to a inchar notification as this is what it + ;** is supposed to be. + cmp ax,69h ;Bogus notification? + jne NH_NoBogusNotify ;No, don't do this + test wTHFlags,TH_WIN30STDMODE ;3.0 standard mode? + jz NH_NoBogusNotify ;No, might be valid in the future... + mov ax,NI_INCHAR ;Put in real notify value +NH_NoBogusNotify: + + ;** Special case notifications: + ;* Notification 63h means that CtlAltSysRq was pressed. For + ;* this, we want to handle as an interrupt, not a notification. + ;* To do this, we set a breakpoint and set a flag so that the + ;** INT3 handler knows what to do with it + cmp ax,63h ;CtlAltSysRq? + jne NH_NoCASRq ;No. + mov ax,[bp + 04h] ;Since we can't use IRET CS:IP, get + mov si,[bp + 02h] ; a safe address in KERNEL + mov WORD PTR dwCASRqCSIP[2],ax ;Save the CS:IP value + cCall AllocCStoDSAlias, <ax> ;Get a data alias to the CS + or ax,ax ;Error? + jnz @F + jmp SHORT DNH_End ;Yes, get out +@@: verw ax ;OK to write to? + jnz DNH_NoWrite ;Yes, so do it +DNH_IRETCSOK: + mov es,ax ;Point with ES + mov WORD PTR dwCASRqCSIP[0],si + mov al,es:[si] ;Get the character there + mov ah,1 ;Make sure there's something in AH + mov wCASRqFlag,ax ;Save the thing for the INT3 handler + mov BYTE PTR es:[si],0cch ;Poke the INT3 in there + mov ax,es ;Get the selector back +DNH_NoWrite: + cCall FreeSelector, <ax> ;Get rid of the alias + jmp SHORT DNH_End ;Get out. This will INT3 soon + +NH_NoCASRq: ; Does not return + + ;** Notifications to ignore here: + ;** Notification 60h is bogus and should be ignored + cmp ax,60h ;PostLoad notification? + jz DNH_End ;Yes, don't report + + ;** Decode the notification + cCall DecodeNotification ;Returns dwData in CX:DX, AX is wID + ; BX is NOTIFYSTRUCT match flags + ;** This is an entry point for notifications from sources other than + ;** PTrace +DNH_Decoded: + + ;** Loop through callbacks + mov di,npNotifyHead ;Point to the start of the list + xor si,si ;FALSE return value is default +DNH_Loop: + push ax + mov ax,ds:[di].ns_pNext ;Save the next pointer in a global + mov npNotifyNext,ax ; so we can chain in NotifyUnregister + pop ax + + or di,di ;End of list? + jz DNH_Done ;Yep. Get out + + ;** If the flags for this notification are zero, we always send it + or bx,bx ;Check the matching flags + jz DNH_DoIt ;Do notification + + ;** See if the flags match + test bx,ds:[di].ns_wFlags ;Check against the NOTIFYSTRUCT flags + jz DNH_Continue ;If zero, no match, don't do it + + ;** Call the user callback +DNH_DoIt: + push ax ;Save everything we need + push bx + push cx + push dx + + push ax ;wID + push cx ;High word of dwData + push dx ;Low word + call DWORD PTR ds:[di].ns_lpfn ;Call the callback (PASCAL style) + mov si,ax ;Get return value in SI + + pop dx ;Restore everything + pop cx + pop bx + pop ax + + ;** If the return value is nonzero, we don't want to give this to + ;** any more callbacks + or si,si ;TRUE return value? + jnz DNH_Done ;Yes, get out + + ;** Get the next callback +DNH_Continue: + mov di,npNotifyNext ;Get next pointer + jmp DNH_Loop ; and loop back + + ;** End of callback loop. +DNH_Done: + + ;** If this was an InChar message but everyone ignored it, force + ;** the return to be an 'i' for 'ignore' on RIPs. This i + ;** only necessary in 3.0 because the 3.1 KERNEL treats 0 + ;** returns just like 'i' + cmp ax,NFY_INCHAR ;Is this an InChar notification? + jne DNH_Default ;No, so ignore + test wTHFlags,TH_WIN30 ;In 3.0? + jz DNH_Default ;No, don't do this + and si,0ffh ;Ignore all but low byte + or si,si ;Non-zero? + jnz DNH_Default ;Yes, return it as the character + mov si,'i' ;Instead of zero, return 'i'gnore. +DNH_Default: + mov [bp - 02h],si ;Return the return code in AX + + ;** Clear off the stack and exit +DNH_End: + mov npNotifyNext,0 ;No current next pointer + + pop es ;Restore all registers + pop ds + popa + pop bp + retf ;Just return + +cEnd NOGEN + + +; NotifyRIPHandler +; Gets called by KERNEL when a RIP occurs. If it returns TRUE, +; KERNEL will act like the RIP was ignored. Otherwise, the RIP +; procedes normally. +; This routine does not need to worry about saving non-C regs + +cProc NotifyRIPHandler, <FAR,PUBLIC> +; parmW wExitCode +cBegin nogen + + ;** Clear PASCAL-style parameters + push bp ;Make a stack frame + mov bp,sp + mov bx,[bp + 6] ;Get the BP value + mov dx,[bp + 8] ;Get the Exit code + mov [bp - 2],ax ;Save it out of the way for now + mov ax,[bp + 4] ;Get the RETF CS value + mov [bp + 8],ax ;Shift down to clear parameters + mov ax,[bp + 2] ;Get the RETF IP value + mov [bp + 6],ax ;Shift down to clear parameters + mov ax,[bp + 0] ;Get the old BP value + mov [bp + 4],ax ;Shift down + add bp,4 ;Move BP down on the stack + mov sp,bp ;Point SP there too + pusha ;Save matching register frame + push ds + push es + + ;** Get the data segment + mov ax,_DATA ;Get TOOLHELP data segment + mov ds,ax + + + ;** Prepare to jump into the notification handler. + ;** The trick here is that if a notification callback returns + ;** non-zero, the RIP has been handled. Otherwise, it has not. + ;** DX holds the exit code here, BX has the old BP value + lea si,ReturnStruct ;Get a pointer to the return struct + mov WORD PTR [si].nrp_dwSize[0],SIZE NFYRIP + mov WORD PTR [si].nrp_dwSize[2],0 + mov ax,ss:[bx + 4] ;Get old CS value from stack + mov [si].nrp_wCS,ax ; (BX is BP from FatalExit stack) + mov ax,ss:[bx + 2] ;Get old IP value + mov [si].nrp_wIP,ax + mov [si].nrp_wSS,ss ;Save SS:BP for stack trace + mov [si].nrp_wBP,bx + mov [si].nrp_wExitCode,dx + mov cx,ds ;Point to structure + mov dx,si + mov bx,NF_RIP ;Get the NOTIFYINFO match flags + mov ax,NFY_RIP ;TOOLHELP ID + + ;** Jump to the real handler + jmp DNH_Decoded ;Jump to alternate entry point + +cEnd nogen + +;** Helper routines + +; DecodeNotification +; Decodes a notification by pointing to a static structure and filling +; this structure with notification-specific information. +; The PTrace notification ID is in AX. +; Returns the ToolHelp ID in AX +; and the dwData value is in CX:DX. + +cProc DecodeNotification, <NEAR,PUBLIC> +cBegin + ;** Point dwData to the structure just in case + mov cx,ds ;Get the segment value + lea dx,ReturnStruct ;Get a pointer to the return struct + xor bx,bx ;Most notifications always match + + ;** The stack frame looks like this: + ;* ------------ + ;* | ES | [BP - 14h] + ;* | DS | [BP - 12h] + ;* | DI | [BP - 10h] + ;* | SI | [BP - 0Eh] + ;* | BP | [BP - 0Ch] + ;* | SP | [BP - 0Ah] + ;* | BX | [BP - 08h] + ;* | DX | [BP - 06h] + ;* | CX | [BP - 04h] + ;* | AX | [BP - 02h] + ;* BP-->| Old BP | [BP - 00h] + ;* ------------ + ;** +FrameES EQU [BP - 14h] +FrameDS EQU [BP - 12h] +FrameDI EQU [BP - 10h] +FrameSI EQU [BP - 0Eh] +FrameBP EQU [BP - 0Ch] +FrameSP EQU [BP - 0Ah] +FrameBX EQU [BP - 08h] +FrameDX EQU [BP - 06h] +FrameCX EQU [BP - 04h] +FrameAX EQU [BP - 02h] + + ;** Check for LoadSeg + cmp ax,NI_LOADSEG ;LoadSeg? + jnz DN_10 ;No + + ;** LoadSeg: + ;* CX is selector + ;* BX is segment number + ;* SI is type: Low bit set for data segment, clear for code + ;* DX is instance count only for data segments + ;** ES:DI module name + mov si,dx ;Point to NFYLOADSEG struct + mov ax,SIZE NFYLOADSEG ;Get the structure size + mov WORD PTR [si].nls_dwSize,ax ;Save the LOWORD of the size + mov WORD PTR [si].nls_dwSize + 2,0 ;HIWORD is zero + mov ax,FrameCX ;Get selector + mov [si].nls_wSelector,ax ;Save in structure + mov ax,FrameBX ;Get segment number + inc ax ;Segment number is 1-based + mov [si].nls_wSegNum,ax ;Save in structure + mov ax,FrameSI ;Get the segment type + mov [si].nls_wType,ax ;Put in structure + mov ax,FrameDX ;Get instance count + mov [si].nls_wcInstance,ax ;Put in structure + mov ax,FrameDI ;Get offset of module name str + mov WORD PTR [si].nls_lpstrModuleName,ax ;Save it + mov ax,FrameES ;Get segment of module name str + mov WORD PTR [si].nls_lpstrModuleName + 2,ax ;Save it + mov ax,NFY_LOADSEG ;Get the TOOLHELP ID + jmp DN_End + + ;** Check for FreeSeg +DN_10: cmp ax,NI_FREESEG ;FreeSeg? + jnz DN_15 ;No + + ;** FreeSeg: + ;** BX is selector + xor cx,cx ;Clear high word + mov dx,FrameBX ;Get the selector + test wTHFlags,TH_WIN30STDMODE ;3.0 standard mode? + jz DN_FS_GotSelValue ;No, what we have is correct + mov si,FrameSP ;Point to old stack frame + mov dx, ss:[si + 6] ;Selector is 6 bytes down + lsl ax, dx + jz DN_FS_CheckLen ;Selector is OK + mov dx, FrameBX ;Revert to BX value + jmp SHORT DN_FS_GotSelValue +DN_FS_CheckLen: + cmp ax, 15 ;If the segment is 15 bytes long, + jne DN_FS_GotSelValue ; this is a bogus selector and is + ; really an arena header. + push es + mov es, dx ;Get handle + cCall HelperHandleToSel, <es:[0ah]> ;Convert to selector + mov dx, ax ;Get handle out of arena header + pop es +DN_FS_GotSelValue: + mov ax,NFY_FREESEG ;Get the TOOLHELP ID + jmp DN_End + + ;** Check for StartDLL +DN_15: cmp ax,NI_LOADDLL + jnz DN_20 + + ;** StartDLL: + ;** CX is CS + ;** BX is IP + ;** SI is Module handle + mov si,dx ;Point with SI + mov ax,SIZE NFYSTARTDLL ;Get the size + mov WORD PTR [si].nsd_dwSize,ax ;Save the LOWORD of the size + mov WORD PTR [si].nsd_dwSize + 2,0 ;HIWORD is always zero + mov ax,FrameSI ;Get the hInstance + mov [si].nsd_hModule,ax ;Save in structure + mov ax,FrameCX ;Get the starting CS + mov [si].nsd_wCS,ax ;Save in structure + mov ax,FrameBX ;Get the starting IP + mov [si].nsd_wIP,ax ;Save in structure + mov ax,NFY_STARTDLL + jmp DN_End + + ;** Check for StartTask +DN_20: cmp ax,NI_STARTTASK ;StartTask? + jnz DN_30 ;No + + ;** StartTask: + ;* CX is CS + ;** BX is IP + mov cx,FrameCX + mov dx,FrameBX + mov ax,NFY_STARTTASK + jmp DN_End + + ;** Check for ExitCall +DN_30: cmp ax,NI_EXITCALL ;ExitCall + jnz DN_40 ;No + + ;** ExitCall: + ;* Exit code is on stack somewhere if we don't have the new + ;** notification handler. If we do, it's in BL. + xor cx,cx ;Clear all but low byte + xor dh,dh + test wTHFlags,TH_GOODPTRACEHOOK ;Do we have the good hook? + jz DN_DoOldHook ;Nope, grope on the stack + mov dl,BYTE PTR FrameBX ;Get the exit code + mov ax,NFY_EXITTASK ;Get the TOOLHELP ID + jmp DN_End +DN_DoOldHook: + mov si,FrameSP ;Point to old stack frame + mov dl,ss:[si + 6] ;Exit code is 6 bytes down on stack + mov ax,NFY_EXITTASK ;Get the TOOLHELP ID + jmp DN_End + + ;** Check for DelModule +DN_40: cmp ax,NI_DELMODULE ;DelModule? + jnz DN_60 ;No + + ;** DelModule: + ;** ES is module handle + xor cx,cx ;Clear HIWORD + mov dx,FrameES ;Get the module handle + mov ax,NFY_DELMODULE ;Get the TOOLHELP ID + jmp DN_End + + ;** Check for TaskSwitchIn +DN_60: cmp ax,NI_TASKIN ;TaskSwitchIn? + jnz DN_70 ;No + + ;** TaskSwitchIn: + ;** No data. Callback should do GetCurrentTask() + xor cx,cx ;Clear data + xor dx,dx + mov ax,NFY_TASKIN ;Get the TOOLHELP ID + mov bx,NF_TASKSWITCH ;Get the NOTIFYSTRUCT match flag + jmp DN_End + + ;** Check for TaskSwitchOut +DN_70: cmp ax,NI_TASKOUT ;TaskSwitchOut? + jnz DN_90 ;No + + ;** TaskSwitchOut: + ;** No data + xor cx,cx ;Clear data + xor dx,dx + mov ax,NFY_TASKOUT ;Get the TOOLHELP ID + mov bx,NF_TASKSWITCH ;Get the NOTIFYSTRUCT match flag + jmp DN_End + + ;** Check for OutStr +DN_90: cmp ax,NI_OUTSTR ;OutStr? + jnz DN_100 ;No + + ;** OutStr: + ;** ES:SI points to string to display in 3.1 + ;** DS:SI in 3.0 + test wTHFlags,TH_WIN30 ;3.0? + jz DN_OS_Win31 ;Nope + mov cx,FrameDS ;Get the segment value + jmp SHORT @F +DN_OS_Win31: + mov cx,FrameES ;Get the segment value +@@: mov dx,FrameSI ; and the offset + mov ax,NFY_OUTSTR ;Get the TOOLHELP ID + jmp DN_End + + ;** Check for InChar +DN_100: cmp ax,NI_INCHAR ;InChar? + jnz DN_105 ;No + + ;** InChar: + ;** No data passed (it wants data back in AL) + xor cx,cx ;Clear dwData + xor dx,dx + mov ax,NFY_INCHAR ;Get the TOOLHELP ID + jmp SHORT DN_End + + ;** NOTE: The following notifications are defined as "NEW" and + ;** are NOT sent through the normal PTrace interface so as to + ;** not break brain-damaged CodeSpew. It stack faults when + ;** it is sent a notification it doesn't understand. So, + ;** here we don't bother decoding any of these unless we have + ;** the new (Win 3.1) style hook +DN_105: test wTHFlags,TH_GOODPTRACEHOOK ;Do we have the advanced hook? + jnz DN_110 ;Yes + jmp SHORT DN_End + + ;** Check for the parameter validation notifications +DN_110: cmp ax,NI_LOGERROR ;SDM_LOGERROR? + jne DN_120 ;No + + ;** SDM_LOGERROR: + ;* CX is Error code + ;** DX:BX is lpInfo + mov si,dx ;Point with SI + mov ax,SIZE NFYLOGERROR ;Get the size + mov WORD PTR [si].nle_dwSize[0],ax ;Save the LOWORD of the size + mov WORD PTR [si].nle_dwSize[2],0 ;HIWORD is always zero + mov ax,FrameCX ;Get the error code + mov [si].nle_wErrCode,ax ;Save in structure + mov ax,FrameDX ;Get the lpInfo + mov WORD PTR [si].nle_lpInfo[2],ax ;Save in structure + mov ax,FrameBX + mov WORD PTR [si].nle_lpInfo[0],ax ;Save in structure + mov ax,NFY_LOGERROR ;Get the TOOLHELP ID + jmp SHORT DN_End + +DN_120: cmp ax,NI_LOGPARAMERROR ;SDM_LOGPARAMERROR? + jne DN_Unknown ;No + + ;** SDM_LOGPARAMERROR: + ;** ES:BX points to a structure: + ;** WORD wErr + ;** FARPROC lpfn + ;** VOID FAR* lpBadParam + mov si,dx ;Point with SI + mov ax,SIZE NFYLOGPARAMERROR ;Struct size + mov WORD PTR [si].nlp_dwSize[0],ax ;Save the LOWORD of the size + mov WORD PTR [si].nlp_dwSize[2],0 ;HIWORD is always zero + mov es,FrameES ;Point to the structure + mov bx,FrameBX + mov ax,es:[bx] ;Get wErr + mov [si].nlp_wErrCode,ax ;Save in structure + mov ax,es:[bx + 2] ;Get lpfn[0] + mov WORD PTR [si].nlp_lpfnErrorAddr[0],ax + mov ax,es:[bx + 4] ;Get lpfn[2] + mov WORD PTR [si].nlp_lpfnErrorAddr[2],ax + mov ax,es:[bx + 6] ;Get lpBadParam[0] + mov WORD PTR [si].nlp_lpBadParam[0],ax + mov ax,es:[bx + 8] ;Get lpBadParam[2] + mov WORD PTR [si].nlp_lpBadParam[2],ax + mov ax,NFY_LOGPARAMERROR ;Get the TOOLHELP ID + xor bx,bx ;Always match + jmp SHORT DN_End + + ;** Must be unknown, return TOOLHELP ID NFY_UNKNOWN with KERNEL value + ;** in LOWORD(wData) +DN_Unknown: + mov dx,ax ;Get the notification value + mov ax,NFY_UNKNOWN ;Unknown KERNEL notification + xor cx,cx ;Clear high WORD + +DN_End: + +cEnd + +sEnd + END + + diff --git a/private/mvdm/wow16/toolhelp/signal.c b/private/mvdm/wow16/toolhelp/signal.c new file mode 100644 index 000000000..674100a3a --- /dev/null +++ b/private/mvdm/wow16/toolhelp/signal.c @@ -0,0 +1,110 @@ +/************************************************************************** + * SIGNAL.C + * + * Routines used to clean up on a forced KERNEL termination of a + * TOOLHELP using app. + * + **************************************************************************/ + +#include <string.h> +#include "toolpriv.h" + +/* ----- Global variables ----- */ + WORD wSignalInstalled; + SIGNAL NEAR *npSignalHead; + +/* SignalRegister + * Registers a default signal proc to a task. This signal proc is + * called when the task is about to be terminated and is called before + * the USER signal proc is called. The registered callback is + * called HelperSignalProc() [HELPER.ASM] and chains to the USER signal + * proc (if any) instead of returning. + */ + +BOOL PASCAL SignalRegister( + HANDLE hTask) +{ + SIGNAL *pSig; + SIGNAL *pTemp; + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* Check to see if this task is already registered */ + for (pSig = npSignalHead ; pSig ; pSig = pSig->pNext) + if (pSig->hTask == hTask) + return FALSE; + + /* Allocate a new SIGNAL structure */ + pSig = (SIGNAL *)LocalAlloc(LMEM_FIXED, sizeof (SIGNAL)); + if (!pSig) + return FALSE; + + /* Fill in the useful fields */ + pSig->hTask = hTask; + pSig->lpfn = (LPFNCALLBACK)HelperSignalProc; + pSig->lpfnOld = (LPFNCALLBACK) + HelperSetSignalProc(hTask, (DWORD)HelperSignalProc); + + /* If this is the only handler, just insert it */ + if (!npSignalHead) + { + pSig->pNext = npSignalHead; + npSignalHead = pSig; + } + + /* Otherwise, insert at the end of the list */ + else + { + for (pTemp = npSignalHead ; pTemp->pNext ; pTemp = pTemp->pNext) + ; + pSig->pNext = pTemp->pNext; + pTemp->pNext = pSig; + } + + return TRUE; +} + + +/* SignalUnRegister + * Called by an app whose callback is no longer to be used. + * NULL hTask uses current task. + */ + +BOOL PASCAL SignalUnRegister( + HANDLE hTask) +{ + SIGNAL *pSig; + SIGNAL *pBefore; + + /* NULL hTask means current task */ + if (!hTask) + hTask = GetCurrentTask(); + + /* First try to find the task */ + pBefore = NULL; + for (pSig = npSignalHead ; pSig ; pSig = pSig->pNext) + if (pSig->hTask == hTask) + break; + else + pBefore = pSig; + if (!pSig) + return FALSE; + + /* Remove it from the list */ + if (!pBefore) + npSignalHead = pSig->pNext; + else + pBefore->pNext = pSig->pNext; + + /* Replace the old signal proc */ + HelperSetSignalProc(hTask, (DWORD)pSig->lpfnOld); + + /* Free the structure */ + LocalFree((HANDLE)pSig); + + return TRUE; +} + + diff --git a/private/mvdm/wow16/toolhelp/stack1.c b/private/mvdm/wow16/toolhelp/stack1.c new file mode 100644 index 000000000..b7ed4a15d --- /dev/null +++ b/private/mvdm/wow16/toolhelp/stack1.c @@ -0,0 +1,155 @@ +/*************************************************************************** + * STACK1.C + * + * Code to support stack tracing on task stacks. + * + ***************************************************************************/ + +#include "toolpriv.h" +#include <newexe.h> +#include <string.h> + +/* ----- Function prototypes ----- */ + + NOEXPORT void StackTraceInfo( + STACKTRACEENTRY FAR *lpStack); + +/* ----- Functions ----- */ + +/* StackTraceFirst + * Starts a task stack trace by returning information about the + * first frame on the task's stack. + */ + +BOOL TOOLHELPAPI StackTraceFirst( + STACKTRACEENTRY FAR *lpStackTrace, + HANDLE hTDB) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpStackTrace || + lpStackTrace->dwSize != sizeof (STACKTRACEENTRY)) + return FALSE; + + /* Get the first value */ + if (!(StackFrameFirst(lpStackTrace, hTDB))) + return FALSE; + + /* Get module and segment number information */ + StackTraceInfo(lpStackTrace); + + return TRUE; +} + + +/* StackTraceCSIPFirst + * Traces the stack of an arbitrary CS:IP. All parameters must be + * given, and once started, the StackTraceNext function can be used + * to trace the remainder of the stack + */ + +BOOL TOOLHELPAPI StackTraceCSIPFirst( + STACKTRACEENTRY FAR *lpStack, + WORD wSS, + WORD wCS, + WORD wIP, + WORD wBP) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpStack || + lpStack->dwSize != sizeof (STACKTRACEENTRY)) + return FALSE; + + /* Get the user information */ + lpStack->wSS = wSS; + lpStack->wCS = wCS; + lpStack->wIP = wIP; + lpStack->wBP = wBP; + + /* Get module and segment number information */ + StackTraceInfo(lpStack); + + /* Set the hTask to the current task as we are in the current task + * context. The CS may not be owned by this task, but at least + * we put a reasonable value in there. + */ + lpStack->hTask = GetCurrentTask(); + + return TRUE; +} + + +/* StackTraceNext + * Continues a stack trace by returning information about the next + * frame on the task's stack. + * structure. + */ + +BOOL TOOLHELPAPI StackTraceNext( + STACKTRACEENTRY FAR *lpStackTrace) +{ + /* Check the version number and verify proper installation */ + if (!wLibInstalled || !lpStackTrace || + lpStackTrace->dwSize != sizeof (STACKTRACEENTRY)) + return FALSE; + + /* Get information about this frame */ + if (!StackFrameNext(lpStackTrace)) + return FALSE; + + /* Get module and segment number information */ + StackTraceInfo(lpStackTrace); + + return TRUE; +} + +/* ----- Helper functions ----- */ + +/* StackTraceInfo + * Gets module and segment number info about the given entry + */ + +NOEXPORT void StackTraceInfo( + STACKTRACEENTRY FAR *lpStack) +{ + GLOBALENTRY GlobalEntry; + struct new_exe FAR *lpNewExe; + struct new_seg1 FAR *lpSeg; + WORD i; + + /* If we have a NULL CS, this is a NEAR frame. Just return because we + * assume the user hasn't trashed the structure. The module and seg + * info will be the same as the last time + */ + if (!lpStack->wCS) + return; + + /* Get information about the code segment block */ + GlobalEntry.dwSize = sizeof (GLOBALENTRY); + if (!GlobalEntryHandle(&GlobalEntry, lpStack->wCS)) + return; + + /* The owner of all code segments is the hModule */ + lpStack->hModule = GlobalEntry.hOwner; + + /* To find the segment number, we look in the EXE header and count the + * listed segments till we find this one + */ + + /* Get a pointer to the EXE Header module */ + lpNewExe = MAKEFARPTR(HelperHandleToSel(lpStack->hModule), 0); + + /* Make sure this is a EXE Header segment */ + if (lpNewExe->ne_magic != NEMAGIC) + return; + + /* Get the list of segments and go for it */ + lpSeg = MAKEFARPTR(HIWORD((DWORD)lpNewExe), lpNewExe->ne_segtab); + for (i = 0 ; i < lpNewExe->ne_cseg ; ++i, ++lpSeg) + if (HelperHandleToSel(lpSeg->ns_handle) == lpStack->wCS) + break; + if (i == lpNewExe->ne_cseg) + return; + + /* Save the segment number (seg numbers start at one) */ + lpStack->wSegment = i + 1; +} diff --git a/private/mvdm/wow16/toolhelp/stack2.asm b/private/mvdm/wow16/toolhelp/stack2.asm new file mode 100644 index 000000000..61bdc4308 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/stack2.asm @@ -0,0 +1,178 @@ +;************************************************************************** +;* STACK2.ASM +;* +;* Assembly support code for stack tracing. +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE TDB.INC + +;** External functions + +externNP HelperVerifySeg +externNP HelperHandleToSel + +;** Functions + +sBegin CODE + assumes CS,CODE + +; StackFrameFirst +; Returns information about the first stack frame and checks it +; for validity as much as possible. The first stack frame is found +; by getting the information from the TDB. If this task is active, +; or if the task was changed in an unusual way, this information +; may be incorrect. If it is, the user must set up the first +; CS, IP, and BP, and BPNext in the user structure, log it as the +; first stack trace and call StackTraceNext directly. + +cProc StackFrameFirst, <NEAR,PUBLIC>, <si,di,ds> + parmD lpStack + parmW hTDB +cBegin + ;** Verify that we have a good TDB first + ;** Start by verifying the selector + mov ax,hTDB ;Get the selector + cCall HelperHandleToSel, <ax> ;Convert it to a selector + push ax ;Save it + mov bx,TDBSize + cCall HelperVerifySeg, <ax,bx> + pop bx ;Get selector back + or ax,ax ;FALSE return? + jnz SHORT SF_SelOk ;Selector's OK + xor ax,ax ;Return FALSE + jmp SHORT SF_End +SF_SelOk: + + ;** Verify that the TDB signature matches + mov ds,bx ;Point with DS + cmp ds:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB? + jz SF_SigOk ;Must be + xor ax,ax ;Return FALSE + jmp SHORT SF_End +SF_SigOk: + + ;** Get the BP value from the task stack and store in structure + les di,lpStack ;Get a pointer to the user struct + mov ax,ds:[TDB_taskSS] ;Get the SS value + mov bx,ds:[TDB_taskSP] ;Get the max segment offset we need + add bx,Task_CS + 2 + cCall HelperVerifySeg, <ax,bx> ;Make sure we can read all this + or ax,ax ;Error? + jz SF_End ;Yes, can't do walk + lds bx,DWORD PTR ds:[TDB_taskSP] ;Get the SS:SP value + mov si,ds:[bx].Task_BP ;Get the BP value from the stack + and si,NOT 1 ;Clear the FAR frame bit, if any + mov es:[di].st_wBP,si ;Store the BP value + mov ax,ds:[bx].Task_IP ;Store initial IP + mov es:[di].st_wIP,ax + mov ax,ds:[bx].Task_CS ;Store the initial CS + mov es:[di].st_wCS,ax + + ;** Return as much info as possible about this first frame + mov ax,hTDB ;Get the TDB handle + mov es:[di].st_hTask,ax ;Save in structure + mov es:[di].st_wSS,ds ;Save the SS value + mov es:[di].st_wFlags,FRAME_FAR ;Force a FAR frame this time + + ;** Try to verify this stuff + xor ax,ax ;In case we need to exit + or si,si ;End of the line? + jz SF_End ;Nope + cmp si,ds:[0ah] ;Compare against stack top + jb SF_End ;Fine with top + cmp si,ds:[0eh] ;Check against stack bottom + jae SF_End ;OK with bottom too + mov ax,1 ;Return TRUE + +SF_End: +cEnd + + +; StackFrameNext +; Returns information in a public structure about the stack frame +; pointed to by the BP value passed in. Returns TRUE if the +; information seems valid, or FALSE if information could not be +; returned. + +cProc StackFrameNext, <NEAR,PUBLIC>, <si,di,ds> + parmD lpStack +cBegin + ;** Get pointers to the frame + les di,lpStack ;Get a pointer to the structure + mov ax,es:[di].st_wSS ;Get the stack segment + mov ds,ax ;Point with DS + + ;** Get the next stack frame + mov si,es:[di].st_wBP ;Get the current BP value + lea ax,[si + 6] ;Get the max stack probe + cmp ax,si ;No stack wraparound allowed + jb SN_End ;If below, we have wrapped + cCall HelperVerifySeg, <ds,ax> ;Make sure the stack is OK + or ax,ax ;OK? + jnz @F ;Yes. + jmp SHORT SN_End ;Return FALSE +@@: mov dx,ds:[si+4] ;DX:CX is the return address + mov cx,ds:[si+2] + mov bx,ds:[si] ;Get next BP value + + ;** Zero BP is end of chain + xor ax,ax ;In case we need to exit + or bx,bx ;End of the line? + jz SN_End ;Nope + + ;** If the new BP is higher on the stack than the old, it's invalid + cmp bx,si ;New BP <= Old BP? + jbe SN_End ;OK. + + ;** Make sure we're still on the stack (variables from KDATA.ASM) + cmp bx,ds:[0ah] ;Compare against stack top + jb SN_End ;Fine with top + cmp bx,ds:[0eh] ;Check against stack bottom + jae SN_End ;OK with bottom too + + ;** Return what we can about the frame + mov es:[di].st_wSS,ds ;Save the SS value + mov es:[di].st_wBP,si ; and the BP value + test bx,1 ;Far or near frame? + jnz SN_FarFrame ;For sure far if BP is odd + + ;** Even when BP is not odd, we may have a far frame + mov ax,cs ;Get our RPL bits + and al,3 ;Mask RPL bits + mov ah,dl ;Get frame's RPL bits + and ah,3 ;Mask RPL bits + cmp al,ah ;If CS is a handle, they won't match + jne SN_NearFrame ;Bits don't match + lar ax,dx ;Get the access bits + test ax,800h ;Is this a code segment? + jz SN_NearFrame ;No. MUST be near frame + lsl ax,dx ;Get the limit + cmp ax,cx ;Inside limit? + jbe SN_NearFrame ;No. MUST be near + + ;** Otherwise, probably is a far frame. It may not be, of course, + ;** because this may be a code seg parameter +SN_FarFrame: + mov es:[di].st_wIP,cx ;Save the offset + mov es:[di].st_wCS,dx ; and selector value + mov ax,FRAME_FAR ;Tell the user what we did + and bx,NOT 1 ;Clear the far frame bit + jmp SHORT SN_20 ;Skip near section + + ;** Must be a near frame +SN_NearFrame: + mov es:[di].st_wIP,cx ;Save the offset + ;Leave the old CS value in + mov ax,FRAME_NEAR ;Tell the user what we did +SN_20: mov es:[di].st_wFlags,ax ;Save in the structure + mov es:[di].st_wBP,bx ;Save BP in the structure + mov ax,1 ;Return TRUE + +SN_End: +cEnd + +sEnd + + END diff --git a/private/mvdm/wow16/toolhelp/string.h b/private/mvdm/wow16/toolhelp/string.h new file mode 100644 index 000000000..2c39b8d91 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/string.h @@ -0,0 +1,121 @@ +/*** +*string.h - declarations for string manipulation functions +* +* Copyright (c) 1985-1990, Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the function declarations for the string +* manipulation functions. +* [ANSI/System V] +* +****/ + +#if defined(_DLL) && !defined(_MT) +#error Cannot define _DLL without _MT +#endif + +#ifdef _MT +#define _FAR_ _far +#else +#define _FAR_ +#endif + +#ifndef _SIZE_T_DEFINED +typedef unsigned int size_t; +#define _SIZE_T_DEFINED +#endif + +/* function prototypes */ + +void _FAR_ * _FAR_ _cdecl memccpy(void _FAR_ *, const void _FAR_ *, + int, unsigned int); +void _FAR_ * _FAR_ _cdecl memchr(const void _FAR_ *, int, size_t); +int _FAR_ _cdecl memcmp(const void _FAR_ *, const void _FAR_ *, + size_t); +int _FAR_ _cdecl memicmp(const void _FAR_ *, const void _FAR_ *, + unsigned int); +void _FAR_ * _FAR_ _cdecl memcpy(void _FAR_ *, const void _FAR_ *, + size_t); +void _FAR_ * _FAR_ _cdecl memmove(void _FAR_ *, const void _FAR_ *, + size_t); +void _FAR_ * _FAR_ _cdecl memset(void _FAR_ *, int, size_t); +void _FAR_ _cdecl movedata(unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int); +char _FAR_ * _FAR_ _cdecl strcat(char _FAR_ *, const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strchr(const char _FAR_ *, int); +int _FAR_ _cdecl strcmp(const char _FAR_ *, const char _FAR_ *); +int _FAR_ _cdecl strcmpi(const char _FAR_ *, const char _FAR_ *); +int _FAR_ _cdecl strcoll(const char _FAR_ *, const char _FAR_ *); +int _FAR_ _cdecl stricmp(const char _FAR_ *, const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strcpy(char _FAR_ *, const char _FAR_ *); +size_t _FAR_ _cdecl strcspn(const char _FAR_ *, const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strdup(const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl _strerror(const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strerror(int); +size_t _FAR_ _cdecl strlen(const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strlwr(char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strncat(char _FAR_ *, const char _FAR_ *, + size_t); +int _FAR_ _cdecl strncmp(const char _FAR_ *, const char _FAR_ *, + size_t); +int _FAR_ _cdecl strnicmp(const char _FAR_ *, const char _FAR_ *, + size_t); +char _FAR_ * _FAR_ _cdecl strncpy(char _FAR_ *, const char _FAR_ *, + size_t); +char _FAR_ * _FAR_ _cdecl strnset(char _FAR_ *, int, size_t); +char _FAR_ * _FAR_ _cdecl strpbrk(const char _FAR_ *, + const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strrchr(const char _FAR_ *, int); +char _FAR_ * _FAR_ _cdecl strrev(char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strset(char _FAR_ *, int); +size_t _FAR_ _cdecl strspn(const char _FAR_ *, const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strstr(const char _FAR_ *, + const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strtok(char _FAR_ *, const char _FAR_ *); +char _FAR_ * _FAR_ _cdecl strupr(char _FAR_ *); +size_t _FAR_ _cdecl strxfrm (char _FAR_ *, const char _FAR_ *, + size_t); + +/* model independent function prototypes */ + +void _far * _far _cdecl _fmemccpy(void _far *, const void _far *, + int, unsigned int); +void _far * _far _cdecl _fmemchr(const void _far *, int, size_t); +int _far _cdecl _fmemcmp(const void _far *, const void _far *, + size_t); +void _far * _far _cdecl _fmemcpy(void _far *, const void _far *, + size_t); +int _far _cdecl _fmemicmp(const void _far *, const void _far *, + unsigned int); +void _far * _far _cdecl _fmemmove(void _far *, const void _far *, + size_t); +void _far * _far _cdecl _fmemset(void _far *, int, size_t); +char _far * _far _cdecl _fstrcat(char _far *, const char _far *); +char _far * _far _cdecl _fstrchr(const char _far *, int); +int _far _cdecl _fstrcmp(const char _far *, const char _far *); +int _far _cdecl _fstricmp(const char _far *, const char _far *); +char _far * _far _cdecl _fstrcpy(char _far *, const char _far *); +size_t _far _cdecl _fstrcspn(const char _far *, const char _far *); +char _far * _far _cdecl _fstrdup(const char _far *); +char _near * _far _cdecl _nstrdup(const char _far *); +size_t _far _cdecl _fstrlen(const char _far *); +char _far * _far _cdecl _fstrlwr(char _far *); +char _far * _far _cdecl _fstrncat(char _far *, const char _far *, + size_t); +int _far _cdecl _fstrncmp(const char _far *, const char _far *, + size_t); +int _far _cdecl _fstrnicmp(const char _far *, const char _far *, + size_t); +char _far * _far _cdecl _fstrncpy(char _far *, const char _far *, + size_t); +char _far * _far _cdecl _fstrnset(char _far *, int, size_t); +char _far * _far _cdecl _fstrpbrk(const char _far *, + const char _far *); +char _far * _far _cdecl _fstrrchr(const char _far *, int); +char _far * _far _cdecl _fstrrev(char _far *); +char _far * _far _cdecl _fstrset(char _far *, int); +size_t _far _cdecl _fstrspn(const char _far *, const char _far *); +char _far * _far _cdecl _fstrstr(const char _far *, + const char _far *); +char _far * _far _cdecl _fstrtok(char _far *, const char _far *); +char _far * _far _cdecl _fstrupr(char _far *); diff --git a/private/mvdm/wow16/toolhelp/task1.c b/private/mvdm/wow16/toolhelp/task1.c new file mode 100644 index 000000000..01286b0af --- /dev/null +++ b/private/mvdm/wow16/toolhelp/task1.c @@ -0,0 +1,73 @@ +/************************************************************************* + * TASK1.C + * + * Routines used to enumerate all tasks. + * + *************************************************************************/ + +#include <string.h> +#include "toolpriv.h" + +/* ----- Functions ----- */ + +/* TaskFirst + * Returns information about the first task in the task chain. + */ + +BOOL TOOLHELPAPI TaskFirst( + TASKENTRY FAR *lpTask) +{ + /* Check for errors */ + if (!wLibInstalled || !lpTask || lpTask->dwSize != sizeof (TASKENTRY)) + return FALSE; + + /* Pass a pointer to the first block to the assembly routine */ + return TaskInfo(lpTask, *(WORD FAR *)MAKEFARPTR(segKernel, npwTDBHead)); +} + + +/* TaskNext + * Returns information about the next task in the task chain. + */ + +BOOL TOOLHELPAPI TaskNext( + TASKENTRY FAR *lpTask) +{ + /* Check for errors */ + if (!wLibInstalled || !lpTask || !lpTask->hNext || + lpTask->dwSize != sizeof (TASKENTRY)) + return FALSE; + + /* Pass a pointer to the next block to the assembly routine */ + return TaskInfo(lpTask, lpTask->hNext); +} + + +/* TaskFindHandle + * Returns information about the task with the given task handle. + */ + +BOOL TOOLHELPAPI TaskFindHandle( + TASKENTRY FAR *lpTask, + HANDLE hTask) +{ + /* Check for errors */ + if (!wLibInstalled || !lpTask || lpTask->dwSize != sizeof (TASKENTRY)) + return FALSE; + +#ifdef WOW + if ( (hTask & 0x4) == 0 && hTask <= 0xffe0 && hTask != 0 ) { + // + // If they are getting a task handle for an htask alias, then + // just fill in the hinst method and return. + // + // Special hack for OLE 2.0's BusyDialog. + // + lpTask->hInst = hTask; + return( TRUE ); + } +#endif + + /* Pass a pointer to the first block to the assembly routine */ + return TaskInfo(lpTask, hTask); +} diff --git a/private/mvdm/wow16/toolhelp/task2.asm b/private/mvdm/wow16/toolhelp/task2.asm new file mode 100644 index 000000000..f01829234 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/task2.asm @@ -0,0 +1,326 @@ +;************************************************************************** +;* TASK2.ASM +;* +;* Assembly support for the task enumeration routines. +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + +PMODE32 = 0 +PMODE = 0 +SWAPPRO = 0 + INCLUDE TDB.INC +ifdef WOW + INCLUDE WOW.INC +endif + +;** Data + +sBegin DATA + +lpfnRetAddr DD 0 ;Return address after stack switch + +sEnd + +;** External functions +externNP HelperHandleToSel +externNP HelperVerifySeg +externFP GetCurrentTask +externFP DirectedYield +ifdef WOW +externFP PostAppMessage +endif + +;** Functions + +.286p + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + + +; TaskSetCSIP +; Allows the user to set the CS:IP of a sleeping task so that it will +; begin execution at this address when the task is yielded to. +; Returns the old address. + +cProc TaskSetCSIP, <PUBLIC,FAR>, <si> + parmW hTask + parmW wCS + parmW wIP +cBegin + assumes DS,nothing + assumes ES,nothing + + ;** If this is the current task, do nothing: we only work on + ;** sleeping tasks + cCall GetCurrentTask ;Gets current task in AX + mov bx,hTask ;Get desired task + cmp ax,bx ;Same? + jne @F ;No, it's OK + xor ax,ax ;Return a DWORD zero + cwd + jmp SHORT TC_End +@@: + + ;** Get the TDB SS:SP + mov es,bx ;Point to TDB with ES + les si,DWORD PTR es:[TDB_TaskSP] ;Get a pointer to the task stack +ifdef WOW + ; + ; ES:SI now points to the place where we had the TDB's SS:SP pointing + ; This spot in wow is actually the SS:BP frame of the WOW16CALL + ; function. The definitions for this frame come from WOW.INC (WOW.H). + ; The addition of this strange value adjusts the SS:SP pointer back + ; onto the stack, undoing a previous adjustment in TASKING.ASM + add si,(vf_vpCSIP-vf_wThunkCSIP) +endif + ;** Change the CS:IP + mov ax,wIP ;Get the new IP value + xchg ax,es:[si].Task_IP ;Swap with the old one + mov dx,wCS ;Get the new CS value + xchg dx,es:[si].Task_CS ;Swap with the old one + +TC_End: +cEnd + + +; TaskGetCSIP +; Gets the next CS:IP that this task will run at. + +cProc TaskGetCSIP, <PUBLIC,FAR>, <si> + parmW hTask +cBegin + assumes DS,nothing + assumes ES,nothing + + ;** If this is the current task, do nothing: we only work on + ;** sleeping tasks + cCall GetCurrentTask ;Gets current task in AX + mov bx,hTask ;Get desired task + cmp ax,bx ;Same? + jne @F ;No, it's OK + xor ax,ax ;Return a DWORD zero + cwd + jmp SHORT TG_End +@@: + + ;** Get the TDB SS:SP + mov es,bx ;Point to TDB with ES + les si,DWORD PTR es:[TDB_TaskSP] ;Get a pointer to the task stack + +ifdef WOW + ; + ; ES:SI now points to the place where we had the TDB's SS:SP pointing + ; This spot in wow is actually the SS:BP frame of the WOW16CALL + ; function. The definitions for this frame come from WOW.INC (WOW.H). + ; The addition of this strange value adjusts the SS:SP pointer back + ; onto the stack, undoing a previous adjustment in TASKING.ASM + add si,(vf_vpCSIP-vf_wThunkCSIP) +endif + ;** Change the CS:IP + mov ax,es:[si].Task_IP ;Get the CS:IP to return + mov dx,es:[si].Task_CS + +TG_End: +cEnd + + +; TaskSwitch +; Switches to the indicated task from the current one. +; Returns FALSE if it couldn't task switch. +; Jumps to the address given by lpJmpAddr + +cProc TaskSwitch, <PUBLIC,FAR>, <si,di> + parmW hTask + parmD lpJmpAddr +cBegin + push ds + mov ax, _DATA ;Make sure to set DS + mov ds, ax + assumes ds,DATA + + ;** Check to make sure TOOLHELP is installed + cmp wLibInstalled,0 ;Library installed? + pop ds + assumes ds,nothing + jnz @F ;Yes + xor ax,ax ;Return FALSE + jmp TS_End ;No. Fail the API +@@: + + ;** Get the task handle + cCall GetCurrentTask ;Get the current task + cmp ax,hTask ;Switch to current task? + jne @F ;No, it's OK + xor ax,ax ;Yes, we can't do that so return FALSE + +ifdef WOW + jmp TS_End +else + jmp SHORT TS_End +endif + +@@: cCall HelperVerifySeg, <hTask,TDB_sig+1> ;Verify the segment + or ax,ax ;Segment OK? + jz TS_End ;Nope. Get out + mov es,hTask ;Get the TDB + xor ax,ax ;Get a zero just in case + cmp es:[TDB_sig], TDB_SIGNATURE ;Signature match? + jne TS_End ;Return FALSE + + ;** Poke in the address to jump to + mov si,es ;Save the hTask + lea ax,TS_FromYield ;Point to new task address + cCall TaskSetCSIP, <si,cs,ax> ;Set the new address + mov es,si ;Get hTask back + + ;** Save the jump address from the stack so we can jump to it later + push ds + mov ax,_DATA ;Point to our data segment + mov ds,ax + assumes ds,DATA + mov ax,WORD PTR lpJmpAddr[0];Get the low word of the ret address + mov WORD PTR lpfnRetAddr[0],ax + mov ax,WORD PTR lpJmpAddr[2];Get the selector value + mov WORD PTR lpfnRetAddr[2],ax + pop ds + +ifdef WOW + ;** Force a task switch by posting a message. This is because the + ;** event count is not used under WOW. + cCall PostAppMessage,<es, 0, 0, 0, 0> +else + ;** Force a task switch by tampering with the event count + inc es:[TDB_nEvents] ;Force at least one event so we + ; will switch to this task next +endif ;WOW + + ;** Switch to the new task. DirectedYield() returns only when this + ;** task is next scheduled + cCall DirectedYield, <si> ;Switch to the new task + mov ax,1 ;Return TRUE + jmp SHORT TS_End ;Get out + + ;** Restore from the directed yield +TS_FromYield: + + ;** Make a stack frame to work on. We can't trash any regs + PUBLIC TS_FromYield + sub sp,4 ;Save room for a far ret frame + push bp ;Make a stack frame + mov bp,sp + pusha ;Save everything + push ds + push es + + ;** Get our jump address from our DS and put in far ret frame + mov ax,_DATA ;Get the TOOLHELP DS + mov ds,ax + mov ax,WORD PTR lpfnRetAddr[0] ;Get the offset + mov [bp + 2],ax ;Put it on the stack + mov ax,WORD PTR lpfnRetAddr[2] ;Get the selector + mov [bp + 4],ax ;Put on the stack + + ;** Restore the event count + mov es,segKernel ;Get the KERNEL segment + mov bx,npwTDBCur ;Get the current task pointer + mov es,es:[bx] ;Get the TDB pointer in ES +ifndef WOW + dec es:[TDB_nEvents] ;Clear the dummy event we put in +endif + + ;** Clear the stack and 'return' to the new address + pop es + pop ds + popa + pop bp + retf + +TS_End: +cEnd + + +; TaskInfo +; +; Returns information about the task with the given block handle + +cProc TaskInfo, <PUBLIC,NEAR>, <si,di,ds> + parmD lpTask + parmW wTask +cBegin + ;** Start by verifying the selector + mov ax,wTask ;Get the selector + cCall HelperHandleToSel, <ax> ;Convert it to a selector + push ax ;Save it + mov bx,TDBSize + cCall HelperVerifySeg, <ax,bx> + pop bx ;Get selector back + or ax,ax ;FALSE return? + jnz TI_SelOk ;Selector's OK + xor ax,ax ;Return FALSE + jmp TI_End +TI_SelOk: + + ;** Verify that the TDB signature matches + mov ds,bx ;Point with DS + cmp ds:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB? + jz TI_SigOk ;Must be + xor ax,ax ;Return FALSE + jmp SHORT TI_End +TI_SigOk: + + ;** Now, get information from the TDB + les di,lpTask ;Point to destination buffer + mov ax,ds:[TDB_next] ;Get the next TDB handle + mov es:[di].te_hNext,ax ;Save in public structure + mov ax,wTask ;Get this task's handle + mov es:[di].te_hTask,ax ;Save in buffer + mov ax,ds:[TDB_Parent] ;Get this task's parent + mov es:[di].te_hTaskParent,ax ;Save + mov ax,ds:[TDB_taskSS] ;Get the SS + mov es:[di].te_wSS,ax + mov ax,ds:[TDB_taskSP] ;Get the SP + mov es:[di].te_wSP,ax + mov ax,ds:[TDB_nEvents] ;Event counter + mov es:[di].te_wcEvents,ax + mov ax,ds:[TDB_Queue] ;Queue pointer + mov es:[di].te_hQueue,ax + mov ax,ds:[TDB_PDB] ;Offset of DOS PSP + mov es:[di].te_wPSPOffset,ax + mov ax,ds:[TDB_Module] ;Instance handle (DS) of task + mov es:[di].te_hInst,ax + mov ax,ds:[TDB_pModule] ;Module database handle + mov es:[di].te_hModule,ax + mov cx,8 ;Copy module name + push di ;Save structure pointer + mov si,TDB_ModName ;Point to the string + add di,te_szModule ; and to the string dest + cld + repnz movsb ;Copy the string + mov BYTE PTR es:[di],0 ;Zero terminate it + pop di ;Get structure pointer back + + ;** Get information from the stack segment. Vars from KDATA.ASM + mov ax,es:[di].te_wSS ;Get the SS value + verr ax ;OK to read here? + jnz TI_SkipThis ;No, so don't do it + mov ds,ax ;Point with DS + mov ax,ds:[0ah] ;Lowest value of SP allowed + mov es:[di].te_wStackTop,ax ;Save in structure + mov ax,ds:[0ch] ;Get stack minimum value so far + mov es:[di].te_wStackMinimum,ax ;Save in structure + mov ax,ds:[0eh] ;Largest value of SP allowed + mov es:[di].te_wStackBottom,ax ;Save in structure + + ;** Return TRUE on success +TI_SkipThis: + mov ax,1 ;Return TRUE code +TI_End: +cEnd + +sEnd + + END diff --git a/private/mvdm/wow16/toolhelp/terminat.asm b/private/mvdm/wow16/toolhelp/terminat.asm new file mode 100644 index 000000000..c04a324c9 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/terminat.asm @@ -0,0 +1,145 @@ + PAGE 60,150 +;*************************************************************************** +;* TERMINAT.ASM +;* +;* Assembly code routine used for the TOOLHELP.DLL app terminate +;* routine. +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE TDB.INC + +;** Symbols +I_EXCEPTION EQU 0 +I_INTERRUPT EQU 1 +MAX_INTERRUPT EQU 5 +GIVE_WDEB386 EQU 8000h +Q_HACK_30 EQU 54h + +.286p + +;** Data + +sBegin DATA + +wTermFlags DW ? ;Save terminate flags across Yield + +sEnd + +;** Imported values +externFP InterruptUnRegister +externFP NotifyUnRegister +externFP GetCurrentTask +externFP FatalAppExit +externFP TaskSetCSIP +externFP DirectedYield +externFP TaskSwitch + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +; TerminateApp +; Terminates the task in one of two ways: TERMINATE_NORMAL or +; TERMINATE_USER_DISPLAY. TERMINATE_NORMAL calls KERNEL to display +; the UAE box and terminates the app. TERMINATE_USER_DISPLAY also +; terminates the app but assumes the user has displayed some warning. +; If the task passed in is not the current task, this function does +; the DirectedYield() to switch to the correct task before terminating +; it. +; This function does not return when terminating the current task +; except when WDEB386 is installed and the (undocumented) GIVE_WDEB386 +; flag is set. +; Caller: TerminateApp( +; HANDLE hTask, (If NULL, does current task) +; WORD wFlags) + +cProc TerminateApp, <FAR,PUBLIC>, <si,di,ds> + parmW hTask + parmW wFlags +cBegin + mov ax, _DATA ;Get our DS + mov ds, ax + + ;** Save the flags in the DS so we can get after DYield + mov ax,wFlags ;Get the parameter flags + mov wTermFlags,ax ;Save them + + ;** Get the task value + cCall GetCurrentTask ;Get the task + mov si,hTask ;Get the hTask value + or si,si ;Zero? + jnz TA_10 ;No + mov es,ax ;Point ES at current task + jmp SHORT TA_NukeCurrent ;In this case we always nuke current +TA_10: + ;** If this is the current task, just nuke it and don't return + cmp ax,si ;Current? + mov es,si ;Point ES at task + je TA_NukeCurrent ;Yes, nuke it directly + + ;** Switch to the new task and prepare to nuke it + lea ax,TA_NewTask ;Get address of new task entry + cCall TaskSwitch, <si,cs,ax> ;Switch to this task + jmp SHORT TA_End ;Get out + + ;** We're in the new task now +TA_NewTask: + mov ax,_DATA ;Get the TOOLHELP DS + mov ds,ax + mov es,segKernel ;Get the KERNEL segment + mov bx,npwTDBCur ;Get the current task pointer + mov es,es:[bx] ;Get the TDB pointer in ES + + ;** HACK ALERT!!!! In order to get USER to allow us to terminate + ;* this app, we are manually nuking the semaphore. This is + ;* at a fixed offsets in the Q structure and only needs to + ;** be done in 3.0 + test wTHFlags,TH_WIN30 ;In 3.0? + jz TA_NukeCurrent ;No, don't do this ugly hack + push es ;Save ES while we play with the queue + mov es,es:[TDB_Queue] ;ES points to queue now + mov bx,Q_HACK_30 ;Get 3.0 offset + mov WORD PTR es:[bx],0 ;Clear the semaphore count + mov WORD PTR es:[bx + 2],0 ; and the semaphore value to wait for + pop es ;ES points to TDB again + +TA_NukeCurrent: + ;** Check the flag values. If NO_UAE_BOX, tell KERNEL + ;** not to display the normal UAE box. + test wTermFlags,NO_UAE_BOX ;Display the box? + jz TA_20 ;Yes, so skip this stuff + or es:[TDB_ErrMode],02 ;Set the no display box flag +TA_20: + ;** Terminate the app using KERNEL + cCall FatalAppExit, <0,0,0> ;Do it + + ;** If we're flagged that this is an internal terminate, we just want + ;* to return if WDEB is installed so that we can pass the + ;** fault on. To do this, we must return here to the caller. + test wFlags,GIVE_WDEB386 ;Internal entry? + jnz TA_End ;Yes, don't nuke app + + ;** If KERNEL doesn't nuke the app (does this if WDEB386 + ;** is installed), nuke it ourselves (no UAE box) + mov es,segKernel ;Get the KERNEL segment + mov bx,npwTDBCur ;Get the current task pointer + mov es,es:[bx] ; in ES + cmp WORD PTR es:[TDB_USignalProc] + 2,0 ;USER signal proc? + jz @F ;No + mov bx,0666h ;Death knell + mov di, -1 + cCall es:[TDB_USignalProc],<es,bx,di,es:[TDB_Module],es:[TDB_Queue]> +@@: mov ax,4CFFH ;Nuke the app + int 21h ;We don't return here + +TA_End: + +cEnd + +sEnd + + END + + diff --git a/private/mvdm/wow16/toolhelp/ththunks.asm b/private/mvdm/wow16/toolhelp/ththunks.asm new file mode 100644 index 000000000..1b940c522 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/ththunks.asm @@ -0,0 +1,50 @@ + TITLE THTHUNKS.ASM + PAGE ,132 +; +; WOW v1.0 +; +; Copyright (c) 1991-1992, Microsoft Corporation +; +; THTHUNKS.ASM +; Thunks in 16-bit space to route Windows API calls to WOW32 +; +; History: +; +; 09-Nov-1992 Dave Hart (davehart) +; Adapted from mvdm\wow16\kernel31\kthunks.asm for ToolHelp +; +; 02-Apr-1991 Matt Felton (mattfe) +; Created. +; + +ifndef WINDEBUG + KDEBUG = 0 + WDEBUG = 0 +else + KDEBUG = 1 + WDEBUG = 1 +endif + + + .286p + + .xlist + include cmacros.inc + include wow.inc + include wowth.inc + .list + +sBegin CODE +assumes CS,CODE +assumes DS,NOTHING +assumes ES,NOTHING + +; Kernel API thunks + + ToolHelpThunk ClassFirst + ToolHelpThunk ClassNext + + +sEnd CODE + +end diff --git a/private/mvdm/wow16/toolhelp/timer.asm b/private/mvdm/wow16/toolhelp/timer.asm new file mode 100644 index 000000000..ee984fa7e --- /dev/null +++ b/private/mvdm/wow16/toolhelp/timer.asm @@ -0,0 +1,180 @@ + PAGE 60,150 +;*************************************************************************** +;* TIMER.ASM +;* +;* Routines used to give a cleaner interface to the VTD. +;* This interface also works on a 286 by calling GetTickCount() in +;* this case. +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC + INCLUDE WINDOWS.INC + +;** Symbols +SI_CRITICAL EQU 1 +DI_CRITICAL EQU 2 + +;** Imports +externA __WinFlags +externFP GetTickCount + +sBegin DATA + +dwLastTimeReturned dd 0 +wLastCountDown dw 0 + + +sEnd + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +; TimerCount +; Returns the count from either the virtual timer device or from the +; Windows function GetTickCount() depending on the processor. +; Prototype: +; BOOL FAR PASCAL TimerCount( +; TIMERINFO FAR *lpTimer) +; + +cProc TimerCount, <FAR,PUBLIC>, <si,di,ds> + parmD lpTimer + localD dwJumpAddr +cBegin + mov ax, _DATA ;Get our data segment + mov es, ax + + ;** Point to the structure + lds si,lpTimer ;Point to the structure + + ;** Make sure the size is correct + xor ax,ax ;FALSE + cmp WORD PTR [si].ti_dwSize[2],0 ;High word must be zero + je @F + jmp TC_End +@@: cmp WORD PTR [si].ti_dwSize[0],SIZE TIMERINFO ;Low word must match + je @F + jmp TC_End +@@: + +ifndef WOW + ;** If we are in standard mode, always use Windows entry point + mov ax,__WinFlags ;Get the flags + test ax,WF_STANDARD ;Standard mode? + jnz TC_TryMMSys ;Yes, don't even try VTD +.386p + ;** Try to get the VTD entry point + mov ax,1684h ;Get device entry point + mov bx,5 ;VTD device number + int 2fh ;Win386 entry point + mov ax,es ;Did we get a value? + or ax,di ; (zero means no device) + jz SHORT TC_UseWinAPI ;No VTD--use Win API + + ;** Get the VTD values + mov WORD PTR dwJumpAddr[0],di ;Save the address + mov WORD PTR dwJumpAddr[2],es ;Save the address + mov ax,0101h ;VTD: ms since start of Win386 + call DWORD PTR dwJumpAddr ;Call the VTD + jc SHORT TC_UseWinAPI ;Carry set means error + mov [si].ti_dwmsSinceStart,eax ;Save in structure + mov ax,0102h ;VTD: ms in this VM + call DWORD PTR dwJumpAddr ;Call the VTD + jc SHORT TC_UseWinAPI ;Carry set means VTD error + mov [si].ti_dwmsThisVM,eax ;Save value in structure + jmp TC_ReturnOK ;We're done +.286p + + ;** See if mmsystem timer is installed +TC_TryMMSys: + cmp WORD PTR es:[lpfntimeGetTime][2], 0 ;Installed? + je TC_UseWinAPI ;No, do this the hard way + call DWORD PTR es:lpfntimeGetTime + + ;** Fill the structure with this information + mov WORD PTR [si].ti_dwmsSinceStart[0],ax + mov WORD PTR [si].ti_dwmsSinceStart[2],dx + mov WORD PTR [si].ti_dwmsThisVM[0],ax + mov WORD PTR [si].ti_dwmsThisVM[2],dx + jmp TC_ReturnOK +endif ; ndef WOW + + ;** Use the Windows API +TC_UseWinAPI: + cCall GetTickCount ;Call the Windows API + mov WORD PTR [si].ti_dwmsSinceStart[0],ax ;Save the value for now + mov WORD PTR [si].ti_dwmsSinceStart[2],dx + + ;** Read the countdown timer. Note that the timer starts at 54 * 1193 + ;** and counts down to zero. Each count is 1193 ms. + xor al,al ;Prepare to read tick count + out 43h,al ;Send to timer + in al,40h ;Get the low byte + mov ah,al ;Save in AH + in al,40h ;Get the high byte + xchg ah,al + mov dx,0ffffh ;Get total countdown amount + sub dx,ax ;Get number of counts expired + mov ax,dx ;Get the number in AX for div + xor dx,dx ;Zero the high word + mov cx,1193 ;Divide to get ms + div cx ;Divide it + mov cx, ax ;cx == saved Curr count + + ;** Now fill the structure. Note that the 'ThisVM' entry is the + ;** same as the 'SinceStart' entry in standard mode. + xor dx, dx + add ax, WORD PTR [si].ti_dwmsSinceStart[0] ;Add this count in + adc dx, WORD PTR [si].ti_dwmsSinceStart[2] + + ;** Check to make sure we didn't mess up. If we did (if the timer + ;** was reset right in the middle of us reading it). If we + ;** messed up, do it again until we get it right. + mov bx, _DATA ;Get our data segment + mov es, bx + cmp dx, WORD PTR es:dwLastTimeReturned[2] + jne TC_TimeOK + cmp ax, WORD PTR es:dwLastTimeReturned[0] + jae TC_TimeOK + + ; New time is less than the old time so estimate the curr time + ; using LastTimeReturned as the base + mov ax, WORD PTR es:dwLastTimeReturned[0] + mov dx, WORD PTR es:dwLastTimeReturned[2] + + xor bx, bx ;check for wrap + cmp cx, word ptr es:wLastCountDown + jae TC_NoWrap ;if wrap + add ax, cx ; += curr count + adc dx, 0 + jmp short TC_TimeOK + +TC_NoWrap: ;else no wrap + mov bx, cx ; += Curr - LastCountDown + sub bx, word ptr es:wLastCountDown + add ax, bx + adc dx, 0 + +TC_TimeOK: + mov word ptr es:wLastCountDown, cx + mov WORD PTR es:dwLastTimeReturned[0], ax + mov WORD PTR es:dwLastTimeReturned[2], dx + mov WORD PTR [si].ti_dwmsSinceStart[0], ax ;Save good count + mov WORD PTR [si].ti_dwmsSinceStart[2], dx + mov WORD PTR [si].ti_dwmsThisVM[0],ax ;Save in structure + mov WORD PTR [si].ti_dwmsThisVM[2],dx ;Save in structure + +TC_ReturnOK: + mov ax,1 ;Return TRUE + +TC_End: + + + +cEnd + +sEnd + +END diff --git a/private/mvdm/wow16/toolhelp/toolhelp.c b/private/mvdm/wow16/toolhelp/toolhelp.c new file mode 100644 index 000000000..c79d2438f --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolhelp.c @@ -0,0 +1,147 @@ +/************************************************************************** + * TOOLHELP.C + * + * Contains the initalization and deinitialization code for the + * TOOLHELP DLL. + * + **************************************************************************/ + +#include "toolpriv.h" +#undef VERSION +#include <mmsystem.h> + +/* ----- Global variables ----- */ + WORD segKernel; + WORD wLibInstalled; + WORD wTHFlags; + HANDLE hMaster; + HANDLE hGDIHeap; + HANDLE hUserHeap; + WORD NEAR *npwExeHead; + WORD NEAR *npwTDBHead; + WORD NEAR *npwTDBCur; + DWORD NEAR *npdwSelTableStart; + WORD NEAR *npwSelTableLen; + FARPROC lpfnGetUserLocalObjType; + FARPROC lpfnFatalExitHook; + FARPROC lpfnNotifyHook; + LPFNUSUD lpfnUserSeeUserDo; + FARPROC lpfnGetFreeSystemResources; + FARPROC lpfntimeGetTime; + WORD wSel; + WORD wLRUCount; + char szKernel[] = "KERNEL"; + +/* ----- Import values ----- */ +#define FATALEXITHOOK MAKEINTRESOURCE(318) +#define GETUSERLOCALOBJTYPE MAKEINTRESOURCE(480) +#define USERSEEUSERDO MAKEINTRESOURCE(216) +#define HASGPHANDLER MAKEINTRESOURCE(338) +#define TOOLHELPHOOK MAKEINTRESOURCE(341) +#define GETFREESYSTEMRESOURCES MAKEINTRESOURCE(284) + + +/* ToolHelpLibMain + * Called by DLL startup code. + * Initializes TOOLHELP.DLL. + */ + +int PASCAL ToolHelpLibMain( + HANDLE hInstance, + WORD wDataSeg, + WORD wcbHeapSize, + LPSTR lpszCmdLine) +{ + HANDLE hKernel; + HANDLE hUser; + HANDLE hMMSys; + + /* Unless we say otherwise, the library is installed OK */ + wLibInstalled = TRUE; + + /* Do the KERNEL type-checking. Puts the results in global variables */ + KernelType(); + + /* If the KERNEL check failed (not in PMODE) return that the library did + * not correctly install but allow the load anyway. + */ + if (!wTHFlags) + { + wLibInstalled = FALSE; + + /* Return success anyway, just fails all API calls */ + return 1; + } + + /* Grab a selector. This is only necessary in Win30StdMode */ + if (wTHFlags & TH_WIN30STDMODE) + wSel = HelperGrabSelector(); + + /* Get the User and GDI heap handles if possible */ + hKernel = GetModuleHandle((LPSTR)szKernel); + hUser = GetModuleHandle("USER"); + hUserHeap = UserGdiDGROUP(hUser); + hGDIHeap = UserGdiDGROUP(GetModuleHandle("GDI")); + + /* Get all the functions we may need. These functions only exist in + * the 3.1 USER and KERNEL. + */ + if (!(wTHFlags & TH_WIN30)) + { + /* FatalExit hook */ + lpfnFatalExitHook = GetProcAddress(hKernel, FATALEXITHOOK); + + /* Internal USER routine to get head of class list */ + lpfnUserSeeUserDo = (LPFNUSUD)(FARPROC) + GetProcAddress(hUser, USERSEEUSERDO); + + /* Identifies objects on USER's local heap */ + lpfnGetUserLocalObjType = GetProcAddress(hUser, GETUSERLOCALOBJTYPE); + + /* Identifies parameter validation GP faults */ + lpfnPV = GetProcAddress(hKernel, HASGPHANDLER); + + /* See if the new TOOLHELP KERNEL hook is around */ + lpfnNotifyHook = (FARPROC) GetProcAddress(hKernel, TOOLHELPHOOK); + if (lpfnNotifyHook) + wTHFlags |= TH_GOODPTRACEHOOK; + + /* Get the USER system resources function */ + lpfnGetFreeSystemResources = (FARPROC) + GetProcAddress(hUser, GETFREESYSTEMRESOURCES); + } + + /* Make sure we don't ever call these in 3.0 */ + else + { + lpfnFatalExitHook = NULL; + lpfnUserSeeUserDo = NULL; + lpfnGetUserLocalObjType = NULL; + lpfnPV = NULL; + } + + /* Try to get the multimedia system timer function address */ + hMMSys = GetModuleHandle("MMSYSTEM"); + if (hMMSys) + { + TIMECAPS tc; + UINT (WINAPI* lpfntimeGetDevCaps)( + TIMECAPS FAR* lpTimeCaps, + UINT wSize); + + /* Call the timer API to see if the timer's really installed, + * and if it is, get the address of the get time function + */ + lpfntimeGetDevCaps = + GetProcAddress(hMMSys, MAKEINTRESOURCE(604)); + if ((*lpfntimeGetDevCaps)(&tc, sizeof (tc)) == TIMERR_NOERROR) + lpfntimeGetTime = + GetProcAddress(hMMSys, MAKEINTRESOURCE(607)); + } + + /* Return success */ + return 1; +} + + + diff --git a/private/mvdm/wow16/toolhelp/toolhelp.def b/private/mvdm/wow16/toolhelp/toolhelp.def new file mode 100644 index 000000000..0d6d6884a --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolhelp.def @@ -0,0 +1,45 @@ +LIBRARY TOOLHELP +DESCRIPTION 'TOOLHELP for WOW - Debug/Tool Helper library' +EXETYPE WINDOWS + +CODE PRELOAD FIXED +DATA PRELOAD FIXED SINGLE + +HEAPSIZE 512 + +EXPORTS + WEP @1 RESIDENTNAME ;Internal + GLOBALHANDLETOSEL @50 + GLOBALFIRST @51 + GLOBALNEXT @52 + GLOBALINFO @53 + GLOBALENTRYHANDLE @54 + GLOBALENTRYMODULE @55 + LOCALINFO @56 + LOCALFIRST @57 + LOCALNEXT @58 + MODULEFIRST @59 + MODULENEXT @60 + MODULEFINDNAME @61 + MODULEFINDHANDLE @62 + TASKFIRST @63 + TASKNEXT @64 + TASKFINDHANDLE @65 + STACKTRACEFIRST @66 + STACKTRACECSIPFIRST @67 + STACKTRACENEXT @68 + CLASSFIRST @69 + CLASSNEXT @70 + SYSTEMHEAPINFO @71 + MEMMANINFO @72 + NOTIFYREGISTER @73 + NOTIFYUNREGISTER @74 + INTERRUPTREGISTER @75 + INTERRUPTUNREGISTER @76 + TERMINATEAPP @77 + MEMORYREAD @78 + MEMORYWRITE @79 + TIMERCOUNT @80 + TASKSETCSIP @81 + TASKGETCSIP @82 + TASKSWITCH @83 diff --git a/private/mvdm/wow16/toolhelp/toolhelp.h b/private/mvdm/wow16/toolhelp/toolhelp.h new file mode 100644 index 000000000..25b918f80 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolhelp.h @@ -0,0 +1,469 @@ +/*****************************************************************************\ +* * +* toolhelp.h - toolhelp.dll functions, types, and definitions * +* * +* Version 1.0 * +* * +* NOTE: windows.h must be #included first * +* * +* Copyright (c) 1992, Microsoft Corp. All rights reserved. * +* * +\*****************************************************************************/ + +#ifndef _INC_TOOLHELP +#define _INC_TOOLHELP + +#ifndef RC_INVOKED +#pragma pack(1) /* Assume byte packing throughout */ +#endif + +#ifdef __cplusplus +extern "C" { /* Assume C declarations for C++ */ +#endif /* __cplusplus */ + +#ifndef _INC_WINDOWS /* If included with 3.0 headers... */ +#define LPCSTR LPSTR +#define WINAPI FAR PASCAL +#define CALLBACK FAR PASCAL +#define UINT WORD +#define HMODULE HANDLE +#define HINSTANCE HANDLE +#define HLOCAL HANDLE +#define HGLOBAL HANDLE +#define HTASK HANDLE +#endif /* _INC_WINDOWS */ + +/****** General symbols ******************************************************/ +#define MAX_DATA 11 +#define MAX_PATH 255 +#define MAX_MODULE_NAME 8 + 1 +#define MAX_CLASSNAME 255 + +/****** Global heap walking ***************************************************/ +typedef struct tagGLOBALINFO +{ + DWORD dwSize; + WORD wcItems; + WORD wcItemsFree; + WORD wcItemsLRU; +} GLOBALINFO; + +typedef struct tagGLOBALENTRY +{ + DWORD dwSize; + DWORD dwAddress; + DWORD dwBlockSize; + HGLOBAL hBlock; + WORD wcLock; + WORD wcPageLock; + WORD wFlags; + BOOL wHeapPresent; + HGLOBAL hOwner; + WORD wType; + WORD wData; + DWORD dwNext; + DWORD dwNextAlt; +} GLOBALENTRY; + +/* GlobalFirst()/GlobalNext() flags */ +#define GLOBAL_ALL 0 +#define GLOBAL_LRU 1 +#define GLOBAL_FREE 2 + +/* GLOBALENTRY.wType entries */ +#define GT_UNKNOWN 0 +#define GT_DGROUP 1 +#define GT_DATA 2 +#define GT_CODE 3 +#define GT_TASK 4 +#define GT_RESOURCE 5 +#define GT_MODULE 6 +#define GT_FREE 7 +#define GT_INTERNAL 8 +#define GT_SENTINEL 9 +#define GT_BURGERMASTER 10 + +/* If GLOBALENTRY.wType==GT_RESOURCE, the following is GLOBALENTRY.wData: */ +#define GD_USERDEFINED 0 +#define GD_CURSORCOMPONENT 1 +#define GD_BITMAP 2 +#define GD_ICONCOMPONENT 3 +#define GD_MENU 4 +#define GD_DIALOG 5 +#define GD_STRING 6 +#define GD_FONTDIR 7 +#define GD_FONT 8 +#define GD_ACCELERATORS 9 +#define GD_RCDATA 10 +#define GD_ERRTABLE 11 +#define GD_CURSOR 12 +#define GD_ICON 14 +#define GD_NAMETABLE 15 +#define GD_MAX_RESOURCE 15 + +/* GLOBALENTRY.wFlags */ +#define GF_PDB_OWNER 0x0100 /* Low byte is KERNEL flags */ + +BOOL WINAPI GlobalInfo(GLOBALINFO FAR* lpGlobalInfo); +BOOL WINAPI GlobalFirst(GLOBALENTRY FAR* lpGlobal, WORD wFlags); +BOOL WINAPI GlobalNext(GLOBALENTRY FAR* lpGlobal, WORD wFlags); +BOOL WINAPI GlobalEntryHandle(GLOBALENTRY FAR* lpGlobal, HGLOBAL hItem); +BOOL WINAPI GlobalEntryModule(GLOBALENTRY FAR* lpGlobal, HMODULE hModule, WORD wSeg); +WORD WINAPI GlobalHandleToSel(HGLOBAL hMem); + +/****** Local heap walking ***************************************************/ + +typedef struct tagLOCALINFO +{ + DWORD dwSize; + WORD wcItems; +} LOCALINFO; + +typedef struct tagLOCALENTRY +{ + DWORD dwSize; + HLOCAL hHandle; + WORD wAddress; + WORD wSize; + WORD wFlags; + WORD wcLock; + WORD wType; + WORD hHeap; + WORD wHeapType; + WORD wNext; +} LOCALENTRY; + +/* LOCALENTRY.wHeapType flags */ +#define NORMAL_HEAP 0 +#define USER_HEAP 1 +#define GDI_HEAP 2 + +/* LOCALENTRY.wFlags */ +#define LF_FIXED 1 +#define LF_FREE 2 +#define LF_MOVEABLE 4 + +/* LOCALENTRY.wType */ +#define LT_NORMAL 0 +#define LT_FREE 0xff +#define LT_GDI_PEN 1 /* LT_GDI_* is for GDI's heap */ +#define LT_GDI_BRUSH 2 +#define LT_GDI_FONT 3 +#define LT_GDI_PALETTE 4 +#define LT_GDI_BITMAP 5 +#define LT_GDI_RGN 6 +#define LT_GDI_DC 7 +#define LT_GDI_DISABLED_DC 8 +#define LT_GDI_METADC 9 +#define LT_GDI_METAFILE 10 +#define LT_GDI_MAX LT_GDI_METAFILE +#define LT_USER_CLASS 1 /* LT_USER_* is for USER's heap */ +#define LT_USER_WND 2 +#define LT_USER_STRING 3 +#define LT_USER_MENU 4 +#define LT_USER_CLIP 5 +#define LT_USER_CBOX 6 +#define LT_USER_PALETTE 7 +#define LT_USER_ED 8 +#define LT_USER_BWL 9 +#define LT_USER_OWNERDRAW 10 +#define LT_USER_SPB 11 +#define LT_USER_CHECKPOINT 12 +#define LT_USER_DCE 13 +#define LT_USER_MWP 14 +#define LT_USER_PROP 15 +#define LT_USER_LBIV 16 +#define LT_USER_MISC 17 +#define LT_USER_ATOMS 18 +#define LT_USER_LOCKINPUTSTATE 19 +#define LT_USER_HOOKLIST 20 +#define LT_USER_USERSEEUSERDOALLOC 21 +#define LT_USER_HOTKEYLIST 22 +#define LT_USER_POPUPMENU 23 +#define LT_USER_HANDLETABLE 32 +#define LT_USER_MAX LT_USER_HANDLETABLE + +BOOL WINAPI LocalInfo(LOCALINFO FAR* lpLocal, HGLOBAL hHeap); +BOOL WINAPI LocalFirst(LOCALENTRY FAR* lpLocal, HGLOBAL hHeap); +BOOL WINAPI LocalNext(LOCALENTRY FAR* lpLocal); + +/****** Stack Tracing ********************************************************/ + +typedef struct tagSTACKTRACEENTRY +{ + DWORD dwSize; + HTASK hTask; + WORD wSS; + WORD wBP; + WORD wCS; + WORD wIP; + HMODULE hModule; + WORD wSegment; + WORD wFlags; +} STACKTRACEENTRY; + +/* STACKTRACEENTRY.wFlags values */ +#define FRAME_FAR 0 +#define FRAME_NEAR 1 + +BOOL WINAPI StackTraceFirst(STACKTRACEENTRY FAR* lpStackTrace, HTASK hTask); +BOOL WINAPI StackTraceCSIPFirst(STACKTRACEENTRY FAR* lpStackTrace, + WORD wSS, WORD wCS, WORD wIP, WORD wBP); +BOOL WINAPI StackTraceNext(STACKTRACEENTRY FAR* lpStackTrace); + +/****** Module list walking **************************************************/ + +typedef struct tagMODULEENTRY +{ + DWORD dwSize; + char szModule[MAX_MODULE_NAME + 1]; + HMODULE hModule; + WORD wcUsage; + char szExePath[MAX_PATH + 1]; + WORD wNext; +} MODULEENTRY; + +BOOL WINAPI ModuleFirst(MODULEENTRY FAR* lpModule); +BOOL WINAPI ModuleNext(MODULEENTRY FAR* lpModule); +HMODULE WINAPI ModuleFindName(MODULEENTRY FAR* lpModule, LPCSTR lpstrName); +HMODULE WINAPI ModuleFindHandle(MODULEENTRY FAR* lpModule, HMODULE hModule); + +/****** Task list walking *****************************************************/ + +typedef struct tagTASKENTRY +{ + DWORD dwSize; + HTASK hTask; + HTASK hTaskParent; + HINSTANCE hInst; + HMODULE hModule; + WORD wSS; + WORD wSP; + WORD wStackTop; + WORD wStackMinimum; + WORD wStackBottom; + WORD wcEvents; + HGLOBAL hQueue; + char szModule[MAX_MODULE_NAME + 1]; + WORD wPSPOffset; + HANDLE hNext; +} TASKENTRY; + +BOOL WINAPI TaskFirst(TASKENTRY FAR* lpTask); +BOOL WINAPI TaskNext(TASKENTRY FAR* lpTask); +BOOL WINAPI TaskFindHandle(TASKENTRY FAR* lpTask, HTASK hTask); +DWORD WINAPI TaskSetCSIP(HTASK hTask, WORD wCS, WORD wIP); +DWORD WINAPI TaskGetCSIP(HTASK hTask); +BOOL WINAPI TaskSwitch(HTASK hTask, DWORD dwNewCSIP); + +/****** Window Class enumeration **********************************************/ + +typedef struct tagCLASSENTRY +{ + DWORD dwSize; + HMODULE hInst; /* This is really an hModule */ + char szClassName[MAX_CLASSNAME + 1]; + WORD wNext; +} CLASSENTRY; + +BOOL WINAPI ClassFirst(CLASSENTRY FAR* lpClass); +BOOL WINAPI ClassNext(CLASSENTRY FAR* lpClass); + +/****** Information functions *************************************************/ + +typedef struct tagMEMMANINFO +{ + DWORD dwSize; + DWORD dwLargestFreeBlock; + DWORD dwMaxPagesAvailable; + DWORD dwMaxPagesLockable; + DWORD dwTotalLinearSpace; + DWORD dwTotalUnlockedPages; + DWORD dwFreePages; + DWORD dwTotalPages; + DWORD dwFreeLinearSpace; + DWORD dwSwapFilePages; + WORD wPageSize; +} MEMMANINFO; + +BOOL WINAPI MemManInfo(MEMMANINFO FAR* lpEnhMode); + +typedef struct tagSYSHEAPINFO +{ + DWORD dwSize; + WORD wUserFreePercent; + WORD wGDIFreePercent; + HGLOBAL hUserSegment; + HGLOBAL hGDISegment; +} SYSHEAPINFO; + +BOOL WINAPI SystemHeapInfo(SYSHEAPINFO FAR* lpSysHeap); + +/****** Interrupt Handling ****************************************************/ + +/* Hooked interrupts */ +#define INT_DIV0 0 +#define INT_1 1 +#define INT_3 3 +#define INT_UDINSTR 6 +#define INT_STKFAULT 12 +#define INT_GPFAULT 13 +#define INT_BADPAGEFAULT 14 +#define INT_CTLALTSYSRQ 256 + +/* TOOLHELP Interrupt callbacks registered with InterruptRegister should + * always be written in assembly language. The stack frame is not + * compatible with high level language conventions. + * + * This stack frame looks as follows to the callback. All registers + * should be preserved across this callback to allow restarting fault. + * ------------ + * | Flags | [SP + 0Eh] + * | CS | [SP + 0Ch] + * | IP | [SP + 0Ah] + * | Handle | [SP + 08h] + * |Exception#| [SP + 06h] + * | AX | [SP + 04h] AX Saved to allow MakeProcInstance + * | Ret CS | [SP + 02h] + * SP---> | Ret IP | [SP + 00h] + * ------------ + */ +BOOL WINAPI InterruptRegister(HTASK hTask, FARPROC lpfnIntCallback); +BOOL WINAPI InterruptUnRegister(HTASK hTask); + +/* Notifications: + * When a notification callback is called, two parameters are passed + * in: a WORD, wID, and another DWORD, dwData. wID is one of + * the values NFY_* below. Callback routines should ignore unrecog- + * nized values to preserve future compatibility. Callback routines + * are also passed a dwData value. This may contain data or may be + * a FAR pointer to a structure, or may not be used depending on + * which notification is being received. + * + * In all cases, if the return value of the callback is TRUE, the + * notification will NOT be passed on to other callbacks. It has + * been handled. This should be used sparingly and only with certain + * notifications. Callbacks almost always return FALSE. + */ + +/* NFY_UNKNOWN: An unknown notification has been returned from KERNEL. Apps + * should ignore these. + */ +#define NFY_UNKNOWN 0 + +/* NFY_LOADSEG: dwData points to a NFYLOADSEG structure */ +#define NFY_LOADSEG 1 +typedef struct tagNFYLOADSEG +{ + DWORD dwSize; + WORD wSelector; + WORD wSegNum; + WORD wType; /* Low bit set if data seg, clear if code seg */ + WORD wcInstance; /* Instance count ONLY VALID FOR DATA SEG */ + LPCSTR lpstrModuleName; +} NFYLOADSEG; + +/* NFY_FREESEG: LOWORD(dwData) is the selector of the segment being freed */ +#define NFY_FREESEG 2 + +/* NFY_STARTDLL: dwData points to a NFYLOADSEG structure */ +#define NFY_STARTDLL 3 +typedef struct tagNFYSTARTDLL +{ + DWORD dwSize; + HMODULE hModule; + WORD wCS; + WORD wIP; +} NFYSTARTDLL; + +/* NFY_STARTTASK: dwData is the CS:IP of the start address of the task */ +#define NFY_STARTTASK 4 + +/* NFY_EXITTASK: The low byte of dwData contains the program exit code */ +#define NFY_EXITTASK 5 + +/* NFY_DELMODULE: LOWORD(dwData) is the handle of the module to be freed */ +#define NFY_DELMODULE 6 + +/* NFY_RIP: dwData points to a NFYRIP structure */ +#define NFY_RIP 7 +typedef struct tagNFYRIP +{ + DWORD dwSize; + WORD wIP; + WORD wCS; + WORD wSS; + WORD wBP; + WORD wExitCode; +} NFYRIP; + +/* NFY_TASKIN: No data. Callback should do GetCurrentTask() */ +#define NFY_TASKIN 8 + +/* NFY_TASKOUT: No data. Callback should do GetCurrentTask() */ +#define NFY_TASKOUT 9 + +/* NFY_INCHAR: Return value from callback is used. If NULL, mapped to 'i' */ +#define NFY_INCHAR 10 + +/* NFY_OUTSTR: dwData points to the string to be displayed */ +#define NFY_OUTSTR 11 + +/* NFY_LOGERROR: dwData points to a NFYLOGERROR struct */ +#define NFY_LOGERROR 12 +typedef struct tagNFYLOGERROR +{ + DWORD dwSize; + UINT wErrCode; + void FAR* lpInfo; /* Error code-dependent */ +} NFYLOGERROR; + +/* NFY_LOGPARAMERROR: dwData points to a NFYLOGPARAMERROR struct */ +#define NFY_LOGPARAMERROR 13 +typedef struct tagNFYLOGPARAMERROR +{ + DWORD dwSize; + UINT wErrCode; + FARPROC lpfnErrorAddr; + void FAR* FAR* lpBadParam; +} NFYLOGPARAMERROR; + +/* NotifyRegister() flags */ +#define NF_NORMAL 0 +#define NF_TASKSWITCH 1 +#define NF_RIP 2 + +typedef BOOL (CALLBACK* LPFNNOTIFYCALLBACK)(WORD wID, DWORD dwData); + +BOOL WINAPI NotifyRegister(HTASK hTask, LPFNNOTIFYCALLBACK lpfn, WORD wFlags); +BOOL WINAPI NotifyUnRegister(HTASK hTask); + +/****** Miscellaneous *********************************************************/ + +void WINAPI TerminateApp(HTASK hTask, WORD wFlags); + +/* TerminateApp() flag values */ +#define UAE_BOX 0 +#define NO_UAE_BOX 1 + +DWORD WINAPI MemoryRead(WORD wSel, DWORD dwOffset, void FAR* lpBuffer, DWORD dwcb); +DWORD WINAPI MemoryWrite(WORD wSel, DWORD dwOffset, void FAR* lpBuffer, DWORD dwcb); + +typedef struct tagTIMERINFO +{ + DWORD dwSize; + DWORD dwmsSinceStart; + DWORD dwmsThisVM; +} TIMERINFO; + +BOOL WINAPI TimerCount(TIMERINFO FAR* lpTimer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#ifndef RC_INVOKED +#pragma pack() /* Revert to default packing */ +#endif + +#endif /* !_INC_TOOLHELP */ diff --git a/private/mvdm/wow16/toolhelp/toolhelp.inc b/private/mvdm/wow16/toolhelp/toolhelp.inc new file mode 100644 index 000000000..37537623b --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolhelp.inc @@ -0,0 +1,292 @@ +;************************************************************************** +;* TOOLHELP.INC +;* +;* Assembly-language public include file for TOOLHELP.DLL +;* +;************************************************************************** + +;** General symbol values +MAX_DATA EQU 11 +MAX_PATH EQU 255 +MAX_MODULE_NAME EQU 8 + 1 ;Leave room for the zero and padding +MAX_CLASSNAME EQU 255 + +; ----- Global heap walking ----- + +GLOBALENTRY STRUC +ge_dwSize DD ? ;Structure version +ge_dwAddress DD ? ;This block's address +ge_dwBlockSize DD ? ;This block's size +ge_hBlock DW ? ;This block's handle +ge_wcLock DW ? ;Lock count +ge_wcPageLock DW ? ;Page lock count +ge_wFlags DW ? ;Block flags +ge_wHeapPresent DW ? ;Size of available local heap +ge_hOwner DW ? ;Owner handle +ge_wType DW ? ;Block type +ge_wData DW ? ;Type-specific data +ge_dwNext DD ? ;Pointer to next block +ge_dwNextAlt DD ? ;Next free/LRU block in chain +GLOBALENTRY ENDS + +; ** GlobalFirst()/GlobalNext() flags +GLOBAL_ALL EQU 0 +GLOBAL_LRU EQU 1 +GLOBAL_FREE EQU 2 + +; ** GLOBALENTRY.ge_wType entries +GT_UNKNOWN EQU 0 +GT_DGROUP EQU 1 +GT_DATA EQU 2 +GT_CODE EQU 3 +GT_TASK EQU 4 +GT_RESOURCE EQU 5 +GT_MODULE EQU 6 +GT_FREE EQU 7 +GT_INTERNAL EQU 8 +GT_SENTINEL EQU 9 +GT_BURGERMASTER EQU 10 + +; ** if GLOBALENTRY.wType==GT_RESOURCE, the following is GLOBALENTRY.wData: +GD_USERDEFINED EQU 0 +GD_CURSORCOMPONENT EQU 1 +GD_BITMAP EQU 2 +GD_ICONCOMPONENT EQU 3 +GD_MENU EQU 4 +GD_DIALOG EQU 5 +GD_STRING EQU 6 +GD_FONTDIR EQU 7 +GD_FONT EQU 8 +GD_ACCELERATORS EQU 9 +GD_RCDATA EQU 10 +GD_ERRTABLE EQU 11 +GD_CURSOR EQU 12 +GD_ICON EQU 14 +GD_NAMETABLE EQU 15 +GD_MAX_RESOURCE EQU 15 + +;** GLOBALENTRY.wFlags +GF_PDB_OWNER EQU 100h ;Low byte is KERNEL flags + +; ----- Local heap walking ----- + +LOCALENTRY STRUC +le_dwSize DD ? +le_hHandle DW ? +le_wAddress DW ? +le_wSize DW ? +le_wFlags DW ? +le_wcLock DW ? +le_wType DW ? +le_hHeap DW ? +le_wHeapType DW ? +le_wNext DW ? +LOCALENTRY ENDS + +; ** LOCALENTRY.wHeapType flags +NORMAL_HEAP EQU 0 +USER_HEAP EQU 1 +GDI_HEAP EQU 2 + +;** LOCALENTRY.wFlags +LF_FIXED EQU 1 +LF_FREE EQU 2 +LF_MOVEABLE EQU 4 + +;** LOCALENTRY.wType +LT_NORMAL EQU 0 +LT_GDI_PEN EQU 1 +LT_GDI_BRUSH EQU 2 +LT_GDI_FONT EQU 3 +LT_GDI_PALETTE EQU 4 +LT_GDI_BITMAP EQU 5 +LT_GDI_RGN EQU 6 +LT_GDI_DC EQU 7 +LT_GDI_DISABLED_DC EQU 8 +LT_GDI_METADC EQU 9 +LT_GDI_METAFILE EQU 10 +LT_GDI_MAX EQU LT_GDI_METAFILE +LT_FREE EQU 0ffh + +; ----- Stack tracing ----- + +STACKTRACEENTRY STRUC +st_dwSize DD ? +st_hTask DW ? +st_wSS DW ? +st_wBP DW ? +st_wCS DW ? +st_wIP DW ? +st_hModule DW ? +st_wSegment DW ? +st_wFlags DW ? +STACKTRACEENTRY ENDS + +;** STACKTRACEENTRY.wFlags values +FRAME_FAR EQU 0 +FRAME_NEAR EQU 1 + +; ----- Module list walking ----- + +MODULEENTRY STRUC +me_dwSize DD ? +me_szModule DB MAX_MODULE_NAME + 1 DUP(?) +me_hModule DW ? +me_wcUsage DW ? +me_szExePath DB MAX_PATH + 1 + 1 DUP(?) +me_wNext DW ? +MODULEENTRY ENDS + +; ----- Task list walking ----- + +TASKENTRY STRUC +te_dwSize DD ? +te_hTask DW ? +te_hTaskParent DW ? +te_hInst DW ? +te_hModule DW ? +te_wSS DW ? +te_wSP DW ? +te_wStackTop DW ? +te_wStackMinimum DW ? +te_wStackBottom DW ? +te_wcEvents DW ? +te_hQueue DW ? +te_szModule DB MAX_MODULE_NAME + 1 DUP(?) +te_wPSPOffset DW ? +te_hNext DW ? +TASKENTRY ENDS + +;** TaskSwitch() return values +TS_ERROR EQU 0 +TS_NEW EQU 1 +TS_OLD EQU 2 + +; ----- Window Class enumeration ----- + +CLASSENTRY STRUC +ce_dwSize DD ? +ce_hInst DW ? +ce_szClassName DB MAX_CLASSNAME + 1 DUP(?) +ce_wNext DW ? +CLASSENTRY ENDS + +; ----- Information functions ----- + +VMEMMANINFO STRUC +vmm_dwSize DD ? +vmm_dwLargestFreeBlock DD ? +vmm_dwMaxPagesAvailable DD ? +vmm_dwMaxPagesLockable DD ? +vmm_dwTotalLinearSpace DD ? +vmm_dwTotalUnlockedPages DD ? +vmm_dwFreePages DD ? +vmm_dwTotalPages DD ? +vmm_dwFreeLinearSpace DD ? +vmm_dwSwapFilePages DD ? +vmm_wPageSize DW ? +VMEMMANINFO ENDS + +SYSHEAPINFO STRUC +si_dwSize DD ? +si_wUserFreePercent DW ? +si_wGDIFreePercent DW ? +si_hUserSegment DW ? +si_hGDISegment DW ? +SYSHEAPINFO ENDS + +; ----- Notifications ----- + +;* NFY_UNKNOWN: An unknown notification has been returned from KERNEL. Apps +;* should ignore these. +;* +NFY_UNKNOWN EQU 0 + +;** NFY_LOADSEG: dwData points to a NFYLOADSEG structure +NFY_LOADSEG EQU 1 +NFYLOADSEG STRUC +nls_dwSize DD ? +nls_wSelector DW ? +nls_wSegNum DW ? +nls_wType DW ? ;Low bit set for DATA, clear for CODE +nls_wcInstance DW ? ;Instance count ONLY VALID FOR DATA! +nls_lpstrModuleName DD ? +NFYLOADSEG ENDS + +;** NFY_FREESEG: LOWORD(dwData) is the selector of the segment being freed +NFY_FREESEG EQU 2 + +;** NFY_STARTDLL: dwData points to a NFYSTARTDLL structure +NFY_STARTDLL EQU 3 +NFYSTARTDLL STRUC +nsd_dwSize DD ? +nsd_hModule DW ? +nsd_wCS DW ? +nsd_wIP DW ? +NFYSTARTDLL ENDS + +;** NFY_STARTTASK: dwData is the CS:IP of the task start address +NFY_STARTTASK EQU 4 + +;** NFY_EXITTASK: The low byte of dwData contains the program exit code +NFY_EXITTASK EQU 5 + +;** NFY_DELMODULE: LOWORD(dwData) is the handle of the module to be freed +NFY_DELMODULE EQU 6 + +;** NFY_RIP: LOWORD(dwData) is the fatal exit code +;** NFY_RIP: dwData points to a NFYRIP structure +NFY_RIP EQU 7 +NFYRIP STRUC +nrp_dwSize DD ? +nrp_wIP DW ? +nrp_wCS DW ? +nrp_wSS DW ? +nrp_wBP DW ? +nrp_wExitCode DW ? +NFYRIP ENDS + +;** NFY_TASKIN: No data. Callback should do GetCurrentTask() +NFY_TASKIN EQU 8 + +;** NFY_TASKOUT: No data. Callback should do GetCurrentTask() +NFY_TASKOUT EQU 9 + +;** NFY_INCHAR: Return value of callback is used. If FALSE, mapped to 'i' +NFY_INCHAR EQU 10 + +;** NFY_OUTSTR: dwData points to the string to be displayed +NFY_OUTSTR EQU 11 + +;** NFY_LOGERROR: dwData points to a NFYLOGERROR struct +NFY_LOGERROR EQU 12 +NFYLOGERROR STRUC +nle_dwSize DD ? +nle_wErrCode DW ? +nle_lpInfo DD ? ;Error-code dependent +NFYLOGERROR ENDS + +;** NFY_LOGPARAMERROR: dwData points to a NFYLOGPARAMERROR struct +NFY_LOGPARAMERROR EQU 13 +NFYLOGPARAMERROR STRUC +nlp_dwSize DD ? +nlp_wErrCode DW ? +nlp_lpfnErrorAddr DD ? +nlp_lpBadParam DD ? +NFYLOGPARAMERROR ENDS + +;** NotifyRegister() flags +NF_NORMAL EQU 0 +NF_TASKSWITCH EQU 1 +NF_RIP EQU 2 + +;** TerminateApp() flag values +UAE_BOX EQU 0 +NO_UAE_BOX EQU 1 + +TIMERINFO STRUC +ti_dwSize DD ? +ti_dwmsSinceStart DD ? +ti_dwmsThisVM DD ? +TIMERINFO ENDS + diff --git a/private/mvdm/wow16/toolhelp/toolhelp.rcv b/private/mvdm/wow16/toolhelp/toolhelp.rcv new file mode 100644 index 000000000..014720674 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolhelp.rcv @@ -0,0 +1,14 @@ +/********************************************************************/ +/* TOOLHELP.RCV */ +/* Version control data generated from layouts.dat */ +/********************************************************************/ +#include <version.h> + + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Windows Debug/Tool helper library" +#define VER_INTERNALNAME_STR "TOOLHELP" +#define VER_ORIGINALFILENAME_STR "TOOLHELP.DLL" + +#include <common.ver> diff --git a/private/mvdm/wow16/toolhelp/toolpriv.h b/private/mvdm/wow16/toolhelp/toolpriv.h new file mode 100644 index 000000000..f14f613aa --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolpriv.h @@ -0,0 +1,220 @@ +/************************************************************************** + * TOOLPRIV.H + * + * Private header file for routines in the tool helper library + * + **************************************************************************/ + +#ifndef TOOLPRIV_H +#define TOOLPRIV_H + +#define BUILDDLL + +#include <windows.h> +#include "toolhelp.h" + +/* ----- Symbols ----- */ +#define VERSION 1 +#ifndef NOEXPORT +#define NOEXPORT static +#endif +#define TOOLHELPAPI WINAPI + +#define FAULT_ACTIVE 2 +#define NUM_EXCEPTIONS 3 + +/* wTHFlag values */ +#define TH_KERNEL_286 1 +#define TH_KERNEL_386 2 +#define TH_WIN30 4 +#define TH_WIN30STDMODE 8 /* THWIN30STDMODE set if TH_WIN30 & Std mode */ +#define TH_GOODPTRACEHOOK 16 +#define TH_GOTOLDPTRACE 32 + +/* ----- Code macros ----- */ +#define MAKEFARPTR(s, o) ((void FAR *)(((WORD)(o)) | \ + (((DWORD)(WORD)(s)) << 16))) + +/* ----- Types ----- */ + +typedef struct tagNOTIFYSTRUCT +{ + struct tagNOTIFYSTRUCT *pNext; + HANDLE hTask; + LPFNNOTIFYCALLBACK lpfn; + WORD wFlags; +} NOTIFYSTRUCT; + +typedef void (FAR PASCAL *LPFNCALLBACK)(void); + +typedef struct tagINTERRUPT +{ + struct tagINTERRUPT *pNext; + HANDLE hTask; + LPFNCALLBACK lpfn; +} INTERRUPT; + +typedef struct tagSIGNAL +{ + struct tagSIGNAL *pNext; + HANDLE hTask; + LPFNCALLBACK lpfn; + LPFNCALLBACK lpfnOld; +} SIGNAL; + +typedef LONG (FAR PASCAL *LPFNUSUD)( + WORD wID, + WORD wParam, + LONG lParam); + +/* ----- TOOLHELP global variables ----- */ + extern WORD segKernel; + extern WORD wLibInstalled; + extern HANDLE hMaster; + extern WORD wTHFlags; + extern HANDLE hUserHeap; + extern HANDLE hGDIHeap; + extern WORD NEAR *npwExeHead; + extern WORD NEAR *npwTDBHead; + extern WORD NEAR *npwTDBCur; + extern DWORD NEAR *npdwSelTableStart; + extern WORD NEAR *npwSelTableLen; + extern WORD wNotifyInstalled; + extern NOTIFYSTRUCT NEAR *npNotifyHead; + extern WORD wIntInstalled; + extern INTERRUPT NEAR *npIntHead; + extern FARPROC lpfnGetUserLocalObjType; + extern FARPROC lpfnFatalExitHook; + extern FARPROC PASCAL lpfnPV; + extern FARPROC lpfnNotifyHook; + extern FARPROC lpfnGetFreeSystemResources; + extern FARPROC lpfntimeGetTime; + extern LPFNUSUD lpfnUserSeeUserDo; + extern WORD wSignalInstalled; + extern SIGNAL NEAR *npSignalHead; + extern NOTIFYSTRUCT NEAR* npNotifyNext; + extern WORD wLRUCount; + +/* ----- Private function prototypes ----- */ + + void PASCAL KernelType(void); + + DWORD PASCAL Walk386First( + WORD wFlags); + + WORD PASCAL Walk386Count( + WORD wFlags); + + void PASCAL Walk386( + DWORD dwBlock, + GLOBALENTRY FAR *lpGlobal, + WORD wFlags); + + DWORD PASCAL Walk386Handle( + HANDLE hBlock); + + WORD PASCAL WalkLoc386Count( + HANDLE hHeap); + + HANDLE PASCAL WalkLoc386First( + HANDLE hHeap); + + void PASCAL WalkLoc386( + WORD wBlock, + LOCALENTRY FAR *lpLocal, + HANDLE hHeap); + + DWORD PASCAL Walk286First( + WORD wFlags); + + WORD PASCAL Walk286Count( + WORD wFlags); + + void PASCAL Walk286( + DWORD dwBlock, + GLOBALENTRY FAR *lpGlobal, + WORD wFlags); + + DWORD PASCAL Walk286Handle( + HANDLE hBlock); + + WORD PASCAL WalkLoc286Count( + HANDLE hHeap); + + HANDLE PASCAL WalkLoc286First( + HANDLE hHeap); + + void PASCAL WalkLoc286( + WORD wBlock, + LOCALENTRY FAR *lpLocal, + HANDLE hHeap); + + BOOL PASCAL TaskInfo( + TASKENTRY FAR *lpTask, + WORD wBlock); + + WORD PASCAL StackFrameFirst( + STACKTRACEENTRY FAR *lpStack, + HANDLE hTDB); + + BOOL PASCAL StackFrameNext( + STACKTRACEENTRY FAR *lpStack); + + HANDLE PASCAL UserGdiDGROUP( + HANDLE hModule); + + DWORD PASCAL UserGdiSpace( + HANDLE hData); + + WORD PASCAL HelperVerifySeg( + WORD wSeg, + WORD wcb); + + WORD PASCAL HelperHandleToSel( + HANDLE h); + + void PASCAL HelperGlobalType( + GLOBALENTRY FAR *lpGlobal); + + WORD PASCAL HelperGrabSelector(void); + + void PASCAL HelperReleaseSelector( + WORD wSelector); + + void PASCAL UserGdiType( + LOCALENTRY FAR *lpLocal); + + BOOL PASCAL NotifyInit(void); + + void PASCAL NotifyUnInit(void); + + BOOL PASCAL NotifyIsHooked( + HANDLE hTask); + + BOOL PASCAL InterruptInit(void); + + void PASCAL InterruptUnInit(void); + + BOOL PASCAL InterruptIsHooked( + HANDLE hTask); + + BOOL PASCAL SignalRegister( + HANDLE hTask); + + BOOL PASCAL SignalUnRegister( + HANDLE hTask); + + void FAR PASCAL HelperSignalProc(void); + + DWORD PASCAL HelperSetSignalProc( + HANDLE hTask, + DWORD lpfn); + + BOOL PASCAL ClassInfo( + CLASSENTRY FAR *lpClass, + WORD wOffset); + + DWORD PASCAL HelperSegLen( + WORD wSeg); + +#endif diff --git a/private/mvdm/wow16/toolhelp/toolpriv.inc b/private/mvdm/wow16/toolhelp/toolpriv.inc new file mode 100644 index 000000000..1001cfd99 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/toolpriv.inc @@ -0,0 +1,112 @@ +;************************************************************************** +;* TOOLPRIV.INC +;* +;* Private assembly-language include file for modules assembled as a +;* part of TOOLHELP.DLL. +;* +;************************************************************************** + +;** CMACROS.INC variables for correct assembly +DOS5 = 1 +PMODE = 1 +?WIN = 0 +?PLM = 1 + +.286p + + INCLUDE CMACROS.INC + INCLUDE TOOLHELP.INC + +externA __WinFlags + +;** TOOLHELP global variables +?PLM = 0 +externW segKernel +externW wLibInstalled +externW hMaster +externW wTHFlags +externW npwExeHead +externW npwTDBHead +externW npwTDBCur +externW hUserHeap +externW hGDIHeap +externW npdwSelTableStart +externW npwSelTableLen +externW wNotifyInstalled +externW npNotifyHead +externW wIntInstalled +externW npIntHead +externD lpfnGetUserLocalObjType +externD lpfnFatalExitHook +externD lpfnUserSeeUserDo +externD lpfnNotifyHook +externD lpfnGetFreeSystemResources +externD lpfntimeGetTime; +externW wSel +externW wSignalInstalled +externW npSignalHead +externW npNotifyNext +externW wLRUCount +?PLM = 1 + +;** Symbols +TRUE = 1 +FALSE = 0 + +KERNEL_286 EQU 1 +KERNEL_386 EQU 2 + +FAULT_ACTIVE EQU 2 + +NUM_EXCEP EQU 3 + +;** wTHFlag values +TH_KERNEL_286 EQU 1 +TH_KERNEL_386 EQU 2 +TH_WIN30 EQU 4 +TH_WIN30STDMODE EQU 8 ;THWIN30STDMODE set if TH_WIN30 & Std mode +TH_GOODPTRACEHOOK EQU 16 +TH_GOTOLDPTRACE EQU 32 + +;** Notification values +NI_INCHAR EQU 01h +NI_OUTSTR EQU 12h +NI_LOADSEG EQU 50h +NI_FREESEG EQU 52h +NI_STARTTASK EQU 59h +NI_EXITCALL EQU 62h +NI_LOADDLL EQU 64h +NI_DELMODULE EQU 65h +NI_TASKOUT EQU 0dh +NI_TASKIN EQU 0eh +NI_CTLALTSYSRQ EQU 63h +NI_LOGERROR EQU 66h +NI_LOGPARAMERROR EQU 67h + +;** Structures + +NOTIFYSTRUCT STRUC +ns_pNext DW ? +ns_hTask DW ? +ns_lpfn DD ? +ns_wFlags DW ? +NOTIFYSTRUCT ENDS + +INTERRUPT STRUC +i_pNext DW ? +i_hTask DW ? +i_lpfn DD ? +INTERRUPT ENDS + +SIGNAL STRUC +si_pNext DW ? +si_hTask DW ? +si_lpfn DD ? +si_lpfnOld DD ? +SIGNAL ENDS + +;** Macros +PubLabel MACRO PLabel + labelNP PLabel + PUBLIC PLabel +ENDM diff --git a/private/mvdm/wow16/toolhelp/usergdi1.c b/private/mvdm/wow16/toolhelp/usergdi1.c new file mode 100644 index 000000000..05aac84ce --- /dev/null +++ b/private/mvdm/wow16/toolhelp/usergdi1.c @@ -0,0 +1,77 @@ +/************************************************************************** + * USERGDI1.C + * + * Returns information about USER.EXE and GDI.EXE + * + **************************************************************************/ + +#include "toolpriv.h" + +/* SystemHeapInfo + * Returns information about USER's and GDI's heaps + */ + +BOOL TOOLHELPAPI SystemHeapInfo( + SYSHEAPINFO FAR* lpSysHeap) +{ + MODULEENTRY ModuleEntry; +#ifndef WOW + DWORD dw; + WORD wFreeK; + WORD wMaxHeapK; +#endif + + /* Check the structure version number and pointer */ + if (!wLibInstalled || !lpSysHeap || + lpSysHeap->dwSize != sizeof (SYSHEAPINFO)) + return FALSE; + + /* Find the user data segment */ + ModuleEntry.dwSize = sizeof (MODULEENTRY); + lpSysHeap->hUserSegment = + UserGdiDGROUP(ModuleFindName(&ModuleEntry, "USER")); + lpSysHeap->hGDISegment = + UserGdiDGROUP(ModuleFindName(&ModuleEntry, "GDI")); + +#ifndef WOW + /* We get the information about the heap percentages differently in + * 3.0 and 3.1 + */ + if ((wTHFlags & TH_WIN30) || !lpfnGetFreeSystemResources) + { + /* Get the space information about USER's heap */ + dw = UserGdiSpace(lpSysHeap->hUserSegment); + wFreeK = LOWORD(dw) / 1024; + wMaxHeapK = HIWORD(dw) / 1024; + if (wMaxHeapK) + lpSysHeap->wUserFreePercent = wFreeK * 100 / wMaxHeapK; + else + lpSysHeap->wUserFreePercent = 0; + + /* Get the space information about GDI's heap */ + dw = UserGdiSpace(lpSysHeap->hGDISegment); + wFreeK = LOWORD(dw) / 1024; + wMaxHeapK = HIWORD(dw) / 1024; + if (wMaxHeapK) + lpSysHeap->wGDIFreePercent = wFreeK * 100 / wMaxHeapK; + else + lpSysHeap->wGDIFreePercent = 0; + } + + /* Get the information from USER in 3.1 */ + else + { + lpSysHeap->wUserFreePercent = + (*(WORD (FAR PASCAL *)(WORD))lpfnGetFreeSystemResources)(2); + lpSysHeap->wGDIFreePercent = + (*(WORD (FAR PASCAL *)(WORD))lpfnGetFreeSystemResources)(1); + } +#else + + lpSysHeap->wUserFreePercent = GetFreeSystemResources(GFSR_USERRESOURCES); + lpSysHeap->wGDIFreePercent = GetFreeSystemResources(GFSR_GDIRESOURCES); + +#endif + + return TRUE; +} diff --git a/private/mvdm/wow16/toolhelp/usergdi2.asm b/private/mvdm/wow16/toolhelp/usergdi2.asm new file mode 100644 index 000000000..35d72aba6 --- /dev/null +++ b/private/mvdm/wow16/toolhelp/usergdi2.asm @@ -0,0 +1,209 @@ +;*************************************************************************** +;* USERGDI2.ASM +;* +;* Assembly routines used in computing heap space remaining for +;* USER, GDI, and any other heaps. +;* +;*************************************************************************** + + INCLUDE TOOLPRIV.INC +SWAPPRO = 0 +PMODE32 = 0 +PMODE = 1 + INCLUDE WINKERN.INC + INCLUDE NEWEXE.INC + +;** This slimy thing is from GDIOBJ.INC and is subtracted from the +;** object type nunbers only in 3.1 +LT_GDI_BASE EQU ('G' or ('O' * 256)) - 1 + +;** External functions + +externNP HelperVerifyLocHeap +externNP HelperHandleToSel + +;** Functions + +sBegin CODE + assumes CS,CODE + assumes DS,DATA + +.286p + +; UserGdiDGROUP +; Returns a handle to the DGROUP segment for a given module +; +; HANDLE UserGdiDGROUP( +; HANDLE hModule) + +cProc UserGdiDGROUP, <PUBLIC,NEAR>, <di,si> + parmW hModule +cBegin + mov ax,hModule ;Get the handle + cCall HelperHandleToSel, <ax> ;Convert to a selector + mov es,ax ;Point with ES for this + xor ax,ax ;Prepare to return NULL + cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database + jnz UGD_End ;It isn't so get out + mov bx,es:[ne_pautodata] ;Point to the segment table entry + mov ax,es:[bx].ns_handle ;Get the handle from the table + cCall HelperHandleToSel, <ax> ;Convert to a selector for return +UGD_End: +cEnd + + +; UserGdiSpace +; This function was stolen from KERNEL where it is used to compute +; the space remaining in the USER and GDI heaps. It actually works +; on any local heap. +; +; DWORD UserGdiSpace( +; HANDLE hData) +; HIWORD of return is maximum size of heap (64K less statics, etc.) +; LOWORD of return is space remaining on heap + +cProc UserGdiSpace, <PUBLIC,NEAR>, <di,si,ds> + parmW hData +cBegin + ;** Count the free space in this heap. First: Is this heap valid? + mov ax,hData ;Get the heap selector + cCall HelperVerifyLocHeap ;Call the verify routine + mov ax,0 ;In case we jump -- set error + mov dx,0 ; Use MOV to not mess up carry + jc UGS_Exit ;No valid local heap!! + + ;** Loop through all local blocks, adding free space + cCall HelperHandleToSel, <hData> ;Convert to selector + mov ds,ax ;Point to the segment + mov di,ds:[6] ;Get pHeapInfo + mov di,[di].hi_first ;First arena header + mov di,[di].la_free_next ;First free block +UGS_Loop: + add ax,[di].la_size ;Add in size of this block + sub ax,SIZE LocalArenaFree ;Less block overhead + mov si,[di].la_free_next ;Get next free block + or si,si ;NULL? + jz UGS_Break ;Yes, say we're done + cmp si,di ;Last block? (points to self) + mov di,si ;Save for next time around + jnz UGS_Loop ;Not last block so loop some more +UGS_Break: + + ;** We have the size of the local heap + mov si,ax ;Save the size + mov cx,ds ;Get the selector in a non-segreg + lsl ax,cx ;Get the size of the segment + neg ax ;64K - segment size + add ax,si ;Add in the free holes in the heap + mov dx,-1 ;Compute the max size of heap + sub dx,ds:[6] ; which is 64K less statics + +UGS_Exit: + +cEnd + + +; UserGdiType +; +; Tries to compute the type of local heap block if possible +; Prototype: +; +; void PASCAL UserGdiType( +; LOCALENTRY FAR *lpLocal) + +cProc UserGdiType, <PUBLIC,NEAR>, <si,di> + parmD lpLocal +cBegin + ;** Get info from our static variables + mov ax,_DATA ;Get the variables first + mov ds,ax ;Point to our DS + mov bx,hUserHeap ;BX=User's heap block + mov cx,hGDIHeap ;CX=GDI's heap block + + ;** See if we can do anything with this heap + les si,lpLocal ;Get a pointer to the structure + mov es:[si].le_wType,LT_NORMAL ;In case we don't find anything + mov ax,es:[si].le_hHeap ;Get the heap pointer + cmp ax,bx ;User's heap? + jnz UGT_10 ;Nope, try next + cCall GetUserType ;Call routine to get user type + jmp SHORT UGT_End ;Get out + +UGT_10: cmp ax,cx ;GDI's heap? + jnz UGT_End ;Nope, can't do anything with it + cCall GetGDIType ;Call routine to get GDI type + +UGT_End: + +cEnd + + +;** Internal helper functions + +; GetUserType +; +; Uses the tags in debug USER.EXE to give information on what type +; block is pointed to by the current LOCALENTRY structure. +; Caller: ES:SI points to the parameter LOCALENTRY structure +; Return: LOCALENTRY structure is correctly updated + +cProc GetUserType, <NEAR> +cBegin + ;** Make sure we have a function to call + cmp WORD PTR lpfnGetUserLocalObjType + 2,0 ;Selector zero? + je GUT_End ;Yes + + ;** Call USER to get the type + push es ;Save ES + mov bx,es:[si].le_wAddress ;Get the block address + sub bx, la_fixedsize ;The USER call needs the arena header + test es:[si].le_wFlags, LF_MOVEABLE ;Moveable block? + jz @F ;No + sub bx, (SIZE LocalArena) - la_fixedsize ;Moveable arena bigger +@@: push bx ;Parameter arena handle + call DWORD PTR lpfnGetUserLocalObjType ;Call the function + pop es + xor ah,ah ;Clear the upper byte + mov es:[si].le_wType,ax ;Save the type +GUT_End: +cEnd + + +; GetGDIType +; +; Uses the tags in debug GDI.EXE to give information on what type +; block is pointed to by the current LOCALENTRY structure. +; Caller: ES:SI points to the parameter LOCALENTRY structure +; Return: LOCALENTRY structure is correctly updated + +cProc GetGDIType, <NEAR>, <ds> +cBegin + ;** All fixed blocks are unknown to us + test es:[si].le_wFlags,LF_FIXED ;Is it fixed? + jz GGT_10 ;Nope + jmp SHORT GGT_End ;Yes, get out +GGT_10: + + ;** Prepare to find the type + cCall HelperHandleToSel,es:[si].le_hHeap ;Get the selector value + mov cx,wTHFlags ;Save for when we trash DS + mov ds,ax ;Get the heap pointer + mov di,es:[si].le_wAddress ;Get the block pointer + + ;** Get the type word + mov ax,[di+2] ;Get the type word from the heap + and ax,05fffh ;Mask out the stock object flag + test cx,TH_WIN30 ;In 3.0? + jnz CGT_Win30 ;Yes + sub ax,LT_GDI_BASE ;No, subtract type tag base +CGT_Win30: + cmp ax,LT_GDI_MAX ;Recognizable type code? + ja GGT_End ;No, get out + mov es:[si].le_wType,ax ;Save in the structure + +GGT_End: +cEnd + +sEnd + END + diff --git a/private/mvdm/wow16/toolhelp/walk286.asm b/private/mvdm/wow16/toolhelp/walk286.asm new file mode 100644 index 000000000..ef358449b --- /dev/null +++ b/private/mvdm/wow16/toolhelp/walk286.asm @@ -0,0 +1,562 @@ +;************************************************************************** +;* walk286.ASM +;* +;* Assembly support code for the KRNL286 global heap routines +;* for TOOLHELP.DLL +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + +PMODE32 = 0 +PMODE = 1 +SWAPPRO = 0 + INCLUDE WINKERN.INC + +;** External functions +externNP HelperVerifySeg +externNP HelperHandleToSel +externNP HelperPDBtoTDB +externNP HelperSegLen + +;** Functions + +sBegin CODE + assumes CS,CODE + +; Walk286Count +; +; Returns the number of blocks in the given list + +cProc Walk286Count, <PUBLIC,NEAR>, <di> + parmW wFlags +cBegin + mov es,hMaster ;Segment of master block + mov ax,wFlags ;Get the flag value + shr ax,1 ;Check for GLOBAL_LRU + jc W2C_LRU ;Bit set, must be LRU + shr ax,1 ;Check for GLOBAL_FREE + jc W2C_Free ;Must be free + ;Must be GLOBAL_ALL + + ;** Get total object count + mov ax,es:[hi_count] ;Get heap count + inc ax ;Bump to include first sentinel + jmp SHORT W2C_End ;Get out + + ;** Get LRU object count +W2C_LRU: + mov ax,es:[gi_lrucount] ;Get the LRU count + jmp SHORT W2C_End ;Get out + + ;** Get Free list object count +W2C_Free: + mov ax,es:[gi_free_count] ;Get free count + jmp SHORT W2C_End ;Get out + + ;** Return the result in AX +W2C_End: + +cEnd + +; Walk286First +; +; Returns a handle to the first block in the 286 global heap. + +cProc Walk286First, <PUBLIC,NEAR>, <di> + parmW wFlags +cBegin + mov es,hMaster ;Segment of master block + mov ax,wFlags ;Get the flag value + shr ax,1 ;Check for GLOBAL_LRU + jc W2F_LRU ;Bit set, must be LRU + shr ax,1 ;Check for GLOBAL_FREE + jc W2F_Free ;Must be free + ;Must be GLOBAL_ALL + + ;** Get first object in complete heap (wFlags == GLOBAL_ALL) + mov ax,es:[hi_first] ;Get handle of first arena header + jmp SHORT W2F_End ;Get out + + ;** Get first object in LRU list +W2F_LRU: + mov ax,es:[gi_lrucount] ;Get the number of objects + or ax,ax ;Are there any objects? + je W2F_End ;No, return NULL + inc es:[gi_lrulock] ;No LRU sweeping for awhile + inc wLRUCount ;Keep a count of this + mov ax,es:[gi_lruchain] ;Get a pointer to the first item + jmp SHORT W2F_End ;Done + + ;** Get first object in Free list +W2F_Free: + mov ax,es:[gi_free_count] ;Get the number of objects + or ax,ax ;Are there any objects? + jz W2F_End ;No, return NULL + mov es,es:[hi_first] ;Get the first object + mov ax,es:[ga_freenext] ;Skip to the first free block + ;Fall through to the return + + ;** Return the result in AX (return DX = NULL) +W2F_End: + xor dx,dx ;Clear high word +cEnd + + +; Walk286 +; +; Takes a pointer to a block and returns a GLOBALENTRY structure +; full of information about the block. If the block pointer is +; NULL, looks at the first block. The last field in the GLOBALENTRY +; structure is used to chain to the next block and is thus used to walk +; the heap. + +cProc Walk286, <PUBLIC,NEAR>, <di,si,ds> + parmD dwBlock + parmD lpGlobal + parmW wFlags +cBegin + ;** Set up to build public structure + mov es,WORD PTR dwBlock ;Point to this block + lds si,lpGlobal ;Point to the GLOBALENTRY structure + + ;** Fill public structure + mov ax,es:[ga_handle] ;Get the handle of the block + mov [si].ge_hBlock,ax ;Put in public structure + mov ax,es:[ga_size] ;Get the size of the block (LOWORD) + mov dx,ax ;Clear high word + shl ax,4 ;Left shift DX:AX by 4 + shr dx,16-4 + mov WORD PTR [si].ge_dwBlockSize,ax ;Put in public structure + mov WORD PTR [si].ge_dwBlockSize + 2,dx ;Put in public structure + mov ax,es:[ga_owner] ;Owning task of block + mov [si].ge_hOwner,ax ;Put in public structure + xor ah,ah ;No upper BYTE + mov al,es:[ga_count] ;Lock count (moveable segments) + mov [si].ge_wcLock,ax ;Put in public structure + mov WORD PTR [si].ge_wcPageLock,0 ;Zero the page lock count + mov al,es:[ga_flags] ;BYTE of flags + xor ah,ah ;No upper BYTE + mov [si].ge_wFlags,ax ;Put in public structure + mov ax,es:[ga_next] ;Put next pointer in structure + mov WORD PTR [si].ge_dwNext,ax + mov WORD PTR [si].ge_dwNext + 2,0 + + ;** Use DPMI to compute linear address of selector + mov ax,6 ;Get Segment Base Address + mov bx,es ;Get the segment value + int 31h ;Call DPMI + mov WORD PTR [si].ge_dwAddress,dx ;Save linear address + mov WORD PTR [si].ge_dwAddress + 2,cx + + ;** If this is a data segment, get local heap information + mov ax,[si].ge_hBlock ;Get the handle + cCall Walk286VerifyLocHeap + mov [si].ge_wHeapPresent,TRUE ;Flag that there's a heap + jnc W2_10 ;There really is no heap + mov [si].ge_wHeapPresent,FALSE ;Flag that there's no heap +W2_10: + + ;** If the owner is a PDB, translate this to the TDB + mov bx,[si].ge_hOwner ;Get the owner + cCall HelperHandleToSel, <bx> ;Translate to selector + mov bx,ax ;Get the selector in BX + cCall HelperVerifySeg, <ax,2> ;Must be two bytes long + or ax,ax ;Is it? + jz W2_15 ;No. + push es ;Save ES for later + mov es,bx ;Point to possible PDB block + cmp es:[0],20CDh ;Int 20h is first word of PDB + jnz W2_12 ;Nope, don't mess with this + mov ax,bx ;Pass parameter in AX + cCall HelperPDBtoTDB ;Get the corresponding TDB + or ax,ax ;Was one found? + jz W2_11 ;No. + mov [si].ge_hOwner,ax ;Make the owner the TDB instead +W2_11: or [si].ge_wFlags,GF_PDB_OWNER ;Flag that a PDB owned block +W2_12: pop es ;Restore ES +W2_15: + + ;** Check for this being the last item in both lists + mov ax,es ;Get the current pointer + cmp ax,es:[ga_next] ;See if we're at the end + jne W2_20 ;No + mov WORD PTR [si].ge_dwNext,0 ;NULL the next pointer + mov WORD PTR [si].ge_dwNext + 2,0 +W2_20: mov ax,es ;Get current pointer + mov cx,wFlags ;Get the flags back + cCall NextLRU286 ;Get next LRU list pointer or 0 + mov WORD PTR [si].ge_dwNextAlt,ax + mov WORD PTR [si].ge_dwNextAlt + 2,0 + +W2_End: +cEnd + + +; Walk286Handle +; +; Finds an arena pointer given a block handle + +cProc Walk286Handle, <PUBLIC,NEAR>, <di,si,ds> + parmW hBlock +cBegin + mov ax,hBlock ;Get the block handle + cCall HelperHandleToSel, <ax> ;Convert to selector + cCall SelToArena286 ;Get the arena pointer + jnc W2H_10 ;Must be OK + xor ax,ax ;Return a 0L + xor dx,dx + jmp SHORT W2H_End ;Error in conversion, get out +W2H_10: mov ax,bx ;Get the low word + xor dx,dx ;No high word +W2H_End: +cEnd + + +; WalkLoc286Count +; +; Returns the number of blocks in the given local heap + +cProc WalkLoc286Count, <PUBLIC,NEAR>, <di> + parmW hHeap +cBegin + ;** Verify that it has a local heap + mov ax, hHeap ;Get the block + cCall Walk286VerifyLocHeap ;Verify it + jnc LC_10 ;It's OK + xor ax,ax ;No heap + jmp SHORT LC_End ;Get out +LC_10: + + ;** Point to the block + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + mov bx,es:[6] ;Get a pointer to the HeapInfo struct + + ;** Get the number of blocks + mov ax,es:[bx].hi_count ;Get the count +LC_End: +cEnd + + +; WalkLoc286First +; +; Returns a handle to the first block in the 286 global heap. + +cProc WalkLoc286First, <PUBLIC,NEAR>, <di> + parmW hHeap +cBegin + ;** Verify that the given global block has a local heap + mov ax, hHeap ;Get the block + cCall Walk286VerifyLocHeap ;Verify it + jnc LF_10 ;It's OK + xor ax,ax ;No heap + jmp SHORT LF_End ;Get out +LF_10: + + ;** Point to the global block + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + mov bx,es:[6] ;Get a pointer to the HeapInfo struct + + ;** Get the first block and return it + mov ax,WORD PTR es:[bx].hi_first ;Get the first block +LF_End: +cEnd + + +; WalkLoc286 +; +; Takes a pointer to a block and returns a GLOBALENTRY structure +; full of information about the block. If the block pointer is +; NULL, looks at the first block. The last field in the GLOBALENTRY +; structure is used to chain to the next block and is thus used to walk +; the heap. + +cProc WalkLoc286, <PUBLIC,NEAR>, <di,si,ds> + parmW wBlock + parmD lpLocal + parmW hHeap +cBegin + ;** Verify that it has a local heap + mov ax, hHeap ;Get the block + cCall Walk286VerifyLocHeap ;Verify it + jnc LW_10 ;It's OK + jmp LW_End ;Get out +LW_10: + + ;** Get variables from the TOOLHELP DGROUP + mov ax,_DATA ;Get the variables first + mov ds,ax ;Point to our DS + mov bx,hUserHeap ;BX=User's heap block + mov cx,hGDIHeap ;CX=GDI's heap block + + ;** Point to the heap + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + lds di,lpLocal ;Point to the LOCALENTRY structure + mov [di].le_wHeapType,NORMAL_HEAP ;In case we don't match below... + cmp bx,hHeap ;User's heap? + jnz LW_3 ;No + mov [di].le_wHeapType,USER_HEAP ;Yes + jmp SHORT LW_5 ;Skip on +LW_3: cmp cx,hHeap ;GDI's heap? + jnz LW_5 ;No + mov [di].le_wHeapType,GDI_HEAP ;Yes +LW_5: + mov si,wBlock ;Get the address of the block + + ;** Get information about the given block + mov bx,es:[si].la_handle ;Get the handle + mov [di].le_hHandle,bx ;Save in the public structure + mov ax,si ;Get block address + add ax,la_fixedsize ;Skip fixed size local arena + mov [di].le_wAddress,ax ;Save the block address + mov ax,es:[si].la_next ;Get the address of the next block + mov [di].le_wNext,ax ;Save the next pointer + sub ax,si ;Compute the size + sub ax,SIZE LocalArena ;Don't count arena size + mov [di].le_wSize,ax ;Save in public structure + mov ax,es:[si].la_prev ;Get the flags + and ax,3 ;Mask out all but flags + mov [di].le_wFlags,ax ;Save in structure + + ;** Moveable arenas are bigger and have a lock count to get + test al,LA_MOVEABLE ;Block has a handle iff it's moveable + jz SHORT LW_NoHandle ;No handle info + sub [di].le_wSize, (SIZE LocalArena) - la_fixedsize + add [di].le_wAddress, (SIZE LocalArena) - la_fixedsize + xor ah,ah ;Clear upper word + mov al,es:[bx].lhe_count ;Get lock count + mov [di].le_wcLock,ax ;Save it + jmp SHORT LW_20 ;Skip no handle info +LW_NoHandle: + mov ax, [di].le_wAddress ;Handle of fixed block is real offset + mov [di].le_hHandle, ax + mov [di].le_wcLock,0 +LW_20: + ;** See if it's the end + cmp [di].le_wNext,si ;Loop pointer? + jnz LW_End ;Nope + mov [di].le_wNext,0 ;Set a zero pointer +LW_End: +cEnd + + +; Walk286VerifyLocHeap +; +; Verifies that the given global block points to a data segment +; with a local heap. +; +; Call: +; AX = Block handle or selector +; Return: +; Carry flag set iff NOT a local heap segment +; +; Destroys all registers except AX, ESI, EDI, DS, and ES + +cProc Walk286VerifyLocHeap, <PUBLIC,NEAR>, <es,si,di> +cBegin + ;** Convert to a handle + cCall HelperHandleToSel, <ax> + + ;** Verify the selector + push ax ;Save the parameter + mov bx,SIZE LocalInfo ;Get the size + cCall HelperVerifySeg, <ax,bx> ;Check the selector + or ax,ax ;Is it valid? + pop ax ;Restore parameter + jnz VLH_SelOK ;Yes. + stc ;Set error flag + jmp SHORT VLH_End ;Get out +VLH_SelOK: + + ;** Check data segment to see if it has a local heap + mov es,ax ;Point to the local block with ES + cCall HelperSegLen, <ax> ;Get the length of the heap + or ax,ax ;Check for error + jz VLH_NoHeap ;Get out on error + mov cx,ax ;Use CX for comparisons + cmp cx,8 ;At least 8 bytes long? + ja VLH_10 ;Yes + stc ;No -- set error flag and get out + jmp SHORT VLH_End +VLH_10: mov bx,es:[6] ;Get offset to HeapInfo struct + or bx,bx ;If NULL, no local heap + jz VLH_NoHeap ;Get out + sub cx,bx ;See if HeapInfo is beyond segment + jbe VLH_NoHeap + sub cx,li_sig + 2 ;Compare against last structure mem + jbe VLH_NoHeap + cmp es:[bx].li_sig,LOCALHEAP_SIG ;Make sure the signature matches + jne VLH_NoHeap ;Doesn't match + clc ;Must be a heap! + jmp SHORT VLH_End + +VLH_NoHeap: + stc ;Set error flag + +VLH_End: +cEnd + + +;** Private helper functions + +; SelToArena286 +; +; Finds the arena entry for the given selector or handle. +; The arena entry is stored 16 bytes before the block in linear +; address space. +; +; Caller: AX = Selector +; Returns: BX = Arena entry +; Trashes everything except segment registers and AX +; Carry set on error + +cProc SelToArena286, <NEAR>, <es,ds,ax> +cBegin + ;** Convert to a handle + cCall HelperHandleToSel, <ax> + + ;** Verify selector + push ax ;Save the parameter + cCall HelperVerifySeg, <ax,1> ;Check the selector + or ax,ax ;Is it valid? + pop ax ;Restore parameter + jnz STA_SelOK ;Must be + stc ;Nope. Set error flag and exit + jmp SHORT STA_End +STA_SelOK: + ;** If this is Win30StdMode, we're in the old KRNL286 which used + ;* an arcane method of finding the arena. If that's the case + ;** here, handle it seperately. + mov bx,_DATA ;Get the static data segment + mov ds,bx + test wTHFlags,TH_WIN30STDMODE ;3.0 Std Mode? + jnz STA_OldKRNL286 ;Yes + mov bx,segKernel ;Get the KERNEL variable segment + mov es,bx + mov bx,npwSelTableLen ;Get the pointer + mov dx,es:[bx] ;Get the selector table length + mov bx,npdwSelTableStart ;Get the start of the sel table + mov bx,es:[bx] ;Get the linear offset (32 bits) + mov es,hMaster ;Point to the arena table + and al,not 7 ;Clear the RPL bits for table lookup + shr ax,2 ;Convert to WORD offset + cmp ax,dx ;Check to see if off the end of table + jb STA_InTable ;It's in the table + stc ;Set error flag--not in table + jmp SHORT STA_End ;Get out +STA_InTable: + add bx,ax ;Add the selector offset + mov bx,es:[bx] ;Do the sel table indirection + clc ;BX now points to arena segment + jmp SHORT STA_End ;Skip the old stuff + +STA_OldKRNL286: + mov bx,ax ;Selector in BX + mov ax,6 ;DPMI function 6, Get Seg Base Addr + int 31h ;Call DPMI + sub dx,10h ;Move back 16 bytes + sbb cx,0 ; (this is a linear address) + mov ax,7 ;DPMI function 7, Set Seg Base Addr + mov bx,wSel ;Use our roving selector + int 31h ;Call DPMI + mov ax,8 ;DPMI function 8, Set Seg Limit + xor cx,cx ;No upper byte + mov dx,16 ;Just 16 bytes + int 31h ;Call DPMI + mov ax,9 ;DPMI function 9, Set Access Rights + mov cl,0b2h ;Desired rights byte + int 31h ;Call DPMI + ;Return arena segment pointer in BX +STA_End: +cEnd + + +; NextLRU286 +; +; Checks the given arena table pointer to see if it is the last +; arena entry on the list. +; Uses a grungy method that is necessitated because of the +; unterminated linked lists we're dealing with here. The count +; stored is the only reliable method for finding the end. So, to +; provide random access to blocks in the heap, the heap is searched +; from top to bottom to find the block and see if it is the last +; one. If it is or if it is not on the given list, returns a 0 +; pointer to the next item. +; +; If this search is for the entire global heap, we do not get the +; LRU link, but return NULL in AX. This speeds this up alot! +; +; Caller: AX = Arena table pointer +; CX = GLOBAL_ALL, GLOBAL_FREE, or GLOBAL_LRU +; Return: AX = Next arena table pointer or 0 if no more +; Trashes all registers except segment registers and SI,DI + +cProc NextLRU286, <NEAR,PUBLIC>, <es,ds,si,di> +cBegin + ;** Decode the flags + mov bx,_DATA ;Get the static data segment + mov ds,bx ;Point with DS + mov es,hMaster ;Segment of master block + cmp cx,GLOBAL_ALL ;Don't do this on full heap search + jne @F + jmp SHORT NL_BadList ;Just return NULL for this one +@@: cmp cx,GLOBAL_FREE ;Check free list? + je NL_Free ;Yes + + ;** Loop through LRU list until we find this item +NL_LRU: + mov si,ax ;Save the selector in AX + mov cx,es:[gi_lrucount] ;Get the number of objects + jcxz NL_Bad ;No object so return end + dec cx ;We don't want to find the last one + jcxz NL_Bad ;1 object so return end + mov ds,es:[gi_lruchain] ;Get a pointer to the first item +NL_LRULoop: + mov ax,ds ;Get in AX so we can compare + cmp si,ax ;Match yet? + je NL_ReturnNext ;Found it, return next item + mov ds,ds:[ga_lrunext] ;Not found yet, get the next one + loop NL_LRULoop ;Loop through all items + + ;** Unlock the LRU sweeping +NL_Bad: dec es:[gi_lrulock] ;OK to LRU sweep now + mov ax, _DATA ;Point to TH Data seg + mov ds, ax + dec wLRUCount ;Keep a count of this + jmp SHORT NL_BadList ;Not here either. Get out + + ;** Loop through free list until we find this item +NL_Free: + mov si,ax ;Save the selector in SI + mov cx,es:[gi_free_count] ;Get the number of objects + jcxz NL_BadList ;0 objects so return end + dec cx ;We don't want to find the last one + jcxz NL_BadList ;1 object so return end + mov ds,es:[hi_first] ;Get a pointer to the first item +NL_FreeLoop: + mov ds,ds:[ga_lrunext] ;Not found yet, get the next one + mov ax,ds ;Get the pointer so we can compare it + cmp ax,si ;Is this the one? + je NL_ReturnNext ;Yes, return it + loop NL_FreeLoop ;Loop through all items + jmp SHORT NL_BadList ;Not here either. Get out + +NL_ReturnNext: + mov ax,ds:[ga_lrunext] ;Return the next one + jmp SHORT NL_End ;Get out + +NL_BadList: + xor ax,ax ;Return zero for error + +NL_End: +cEnd + +sEnd + END + diff --git a/private/mvdm/wow16/toolhelp/walk386.asm b/private/mvdm/wow16/toolhelp/walk386.asm new file mode 100644 index 000000000..2be63562b --- /dev/null +++ b/private/mvdm/wow16/toolhelp/walk386.asm @@ -0,0 +1,541 @@ +;************************************************************************** +;* walk386.ASM +;* +;* Assembly support code for the KERNEL386 global heap routines +;* for TOOLHELP.DLL +;* +;************************************************************************** + + INCLUDE TOOLPRIV.INC + +PMODE32 = 1 +PMODE = 0 +SWAPPRO = 0 + INCLUDE WINKERN.INC + +;** External functions +externNP HelperVerifySeg +externNP HelperHandleToSel +externNP HelperPDBtoTDB +externNP HelperSegLen + +;** Functions + +sBegin CODE + assumes CS,CODE +.386p + +; Walk386Count +; +; Returns the number of blocks in the given list + +cProc Walk386Count, <PUBLIC,NEAR>, <di> + parmW wFlags +cBegin + mov es,hMaster ;Segment of master block + mov ax,wFlags ;Get the flag value + shr ax,1 ;Check for GLOBAL_LRU + jc SHORT W3C_LRU ;Bit set, must be LRU + shr ax,1 ;Check for GLOBAL_FREE + jc SHORT W3C_Free ;Must be free + ;Must be GLOBAL_ALL + + ;** Get total object count + mov ax,es:[hi_count] ;Get heap count + inc ax ;Bump to include first sentinel + jmp SHORT W3C_End ;Get out + + ;** Get LRU object count +W3C_LRU: + mov ax,es:[gi_lrucount] ;Get the LRU count + jmp SHORT W3C_End ;Get out + + ;** Get Free list object count +W3C_Free: + mov ax,es:[gi_free_count] ;Get free count + jmp SHORT W3C_End ;Get out + + ;** Return the result in AX +W3C_End: + +cEnd + +; Walk386First +; +; Returns a handle to the first block in the 386 global heap. + +cProc Walk386First, <PUBLIC,NEAR>, <di> + parmW wFlags +cBegin + mov es,hMaster ;Segment of master block + mov ax,wFlags ;Get the flag value + shr ax,1 ;Check for GLOBAL_LRU + jc SHORT W3F_LRU ;Bit set, must be LRU + shr ax,1 ;Check for GLOBAL_FREE + jc SHORT W3F_Free ;Must be free + ;Must be GLOBAL_ALL + + ;** Get first object in complete heap (wFlags == GLOBAL_ALL) + mov edi,es:[phi_first] ;Get offset of first arena header + jmp SHORT W3F_End ;Get out + + ;** Get first object in LRU list +W3F_LRU: + xor edi,edi ;Zero upper word + mov di,es:[gi_lrucount] ;Get the number of objects + or di,di ;Are there any objects? + jz SHORT W3F_End ;No, return NULL + mov edi,es:[gi_lruchain] ;Get a pointer to the first item + jmp SHORT W3F_End ;Done + + ;** Get first object in Free list +W3F_Free: + xor edi,edi ;Zero upper word + mov di,es:[gi_free_count] ;Get the number of objects + or di,di ;Are there any objects? + jz SHORT W3F_End ;No, return NULL + mov edi,es:[phi_first] ;Get the first object + mov edi,es:[edi].pga_freenext ;Skip to the first free block + ;Fall through to the return + + ;** Save the result in DX:AX for the return +W3F_End: + mov ax,di ;Get the low word + shr edi,16 ;Get the high word out + mov dx,di ;Get the high word +cEnd + + +; Walk386 +; +; Takes a pointer to a block and returns a GLOBALENTRY structure +; full of information about the block. If the block pointer is +; NULL, looks at the first block. The last field in the GLOBALENTRY +; structure is used to chain to the next block and is thus used to walk +; the heap. + +cProc Walk386, <PUBLIC,NEAR>, <di,si,ds> + parmD dwBlock + parmD lpGlobal + parmW wFlags +cBegin + ;** Set up to build public structure + mov es,hMaster ;Get the correct segment + mov edi,dwBlock ;Point to this block + lds si,lpGlobal ;Point to the GLOBALENTRY structure + + ;** Fill public structure + mov ax,es:[edi].pga_handle ;Get the handle of the block + mov [si].ge_hBlock,ax ;Put in public structure + mov eax,es:[edi].pga_address ;Get linear address of block + mov [si].ge_dwAddress,eax ;Put in public structure + mov eax,es:[edi].pga_size ;Get the size of the block + mov [si].ge_dwBlockSize,eax ;Put in public structure + mov ax,es:[edi].pga_owner ;Owning task of block + mov [si].ge_hOwner,ax ;Put in public structure + xor ah,ah + mov al,es:[edi].pga_count ;Lock count (moveable segments) + mov [si].ge_wcLock,ax ;Put in public structure + mov al,es:[edi].pga_pglock ;Page lock count + mov [si].ge_wcPageLock,ax ;Save in structure + mov al,es:[edi].pga_flags ;Word of flags + xor ah,ah + mov [si].ge_wFlags,ax ;Put in public structure + mov eax,es:[edi].pga_next ;Put next pointer in structure + mov [si].ge_dwNext,eax + + ;** If this is a data segment, get local heap information + mov ax,[si].ge_hBlock ;Get the handle + cCall Walk386VerifyLocHeap + mov [si].ge_wHeapPresent,TRUE ;Flag that there's a heap + jnc SHORT W3_10 ;There really is no heap + mov [si].ge_wHeapPresent,FALSE ;Flag that there's no heap +W3_10: + + ;** If the owner is a PDB, translate this to the TDB + mov bx,[si].ge_hOwner ;Get the owner + cCall HelperHandleToSel, <bx> ;Translate to selector + mov bx,ax ;Get the selector in BX + cCall HelperVerifySeg, <ax,2> ;Must be two bytes long + or ax,ax ;Is it? + jz SHORT W3_15 ;No. + push es ;Save ES for later + mov es,bx ;Point to possible PDB block + cmp es:[0],20CDh ;Int 20h is first word of PDB + jnz SHORT W3_12 ;Nope, don't mess with this + mov ax,bx ;Pass parameter in AX + cCall HelperPDBtoTDB ;Get the corresponding TDB + or ax,ax ;Was one found? + jz SHORT W3_11 ;No. + mov [si].ge_hOwner,ax ;Make the owner the TDB instead +W3_11: or [si].ge_wFlags,GF_PDB_OWNER ;Flag that a PDB owned block +W3_12: pop es ;Restore ES +W3_15: + + ;** Check for this being the last item in both lists + cmp edi,es:[edi].pga_next ;See if we're at the end + jnz SHORT W3_20 ;No + mov [si].ge_dwNext,0 ;NULL the next pointer +W3_20: mov eax,edi ;Get current pointer + mov cx,wFlags ;Get the flags back + cCall NextLRU386 ;Get next LRU list pointer or 0 + mov [si].ge_dwNextAlt,eax ;Save it + +W3_End: +cEnd + + +; Walk386Handle +; +; Finds an arena pointer given a block handle + +cProc Walk386Handle, <PUBLIC,NEAR>, <di,si,ds> + parmW hBlock +cBegin + mov ax,hBlock ;Get the block handle + cCall HelperHandleToSel, <ax> ;Convert to selector + cCall SelToArena386 ;Get the arena pointer + jnc SHORT W3H_10 ;Must be OK + xor ax,ax ;Return a 0L + xor dx,dx + jmp SHORT W3H_End ;Error in conversion, get out +W3H_10: mov ax,bx ;Get the low word + shr ebx,16 ;Get the high word in DX + mov dx,bx +W3H_End: +cEnd + + +; WalkLoc386Count +; +; Returns the number of blocks in the given local heap + +cProc WalkLoc386Count, <PUBLIC,NEAR>, <di> + parmW hHeap +cBegin + ;** Verify that it has a local heap + mov ax, hHeap ;Get the block + cCall Walk386VerifyLocHeap ;Verify it + jnc SHORT LC_10 ;It's OK + xor ax,ax ;No heap + jmp SHORT LC_End ;Get out +LC_10: + + ;** Point to the block + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + mov bx,es:[6] ;Get a pointer to the HeapInfo struct + + ;** Get the number of blocks + mov ax,es:[bx].hi_count ;Get the count +LC_End: +cEnd + + +; WalkLoc386First +; +; Returns a handle to the first block in the 386 global heap. + +cProc WalkLoc386First, <PUBLIC,NEAR>, <di> + parmW hHeap +cBegin + ;** Verify that the given global block has a local heap + mov ax, hHeap ;Get the block + cCall Walk386VerifyLocHeap ;Verify it + jnc SHORT LF_10 ;It's OK + xor ax,ax ;No heap + jmp SHORT LF_End ;Get out +LF_10: + + ;** Point to the global block + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + mov bx,es:[6] ;Get a pointer to the HeapInfo struct + + ;** Get the first block and return it + mov ax,WORD PTR es:[bx].hi_first ;Get the first block +LF_End: +cEnd + + +; WalkLoc386 +; +; Takes a pointer to a block and returns a GLOBALENTRY structure +; full of information about the block. If the block pointer is +; NULL, looks at the first block. The last field in the GLOBALENTRY +; structure is used to chain to the next block and is thus used to walk +; the heap. + +cProc WalkLoc386, <PUBLIC,NEAR>, <di,si,ds> + parmW wBlock + parmD lpLocal + parmW hHeap +cBegin + ;** Verify that it has a local heap + mov ax, hHeap ;Get the block + cCall Walk386VerifyLocHeap ;Verify it + jnc SHORT LW_10 ;It's OK + jmp LW_End ;Get out +LW_10: + + ;** Get variables from the TOOLHELP DGROUP + mov ax,_DATA ;Get the variables first + mov ds,ax ;Point to our DS + mov bx,hUserHeap ;BX=User's heap block + mov cx,hGDIHeap ;CX=GDI's heap block + + ;** Point to the heap + mov ax,hHeap ;Get the heap block + cCall HelperHandleToSel, <ax> ;Convert it to a selector + mov es,ax ;Use ES to point to it + lds di,lpLocal ;Point to the LOCALENTRY structure + mov [di].le_wHeapType,NORMAL_HEAP ;In case we don't match below... + cmp bx,hHeap ;User's heap? + jnz SHORT W3_3 ;No + mov [di].le_wHeapType,USER_HEAP ;Yes + jmp SHORT W3_5 ;Skip on +W3_3: cmp cx,hHeap ;GDI's heap? + jnz SHORT W3_5 ;No + mov [di].le_wHeapType,GDI_HEAP ;Yes +W3_5: + mov si,wBlock ;Get the address of the block + + ;** Get information about the given block + mov bx,es:[si].la_handle ;Get the handle + mov [di].le_hHandle,bx ;Save in the public structure + mov ax,si ;Get block address + add ax,la_fixedsize ;Skip fixed size local arena + mov [di].le_wAddress,ax ;Save the block address + mov ax,es:[si].la_next ;Get the address of the next block + mov [di].le_wNext,ax ;Save the next pointer + sub ax,si ;Compute the size + sub ax,la_fixedsize ;Size of fixed arena + mov [di].le_wSize,ax ;Save in public structure + mov ax,es:[si].la_prev ;Get the flags + and ax,3 ;Mask out all but flags + mov [di].le_wFlags,ax ;Save in structure + + ;** Moveable arenas are bigger and have a lock count to get + test al,LA_MOVEABLE ;Block has a handle iff it's moveable + jz SHORT LW_NoHandle ;No handle info + sub [di].le_wSize, (SIZE LocalArena) - la_fixedsize + add [di].le_wAddress, (SIZE LocalArena) - la_fixedsize + xor ah,ah ;Clear upper word + mov al,es:[bx].lhe_count ;Get lock count + mov [di].le_wcLock,ax ;Save it + jmp SHORT LW_20 ;Skip no handle info +LW_NoHandle: + mov ax, [di].le_wAddress ;Handle of fixed block is real offset + mov [di].le_hHandle, ax + mov [di].le_wcLock,0 ;No lock count either +LW_20: + ;** See if it's the end + cmp [di].le_wNext,0 ;Check for NULL pointer just in case + jz SHORT LW_End ;It is so get out + cmp [di].le_wNext,si ;Loop pointer? + jnz SHORT LW_End ;Nope + mov [di].le_wNext,0 ;Set a zero pointer +LW_End: +cEnd + + +; Walk386VerifyLocHeap +; +; Verifies that the given global block points to a data segment +; with a local heap. +; +; Call: +; AX = Block handle or selector +; Return: +; Carry flag set iff NOT a local heap segment +; +; Destroys all registers except AX, ESI, EDI, DS, and ES + +cProc Walk386VerifyLocHeap, <PUBLIC,NEAR>, <es> +cBegin + push esi ;Save certain registers + push edi + + ;** Convert to a selector + cCall HelperHandleToSel, <ax> + + ;** Verify that the selector is long enough + push ax ;Save parameter + mov bx,SIZE LocalInfo + cCall HelperVerifySeg, <ax,bx> ;Check the selector + or ax,ax ;Is it valid? + pop ax ;Restore parameter + jnz SHORT VLH_SelOK ;Yes + stc ;Set error flag + jmp SHORT VLH_End ;Get out +VLH_SelOK: + + ;** Check data segment to see if it has a local heap + mov es,ax ;Point to the local block with ES + cCall HelperSegLen, <ax> ;Get the length of the heap + movzx ecx,dx ;ECX gets the length + shl ecx,16 ;Put high word in high word of EAX + mov cx,ax ;Get the low word + cmp ecx,8 ;At least 8 bytes long? + ja SHORT VLH_10 ;Yes + stc ;No -- set error flag and get out + jmp SHORT VLH_End +VLH_10: xor ebx,ebx ;Clear high word + mov bx,es:[6] ;Get offset to HeapInfo struct + or bx,bx ;If NULL, no local heap + jz SHORT VLH_NoHeap ;Get out + sub ecx,ebx ;See if HeapInfo is beyond segment + jbe SHORT VLH_NoHeap + sub ecx,li_sig + 2 ;Compare against last structure mem + jbe SHORT VLH_NoHeap + cmp es:[bx].li_sig,LOCALHEAP_SIG ;Make sure the signature matches + jne SHORT VLH_NoHeap ;Doesn't match + clc ;Must be a heap! + jmp SHORT VLH_End + +VLH_NoHeap: + stc ;Set error flag + +VLH_End: + pop edi + pop esi +cEnd + + +;** Private helper functions + +; SelToArena386 +; +; Finds the arena entry for the given selector or handle. +; Uses the selector table which is located just after the arena table. +; Since this is a large segment, use 32 bit offsets into it. +; The selector-to-arena mapping table is simply an array of arena +; offsets indexed by the (selector number >> 3) * 4 [DWORD offsets]. +; +; Caller: AX = Selector +; Returns: DX:EBX = Arena entry +; Trashes everything except segment registers and AX +; Carry set on error + +cProc SelToArena386, <NEAR,PUBLIC>, <es,ds,ax> +cBegin + ;** Convert to a selector + cCall HelperHandleToSel, <ax> + + ;** Verify selector + push ax ;Save parameter + cCall HelperVerifySeg, <ax,1> ;Check the selector + or ax,ax ;Is it valid? + pop ax ;Restore parameter + jnz SHORT STA_SelOK ;Must be + stc ;Nope. Set error flag and exit + jmp SHORT STA_End +STA_SelOK: + mov bx,_DATA ;Get the static data segment + mov ds,bx + mov bx,segKernel ;Get the KERNEL variable segment + mov es,bx + mov bx,npwSelTableLen ;Get the pointer + mov dx,es:[bx] ;Get the selector table length + mov bx,npdwSelTableStart ;Get the start of the sel table + mov ebx,es:[bx] ;Get the linear offset (32 bits) + mov es,hMaster ;Point to the arena table + and al,not 7 ;Clear the RPL bits for table lookup + shr ax,1 ;Convert to DWORD offset + cmp ax,dx ;Check to see if off the end of table + jb SHORT STA_InTable ;It's in the table + stc ;Set error flag--not in table + jmp SHORT STA_End ;Get out +STA_InTable: + movzx eax,ax ;Convert the offset to 32 bits + add ebx,eax ;Add the selector offset + mov ebx,es:[ebx] ;Do the sel table indirection + mov dx,hMaster ;DX points to the arena segment + clc ;No error; return OK +STA_End: +cEnd + + +; NextLRU386 +; +; Checks the given arena table pointer to see if it is the last +; arena entry on the list. +; Uses a grungy method that is necessitated because of the +; unterminated linked lists we're dealing with here. The count +; stored is the only reliable method for finding the end. So, to +; provide random access to blocks in the heap, the heap is searched +; from top to bottom to find the block and see if it is the last +; one. If it is or if it is not on the given list, returns a 0 +; pointer to the next item. +; +; If this search is for the entire global heap, we do not get the +; LRU link, but return NULL in EAX. This speeds this up alot! +; +; Caller: EAX = Arena table pointer +; CX = GLOBAL_ALL, GLOBAL_FREE, or GLOBAL_LRU +; Return: EAX = Next arena table pointer or 0 if no more +; Trashes all registers except segment registers and ESI,EDI + +cProc NextLRU386, <NEAR,PUBLIC>, <es,ds> +cBegin + push esi + push edi + + mov bx,_DATA ;Get the static data segment + mov ds,bx ;Point with DS + mov es,hMaster ;Segment of master block + cmp cx,GLOBAL_ALL ;If doing entire heap, don't do this! + je SHORT NL_BadList ;Must not be entire heap + shr cx,1 ;Check for GLOBAL_LRU + jc SHORT NL_LRU ;Bit set, must be LRU + shr cx,1 ;Check for GLOBAL_FREE + jc SHORT NL_Free ;Must be free + ;Must be GLOBAL_ALL + + ;** Decide which list the block is in then hand off + cmp es:[eax].pga_owner,0 ;If the owner is zero, it's free + jz SHORT NL_Free ;Handle this as if on free list + ;Otherwise, might be on LRU list + + ;** Loop through LRU list until we find this item +NL_LRU: + mov cx,es:[gi_lrucount] ;Get the number of objects + jcxz NL_Bad ;No object so return end + dec cx ;We don't want to find the last one + jcxz NL_Bad ;1 object so return end + mov edi,es:[gi_lruchain] ;Get a pointer to the first item +NL_LRULoop: + cmp edi,eax ;Match yet? + jz SHORT NL_ReturnNext ;Found it, return next item + mov edi,es:[edi].pga_lrunext ;Not found yet, get the next one + loop NL_LRULoop ;Loop through all items +NL_Bad: jmp SHORT NL_BadList ;Not here either. Get out + + ;** Loop through free list until we find this item +NL_Free: + mov cx,es:[gi_free_count] ;Get the number of objects + jcxz NL_BadList ;1 object so return end + mov edi,es:[phi_first] ;Get a pointer to the first item +NL_FreeLoop: + mov edi,es:[edi].pga_lrunext ;Not found yet, get the next one + cmp edi,eax ;Match yet? + jz SHORT NL_ReturnNext ;Found it, return next item + loop NL_FreeLoop ;Loop through all items + jmp SHORT NL_BadList ;Not here either. Get out + +NL_ReturnNext: + mov eax,es:[edi].pga_lrunext ;Return the next one + jmp SHORT NL_End ;Get out + +NL_BadList: + xor eax,eax ;Return zero for error + +NL_End: + pop edi + pop esi +cEnd + +sEnd + END |