Permalink
6706 lines (5611 sloc) 226 KB
// 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: DllImport.cpp
//
//
// P/Invoke support.
//
#include "common.h"
#include "vars.hpp"
#include "stublink.h"
#include "threads.h"
#include "excep.h"
#include "dllimport.h"
#include "method.hpp"
#include "siginfo.hpp"
#include "comdelegate.h"
#include "ceeload.h"
#include "mlinfo.h"
#include "eeconfig.h"
#include "comutilnative.h"
#include "corhost.h"
#include "asmconstants.h"
#include "mdaassistants.h"
#include "customattribute.h"
#include "ilstubcache.h"
#include "typeparse.h"
#include "sigbuilder.h"
#include "sigformat.h"
#include "strongnameholders.h"
#include "ecall.h"
#include <formattype.h>
#include "../md/compiler/custattr.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
#include "clrtocomcall.h"
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_PREJIT
#include "compile.h"
#endif // FEATURE_PREJIT
#include "eventtrace.h"
#include "clr/fs/path.h"
using namespace clr::fs;
// remove when we get an updated SDK
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx)
{
LIMITED_METHOD_CONTRACT;
if (pClause->kind == ILStubEHClause::kNone)
return;
int idx = *pCurIdx;
*pCurIdx = idx + 1;
CorExceptionFlag flags;
switch (pClause->kind)
{
case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break;
case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break;
default:
UNREACHABLE_MSG("unexpected ILStubEHClause kind");
}
_ASSERTE(idx < nClauses);
pEHSect->Fat.Clauses[idx].Flags = flags;
pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset;
pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength;
pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset;
pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength;
pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken;
}
VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo)
{
LIMITED_METHOD_CONTRACT;
pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat);
pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses);
int curIdx = 0;
AppendEHClause(nClauses, pEHSect, pOne, &curIdx);
AppendEHClause(nClauses, pEHSect, pTwo, &curIdx);
}
StubSigDesc::StubSigDesc(MethodDesc *pMD, PInvokeStaticSigInfo* pSigInfo /*= NULL*/)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
SUPPORTS_DAC;
}
CONTRACTL_END
m_pMD = pMD;
if (pSigInfo != NULL)
{
m_sig = pSigInfo->GetSignature();
m_pModule = pSigInfo->GetModule();
}
else
{
_ASSERTE(pMD != NULL);
m_sig = pMD->GetSignature();
m_pModule = pMD->GetModule(); // Used for token resolution.
}
if (pMD != NULL)
{
m_tkMethodDef = pMD->GetMemberDef();
SigTypeContext::InitTypeContext(pMD, &m_typeContext);
m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
}
else
{
m_tkMethodDef = mdMethodDefNil;
m_pLoaderModule = m_pModule;
}
INDEBUG(InitDebugNames());
}
StubSigDesc::StubSigDesc(MethodDesc *pMD, Signature sig, Module *pModule)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
SUPPORTS_DAC;
PRECONDITION(!sig.IsEmpty());
PRECONDITION(pModule != NULL);
}
CONTRACTL_END
m_pMD = pMD;
m_sig = sig;
m_pModule = pModule;
if (pMD != NULL)
{
m_tkMethodDef = pMD->GetMemberDef();
SigTypeContext::InitTypeContext(pMD, &m_typeContext);
m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
}
else
{
m_tkMethodDef = mdMethodDefNil;
m_pLoaderModule = m_pModule;
}
INDEBUG(InitDebugNames());
}
#ifndef DACCESS_COMPILE
class StubState
{
public:
virtual void SetLastError(BOOL fSetLastError) = 0;
virtual void BeginEmit(DWORD dwStubFlags) = 0;
virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) = 0;
virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset) = 0;
virtual void MarshalLCID(int argIdx) = 0;
#ifdef FEATURE_COMINTEROP
virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray) = 0;
virtual void MarshalFactoryReturn() = 0;
#endif // FEATURE_COMINTEROP
virtual void EmitInvokeTarget(MethodDesc *pStubMD) = 0;
virtual void FinishEmit(MethodDesc* pMD) = 0;
virtual ~StubState()
{
LIMITED_METHOD_CONTRACT;
}
};
class ILStubState : public StubState
{
protected:
ILStubState(
Module* pStubModule,
const Signature &signature,
SigTypeContext* pTypeContext,
BOOL fTargetHasThis,
BOOL fStubHasThis,
DWORD dwStubFlags,
int iLCIDParamIdx,
MethodDesc* pTargetMD)
: m_slIL(dwStubFlags, pStubModule, signature, pTypeContext, pTargetMD, iLCIDParamIdx, fTargetHasThis, fStubHasThis)
{
STANDARD_VM_CONTRACT;
m_fSetLastError = 0;
}
public:
void SetLastError(BOOL fSetLastError)
{
LIMITED_METHOD_CONTRACT;
m_fSetLastError = fSetLastError;
}
// We use three stub linkers to generate IL stubs. The pre linker is the main one. It does all the marshaling and
// then calls the target method. The post return linker is only used to unmarshal the return value after we return
// from the target method. The post linker handles all the unmarshaling for by ref arguments and clean-up. It
// also checks if we should throw an exception etc.
//
// Currently, we have two "emittable" ILCodeLabel's. The first one is at the beginning of the pre linker. This
// label is used to emit code to declare and initialize clean-up flags. Each argument which requires clean-up
// emits one flag. This flag is set only after the marshaling is done, and it is checked before we do any clean-up
// in the finally.
//
// The second "emittable" ILCodeLabel is at the beginning of the post linker. It is used to emit code which is
// not safe to run in the case of an exception. The rest of the post linker is wrapped in a finally, and it contains
// with the necessary clean-up which should be executed in both normal and exception cases.
void BeginEmit(DWORD dwStubFlags)
{
WRAPPER_NO_CONTRACT;
m_slIL.Begin(dwStubFlags);
m_dwStubFlags = dwStubFlags;
}
void MarshalReturn(MarshalInfo* pInfo, int argOffset)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pInfo));
}
CONTRACTL_END;
pInfo->GenerateReturnIL(&m_slIL, argOffset,
SF_IsForwardStub(m_dwStubFlags),
SF_IsFieldGetterStub(m_dwStubFlags),
SF_IsHRESULTSwapping(m_dwStubFlags));
}
void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pInfo));
}
CONTRACTL_END;
pInfo->GenerateArgumentIL(&m_slIL, argOffset, nativeStackOffset, SF_IsForwardStub(m_dwStubFlags));
}
#ifdef FEATURE_COMINTEROP
// Marshal the hidden length parameter for the managed parameter in pInfo
virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray)
{
STANDARD_VM_CONTRACT;
pInfo->MarshalHiddenLengthArgument(&m_slIL, SF_IsForwardStub(m_dwStubFlags), isForReturnArray);
if (SF_IsReverseStub(m_dwStubFlags))
{
// Hidden length arguments appear explicitly in the native signature
// however, they are not in the managed signature.
m_slIL.AdjustTargetStackDeltaForExtraParam();
}
}
void MarshalFactoryReturn()
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(SF_IsCOMStub(m_dwStubFlags));
PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
}
CONTRACTL_END;
ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
/*
* SETUP
*/
// create a local to hold the returned pUnk and initialize to 0 in case the factory fails
// and we try to release it during cleanup
LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
pcsSetup->EmitLoadNullPtr();
pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
DWORD dwInnerIInspectableLocalNum = -1;
DWORD dwOuterIInspectableLocalNum = -1;
if (SF_IsWinRTCompositionStub(m_dwStubFlags))
{
// Create locals to store the outer and inner IInspectable values and initialize to null
// Note that we do this in the setup stream so that we're guaranteed to have a null-initialized
// value in the cleanup stream
LocalDesc locDescOuterIInspectable(ELEMENT_TYPE_I);
dwOuterIInspectableLocalNum = pcsSetup->NewLocal(locDescOuterIInspectable);
pcsSetup->EmitLoadNullPtr();
pcsSetup->EmitSTLOC(dwOuterIInspectableLocalNum);
LocalDesc locDescInnerIInspectable(ELEMENT_TYPE_I);
dwInnerIInspectableLocalNum = pcsSetup->NewLocal(locDescInnerIInspectable);
pcsSetup->EmitLoadNullPtr();
pcsSetup->EmitSTLOC(dwInnerIInspectableLocalNum);
}
/*
* DISPATCH
*/
// For composition factories, add the two extra params
if (SF_IsWinRTCompositionStub(m_dwStubFlags))
{
// Get outer IInspectable. The helper will return NULL if this is the "top-level" constructor,
// and the appropriate outer pointer otherwise.
pcsDispatch->EmitLoadThis();
m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_OUTER_INSPECTABLE, 2, 1);
pcsDispatch->EmitSTLOC(dwOuterIInspectableLocalNum);
// load the outer IInspectable (3rd last argument)
pcsDispatch->SetStubTargetArgType(ELEMENT_TYPE_I, false);
pcsDispatch->EmitLDLOC(dwOuterIInspectableLocalNum);
// pass pointer to where inner non-delegating IInspectable should be stored (2nd last argument)
LocalDesc locDescInnerPtr(ELEMENT_TYPE_I);
locDescInnerPtr.MakeByRef();
pcsDispatch->SetStubTargetArgType(&locDescInnerPtr, false);
pcsDispatch->EmitLDLOCA(dwInnerIInspectableLocalNum);
}
// pass pointer to the local to the factory method (last argument)
locDescFactoryRetVal.MakeByRef();
pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
pcsDispatch->EmitLDLOCA(dwFactoryRetValLocalNum);
/*
* UNMARSHAL
*/
// Mark that the factory method has succesfully returned and so cleanup will be necessary after
// this point.
m_slIL.EmitSetArgMarshalIndex(pcsUnmarshal, NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL);
// associate the 'this' RCW with one of the returned interface pointers
pcsUnmarshal->EmitLoadThis();
// now we need to find the right interface pointer to load
if (dwInnerIInspectableLocalNum != -1)
{
// We may have a composition scenario
ILCodeLabel* pNonCompositionLabel = pcsUnmarshal->NewCodeLabel();
ILCodeLabel* pLoadedLabel = pcsUnmarshal->NewCodeLabel();
// Did we pass an outer IInspectable?
pcsUnmarshal->EmitLDLOC(dwOuterIInspectableLocalNum);
pcsUnmarshal->EmitBRFALSE(pNonCompositionLabel);
// yes, this is a composition scenario
{
// ignore the delegating interface pointer (will be released in cleanup below) - we can
// re-create it by QI'ing the non-delegating one.
// Note that using this could be useful in the future (avoids an extra QueryInterface call)
// Just load the non-delegating interface pointer
pcsUnmarshal->EmitLDLOCA(dwInnerIInspectableLocalNum);
pcsUnmarshal->EmitBR(pLoadedLabel);
}
// else, no this is a non-composition scenario
{
pcsUnmarshal->EmitLabel(pNonCompositionLabel);
// ignore the non-delegating interface pointer (which should be null, but will regardless get
// cleaned up below in the event the factory doesn't follow the pattern properly).
// Just load the regular delegating interface pointer
pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
}
pcsUnmarshal->EmitLabel(pLoadedLabel);
}
else
{
// Definitely can't be a composition scenario - use the only pointer we have
pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
}
pcsUnmarshal->EmitCALL(METHOD__MARSHAL__INITIALIZE_WRAPPER_FOR_WINRT, 2, 0);
/*
* CLEANUP
*/
// release the returned interface pointer in the finally block
m_slIL.SetCleanupNeeded();
ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
m_slIL.EmitCheckForArgCleanup(pcsCleanup,
NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL,
NDirectStubLinker::BranchIfNotMarshaled,
pSkipCleanupLabel);
EmitInterfaceClearNative(pcsCleanup, dwFactoryRetValLocalNum);
// Note that it's a no-op to pass NULL to Clear_Native, so we call it even though we don't
// know if we assigned to the inner/outer IInspectable
if (dwInnerIInspectableLocalNum != -1)
{
EmitInterfaceClearNative(pcsCleanup, dwInnerIInspectableLocalNum);
}
if (dwOuterIInspectableLocalNum != -1)
{
EmitInterfaceClearNative(pcsCleanup, dwOuterIInspectableLocalNum);
}
pcsCleanup->EmitLabel(pSkipCleanupLabel);
}
static void EmitInterfaceClearNative(ILCodeStream* pcsEmit, DWORD dwLocalNum)
{
STANDARD_VM_CONTRACT;
ILCodeLabel *pSkipClearNativeLabel = pcsEmit->NewCodeLabel();
pcsEmit->EmitLDLOC(dwLocalNum);
pcsEmit->EmitBRFALSE(pSkipClearNativeLabel);
pcsEmit->EmitLDLOC(dwLocalNum);
pcsEmit->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
pcsEmit->EmitLabel(pSkipClearNativeLabel);
}
#endif // FEATURE_COMINTEROP
void MarshalLCID(int argIdx)
{
STANDARD_VM_CONTRACT;
ILCodeStream* pcs = m_slIL.GetDispatchCodeStream();
#ifdef FEATURE_USE_LCID
if (SF_IsReverseStub(m_dwStubFlags))
{
if ((m_slIL.GetStubTargetCallingConv() & IMAGE_CEE_CS_CALLCONV_HASTHIS) == IMAGE_CEE_CS_CALLCONV_HASTHIS)
{
// the arg number will be incremented by LDARG if we are in an instance method
_ASSERTE(argIdx > 0);
argIdx--;
}
LocalDesc locDescThread(MscorlibBinder::GetClass(CLASS__THREAD));
DWORD dwThreadLocalNum = pcs->NewLocal(locDescThread);
// call Thread.get_CurrentThread()
pcs->EmitCALL(METHOD__THREAD__GET_CURRENT_THREAD, 0, 1);
pcs->EmitDUP();
pcs->EmitSTLOC(dwThreadLocalNum);
// call current_thread.get_CurrentCulture()
pcs->EmitCALL(METHOD__THREAD__GET_CULTURE, 1, 1);
// save the current culture
LocalDesc locDescCulture(MscorlibBinder::GetClass(CLASS__CULTURE_INFO));
DWORD dwCultureLocalNum = pcs->NewLocal(locDescCulture);
pcs->EmitSTLOC(dwCultureLocalNum);
// set a new one based on the LCID passed from unmanaged
pcs->EmitLDLOC(dwThreadLocalNum);
pcs->EmitLDARG(argIdx);
// call CultureInfo..ctor(lcid)
// call current_thread.set_CurrentCulture(culture)
pcs->EmitNEWOBJ(METHOD__CULTURE_INFO__INT_CTOR, 1);
pcs->EmitCALL(METHOD__THREAD__SET_CULTURE, 2, 0);
// and restore the current one after the call
m_slIL.SetCleanupNeeded();
ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
// call current_thread.set_CurrentCulture(original_culture)
pcsCleanup->EmitLDLOC(dwThreadLocalNum);
pcsCleanup->EmitLDLOC(dwCultureLocalNum);
pcsCleanup->EmitCALL(METHOD__THREAD__SET_CULTURE, 1, 1);
}
else
{
if (SF_IsCOMStub(m_dwStubFlags))
{
// We used to get LCID from current thread's culture here. The code
// was replaced by the hardcoded LCID_ENGLISH_US as requested by VSTO.
pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
}
else
{
// call Thread.get_CurrentThread()
// call current_thread.get_CurrentCulture()
pcs->EmitCALL(METHOD__THREAD__GET_CURRENT_THREAD, 0, 1);
pcs->EmitCALL(METHOD__THREAD__GET_CULTURE, 1, 1);
//call CultureInfo.get_LCID(this)
pcs->EmitCALL(METHOD__CULTURE_INFO__GET_ID, 1, 1);
}
}
#else // FEATURE_USE_LCID
if (SF_IsForwardStub(m_dwStubFlags))
{
pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
}
#endif // FEATURE_USE_LCID
// add the extra arg to the unmanaged signature
LocalDesc locDescNative(ELEMENT_TYPE_I4);
pcs->SetStubTargetArgType(&locDescNative, false);
if (SF_IsReverseStub(m_dwStubFlags))
{
// reverse the effect of SetStubTargetArgType on the stack delta
// (the LCID argument is explicitly passed from unmanaged but does not
// show up in the managed signature in any way)
m_slIL.AdjustTargetStackDeltaForExtraParam();
}
}
void SwapStubSignatures(MethodDesc* pStubMD)
{
STANDARD_VM_CONTRACT;
//
// Since the stub handles native-to-managed transitions, we have to swap the
// stub-state-calculated stub target sig with the stub sig itself. This is
// because the stub target sig represents the native signature and the stub
// sig represents the managed signature.
//
// The first step is to convert the managed signature to a module-independent
// signature and then pass it off to SetStubTargetMethodSig. Note that the
// ILStubResolver will copy the sig, so we only need to make a temporary copy
// of it.
//
SigBuilder sigBuilder;
{
SigPointer sigPtr(pStubMD->GetSig());
sigPtr.ConvertToInternalSignature(pStubMD->GetModule(), NULL, &sigBuilder);
}
//
// The second step is to reset the sig on the stub MethodDesc to be the
// stub-state-calculated stub target sig.
//
{
//
// make a domain-local copy of the sig so that this state can outlive the
// compile time state.
//
DWORD cbNewSig;
PCCOR_SIGNATURE pNewSig;
cbNewSig = GetStubTargetMethodSigLength();
pNewSig = (PCCOR_SIGNATURE)(void *)pStubMD->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig));
memcpyNoGCRefs((void *)pNewSig, GetStubTargetMethodSig(), cbNewSig);
pStubMD->AsDynamicMethodDesc()->SetStoredMethodSig(pNewSig, cbNewSig);
SigPointer sigPtr(pNewSig, cbNewSig);
ULONG callConvInfo;
IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo));
if (callConvInfo & CORINFO_CALLCONV_HASTHIS)
{
((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags &= ~mdStatic;
pStubMD->ClearStatic();
}
else
{
((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags |= mdStatic;
pStubMD->SetStatic();
}
#ifndef _TARGET_X86_
// we store the real managed argument stack size in the stub MethodDesc on non-X86
UINT stackSize = pStubMD->SizeOfArgStack();
if (!FitsInU2(stackSize))
COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
pStubMD->AsDynamicMethodDesc()->SetNativeStackArgSize(static_cast<WORD>(stackSize));
#endif // _TARGET_X86_
}
DWORD cbTempModuleIndependentSigLength;
BYTE * pTempModuleIndependentSig = (BYTE *)sigBuilder.GetSignature(&cbTempModuleIndependentSigLength);
// Finish it
SetStubTargetMethodSig(pTempModuleIndependentSig,
cbTempModuleIndependentSigLength);
}
void EmitInvokeTarget(MethodDesc *pStubMD)
{
STANDARD_VM_CONTRACT;
m_slIL.DoNDirect(m_slIL.GetDispatchCodeStream(), m_dwStubFlags, pStubMD);
}
#ifdef FEATURE_COMINTEROP
void EmitExceptionHandler(LocalDesc* pNativeReturnType, LocalDesc* pManagedReturnType,
ILCodeLabel** ppTryEndAndCatchBeginLabel, ILCodeLabel ** ppCatchEndAndReturnLabel)
{
STANDARD_VM_CONTRACT;
ILCodeStream* pcsExceptionHandler = m_slIL.NewCodeStream(ILStubLinker::kExceptionHandler);
*ppTryEndAndCatchBeginLabel = pcsExceptionHandler->NewCodeLabel();
*ppCatchEndAndReturnLabel = pcsExceptionHandler->NewCodeLabel();
pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
pcsExceptionHandler->EmitLabel(*ppTryEndAndCatchBeginLabel);
BYTE nativeReturnElemType = pNativeReturnType->ElementType[0]; // return type of the stub
BYTE managedReturnElemType = pManagedReturnType->ElementType[0]; // return type of the mananged target
bool returnTheHRESULT = SF_IsHRESULTSwapping(m_dwStubFlags) ||
(managedReturnElemType == ELEMENT_TYPE_I4) ||
(managedReturnElemType == ELEMENT_TYPE_U4);
#ifdef MDA_SUPPORTED
if (!returnTheHRESULT)
{
MdaExceptionSwallowedOnCallFromCom* mda = MDA_GET_ASSISTANT(ExceptionSwallowedOnCallFromCom);
if (mda)
{
// on the stack: exception object, but the stub linker doesn't know it
pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__TRIGGER_EXCEPTION_SWALLOWED_MDA,
1, // WARNING: This method takes 2 input args, the exception object and the stub context.
// But the ILStubLinker has no knowledge that the exception object is on the
// stack (because it is unaware that we've just entered a catch block), so we
// lie and claim that we only take one input argument.
1); // returns the exception object back
}
}
#endif // MDA_SUPPORTED
DWORD retvalLocalNum = m_slIL.GetReturnValueLocalNum();
BinderMethodID getHRForException;
if (SF_IsWinRTStub(m_dwStubFlags))
{
getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION_WINRT;
}
else
{
getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION;
}
pcsExceptionHandler->EmitCALL(getHRForException,
0, // WARNING: This method takes 1 input arg, the exception object. But the ILStubLinker
// has no knowledge that the exception object is on the stack (because it is
// unaware that we've just entered a catch block), so we lie and claim that we
// don't take any input arguments.
1);
switch (nativeReturnElemType)
{
default:
UNREACHABLE_MSG("Unexpected element type found on native return type.");
break;
case ELEMENT_TYPE_VOID:
_ASSERTE(retvalLocalNum == (DWORD)-1);
pcsExceptionHandler->EmitPOP();
break;
case ELEMENT_TYPE_I4:
case ELEMENT_TYPE_U4:
{
if (!returnTheHRESULT)
{
pcsExceptionHandler->EmitPOP();
pcsExceptionHandler->EmitLDC(0);
pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
}
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
}
break;
case ELEMENT_TYPE_R4:
pcsExceptionHandler->EmitPOP();
pcsExceptionHandler->EmitLDC_R4(CLR_NAN_32);
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
break;
case ELEMENT_TYPE_R8:
pcsExceptionHandler->EmitPOP();
pcsExceptionHandler->EmitLDC_R8(CLR_NAN_64);
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
break;
case ELEMENT_TYPE_INTERNAL:
{
TypeHandle returnTypeHnd = pNativeReturnType->InternalToken;
CONSISTENCY_CHECK(returnTypeHnd.IsValueType());
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitLDLOCA(retvalLocalNum);
pcsExceptionHandler->EmitINITOBJ(m_slIL.GetDispatchCodeStream()->GetToken(returnTypeHnd));
}
break;
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_CHAR:
case ELEMENT_TYPE_I1:
case ELEMENT_TYPE_U1:
case ELEMENT_TYPE_I2:
case ELEMENT_TYPE_U2:
case ELEMENT_TYPE_I8:
case ELEMENT_TYPE_U8:
case ELEMENT_TYPE_I:
case ELEMENT_TYPE_U:
pcsExceptionHandler->EmitPOP();
pcsExceptionHandler->EmitLDC(0);
pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
break;
}
pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
pcsExceptionHandler->EmitLabel(*ppCatchEndAndReturnLabel);
if (nativeReturnElemType != ELEMENT_TYPE_VOID)
{
_ASSERTE(retvalLocalNum != (DWORD)-1);
pcsExceptionHandler->EmitLDLOC(retvalLocalNum);
}
pcsExceptionHandler->EmitRET();
}
#endif // FEATURE_COMINTEROP
void FinishEmit(MethodDesc* pStubMD)
{
STANDARD_VM_CONTRACT;
ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
if (SF_IsHRESULTSwapping(m_dwStubFlags) && m_slIL.StubHasVoidReturnType())
{
// if the return type is void, but we're doing HRESULT swapping, we
// need to set the return type here. Otherwise, the return value
// marshaler will do this.
pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // HRESULT
if (SF_IsReverseStub(m_dwStubFlags))
{
// reverse interop needs to seed the return value if the
// managed function returns void but we're doing hresult
// swapping.
pcsUnmarshal->EmitLDC(S_OK);
}
}
LocalDesc nativeReturnType;
LocalDesc managedReturnType;
bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(m_dwStubFlags)
&& !SF_IsFieldGetterStub(m_dwStubFlags)
&& !SF_IsFieldSetterStub(m_dwStubFlags);
#ifdef FEATURE_COMINTEROP
if (hasTryCatchForHRESULT)
{
m_slIL.GetStubTargetReturnType(&nativeReturnType);
m_slIL.GetStubReturnType(&managedReturnType);
}
#endif // FEATURE_COMINTEROP
if (SF_IsHRESULTSwapping(m_dwStubFlags) && SF_IsReverseStub(m_dwStubFlags))
{
m_slIL.AdjustTargetStackDeltaForReverseInteropHRESULTSwapping();
}
if (SF_IsForwardCOMStub(m_dwStubFlags))
{
// Compensate for the 'this' parameter.
m_slIL.AdjustTargetStackDeltaForExtraParam();
}
#if defined(_TARGET_X86_)
// unmanaged CALLI will get an extra arg with the real target address if host hook is enabled
if (SF_IsCALLIStub(m_dwStubFlags) && NDirect::IsHostHookEnabled())
{
pcsMarshal->SetStubTargetArgType(ELEMENT_TYPE_I, false);
}
#endif // _TARGET_X86_
// Don't touch target signatures from this point on otherwise it messes up the
// cache in ILStubState::GetStubTargetMethodSig.
#ifdef _DEBUG
{
// The native and local signatures should not have any tokens.
// All token references should have been converted to
// ELEMENT_TYPE_INTERNAL.
//
// Note that MetaSig::GetReturnType and NextArg will normalize
// ELEMENT_TYPE_INTERNAL back to CLASS or VALUETYPE.
//
// <TODO> need to recursively check ELEMENT_TYPE_FNPTR signatures </TODO>
SigTypeContext typeContext; // this is an empty type context: COM calls are guaranteed to not be generics.
MetaSig nsig(
GetStubTargetMethodSig(),
GetStubTargetMethodSigLength(),
MscorlibBinder::GetModule(),
&typeContext);
CorElementType type;
IfFailThrow(nsig.GetReturnProps().PeekElemType(&type));
CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
while (ELEMENT_TYPE_END != (type = nsig.NextArg()))
{
IfFailThrow(nsig.GetArgProps().PeekElemType(&type));
CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
}
}
#endif // _DEBUG
#ifdef FEATURE_COMINTEROP
if (SF_IsForwardCOMStub(m_dwStubFlags))
{
#if defined(MDA_SUPPORTED)
// We won't use this NGEN'ed stub if RaceOnRCWCleanup is enabled at run-time
if (!SF_IsNGENedStub(m_dwStubFlags))
{
// This code may change the type of the frame we use, so it has to be run before the code below where we
// retrieve the stack arg size based on the frame type.
MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
if (mda)
{
// Here we have to register the RCW of the "this" object to the RCWStack and schedule the clean-up for it.
// Emit a call to StubHelpers::StubRegisterRCW() and StubHelpers::StubUnregisterRCW() to do this.
m_slIL.EmitLoadRCWThis(pcsMarshal, m_dwStubFlags);
pcsMarshal->EmitCALL(METHOD__STUBHELPERS__STUB_REGISTER_RCW, 1, 0);
// We use an extra local to track whether we need to unregister the RCW on cleanup
ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
DWORD dwRCWRegisteredLocalNum = pcsSetup->NewLocal(ELEMENT_TYPE_BOOLEAN);
pcsSetup->EmitLDC(0);
pcsSetup->EmitSTLOC(dwRCWRegisteredLocalNum);
pcsMarshal->EmitLDC(1);
pcsMarshal->EmitSTLOC(dwRCWRegisteredLocalNum);
ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
m_slIL.SetCleanupNeeded();
pcsCleanup->EmitLDLOC(dwRCWRegisteredLocalNum);
pcsCleanup->EmitBRFALSE(pSkipCleanupLabel);
m_slIL.EmitLoadRCWThis(pcsCleanup, m_dwStubFlags);
pcsCleanup->EmitCALL(METHOD__STUBHELPERS__STUB_UNREGISTER_RCW, 1, 0);
pcsCleanup->EmitLabel(pSkipCleanupLabel);
}
}
#endif // MDA_SUPPORTED
}
#endif // FEATURE_COMINTEROP
// <NOTE>
// The profiler helpers below must be called immediately before and after the call to the target.
// The debugger trace call helpers are invoked from StubRareDisableWorker
// </NOTE>
#if defined(PROFILING_SUPPORTED)
DWORD dwMethodDescLocalNum = -1;
// Notify the profiler of call out of the runtime
if (!SF_IsReverseCOMStub(m_dwStubFlags) && (CORProfilerTrackTransitions() || SF_IsNGENedStubForProfiling(m_dwStubFlags)))
{
dwMethodDescLocalNum = m_slIL.EmitProfilerBeginTransitionCallback(pcsDispatch, m_dwStubFlags);
_ASSERTE(dwMethodDescLocalNum != -1);
}
#endif // PROFILING_SUPPORTED
#ifdef MDA_SUPPORTED
if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
MDA_GET_ASSISTANT(GcManagedToUnmanaged))
{
m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
}
#endif // MDA_SUPPORTED
// For CoreClr, clear the last error before calling the target that returns last error.
// There isn't always a way to know the function have failed without checking last error,
// in particular on Unix.
if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CLEAR_LAST_ERROR, 0, 0);
}
// Invoke the target (calli, call method, call delegate, get/set field, etc.)
EmitInvokeTarget(pStubMD);
// Saving last error must be the first thing we do after returning from the target
if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__SET_LAST_ERROR, 0, 0);
}
#if defined(_TARGET_X86_)
if (SF_IsForwardDelegateStub(m_dwStubFlags))
{
// the delegate may have an intercept stub attached to its sync block so we should
// prevent it from being garbage collected when the call is in progress
pcsDispatch->EmitLoadThis();
pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
}
#endif // defined(_TARGET_X86_)
#ifdef MDA_SUPPORTED
if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
MDA_GET_ASSISTANT(GcUnmanagedToManaged))
{
m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
}
#endif // MDA_SUPPORTED
#ifdef VERIFY_HEAP
if (SF_IsForwardStub(m_dwStubFlags) && g_pConfig->InteropValidatePinnedObjects())
{
// call StubHelpers.ValidateObject/StubHelpers.ValidateByref on pinned locals
m_slIL.EmitObjectValidation(pcsDispatch, m_dwStubFlags);
}
#endif // VERIFY_HEAP
#if defined(PROFILING_SUPPORTED)
// Notify the profiler of return back into the runtime
if (dwMethodDescLocalNum != -1)
{
m_slIL.EmitProfilerEndTransitionCallback(pcsDispatch, m_dwStubFlags, dwMethodDescLocalNum);
}
#endif // PROFILING_SUPPORTED
#ifdef FEATURE_COMINTEROP
if (SF_IsForwardCOMStub(m_dwStubFlags))
{
// Make sure that the RCW stays alive for the duration of the call. Note that if we do HRESULT
// swapping, we'll pass 'this' to GetCOMHRExceptionObject after returning from the target so
// GC.KeepAlive is not necessary.
if (!SF_IsHRESULTSwapping(m_dwStubFlags))
{
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
}
}
#endif // FEATURE_COMINTEROP
if (SF_IsHRESULTSwapping(m_dwStubFlags))
{
if (SF_IsForwardStub(m_dwStubFlags))
{
ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
pcsDispatch->EmitDUP();
pcsDispatch->EmitLDC(0);
pcsDispatch->EmitBGE(pSkipThrowLabel);
#ifdef FEATURE_COMINTEROP
if (SF_IsCOMStub(m_dwStubFlags))
{
m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
if (SF_IsWinRTStub(m_dwStubFlags))
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT_WINRT, 3, 1);
}
else
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT, 3, 1);
}
}
else
#endif // FEATURE_COMINTEROP
{
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_HR_EXCEPTION_OBJECT, 1, 1);
}
pcsDispatch->EmitTHROW();
pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
pcsDispatch->EmitLabel(pSkipThrowLabel);
pcsDispatch->EmitPOP();
}
}
m_slIL.End(m_dwStubFlags);
if (!hasTryCatchForHRESULT) // we will 'leave' the try scope and then 'ret' from outside
{
pcsUnmarshal->EmitRET();
}
CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB);
if (m_slIL.HasInteropParamExceptionInfo())
{
// This code will not use the secret parameter, so we do not
// tell the JIT to bother with it.
m_slIL.ClearCode();
m_slIL.GenerateInteropParamException(pcsMarshal);
}
else if (SF_IsFieldGetterStub(m_dwStubFlags) || SF_IsFieldSetterStub(m_dwStubFlags))
{
// Field access stubs are not shared and do not use the secret parameter.
}
#ifndef _WIN64
else if (SF_IsForwardDelegateStub(m_dwStubFlags) ||
(SF_IsForwardCOMStub(m_dwStubFlags) && SF_IsWinRTDelegateStub(m_dwStubFlags)))
{
// Forward delegate stubs get all the context they need in 'this' so they
// don't use the secret parameter. Except for AMD64 where we use the secret
// argument to pass the real target to the stub-for-host.
}
#endif // !_WIN64
else
{
// All other IL stubs will need to use the secret parameter.
jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM);
}
if (SF_IsReverseStub(m_dwStubFlags))
{
SwapStubSignatures(pStubMD);
}
ILCodeLabel* pTryEndAndCatchBeginLabel = NULL; // try ends at the same place the catch begins
ILCodeLabel* pCatchEndAndReturnLabel = NULL; // catch ends at the same place we resume afterwards
#ifdef FEATURE_COMINTEROP
if (hasTryCatchForHRESULT)
{
EmitExceptionHandler(&nativeReturnType, &managedReturnType, &pTryEndAndCatchBeginLabel, &pCatchEndAndReturnLabel);
}
#endif // FEATURE_COMINTEROP
UINT maxStack;
size_t cbCode;
DWORD cbSig;
BYTE * pbBuffer;
BYTE * pbLocalSig;
cbCode = m_slIL.Link(&maxStack);
cbSig = m_slIL.GetLocalSigSize();
ILStubResolver * pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
COR_ILMETHOD_DECODER * pILHeader = pResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
pbBuffer = (BYTE *)pILHeader->Code;
pbLocalSig = (BYTE *)pILHeader->LocalVarSig;
_ASSERTE(cbSig == pILHeader->cbLocalVarSig);
ILStubEHClause cleanupTryFinally = { 0 };
ILStubEHClause convertToHRTryCatch = { 0 };
m_slIL.GetCleanupFinallyOffsets(&cleanupTryFinally);
#ifdef FEATURE_COMINTEROP
if (hasTryCatchForHRESULT)
{
convertToHRTryCatch.kind = ILStubEHClause::kTypedCatch;
convertToHRTryCatch.dwTryBeginOffset = 0;
convertToHRTryCatch.dwHandlerBeginOffset = ((DWORD)pTryEndAndCatchBeginLabel->GetCodeOffset());
convertToHRTryCatch.cbTryLength = convertToHRTryCatch.dwHandlerBeginOffset - convertToHRTryCatch.dwTryBeginOffset;
convertToHRTryCatch.cbHandlerLength = ((DWORD)pCatchEndAndReturnLabel->GetCodeOffset()) - convertToHRTryCatch.dwHandlerBeginOffset;
convertToHRTryCatch.dwTypeToken = pcsDispatch->GetToken(g_pObjectClass);
}
#endif // FEATURE_COMINTEROP
int nEHClauses = 0;
if (convertToHRTryCatch.cbHandlerLength != 0)
nEHClauses++;
if (cleanupTryFinally.cbHandlerLength != 0)
nEHClauses++;
if (nEHClauses > 0)
{
COR_ILMETHOD_SECT_EH* pEHSect = pResolver->AllocEHSect(nEHClauses);
PopulateEHSect(pEHSect, nEHClauses, &cleanupTryFinally, &convertToHRTryCatch);
}
m_slIL.GenerateCode(pbBuffer, cbCode);
m_slIL.GetLocalSig(pbLocalSig, cbSig);
pResolver->SetJitFlags(jitFlags);
#ifdef LOGGING
LOG((LF_STUBS, LL_INFO1000, "---------------------------------------------------------------------\n"));
LOG((LF_STUBS, LL_INFO1000, "NDirect IL stub dump: %s::%s\n", pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
if (LoggingEnabled() && LoggingOn(LF_STUBS, LL_INFO1000))
{
CQuickBytes qbManaged;
CQuickBytes qbLocal;
PCCOR_SIGNATURE pManagedSig;
ULONG cManagedSig;
IMDInternalImport* pIMDI = MscorlibBinder::GetModule()->GetMDImport();
pStubMD->GetSig(&pManagedSig, &cManagedSig);
PrettyPrintSig(pManagedSig, cManagedSig, "*", &qbManaged, pStubMD->GetMDImport(), NULL);
PrettyPrintSig(pbLocalSig, cbSig, NULL, &qbLocal, pIMDI, NULL);
LOG((LF_STUBS, LL_INFO1000, "incoming managed sig: %p: %s\n", pManagedSig, qbManaged.Ptr()));
LOG((LF_STUBS, LL_INFO1000, "locals sig: %p: %s\n", pbLocalSig+1, qbLocal.Ptr()));
if (cleanupTryFinally.cbHandlerLength != 0)
{
LOG((LF_STUBS, LL_INFO1000, "try_begin: 0x%04x try_end: 0x%04x finally_begin: 0x%04x finally_end: 0x%04x \n",
cleanupTryFinally.dwTryBeginOffset, cleanupTryFinally.dwTryBeginOffset + cleanupTryFinally.cbTryLength,
cleanupTryFinally.dwHandlerBeginOffset, cleanupTryFinally.dwHandlerBeginOffset + cleanupTryFinally.cbHandlerLength));
}
if (convertToHRTryCatch.cbHandlerLength != 0)
{
LOG((LF_STUBS, LL_INFO1000, "try_begin: 0x%04x try_end: 0x%04x catch_begin: 0x%04x catch_end: 0x%04x type_token: 0x%08x\n",
convertToHRTryCatch.dwTryBeginOffset, convertToHRTryCatch.dwTryBeginOffset + convertToHRTryCatch.cbTryLength,
convertToHRTryCatch.dwHandlerBeginOffset, convertToHRTryCatch.dwHandlerBeginOffset + convertToHRTryCatch.cbHandlerLength,
convertToHRTryCatch.dwTypeToken));
}
LogILStubFlags(LF_STUBS, LL_INFO1000, m_dwStubFlags);
m_slIL.LogILStub(jitFlags);
}
LOG((LF_STUBS, LL_INFO1000, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"));
#endif // LOGGING
}
#ifdef LOGGING
//---------------------------------------------------------------------------------------
//
static inline void LogOneFlag(DWORD flags, DWORD flag, LPCSTR str, DWORD facility, DWORD level)
{
LIMITED_METHOD_CONTRACT;
if (flags & flag)
{
LOG((facility, level, str));
}
}
static void LogILStubFlags(DWORD facility, DWORD level, DWORD dwStubFlags)
{
LIMITED_METHOD_CONTRACT;
LOG((facility, level, "dwStubFlags: 0x%08x\n", dwStubFlags));
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_CONVSIGASVARARG, " NDIRECTSTUB_FL_CONVSIGASVARARG\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_BESTFIT, " NDIRECTSTUB_FL_BESTFIT\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR, " NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUB, " NDIRECTSTUB_FL_NGENEDSTUB\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DELEGATE, " NDIRECTSTUB_FL_DELEGATE\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DOHRESULTSWAPPING, " NDIRECTSTUB_FL_DOHRESULTSWAPPING\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_REVERSE_INTEROP, " NDIRECTSTUB_FL_REVERSE_INTEROP\n", facility, level);
#ifdef FEATURE_COMINTEROP
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_COM, " NDIRECTSTUB_FL_COM\n", facility, level);
#endif // FEATURE_COMINTEROP
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING, " NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL, " NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_UNMANAGED_CALLI, " NDIRECTSTUB_FL_UNMANAGED_CALLI\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_TRIGGERCCTOR, " NDIRECTSTUB_FL_TRIGGERCCTOR\n", facility, level);
#ifdef FEATURE_COMINTEROP
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDGETTER, " NDIRECTSTUB_FL_FIELDGETTER\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDSETTER, " NDIRECTSTUB_FL_FIELDSETTER\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRT, " NDIRECTSTUB_FL_WINRT\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTDELEGATE, " NDIRECTSTUB_FL_WINRTDELEGATE\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSHAREDGENERIC, " NDIRECTSTUB_FL_WINRTSHAREDGENERIC\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCTOR, " NDIRECTSTUB_FL_WINRTCTOR\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCOMPOSITION, " NDIRECTSTUB_FL_WINRTCOMPOSITION\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSTATIC, " NDIRECTSTUB_FL_WINRTSTATIC\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTHASREDIRECTION, " NDIRECTSTUB_FL_WINRTHASREDIRECTION\n", facility, level);
#endif // FEATURE_COMINTEROP
//
// no need to log the internal flags, let's just assert what we expect to see...
//
CONSISTENCY_CHECK(!SF_IsCOMLateBoundStub(dwStubFlags));
CONSISTENCY_CHECK(!SF_IsCOMEventCallStub(dwStubFlags));
DWORD dwKnownMask =
NDIRECTSTUB_FL_CONVSIGASVARARG |
NDIRECTSTUB_FL_BESTFIT |
NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR |
NDIRECTSTUB_FL_NGENEDSTUB |
NDIRECTSTUB_FL_DELEGATE |
NDIRECTSTUB_FL_DOHRESULTSWAPPING |
NDIRECTSTUB_FL_REVERSE_INTEROP |
NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING |
NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL |
NDIRECTSTUB_FL_UNMANAGED_CALLI |
NDIRECTSTUB_FL_TRIGGERCCTOR |
#ifdef FEATURE_COMINTEROP
NDIRECTSTUB_FL_COM |
NDIRECTSTUB_FL_COMLATEBOUND | // internal
NDIRECTSTUB_FL_COMEVENTCALL | // internal
NDIRECTSTUB_FL_FIELDGETTER |
NDIRECTSTUB_FL_FIELDSETTER |
NDIRECTSTUB_FL_WINRT |
NDIRECTSTUB_FL_WINRTDELEGATE |
NDIRECTSTUB_FL_WINRTCTOR |
NDIRECTSTUB_FL_WINRTCOMPOSITION |
NDIRECTSTUB_FL_WINRTSTATIC |
NDIRECTSTUB_FL_WINRTHASREDIRECTION |
#endif // FEATURE_COMINTEROP
NULL;
DWORD dwUnknownFlags = dwStubFlags & ~dwKnownMask;
if (0 != dwUnknownFlags)
{
LOG((facility, level, "UNKNOWN FLAGS: 0x%08x\n", dwUnknownFlags));
}
}
#endif // LOGGING
PCCOR_SIGNATURE GetStubTargetMethodSig()
{
CONTRACT(PCCOR_SIGNATURE)
{
STANDARD_VM_CHECK;
POSTCONDITION(CheckPointer(RETVAL, NULL_NOT_OK));
}
CONTRACT_END;
BYTE *pb;
if (!m_qbNativeFnSigBuffer.Size())
{
DWORD cb = m_slIL.GetStubTargetMethodSigSize();
pb = (BYTE *)m_qbNativeFnSigBuffer.AllocThrows(cb);
m_slIL.GetStubTargetMethodSig(pb, cb);
}
else
{
pb = (BYTE*)m_qbNativeFnSigBuffer.Ptr();
}
RETURN pb;
}
DWORD
GetStubTargetMethodSigLength()
{
WRAPPER_NO_CONTRACT;
return m_slIL.GetStubTargetMethodSigSize();
}
void SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig)
{
WRAPPER_NO_CONTRACT;
m_slIL.SetStubTargetMethodSig(pSig, cSig);
m_qbNativeFnSigBuffer.Shrink(0);
}
TokenLookupMap* GetTokenLookupMap() { WRAPPER_NO_CONTRACT; return m_slIL.GetTokenLookupMap(); }
protected:
CQuickBytes m_qbNativeFnSigBuffer;
NDirectStubLinker m_slIL;
BOOL m_fSetLastError;
DWORD m_dwStubFlags;
};
class PInvoke_ILStubState : public ILStubState
{
public:
PInvoke_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
CorPinvokeMap unmgdCallConv, int iLCIDParamIdx, MethodDesc* pTargetMD)
: ILStubState(
pStubModule,
signature,
pTypeContext,
TargetHasThis(dwStubFlags),
StubHasThis(dwStubFlags),
dwStubFlags,
iLCIDParamIdx,
pTargetMD)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardStub(dwStubFlags))
{
m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags));
}
}
private:
static BOOL TargetHasThis(DWORD dwStubFlags)
{
//
// in reverse pinvoke on delegate, the managed target will
// have a 'this' pointer, but the unmanaged signature does
// not.
//
return SF_IsReverseDelegateStub(dwStubFlags);
}
static BOOL StubHasThis(DWORD dwStubFlags)
{
//
// in forward pinvoke on a delegate, the stub will have a
// 'this' pointer, but the unmanaged target will not.
//
return SF_IsForwardDelegateStub(dwStubFlags);
}
};
#ifdef FEATURE_COMINTEROP
class CLRToCOM_ILStubState : public ILStubState
{
public:
CLRToCOM_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
int iLCIDParamIdx, MethodDesc* pTargetMD)
: ILStubState(
pStubModule,
signature,
pTypeContext,
TRUE,
!SF_IsWinRTStaticStub(dwStubFlags), // fStubHasThis
dwStubFlags,
iLCIDParamIdx,
pTargetMD)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardStub(dwStubFlags))
{
m_slIL.SetCallingConvention(pmCallConvStdcall, SF_IsVarArgStub(dwStubFlags));
}
}
void BeginEmit(DWORD dwStubFlags) // CLR to COM IL
{
STANDARD_VM_CONTRACT;
ILStubState::BeginEmit(dwStubFlags);
ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
// add the 'this' COM IP parameter to the target CALLI
m_slIL.GetMarshalCodeStream()->SetStubTargetArgType(ELEMENT_TYPE_I, false);
// convert 'this' to COM IP and the target method entry point
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
#ifdef _TARGET_64BIT_
if (SF_IsWinRTDelegateStub(m_dwStubFlags))
{
// write the stub context (EEImplMethodDesc representing the Invoke)
// into the secret arg so it shows up in the InlinedCallFrame and can
// be used by stub for host
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
pcsDispatch->EmitSTIND_I();
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
}
else
#endif // _TARGET_64BIT_
{
m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
}
pcsDispatch->EmitLDLOCA(m_slIL.GetTargetEntryPointLocalNum());
BinderMethodID getCOMIPMethod;
bool fDoPostCallIPCleanup = true;
if (!SF_IsNGENedStub(dwStubFlags) && NDirect::IsHostHookEnabled())
{
// always use the non-optimized helper if we are hosted
getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW;
}
else if (SF_IsWinRTStub(dwStubFlags))
{
// WinRT uses optimized helpers
if (SF_IsWinRTSharedGenericStub(dwStubFlags))
getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_SHARED_GENERIC;
else if (SF_IsWinRTDelegateStub(dwStubFlags))
getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_DELEGATE;
else
getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT;
// GetCOMIPFromRCW_WinRT, GetCOMIPFromRCW_WinRTSharedGeneric, and GetCOMIPFromRCW_WinRTDelegate
// always cache the COM interface pointer so no post-call cleanup is needed
fDoPostCallIPCleanup = false;
}
else
{
// classic COM interop uses the non-optimized helper
getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW;
}
DWORD dwIPRequiresCleanupLocalNum = (DWORD)-1;
if (fDoPostCallIPCleanup)
{
dwIPRequiresCleanupLocalNum = pcsDispatch->NewLocal(ELEMENT_TYPE_BOOLEAN);
pcsDispatch->EmitLDLOCA(dwIPRequiresCleanupLocalNum);
// StubHelpers.GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease)
pcsDispatch->EmitCALL(getCOMIPMethod, 4, 1);
}
else
{
// StubHelpers.GetCOMIPFromRCW_WinRT*(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget)
pcsDispatch->EmitCALL(getCOMIPMethod, 3, 1);
}
// save it because we'll need it to compute the CALLI target and release it
pcsDispatch->EmitDUP();
pcsDispatch->EmitSTLOC(m_slIL.GetTargetInterfacePointerLocalNum());
if (fDoPostCallIPCleanup)
{
// make sure it's Release()'ed after the call
m_slIL.SetCleanupNeeded();
ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
ILCodeLabel *pSkipThisCleanup = pcsCleanup->NewCodeLabel();
// and if it requires cleanup (i.e. it's not taken from the RCW cache)
pcsCleanup->EmitLDLOC(dwIPRequiresCleanupLocalNum);
pcsCleanup->EmitBRFALSE(pSkipThisCleanup);
pcsCleanup->EmitLDLOC(m_slIL.GetTargetInterfacePointerLocalNum());
pcsCleanup->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
pcsCleanup->EmitLabel(pSkipThisCleanup);
}
}
};
class COMToCLR_ILStubState : public ILStubState
{
public:
COMToCLR_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
int iLCIDParamIdx, MethodDesc* pTargetMD)
: ILStubState(
pStubModule,
signature,
pTypeContext,
TRUE,
TRUE,
dwStubFlags,
iLCIDParamIdx,
pTargetMD)
{
STANDARD_VM_CONTRACT;
}
void BeginEmit(DWORD dwStubFlags) // COM to CLR IL
{
STANDARD_VM_CONTRACT;
ILStubState::BeginEmit(dwStubFlags);
if (SF_IsWinRTStaticStub(dwStubFlags))
{
// we are not loading 'this' because the target is static
m_slIL.AdjustTargetStackDeltaForExtraParam();
}
else
{
// load this
m_slIL.GetDispatchCodeStream()->EmitLoadThis();
}
}
void MarshalFactoryReturn()
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
}
CONTRACTL_END;
ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
ILCodeStream *pcsExCleanup = m_slIL.GetExceptionCleanupCodeStream();
LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
pcsSetup->EmitLoadNullPtr();
pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
locDescFactoryRetVal.MakeByRef();
// expect one additional argument - pointer to a location that receives the created instance
DWORD dwRetValArgNum = pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
m_slIL.AdjustTargetStackDeltaForExtraParam();
// convert 'this' to an interface pointer corresponding to the default interface of this class
pcsUnmarshal->EmitLoadThis();
pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_RETURN_VALUE, 2, 1);
pcsUnmarshal->EmitSTLOC(dwFactoryRetValLocalNum);
// assign it to the location pointed to by the argument
pcsUnmarshal->EmitLDARG(dwRetValArgNum);
pcsUnmarshal->EmitLDLOC(dwFactoryRetValLocalNum);
pcsUnmarshal->EmitSTIND_I();
// on exception, we want to release the IInspectable's and assign NULL to output locations
m_slIL.SetExceptionCleanupNeeded();
EmitInterfaceClearNative(pcsExCleanup, dwFactoryRetValLocalNum);
// *retVal = NULL
pcsExCleanup->EmitLDARG(dwRetValArgNum);
pcsExCleanup->EmitLoadNullPtr();
pcsExCleanup->EmitSTIND_I();
}
};
class COMToCLRFieldAccess_ILStubState : public COMToCLR_ILStubState
{
public:
COMToCLRFieldAccess_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext,
DWORD dwStubFlags, FieldDesc* pFD)
: COMToCLR_ILStubState(
pStubModule,
signature,
pTypeContext,
dwStubFlags,
-1,
NULL)
{
STANDARD_VM_CONTRACT;
_ASSERTE(pFD != NULL);
m_pFD = pFD;
}
void EmitInvokeTarget(MethodDesc *pStubMD)
{
STANDARD_VM_CONTRACT;
ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
if (SF_IsFieldGetterStub(m_dwStubFlags))
{
pcsDispatch->EmitLDFLD(pcsDispatch->GetToken(m_pFD));
}
else
{
CONSISTENCY_CHECK(SF_IsFieldSetterStub(m_dwStubFlags));
pcsDispatch->EmitSTFLD(pcsDispatch->GetToken(m_pFD));
}
}
protected:
FieldDesc *m_pFD;
};
#endif // FEATURE_COMINTEROP
NDirectStubLinker::NDirectStubLinker(
DWORD dwStubFlags,
Module* pModule,
const Signature &signature,
SigTypeContext *pTypeContext,
MethodDesc* pTargetMD,
int iLCIDParamIdx,
BOOL fTargetHasThis,
BOOL fStubHasThis)
: ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags)),
m_pCleanupFinallyBeginLabel(NULL),
m_pCleanupFinallyEndLabel(NULL),
m_pSkipExceptionCleanupLabel(NULL),
#ifdef FEATURE_COMINTEROP
m_dwWinRTFactoryObjectLocalNum(-1),
#endif // FEATURE_COMINTEROP
m_fHasCleanupCode(FALSE),
m_fHasExceptionCleanupCode(FALSE),
m_fCleanupWorkListIsSetup(FALSE),
m_dwThreadLocalNum(-1),
m_dwCleanupWorkListLocalNum(-1),
m_dwRetValLocalNum(-1),
m_ErrorResID(-1),
m_ErrorParamIdx(-1),
m_iLCIDParamIdx(iLCIDParamIdx),
m_dwStubFlags(dwStubFlags)
{
STANDARD_VM_CONTRACT;
m_pcsSetup = NewCodeStream(ILStubLinker::kSetup); // do any one-time setup work
m_pcsMarshal = NewCodeStream(ILStubLinker::kMarshal); // marshals arguments
m_pcsDispatch = NewCodeStream(ILStubLinker::kDispatch); // sets up arguments and makes call
m_pcsRetUnmarshal = NewCodeStream(ILStubLinker::kReturnUnmarshal); // unmarshals return value
m_pcsUnmarshal = NewCodeStream(ILStubLinker::kUnmarshal); // unmarshals arguments
m_pcsExceptionCleanup = NewCodeStream(ILStubLinker::kExceptionCleanup); // MAY NOT THROW: goes in a finally and does exception-only cleanup
m_pcsCleanup = NewCodeStream(ILStubLinker::kCleanup); // MAY NOT THROW: goes in a finally and does unconditional cleanup
//
// Add locals
m_dwArgMarshalIndexLocalNum = NewLocal(ELEMENT_TYPE_I4);
m_pcsMarshal->EmitLDC(0);
m_pcsMarshal->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
#ifdef FEATURE_COMINTEROP
//
// Forward COM interop needs a local to hold target interface pointer
//
if (SF_IsForwardCOMStub(m_dwStubFlags))
{
m_dwTargetEntryPointLocalNum = NewLocal(ELEMENT_TYPE_I);
m_dwTargetInterfacePointerLocalNum = NewLocal(ELEMENT_TYPE_I);
m_pcsSetup->EmitLoadNullPtr();
m_pcsSetup->EmitSTLOC(m_dwTargetInterfacePointerLocalNum);
}
#endif // FEATURE_COMINTEROP
}
void NDirectStubLinker::SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL fIsVarArg)
{
LIMITED_METHOD_CONTRACT;
ULONG uNativeCallingConv = 0;
#if !defined(_TARGET_X86_)
if (fIsVarArg)
{
// The JIT has to use a different calling convention for unmanaged vararg targets on 64-bit and ARM:
// any float values must be duplicated in the corresponding general-purpose registers.
uNativeCallingConv = CORINFO_CALLCONV_NATIVEVARARG;
}
else
#endif // !_TARGET_X86_
{
switch (unmngCallConv)
{
case pmCallConvCdecl:
uNativeCallingConv = CORINFO_CALLCONV_C;
break;
case pmCallConvStdcall:
uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
break;
case pmCallConvThiscall:
uNativeCallingConv = CORINFO_CALLCONV_THISCALL;
break;
default:
_ASSERTE(!"Invalid calling convention.");
uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
break;
}
}
SetStubTargetCallingConv((CorCallingConvention)uNativeCallingConv);
}
void NDirectStubLinker::EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx)
{
WRAPPER_NO_CONTRACT;
//
// This sets our state local variable that tracks the progress of the stub execution.
// In the finally block we test this variable to see what cleanup we need to do. The
// variable starts with the value of 0 and is assigned the following values as the
// stub executes:
//
// CLEANUP_INDEX_ARG0_MARSHAL + 1 - 1st argument marshaled
// CLEANUP_INDEX_ARG0_MARSHAL + 2 - 2nd argument marshaled
// ...
// CLEANUP_INDEX_ARG0_MARSHAL + n - nth argument marshaled
// CLEANUP_INDEX_RETVAL_UNMARSHAL + 1 - return value unmarshaled
// CLEANUP_INDEX_ARG0_UNMARSHAL + 1 - 1st argument unmarshaled
// CLEANUP_INDEX_ARG0_UNMARSHAL + 2 - 2nd argument unmarshaled
// ...
// CLEANUP_INDEX_ARG0_UNMARSHAL + n - nth argument unmarshaled
// CLEANUP_INDEX_ALL_DONE + 1 - ran to completion, no exception thrown
//
// Note: There may be gaps, i.e. if say 2nd argument does not need cleanup, the
// state variable will never be assigned the corresponding value. However, the
// value must always monotonically increase so we can use <=, >, etc.
//
pcsEmit->EmitLDC(uArgIdx + 1);
pcsEmit->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
}
void NDirectStubLinker::EmitCheckForArgCleanup(ILCodeStream* pcsEmit, UINT uArgIdx, ArgCleanupBranchKind branchKind, ILCodeLabel* pSkipCleanupLabel)
{
STANDARD_VM_CONTRACT;
SetCleanupNeeded();
// See EmitSetArgMarshalIndex.
pcsEmit->EmitLDLOC(m_dwArgMarshalIndexLocalNum);
pcsEmit->EmitLDC(uArgIdx);
switch (branchKind)
{
case BranchIfMarshaled:
{
// we branch to the label if the argument has been marshaled
pcsEmit->EmitBGT(pSkipCleanupLabel);
break;
}
case BranchIfNotMarshaled:
{
// we branch to the label if the argument has not been marshaled
pcsEmit->EmitBLE(pSkipCleanupLabel);
break;
}
default:
UNREACHABLE();
}
}
int NDirectStubLinker::GetLCIDParamIdx()
{
LIMITED_METHOD_CONTRACT;
return m_iLCIDParamIdx;
}
ILCodeStream* NDirectStubLinker::GetSetupCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsSetup;
}
ILCodeStream* NDirectStubLinker::GetMarshalCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsMarshal;
}
ILCodeStream* NDirectStubLinker::GetUnmarshalCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsUnmarshal;
}
ILCodeStream* NDirectStubLinker::GetReturnUnmarshalCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsRetUnmarshal;
}
ILCodeStream* NDirectStubLinker::GetDispatchCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsDispatch;
}
ILCodeStream* NDirectStubLinker::GetCleanupCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsCleanup;
}
ILCodeStream* NDirectStubLinker::GetExceptionCleanupCodeStream()
{
LIMITED_METHOD_CONTRACT;
return m_pcsExceptionCleanup;
}
void NDirectStubLinker::AdjustTargetStackDeltaForExtraParam()
{
LIMITED_METHOD_CONTRACT;
//
// Compensate for the extra parameter.
//
m_iTargetStackDelta++;
}
void NDirectStubLinker::AdjustTargetStackDeltaForReverseInteropHRESULTSwapping()
{
WRAPPER_NO_CONTRACT;
//
// In the case of reverse pinvoke, we build up the 'target'
// signature as if it were normal forward pinvoke and then
// switch that signature (representing the native sig) with
// the stub's sig (representing the managed sig). However,
// as a side-effect, our calcualted target stack delta is
// wrong.
//
// The only way that we support a different stack delta is
// through hresult swapping. So this code "undoes" the
// deltas that would have been applied in that case.
//
if (StubHasVoidReturnType())
{
//
// If the managed return type is void, undo the HRESULT
// return type added to our target sig for HRESULT swapping.
// No extra argument will have been added because it makes
// no sense to add an extry byref void argument.
//
m_iTargetStackDelta--;
}
else
{
//
// no longer pop the extra byref argument from the stack
//
m_iTargetStackDelta++;
}
}
void NDirectStubLinker::SetInteropParamExceptionInfo(UINT resID, UINT paramIdx)
{
LIMITED_METHOD_CONTRACT;
// only keep the first one
if (HasInteropParamExceptionInfo())
{
return;
}
m_ErrorResID = resID;
m_ErrorParamIdx = paramIdx;
}
bool NDirectStubLinker::HasInteropParamExceptionInfo()
{
LIMITED_METHOD_CONTRACT;
return !(((DWORD)-1 == m_ErrorResID) && ((DWORD)-1 == m_ErrorParamIdx));
}
void NDirectStubLinker::GenerateInteropParamException(ILCodeStream* pcsEmit)
{
STANDARD_VM_CONTRACT;
pcsEmit->EmitLDC(m_ErrorResID);
pcsEmit->EmitLDC(m_ErrorParamIdx);
pcsEmit->EmitCALL(METHOD__STUBHELPERS__THROW_INTEROP_PARAM_EXCEPTION, 2, 0);
pcsEmit->EmitLDNULL();
pcsEmit->EmitTHROW();
}
#ifdef FEATURE_COMINTEROP
DWORD NDirectStubLinker::GetTargetInterfacePointerLocalNum()
{
LIMITED_METHOD_CONTRACT;
CONSISTENCY_CHECK(m_dwTargetInterfacePointerLocalNum != (DWORD)-1);
return m_dwTargetInterfacePointerLocalNum;
}
DWORD NDirectStubLinker::GetTargetEntryPointLocalNum()
{
LIMITED_METHOD_CONTRACT;
CONSISTENCY_CHECK(m_dwTargetEntryPointLocalNum != (DWORD)-1);
return m_dwTargetEntryPointLocalNum;
}
void NDirectStubLinker::EmitLoadRCWThis(ILCodeStream *pcsEmit, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardStub(dwStubFlags) &&
(SF_IsWinRTCtorStub(dwStubFlags) || SF_IsWinRTStaticStub(dwStubFlags)))
{
// WinRT ctor/static stubs make the call on the factory object instead of 'this'
if (m_dwWinRTFactoryObjectLocalNum == (DWORD)-1)
{
m_dwWinRTFactoryObjectLocalNum = NewLocal(ELEMENT_TYPE_OBJECT);
// get the factory object
EmitLoadStubContext(m_pcsSetup, dwStubFlags);
m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_OBJECT, 1, 1);
m_pcsSetup->EmitSTLOC(m_dwWinRTFactoryObjectLocalNum);
}
pcsEmit->EmitLDLOC(m_dwWinRTFactoryObjectLocalNum);
}
else
{
pcsEmit->EmitLoadThis();
}
}
#endif // FEATURE_COMINTEROP
DWORD NDirectStubLinker::GetCleanupWorkListLocalNum()
{
LIMITED_METHOD_CONTRACT;
CONSISTENCY_CHECK(m_dwCleanupWorkListLocalNum != (DWORD)-1);
return m_dwCleanupWorkListLocalNum;
}
DWORD NDirectStubLinker::GetThreadLocalNum()
{
STANDARD_VM_CONTRACT;
if (m_dwThreadLocalNum == (DWORD)-1)
{
// The local is created and initialized lazily when first asked.
m_dwThreadLocalNum = NewLocal(ELEMENT_TYPE_I);
m_pcsSetup->EmitCALL(METHOD__THREAD__INTERNAL_GET_CURRENT_THREAD, 0, 1);
m_pcsSetup->EmitSTLOC(m_dwThreadLocalNum);
}
return m_dwThreadLocalNum;
}
DWORD NDirectStubLinker::GetReturnValueLocalNum()
{
LIMITED_METHOD_CONTRACT;
return m_dwRetValLocalNum;
}
BOOL NDirectStubLinker::IsCleanupNeeded()
{
LIMITED_METHOD_CONTRACT;
return (m_fHasCleanupCode || IsCleanupWorkListSetup());
}
BOOL NDirectStubLinker::IsExceptionCleanupNeeded()
{
LIMITED_METHOD_CONTRACT;
return m_fHasExceptionCleanupCode;
}
void NDirectStubLinker::InitCleanupCode()
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(NULL == m_pCleanupFinallyBeginLabel);
}
CONTRACTL_END;
m_pCleanupFinallyBeginLabel = NewCodeLabel();
m_pcsExceptionCleanup->EmitLabel(m_pCleanupFinallyBeginLabel);
}
void NDirectStubLinker::InitExceptionCleanupCode()
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(NULL == m_pSkipExceptionCleanupLabel);
}
CONTRACTL_END;
SetCleanupNeeded();
// we want to skip the entire exception cleanup if no exception has been thrown
m_pSkipExceptionCleanupLabel = NewCodeLabel();
EmitCheckForArgCleanup(m_pcsExceptionCleanup, CLEANUP_INDEX_ALL_DONE, BranchIfMarshaled, m_pSkipExceptionCleanupLabel);
}
void NDirectStubLinker::SetCleanupNeeded()
{
WRAPPER_NO_CONTRACT;
if (!m_fHasCleanupCode)
{
m_fHasCleanupCode = TRUE;
InitCleanupCode();
}
}
void NDirectStubLinker::SetExceptionCleanupNeeded()
{
WRAPPER_NO_CONTRACT;
if (!m_fHasExceptionCleanupCode)
{
m_fHasExceptionCleanupCode = TRUE;
InitExceptionCleanupCode();
}
}
void NDirectStubLinker::NeedsCleanupList()
{
STANDARD_VM_CONTRACT;
if (!IsCleanupWorkListSetup())
{
m_fCleanupWorkListIsSetup = TRUE;
SetCleanupNeeded();
// we setup a new local that will hold the cleanup work list
LocalDesc desc(MscorlibBinder::GetClass(CLASS__CLEANUP_WORK_LIST));
m_dwCleanupWorkListLocalNum = NewLocal(desc);
}
}
BOOL NDirectStubLinker::IsCleanupWorkListSetup ()
{
LIMITED_METHOD_CONTRACT;
return m_fCleanupWorkListIsSetup;
}
void NDirectStubLinker::LoadCleanupWorkList(ILCodeStream* pcsEmit)
{
STANDARD_VM_CONTRACT;
NeedsCleanupList();
pcsEmit->EmitLDLOCA(GetCleanupWorkListLocalNum());
}
void NDirectStubLinker::Begin(DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
#ifdef FEATURE_COMINTEROP
if (SF_IsWinRTHasRedirection(dwStubFlags))
{
_ASSERTE(SF_IsForwardCOMStub(dwStubFlags));
// The very first thing we need to do is check whether the call should be routed to
// the marshaling stub for the corresponding projected WinRT interface. If so, we
// tail-call there.
m_pcsSetup->EmitLoadThis();
EmitLoadStubContext(m_pcsSetup, dwStubFlags);
m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__SHOULD_CALL_WINRT_INTERFACE, 2, 1);
ILCodeLabel *pNoRedirection = m_pcsSetup->NewCodeLabel();
m_pcsSetup->EmitBRFALSE(pNoRedirection);
MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(
GetTargetMD(),
TypeHandle::Interop_ManagedToNative);
CONSISTENCY_CHECK(pAdapterMD != NULL && !pAdapterMD->HasMethodInstantiation());
m_pcsSetup->EmitJMP(m_pcsSetup->GetToken(pAdapterMD));
m_pcsSetup->EmitLabel(pNoRedirection);
}
#endif // FEATURE_COMINTEROP
if (SF_IsForwardStub(dwStubFlags))
{
if (SF_IsStubWithCctorTrigger(dwStubFlags))
{
EmitLoadStubContext(m_pcsSetup, dwStubFlags);
m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__INIT_DECLARING_TYPE, 1, 0);
}
}
else
{
#ifdef MDA_SUPPORTED
if (!SF_IsNGENedStub(dwStubFlags) && MDA_GET_ASSISTANT(GcUnmanagedToManaged))
{
EmitCallGcCollectForMDA(m_pcsSetup, dwStubFlags);
}
#endif // MDA_SUPPORTED
if (SF_IsDelegateStub(dwStubFlags))
{
#if defined(MDA_SUPPORTED)
// GC was induced (gcUnmanagedToManagedMDA), arguments have been marshaled, and we are about
// to touch the UMEntryThunk and extract the delegate target from it so this is the right time
// to do the collected delegate MDA check.
// The call to CheckCollectedDelegateMDA is emitted regardless of whether the MDA is on at the
// moment. This is to avoid having to ignore NGENed stubs without the call just as we do for
// the GC MDA (callbackOncollectedDelegateMDA is turned on under managed debugger by default
// so the impact would be substantial). The helper bails out fast if the MDA is not enabled.
EmitLoadStubContext(m_pcsDispatch, dwStubFlags);
m_pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CHECK_COLLECTED_DELEGATE_MDA, 1, 0);
#endif // MDA_SUPPORTED
//
// recover delegate object from UMEntryThunk
EmitLoadStubContext(m_pcsDispatch, dwStubFlags); // load UMEntryThunk*
m_pcsDispatch->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
m_pcsDispatch->EmitADD();
m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE
m_pcsDispatch->EmitLDIND_REF(); // get Delegate object
m_pcsDispatch->EmitLDFLD(GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__TARGET)));
}
}
m_pCleanupTryBeginLabel = NewCodeLabel();
m_pcsMarshal->EmitLabel(m_pCleanupTryBeginLabel);
}
void NDirectStubLinker::End(DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
ILCodeStream* pcs = m_pcsUnmarshal;
bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(dwStubFlags)
&& !SF_IsFieldGetterStub(dwStubFlags)
&& !SF_IsFieldSetterStub(dwStubFlags);
//
// Create a local for the return value and store the return value in it.
//
if (IsCleanupNeeded() || hasTryCatchForHRESULT)
{
// Save the return value if necessary, since the IL stack will be emptied when we leave a try block.
LocalDesc locDescRetVal;
if (SF_IsForwardStub(dwStubFlags))
{
GetStubReturnType(&locDescRetVal);
}
else
{
GetStubTargetReturnType(&locDescRetVal);
}
if (!( (locDescRetVal.cbType == 1) && (locDescRetVal.ElementType[0] == ELEMENT_TYPE_VOID) ))
{
m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
if (SF_IsReverseStub(dwStubFlags) && StubHasVoidReturnType())
{
// if the target returns void and we are doing HRESULT swapping, S_OK is loaded
// in the unmarshal stream
m_pcsUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
}
else
{
// otherwise the return value is loaded in the return unmarshal stream
m_pcsRetUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
}
}
else if (hasTryCatchForHRESULT && (locDescRetVal.ElementType[0] != ELEMENT_TYPE_VOID))
{
m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
}
}
//
// Emit end-of-try and end-of-finally code for the try/finally
//
if (IsCleanupNeeded())
{
m_pCleanupFinallyEndLabel = NewCodeLabel();
m_pCleanupTryEndLabel = NewCodeLabel();
if (IsExceptionCleanupNeeded())
{
// if we made it here, no exception has been thrown
EmitSetArgMarshalIndex(m_pcsUnmarshal, CLEANUP_INDEX_ALL_DONE);
}
// Emit a leave at the end of the try block. If we have an outer try/catch, we need
// to leave to the beginning of the ExceptionHandler code stream, which follows the
// Cleanup code stream. If we don't, we can just leave to the tail end of the
// Unmarshal code stream where we'll emit our RET.
ILCodeLabel* pLeaveTarget = m_pCleanupTryEndLabel;
if (hasTryCatchForHRESULT)
{
pLeaveTarget = m_pCleanupFinallyEndLabel;
}
m_pcsUnmarshal->EmitLEAVE(pLeaveTarget);
m_pcsUnmarshal->EmitLabel(m_pCleanupTryEndLabel);
// Emit a call to destroy the clean-up list if needed.
if (IsCleanupWorkListSetup())
{
LoadCleanupWorkList(m_pcsCleanup);
m_pcsCleanup->EmitCALL(METHOD__STUBHELPERS__DESTROY_CLEANUP_LIST, 1, 0);
}
// Emit the endfinally.
m_pcsCleanup->EmitENDFINALLY();
m_pcsCleanup->EmitLabel(m_pCleanupFinallyEndLabel);
}
#ifdef MDA_SUPPORTED
if (SF_IsReverseStub(dwStubFlags) && !SF_IsNGENedStub(dwStubFlags) &&
MDA_GET_ASSISTANT(GcManagedToUnmanaged))
{
EmitCallGcCollectForMDA(pcs, dwStubFlags);
}
#endif // MDA_SUPPORTED
if (IsExceptionCleanupNeeded())
{
m_pcsExceptionCleanup->EmitLabel(m_pSkipExceptionCleanupLabel);
}
// Reload the return value
if ((m_dwRetValLocalNum != (DWORD)-1) && !hasTryCatchForHRESULT)
{
pcs->EmitLDLOC(m_dwRetValLocalNum);
}
}
void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardStub(dwStubFlags)) // managed-to-native
{
if (SF_IsDelegateStub(dwStubFlags)) // delegate invocation
{
// get the delegate unmanaged target - we call a helper instead of just grabbing
// the _methodPtrAux field because we may need to intercept the call for host, MDA, etc.
pcsEmit->EmitLoadThis();
#ifdef _TARGET_64BIT_
// on AMD64 GetDelegateTarget will return address of the generic stub for host when we are hosted
// and update the secret argument with real target - the secret arg will be embedded in the
// InlinedCallFrame by the JIT and fetched via TLS->Thread->Frame->Datum by the stub for host
pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
#else // !_TARGET_64BIT_
// we don't need to do this on x86 because stub for host is generated dynamically per target
pcsEmit->EmitLDNULL();
#endif // !_TARGET_64BIT_
pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 2, 1);
}
else // direct invocation
{
if (SF_IsCALLIStub(dwStubFlags)) // unmanaged CALLI
{
// if we ever NGEN CALLI stubs, this would have to be done differently
_ASSERTE(!SF_IsNGENedStub(dwStubFlags));
// for managed-to-unmanaged CALLI that requires marshaling, the target is passed
// as the secret argument to the stub by GenericPInvokeCalliHelper (asmhelpers.asm)
EmitLoadStubContext(pcsEmit, dwStubFlags);
#ifdef _WIN64
// the secret arg has been shifted to left and ORed with 1 (see code:GenericPInvokeCalliHelper)
pcsEmit->EmitLDC(1);
pcsEmit->EmitSHR_UN();
#endif
}
else
#ifdef FEATURE_COMINTEROP
if (!SF_IsCOMStub(dwStubFlags)) // forward P/Invoke
#endif // FEATURE_COMINTEROP
{
EmitLoadStubContext(pcsEmit, dwStubFlags);
{
// Perf: inline the helper for now
//pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_NDIRECT_TARGET, 1, 1);
pcsEmit->EmitLDC(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
pcsEmit->EmitADD();
if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
{
pcsEmit->EmitDUP();
}
pcsEmit->EmitLDIND_I();
if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
{
pcsEmit->EmitADD();
}
pcsEmit->EmitLDIND_I();
}
}
#ifdef FEATURE_COMINTEROP
else
{
// this is a CLR -> COM call
// the target has been computed by StubHelpers::GetCOMIPFromRCW
pcsEmit->EmitLDLOC(m_dwTargetEntryPointLocalNum);
}
#endif // FEATURE_COMINTEROP
}
}
else // native-to-managed
{
if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate
{
int tokDelegate_methodPtr = pcsEmit->GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__METHOD_PTR));
EmitLoadStubContext(pcsEmit, dwStubFlags);
pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
pcsEmit->EmitADD();
pcsEmit->EmitLDIND_I(); // Get OBJECTHANDLE
pcsEmit->EmitLDIND_REF(); // Get Delegate object
pcsEmit->EmitLDFLD(tokDelegate_methodPtr); // get _methodPtr
}
#ifdef FEATURE_COMINTEROP
else if (SF_IsCOMStub(dwStubFlags)) // COM -> CLR call
{
// managed target is passed directly in the secret argument
EmitLoadStubContext(pcsEmit, dwStubFlags);
}
#endif // FEATURE_COMINTEROP
else // direct reverse P/Invoke (CoreCLR hosting)
{
EmitLoadStubContext(pcsEmit, dwStubFlags);
CONSISTENCY_CHECK(0 == offsetof(UMEntryThunk, m_pManagedTarget)); // if this changes, just add back the EmitLDC/EmitADD below
// pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pManagedTarget));
// pcsEmit->EmitADD();
pcsEmit->EmitLDIND_I(); // Get UMEntryThunk::m_pManagedTarget
}
}
// For managed-to-native calls, the rest of the work is done by the JIT. It will
// erect InlinedCallFrame, flip GC mode, and use the specified calling convention
// to call the target. For native-to-managed calls, this is an ordinary managed
// CALLI and nothing special happens.
pcsEmit->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, 0, m_iTargetStackDelta);
}
void NDirectStubLinker::EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwPinnedLocal)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardPInvokeStub(m_dwStubFlags) && !SF_IsForwardDelegateStub(m_dwStubFlags))
{
// get the secret argument via intrinsic
pslILEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
}
else
{
// no secret argument
pslILEmit->EmitLoadNullPtr();
}
pslILEmit->EmitLDLOC(dwPinnedLocal);
pslILEmit->EmitCALL(METHOD__STUBHELPERS__LOG_PINNED_ARGUMENT, 2, 0);
}
void NDirectStubLinker::GetCleanupFinallyOffsets(ILStubEHClause * pClause)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pClause));
}
CONTRACTL_END;
if (m_pCleanupFinallyEndLabel)
{
_ASSERTE(m_pCleanupFinallyBeginLabel);
_ASSERTE(m_pCleanupTryBeginLabel);
_ASSERTE(m_pCleanupTryEndLabel);
pClause->kind = ILStubEHClause::kFinally;
pClause->dwTryBeginOffset = (DWORD)m_pCleanupTryBeginLabel->GetCodeOffset();
pClause->cbTryLength = (DWORD)m_pCleanupTryEndLabel->GetCodeOffset() - pClause->dwTryBeginOffset;
pClause->dwHandlerBeginOffset = (DWORD)m_pCleanupFinallyBeginLabel->GetCodeOffset();
pClause->cbHandlerLength = (DWORD)m_pCleanupFinallyEndLabel->GetCodeOffset() - pClause->dwHandlerBeginOffset;
}
}
void NDirectStubLinker::ClearCode()
{
WRAPPER_NO_CONTRACT;
ILStubLinker::ClearCode();
m_pCleanupTryBeginLabel = 0;
m_pCleanupTryEndLabel = 0;
m_pCleanupFinallyBeginLabel = 0;
m_pCleanupFinallyEndLabel = 0;
}
#ifdef PROFILING_SUPPORTED
DWORD NDirectStubLinker::EmitProfilerBeginTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
if (SF_IsForwardDelegateStub(dwStubFlags) || SF_IsCALLIStub(dwStubFlags))
{
// secret argument does not contain MD nor UMEntryThunk
pcsEmit->EmitLoadNullPtr();
}
else
{
EmitLoadStubContext(pcsEmit, dwStubFlags);
}
if (SF_IsForwardStub(dwStubFlags))
{
pcsEmit->EmitLDLOC(GetThreadLocalNum());
}
else
{
// we use a null pThread to indicate reverse interop
pcsEmit->EmitLDC(NULL);
}
// In the unmanaged delegate case, we need the "this" object to retrieve the MD
// in StubHelpers::ProfilerEnterCallback().
if (SF_IsDelegateStub(dwStubFlags))
{
if (SF_IsForwardStub(dwStubFlags))
{
pcsEmit->EmitLoadThis();
}
else
{
EmitLoadStubContext(pcsEmit, dwStubFlags); // load UMEntryThunk*
pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
pcsEmit->EmitADD();
pcsEmit->EmitLDIND_I(); // get OBJECTHANDLE
pcsEmit->EmitLDIND_REF(); // get Delegate object
}
}
else
{
pcsEmit->EmitLDC(NULL);
}
pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_BEGIN_TRANSITION_CALLBACK, 3, 1);
// Store the MD for StubHelpers::ProfilerLeaveCallback().
DWORD dwMethodDescLocalNum = pcsEmit->NewLocal(ELEMENT_TYPE_I);
pcsEmit->EmitSTLOC(dwMethodDescLocalNum);
return dwMethodDescLocalNum;
}
void NDirectStubLinker::EmitProfilerEndTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags, DWORD dwMethodDescLocalNum)
{
STANDARD_VM_CONTRACT;
pcsEmit->EmitLDLOC(dwMethodDescLocalNum);
if (SF_IsReverseStub(dwStubFlags))
{
// we use a null pThread to indicate reverse interop
pcsEmit->EmitLDC(NULL);
}
else
{
pcsEmit->EmitLDLOC(GetThreadLocalNum());
}
pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_END_TRANSITION_CALLBACK, 2, 0);
}
#endif // PROFILING_SUPPPORTED
#ifdef VERIFY_HEAP
void NDirectStubLinker::EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNum, bool fIsByref, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
pcsEmit->EmitLDLOC(dwLocalNum);
if (SF_IsDelegateStub(dwStubFlags))
{
pcsEmit->EmitLoadNullPtr();
pcsEmit->EmitLoadThis();
}
else if (SF_IsCALLIStub(dwStubFlags))
{
pcsEmit->EmitLoadNullPtr();
pcsEmit->EmitLDNULL();
}
else
{
// P/Invoke, CLR->COM
EmitLoadStubContext(pcsEmit, dwStubFlags);
pcsEmit->EmitLDNULL();
}
if (fIsByref)
{
// StubHelpers.ValidateByref(byref, pMD, pThis)
pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 3, 0);
}
else
{
// StubHelpers.ValidateObject(obj, pMD, pThis)
pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 3, 0);
}
}
void NDirectStubLinker::EmitObjectValidation(ILCodeStream* pcsEmit, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
// generate validation callouts for pinned locals
CQuickBytes qbLocalSig;
DWORD cbSig = GetLocalSigSize();
qbLocalSig.AllocThrows(cbSig);
PCOR_SIGNATURE pSig = (PCOR_SIGNATURE)qbLocalSig.Ptr();
GetLocalSig(pSig, cbSig);
SigPointer ptr(pSig, cbSig);
IfFailThrow(ptr.GetData(NULL)); // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
ULONG numLocals;
IfFailThrow(ptr.GetData(&numLocals));
for (ULONG i = 0; i < numLocals; i++)
{
BYTE modifier;
IfFailThrow(ptr.PeekByte(&modifier));
if (modifier == ELEMENT_TYPE_PINNED)
{
IfFailThrow(ptr.GetByte(NULL));
IfFailThrow(ptr.PeekByte(&modifier));
EmitValidateLocal(pcsEmit, i, (modifier == ELEMENT_TYPE_BYREF), dwStubFlags);
}
IfFailThrow(ptr.SkipExactlyOne());
}
}
#endif // VERIFY_HEAP
// Loads the 'secret argument' passed to the stub.
void NDirectStubLinker::EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
CONSISTENCY_CHECK(!SF_IsForwardDelegateStub(dwStubFlags));
CONSISTENCY_CHECK(!SF_IsFieldGetterStub(dwStubFlags) && !SF_IsFieldSetterStub(dwStubFlags));
#ifdef FEATURE_COMINTEROP
if (SF_IsWinRTDelegateStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags))
{
// we have the delegate 'this' but we need the EEImpl/Instantiated 'Invoke' MD pointer
// (Delegate.GetInvokeMethod does not return exact instantiated MD so we call our own helper)
pcsEmit->EmitLoadThis();
pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_INVOKE_METHOD, 1, 1);
}
else
#endif // FEATURE_COMINTEROP
{
// get the secret argument via intrinsic
pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
}
}
#ifdef MDA_SUPPORTED
void NDirectStubLinker::EmitCallGcCollectForMDA(ILCodeStream *pcsEmit, DWORD dwStubFlags)
{
STANDARD_VM_CONTRACT;
ILCodeLabel *pSkipGcLabel = NULL;
if (SF_IsForwardPInvokeStub(dwStubFlags) &&
!SF_IsDelegateStub(dwStubFlags) &&
!SF_IsCALLIStub(dwStubFlags))
{
// don't call GC if this is a QCall
EmitLoadStubContext(pcsEmit, dwStubFlags);
pcsEmit->EmitCALL(METHOD__STUBHELPERS__IS_QCALL, 1, 1);
pSkipGcLabel = pcsEmit->NewCodeLabel();
pcsEmit->EmitBRTRUE(pSkipGcLabel);
}
pcsEmit->EmitCALL(METHOD__STUBHELPERS__TRIGGER_GC_FOR_MDA, 0, 0);
if (pSkipGcLabel != NULL)
{
pcsEmit->EmitLabel(pSkipGcLabel);
}
}
#endif // MDA_SUPPORTED
#ifdef FEATURE_COMINTEROP
class DispatchStubState : public StubState // For CLR-to-COM late-bound/eventing calls
{
public:
DispatchStubState()
: m_dwStubFlags(0),
m_lateBoundFlags(0)
{
WRAPPER_NO_CONTRACT;
}
void SetLastError(BOOL fSetLastError)
{
LIMITED_METHOD_CONTRACT;
CONSISTENCY_CHECK(!fSetLastError);
}
void BeginEmit(DWORD dwStubFlags)
{
LIMITED_METHOD_CONTRACT;
CONSISTENCY_CHECK(SF_IsCOMStub(dwStubFlags));
m_dwStubFlags = dwStubFlags;
}
void MarshalReturn(MarshalInfo* pInfo, int argOffset)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pInfo));
}
CONTRACTL_END;
}
void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pInfo));
}
CONTRACTL_END;
if (SF_IsCOMLateBoundStub(m_dwStubFlags) && pInfo->GetDispWrapperType() != 0)
{
m_lateBoundFlags |= ComPlusCallInfo::kRequiresArgumentWrapping;
}
}
void MarshalLCID(int argIdx)
{
LIMITED_METHOD_CONTRACT;
}
#ifdef FEATURE_COMINTEROP
void MarshalHiddenLengthArgument(MarshalInfo *, BOOL)
{
LIMITED_METHOD_CONTRACT;
}
void MarshalFactoryReturn()
{
LIMITED_METHOD_CONTRACT;
UNREACHABLE();
}
#endif // FEATURE_COMINTEROP
void EmitInvokeTarget(MethodDesc *pStubMD)
{
LIMITED_METHOD_CONTRACT;
UNREACHABLE_MSG("Should never come to DispatchStubState::EmitInvokeTarget");
}
void FinishEmit(MethodDesc *pMD)
{
STANDARD_VM_CONTRACT;
// set flags directly on the interop MD
_ASSERTE(pMD->IsComPlusCall());
((ComPlusCallMethodDesc *)pMD)->SetLateBoundFlags(m_lateBoundFlags);
}
protected:
DWORD m_dwStubFlags;
BYTE m_lateBoundFlags; // ComPlusCallMethodDesc::Flags
};
#endif // FEATURE_COMINTEROP
void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
// initialize data members
m_wFlags = 0;
m_pModule = pModule;
m_callConv = (CorPinvokeMap)0;
SetBestFitMapping (TRUE);
SetThrowOnUnmappableChar (FALSE);
SetLinkFlags (nlfNone);
SetCharSet (nltAnsi);
m_error = 0;
// assembly/type level m_bestFit & m_bThrowOnUnmappableChar
BOOL bBestFit;
BOOL bThrowOnUnmappableChar;
if (pMT != NULL)
{
EEClass::GetBestFitMapping(pMT, &bBestFit, &bThrowOnUnmappableChar);
}
else
{
ReadBestFitCustomAttribute(m_pModule->GetMDImport(), mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
}
SetBestFitMapping (bBestFit);
SetThrowOnUnmappableChar (bThrowOnUnmappableChar);
}
void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
PreInit(pMD->GetModule(), pMD->GetMethodTable());
SetIsStatic (pMD->IsStatic());
m_sig = pMD->GetSignature();
if (pMD->IsEEImpl())
{
CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
SetIsDelegateInterop(TRUE);
}
}
PInvokeStaticSigInfo::PInvokeStaticSigInfo(
MethodDesc* pMD, LPCUTF8 *pLibName, LPCUTF8 *pEntryPointName, ThrowOnError throwOnError)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
DllImportInit(pMD, pLibName, pEntryPointName);
if (throwOnError)
ReportErrors();
}
PInvokeStaticSigInfo::PInvokeStaticSigInfo(MethodDesc* pMD, ThrowOnError throwOnError)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pMD));
}
CONTRACTL_END;
HRESULT hr = S_OK;
MethodTable * pMT = pMD->GetMethodTable();
if (!pMT->IsDelegate())
{
DllImportInit(pMD, NULL, NULL);
return;
}
// initialize data members to defaults
PreInit(pMD);
// System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
BYTE* pData = NULL;
LONG cData = 0;
CorPinvokeMap callConv = (CorPinvokeMap)0;
HRESULT hRESULT = pMT->GetMDImport()->GetCustomAttributeByName(
pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, (const VOID **)(&pData), (ULONG *)&cData);
IfFailThrow(hRESULT);
if(cData != 0)
{
CustomAttributeParser ca(pData, cData);
CaArg args[1];
args[0].InitEnum(SERIALIZATION_TYPE_I4, (ULONG)m_callConv);
IfFailGo(ParseKnownCaArgs(ca, args, lengthof(args)));
enum UnmanagedFunctionPointerNamedArgs
{
MDA_CharSet,
MDA_BestFitMapping,
MDA_ThrowOnUnmappableChar,
MDA_SetLastError,
MDA_PreserveSig,
MDA_Last,
};
CaNamedArg namedArgs[MDA_Last];
namedArgs[MDA_CharSet].InitI4FieldEnum("CharSet", "System.Runtime.InteropServices.CharSet", (ULONG)GetCharSet());
namedArgs[MDA_BestFitMapping].InitBoolField("BestFitMapping", (ULONG)GetBestFitMapping());
namedArgs[MDA_ThrowOnUnmappableChar].InitBoolField("ThrowOnUnmappableChar", (ULONG)GetThrowOnUnmappableChar());
namedArgs[MDA_SetLastError].InitBoolField("SetLastError", 0);
namedArgs[MDA_PreserveSig].InitBoolField("PreserveSig", 0);
IfFailGo(ParseKnownCaNamedArgs(ca, namedArgs, lengthof(namedArgs)));
callConv = (CorPinvokeMap)(args[0].val.u4 << 8);
CorNativeLinkType nlt = (CorNativeLinkType)0;
// XXX Tue 07/19/2005
// Keep in sync with the handling of CorPInvokeMap in
// PInvokeStaticSigInfo::DllImportInit.
switch( namedArgs[MDA_CharSet].val.u4 )
{
case 0:
case nltAnsi:
nlt = nltAnsi; break;
case nltUnicode:
case nltAuto: // Since Win9x isn't supported anymore, nltAuto always represents unicode strings.
nlt = nltUnicode; break;
default:
hr = E_FAIL; goto ErrExit;
}
SetCharSet ( nlt );
SetBestFitMapping (namedArgs[MDA_BestFitMapping].val.u1);
SetThrowOnUnmappableChar (namedArgs[MDA_ThrowOnUnmappableChar].val.u1);
if (namedArgs[MDA_SetLastError].val.u1)
SetLinkFlags ((CorNativeLinkFlags)(nlfLastError | GetLinkFlags()));
if (namedArgs[MDA_PreserveSig].val.u1)
SetLinkFlags ((CorNativeLinkFlags)(nlfNoMangle | GetLinkFlags()));
}
ErrExit:
if (hr != S_OK)
SetError(IDS_EE_NDIRECT_BADNATL);
InitCallConv(callConv, pMD->IsVarArg());
if (throwOnError)
ReportErrors();
}
PInvokeStaticSigInfo::PInvokeStaticSigInfo(
Signature sig, Module* pModule, ThrowOnError throwOnError)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pModule));
}
CONTRACTL_END;
PreInit(pModule, NULL);
m_sig = sig;
SetIsStatic (!(MetaSig::GetCallingConvention(pModule, sig) & IMAGE_CEE_CS_CALLCONV_HASTHIS));
InitCallConv((CorPinvokeMap)0, FALSE);
if (throwOnError)
ReportErrors();
}
void PInvokeStaticSigInfo::DllImportInit(MethodDesc* pMD, LPCUTF8 *ppLibName, LPCUTF8 *ppEntryPointName)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pMD));
// These preconditions to prevent multithreaded regression
// where pMD->ndirect.m_szLibName was passed in directly, cleared
// by this API, then accessed on another thread before being reset here.
PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
}
CONTRACTL_END;
// initialize data members to defaults
PreInit(pMD);
// System.Runtime.InteropServices.DllImportAttribute
IMDInternalImport *pInternalImport = pMD->GetMDImport();
CorPinvokeMap mappingFlags = pmMaxValue;
mdModuleRef modref = mdModuleRefNil;
if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
{
InitCallConv((CorPinvokeMap)0, pMD->IsVarArg());
return;
}
// out parameter pEntryPointName
if (ppEntryPointName && *ppEntryPointName == NULL)
*ppEntryPointName = pMD->GetName();
// out parameter pLibName
if (ppLibName != NULL)
{
if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
{
SetError(IDS_CLASSLOAD_BADFORMAT);
return;
}
}
// m_callConv
InitCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask), pMD->IsVarArg());
// m_bestFit
CorPinvokeMap bestFitMask = (CorPinvokeMap)(mappingFlags & pmBestFitMask);
if (bestFitMask == pmBestFitEnabled)
SetBestFitMapping (TRUE);
else if (bestFitMask == pmBestFitDisabled)
SetBestFitMapping (FALSE);
// m_bThrowOnUnmappableChar
CorPinvokeMap unmappableMask = (CorPinvokeMap)(mappingFlags & pmThrowOnUnmappableCharMask);
if (unmappableMask == pmThrowOnUnmappableCharEnabled)
SetThrowOnUnmappableChar (TRUE);
else if (unmappableMask == pmThrowOnUnmappableCharDisabled)
SetThrowOnUnmappableChar (FALSE);
// inkFlags : CorPinvoke -> CorNativeLinkFlags
if (mappingFlags & pmSupportsLastError)
SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfLastError));
if (mappingFlags & pmNoMangle)
SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
// XXX Tue 07/19/2005
// Keep in sync with the handling of CorNativeLinkType in
// PInvokeStaticSigInfo::PInvokeStaticSigInfo.
// charset : CorPinvoke -> CorNativeLinkType
CorPinvokeMap charSetMask = (CorPinvokeMap)(mappingFlags & (pmCharSetNotSpec | pmCharSetAnsi | pmCharSetUnicode | pmCharSetAuto));
if (charSetMask == pmCharSetNotSpec || charSetMask == pmCharSetAnsi)
{
SetCharSet (nltAnsi);
}
else if (charSetMask == pmCharSetUnicode || charSetMask == pmCharSetAuto)
{
// Since Win9x isn't supported anymore, pmCharSetAuto always represents unicode strings.
SetCharSet (nltUnicode);
}
else
{
SetError(IDS_EE_NDIRECT_BADNATL);
}
}
inline CorPinvokeMap GetDefaultCallConv(BOOL bIsVarArg)
{
#ifdef PLATFORM_UNIX
return pmCallConvCdecl;
#else // PLATFORM_UNIX
return bIsVarArg ? pmCallConvCdecl : pmCallConvStdcall;
#endif // !PLATFORM_UNIX
}
void PInvokeStaticSigInfo::InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END
// Convert WinAPI methods to either StdCall or CDecl based on if they are varargs or not.
if (callConv == pmCallConvWinapi)
callConv = GetDefaultCallConv(bIsVarArg);
CorPinvokeMap sigCallConv = (CorPinvokeMap)0;
BOOL fSuccess = MetaSig::GetUnmanagedCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
if (!fSuccess)
{
SetError(IDS_EE_NDIRECT_BADNATL); //Bad metadata format
}
// Do the same WinAPI to StdCall or CDecl for the signature calling convention as well. We need
// to do this before we check to make sure the PInvoke map calling convention and the
// signature calling convention match for compatibility reasons.
if (sigCallConv == pmCallConvWinapi)
sigCallConv = GetDefaultCallConv(bIsVarArg);
if (callConv != 0 && sigCallConv != 0 && callConv != sigCallConv)
SetError(IDS_EE_NDIRECT_BADNATL_CALLCONV);
if (callConv == 0 && sigCallConv == 0)
m_callConv = GetDefaultCallConv(bIsVarArg);
else if (callConv != 0)
m_callConv = callConv;
else
m_callConv = sigCallConv;
if (bIsVarArg && m_callConv != pmCallConvCdecl)
SetError(IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV);
}
void PInvokeStaticSigInfo::ReportErrors()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
if (m_error != 0)
COMPlusThrow(kTypeLoadException, m_error);
}
//---------------------------------------------------------
// Does a class or method have a NAT_L CustomAttribute?
//
// S_OK = yes
// S_FALSE = no
// FAILED = unknown because something failed.
//---------------------------------------------------------
/*static*/
HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pInternalImport));
PRECONDITION(TypeFromToken(token) == mdtMethodDef);
}
CONTRACTL_END;
// Check method flags first before trying to find the custom value
if (!IsReallyMdPinvokeImpl(dwMemberAttrs))
return S_FALSE;
DWORD mappingFlags;
LPCSTR pszImportName;
mdModuleRef modref;
if (SUCCEEDED(pInternalImport->GetPinvokeMap(token, &mappingFlags, &pszImportName, &modref)))
return S_OK;
return S_FALSE;
}
// Either MD or signature & module must be given.
/*static*/
BOOL NDirect::MarshalingRequired(MethodDesc *pMD, PCCOR_SIGNATURE pSig /*= NULL*/, Module *pModule /*= NULL*/)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(pMD != NULL || (pSig != NULL && pModule != NULL));
}
CONTRACTL_END;
// As a by-product, when returning FALSE we will also set the native stack size to the MD if it's
// an NDirectMethodDesc. This number is needed to link the P/Invoke (it determines the @n entry
// point name suffix and affects alignment thunk generation on the Mac). If this method returns
// TRUE, the stack size will be set when building the marshaling IL stub.
DWORD dwStackSize = 0;
CorPinvokeMap callConv = (CorPinvokeMap)0;
if (pMD != NULL)
{
if (pMD->IsNDirect() || pMD->IsComPlusCall())
{
// HRESULT swapping is handled by stub
if ((pMD->GetImplAttrs() & miPreserveSig) == 0)
return TRUE;
}
// SetLastError is handled by stub
PInvokeStaticSigInfo sigInfo(pMD);
if (sigInfo.GetLinkFlags() & nlfLastError)
return TRUE;
// LCID argument is handled by stub
if (GetLCIDParameterIndex(pMD) != -1)
return TRUE;
// making sure that cctor has run may be handled by stub
if (pMD->IsNDirect() && ((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
return TRUE;
callConv = sigInfo.GetCallConv();
}
if (pSig == NULL)
{
PREFIX_ASSUME(pMD != NULL);
pSig = pMD->GetSig();
pModule = pMD->GetModule();
}
// Check to make certain that the signature only contains types that marshal trivially
SigPointer ptr(pSig);
IfFailThrow(ptr.GetCallingConvInfo(NULL));
ULONG numArgs;
IfFailThrow(ptr.GetData(&numArgs));
numArgs++; // +1 for return type
// We'll need to parse parameter native types
mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef));
IMDInternalImport *pMDImport = pModule->GetMDImport();
SigTypeContext emptyTypeContext;
mdMethodDef methodToken = mdMethodDefNil;
if (pMD != NULL)
{
methodToken = pMD->GetMemberDef();
}
CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
for (ULONG i = 0; i < numArgs; i++)
{
SigPointer arg = ptr;
CorElementType type;
IfFailThrow(arg.PeekElemType(&type));
switch (type)
{
case ELEMENT_TYPE_PTR:
{
IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR
IfFailThrow(arg.PeekElemType(&type));
if (type == ELEMENT_TYPE_VALUETYPE)
{
if ((arg.HasCustomModifier(pModule,
"Microsoft.VisualC.NeedsCopyConstructorModifier",
ELEMENT_TYPE_CMOD_REQD)) ||
(arg.HasCustomModifier(pModule,
"System.Runtime.CompilerServices.IsCopyConstructed",
ELEMENT_TYPE_CMOD_REQD)))
{
return TRUE;
}
}
if (i > 0) dwStackSize += sizeof(SLOT);
break;
}
case ELEMENT_TYPE_INTERNAL:
// this check is not functional in DAC and provides no security against a malicious dump
// the DAC is prepared to receive an invalid type handle
#ifndef DACCESS_COMPILE
if (pModule->IsSigInIL(arg.GetPtr()))
THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule);
#endif
/* Fall thru */
case ELEMENT_TYPE_VALUETYPE:
{
TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext);
// JIT can handle internal blittable value types
if (!hndArgType.IsBlittable() && !hndArgType.IsEnum())
{
return TRUE;
}
// return value is fine as long as it can be normalized to an integer
if (i == 0)
{
CorElementType normalizedType = hndArgType.GetInternalCorElementType();
if (normalizedType == ELEMENT_TYPE_VALUETYPE)
{
// it is a structure even after normalization
return TRUE;
}
}
else
{
dwStackSize += StackElemSize(hndArgType.GetSize());
}
break;
}
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_CHAR:
{
// Bool requires marshaling
// Char may require marshaling (MARSHAL_TYPE_ANSICHAR)
return TRUE;
}
default:
{
if (CorTypeInfo::IsPrimitiveType(type) || type == ELEMENT_TYPE_FNPTR)
{
if (i > 0) dwStackSize += StackElemSize(CorTypeInfo::Size(type));
}
else
{
// other non-primitive type - requires marshaling
return TRUE;
}
}
}
// check for explicit MarshalAs
NativeTypeParamInfo paramInfo;
if (pParamTokenArray[i] != mdParamDefNil)
{
if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, &paramInfo) ||
paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT)
{
// Presence of MarshalAs does not necessitate marshaling (it could as well be the default
// for the type), but it's a good enough heuristic. We definitely don't want to duplicate
// the logic from code:MarshalInfo.MarshalInfo here.
return TRUE;
}
}
IfFailThrow(ptr.SkipExactlyOne());
}
if (!FitsInU2(dwStackSize))
return TRUE;
// do not set the stack size for varargs - the number is call site specific
if (pMD != NULL && !pMD->IsVarArg())
{
if (pMD->IsNDirect())
{
((NDirectMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize), callConv);
}
#ifdef FEATURE_COMINTEROP
else if (pMD->IsComPlusCall())
{
// calling convention is always stdcall
((ComPlusCallMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize));
}
#endif // FEATURE_COMINTEROP
}
return FALSE;
}
// factorization of CreateNDirectStubWorker
static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig,
mdParamDef* params,
CorNativeLinkType nlType,
CorNativeLinkFlags nlFlags,
UINT argidx, // this is used for reverse pinvoke hresult swapping
StubState* pss,
BOOL fThisCall,
int argOffset,
DWORD dwStubFlags,
MethodDesc *pMD,
UINT& nativeStackOffset,
bool& fStubNeedsCOM,
int nativeArgIndex
DEBUG_ARG(LPCUTF8 pDebugName)
DEBUG_ARG(LPCUTF8 pDebugClassName)
)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(params));
PRECONDITION(CheckPointer(pss));
PRECONDITION(CheckPointer(pMD, NULL_OK));
}
CONTRACTL_END;
MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
MarshalInfo::MarshalScenario ms;
#ifdef FEATURE_COMINTEROP
if (SF_IsCOMStub(dwStubFlags))
{
if (SF_IsWinRTStub(dwStubFlags))
ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
else
ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
}
else
#endif // FEATURE_COMINTEROP
{
ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
}
#ifdef FEATURE_COMINTEROP
if (SF_IsWinRTCtorStub(dwStubFlags))
{
_ASSERTE(msig.GetReturnType() == ELEMENT_TYPE_VOID);
_ASSERTE(SF_IsHRESULTSwapping(dwStubFlags));
pss->MarshalFactoryReturn();
nativeStackOffset += sizeof(LPVOID);
if (SF_IsWinRTCompositionStub(dwStubFlags))
{
nativeStackOffset += 2 * sizeof(LPVOID);
}
}
else
#endif // FEATURE_COMINTEROP
if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
{
MarshalInfo returnInfo(msig.GetModule(),
msig.GetReturnProps(),
msig.GetSigTypeContext(),
params[0],
ms,
nlType,
nlFlags,
FALSE,
argidx,
msig.NumFixedArgs(),
SF_IsBestFit(dwStubFlags),
SF_IsThrowOnUnmappableChar(dwStubFlags),
TRUE,
pMD,
TRUE
DEBUG_ARG(pDebugName)
DEBUG_ARG(pDebugClassName)
DEBUG_ARG(0)
);
marshalType = returnInfo.GetMarshalType();
fStubNeedsCOM |= returnInfo.MarshalerRequiresCOM();
#ifdef FEATURE_COMINTEROP
if (marshalType == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
{
// Hidden length arrays are only valid with HRESULT swapping
if (!SF_IsHRESULTSwapping(dwStubFlags))
{
COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
}
// We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
returnInfo.SetHiddenLengthParamIndex(static_cast<UINT16>(nativeArgIndex));
// Inject the hidden argument so that it winds up at the end of the method signature
pss->MarshalHiddenLengthArgument(&returnInfo, TRUE);
nativeStackOffset += returnInfo.GetHiddenLengthParamStackSize();
if (SF_IsReverseStub(dwStubFlags))
{
++argOffset;
}
}
if (SF_IsCOMStub(dwStubFlags))
{
if (marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS ||
marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS ||
marshalType == MarshalInfo::MARSHAL_TYPE_GUID ||
marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL)
{
#ifndef _TARGET_X86_
// We cannot optimize marshalType to MARSHAL_TYPE_GENERIC_* because the JIT works with exact types
// and would refuse to compile the stub if it implicitly converted between scalars and value types (also see
// code:MarshalInfo.MarhalInfo where we do the optimization on x86). We want to throw only if the structure
// is too big to be returned in registers.
if (marshalType != MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS ||
IsUnmanagedValueTypeReturnedByRef(returnInfo.GetNativeArgSize()))
#endif // _TARGET_X86_
{
if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
{
// Note that this limitation is very likely not needed anymore and could be lifted if we care.
COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
}
}
pss->MarshalReturn(&returnInfo, argOffset);
}
else
{
// We don't support native methods that return VARIANTs directly.
if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT)
{
if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
{
COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
}
}
pss->MarshalReturn(&returnInfo, argOffset);
}
}
else
#endif // FEATURE_COMINTEROP
{
if (marshalType > MarshalInfo::MARSHAL_TYPE_DOUBLE && IsUnsupportedValueTypeReturn(msig))
{
if (marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS
|| marshalType == MarshalInfo::MARSHAL_TYPE_GUID
|| marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL
#ifdef FEATURE_COMINTEROP
|| marshalType == MarshalInfo::MARSHAL_TYPE_DATETIME
#endif // FEATURE_COMINTEROP
)
{
if (SF_IsHRESULTSwapping(dwStubFlags))
{
// V1 restriction: we could implement this but it's late in the game to do so.
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
}
}
else if (marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF)
{
COMPlusThrow(kMarshalDirectiveException, IDS_EE_BADMARSHAL_HANDLEREFRESTRICTION);
}
else
{
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
}
}
#ifdef FEATURE_COMINTEROP
if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT && !SF_IsHRESULTSwapping(dwStubFlags))
{
// No support for returning variants. This is a V1 restriction, due to the late date,
// don't want to add the special-case code to support this in light of low demand.
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NOVARIANTRETURN);
}
#endif // FEATURE_COMINTEROP
pss->MarshalReturn(&returnInfo, argOffset);
}
}
return marshalType;
}
static inline UINT GetStackOffsetFromStackSize(UINT stackSize, bool fThisCall)
{
LIMITED_METHOD_CONTRACT;
#ifdef _TARGET_X86_
if (fThisCall)
{
// -1 means that the argument is not on the stack
return (stackSize >= sizeof(SLOT) ? (stackSize - sizeof(SLOT)) : (UINT)-1);
}
#endif // _TARGET_X86_
return stackSize;
}
#ifdef FEATURE_COMINTEROP
struct HiddenParameterInfo
{
MarshalInfo *pManagedParam; // Managed parameter which required the hidden parameter
int nativeIndex; // 0 based index into the native method signature where the hidden parameter should be injected
};
// Get the indexes of any hidden length parameters to be marshaled for the method
//
// At return, each value in the ppParamIndexes array is a 0 based index into the native method signature where
// the length parameter for a hidden length array should be passed. The MarshalInfo objects will also be
// updated such that they all have explicit marshaling information.
//
// The caller is responsible for freeing the memory pointed to by ppParamIndexes
void CheckForHiddenParameters(DWORD cParamMarshalInfo,
__in_ecount(cParamMarshalInfo) MarshalInfo *pParamMarshalInfo,
__out DWORD *pcHiddenNativeParameters,
__out HiddenParameterInfo **ppHiddenNativeParameters)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pParamMarshalInfo));
PRECONDITION(CheckPointer(pcHiddenNativeParameters));
PRECONDITION(CheckPointer(ppHiddenNativeParameters));
}
CONTRACTL_END;
NewArrayHolder<HiddenParameterInfo> hiddenParamInfo(new HiddenParameterInfo[cParamMarshalInfo]);
DWORD foundInfoCount = 0;
for (DWORD iParam = 0; iParam < cParamMarshalInfo; ++iParam)
{
// Look for hidden length arrays, which all require additional parameters to be added
if (pParamMarshalInfo[iParam].GetMarshalType() == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
{
DWORD currentNativeIndex = iParam + foundInfoCount;
// The location of the length parameter is implicitly just before the array pointer.
// We'll give it our current index, and bumping the found count will push us back a slot.
// We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
pParamMarshalInfo[iParam].SetHiddenLengthParamIndex(static_cast<UINT16>(currentNativeIndex));
hiddenParamInfo[foundInfoCount].nativeIndex = pParamMarshalInfo[iParam].HiddenLengthParamIndex();
hiddenParamInfo[foundInfoCount].pManagedParam = &(pParamMarshalInfo[iParam]);
++foundInfoCount;
}
}
*pcHiddenNativeParameters = foundInfoCount;
*ppHiddenNativeParameters = hiddenParamInfo.Extract();
}
bool IsHiddenParameter(int nativeArgIndex,
DWORD cHiddenParameters,
__in_ecount(cHiddenParameters) HiddenParameterInfo *pHiddenParameters,
__out HiddenParameterInfo **ppHiddenParameterInfo)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(cHiddenParameters == 0 || CheckPointer(pHiddenParameters));
PRECONDITION(CheckPointer(ppHiddenParameterInfo));
}
CONTRACTL_END;
*ppHiddenParameterInfo = NULL;
for (DWORD i = 0; i < cHiddenParameters; ++i)
{
_ASSERTE(pHiddenParameters[i].nativeIndex != -1);
if (pHiddenParameters[i].nativeIndex == nativeArgIndex)
{
*ppHiddenParameterInfo = &(pHiddenParameters[i]);
return true;
}
}
return false;
}
#endif // FEATURE_COMINTEROP
//---------------------------------------------------------
// Creates a new stub for a N/Direct call. Return refcount is 1.
// Note that this function may now throw if it fails to create
// a stub.
//---------------------------------------------------------
static void CreateNDirectStubWorker(StubState* pss,
StubSigDesc* pSigDesc,
CorNativeLinkType nlType,
CorNativeLinkFlags nlFlags,
CorPinvokeMap unmgdCallConv,
DWORD dwStubFlags,
MethodDesc *pMD,
mdParamDef* pParamTokenArray,
int iLCIDArg
)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pss));
PRECONDITION(CheckPointer(pSigDesc));
PRECONDITION(CheckPointer(pMD, NULL_OK));
PRECONDITION(!pMD || pMD->IsILStub() || (0 != pMD->GetMethodTable()->IsDelegate()) == SF_IsDelegateStub(dwStubFlags));
}
CONTRACTL_END;
SF_ConsistencyCheck(dwStubFlags);
#ifdef _DEBUG
if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
#endif // _DEBUG
Stub* pstub = NULL;
if (SF_IsCOMStub(dwStubFlags))
{
_ASSERTE(0 == nlType);
_ASSERTE(0 == nlFlags);
_ASSERTE(0 == unmgdCallConv);
}
else
{
_ASSERTE(nlType == nltAnsi || nlType == nltUnicode);
}
Module *pModule = pSigDesc->m_pModule;
//
// Set up signature walking objects.
//
MetaSig msig(pSigDesc->m_sig,
pModule,
&pSigDesc->m_typeContext);
if (SF_IsVarArgStub(dwStubFlags))
msig.SetTreatAsVarArg();
bool fThisCall = (unmgdCallConv == pmCallConvThiscall);
pss->SetLastError(nlFlags & nlfLastError);
// This has been in the product since forward P/Invoke via delegates was
// introduced. It's wrong, but please keep it for backward compatibility.
if (SF_IsDelegateStub(dwStubFlags))
pss->SetLastError(TRUE);
pss->BeginEmit(dwStubFlags);
if (-1 != iLCIDArg)
{
// LCID is not supported on WinRT
_ASSERTE(!SF_IsWinRTStub(dwStubFlags));
// The code to handle the LCID will call MarshalLCID before calling MarshalArgument
// on the argument the LCID should go after. So we just bump up the index here.
iLCIDArg++;
}
int numArgs = msig.NumFixedArgs();
// thiscall must have at least one parameter (the "this")
if (fThisCall && numArgs == 0)
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
//
// Now, emit the IL.
//
int argOffset = 0;
MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
//
// Marshal the return value.
//
UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? sizeof(SLOT) : 0);
bool fHasCopyCtorArgs = false;
bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags);
// Normally we would like this to be false so that we use the correct signature
// in the IL_STUB, (i.e if it returns a value class then the signature will use that)
// When this bool is true we change the return type to void and explicitly add a
// return buffer argument as the first argument.
BOOL fMarshalReturnValueFirst = false;
// We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping!
//
if (!SF_IsHRESULTSwapping(dwStubFlags))
{
#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
// JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer.
// Therefore instead we change the signature of calli to return void and make the return buffer as first
// argument. This matches the ABI i.e. return buffer is passed as first arg. So native target will get the
// return buffer in correct register.
// The return structure secret arg comes first, however byvalue return is processed at
// the end because it could be the HRESULT-swapped argument which always comes last.
#ifdef UNIX_X86_ABI
// For functions with value type class, managed and unmanaged calling convention differ
fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig);
#else // UNIX_X86_ABI
fMarshalReturnValueFirst = HasRetBuffArg(&msig);
#endif // UNIX_X86_ABI
#endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
}
if (fMarshalReturnValueFirst)
{
marshalType = DoMarshalReturnValue(msig,
pParamTokenArray,
nlType,
nlFlags,
0,
pss,
fThisCall,
argOffset,
dwStubFlags,
pMD,
nativeStackSize,
fStubNeedsCOM,
0
DEBUG_ARG(pSigDesc->m_pDebugName)
DEBUG_ARG(pSigDesc->m_pDebugClassName)
);
if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE ||
marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY ||
marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET ||
marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF ||
marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
#ifdef FEATURE_COMINTEROP
|| marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
#endif // FEATURE_COMINTEROP
)
{
// These are special non-blittable types returned by-ref in managed,
// but marshaled as primitive values returned by-value in unmanaged.
}
else
{
// This is an ordinary value type - see if it is returned by-ref.
MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
{
nativeStackSize += sizeof(LPVOID);
}
}
}
//
// Marshal the arguments
//
MarshalInfo::MarshalScenario ms;
#ifdef FEATURE_COMINTEROP
if (SF_IsCOMStub(dwStubFlags))
{
if (SF_IsWinRTStub(dwStubFlags))
ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
else
ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
}
else
#endif // FEATURE_COMINTEROP
{
ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
}
// Build up marshaling information for each of the method's parameters
SIZE_T cbParamMarshalInfo;
if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numArgs, cbParamMarshalInfo))
{
COMPlusThrowHR(COR_E_OVERFLOW);
}
NewArrayHolder<BYTE> pbParamMarshalInfo(new BYTE[cbParamMarshalInfo]);
MarshalInfo *pParamMarshalInfo = reinterpret_cast<MarshalInfo *>(pbParamMarshalInfo.GetValue());
MetaSig paramInfoMSig(msig);
for (int i = 0; i < numArgs; ++i)
{
paramInfoMSig.NextArg();
new(&(pParamMarshalInfo[i])) MarshalInfo(paramInfoMSig.GetModule(),
paramInfoMSig.GetArgProps(),
paramInfoMSig.GetSigTypeContext(),
pParamTokenArray[i + 1],
ms,
nlType,
nlFlags,
TRUE,
i + 1,
numArgs,
SF_IsBestFit(dwStubFlags),
SF_IsThrowOnUnmappableChar(dwStubFlags),
TRUE,
pMD,
TRUE
DEBUG_ARG(pSigDesc->m_pDebugName)
DEBUG_ARG(pSigDesc->m_pDebugClassName)
DEBUG_ARG(i + 1));
}
#ifdef FEATURE_COMINTEROP
// Check to see if we need to inject any additional hidden parameters
DWORD cHiddenNativeParameters;
NewArrayHolder<HiddenParameterInfo> pHiddenNativeParameters;
CheckForHiddenParameters(numArgs, pParamMarshalInfo, &cHiddenNativeParameters, &pHiddenNativeParameters);
// Hidden parameters and LCID do not mix
_ASSERTE(!(cHiddenNativeParameters > 0 && iLCIDArg != -1));
#endif // FEATURE_COMINTEROP
// Marshal the parameters
int argidx = 1;
int nativeArgIndex = 0;
while (argidx <= numArgs)
{
#ifdef FEATURE_COMINTEROP
HiddenParameterInfo *pHiddenParameter;
// Check to see if we need to inject a hidden parameter
if (IsHiddenParameter(nativeArgIndex, cHiddenNativeParameters, pHiddenNativeParameters, &pHiddenParameter))
{
pss->MarshalHiddenLengthArgument(pHiddenParameter->pManagedParam, FALSE);
nativeStackSize += pHiddenParameter->pManagedParam->GetHiddenLengthParamStackSize();
if (SF_IsReverseStub(dwStubFlags))
{
++argOffset;
}
}
else
#endif // FEATURE_COMINTEROP
{
//
// Check to see if this is the parameter after which we need to insert the LCID.
//
if (argidx == iLCIDArg)
{
pss->MarshalLCID(argidx);
nativeStackSize += sizeof(LPVOID);
if (SF_IsReverseStub(dwStubFlags))
argOffset++;
}
msig.NextArg();
MarshalInfo &info = pParamMarshalInfo[argidx - 1];
#ifdef FEATURE_COMINTEROP
// For the hidden-length array, length parameters must occur before the parameter containing the array pointer
_ASSERTE(info.GetMarshalType() != MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY || nativeArgIndex > info.HiddenLengthParamIndex());
#endif // FEATURE_COMINTEROP
pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
nativeStackSize += info.GetNativeArgSize();
fStubNeedsCOM |= info.MarshalerRequiresCOM();
if (fThisCall && argidx == 1)
{
// make sure that the first parameter is enregisterable
if (info.GetNativeArgSize() > sizeof(SLOT))
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
}
argidx++;
}
++nativeArgIndex;
}
// Check to see if this is the parameter after which we need to insert the LCID.
if (argidx == iLCIDArg)
{