windows-nt/Source/XPSP1/NT/sdktools/apimon/apidll/i386/thunk.asm
2020-09-26 16:20:57 +08:00

823 lines
18 KiB
NASM

; Copyright (c) 1995 Microsoft Corporation
;
; Module Name:
;
; thunk.s
;
; Abstract:
;
; Implements the API thunk that gets executed for all
; re-directed APIS.
;
; Author:
;
; Wesley Witt (wesw) 28-June-1995
;
; Environment:
;
; User Mode
;
.386p
include ks386.inc
include callconv.inc
TRACE equ 0
EXTRNP _HandleDynamicDllLoadA,2
EXTRNP _HandleDynamicDllLoadW,2
EXTRNP _HandleDynamicDllFree,1
EXTRNP _HandleGetProcAddress,1
EXTRNP _HandleRegisterClassA,1
EXTRNP _HandleRegisterClassW,1
EXTRNP _HandleSetWindowLong,3
EXTRNP _QueryPerformanceCounter,1
EXTRNP _ApiTrace,9
EXTRNP _GetApiInfo,4
extrn _FastCounterAvail:DWORD
extrn _ApiCounter:DWORD
extrn _ApiTimingEnabled:DWORD
extrn _ApiTraceEnabled:DWORD
extrn _pTlsGetValue:DWORD
extrn _pTlsSetValue:DWORD
extrn _pGetLastError:DWORD
extrn _pSetLastError:DWORD
extrn _TlsReEnter:DWORD
extrn _TlsStack:DWORD
extrn _ThunkOverhead:DWORD
extrn _ThunkCallOverhead:DWORD
if TRACE
extrn _dprintf:NEAR
endif
APITYPE_NORMAL equ 0
APITYPE_LOADLIBRARYA equ 1
APITYPE_LOADLIBRARYW equ 2
APITYPE_FREELIBRARY equ 3
APITYPE_REGISTERCLASSA equ 4
APITYPE_REGISTERCLASSW equ 5
APITYPE_GETPROCADDRESS equ 6
APITYPE_SETWINDOWLONG equ 7
APITYPE_WNDPROC equ 8
APITYPE_INVALID equ 9
DllEnabledOffset equ 52
ApiInfoAddressOffset equ 4
ApiInfoCountOffset equ 12
ApiInfoTimeOffset equ 16
ApiInfoCalleeTimeOffset equ 24
ApiInfoNestCountOffset equ 32
ApiInfoTraceEnabled equ 36
ApiInfoTableOffset equ 40
ApiTableCountOffset equ 8
API_TRACE equ 1
API_FULLTRACE equ 2
LastErrorSave equ 0
EdiSave equ 4
EsiSave equ 8
EdxSave equ 12
EcxSave equ 16
EbxSave equ 20
EaxSave equ 24
ApiFlagSave equ 28
DllInfoSave equ 32
ApiInfoSave equ 36
RetAddrSave equ 40
StackSize equ 44
EbpFrm equ 0
DllInfoFrm equ 4
ApiInfoFrm equ 8
ApiFlagFrm equ 12
ApiBiasFrm equ 16
RetAddrFrm equ 20
LastErrorFrm equ 24
Arg0Frm equ 28
Arg1Frm equ 32
Arg2Frm equ 36
Arg3Frm equ 40
Arg4Frm equ 44
Arg5Frm equ 48
Arg6Frm equ 52
Arg7Frm equ 56
OverheadTimeFrm equ 64
FuncTimeFrm equ 72
CalleeTimeFrm equ 80
TempTimeFrm equ 88
FrameSize equ 96
;
; Routine Description:
;
; This MACRO gets a performance counter value.
; If we are running on a uni-processor pentium
; then we can use the rdtsc instruction. Otherwise
; we must use the QueryPerformanceCounter API.
;
; Arguments:
;
; CounterOffset - the offset from ebp where the
; counter data is to be stored.
;
; Return Value:
;
; None.
;
GET_PERFORMANCE_COUNTER macro CounterOffset
local DoPentium,PentiumExit
mov eax,[_FastCounterAvail]
mov eax,[eax]
or eax,eax
jnz DoPentium
mov eax,ebp
add eax,CounterOffset
push eax
call _QueryPerformanceCounter@4
jmp PentiumExit
DoPentium:
db 0fh,31h ; RDTSC instruction
mov [ebp+CounterOffset],eax
mov [ebp+CounterOffset+4],edx
PentiumExit:
endm
_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
if TRACE
Msg1 db 'ApiMonThunk() 0x%08x',0ah,00
Msg2 db 'ApiMonThunkComplete() 0x%08x',0ah,00
endif
;
; Routine Description:
;
; This function is jumped to after a monitored
; API has completed. Here we do our cleanup
; and then branch to the caller's return address.
;
; Arguments:
;
; ebp - points to the api's frame on our parallel stack
; eax - return value
; edx - return value
;
; Return Value:
;
; None.
;
cPublicProc _ApiMonThunkComplete,0
;
; save registers
;
push eax
push ebx
push ecx
push edx
push esi
push edi
;
; save the last error value
;
call _pGetLastError
push eax
if TRACE
;
; trace the call
;
mov eax,[ebp+ApiInfoFrm]
mov eax,[eax+ApiInfoAddressOffset]
push eax
push offset FLAT:Msg2
call _dprintf
add esp,8
endif
;
; check for counting enabled
;
mov edx,[ebp+DllInfoFrm]
mov eax,[edx+DllEnabledOffset]
or eax,eax
jz NoCounting
;
; decrement nesting count for this api
;
mov eax,[ebp+ApiInfoFrm]
dec dword ptr [eax+ApiInfoNestCountOffset]
;
; Compute total function time
;
GET_PERFORMANCE_COUNTER TempTimeFrm
mov eax,[ebp+TempTimeFrm]
mov edx,[ebp+TempTimeFrm+4]
sub eax,[ebp+FuncTimeFrm]
sbb edx,[ebp+FuncTimeFrm+4]
sub eax,_ThunkOverhead
sbb edx,0
mov [ebp+FuncTimeFrm],eax
mov [ebp+FuncTimeFrm+4],edx
;
; Remove function time from our overhead time
; by adding advancing the overhead start time
;
add [ebp+OverheadTimeFrm],eax
adc [ebp+OverheadTimeFrm+4],edx
;
; accumulate function time for the API
;
mov edi,[ebp+ApiInfoFrm]
add [edi+ApiInfoTimeOffset],eax
adc [edi+ApiInfoTimeOffset+4],edx
;
; add time to callee time of parent frame
;
add [ebp - FrameSize + CalleeTimeFrm],eax
adc [ebp - FrameSize + CalleeTimeFrm+4],edx
;
; accumulate own callee time for the API
;
mov eax, [ebp+CalleeTimeFrm]
mov edx, [ebp+CalleeTimeFrm+4]
add [edi+ApiInfoCalleeTimeOffset],eax
adc [edi+ApiInfoCalleeTimeOffset+4],edx
NoCounting:
;
; handle load library and get process address specialy
;
mov ecx,[ebp+ApiFlagFrm]
or ecx,ecx
jz ThunkNormal
;
; branch to the correct handler
;
cmp ecx,APITYPE_LOADLIBRARYA
jz DoLoadLibraryA
cmp ecx,APITYPE_LOADLIBRARYW
jz DoLoadLibraryW
cmp ecx,APITYPE_FREELIBRARY
jz DoFreeLibrary
cmp ecx,APITYPE_GETPROCADDRESS
jz DoGetProcAddress
cmp ecx,APITYPE_WNDPROC ; no tracing for wnd procs yet
jz NoTracing
jmp ThunkNormal
DoFreeLibrary:
push [ebp+Arg0Frm]
call _HandleDynamicDllFree@4
jmp ThunkNormal
DoLoadLibraryW:
push [ebp+Arg0Frm]
push [esp+EaxSave+4]
call _HandleDynamicDllLoadW@8
jmp ThunkNormal
DoLoadLibraryA:
push [ebp+Arg0Frm]
push [esp+EaxSave+4]
call _HandleDynamicDllLoadA@8
jmp ThunkNormal
DoGetProcAddress:
push [esp+EaxSave]
call _HandleGetProcAddress@4
mov [esp+EaxSave],eax
jmp ThunkNormal
ThunkNormal:
;
; do the api tracing?
;
mov eax,[_ApiTraceEnabled]
mov eax,[eax]
or eax, eax
jz NoTracing
mov eax,[ebp+DllInfoFrm]
mov eax,[eax+DllEnabledOffset]
or eax,eax
jz NoTracing
mov eax,[ebp+ApiInfoFrm]
mov eax,[eax+ApiInfoTraceEnabled]
or eax,eax
jz NoTracing
;
; trace the api
;
mov eax,[esp+EaxSave] ; get ret value
push [esp+LastErrorSave] ; last error value
push [ebp+FuncTimeFrm+4] ; function duration
push [ebp+FuncTimeFrm]
push [ebp+TempTimeFrm+4] ; exit time
push [ebp+TempTimeFrm]
push [ebp+RetAddrFrm] ; caller's address
push eax ; return value
lea eax,[ebp+Arg0Frm] ; parameter array
push eax
push [ebp+ApiInfoFrm] ; apiinfo pointer
call _ApiTrace@36
NoTracing:
;
; Compute our overhead time
;
GET_PERFORMANCE_COUNTER TempTimeFrm
mov eax,[ebp+TempTimeFrm]
mov edx,[ebp+TempTimeFrm+4]
sub eax,[ebp+OverheadTimeFrm]
sbb edx,[ebp+OverheadTimeFrm+4]
add eax,_ThunkCallOverhead
adc edx,0
;
; Subtract from parent's function time
; by advancing the function start time
;
add [ebp - FrameSize + FuncTimeFrm],eax
adc [ebp - FrameSize + FuncTimeFrm+4],edx
;
; destroy the frame on the stack
;
push _TlsStack
call _pTlsGetValue
mov [eax],ebp
;
; reset the last error value (already on the stack)
;
call _pSetLastError
;
; restore the registers
;
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
push [ebp+RetAddrFrm]
mov ebp,[ebp]
;
; finally branch back to the caller
;
ret
stdENDP _ApiMonThunkComplete
;
; Routine Description:
;
; This function is the entry point for the api
; monitor thunk.
;
; Arguments:
;
; [esp+0] - API flag
; [esp+4] - DLLINFO pointer
; [esp+8] - APIINFO pointer
;
; Return Value:
;
; None.
;
cPublicProc _ApiMonThunk,0
;
; save regs
;
push eax
push ebx
push ecx
push edx
push esi
push edi
;
; save the last error value
;
call _pGetLastError
push eax
;
; get the reentry flag
;
push _TlsReEnter
call _pTlsGetValue
;
; don't enter if disallow flag is set
;
or eax,eax
jz ThunkOk
BadStack:
;
; replace ApiInfo pointer with Api address
; so we can ret to it
;
mov ebx,[esp+ApiInfoSave]
mov eax,dword ptr [ebx+ApiInfoAddressOffset]
mov [esp+ApiInfoSave],eax
;
; reset the last error value
;
call _pSetLastError
;
; restore the registers
;
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
add sp,8 ; dump ApiFlag & DllInfo space
ret ; jmp to real Api
ThunkOk:
;
; get the parallel stack pointer
;
push _TlsStack
call _pTlsGetValue
or eax,eax
jz BadStack
;
; setup the frame pointer
;
mov edx,[eax]
mov [edx],ebp
mov ebp,edx
;
; create a frame on the stack by advancing the pointer
;
add edx,FrameSize
mov [eax],edx
;
; clear ApiBias, entry from _penter uses bias
;
mov [ebp+ApiBiasFrm],0
Thunk_Middle:
;
; get the arguments from the mini-thunk
;
mov eax,[esp+ApiFlagSave]
mov [ebp+ApiFlagFrm],eax
mov eax,[esp+DllInfoSave]
mov [ebp+DllInfoFrm],eax
mov eax,[esp+RetAddrSave]
mov [ebp+RetAddrFrm],eax
mov eax,[esp+ApiInfoSave]
mov [ebp+ApiInfoFrm],eax
;
; save the real arguments
;
mov eax,[esp+StackSize]
mov [ebp+Arg0Frm],eax
mov eax,[esp+StackSize+4]
mov [ebp+Arg1Frm],eax
mov eax,[esp+StackSize+8]
mov [ebp+Arg2Frm],eax
mov eax,[esp+StackSize+12]
mov [ebp+Arg3Frm],eax
mov eax,[esp+StackSize+16]
mov [ebp+Arg4Frm],eax
mov eax,[esp+StackSize+20]
mov [ebp+Arg5Frm],eax
mov eax,[esp+StackSize+24]
mov [ebp+Arg6Frm],eax
mov eax,[esp+StackSize+28]
mov [ebp+Arg7Frm],eax
;
; zero the callee time
;
mov [ebp+CalleeTimeFrm],0
mov [ebp+CalleeTimeFrm+4],0
;
; start the overhead timer, because it is variable from here on
;
GET_PERFORMANCE_COUNTER OverheadTimeFrm
;
; Do special API processing
;
mov eax,[ebp+ApiFlagFrm]
or eax,eax
jz ThunkNotSpecial
cmp eax,APITYPE_REGISTERCLASSA
jz DoRegisterClassA
cmp eax,APITYPE_REGISTERCLASSW
jz DoRegisterClassW
cmp eax,APITYPE_SETWINDOWLONG
jz DoSetWindowLong
jmp ThunkNotSpecial
DoRegisterClassA:
push [ebp+Arg0Frm]
call _HandleRegisterClassA@4
jmp ThunkNotSpecial
DoRegisterClassW:
push [ebp+Arg0Frm]
call _HandleRegisterClassW@4
jmp ThunkNotSpecial
DoSetWindowLong:
push [ebp+Arg2Frm]
push [ebp+Arg1Frm]
push [ebp+Arg0Frm]
call _HandleSetWindowLong@12
mov [esp+StackSize+8],eax ; Replace new long value parameter
ThunkNotSpecial:
;
; change the return address to point to completion routine
;
mov [esp+RetAddrSave],_ApiMonThunkComplete
if TRACE
;
; trace the call
;
mov eax,[ebp+ApiInfoFrm]
mov eax,[eax+ApiInfoAddressOffset]
push eax
push offset FLAT:Msg1
call _dprintf
add esp,8
endif
;
; check to see if api counting is enabled
; if not then bypass the counting code
;
mov eax,[ebp+DllInfoFrm]
mov eax,[eax+DllEnabledOffset]
or eax, eax
jz ThunkBypass
;
; increment the api's counters
;
mov eax,[ebp+ApiInfoFrm]
inc dword ptr [eax+ApiInfoCountOffset]
inc dword ptr [eax+ApiInfoNestCountOffset]
;
; increment the global api counter
;
mov eax,_ApiCounter
inc dword ptr [eax]
ThunkBypass:
;
; Replace ApiInfo pointer with ApiAddr (+bias)
;
mov ebx,[ebp+ApiInfoFrm]
mov eax,dword ptr [ebx+ApiInfoAddressOffset]
mov ecx,[ebp+ApiBiasFrm]
add eax,ecx
mov [esp+ApiInfoSave],eax
;
; start the function timer here
;
GET_PERFORMANCE_COUNTER FuncTimeFrm
;
; reset the last error value (already on stack)
;
call _pSetLastError
;
; restore the registers
;
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
add sp,8 ; dump ApiFlag & DllInfo from stack
ret ; jump to Api
stdENDP _ApiMonThunk
;
; Routine Description:
;
; This function is called when an application
; is compiled with -Gh. It performs the same
; function as ApiMonThunk does for non-instrumented
; code.
;
; Arguments:
;
; None.
;
; Return Value:
;
; None.
;
align dword
public __penter
__penter proc
;
; allot space on stack for two api thunk parameters
; the third will replace the return address
;
sub sp,8
;
; save regs
;
push eax
push ebx
push ecx
push edx
push esi
push edi
;
; save the last error value
;
call _pGetLastError
push eax
;
; get the parallel stack pointer
;
push _TlsStack
call _pTlsGetValue
or eax,eax
jnz Good_Stack
;
; reset the last error value
;
call _pSetLastError
;
; restore the stack
;
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
add sp,8
;
; jump to the real api
;
ret
Good_Stack:
;
; setup the frame pointer
;
mov edx,[eax]
mov [edx],ebp
mov ebp,edx
;
; create a frame on the stack
;
add edx,FrameSize
mov [eax],edx
;
; get Api info from the return address, which is really
; the address of the function that is being profiled
;
mov eax,[esp+ApiInfoSave] ; this is really the return to the Api
mov [ebp+RetAddrFrm],eax ; save for exit in case Api not found
lea ecx,[esp+ApiFlagSave]
push eax
push ecx
add ecx,4
push ecx
add ecx,4
push ecx
call _GetApiInfo@16
or eax,eax
jz Api_NotFound
mov [ebp+ApiBiasFrm],5
jmp Thunk_Middle
Api_NotFound:
;
; put back saved return address
;
mov eax,[ebp+RetAddrFrm]
mov [esp+ApiInfoSave],eax
;
; tear down this frame
;
push _TlsStack
call _pTlsGetValue
mov [eax],ebp
;
; reset the last error value (already on the stack)
;
call _pSetLastError
;
; restore the registers
;
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
mov ebp,[ebp]
;
; discard unused api thunk space
;
add esp,8
;
; jump to the real api
;
ret
__penter endp
_TEXT ENDS
end