summaryrefslogtreecommitdiff
path: root/src/vm/i386/asmhelpers.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/i386/asmhelpers.asm')
-rw-r--r--src/vm/i386/asmhelpers.asm2400
1 files changed, 2400 insertions, 0 deletions
diff --git a/src/vm/i386/asmhelpers.asm b/src/vm/i386/asmhelpers.asm
new file mode 100644
index 0000000000..66a22b7962
--- /dev/null
+++ b/src/vm/i386/asmhelpers.asm
@@ -0,0 +1,2400 @@
+; Licensed to the .NET Foundation under one or more agreements.
+; The .NET Foundation licenses this file to you under the MIT license.
+; See the LICENSE file in the project root for more information.
+
+; ==++==
+;
+
+;
+; ==--==
+;
+; FILE: asmhelpers.asm
+;
+; *** NOTE: If you make changes to this file, propagate the changes to
+; asmhelpers.s in this directory
+;
+
+;
+; ======================================================================================
+
+ .586
+ .model flat
+
+include asmconstants.inc
+
+ assume fs: nothing
+ option casemap:none
+ .code
+
+EXTERN __imp__RtlUnwind@16:DWORD
+ifdef _DEBUG
+EXTERN _HelperMethodFrameConfirmState@20:PROC
+endif
+ifdef FEATURE_MIXEDMODE
+EXTERN _IJWNOADThunkJumpTargetHelper@4:PROC
+endif
+EXTERN _StubRareEnableWorker@4:PROC
+ifdef FEATURE_COMINTEROP
+EXTERN _StubRareDisableHRWorker@4:PROC
+endif ; FEATURE_COMINTEROP
+EXTERN _StubRareDisableTHROWWorker@4:PROC
+EXTERN __imp__TlsGetValue@4:DWORD
+TlsGetValue PROTO stdcall
+ifdef FEATURE_HIJACK
+EXTERN _OnHijackWorker@4:PROC
+endif ;FEATURE_HIJACK
+EXTERN _COMPlusEndCatch@20:PROC
+EXTERN _COMPlusFrameHandler:PROC
+ifdef FEATURE_COMINTEROP
+EXTERN _COMPlusFrameHandlerRevCom:PROC
+endif ; FEATURE_COMINTEROP
+EXTERN __alloca_probe:PROC
+EXTERN _NDirectImportWorker@4:PROC
+EXTERN _UMThunkStubRareDisableWorker@8:PROC
+ifndef FEATURE_IMPLICIT_TLS
+ifdef ENABLE_GET_THREAD_GENERIC_FULL_CHECK
+; This is defined in C (threads.cpp) and enforces EE_THREAD_NOT_REQUIRED contracts
+GetThreadGenericFullCheck EQU ?GetThreadGenericFullCheck@@YGPAVThread@@XZ
+EXTERN GetThreadGenericFullCheck:PROC
+endif ; ENABLE_GET_THREAD_GENERIC_FULL_CHECK
+
+EXTERN _gThreadTLSIndex:DWORD
+EXTERN _gAppDomainTLSIndex:DWORD
+endif ; FEATURE_IMPLICIT_TLS
+
+EXTERN _VarargPInvokeStubWorker@12:PROC
+EXTERN _GenericPInvokeCalliStubWorker@12:PROC
+
+; To debug that LastThrownObjectException really is EXCEPTION_COMPLUS
+ifdef TRACK_CXX_EXCEPTION_CODE_HACK
+EXTERN __imp____CxxFrameHandler:PROC
+endif
+
+EXTERN _GetThread@0:PROC
+EXTERN _GetAppDomain@0:PROC
+
+ifdef MDA_SUPPORTED
+EXTERN _PInvokeStackImbalanceWorker@8:PROC
+endif
+
+ifndef FEATURE_CORECLR
+EXTERN _CopyCtorCallStubWorker@4:PROC
+endif
+
+EXTERN _PreStubWorker@8:PROC
+
+ifdef FEATURE_COMINTEROP
+EXTERN _CLRToCOMWorker@8:PROC
+endif
+
+ifdef FEATURE_REMOTING
+EXTERN _TransparentProxyStubWorker@8:PROC
+endif
+
+ifdef FEATURE_PREJIT
+EXTERN _ExternalMethodFixupWorker@16:PROC
+EXTERN _VirtualMethodFixupWorker@8:PROC
+EXTERN _StubDispatchFixupWorker@16:PROC
+endif
+
+ifdef FEATURE_COMINTEROP
+EXTERN _ComPreStubWorker@8:PROC
+endif
+
+ifdef FEATURE_READYTORUN
+EXTERN _DynamicHelperWorker@20:PROC
+endif
+
+ifdef FEATURE_REMOTING
+EXTERN _InContextTPQuickDispatchAsmStub@0:PROC
+endif
+
+EXTERN @JIT_InternalThrow@4:PROC
+
+EXTERN @ProfileEnter@8:PROC
+EXTERN @ProfileLeave@8:PROC
+EXTERN @ProfileTailcall@8:PROC
+
+UNREFERENCED macro arg
+ local unref
+ unref equ size arg
+endm
+
+FASTCALL_FUNC macro FuncName,cbArgs
+FuncNameReal EQU @&FuncName&@&cbArgs
+FuncNameReal proc public
+endm
+
+FASTCALL_ENDFUNC macro
+FuncNameReal endp
+endm
+
+ifdef FEATURE_COMINTEROP
+ifdef _DEBUG
+ CPFH_STACK_SIZE equ SIZEOF_FrameHandlerExRecord + STACK_OVERWRITE_BARRIER_SIZE*4
+else ; _DEBUG
+ CPFH_STACK_SIZE equ SIZEOF_FrameHandlerExRecord
+endif ; _DEBUG
+
+PUSH_CPFH_FOR_COM macro trashReg, pFrameBaseReg, pFrameOffset
+
+ ;
+ ; Setup the FrameHandlerExRecord
+ ;
+ push dword ptr [pFrameBaseReg + pFrameOffset]
+ push _COMPlusFrameHandlerRevCom
+ mov trashReg, fs:[0]
+ push trashReg
+ mov fs:[0], esp
+
+ifdef _DEBUG
+ mov trashReg, STACK_OVERWRITE_BARRIER_SIZE
+@@:
+ push STACK_OVERWRITE_BARRIER_VALUE
+ dec trashReg
+ jnz @B
+endif ; _DEBUG
+
+endm ; PUSH_CPFH_FOR_COM
+
+
+POP_CPFH_FOR_COM macro trashReg
+
+ ;
+ ; Unlink FrameHandlerExRecord from FS:0 chain
+ ;
+ifdef _DEBUG
+ add esp, STACK_OVERWRITE_BARRIER_SIZE*4
+endif
+ mov trashReg, [esp + OFFSETOF__FrameHandlerExRecord__m_ExReg__Next]
+ mov fs:[0], trashReg
+ add esp, SIZEOF_FrameHandlerExRecord
+
+endm ; POP_CPFH_FOR_COM
+endif ; FEATURE_COMINTEROP
+
+;
+; FramedMethodFrame prolog
+;
+STUB_PROLOG macro
+ ; push ebp-frame
+ push ebp
+ mov ebp,esp
+
+ ; save CalleeSavedRegisters
+ push ebx
+ push esi
+ push edi
+
+ ; push ArgumentRegisters
+ push ecx
+ push edx
+endm
+
+;
+; FramedMethodFrame epilog
+;
+STUB_EPILOG macro
+ ; pop ArgumentRegisters
+ pop edx
+ pop ecx
+
+ ; pop CalleeSavedRegisters
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+endm
+
+;
+; FramedMethodFrame epilog
+;
+STUB_EPILOG_RETURN macro
+ ; pop ArgumentRegisters
+ add esp, 8
+
+ ; pop CalleeSavedRegisters
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+endm
+
+STUB_PROLOG_2_HIDDEN_ARGS macro
+
+ ;
+ ; The stub arguments are where we want to setup the TransitionBlock. We will
+ ; setup the TransitionBlock later once we can trash them
+ ;
+ ; push ebp-frame
+ ; push ebp
+ ; mov ebp,esp
+
+ ; save CalleeSavedRegisters
+ ; push ebx
+
+ push esi
+ push edi
+
+ ; push ArgumentRegisters
+ push ecx
+ push edx
+
+ mov ecx, [esp + 4*4]
+ mov edx, [esp + 5*4]
+
+ ; Setup up proper EBP frame now that the stub arguments can be trashed
+ mov [esp + 4*4],ebx
+ mov [esp + 5*4],ebp
+ lea ebp, [esp + 5*4]
+endm
+
+ResetCurrentContext PROC stdcall public
+ LOCAL ctrlWord:WORD
+
+ ; Clear the direction flag (used for rep instructions)
+ cld
+
+ fnstcw ctrlWord
+ fninit ; reset FPU
+ and ctrlWord, 0f00h ; preserve precision and rounding control
+ or ctrlWord, 007fh ; mask all exceptions
+ fldcw ctrlWord ; preserve precision control
+ RET
+ResetCurrentContext ENDP
+
+;Incoming:
+; ESP+4: Pointer to buffer to which FPU state should be saved
+_CaptureFPUContext@4 PROC public
+
+ mov ecx, [esp+4]
+ fnstenv [ecx]
+ retn 4
+
+_CaptureFPUContext@4 ENDP
+
+; Incoming:
+; ESP+4: Pointer to buffer from which FPU state should be restored
+_RestoreFPUContext@4 PROC public
+
+ mov ecx, [esp+4]
+ fldenv [ecx]
+ retn 4
+
+_RestoreFPUContext@4 ENDP
+
+ifndef FEATURE_CORECLR
+ifdef _DEBUG
+; For C++ exceptions, we desperately need to know the SEH code. This allows us to properly
+; distinguish managed exceptions from C++ exceptions from standard SEH like hard stack overflow.
+; We do this by providing our own handler that squirrels away the exception code and then
+; defers to the C++ service. Fortunately, two symbols exist for the C++ symbol.
+___CxxFrameHandler3 PROC public
+
+ ; We don't know what arguments are passed to us (except for the first arg on stack)
+ ; It turns out that EAX is part of the non-standard calling convention of this
+ ; function.
+
+ push eax
+ push edx
+
+ cmp dword ptr [_gThreadTLSIndex], -1
+ je Chain ; CLR is not initialized yet
+
+ call _GetThread@0
+
+ test eax, eax ; not a managed thread
+ jz Chain
+
+ mov edx, [esp + 0ch] ; grab the first argument
+ mov edx, [edx] ; grab the SEH exception code
+
+ mov dword ptr [eax + Thread_m_LastCxxSEHExceptionCode], edx
+
+Chain:
+
+ pop edx
+
+ ; [esp] contains the value of EAX we must restore. We would like
+ ; [esp] to contain the address of the real imported CxxFrameHandler
+ ; so we can chain to it.
+
+ mov eax, [__imp____CxxFrameHandler]
+ mov eax, [eax]
+ xchg [esp], eax
+
+ ret
+
+___CxxFrameHandler3 ENDP
+endif ; _DEBUG
+endif ; FEATURE_CORECLR
+
+; Register CLR exception handlers defined on the C++ side with SAFESEH.
+; Note that these directives must be in a file that defines symbols that will be used during linking,
+; otherwise it's possible that the resulting .obj will completly be ignored by the linker and these
+; directives will have no effect.
+COMPlusFrameHandler proto c
+.safeseh COMPlusFrameHandler
+
+COMPlusNestedExceptionHandler proto c
+.safeseh COMPlusNestedExceptionHandler
+
+FastNExportExceptHandler proto c
+.safeseh FastNExportExceptHandler
+
+UMThunkPrestubHandler proto c
+.safeseh UMThunkPrestubHandler
+
+ifdef FEATURE_COMINTEROP
+COMPlusFrameHandlerRevCom proto c
+.safeseh COMPlusFrameHandlerRevCom
+endif
+
+; Note that RtlUnwind trashes EBX, ESI and EDI, so this wrapper preserves them
+CallRtlUnwind PROC stdcall public USES ebx esi edi, pEstablisherFrame :DWORD, callback :DWORD, pExceptionRecord :DWORD, retVal :DWORD
+
+ push retVal
+ push pExceptionRecord
+ push callback
+ push pEstablisherFrame
+ call dword ptr [__imp__RtlUnwind@16]
+
+ ; return 1
+ push 1
+ pop eax
+
+ RET
+CallRtlUnwind ENDP
+
+_ResumeAtJitEHHelper@4 PROC public
+ mov edx, [esp+4] ; edx = pContext (EHContext*)
+
+ mov ebx, [edx+EHContext_Ebx]
+ mov esi, [edx+EHContext_Esi]
+ mov edi, [edx+EHContext_Edi]
+ mov ebp, [edx+EHContext_Ebp]
+ mov ecx, [edx+EHContext_Esp]
+ mov eax, [edx+EHContext_Eip]
+ mov [ecx-4], eax
+ mov eax, [edx+EHContext_Eax]
+ mov [ecx-8], eax
+ mov eax, [edx+EHContext_Ecx]
+ mov [ecx-0Ch], eax
+ mov eax, [edx+EHContext_Edx]
+ mov [ecx-10h], eax
+ lea esp, [ecx-10h]
+ pop edx
+ pop ecx
+ pop eax
+ ret
+_ResumeAtJitEHHelper@4 ENDP
+
+; int __stdcall CallJitEHFilterHelper(size_t *pShadowSP, EHContext *pContext);
+; on entry, only the pContext->Esp, Ebx, Esi, Edi, Ebp, and Eip are initialized
+_CallJitEHFilterHelper@8 PROC public
+ push ebp
+ mov ebp, esp
+ push ebx
+ push esi
+ push edi
+
+ pShadowSP equ [ebp+8]
+ pContext equ [ebp+12]
+
+ mov eax, pShadowSP ; Write esp-4 to the shadowSP slot
+ test eax, eax
+ jz DONE_SHADOWSP_FILTER
+ mov ebx, esp
+ sub ebx, 4
+ or ebx, SHADOW_SP_IN_FILTER_ASM
+ mov [eax], ebx
+ DONE_SHADOWSP_FILTER:
+
+ mov edx, [pContext]
+ mov eax, [edx+EHContext_Eax]
+ mov ebx, [edx+EHContext_Ebx]
+ mov esi, [edx+EHContext_Esi]
+ mov edi, [edx+EHContext_Edi]
+ mov ebp, [edx+EHContext_Ebp]
+
+ call dword ptr [edx+EHContext_Eip]
+ifdef _DEBUG
+ nop ; Indicate that it is OK to call managed code directly from here
+endif
+
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp ; don't use 'leave' here, as ebp as been trashed
+ retn 8
+_CallJitEHFilterHelper@8 ENDP
+
+
+; void __stdcall CallJITEHFinallyHelper(size_t *pShadowSP, EHContext *pContext);
+; on entry, only the pContext->Esp, Ebx, Esi, Edi, Ebp, and Eip are initialized
+_CallJitEHFinallyHelper@8 PROC public
+ push ebp
+ mov ebp, esp
+ push ebx
+ push esi
+ push edi
+
+ pShadowSP equ [ebp+8]
+ pContext equ [ebp+12]
+
+ mov eax, pShadowSP ; Write esp-4 to the shadowSP slot
+ test eax, eax
+ jz DONE_SHADOWSP_FINALLY
+ mov ebx, esp
+ sub ebx, 4
+ mov [eax], ebx
+ DONE_SHADOWSP_FINALLY:
+
+ mov edx, [pContext]
+ mov eax, [edx+EHContext_Eax]
+ mov ebx, [edx+EHContext_Ebx]
+ mov esi, [edx+EHContext_Esi]
+ mov edi, [edx+EHContext_Edi]
+ mov ebp, [edx+EHContext_Ebp]
+ call dword ptr [edx+EHContext_Eip]
+ifdef _DEBUG
+ nop ; Indicate that it is OK to call managed code directly from here
+endif
+
+ ; Reflect the changes to the context and only update non-volatile registers.
+ ; This will be used later to update REGDISPLAY
+ mov edx, [esp+12+12]
+ mov [edx+EHContext_Ebx], ebx
+ mov [edx+EHContext_Esi], esi
+ mov [edx+EHContext_Edi], edi
+ mov [edx+EHContext_Ebp], ebp
+
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp ; don't use 'leave' here, as ebp as been trashed
+ retn 8
+_CallJitEHFinallyHelper@8 ENDP
+
+
+_GetSpecificCpuTypeAsm@0 PROC public
+ push ebx ; ebx is trashed by the cpuid calls
+
+ ; See if the chip supports CPUID
+ pushfd
+ pop ecx ; Get the EFLAGS
+ mov eax, ecx ; Save for later testing
+ xor ecx, 200000h ; Invert the ID bit.
+ push ecx
+ popfd ; Save the updated flags.
+ pushfd
+ pop ecx ; Retrieve the updated flags
+ xor ecx, eax ; Test if it actually changed (bit set means yes)
+ push eax
+ popfd ; Restore the flags
+
+ test ecx, 200000h
+ jz Assume486
+
+ xor eax, eax
+ cpuid
+
+ test eax, eax
+ jz Assume486 ; brif CPUID1 not allowed
+
+ mov eax, 1
+ cpuid
+
+ ; filter out everything except family and model
+ ; Note that some multi-procs have different stepping number for each proc
+ and eax, 0ff0h
+
+ jmp CpuTypeDone
+
+Assume486:
+ mov eax, 0400h ; report 486
+CpuTypeDone:
+ pop ebx
+ retn
+_GetSpecificCpuTypeAsm@0 ENDP
+
+; DWORD __stdcall GetSpecificCpuFeaturesAsm(DWORD *pInfo);
+_GetSpecificCpuFeaturesAsm@4 PROC public
+ push ebx ; ebx is trashed by the cpuid calls
+
+ ; See if the chip supports CPUID
+ pushfd
+ pop ecx ; Get the EFLAGS
+ mov eax, ecx ; Save for later testing
+ xor ecx, 200000h ; Invert the ID bit.
+ push ecx
+ popfd ; Save the updated flags.
+ pushfd
+ pop ecx ; Retrieve the updated flags
+ xor ecx, eax ; Test if it actually changed (bit set means yes)
+ push eax
+ popfd ; Restore the flags
+
+ test ecx, 200000h
+ jz CpuFeaturesFail
+
+ xor eax, eax
+ cpuid
+
+ test eax, eax
+ jz CpuFeaturesDone ; br if CPUID1 not allowed
+
+ mov eax, 1
+ cpuid
+ mov eax, edx ; return all feature flags
+ mov edx, [esp+8]
+ test edx, edx
+ jz CpuFeaturesDone
+ mov [edx],ebx ; return additional useful information
+ jmp CpuFeaturesDone
+
+CpuFeaturesFail:
+ xor eax, eax ; Nothing to report
+CpuFeaturesDone:
+ pop ebx
+ retn 4
+_GetSpecificCpuFeaturesAsm@4 ENDP
+
+
+;-----------------------------------------------------------------------
+; The out-of-line portion of the code to enable preemptive GC.
+; After the work is done, the code jumps back to the "pRejoinPoint"
+; which should be emitted right after the inline part is generated.
+;
+; Assumptions:
+; ebx = Thread
+; Preserves
+; all registers except ecx.
+;
+;-----------------------------------------------------------------------
+_StubRareEnable proc public
+ push eax
+ push edx
+
+ push ebx
+ call _StubRareEnableWorker@4
+
+ pop edx
+ pop eax
+ retn
+_StubRareEnable ENDP
+
+ifdef FEATURE_COMINTEROP
+_StubRareDisableHR proc public
+ push edx
+
+ push ebx ; Thread
+ call _StubRareDisableHRWorker@4
+
+ pop edx
+ retn
+_StubRareDisableHR ENDP
+endif ; FEATURE_COMINTEROP
+
+_StubRareDisableTHROW proc public
+ push eax
+ push edx
+
+ push ebx ; Thread
+ call _StubRareDisableTHROWWorker@4
+
+ pop edx
+ pop eax
+ retn
+_StubRareDisableTHROW endp
+
+
+ifdef FEATURE_MIXEDMODE
+; VOID __stdcall IJWNOADThunkJumpTarget(void);
+; This routine is used by the IJWNOADThunk to determine the callsite of the domain-specific stub to call.
+_IJWNOADThunkJumpTarget@0 proc public
+
+ push ebp
+ mov ebp, esp
+
+ ; EAX contains IJWNOADThunk*
+ ; Must retain ebx, ecx, edx, esi, edi.
+
+ ; save ebx - holds the IJWNOADThunk*
+ ; save ecx - holds the current AppDomain ID.
+ ; save edx - holds the cached AppDomain ID.
+ push ebx
+ push ecx
+
+ ; put the IJWNOADThunk into ebx for safe keeping
+ mov ebx, eax
+
+ ; get thread - assumes registers are preserved
+ call _GetThread@0
+
+ ; if thread is null, go down un-optimized path
+ test eax,eax
+ jz cachemiss
+
+ ; get current domain - assumes registers are preserved
+ call _GetAppDomain@0
+
+ ; if domain is null, go down un-optimized path
+ test eax,eax
+ jz cachemiss
+
+ ; get the current appdomain id
+ mov ecx, [eax + AppDomain__m_dwId]
+
+ ; test it against each cache location
+ mov eax, ebx
+ add eax, IJWNOADThunk__m_cache
+ cmp ecx, [eax]
+ je cachehit
+
+ add eax, IJWNOADThunk__NextCacheOffset
+ cmp ecx, [eax]
+ je cachehit
+
+ add eax, IJWNOADThunk__NextCacheOffset
+ cmp ecx, [eax]
+ je cachehit
+
+ add eax, IJWNOADThunk__NextCacheOffset
+ cmp ecx, [eax]
+ je cachehit
+
+cachemiss:
+ ; save extra registers
+ push edx
+ push esi
+ push edi
+
+ ; call unoptimized path
+ push ebx ; only arg is IJWNOADThunk*
+ call _IJWNOADThunkJumpTargetHelper@4
+
+ ; restore extra registers
+ pop edi
+ pop esi
+ pop edx
+
+ ; jump back up to the epilog
+ jmp complete
+
+cachehit:
+ ; found a matching ADID, get the code addr.
+ mov eax, [eax + IJWNOADThunk__CodeAddrOffsetFromADID]
+
+ ; if the callsite is null, go down the un-optimized path
+ test eax, eax
+ jz cachemiss
+
+complete:
+ ; restore regs
+ pop ecx
+ pop ebx
+
+ mov esp, ebp
+ pop ebp
+
+ ; Jump to callsite
+ jmp eax
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+_IJWNOADThunkJumpTarget@0 endp
+
+endif
+
+InternalExceptionWorker proc public
+ pop edx ; recover RETADDR
+ add esp, eax ; release caller's args
+ push edx ; restore RETADDR
+ jmp @JIT_InternalThrow@4
+InternalExceptionWorker endp
+
+; EAX -> number of caller arg bytes on the stack that we must remove before going
+; to the throw helper, which assumes the stack is clean.
+_ArrayOpStubNullException proc public
+ ; kFactorReg and kTotalReg could not have been modified, but let's pop
+ ; them anyway for consistency and to avoid future bugs.
+ pop esi
+ pop edi
+ mov ecx, CORINFO_NullReferenceException_ASM
+ jmp InternalExceptionWorker
+_ArrayOpStubNullException endp
+
+; EAX -> number of caller arg bytes on the stack that we must remove before going
+; to the throw helper, which assumes the stack is clean.
+_ArrayOpStubRangeException proc public
+ ; kFactorReg and kTotalReg could not have been modified, but let's pop
+ ; them anyway for consistency and to avoid future bugs.
+ pop esi
+ pop edi
+ mov ecx, CORINFO_IndexOutOfRangeException_ASM
+ jmp InternalExceptionWorker
+_ArrayOpStubRangeException endp
+
+; EAX -> number of caller arg bytes on the stack that we must remove before going
+; to the throw helper, which assumes the stack is clean.
+_ArrayOpStubTypeMismatchException proc public
+ ; kFactorReg and kTotalReg could not have been modified, but let's pop
+ ; them anyway for consistency and to avoid future bugs.
+ pop esi
+ pop edi
+ mov ecx, CORINFO_ArrayTypeMismatchException_ASM
+ jmp InternalExceptionWorker
+_ArrayOpStubTypeMismatchException endp
+
+;------------------------------------------------------------------------------
+; This helper routine enregisters the appropriate arguments and makes the
+; actual call.
+;------------------------------------------------------------------------------
+; void STDCALL CallDescrWorkerInternal(CallDescrWorkerParams * pParams)
+CallDescrWorkerInternal PROC stdcall public USES EBX,
+ pParams: DWORD
+
+ mov ebx, pParams
+
+ mov ecx, [ebx+CallDescrData__numStackSlots]
+ mov eax, [ebx+CallDescrData__pSrc] ; copy the stack
+ test ecx, ecx
+ jz donestack
+ lea eax, [eax+4*ecx-4] ; last argument
+ push dword ptr [eax]
+ dec ecx
+ jz donestack
+ sub eax, 4
+ push dword ptr [eax]
+ dec ecx
+ jz donestack
+stackloop:
+ sub eax, 4
+ push dword ptr [eax]
+ dec ecx
+ jnz stackloop
+donestack:
+
+ ; now we must push each field of the ArgumentRegister structure
+ mov eax, [ebx+CallDescrData__pArgumentRegisters]
+ mov edx, dword ptr [eax]
+ mov ecx, dword ptr [eax+4]
+
+ call [ebx+CallDescrData__pTarget]
+ifdef _DEBUG
+ nop ; This is a tag that we use in an assert. Fcalls expect to
+ ; be called from Jitted code or from certain blessed call sites like
+ ; this one. (See HelperMethodFrame::InsureInit)
+endif
+
+ ; Save FP return value if necessary
+ mov ecx, [ebx+CallDescrData__fpReturnSize]
+ cmp ecx, 0
+ je ReturnsInt
+
+ cmp ecx, 4
+ je ReturnsFloat
+ cmp ecx, 8
+ je ReturnsDouble
+ ; unexpected
+ jmp Epilog
+
+ReturnsInt:
+ mov [ebx+CallDescrData__returnValue], eax
+ mov [ebx+CallDescrData__returnValue+4], edx
+
+Epilog:
+ RET
+
+ReturnsFloat:
+ fstp dword ptr [ebx+CallDescrData__returnValue] ; Spill the Float return value
+ jmp Epilog
+
+ReturnsDouble:
+ fstp qword ptr [ebx+CallDescrData__returnValue] ; Spill the Double return value
+ jmp Epilog
+
+CallDescrWorkerInternal endp
+
+ifdef _DEBUG
+; int __fastcall HelperMethodFrameRestoreState(HelperMethodFrame*, struct MachState *)
+FASTCALL_FUNC HelperMethodFrameRestoreState,8
+ mov eax, edx ; eax = MachState*
+else
+; int __fastcall HelperMethodFrameRestoreState(struct MachState *)
+FASTCALL_FUNC HelperMethodFrameRestoreState,4
+ mov eax, ecx ; eax = MachState*
+endif
+ ; restore the registers from the m_MachState stucture. Note that
+ ; we only do this for register that where not saved on the stack
+ ; at the time the machine state snapshot was taken.
+
+ cmp [eax+MachState__pRetAddr], 0
+
+ifdef _DEBUG
+ jnz noConfirm
+ push ebp
+ push ebx
+ push edi
+ push esi
+ push ecx ; HelperFrame*
+ call _HelperMethodFrameConfirmState@20
+ ; on return, eax = MachState*
+ cmp [eax+MachState__pRetAddr], 0
+noConfirm:
+endif
+
+ jz doRet
+
+ lea edx, [eax+MachState__esi] ; Did we have to spill ESI
+ cmp [eax+MachState__pEsi], edx
+ jnz SkipESI
+ mov esi, [edx] ; Then restore it
+SkipESI:
+
+ lea edx, [eax+MachState__edi] ; Did we have to spill EDI
+ cmp [eax+MachState__pEdi], edx
+ jnz SkipEDI
+ mov edi, [edx] ; Then restore it
+SkipEDI:
+
+ lea edx, [eax+MachState__ebx] ; Did we have to spill EBX
+ cmp [eax+MachState__pEbx], edx
+ jnz SkipEBX
+ mov ebx, [edx] ; Then restore it
+SkipEBX:
+
+ lea edx, [eax+MachState__ebp] ; Did we have to spill EBP
+ cmp [eax+MachState__pEbp], edx
+ jnz SkipEBP
+ mov ebp, [edx] ; Then restore it
+SkipEBP:
+
+doRet:
+ xor eax, eax
+ retn
+FASTCALL_ENDFUNC HelperMethodFrameRestoreState
+
+
+ifndef FEATURE_IMPLICIT_TLS
+;---------------------------------------------------------------------------
+; Portable GetThread() function: used if no platform-specific optimizations apply.
+; This is in assembly code because we count on edx not getting trashed on calls
+; to this function.
+;---------------------------------------------------------------------------
+; Thread* __stdcall GetThreadGeneric(void);
+GetThreadGeneric PROC stdcall public USES ecx edx
+
+ifdef _DEBUG
+ cmp dword ptr [_gThreadTLSIndex], -1
+ jnz @F
+ int 3
+@@:
+endif
+ifdef ENABLE_GET_THREAD_GENERIC_FULL_CHECK
+ ; non-PAL, debug-only GetThreadGeneric should defer to GetThreadGenericFullCheck
+ ; to do extra contract enforcement. (See GetThreadGenericFullCheck for details.)
+ ; This code is intentionally not added to asmhelper.s, as this enforcement is only
+ ; implemented for non-PAL builds.
+ call GetThreadGenericFullCheck
+else
+ push dword ptr [_gThreadTLSIndex]
+ call dword ptr [__imp__TlsGetValue@4]
+endif
+ ret
+GetThreadGeneric ENDP
+
+;---------------------------------------------------------------------------
+; Portable GetAppdomain() function: used if no platform-specific optimizations apply.
+; This is in assembly code because we count on edx not getting trashed on calls
+; to this function.
+;---------------------------------------------------------------------------
+; Appdomain* __stdcall GetAppDomainGeneric(void);
+GetAppDomainGeneric PROC stdcall public USES ecx edx
+
+ifdef _DEBUG
+ cmp dword ptr [_gAppDomainTLSIndex], -1
+ jnz @F
+ int 3
+@@:
+endif
+
+ push dword ptr [_gAppDomainTLSIndex]
+ call dword ptr [__imp__TlsGetValue@4]
+ ret
+GetAppDomainGeneric ENDP
+endif
+
+
+ifdef FEATURE_HIJACK
+
+; A JITted method's return address was hijacked to return to us here.
+; VOID OnHijackTripThread()
+OnHijackTripThread PROC stdcall public
+
+ ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay
+ ; and HijackArgs
+ push eax ; make room for the real return address (Eip)
+ push ebp
+ push eax
+ push ecx
+ push edx
+ push ebx
+ push esi
+ push edi
+
+ ; unused space for floating point state
+ sub esp,12
+
+ push esp
+ call _OnHijackWorker@4
+
+ ; unused space for floating point state
+ add esp,12
+
+ pop edi
+ pop esi
+ pop ebx
+ pop edx
+ pop ecx
+ pop eax
+ pop ebp
+ retn ; return to the correct place, adjusted by our caller
+OnHijackTripThread ENDP
+
+; VOID OnHijackFPTripThread()
+OnHijackFPTripThread PROC stdcall public
+
+ ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay
+ ; and HijackArgs
+ push eax ; make room for the real return address (Eip)
+ push ebp
+ push eax
+ push ecx
+ push edx
+ push ebx
+ push esi
+ push edi
+
+ sub esp,12
+
+ ; save top of the floating point stack (there is return value passed in it)
+ ; save full 10 bytes to avoid precision loss
+ fstp tbyte ptr [esp]
+
+ push esp
+ call _OnHijackWorker@4
+
+ ; restore top of the floating point stack
+ fld tbyte ptr [esp]
+
+ add esp,12
+
+ pop edi
+ pop esi
+ pop ebx
+ pop edx
+ pop ecx
+ pop eax
+ pop ebp
+ retn ; return to the correct place, adjusted by our caller
+OnHijackFPTripThread ENDP
+
+endif ; FEATURE_HIJACK
+
+
+; Note that the debugger skips this entirely when doing SetIP,
+; since COMPlusCheckForAbort should always return 0. Excep.cpp:LeaveCatch
+; asserts that to be true. If this ends up doing more work, then the
+; debugger may need additional support.
+; void __stdcall JIT_EndCatch();
+JIT_EndCatch PROC stdcall public
+
+ ; make temp storage for return address, and push the address of that
+ ; as the last arg to COMPlusEndCatch
+ mov ecx, [esp]
+ push ecx;
+ push esp;
+
+ ; push the rest of COMPlusEndCatch's args, right-to-left
+ push esi
+ push edi
+ push ebx
+ push ebp
+
+ call _COMPlusEndCatch@20 ; returns old esp value in eax, stores jump address
+ ; now eax = new esp, [esp] = new eip
+
+ pop edx ; edx = new eip
+ mov esp, eax ; esp = new esp
+ jmp edx ; eip = new eip
+
+JIT_EndCatch ENDP
+
+;==========================================================================
+; This function is reached only via the embedded ImportThunkGlue code inside
+; an NDirectMethodDesc. It's purpose is to load the DLL associated with an
+; N/Direct method, then backpatch the DLL target into the methoddesc.
+;
+; Initial state:
+;
+; Preemptive GC is *enabled*: we are actually in an unmanaged state.
+;
+;
+; [esp+...] - The *unmanaged* parameters to the DLL target.
+; [esp+4] - Return address back into the JIT'ted code that made
+; the DLL call.
+; [esp] - Contains the "return address." Because we got here
+; thru a call embedded inside a MD, this "return address"
+; gives us an easy to way to find the MD (which was the
+; whole purpose of the embedded call manuever.)
+;
+;
+;
+;==========================================================================
+_NDirectImportThunk@0 proc public
+
+ ; Preserve argument registers
+ push ecx
+ push edx
+
+ ; Invoke the function that does the real work.
+ push eax
+ call _NDirectImportWorker@4
+
+ ; Restore argument registers
+ pop edx
+ pop ecx
+
+ ; If we got back from NDirectImportWorker, the MD has been successfully
+ ; linked and "eax" contains the DLL target. Proceed to execute the
+ ; original DLL call.
+ jmp eax ; Jump to DLL target
+_NDirectImportThunk@0 endp
+
+;==========================================================================
+; The call in fixup precode initally points to this function.
+; The pupose of this function is to load the MethodDesc and forward the call the prestub.
+_PrecodeFixupThunk@0 proc public
+
+ pop eax ; Pop the return address. It points right after the call instruction in the precode.
+ push esi
+ push edi
+
+ ; Inline computation done by FixupPrecode::GetMethodDesc()
+ movzx esi,byte ptr [eax+2] ; m_PrecodeChunkIndex
+ movzx edi,byte ptr [eax+1] ; m_MethodDescChunkIndex
+ mov eax,dword ptr [eax+esi*8+3]
+ lea eax,[eax+edi*4]
+
+ pop edi
+ pop esi
+ jmp _ThePreStub@0
+
+_PrecodeFixupThunk@0 endp
+
+; LPVOID __stdcall CTPMethodTable__CallTargetHelper2(
+; const void *pTarget,
+; LPVOID pvFirst,
+; LPVOID pvSecond)
+CTPMethodTable__CallTargetHelper2 proc stdcall public,
+ pTarget : DWORD,
+ pvFirst : DWORD,
+ pvSecond : DWORD
+ mov ecx, pvFirst
+ mov edx, pvSecond
+
+ call pTarget
+ifdef _DEBUG
+ nop ; Mark this as a special call site that can
+ ; directly call unmanaged code
+endif
+ ret
+CTPMethodTable__CallTargetHelper2 endp
+
+; LPVOID __stdcall CTPMethodTable__CallTargetHelper3(
+; const void *pTarget,
+; LPVOID pvFirst,
+; LPVOID pvSecond,
+; LPVOID pvThird)
+CTPMethodTable__CallTargetHelper3 proc stdcall public,
+ pTarget : DWORD,
+ pvFirst : DWORD,
+ pvSecond : DWORD,
+ pvThird : DWORD
+ push pvThird
+
+ mov ecx, pvFirst
+ mov edx, pvSecond
+
+ call pTarget
+ifdef _DEBUG
+ nop ; Mark this as a special call site that can
+ ; directly call unmanaged code
+endif
+ ret
+CTPMethodTable__CallTargetHelper3 endp
+
+
+; void __stdcall setFPReturn(int fpSize, INT64 retVal)
+_setFPReturn@12 proc public
+ mov ecx, [esp+4]
+
+ ; leave the return value in eax:edx if it is not the floating point case
+ mov eax, [esp+8]
+ mov edx, [esp+12]
+
+ cmp ecx, 4
+ jz setFPReturn4
+
+ cmp ecx, 8
+ jnz setFPReturnNot8
+ fld qword ptr [esp+8]
+setFPReturnNot8:
+ retn 12
+
+setFPReturn4:
+ fld dword ptr [esp+8]
+ retn 12
+_setFPReturn@12 endp
+
+; void __stdcall getFPReturn(int fpSize, INT64 *pretVal)
+_getFPReturn@8 proc public
+ mov ecx, [esp+4]
+ mov eax, [esp+8]
+ cmp ecx, 4
+ jz getFPReturn4
+
+ cmp ecx, 8
+ jnz getFPReturnNot8
+ fstp qword ptr [eax]
+getFPReturnNot8:
+ retn 8
+
+getFPReturn4:
+ fstp dword ptr [eax]
+ retn 8
+_getFPReturn@8 endp
+
+; void __stdcall UM2MThunk_WrapperHelper(void *pThunkArgs,
+; int argLen,
+; void *pAddr,
+; UMEntryThunk *pEntryThunk,
+; Thread *pThread)
+UM2MThunk_WrapperHelper proc stdcall public,
+ pThunkArgs : DWORD,
+ argLen : DWORD,
+ pAddr : DWORD,
+ pEntryThunk : DWORD,
+ pThread : DWORD
+ UNREFERENCED argLen
+
+ push ebx
+
+ mov eax, pEntryThunk
+ mov ecx, pThread
+ mov ebx, pThunkArgs
+ call pAddr
+
+ pop ebx
+
+ ret
+UM2MThunk_WrapperHelper endp
+
+; VOID __cdecl UMThunkStubRareDisable()
+;<TODO>
+; @todo: this is very similar to StubRareDisable
+;</TODO>
+_UMThunkStubRareDisable proc public
+ push eax
+ push ecx
+
+ push eax ; Push the UMEntryThunk
+ push ecx ; Push thread
+ call _UMThunkStubRareDisableWorker@8
+
+ pop ecx
+ pop eax
+ retn
+_UMThunkStubRareDisable endp
+
+
+;+----------------------------------------------------------------------------
+;
+; Method: CRemotingServices::CheckForContextMatch public
+;
+; Synopsis: This code generates a check to see if the current context and
+; the context of the proxy match.
+;
+;+----------------------------------------------------------------------------
+;
+; returns zero if contexts match
+; returns non-zero if contexts do not match
+;
+; UINT_PTR __stdcall CRemotingServices__CheckForContextMatch(Object* pStubData)
+ifdef FEATURE_REMOTING
+_CRemotingServices__CheckForContextMatch@4 proc public
+ push ebx ; spill ebx
+ mov ebx, [eax+4] ; Get the internal context id by unboxing
+ ; the stub data
+ call _GetThread@0 ; Get the current thread, assumes that the
+ ; registers are preserved
+ mov eax, [eax+Thread_m_Context] ; Get the current context from the
+ ; thread
+ sub eax, ebx ; Get the pointer to the context from the
+ ; proxy and compare with the current context
+ pop ebx ; restore the value of ebx
+ retn
+_CRemotingServices__CheckForContextMatch@4 endp
+endif ; FEATURE_REMOTING
+
+;+----------------------------------------------------------------------------
+;
+; Method: CRemotingServices::DispatchInterfaceCall public
+;
+; Synopsis:
+; Push that method desc on the stack and jump to the
+; transparent proxy stub to execute the call.
+; WARNING!! This MethodDesc is not the methoddesc in the vtable
+; of the object instead it is the methoddesc in the vtable of
+; the interface class. Since we use the MethodDesc only to probe
+; the stack via the signature of the method call we are safe.
+; If we want to get any object vtable/class specific
+; information this is not safe.
+;
+;
+;+----------------------------------------------------------------------------
+; void __stdcall CRemotingServices__DispatchInterfaceCall()
+ifdef FEATURE_REMOTING
+_CRemotingServices__DispatchInterfaceCall@0 proc public
+ ; push MethodDesc* passed in eax by precode and forward to the worker
+ push eax
+
+ ; NOTE: At this point the stack looks like
+ ;
+ ; esp---> saved MethodDesc of Interface method
+ ; return addr of calling function
+ ;
+ mov eax, [ecx + TransparentProxyObject___stubData]
+ call [ecx + TransparentProxyObject___stub]
+ifdef _DEBUG
+ nop ; Mark this as a special call site that can directly
+ ; call managed code
+endif
+ test eax, eax
+ jnz CtxMismatch
+ jmp _InContextTPQuickDispatchAsmStub@0
+
+CtxMismatch:
+ pop eax ; restore MethodDesc *
+ jmp _TransparentProxyStub_CrossContext@0 ; jump to slow TP stub
+_CRemotingServices__DispatchInterfaceCall@0 endp
+endif ; FEATURE_REMOTING
+
+
+;+----------------------------------------------------------------------------
+;
+; Method: CRemotingServices::CallFieldGetter private
+;
+; Synopsis: Calls the field getter function (Object::__FieldGetter) in
+; managed code by setting up the stack and calling the target
+;
+;
+;+----------------------------------------------------------------------------
+; void __stdcall CRemotingServices__CallFieldGetter(
+; MethodDesc *pMD,
+; LPVOID pThis,
+; LPVOID pFirst,
+; LPVOID pSecond,
+; LPVOID pThird)
+ifdef FEATURE_REMOTING
+CRemotingServices__CallFieldGetter proc stdcall public,
+ pMD : DWORD,
+ pThis : DWORD,
+ pFirst : DWORD,
+ pSecond : DWORD,
+ pThird : DWORD
+
+ push [pSecond] ; push the second argument on the stack
+ push [pThird] ; push the third argument on the stack
+
+ mov ecx, [pThis] ; enregister pThis, the 'this' pointer
+ mov edx, [pFirst] ; enregister pFirst, the first argument
+
+ mov eax, [pMD] ; load MethodDesc of object::__FieldGetter
+ call _TransparentProxyStub_CrossContext@0 ; call the TP stub
+
+ ret
+CRemotingServices__CallFieldGetter endp
+endif ; FEATURE_REMOTING
+
+;+----------------------------------------------------------------------------
+;
+; Method: CRemotingServices::CallFieldSetter private
+;
+; Synopsis: Calls the field setter function (Object::__FieldSetter) in
+; managed code by setting up the stack and calling the target
+;
+;
+;+----------------------------------------------------------------------------
+; void __stdcall CRemotingServices__CallFieldSetter(
+; MethodDesc *pMD,
+; LPVOID pThis,
+; LPVOID pFirst,
+; LPVOID pSecond,
+; LPVOID pThird)
+ifdef FEATURE_REMOTING
+CRemotingServices__CallFieldSetter proc stdcall public,
+ pMD : DWORD,
+ pThis : DWORD,
+ pFirst : DWORD,
+ pSecond : DWORD,
+ pThird : DWORD
+
+ push [pSecond] ; push the field name (second arg)
+ push [pThird] ; push the object (third arg) on the stack
+
+ mov ecx, [pThis] ; enregister pThis, the 'this' pointer
+ mov edx, [pFirst] ; enregister the first argument
+
+ mov eax, [pMD] ; load MethodDesc of object::__FieldGetter
+ call _TransparentProxyStub_CrossContext@0 ; call the TP stub
+
+ ret
+CRemotingServices__CallFieldSetter endp
+endif ; FEATURE_REMOTING
+
+;+----------------------------------------------------------------------------
+;
+; Method: CTPMethodTable::GenericCheckForContextMatch private
+;
+; Synopsis: Calls the stub in the TP & returns TRUE if the contexts
+; match, FALSE otherwise.
+;
+; Note: 1. Called during FieldSet/Get, used for proxy extensibility
+;
+;+----------------------------------------------------------------------------
+; BOOL __stdcall CTPMethodTable__GenericCheckForContextMatch(Object* orTP)
+ifdef FEATURE_REMOTING
+CTPMethodTable__GenericCheckForContextMatch proc stdcall public uses ecx, tp : DWORD
+
+ mov ecx, [tp]
+ mov eax, [ecx + TransparentProxyObject___stubData]
+ call [ecx + TransparentProxyObject___stub]
+ifdef _DEBUG
+ nop ; Mark this as a special call site that can directly
+ ; call managed code
+endif
+ test eax, eax
+ mov eax, 0
+ setz al
+ ; NOTE: In the CheckForXXXMatch stubs (for URT ctx/ Ole32 ctx) eax is
+ ; non-zero if contexts *do not* match & zero if they do.
+ ret
+CTPMethodTable__GenericCheckForContextMatch endp
+endif ; FEATURE_REMOTING
+
+
+; void __stdcall JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle)
+_JIT_ProfilerEnterLeaveTailcallStub@4 proc public
+ ; this function must preserve all registers, including scratch
+ retn 4
+_JIT_ProfilerEnterLeaveTailcallStub@4 endp
+
+;
+; Used to get the current instruction pointer value
+;
+; UINT_PTR __stdcall GetCurrentIP(void);
+_GetCurrentIP@0 proc public
+ mov eax, [esp]
+ retn
+_GetCurrentIP@0 endp
+
+; LPVOID __stdcall GetCurrentSP(void);
+_GetCurrentSP@0 proc public
+ mov eax, esp
+ retn
+_GetCurrentSP@0 endp
+
+
+; void __stdcall ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID);
+_ProfileEnterNaked@4 proc public
+ push esi
+ push edi
+
+ ;
+ ; Push in reverse order the fields of ProfilePlatformSpecificData
+ ;
+ push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field
+ push ebp ; Methods are always EBP framed
+ add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller
+ ; Skip past saved EBP value: 4 bytes
+ ; - plus return address from caller's caller: 4 bytes
+ ;
+ ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack
+ ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass
+ ; the return address from caller's caller which is Foo() in this example.
+ ;
+ ; ProfileEnterNaked()
+ ; Bar()
+ ; Foo()
+ ;
+ ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller.
+
+ push ecx ; -- struct ecx field
+ push edx ; -- struct edx field
+ push eax ; -- struct eax field
+ push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field
+ push 0 ; Create buffer space in the structure -- struct floatBuffer field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field
+ push 0 ; Create buffer space in the structure -- struct functionId field
+
+ mov edx, esp ; the address of the Platform structure
+ mov ecx, [esp+52]; The functionIDOrClientID parameter that was pushed to FunctionEnter
+ ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes
+ ; - plus saved edi, esi : 8 bytes
+ ; - plus return address from caller: 4 bytes
+
+ call @ProfileEnter@8
+
+ add esp, 20 ; Remove buffer space
+ pop eax
+ pop edx
+ pop ecx
+ add esp, 8 ; Remove buffer space
+ pop edi
+ pop esi
+
+ retn 4
+_ProfileEnterNaked@4 endp
+
+; void __stdcall ProfileLeaveNaked(FunctionIDOrClientID functionIDOrClientID);
+_ProfileLeaveNaked@4 proc public
+ push ecx ; We do not strictly need to save ECX, however
+ ; emitNoGChelper(CORINFO_HELP_PROF_FCN_LEAVE) returns true in the JITcompiler
+ push edx ; Return value may be in EAX:EDX
+
+ ;
+ ; Push in reverse order the fields of ProfilePlatformSpecificData
+ ;
+ push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field
+ push ebp ; Methods are always EBP framed
+ add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller
+ ; Skip past saved EBP value: 4 bytes
+ ; - plus return address from caller's caller: 4 bytes
+ ;
+ ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack
+ ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass
+ ; the return address from caller's caller which is Foo() in this example.
+ ;
+ ; ProfileEnterNaked()
+ ; Bar()
+ ; Foo()
+ ;
+ ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller.
+
+ push ecx ; -- struct ecx field
+ push edx ; -- struct edx field
+ push eax ; -- struct eax field
+
+ ; Check if we need to save off any floating point registers
+ fstsw ax
+ and ax, 3800h ; Check the top-of-fp-stack bits
+ cmp ax, 0 ; If non-zero, we have something to save
+ jnz SaveFPReg
+
+ push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field
+ push 0 ; Create buffer space in the structure -- struct floatBuffer field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field
+ jmp Continue
+
+SaveFPReg:
+ push 1 ; mark that a float value is present -- struct floatingPointValuePresent field
+ sub esp, 4 ; Make room for the FP value
+ fst dword ptr [esp] ; Copy the FP value to the buffer as a float -- struct floatBuffer field
+ sub esp, 8 ; Make room for the FP value
+ fstp qword ptr [esp] ; Copy FP values to the buffer as a double -- struct doubleBuffer1 and doubleBuffer2 fields
+
+Continue:
+ push 0 ; Create buffer space in the structure -- struct functionId field
+
+ mov edx, esp ; the address of the Platform structure
+ mov ecx, [esp+52]; The clientData that was pushed to FunctionEnter
+ ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes
+ ; - plus saved edx, ecx : 8 bytes
+ ; - plus return address from caller: 4 bytes
+
+ call @ProfileLeave@8
+
+ ;
+ ; Now see if we have to restore and floating point registers
+ ;
+
+ cmp [esp + 16], 0
+ jz NoRestore
+
+ fld qword ptr [esp + 4]
+
+NoRestore:
+
+ add esp, 20 ; Remove buffer space
+ pop eax
+ add esp, 16 ; Remove buffer space
+ pop edx
+ pop ecx
+ retn 4
+_ProfileLeaveNaked@4 endp
+
+
+; void __stdcall ProfileTailcallNaked(FunctionIDOrClientID functionIDOrClientID);
+_ProfileTailcallNaked@4 proc public
+ push ecx
+ push edx
+
+ ;
+ ; Push in reverse order the fields of ProfilePlatformSpecificData
+ ;
+ push dword ptr [esp+8] ; EIP of the managed code that we return to. -- struct ip field
+ push ebp ; Methods are always EBP framed
+ add [esp], 8 ; Skip past the return IP, straight to the stack args that were passed to our caller
+ ; Skip past saved EBP value: 4 bytes
+ ; - plus return address from caller's caller: 4 bytes
+ ;
+ ; Assuming Foo() calls Bar(), and Bar() calls ProfileEnterNake() as illustrated (stack
+ ; grows up). We want to get what Foo() passed on the stack to Bar(), so we need to pass
+ ; the return address from caller's caller which is Foo() in this example.
+ ;
+ ; ProfileEnterNaked()
+ ; Bar()
+ ; Foo()
+ ;
+ ; [ESP] is now the ESP of caller's caller pointing to the arguments to the caller.
+
+ push ecx ; -- struct ecx field
+ push edx ; -- struct edx field
+ push eax ; -- struct eax field
+ push 0 ; Create buffer space in the structure -- struct floatingPointValuePresent field
+ push 0 ; Create buffer space in the structure -- struct floatBuffer field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer2 field
+ push 0 ; Create buffer space in the structure -- struct doubleBuffer1 field
+ push 0 ; Create buffer space in the structure -- struct functionId field
+
+ mov edx, esp ; the address of the Platform structure
+ mov ecx, [esp+52]; The clientData that was pushed to FunctionEnter
+ ; Skip past ProfilePlatformSpecificData we pushed: 40 bytes
+ ; - plus saved edx, ecx : 8 bytes
+ ; - plus return address from caller: 4 bytes
+
+ call @ProfileTailcall@8
+
+ add esp, 40 ; Remove buffer space
+ pop edx
+ pop ecx
+ retn 4
+_ProfileTailcallNaked@4 endp
+
+;==========================================================================
+; Invoked for vararg forward P/Invoke calls as a stub.
+; Except for secret return buffer, arguments come on the stack so EDX is available as scratch.
+; EAX - the NDirectMethodDesc
+; ECX - may be return buffer address
+; [ESP + 4] - the VASigCookie
+;
+_VarargPInvokeStub@0 proc public
+ ; EDX <- VASigCookie
+ mov edx, [esp + 4] ; skip retaddr
+
+ mov edx, [edx + VASigCookie__StubOffset]
+ test edx, edx
+
+ jz GoCallVarargWorker
+ ; ---------------------------------------
+
+ ; EAX contains MD ptr for the IL stub
+ jmp edx
+
+GoCallVarargWorker:
+ ;
+ ; MD ptr in EAX, VASigCookie ptr at [esp+4]
+ ;
+
+ STUB_PROLOG
+
+ mov esi, esp
+
+ ; save pMD
+ push eax
+
+ push eax ; pMD
+ push dword ptr [esi + 4*7] ; pVaSigCookie
+ push esi ; pTransitionBlock
+
+ call _VarargPInvokeStubWorker@12
+
+ ; restore pMD
+ pop eax
+
+ STUB_EPILOG
+
+ ; jump back to the helper - this time it won't come back here as the stub already exists
+ jmp _VarargPInvokeStub@0
+
+_VarargPInvokeStub@0 endp
+
+;==========================================================================
+; Invoked for marshaling-required unmanaged CALLI calls as a stub.
+; EAX - the unmanaged target
+; ECX, EDX - arguments
+; [ESP + 4] - the VASigCookie
+;
+_GenericPInvokeCalliHelper@0 proc public
+ ; save the target
+ push eax
+
+ ; EAX <- VASigCookie
+ mov eax, [esp + 8] ; skip target and retaddr
+
+ mov eax, [eax + VASigCookie__StubOffset]
+ test eax, eax
+
+ jz GoCallCalliWorker
+ ; ---------------------------------------
+
+ push eax
+
+ ; stack layout at this point:
+ ;
+ ; | ... |
+ ; | stack arguments | ESP + 16
+ ; +----------------------+
+ ; | VASigCookie* | ESP + 12
+ ; +----------------------+
+ ; | return address | ESP + 8
+ ; +----------------------+
+ ; | CALLI target address | ESP + 4
+ ; +----------------------+
+ ; | stub entry point | ESP + 0
+ ; ------------------------
+
+ ; remove VASigCookie from the stack
+ mov eax, [esp + 8]
+ mov [esp + 12], eax
+
+ ; move stub entry point below the RA
+ mov eax, [esp]
+ mov [esp + 8], eax
+
+ ; load EAX with the target address
+ pop eax
+ pop eax
+
+ ; stack layout at this point:
+ ;
+ ; | ... |
+ ; | stack arguments | ESP + 8
+ ; +----------------------+
+ ; | return address | ESP + 4
+ ; +----------------------+
+ ; | stub entry point | ESP + 0
+ ; ------------------------
+
+ ; CALLI target address is in EAX
+ ret
+
+GoCallCalliWorker:
+ ; the target is on the stack and will become m_Datum of PInvokeCalliFrame
+ ; call the stub generating worker
+ pop eax
+
+ ;
+ ; target ptr in EAX, VASigCookie ptr in EDX
+ ;
+
+ STUB_PROLOG
+
+ mov esi, esp
+
+ ; save target
+ push eax
+
+ push eax ; unmanaged target
+ push dword ptr [esi + 4*7] ; pVaSigCookie (first stack argument)
+ push esi ; pTransitionBlock
+
+ call _GenericPInvokeCalliStubWorker@12
+
+ ; restore target
+ pop eax
+
+ STUB_EPILOG
+
+ ; jump back to the helper - this time it won't come back here as the stub already exists
+ jmp _GenericPInvokeCalliHelper@0
+
+_GenericPInvokeCalliHelper@0 endp
+
+ifdef MDA_SUPPORTED
+
+;==========================================================================
+; Invoked from on-the-fly generated stubs when the stack imbalance MDA is
+; enabled. The common low-level work for both direct P/Invoke and unmanaged
+; delegate P/Invoke happens here. PInvokeStackImbalanceWorker is where the
+; actual imbalance check is implemented.
+; [ESP + 4] - the StackImbalanceCookie
+; [EBP + 8] - stack arguments (EBP frame pushed by the calling stub)
+;
+_PInvokeStackImbalanceHelper@0 proc public
+ ; StackImbalanceCookie to EBX
+ push ebx
+ lea ebx, [esp + 8]
+
+ push esi
+ push edi
+
+ ; copy stack args
+ mov edx, ecx
+ mov ecx, [ebx + StackImbalanceCookie__m_dwStackArgSize]
+ sub esp, ecx
+
+ shr ecx, 2
+ lea edi, [esp]
+ lea esi, [ebp + 8]
+
+ cld
+ rep movsd
+
+ ; record pre-call ESP
+ mov [ebx + StackImbalanceCookie__m_dwSavedEsp], esp
+
+ ; call the target (restore ECX in case it's a thiscall)
+ mov ecx, edx
+ call [ebx + StackImbalanceCookie__m_pTarget]
+
+ ; record post-call ESP and restore ESP to pre-pushed state
+ mov ecx, esp
+ lea esp, [ebp - SIZEOF_StackImbalanceCookie - 16] ; 4 DWORDs and the cookie have been pushed
+
+ ; save return value
+ push eax
+ push edx
+ sub esp, 12
+
+.errnz (StackImbalanceCookie__HAS_FP_RETURN_VALUE AND 00ffffffh), HAS_FP_RETURN_VALUE has changed - update asm code
+
+ ; save top of the floating point stack if the target has FP retval
+ test byte ptr [ebx + StackImbalanceCookie__m_callConv + 3], (StackImbalanceCookie__HAS_FP_RETURN_VALUE SHR 24)
+ jz noFPURetVal
+ fstp tbyte ptr [esp] ; save full 10 bytes to avoid precision loss
+noFPURetVal:
+
+ ; call PInvokeStackImbalanceWorker(StackImbalanceCookie *pSICookie, DWORD dwPostESP)
+ push ecx
+ push ebx
+ call _PInvokeStackImbalanceWorker@8
+
+ ; restore return value
+ test byte ptr [ebx + StackImbalanceCookie__m_callConv + 3], (StackImbalanceCookie__HAS_FP_RETURN_VALUE SHR 24)
+ jz noFPURetValToRestore
+ fld tbyte ptr [esp]
+noFPURetValToRestore:
+
+ add esp, 12
+ pop edx
+ pop eax
+
+ ; restore registers
+ pop edi
+ pop esi
+
+ pop ebx
+
+ ; EBP frame and original stack arguments will be removed by the caller
+ ret
+_PInvokeStackImbalanceHelper@0 endp
+
+endif ; MDA_SUPPORTED
+
+ifdef FEATURE_COMINTEROP
+
+;==========================================================================
+; This is a fast alternative to CallDescr* tailored specifically for
+; COM to CLR calls. Stack arguments don't come in a continuous buffer
+; and secret argument can be passed in EAX.
+;
+
+; extern "C" ARG_SLOT __fastcall COMToCLRDispatchHelper(
+; INT_PTR dwArgECX, ; ecx
+; INT_PTR dwArgEDX, ; edx
+; PCODE pTarget, ; [esp + 4]
+; PCODE pSecretArg, ; [esp + 8]
+; INT_PTR *pInputStack, ; [esp + c]
+; WORD wOutputStackSlots, ; [esp +10]
+; UINT16 *pOutputStackOffsets, ; [esp +14]
+; Frame *pCurFrame); ; [esp +18]
+
+FASTCALL_FUNC COMToCLRDispatchHelper, 32
+
+ ; ecx: dwArgECX
+ ; edx: dwArgEDX
+
+ offset_pTarget equ 4
+ offset_pSecretArg equ 8
+ offset_pInputStack equ 0Ch
+ offset_wOutputStackSlots equ 10h
+ offset_pOutputStackOffsets equ 14h
+ offset_pCurFrame equ 18h
+
+ movzx eax, word ptr [esp + offset_wOutputStackSlots]
+ test eax, eax
+ jnz CopyStackArgs
+
+ ; There are no stack args to copy and ECX and EDX are already setup
+ ; with the correct arguments for the callee, so we just have to
+ ; push the CPFH and make the call.
+
+ PUSH_CPFH_FOR_COM eax, esp, offset_pCurFrame ; trashes eax
+
+ mov eax, [esp + offset_pSecretArg + CPFH_STACK_SIZE]
+ call [esp + offset_pTarget + CPFH_STACK_SIZE]
+ifdef _DEBUG
+ nop ; This is a tag that we use in an assert.
+endif
+
+ POP_CPFH_FOR_COM ecx ; trashes ecx
+
+ ret 18h
+
+
+CopyStackArgs:
+ ; eax: num stack slots
+ ; ecx: dwArgECX
+ ; edx: dwArgEDX
+
+ push ebp
+ mov ebp, esp
+ push ebx
+ push esi
+ push edi
+
+ ebpFrame_adjust equ 4h
+ ebp_offset_pCurFrame equ ebpFrame_adjust + offset_pCurFrame
+
+ PUSH_CPFH_FOR_COM ebx, ebp, ebp_offset_pCurFrame ; trashes ebx
+
+ mov edi, [ebp + ebpFrame_adjust + offset_pOutputStackOffsets]
+ mov esi, [ebp + ebpFrame_adjust + offset_pInputStack]
+
+ ; eax: num stack slots
+ ; ecx: dwArgECX
+ ; edx: dwArgEDX
+ ; edi: pOutputStackOffsets
+ ; esi: pInputStack
+
+CopyStackLoop:
+ dec eax
+ movzx ebx, word ptr [edi + 2 * eax] ; ebx <- input stack offset
+ push [esi + ebx] ; stack <- value on the input stack
+ jnz CopyStackLoop
+
+ ; ECX and EDX are setup with the correct arguments for the callee,
+ ; and we've copied the stack arguments over as well, so now it's
+ ; time to make the call.
+
+ mov eax, [ebp + ebpFrame_adjust + offset_pSecretArg]
+ call [ebp + ebpFrame_adjust + offset_pTarget]
+ifdef _DEBUG
+ nop ; This is a tag that we use in an assert.
+endif
+
+ POP_CPFH_FOR_COM ecx ; trashes ecx
+
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+
+ ret 18h
+
+FASTCALL_ENDFUNC
+
+endif ; FEATURE_COMINTEROP
+
+ifndef FEATURE_CORECLR
+
+;==========================================================================
+; This is small stub whose purpose is to record current stack pointer and
+; call CopyCtorCallStubWorker to invoke copy constructors and destructors
+; as appropriate. This stub operates on arguments already pushed to the
+; stack by JITted IL stub and must not create a new frame, i.e. it must tail
+; call to the target for it to see the arguments that copy ctors have been
+; called on.
+;
+_CopyCtorCallStub@0 proc public
+ ; there may be an argument in ecx - save it
+ push ecx
+
+ ; push pointer to arguments
+ lea edx, [esp + 8]
+ push edx
+
+ call _CopyCtorCallStubWorker@4
+
+ ; restore ecx and tail call to the target
+ pop ecx
+ jmp eax
+_CopyCtorCallStub@0 endp
+
+endif ; !FEATURE_CORECLR
+
+ifdef FEATURE_PREJIT
+
+;==========================================================================
+_StubDispatchFixupStub@0 proc public
+
+ STUB_PROLOG
+
+ mov esi, esp
+
+ push 0
+ push 0
+
+ push eax ; siteAddrForRegisterIndirect (for tailcalls)
+ push esi ; pTransitionBlock
+
+ call _StubDispatchFixupWorker@16
+
+ STUB_EPILOG
+
+_StubDispatchFixupPatchLabel@0:
+public _StubDispatchFixupPatchLabel@0
+
+ ; Tailcall target
+ jmp eax
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_StubDispatchFixupStub@0 endp
+
+;==========================================================================
+_ExternalMethodFixupStub@0 proc public
+
+ pop eax ; pop off the return address to the stub
+ ; leaving the actual caller's return address on top of the stack
+
+ STUB_PROLOG
+
+ mov esi, esp
+
+ ; EAX is return address into CORCOMPILE_EXTERNAL_METHOD_THUNK. Subtract 5 to get start address.
+ sub eax, 5
+
+ push 0
+ push 0
+
+ push eax
+
+ ; pTransitionBlock
+ push esi
+
+ call _ExternalMethodFixupWorker@16
+
+ ; eax now contains replacement stub. PreStubWorker will never return
+ ; NULL (it throws an exception if stub creation fails.)
+
+ ; From here on, mustn't trash eax
+
+ STUB_EPILOG
+
+_ExternalMethodFixupPatchLabel@0:
+public _ExternalMethodFixupPatchLabel@0
+
+ ; Tailcall target
+ jmp eax
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_ExternalMethodFixupStub@0 endp
+
+ifdef FEATURE_READYTORUN
+;==========================================================================
+_DelayLoad_MethodCall@0 proc public
+
+ STUB_PROLOG_2_HIDDEN_ARGS
+
+ mov esi, esp
+
+ push ecx
+ push edx
+
+ push eax
+
+ ; pTransitionBlock
+ push esi
+
+ call _ExternalMethodFixupWorker@16
+
+ ; eax now contains replacement stub. PreStubWorker will never return
+ ; NULL (it throws an exception if stub creation fails.)
+
+ ; From here on, mustn't trash eax
+
+ STUB_EPILOG
+
+ ; Share the patch label
+ jmp _ExternalMethodFixupPatchLabel@0
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_DelayLoad_MethodCall@0 endp
+endif
+
+;=======================================================================================
+; The call in softbound vtable slots initially points to this function.
+; The pupose of this function is to transfer the control to right target and
+; to optionally patch the target of the jump so that we do not take this slow path again.
+;
+_VirtualMethodFixupStub@0 proc public
+
+ pop eax ; Pop the return address. It points right after the call instruction in the thunk.
+ sub eax,5 ; Calculate the address of the thunk
+
+ ; Push ebp frame to get good callstack under debugger
+ push ebp
+ mov ebp, esp
+
+ ; Preserve argument registers
+ push ecx
+ push edx
+
+ push eax ; address of the thunk
+ push ecx ; this ptr
+ call _VirtualMethodFixupWorker@8
+
+ ; Restore argument registers
+ pop edx
+ pop ecx
+
+ ; Pop ebp frame
+ pop ebp
+
+_VirtualMethodFixupPatchLabel@0:
+public _VirtualMethodFixupPatchLabel@0
+
+ ; Proceed to execute the actual method.
+ jmp eax
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_VirtualMethodFixupStub@0 endp
+
+endif ; FEATURE_PREJIT
+
+;==========================================================================
+; The prestub
+_ThePreStub@0 proc public
+
+ STUB_PROLOG
+
+ mov esi, esp
+
+ ; EAX contains MethodDesc* from the precode. Push it here as argument
+ ; for PreStubWorker
+ push eax
+
+ push esi
+
+ call _PreStubWorker@8
+
+ ; eax now contains replacement stub. PreStubWorker will never return
+ ; NULL (it throws an exception if stub creation fails.)
+
+ ; From here on, mustn't trash eax
+
+ STUB_EPILOG
+
+ ; Tailcall target
+ jmp eax
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_ThePreStub@0 endp
+
+; This method does nothing. It's just a fixed function for the debugger to put a breakpoint
+; on so that it can trace a call target.
+_ThePreStubPatch@0 proc public
+ ; make sure that the basic block is unique
+ test eax,34
+_ThePreStubPatchLabel@0:
+public _ThePreStubPatchLabel@0
+ ret
+_ThePreStubPatch@0 endp
+
+ifdef FEATURE_COMINTEROP
+;==========================================================================
+; CLR -> COM generic or late-bound call
+_GenericComPlusCallStub@0 proc public
+
+ STUB_PROLOG
+
+ ; pTransitionBlock
+ mov esi, esp
+
+ ; return value
+ sub esp, 8
+
+ ; save pMD
+ mov ebx, eax
+
+ push eax ; pMD
+ push esi ; pTransitionBlock
+ call _CLRToCOMWorker@8
+
+ push eax
+ call _setFPReturn@12 ; pop & set the return value
+
+ ; From here on, mustn't trash eax:edx
+
+ ; Get pComPlusCallInfo for return thunk
+ mov ecx, [ebx + ComPlusCallMethodDesc__m_pComPlusCallInfo]
+
+ STUB_EPILOG_RETURN
+
+ ; Tailcall return thunk
+ jmp [ecx + ComPlusCallInfo__m_pRetThunk]
+
+ ; This will never be executed. It is just to help out stack-walking logic
+ ; which disassembles the epilog to unwind the stack.
+ ret
+
+_GenericComPlusCallStub@0 endp
+endif ; FEATURE_COMINTEROP
+
+ifdef FEATURE_REMOTING
+_TransparentProxyStub@0 proc public
+ ; push slot passed in eax
+ push eax
+
+ ; Move into eax the stub data and call the stub
+ mov eax, [ecx + TransparentProxyObject___stubData]
+ call [ecx + TransparentProxyObject___stub]
+ifdef _DEBUG
+ nop ; Mark this as a special call site that can directly
+ ; call managed code
+endif
+ test eax, eax
+ jnz CtxMismatch2
+
+ mov eax, [ecx + TransparentProxyObject___pMT]
+
+ push ebx ; spill EBX
+
+ ; Convert the slot number into the code address
+ ; See MethodTable.h for details on vtable layout
+
+ mov ebx, [esp + 4] ; Reload the slot
+ shr ebx, ASM__VTABLE_SLOTS_PER_CHUNK_LOG2 ; indirectionSlotNumber
+
+ mov eax,[eax + ebx*4 + SIZEOF_MethodTable]
+
+ mov ebx, [esp + 4] ; use unchanged slot from above
+ and ebx, ASM__VTABLE_SLOTS_PER_CHUNK-1 ; offsetInChunk
+ mov eax, [eax + ebx*4]
+
+ ; At this point, eax contains the code address
+
+ ; Restore EBX
+ pop ebx
+
+ ; Remove the slot number from the stack
+ lea esp, [esp+4]
+
+ jmp eax
+
+ ; CONTEXT MISMATCH CASE, call out to the real proxy to dispatch
+
+CtxMismatch2:
+ pop eax ; restore MethodDesc *
+ jmp _TransparentProxyStub_CrossContext@0 ; jump to slow TP stub
+
+_TransparentProxyStub@0 endp
+
+_TransparentProxyStub_CrossContext@0 proc public
+
+ STUB_PROLOG
+
+ ; pTransitionBlock
+ mov esi, esp
+
+ ; return value
+ sub esp, 3*4 ; 64-bit return value + cb stack pop
+
+ push eax ; pMD
+ push esi ; pTransitionBlock
+ call _TransparentProxyStubWorker@8
+
+ pop ebx ; cbStackPop
+
+ push eax
+ call _setFPReturn@12 ; pop & set the return value
+
+ ; From here on, mustn't trash eax:edx
+ mov ecx, ebx ; cbStackPop
+
+ mov ebx, [esp+6*4] ; get retaddr
+ mov [esp+6*4+ecx], ebx ; put it where it belongs
+
+ STUB_EPILOG_RETURN
+
+ add esp, ecx ; pop all the args
+ ret
+
+_TransparentProxyStub_CrossContext@0 endp
+
+; This method does nothing. It's just a fixed function for the debugger to put a breakpoint
+; on so that it can trace a call target.
+_TransparentProxyStubPatch@0 proc public
+ ; make sure that the basic block is unique
+ test eax,12
+_TransparentProxyStubPatchLabel@0:
+public _TransparentProxyStubPatchLabel@0
+ ret
+_TransparentProxyStubPatch@0 endp
+
+endif ; FEATURE_REMOTING
+
+ifdef FEATURE_COMINTEROP
+;--------------------------------------------------------------------------
+; This is the code that all com call method stubs run initially.
+; Most of the real work occurs in ComStubWorker(), a C++ routine.
+; The template only does the part that absolutely has to be in assembly
+; language.
+;--------------------------------------------------------------------------
+_ComCallPreStub@0 proc public
+ pop eax ;ComCallMethodDesc*
+
+ ; push ebp-frame
+ push ebp
+ mov ebp,esp
+
+ ; save CalleeSavedRegisters
+ push ebx
+ push esi
+ push edi
+
+ push eax ; ComCallMethodDesc*
+ sub esp, 5*4 ; next, vtable, gscookie, 64-bit error return
+
+ lea edi, [esp]
+ lea esi, [esp+3*4]
+
+ push edi ; pErrorReturn
+ push esi ; pFrame
+ call _ComPreStubWorker@8
+
+ ; eax now contains replacement stub. ComStubWorker will return NULL if stub creation fails
+ cmp eax, 0
+ je nostub ; oops we could not create a stub
+
+ add esp, 6*4
+
+ ; pop CalleeSavedRegisters
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+
+ jmp eax ; Reexecute with replacement stub.
+ ; We will never get here. This "ret" is just so that code-disassembling
+ ; profilers know to stop disassembling any further
+ ret
+
+nostub:
+
+ ; Even though the ComPreStubWorker sets a 64 bit value as the error return code.
+ ; Only the lower 32 bits contain usefula data. The reason for this is that the
+ ; possible error return types are: failure HRESULT, 0 and floating point 0.
+ ; In each case, the data fits in 32 bits. Instead, we use the upper half of
+ ; the return value to store number of bytes to pop
+ mov eax, [edi]
+ mov edx, [edi+4]
+
+ add esp, 6*4
+
+ ; pop CalleeSavedRegisters
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+
+ pop ecx ; return address
+ add esp, edx ; pop bytes of the stack
+ push ecx ; return address
+
+ ; We need to deal with the case where the method is PreserveSig=true and has an 8
+ ; byte return type. There are 2 types of 8 byte return types: integer and floating point.
+ ; For integer 8 byte return types, we always return 0 in case of failure. For floating
+ ; point return types, we return the value in the floating point register. In both cases
+ ; edx should be 0.
+ xor edx, edx ; edx <-- 0
+
+ ret
+
+_ComCallPreStub@0 endp
+endif ; FEATURE_COMINTEROP
+
+ifdef FEATURE_READYTORUN
+;==========================================================================
+; Define helpers for delay loading of readytorun helpers
+
+DYNAMICHELPER macro frameFlags, suffix
+
+_DelayLoad_Helper&suffix&@0 proc public
+
+ STUB_PROLOG_2_HIDDEN_ARGS
+
+ mov esi, esp
+
+ push frameFlags
+ push ecx ; module
+ push edx ; section index
+
+ push eax ; indirection cell address.
+ push esi ; pTransitionBlock
+
+ call _DynamicHelperWorker@20
+ test eax,eax
+ jnz @F
+
+ mov eax, [esi] ; The result is stored in the argument area of the transition block
+ STUB_EPILOG_RETURN
+ ret
+
+@@:
+ STUB_EPILOG
+ jmp eax
+
+_DelayLoad_Helper&suffix&@0 endp
+
+ endm
+
+DYNAMICHELPER DynamicHelperFrameFlags_Default
+DYNAMICHELPER DynamicHelperFrameFlags_ObjectArg, _Obj
+DYNAMICHELPER <DynamicHelperFrameFlags_ObjectArg OR DynamicHelperFrameFlags_ObjectArg2>, _ObjObj
+
+endif ; FEATURE_READYTORUN
+
+ end