556 lines
22 KiB
NASM
556 lines
22 KiB
NASM
;**************************************************************************
|
|
;* 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
|