1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
19 #include "dllimport.h"
21 #include "siginfo.hpp"
22 #include "comdelegate.h"
26 #include "comutilnative.h"
28 #include "asmconstants.h"
29 #include "mdaassistants.h"
30 #include "customattribute.h"
31 #include "ilstubcache.h"
32 #include "typeparse.h"
33 #include "sigbuilder.h"
34 #include "sigformat.h"
35 #include "strongnameholders.h"
38 #include <formattype.h>
39 #include "../md/compiler/custattr.h"
41 #ifdef FEATURE_COMINTEROP
42 #include "runtimecallablewrapper.h"
43 #include "clrtocomcall.h"
44 #endif // FEATURE_COMINTEROP
48 #endif // FEATURE_PREJIT
50 #include "eventtrace.h"
51 #include "clr/fs/path.h"
52 using namespace clr::fs;
54 // remove when we get an updated SDK
55 #define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
56 #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
58 void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx)
60 LIMITED_METHOD_CONTRACT;
61 if (pClause->kind == ILStubEHClause::kNone)
67 CorExceptionFlag flags;
68 switch (pClause->kind)
70 case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break;
71 case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break;
73 UNREACHABLE_MSG("unexpected ILStubEHClause kind");
75 _ASSERTE(idx < nClauses);
76 pEHSect->Fat.Clauses[idx].Flags = flags;
77 pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset;
78 pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength;
79 pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset;
80 pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength;
81 pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken;
84 VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo)
86 LIMITED_METHOD_CONTRACT;
87 pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat);
88 pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses);
91 AppendEHClause(nClauses, pEHSect, pOne, &curIdx);
92 AppendEHClause(nClauses, pEHSect, pTwo, &curIdx);
95 StubSigDesc::StubSigDesc(MethodDesc *pMD, PInvokeStaticSigInfo* pSigInfo /*= NULL*/)
106 if (pSigInfo != NULL)
108 m_sig = pSigInfo->GetSignature();
109 m_pModule = pSigInfo->GetModule();
113 _ASSERTE(pMD != NULL);
114 m_sig = pMD->GetSignature();
115 m_pModule = pMD->GetModule(); // Used for token resolution.
120 m_tkMethodDef = pMD->GetMemberDef();
121 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
122 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
126 m_tkMethodDef = mdMethodDefNil;
127 m_pLoaderModule = m_pModule;
130 INDEBUG(InitDebugNames());
133 StubSigDesc::StubSigDesc(MethodDesc *pMD, Signature sig, Module *pModule)
140 PRECONDITION(!sig.IsEmpty());
141 PRECONDITION(pModule != NULL);
151 m_tkMethodDef = pMD->GetMemberDef();
152 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
153 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
157 m_tkMethodDef = mdMethodDefNil;
158 m_pLoaderModule = m_pModule;
161 INDEBUG(InitDebugNames());
164 #ifndef DACCESS_COMPILE
169 virtual void SetLastError(BOOL fSetLastError) = 0;
170 virtual void BeginEmit(DWORD dwStubFlags) = 0;
171 virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) = 0;
172 virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset) = 0;
173 virtual void MarshalLCID(int argIdx) = 0;
175 #ifdef FEATURE_COMINTEROP
176 virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray) = 0;
177 virtual void MarshalFactoryReturn() = 0;
178 #endif // FEATURE_COMINTEROP
180 virtual void EmitInvokeTarget(MethodDesc *pStubMD) = 0;
182 virtual void FinishEmit(MethodDesc* pMD) = 0;
186 LIMITED_METHOD_CONTRACT;
190 class ILStubState : public StubState
196 const Signature &signature,
197 SigTypeContext* pTypeContext,
202 MethodDesc* pTargetMD)
203 : m_slIL(dwStubFlags, pStubModule, signature, pTypeContext, pTargetMD, iLCIDParamIdx, fTargetHasThis, fStubHasThis)
205 STANDARD_VM_CONTRACT;
211 void SetLastError(BOOL fSetLastError)
213 LIMITED_METHOD_CONTRACT;
215 m_fSetLastError = fSetLastError;
218 // We use three stub linkers to generate IL stubs. The pre linker is the main one. It does all the marshaling and
219 // then calls the target method. The post return linker is only used to unmarshal the return value after we return
220 // from the target method. The post linker handles all the unmarshaling for by ref arguments and clean-up. It
221 // also checks if we should throw an exception etc.
223 // Currently, we have two "emittable" ILCodeLabel's. The first one is at the beginning of the pre linker. This
224 // label is used to emit code to declare and initialize clean-up flags. Each argument which requires clean-up
225 // emits one flag. This flag is set only after the marshaling is done, and it is checked before we do any clean-up
228 // The second "emittable" ILCodeLabel is at the beginning of the post linker. It is used to emit code which is
229 // not safe to run in the case of an exception. The rest of the post linker is wrapped in a finally, and it contains
230 // with the necessary clean-up which should be executed in both normal and exception cases.
231 void BeginEmit(DWORD dwStubFlags)
234 m_slIL.Begin(dwStubFlags);
235 m_dwStubFlags = dwStubFlags;
238 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
244 PRECONDITION(CheckPointer(pInfo));
248 pInfo->GenerateReturnIL(&m_slIL, argOffset,
249 SF_IsForwardStub(m_dwStubFlags),
250 SF_IsFieldGetterStub(m_dwStubFlags),
251 SF_IsHRESULTSwapping(m_dwStubFlags));
254 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
259 PRECONDITION(CheckPointer(pInfo));
263 pInfo->GenerateArgumentIL(&m_slIL, argOffset, nativeStackOffset, SF_IsForwardStub(m_dwStubFlags));
266 #ifdef FEATURE_COMINTEROP
267 // Marshal the hidden length parameter for the managed parameter in pInfo
268 virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray)
270 STANDARD_VM_CONTRACT;
272 pInfo->MarshalHiddenLengthArgument(&m_slIL, SF_IsForwardStub(m_dwStubFlags), isForReturnArray);
274 if (SF_IsReverseStub(m_dwStubFlags))
276 // Hidden length arguments appear explicitly in the native signature
277 // however, they are not in the managed signature.
278 m_slIL.AdjustTargetStackDeltaForExtraParam();
282 void MarshalFactoryReturn()
287 PRECONDITION(SF_IsCOMStub(m_dwStubFlags));
288 PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
292 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
293 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
294 ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
295 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
301 // create a local to hold the returned pUnk and initialize to 0 in case the factory fails
302 // and we try to release it during cleanup
303 LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
304 DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
305 pcsSetup->EmitLoadNullPtr();
306 pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
308 DWORD dwInnerIInspectableLocalNum = -1;
309 DWORD dwOuterIInspectableLocalNum = -1;
310 if (SF_IsWinRTCompositionStub(m_dwStubFlags))
312 // Create locals to store the outer and inner IInspectable values and initialize to null
313 // Note that we do this in the setup stream so that we're guaranteed to have a null-initialized
314 // value in the cleanup stream
315 LocalDesc locDescOuterIInspectable(ELEMENT_TYPE_I);
316 dwOuterIInspectableLocalNum = pcsSetup->NewLocal(locDescOuterIInspectable);
317 pcsSetup->EmitLoadNullPtr();
318 pcsSetup->EmitSTLOC(dwOuterIInspectableLocalNum);
319 LocalDesc locDescInnerIInspectable(ELEMENT_TYPE_I);
320 dwInnerIInspectableLocalNum = pcsSetup->NewLocal(locDescInnerIInspectable);
321 pcsSetup->EmitLoadNullPtr();
322 pcsSetup->EmitSTLOC(dwInnerIInspectableLocalNum);
329 // For composition factories, add the two extra params
330 if (SF_IsWinRTCompositionStub(m_dwStubFlags))
332 // Get outer IInspectable. The helper will return NULL if this is the "top-level" constructor,
333 // and the appropriate outer pointer otherwise.
334 pcsDispatch->EmitLoadThis();
335 m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
336 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_OUTER_INSPECTABLE, 2, 1);
337 pcsDispatch->EmitSTLOC(dwOuterIInspectableLocalNum);
339 // load the outer IInspectable (3rd last argument)
340 pcsDispatch->SetStubTargetArgType(ELEMENT_TYPE_I, false);
341 pcsDispatch->EmitLDLOC(dwOuterIInspectableLocalNum);
343 // pass pointer to where inner non-delegating IInspectable should be stored (2nd last argument)
344 LocalDesc locDescInnerPtr(ELEMENT_TYPE_I);
345 locDescInnerPtr.MakeByRef();
346 pcsDispatch->SetStubTargetArgType(&locDescInnerPtr, false);
347 pcsDispatch->EmitLDLOCA(dwInnerIInspectableLocalNum);
350 // pass pointer to the local to the factory method (last argument)
351 locDescFactoryRetVal.MakeByRef();
352 pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
353 pcsDispatch->EmitLDLOCA(dwFactoryRetValLocalNum);
359 // Mark that the factory method has succesfully returned and so cleanup will be necessary after
361 m_slIL.EmitSetArgMarshalIndex(pcsUnmarshal, NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL);
363 // associate the 'this' RCW with one of the returned interface pointers
364 pcsUnmarshal->EmitLoadThis();
366 // now we need to find the right interface pointer to load
367 if (dwInnerIInspectableLocalNum != -1)
369 // We may have a composition scenario
370 ILCodeLabel* pNonCompositionLabel = pcsUnmarshal->NewCodeLabel();
371 ILCodeLabel* pLoadedLabel = pcsUnmarshal->NewCodeLabel();
373 // Did we pass an outer IInspectable?
374 pcsUnmarshal->EmitLDLOC(dwOuterIInspectableLocalNum);
375 pcsUnmarshal->EmitBRFALSE(pNonCompositionLabel);
377 // yes, this is a composition scenario
379 // ignore the delegating interface pointer (will be released in cleanup below) - we can
380 // re-create it by QI'ing the non-delegating one.
381 // Note that using this could be useful in the future (avoids an extra QueryInterface call)
382 // Just load the non-delegating interface pointer
383 pcsUnmarshal->EmitLDLOCA(dwInnerIInspectableLocalNum);
384 pcsUnmarshal->EmitBR(pLoadedLabel);
386 // else, no this is a non-composition scenario
388 pcsUnmarshal->EmitLabel(pNonCompositionLabel);
390 // ignore the non-delegating interface pointer (which should be null, but will regardless get
391 // cleaned up below in the event the factory doesn't follow the pattern properly).
392 // Just load the regular delegating interface pointer
393 pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
396 pcsUnmarshal->EmitLabel(pLoadedLabel);
400 // Definitely can't be a composition scenario - use the only pointer we have
401 pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
404 pcsUnmarshal->EmitCALL(METHOD__WINDOWSRUNTIMEMARSHAL__INITIALIZE_WRAPPER, 2, 0);
410 // release the returned interface pointer in the finally block
411 m_slIL.SetCleanupNeeded();
413 ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
415 m_slIL.EmitCheckForArgCleanup(pcsCleanup,
416 NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL,
417 NDirectStubLinker::BranchIfNotMarshaled,
420 EmitInterfaceClearNative(pcsCleanup, dwFactoryRetValLocalNum);
422 // Note that it's a no-op to pass NULL to Clear_Native, so we call it even though we don't
423 // know if we assigned to the inner/outer IInspectable
424 if (dwInnerIInspectableLocalNum != -1)
426 EmitInterfaceClearNative(pcsCleanup, dwInnerIInspectableLocalNum);
428 if (dwOuterIInspectableLocalNum != -1)
430 EmitInterfaceClearNative(pcsCleanup, dwOuterIInspectableLocalNum);
433 pcsCleanup->EmitLabel(pSkipCleanupLabel);
436 static void EmitInterfaceClearNative(ILCodeStream* pcsEmit, DWORD dwLocalNum)
438 STANDARD_VM_CONTRACT;
440 ILCodeLabel *pSkipClearNativeLabel = pcsEmit->NewCodeLabel();
441 pcsEmit->EmitLDLOC(dwLocalNum);
442 pcsEmit->EmitBRFALSE(pSkipClearNativeLabel);
443 pcsEmit->EmitLDLOC(dwLocalNum);
444 pcsEmit->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
445 pcsEmit->EmitLabel(pSkipClearNativeLabel);
448 #endif // FEATURE_COMINTEROP
450 void MarshalLCID(int argIdx)
452 STANDARD_VM_CONTRACT;
454 ILCodeStream* pcs = m_slIL.GetDispatchCodeStream();
456 #ifdef FEATURE_USE_LCID
457 if (SF_IsReverseStub(m_dwStubFlags))
459 if ((m_slIL.GetStubTargetCallingConv() & IMAGE_CEE_CS_CALLCONV_HASTHIS) == IMAGE_CEE_CS_CALLCONV_HASTHIS)
461 // the arg number will be incremented by LDARG if we are in an instance method
462 _ASSERTE(argIdx > 0);
466 // call CultureInfo.get_CurrentCulture()
467 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
469 // save the current culture
470 LocalDesc locDescCulture(MscorlibBinder::GetClass(CLASS__CULTURE_INFO));
471 DWORD dwCultureLocalNum = pcs->NewLocal(locDescCulture);
473 pcs->EmitSTLOC(dwCultureLocalNum);
475 // set a new one based on the LCID passed from unmanaged
476 pcs->EmitLDARG(argIdx);
478 // call CultureInfo..ctor(lcid)
479 // call CultureInfo.set_CurrentCulture(culture)
480 pcs->EmitNEWOBJ(METHOD__CULTURE_INFO__INT_CTOR, 1);
481 pcs->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
483 // and restore the current one after the call
484 m_slIL.SetCleanupNeeded();
485 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
487 // call CultureInfo.set_CurrentCulture(original_culture)
488 pcsCleanup->EmitLDLOC(dwCultureLocalNum);
489 pcsCleanup->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
493 if (SF_IsCOMStub(m_dwStubFlags))
495 // We used to get LCID from current thread's culture here. The code
496 // was replaced by the hardcoded LCID_ENGLISH_US as requested by VSTO.
497 pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
501 // call CultureInfo.get_CurrentCulture()
502 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
504 //call CultureInfo.get_LCID(this)
505 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_ID, 1, 1);
508 #else // FEATURE_USE_LCID
509 if (SF_IsForwardStub(m_dwStubFlags))
511 pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
513 #endif // FEATURE_USE_LCID
515 // add the extra arg to the unmanaged signature
516 LocalDesc locDescNative(ELEMENT_TYPE_I4);
517 pcs->SetStubTargetArgType(&locDescNative, false);
519 if (SF_IsReverseStub(m_dwStubFlags))
521 // reverse the effect of SetStubTargetArgType on the stack delta
522 // (the LCID argument is explicitly passed from unmanaged but does not
523 // show up in the managed signature in any way)
524 m_slIL.AdjustTargetStackDeltaForExtraParam();
529 void SwapStubSignatures(MethodDesc* pStubMD)
531 STANDARD_VM_CONTRACT;
534 // Since the stub handles native-to-managed transitions, we have to swap the
535 // stub-state-calculated stub target sig with the stub sig itself. This is
536 // because the stub target sig represents the native signature and the stub
537 // sig represents the managed signature.
539 // The first step is to convert the managed signature to a module-independent
540 // signature and then pass it off to SetStubTargetMethodSig. Note that the
541 // ILStubResolver will copy the sig, so we only need to make a temporary copy
544 SigBuilder sigBuilder;
547 SigPointer sigPtr(pStubMD->GetSig());
548 sigPtr.ConvertToInternalSignature(pStubMD->GetModule(), NULL, &sigBuilder);
552 // The second step is to reset the sig on the stub MethodDesc to be the
553 // stub-state-calculated stub target sig.
557 // make a domain-local copy of the sig so that this state can outlive the
558 // compile time state.
561 PCCOR_SIGNATURE pNewSig;
563 cbNewSig = GetStubTargetMethodSigLength();
564 pNewSig = (PCCOR_SIGNATURE)(void *)pStubMD->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig));
566 memcpyNoGCRefs((void *)pNewSig, GetStubTargetMethodSig(), cbNewSig);
568 pStubMD->AsDynamicMethodDesc()->SetStoredMethodSig(pNewSig, cbNewSig);
570 SigPointer sigPtr(pNewSig, cbNewSig);
572 IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo));
574 if (callConvInfo & CORINFO_CALLCONV_HASTHIS)
576 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags &= ~mdStatic;
577 pStubMD->ClearStatic();
581 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags |= mdStatic;
582 pStubMD->SetStatic();
586 // we store the real managed argument stack size in the stub MethodDesc on non-X86
587 UINT stackSize = pStubMD->SizeOfNativeArgStack();
589 if (!FitsInU2(stackSize))
590 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
592 pStubMD->AsDynamicMethodDesc()->SetNativeStackArgSize(static_cast<WORD>(stackSize));
593 #endif // _TARGET_X86_
596 DWORD cbTempModuleIndependentSigLength;
597 BYTE * pTempModuleIndependentSig = (BYTE *)sigBuilder.GetSignature(&cbTempModuleIndependentSigLength);
600 SetStubTargetMethodSig(pTempModuleIndependentSig,
601 cbTempModuleIndependentSigLength);
604 void EmitInvokeTarget(MethodDesc *pStubMD)
606 STANDARD_VM_CONTRACT;
608 m_slIL.DoNDirect(m_slIL.GetDispatchCodeStream(), m_dwStubFlags, pStubMD);
611 #ifdef FEATURE_COMINTEROP
612 void EmitExceptionHandler(LocalDesc* pNativeReturnType, LocalDesc* pManagedReturnType,
613 ILCodeLabel** ppTryEndAndCatchBeginLabel, ILCodeLabel ** ppCatchEndAndReturnLabel)
615 STANDARD_VM_CONTRACT;
617 ILCodeStream* pcsExceptionHandler = m_slIL.NewCodeStream(ILStubLinker::kExceptionHandler);
618 *ppTryEndAndCatchBeginLabel = pcsExceptionHandler->NewCodeLabel();
619 *ppCatchEndAndReturnLabel = pcsExceptionHandler->NewCodeLabel();
621 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
622 pcsExceptionHandler->EmitLabel(*ppTryEndAndCatchBeginLabel);
624 BYTE nativeReturnElemType = pNativeReturnType->ElementType[0]; // return type of the stub
625 BYTE managedReturnElemType = pManagedReturnType->ElementType[0]; // return type of the mananged target
627 bool returnTheHRESULT = SF_IsHRESULTSwapping(m_dwStubFlags) ||
628 (managedReturnElemType == ELEMENT_TYPE_I4) ||
629 (managedReturnElemType == ELEMENT_TYPE_U4);
632 if (!returnTheHRESULT)
634 MdaExceptionSwallowedOnCallFromCom* mda = MDA_GET_ASSISTANT(ExceptionSwallowedOnCallFromCom);
637 // on the stack: exception object, but the stub linker doesn't know it
638 pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
639 pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__TRIGGER_EXCEPTION_SWALLOWED_MDA,
640 1, // WARNING: This method takes 2 input args, the exception object and the stub context.
641 // But the ILStubLinker has no knowledge that the exception object is on the
642 // stack (because it is unaware that we've just entered a catch block), so we
643 // lie and claim that we only take one input argument.
644 1); // returns the exception object back
647 #endif // MDA_SUPPORTED
649 DWORD retvalLocalNum = m_slIL.GetReturnValueLocalNum();
650 BinderMethodID getHRForException;
651 if (SF_IsWinRTStub(m_dwStubFlags))
653 getHRForException = METHOD__WINDOWSRUNTIMEMARSHAL__GET_HR_FOR_EXCEPTION;
657 getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION;
660 pcsExceptionHandler->EmitCALL(getHRForException,
661 0, // WARNING: This method takes 1 input arg, the exception object. But the ILStubLinker
662 // has no knowledge that the exception object is on the stack (because it is
663 // unaware that we've just entered a catch block), so we lie and claim that we
664 // don't take any input arguments.
667 switch (nativeReturnElemType)
670 UNREACHABLE_MSG("Unexpected element type found on native return type.");
672 case ELEMENT_TYPE_VOID:
673 _ASSERTE(retvalLocalNum == (DWORD)-1);
674 pcsExceptionHandler->EmitPOP();
676 case ELEMENT_TYPE_I4:
677 case ELEMENT_TYPE_U4:
679 if (!returnTheHRESULT)
681 pcsExceptionHandler->EmitPOP();
682 pcsExceptionHandler->EmitLDC(0);
683 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
685 _ASSERTE(retvalLocalNum != (DWORD)-1);
686 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
689 case ELEMENT_TYPE_R4:
690 pcsExceptionHandler->EmitPOP();
691 pcsExceptionHandler->EmitLDC_R4(CLR_NAN_32);
692 _ASSERTE(retvalLocalNum != (DWORD)-1);
693 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
695 case ELEMENT_TYPE_R8:
696 pcsExceptionHandler->EmitPOP();
697 pcsExceptionHandler->EmitLDC_R8(CLR_NAN_64);
698 _ASSERTE(retvalLocalNum != (DWORD)-1);
699 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
701 case ELEMENT_TYPE_INTERNAL:
703 TypeHandle returnTypeHnd = pNativeReturnType->InternalToken;
704 CONSISTENCY_CHECK(returnTypeHnd.IsValueType());
705 _ASSERTE(retvalLocalNum != (DWORD)-1);
706 pcsExceptionHandler->EmitLDLOCA(retvalLocalNum);
707 pcsExceptionHandler->EmitINITOBJ(m_slIL.GetDispatchCodeStream()->GetToken(returnTypeHnd));
710 case ELEMENT_TYPE_BOOLEAN:
711 case ELEMENT_TYPE_CHAR:
712 case ELEMENT_TYPE_I1:
713 case ELEMENT_TYPE_U1:
714 case ELEMENT_TYPE_I2:
715 case ELEMENT_TYPE_U2:
716 case ELEMENT_TYPE_I8:
717 case ELEMENT_TYPE_U8:
720 pcsExceptionHandler->EmitPOP();
721 pcsExceptionHandler->EmitLDC(0);
722 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
723 _ASSERTE(retvalLocalNum != (DWORD)-1);
724 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
728 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
729 pcsExceptionHandler->EmitLabel(*ppCatchEndAndReturnLabel);
730 if (nativeReturnElemType != ELEMENT_TYPE_VOID)
732 _ASSERTE(retvalLocalNum != (DWORD)-1);
733 pcsExceptionHandler->EmitLDLOC(retvalLocalNum);
735 pcsExceptionHandler->EmitRET();
737 #endif // FEATURE_COMINTEROP
739 void FinishEmit(MethodDesc* pStubMD)
741 STANDARD_VM_CONTRACT;
743 ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
744 ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
745 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
747 if (SF_IsHRESULTSwapping(m_dwStubFlags) && m_slIL.StubHasVoidReturnType())
749 // if the return type is void, but we're doing HRESULT swapping, we
750 // need to set the return type here. Otherwise, the return value
751 // marshaler will do this.
752 pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // HRESULT
754 if (SF_IsReverseStub(m_dwStubFlags))
756 // reverse interop needs to seed the return value if the
757 // managed function returns void but we're doing hresult
759 pcsUnmarshal->EmitLDC(S_OK);
763 LocalDesc nativeReturnType;
764 LocalDesc managedReturnType;
765 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(m_dwStubFlags)
766 && !SF_IsFieldGetterStub(m_dwStubFlags)
767 && !SF_IsFieldSetterStub(m_dwStubFlags);
769 #ifdef FEATURE_COMINTEROP
770 if (hasTryCatchForHRESULT)
772 m_slIL.GetStubTargetReturnType(&nativeReturnType);
773 m_slIL.GetStubReturnType(&managedReturnType);
775 #endif // FEATURE_COMINTEROP
777 if (SF_IsHRESULTSwapping(m_dwStubFlags) && SF_IsReverseStub(m_dwStubFlags))
779 m_slIL.AdjustTargetStackDeltaForReverseInteropHRESULTSwapping();
782 if (SF_IsForwardCOMStub(m_dwStubFlags))
784 // Compensate for the 'this' parameter.
785 m_slIL.AdjustTargetStackDeltaForExtraParam();
788 // Don't touch target signatures from this point on otherwise it messes up the
789 // cache in ILStubState::GetStubTargetMethodSig.
793 // The native and local signatures should not have any tokens.
794 // All token references should have been converted to
795 // ELEMENT_TYPE_INTERNAL.
797 // Note that MetaSig::GetReturnType and NextArg will normalize
798 // ELEMENT_TYPE_INTERNAL back to CLASS or VALUETYPE.
800 // <TODO> need to recursively check ELEMENT_TYPE_FNPTR signatures </TODO>
802 SigTypeContext typeContext; // this is an empty type context: COM calls are guaranteed to not be generics.
804 GetStubTargetMethodSig(),
805 GetStubTargetMethodSigLength(),
806 MscorlibBinder::GetModule(),
810 IfFailThrow(nsig.GetReturnProps().PeekElemType(&type));
811 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
813 while (ELEMENT_TYPE_END != (type = nsig.NextArg()))
815 IfFailThrow(nsig.GetArgProps().PeekElemType(&type));
816 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
821 #ifdef FEATURE_COMINTEROP
822 if (SF_IsForwardCOMStub(m_dwStubFlags))
824 #if defined(MDA_SUPPORTED)
825 // We won't use this NGEN'ed stub if RaceOnRCWCleanup is enabled at run-time
826 if (!SF_IsNGENedStub(m_dwStubFlags))
828 // This code may change the type of the frame we use, so it has to be run before the code below where we
829 // retrieve the stack arg size based on the frame type.
830 MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
833 // Here we have to register the RCW of the "this" object to the RCWStack and schedule the clean-up for it.
834 // Emit a call to StubHelpers::StubRegisterRCW() and StubHelpers::StubUnregisterRCW() to do this.
835 m_slIL.EmitLoadRCWThis(pcsMarshal, m_dwStubFlags);
836 pcsMarshal->EmitCALL(METHOD__STUBHELPERS__STUB_REGISTER_RCW, 1, 0);
838 // We use an extra local to track whether we need to unregister the RCW on cleanup
839 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
840 DWORD dwRCWRegisteredLocalNum = pcsSetup->NewLocal(ELEMENT_TYPE_BOOLEAN);
841 pcsSetup->EmitLDC(0);
842 pcsSetup->EmitSTLOC(dwRCWRegisteredLocalNum);
844 pcsMarshal->EmitLDC(1);
845 pcsMarshal->EmitSTLOC(dwRCWRegisteredLocalNum);
847 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
848 ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
850 m_slIL.SetCleanupNeeded();
851 pcsCleanup->EmitLDLOC(dwRCWRegisteredLocalNum);
852 pcsCleanup->EmitBRFALSE(pSkipCleanupLabel);
854 m_slIL.EmitLoadRCWThis(pcsCleanup, m_dwStubFlags);
855 pcsCleanup->EmitCALL(METHOD__STUBHELPERS__STUB_UNREGISTER_RCW, 1, 0);
857 pcsCleanup->EmitLabel(pSkipCleanupLabel);
860 #endif // MDA_SUPPORTED
862 #endif // FEATURE_COMINTEROP
865 // The profiler helpers below must be called immediately before and after the call to the target.
866 // The debugger trace call helpers are invoked from StubRareDisableWorker
869 #if defined(PROFILING_SUPPORTED)
870 DWORD dwMethodDescLocalNum = (DWORD)-1;
872 // Notify the profiler of call out of the runtime
873 if (!SF_IsReverseCOMStub(m_dwStubFlags) && (CORProfilerTrackTransitions() || SF_IsNGENedStubForProfiling(m_dwStubFlags)))
875 dwMethodDescLocalNum = m_slIL.EmitProfilerBeginTransitionCallback(pcsDispatch, m_dwStubFlags);
876 _ASSERTE(dwMethodDescLocalNum != (DWORD)-1);
878 #endif // PROFILING_SUPPORTED
881 if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
882 MDA_GET_ASSISTANT(GcManagedToUnmanaged))
884 m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
886 #endif // MDA_SUPPORTED
888 // For CoreClr, clear the last error before calling the target that returns last error.
889 // There isn't always a way to know the function have failed without checking last error,
890 // in particular on Unix.
891 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
893 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CLEAR_LAST_ERROR, 0, 0);
896 // Invoke the target (calli, call method, call delegate, get/set field, etc.)
897 EmitInvokeTarget(pStubMD);
899 // Saving last error must be the first thing we do after returning from the target
900 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
902 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__SET_LAST_ERROR, 0, 0);
905 #if defined(_TARGET_X86_)
906 if (SF_IsForwardDelegateStub(m_dwStubFlags))
908 // the delegate may have an intercept stub attached to its sync block so we should
909 // prevent it from being garbage collected when the call is in progress
910 pcsDispatch->EmitLoadThis();
911 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
913 #endif // defined(_TARGET_X86_)
916 if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
917 MDA_GET_ASSISTANT(GcUnmanagedToManaged))
919 m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
921 #endif // MDA_SUPPORTED
924 if (SF_IsForwardStub(m_dwStubFlags) && g_pConfig->InteropValidatePinnedObjects())
926 // call StubHelpers.ValidateObject/StubHelpers.ValidateByref on pinned locals
927 m_slIL.EmitObjectValidation(pcsDispatch, m_dwStubFlags);
929 #endif // VERIFY_HEAP
931 #if defined(PROFILING_SUPPORTED)
932 // Notify the profiler of return back into the runtime
933 if (dwMethodDescLocalNum != (DWORD)-1)
935 m_slIL.EmitProfilerEndTransitionCallback(pcsDispatch, m_dwStubFlags, dwMethodDescLocalNum);
937 #endif // PROFILING_SUPPORTED
939 #ifdef FEATURE_COMINTEROP
940 if (SF_IsForwardCOMStub(m_dwStubFlags))
942 // Make sure that the RCW stays alive for the duration of the call. Note that if we do HRESULT
943 // swapping, we'll pass 'this' to GetCOMHRExceptionObject after returning from the target so
944 // GC.KeepAlive is not necessary.
945 if (!SF_IsHRESULTSwapping(m_dwStubFlags))
947 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
948 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
951 #endif // FEATURE_COMINTEROP
953 if (SF_IsHRESULTSwapping(m_dwStubFlags))
955 if (SF_IsForwardStub(m_dwStubFlags))
957 ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
959 pcsDispatch->EmitDUP();
960 pcsDispatch->EmitLDC(0);
961 pcsDispatch->EmitBGE(pSkipThrowLabel);
963 #ifdef FEATURE_COMINTEROP
964 if (SF_IsCOMStub(m_dwStubFlags))
966 m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
967 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
969 if (SF_IsWinRTStub(m_dwStubFlags))
971 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT_WINRT, 3, 1);
975 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT, 3, 1);
979 #endif // FEATURE_COMINTEROP
981 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_HR_EXCEPTION_OBJECT, 1, 1);
984 pcsDispatch->EmitTHROW();
985 pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
986 pcsDispatch->EmitLabel(pSkipThrowLabel);
987 pcsDispatch->EmitPOP();
991 m_slIL.End(m_dwStubFlags);
992 if (!hasTryCatchForHRESULT) // we will 'leave' the try scope and then 'ret' from outside
994 pcsUnmarshal->EmitRET();
997 CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB);
999 if (m_slIL.HasInteropParamExceptionInfo())
1001 // This code will not use the secret parameter, so we do not
1002 // tell the JIT to bother with it.
1004 m_slIL.GenerateInteropParamException(pcsMarshal);
1006 else if (SF_IsFieldGetterStub(m_dwStubFlags) || SF_IsFieldSetterStub(m_dwStubFlags))
1008 // Field access stubs are not shared and do not use the secret parameter.
1011 else if (SF_IsForwardDelegateStub(m_dwStubFlags) ||
1012 (SF_IsForwardCOMStub(m_dwStubFlags) && SF_IsWinRTDelegateStub(m_dwStubFlags)))
1014 // Forward delegate stubs get all the context they need in 'this' so they
1015 // don't use the secret parameter. Except for AMD64 where we use the secret
1016 // argument to pass the real target to the stub-for-host.
1021 // All other IL stubs will need to use the secret parameter.
1022 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM);
1025 if (SF_IsReverseStub(m_dwStubFlags))
1027 SwapStubSignatures(pStubMD);
1030 ILCodeLabel* pTryEndAndCatchBeginLabel = NULL; // try ends at the same place the catch begins
1031 ILCodeLabel* pCatchEndAndReturnLabel = NULL; // catch ends at the same place we resume afterwards
1032 #ifdef FEATURE_COMINTEROP
1033 if (hasTryCatchForHRESULT)
1035 EmitExceptionHandler(&nativeReturnType, &managedReturnType, &pTryEndAndCatchBeginLabel, &pCatchEndAndReturnLabel);
1037 #endif // FEATURE_COMINTEROP
1045 cbCode = m_slIL.Link(&maxStack);
1046 cbSig = m_slIL.GetLocalSigSize();
1048 ILStubResolver * pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
1049 COR_ILMETHOD_DECODER * pILHeader = pResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
1050 pbBuffer = (BYTE *)pILHeader->Code;
1051 pbLocalSig = (BYTE *)pILHeader->LocalVarSig;
1052 _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
1054 ILStubEHClause cleanupTryFinally = { 0 };
1055 ILStubEHClause convertToHRTryCatch = { 0 };
1056 m_slIL.GetCleanupFinallyOffsets(&cleanupTryFinally);
1058 #ifdef FEATURE_COMINTEROP
1059 if (hasTryCatchForHRESULT)
1061 convertToHRTryCatch.kind = ILStubEHClause::kTypedCatch;
1062 convertToHRTryCatch.dwTryBeginOffset = 0;
1063 convertToHRTryCatch.dwHandlerBeginOffset = ((DWORD)pTryEndAndCatchBeginLabel->GetCodeOffset());
1064 convertToHRTryCatch.cbTryLength = convertToHRTryCatch.dwHandlerBeginOffset - convertToHRTryCatch.dwTryBeginOffset;
1065 convertToHRTryCatch.cbHandlerLength = ((DWORD)pCatchEndAndReturnLabel->GetCodeOffset()) - convertToHRTryCatch.dwHandlerBeginOffset;
1066 convertToHRTryCatch.dwTypeToken = pcsDispatch->GetToken(g_pObjectClass);
1068 #endif // FEATURE_COMINTEROP
1072 if (convertToHRTryCatch.cbHandlerLength != 0)
1075 if (cleanupTryFinally.cbHandlerLength != 0)
1080 COR_ILMETHOD_SECT_EH* pEHSect = pResolver->AllocEHSect(nEHClauses);
1081 PopulateEHSect(pEHSect, nEHClauses, &cleanupTryFinally, &convertToHRTryCatch);
1084 m_slIL.GenerateCode(pbBuffer, cbCode);
1085 m_slIL.GetLocalSig(pbLocalSig, cbSig);
1087 pResolver->SetJitFlags(jitFlags);
1090 LOG((LF_STUBS, LL_INFO1000, "---------------------------------------------------------------------\n"));
1091 LOG((LF_STUBS, LL_INFO1000, "NDirect IL stub dump: %s::%s\n", pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
1092 if (LoggingEnabled() && LoggingOn(LF_STUBS, LL_INFO1000))
1094 CQuickBytes qbManaged;
1095 CQuickBytes qbLocal;
1097 PCCOR_SIGNATURE pManagedSig;
1100 IMDInternalImport* pIMDI = MscorlibBinder::GetModule()->GetMDImport();
1102 pStubMD->GetSig(&pManagedSig, &cManagedSig);
1104 PrettyPrintSig(pManagedSig, cManagedSig, "*", &qbManaged, pStubMD->GetMDImport(), NULL);
1105 PrettyPrintSig(pbLocalSig, cbSig, NULL, &qbLocal, pIMDI, NULL);
1107 LOG((LF_STUBS, LL_INFO1000, "incoming managed sig: %p: %s\n", pManagedSig, qbManaged.Ptr()));
1108 LOG((LF_STUBS, LL_INFO1000, "locals sig: %p: %s\n", pbLocalSig+1, qbLocal.Ptr()));
1110 if (cleanupTryFinally.cbHandlerLength != 0)
1112 LOG((LF_STUBS, LL_INFO1000, "try_begin: 0x%04x try_end: 0x%04x finally_begin: 0x%04x finally_end: 0x%04x \n",
1113 cleanupTryFinally.dwTryBeginOffset, cleanupTryFinally.dwTryBeginOffset + cleanupTryFinally.cbTryLength,
1114 cleanupTryFinally.dwHandlerBeginOffset, cleanupTryFinally.dwHandlerBeginOffset + cleanupTryFinally.cbHandlerLength));
1116 if (convertToHRTryCatch.cbHandlerLength != 0)
1118 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",
1119 convertToHRTryCatch.dwTryBeginOffset, convertToHRTryCatch.dwTryBeginOffset + convertToHRTryCatch.cbTryLength,
1120 convertToHRTryCatch.dwHandlerBeginOffset, convertToHRTryCatch.dwHandlerBeginOffset + convertToHRTryCatch.cbHandlerLength,
1121 convertToHRTryCatch.dwTypeToken));
1124 LogILStubFlags(LF_STUBS, LL_INFO1000, m_dwStubFlags);
1126 m_slIL.LogILStub(jitFlags);
1128 LOG((LF_STUBS, LL_INFO1000, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"));
1132 // Publish ETW events for IL stubs
1135 // If the category and the event is enabled...
1136 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ILStubGenerated))
1138 EtwOnILStubGenerated(
1143 &convertToHRTryCatch,
1152 //---------------------------------------------------------------------------------------
1155 EtwOnILStubGenerated(
1156 MethodDesc * pStubMD,
1157 PCCOR_SIGNATURE pbLocalSig,
1159 CORJIT_FLAGS jitFlags,
1160 ILStubEHClause * pConvertToHRTryCatchBounds,
1161 ILStubEHClause * pCleanupTryFinallyBounds,
1165 STANDARD_VM_CONTRACT;
1168 // Interop Method Information
1170 MethodDesc *pTargetMD = m_slIL.GetTargetMD();
1171 SString strNamespaceOrClassName, strMethodName, strMethodSignature;
1172 UINT64 uModuleId = 0;
1176 pTargetMD->GetMethodInfoWithNewSig(strNamespaceOrClassName, strMethodName, strMethodSignature);
1177 uModuleId = (UINT64)pTargetMD->GetModule()->GetAddrModuleID();
1181 // Stub Method Signature
1183 SString stubNamespaceOrClassName, stubMethodName, stubMethodSignature;
1184 pStubMD->GetMethodInfoWithNewSig(stubNamespaceOrClassName, stubMethodName, stubMethodSignature);
1186 IMDInternalImport *pStubImport = pStubMD->GetModule()->GetMDImport();
1188 CQuickBytes qbLocal;
1189 PrettyPrintSig(pbLocalSig, (DWORD)cbSig, NULL, &qbLocal, pStubImport, NULL);
1191 SString strLocalSig(SString::Utf8, (LPCUTF8)qbLocal.Ptr());
1196 SString strNativeSignature(SString::Utf8);
1197 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
1199 // Reverse interop. Use StubSignature
1200 strNativeSignature = stubMethodSignature;
1204 // Forward interop. Use StubTarget siganture
1205 PCCOR_SIGNATURE pCallTargetSig = GetStubTargetMethodSig();
1206 DWORD cCallTargetSig = GetStubTargetMethodSigLength();
1208 CQuickBytes qbCallTargetSig;
1210 PrettyPrintSig(pCallTargetSig, cCallTargetSig, "", &qbCallTargetSig, pStubImport, NULL);
1212 strNativeSignature.SetUTF8((LPCUTF8)qbCallTargetSig.Ptr());
1216 // Dump IL stub code
1218 SString strILStubCode;
1219 strILStubCode.Preallocate(4096); // Preallocate 4K bytes to avoid unnecessary growth
1221 SString codeSizeFormat;
1222 codeSizeFormat.LoadResource(CCompRC::Optional, IDS_EE_INTEROP_CODE_SIZE_COMMENT);
1223 strILStubCode.AppendPrintf(W("// %s\t%d (0x%04x)\n"), codeSizeFormat.GetUnicode(), cbCode, cbCode);
1224 strILStubCode.AppendPrintf(W(".maxstack %d \n"), maxStack);
1225 strILStubCode.AppendPrintf(W(".locals %s\n"), strLocalSig.GetUnicode());
1227 m_slIL.LogILStub(jitFlags, &strILStubCode);
1229 if (pConvertToHRTryCatchBounds->cbTryLength != 0 && pConvertToHRTryCatchBounds->cbHandlerLength != 0)
1231 strILStubCode.AppendPrintf(
1232 W(".try IL_%04x to IL_%04x catch handler IL_%04x to IL_%04x\n"),
1233 pConvertToHRTryCatchBounds->dwTryBeginOffset,
1234 pConvertToHRTryCatchBounds->dwTryBeginOffset + pConvertToHRTryCatchBounds->cbTryLength,
1235 pConvertToHRTryCatchBounds->dwHandlerBeginOffset,
1236 pConvertToHRTryCatchBounds->dwHandlerBeginOffset + pConvertToHRTryCatchBounds->cbHandlerLength);
1239 if (pCleanupTryFinallyBounds->cbTryLength != 0 && pCleanupTryFinallyBounds->cbHandlerLength != 0)
1241 strILStubCode.AppendPrintf(
1242 W(".try IL_%04x to IL_%04x finally handler IL_%04x to IL_%04x\n"),
1243 pCleanupTryFinallyBounds->dwTryBeginOffset,
1244 pCleanupTryFinallyBounds->dwTryBeginOffset + pCleanupTryFinallyBounds->cbTryLength,
1245 pCleanupTryFinallyBounds->dwHandlerBeginOffset,
1246 pCleanupTryFinallyBounds->dwHandlerBeginOffset + pCleanupTryFinallyBounds->cbHandlerLength);
1253 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
1254 dwFlags |= ETW_IL_STUB_FLAGS_REVERSE_INTEROP;
1255 #ifdef FEATURE_COMINTEROP
1256 if (m_dwStubFlags & NDIRECTSTUB_FL_COM)
1257 dwFlags |= ETW_IL_STUB_FLAGS_COM_INTEROP;
1258 #endif // FEATURE_COMINTEROP
1259 if (m_dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUB)
1260 dwFlags |= ETW_IL_STUB_FLAGS_NGENED_STUB;
1261 if (m_dwStubFlags & NDIRECTSTUB_FL_DELEGATE)
1262 dwFlags |= ETW_IL_STUB_FLAGS_DELEGATE;
1263 if (m_dwStubFlags & NDIRECTSTUB_FL_CONVSIGASVARARG)
1264 dwFlags |= ETW_IL_STUB_FLAGS_VARARG;
1265 if (m_dwStubFlags & NDIRECTSTUB_FL_UNMANAGED_CALLI)
1266 dwFlags |= ETW_IL_STUB_FLAGS_UNMANAGED_CALLI;
1270 dwToken = pTargetMD->GetMemberDef();
1274 // Truncate string fields. Make sure the whole event is less than 64KB
1276 TruncateUnicodeString(strNamespaceOrClassName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1277 TruncateUnicodeString(strMethodName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1278 TruncateUnicodeString(strMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1279 TruncateUnicodeString(strNativeSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1280 TruncateUnicodeString(stubMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1281 TruncateUnicodeString(strILStubCode, ETW_IL_STUB_EVENT_CODE_STRING_FIELD_MAXSIZE);
1286 FireEtwILStubGenerated(
1287 GetClrInstanceId(), // ClrInstanceId
1288 uModuleId, // ModuleIdentifier
1289 (UINT64)pStubMD, // StubMethodIdentifier
1290 dwFlags, // StubFlags
1291 dwToken, // ManagedInteropMethodToken
1292 strNamespaceOrClassName.GetUnicode(), // ManagedInteropMethodNamespace
1293 strMethodName.GetUnicode(), // ManagedInteropMethodName
1294 strMethodSignature.GetUnicode(), // ManagedInteropMethodSignature
1295 strNativeSignature.GetUnicode(), // NativeSignature
1296 stubMethodSignature.GetUnicode(), // StubMethodSigature
1297 strILStubCode.GetUnicode() // StubMethodILCode
1299 } // EtwOnILStubGenerated
1302 //---------------------------------------------------------------------------------------
1304 static inline void LogOneFlag(DWORD flags, DWORD flag, LPCSTR str, DWORD facility, DWORD level)
1306 LIMITED_METHOD_CONTRACT;
1309 LOG((facility, level, str));
1313 static void LogILStubFlags(DWORD facility, DWORD level, DWORD dwStubFlags)
1315 LIMITED_METHOD_CONTRACT;
1316 LOG((facility, level, "dwStubFlags: 0x%08x\n", dwStubFlags));
1317 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_CONVSIGASVARARG, " NDIRECTSTUB_FL_CONVSIGASVARARG\n", facility, level);
1318 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_BESTFIT, " NDIRECTSTUB_FL_BESTFIT\n", facility, level);
1319 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR, " NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR\n", facility, level);
1320 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUB, " NDIRECTSTUB_FL_NGENEDSTUB\n", facility, level);
1321 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DELEGATE, " NDIRECTSTUB_FL_DELEGATE\n", facility, level);
1322 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DOHRESULTSWAPPING, " NDIRECTSTUB_FL_DOHRESULTSWAPPING\n", facility, level);
1323 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_REVERSE_INTEROP, " NDIRECTSTUB_FL_REVERSE_INTEROP\n", facility, level);
1324 #ifdef FEATURE_COMINTEROP
1325 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_COM, " NDIRECTSTUB_FL_COM\n", facility, level);
1326 #endif // FEATURE_COMINTEROP
1327 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING, " NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING\n", facility, level);
1328 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL, " NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL\n", facility, level);
1329 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_UNMANAGED_CALLI, " NDIRECTSTUB_FL_UNMANAGED_CALLI\n", facility, level);
1330 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_TRIGGERCCTOR, " NDIRECTSTUB_FL_TRIGGERCCTOR\n", facility, level);
1331 #ifdef FEATURE_COMINTEROP
1332 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDGETTER, " NDIRECTSTUB_FL_FIELDGETTER\n", facility, level);
1333 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDSETTER, " NDIRECTSTUB_FL_FIELDSETTER\n", facility, level);
1334 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRT, " NDIRECTSTUB_FL_WINRT\n", facility, level);
1335 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTDELEGATE, " NDIRECTSTUB_FL_WINRTDELEGATE\n", facility, level);
1336 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSHAREDGENERIC, " NDIRECTSTUB_FL_WINRTSHAREDGENERIC\n", facility, level);
1337 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCTOR, " NDIRECTSTUB_FL_WINRTCTOR\n", facility, level);
1338 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCOMPOSITION, " NDIRECTSTUB_FL_WINRTCOMPOSITION\n", facility, level);
1339 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSTATIC, " NDIRECTSTUB_FL_WINRTSTATIC\n", facility, level);
1340 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTHASREDIRECTION, " NDIRECTSTUB_FL_WINRTHASREDIRECTION\n", facility, level);
1341 #endif // FEATURE_COMINTEROP
1344 // no need to log the internal flags, let's just assert what we expect to see...
1346 CONSISTENCY_CHECK(!SF_IsCOMLateBoundStub(dwStubFlags));
1347 CONSISTENCY_CHECK(!SF_IsCOMEventCallStub(dwStubFlags));
1350 NDIRECTSTUB_FL_CONVSIGASVARARG |
1351 NDIRECTSTUB_FL_BESTFIT |
1352 NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR |
1353 NDIRECTSTUB_FL_NGENEDSTUB |
1354 NDIRECTSTUB_FL_DELEGATE |
1355 NDIRECTSTUB_FL_DOHRESULTSWAPPING |
1356 NDIRECTSTUB_FL_REVERSE_INTEROP |
1357 NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING |
1358 NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL |
1359 NDIRECTSTUB_FL_UNMANAGED_CALLI |
1360 NDIRECTSTUB_FL_TRIGGERCCTOR |
1361 #ifdef FEATURE_COMINTEROP
1362 NDIRECTSTUB_FL_COM |
1363 NDIRECTSTUB_FL_COMLATEBOUND | // internal
1364 NDIRECTSTUB_FL_COMEVENTCALL | // internal
1365 NDIRECTSTUB_FL_FIELDGETTER |
1366 NDIRECTSTUB_FL_FIELDSETTER |
1367 NDIRECTSTUB_FL_WINRT |
1368 NDIRECTSTUB_FL_WINRTDELEGATE |
1369 NDIRECTSTUB_FL_WINRTCTOR |
1370 NDIRECTSTUB_FL_WINRTCOMPOSITION |
1371 NDIRECTSTUB_FL_WINRTSTATIC |
1372 NDIRECTSTUB_FL_WINRTHASREDIRECTION |
1373 #endif // FEATURE_COMINTEROP
1376 DWORD dwUnknownFlags = dwStubFlags & ~dwKnownMask;
1377 if (0 != dwUnknownFlags)
1379 LOG((facility, level, "UNKNOWN FLAGS: 0x%08x\n", dwUnknownFlags));
1384 PCCOR_SIGNATURE GetStubTargetMethodSig()
1386 CONTRACT(PCCOR_SIGNATURE)
1389 POSTCONDITION(CheckPointer(RETVAL, NULL_NOT_OK));
1395 if (!m_qbNativeFnSigBuffer.Size())
1397 DWORD cb = m_slIL.GetStubTargetMethodSigSize();
1398 pb = (BYTE *)m_qbNativeFnSigBuffer.AllocThrows(cb);
1400 m_slIL.GetStubTargetMethodSig(pb, cb);
1404 pb = (BYTE*)m_qbNativeFnSigBuffer.Ptr();
1411 GetStubTargetMethodSigLength()
1413 WRAPPER_NO_CONTRACT;
1415 return m_slIL.GetStubTargetMethodSigSize();
1418 void SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig)
1420 WRAPPER_NO_CONTRACT;
1422 m_slIL.SetStubTargetMethodSig(pSig, cSig);
1423 m_qbNativeFnSigBuffer.Shrink(0);
1426 TokenLookupMap* GetTokenLookupMap() { WRAPPER_NO_CONTRACT; return m_slIL.GetTokenLookupMap(); }
1429 CQuickBytes m_qbNativeFnSigBuffer;
1430 NDirectStubLinker m_slIL;
1431 BOOL m_fSetLastError;
1432 DWORD m_dwStubFlags;
1436 class PInvoke_ILStubState : public ILStubState
1440 PInvoke_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1441 CorPinvokeMap unmgdCallConv, int iLCIDParamIdx, MethodDesc* pTargetMD)
1446 TargetHasThis(dwStubFlags),
1447 StubHasThis(dwStubFlags),
1452 STANDARD_VM_CONTRACT;
1454 if (SF_IsForwardStub(dwStubFlags))
1456 m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags));
1461 static BOOL TargetHasThis(DWORD dwStubFlags)
1464 // in reverse pinvoke on delegate, the managed target will
1465 // have a 'this' pointer, but the unmanaged signature does
1468 return SF_IsReverseDelegateStub(dwStubFlags);
1471 static BOOL StubHasThis(DWORD dwStubFlags)
1474 // in forward pinvoke on a delegate, the stub will have a
1475 // 'this' pointer, but the unmanaged target will not.
1477 return SF_IsForwardDelegateStub(dwStubFlags);
1481 #ifdef FEATURE_COMINTEROP
1482 class CLRToCOM_ILStubState : public ILStubState
1486 CLRToCOM_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1487 int iLCIDParamIdx, MethodDesc* pTargetMD)
1493 !SF_IsWinRTStaticStub(dwStubFlags), // fStubHasThis
1498 STANDARD_VM_CONTRACT;
1500 if (SF_IsForwardStub(dwStubFlags))
1502 m_slIL.SetCallingConvention(pmCallConvStdcall, SF_IsVarArgStub(dwStubFlags));
1506 void BeginEmit(DWORD dwStubFlags) // CLR to COM IL
1508 STANDARD_VM_CONTRACT;
1510 ILStubState::BeginEmit(dwStubFlags);
1512 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
1514 // add the 'this' COM IP parameter to the target CALLI
1515 m_slIL.GetMarshalCodeStream()->SetStubTargetArgType(ELEMENT_TYPE_I, false);
1517 // convert 'this' to COM IP and the target method entry point
1518 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
1520 #ifdef _TARGET_64BIT_
1521 if (SF_IsWinRTDelegateStub(m_dwStubFlags))
1523 // write the stub context (EEImplMethodDesc representing the Invoke)
1524 // into the secret arg so it shows up in the InlinedCallFrame and can
1525 // be used by stub for host
1527 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
1528 m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
1529 pcsDispatch->EmitSTIND_I();
1530 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
1533 #endif // _TARGET_64BIT_
1535 m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
1538 pcsDispatch->EmitLDLOCA(m_slIL.GetTargetEntryPointLocalNum());
1540 BinderMethodID getCOMIPMethod;
1541 bool fDoPostCallIPCleanup = true;
1543 if (SF_IsWinRTStub(dwStubFlags))
1545 // WinRT uses optimized helpers
1546 if (SF_IsWinRTSharedGenericStub(dwStubFlags))
1547 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_SHARED_GENERIC;
1548 else if (SF_IsWinRTDelegateStub(dwStubFlags))
1549 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_DELEGATE;
1551 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT;
1553 // GetCOMIPFromRCW_WinRT, GetCOMIPFromRCW_WinRTSharedGeneric, and GetCOMIPFromRCW_WinRTDelegate
1554 // always cache the COM interface pointer so no post-call cleanup is needed
1555 fDoPostCallIPCleanup = false;
1559 // classic COM interop uses the non-optimized helper
1560 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW;
1563 DWORD dwIPRequiresCleanupLocalNum = (DWORD)-1;
1564 if (fDoPostCallIPCleanup)
1566 dwIPRequiresCleanupLocalNum = pcsDispatch->NewLocal(ELEMENT_TYPE_BOOLEAN);
1567 pcsDispatch->EmitLDLOCA(dwIPRequiresCleanupLocalNum);
1569 // StubHelpers.GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease)
1570 pcsDispatch->EmitCALL(getCOMIPMethod, 4, 1);
1574 // StubHelpers.GetCOMIPFromRCW_WinRT*(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget)
1575 pcsDispatch->EmitCALL(getCOMIPMethod, 3, 1);
1579 // save it because we'll need it to compute the CALLI target and release it
1580 pcsDispatch->EmitDUP();
1581 pcsDispatch->EmitSTLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1583 if (fDoPostCallIPCleanup)
1585 // make sure it's Release()'ed after the call
1586 m_slIL.SetCleanupNeeded();
1587 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
1589 ILCodeLabel *pSkipThisCleanup = pcsCleanup->NewCodeLabel();
1591 // and if it requires cleanup (i.e. it's not taken from the RCW cache)
1592 pcsCleanup->EmitLDLOC(dwIPRequiresCleanupLocalNum);
1593 pcsCleanup->EmitBRFALSE(pSkipThisCleanup);
1595 pcsCleanup->EmitLDLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1596 pcsCleanup->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
1597 pcsCleanup->EmitLabel(pSkipThisCleanup);
1602 class COMToCLR_ILStubState : public ILStubState
1606 COMToCLR_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1607 int iLCIDParamIdx, MethodDesc* pTargetMD)
1618 STANDARD_VM_CONTRACT;
1621 void BeginEmit(DWORD dwStubFlags) // COM to CLR IL
1623 STANDARD_VM_CONTRACT;
1625 ILStubState::BeginEmit(dwStubFlags);
1627 if (SF_IsWinRTStaticStub(dwStubFlags))
1629 // we are not loading 'this' because the target is static
1630 m_slIL.AdjustTargetStackDeltaForExtraParam();
1635 m_slIL.GetDispatchCodeStream()->EmitLoadThis();
1639 void MarshalFactoryReturn()
1644 PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
1648 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
1649 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
1650 ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
1651 ILCodeStream *pcsExCleanup = m_slIL.GetExceptionCleanupCodeStream();
1653 LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
1654 DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
1655 pcsSetup->EmitLoadNullPtr();
1656 pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
1658 locDescFactoryRetVal.MakeByRef();
1660 // expect one additional argument - pointer to a location that receives the created instance
1661 DWORD dwRetValArgNum = pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
1662 m_slIL.AdjustTargetStackDeltaForExtraParam();
1664 // convert 'this' to an interface pointer corresponding to the default interface of this class
1665 pcsUnmarshal->EmitLoadThis();
1666 pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
1667 pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_RETURN_VALUE, 2, 1);
1668 pcsUnmarshal->EmitSTLOC(dwFactoryRetValLocalNum);
1670 // assign it to the location pointed to by the argument
1671 pcsUnmarshal->EmitLDARG(dwRetValArgNum);
1672 pcsUnmarshal->EmitLDLOC(dwFactoryRetValLocalNum);
1673 pcsUnmarshal->EmitSTIND_I();
1675 // on exception, we want to release the IInspectable's and assign NULL to output locations
1676 m_slIL.SetExceptionCleanupNeeded();
1678 EmitInterfaceClearNative(pcsExCleanup, dwFactoryRetValLocalNum);
1681 pcsExCleanup->EmitLDARG(dwRetValArgNum);
1682 pcsExCleanup->EmitLoadNullPtr();
1683 pcsExCleanup->EmitSTIND_I();
1688 class COMToCLRFieldAccess_ILStubState : public COMToCLR_ILStubState
1692 COMToCLRFieldAccess_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext,
1693 DWORD dwStubFlags, FieldDesc* pFD)
1694 : COMToCLR_ILStubState(
1702 STANDARD_VM_CONTRACT;
1704 _ASSERTE(pFD != NULL);
1708 void EmitInvokeTarget(MethodDesc *pStubMD)
1710 STANDARD_VM_CONTRACT;
1712 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
1714 if (SF_IsFieldGetterStub(m_dwStubFlags))
1716 pcsDispatch->EmitLDFLD(pcsDispatch->GetToken(m_pFD));
1720 CONSISTENCY_CHECK(SF_IsFieldSetterStub(m_dwStubFlags));
1721 pcsDispatch->EmitSTFLD(pcsDispatch->GetToken(m_pFD));
1728 #endif // FEATURE_COMINTEROP
1731 NDirectStubLinker::NDirectStubLinker(
1734 const Signature &signature,
1735 SigTypeContext *pTypeContext,
1736 MethodDesc* pTargetMD,
1738 BOOL fTargetHasThis,
1740 : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags), SF_IsReverseStub(dwStubFlags)),
1741 m_pCleanupFinallyBeginLabel(NULL),
1742 m_pCleanupFinallyEndLabel(NULL),
1743 m_pSkipExceptionCleanupLabel(NULL),
1744 #ifdef FEATURE_COMINTEROP
1745 m_dwWinRTFactoryObjectLocalNum(-1),
1746 #endif // FEATURE_COMINTEROP
1747 m_fHasCleanupCode(FALSE),
1748 m_fHasExceptionCleanupCode(FALSE),
1749 m_fCleanupWorkListIsSetup(FALSE),
1750 m_targetHasThis(fTargetHasThis),
1751 m_dwThreadLocalNum(-1),
1752 m_dwCleanupWorkListLocalNum(-1),
1753 m_dwRetValLocalNum(-1),
1755 m_ErrorParamIdx(-1),
1756 m_iLCIDParamIdx(iLCIDParamIdx),
1757 m_dwStubFlags(dwStubFlags)
1759 STANDARD_VM_CONTRACT;
1762 m_pcsSetup = NewCodeStream(ILStubLinker::kSetup); // do any one-time setup work
1763 m_pcsMarshal = NewCodeStream(ILStubLinker::kMarshal); // marshals arguments
1764 m_pcsDispatch = NewCodeStream(ILStubLinker::kDispatch); // sets up arguments and makes call
1765 m_pcsRetUnmarshal = NewCodeStream(ILStubLinker::kReturnUnmarshal); // unmarshals return value
1766 m_pcsUnmarshal = NewCodeStream(ILStubLinker::kUnmarshal); // unmarshals arguments
1767 m_pcsExceptionCleanup = NewCodeStream(ILStubLinker::kExceptionCleanup); // MAY NOT THROW: goes in a finally and does exception-only cleanup
1768 m_pcsCleanup = NewCodeStream(ILStubLinker::kCleanup); // MAY NOT THROW: goes in a finally and does unconditional cleanup
1772 m_dwArgMarshalIndexLocalNum = NewLocal(ELEMENT_TYPE_I4);
1773 m_pcsMarshal->EmitLDC(0);
1774 m_pcsMarshal->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1776 #ifdef FEATURE_COMINTEROP
1778 // Forward COM interop needs a local to hold target interface pointer
1780 if (SF_IsForwardCOMStub(m_dwStubFlags))
1782 m_dwTargetEntryPointLocalNum = NewLocal(ELEMENT_TYPE_I);
1783 m_dwTargetInterfacePointerLocalNum = NewLocal(ELEMENT_TYPE_I);
1784 m_pcsSetup->EmitLoadNullPtr();
1785 m_pcsSetup->EmitSTLOC(m_dwTargetInterfacePointerLocalNum);
1787 #endif // FEATURE_COMINTEROP
1790 void NDirectStubLinker::SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL fIsVarArg)
1792 LIMITED_METHOD_CONTRACT;
1793 ULONG uNativeCallingConv = 0;
1795 #if !defined(_TARGET_X86_)
1798 // The JIT has to use a different calling convention for unmanaged vararg targets on 64-bit and ARM:
1799 // any float values must be duplicated in the corresponding general-purpose registers.
1800 uNativeCallingConv = CORINFO_CALLCONV_NATIVEVARARG;
1803 #endif // !_TARGET_X86_
1805 switch (unmngCallConv)
1807 case pmCallConvCdecl:
1808 uNativeCallingConv = CORINFO_CALLCONV_C;
1810 case pmCallConvStdcall:
1811 uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
1813 case pmCallConvThiscall:
1814 uNativeCallingConv = CORINFO_CALLCONV_THISCALL;
1817 _ASSERTE(!"Invalid calling convention.");
1818 uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
1823 SetStubTargetCallingConv((CorCallingConvention)uNativeCallingConv);
1826 void NDirectStubLinker::EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx)
1828 WRAPPER_NO_CONTRACT;
1831 // This sets our state local variable that tracks the progress of the stub execution.
1832 // In the finally block we test this variable to see what cleanup we need to do. The
1833 // variable starts with the value of 0 and is assigned the following values as the
1836 // CLEANUP_INDEX_ARG0_MARSHAL + 1 - 1st argument marshaled
1837 // CLEANUP_INDEX_ARG0_MARSHAL + 2 - 2nd argument marshaled
1839 // CLEANUP_INDEX_ARG0_MARSHAL + n - nth argument marshaled
1840 // CLEANUP_INDEX_RETVAL_UNMARSHAL + 1 - return value unmarshaled
1841 // CLEANUP_INDEX_ARG0_UNMARSHAL + 1 - 1st argument unmarshaled
1842 // CLEANUP_INDEX_ARG0_UNMARSHAL + 2 - 2nd argument unmarshaled
1844 // CLEANUP_INDEX_ARG0_UNMARSHAL + n - nth argument unmarshaled
1845 // CLEANUP_INDEX_ALL_DONE + 1 - ran to completion, no exception thrown
1847 // Note: There may be gaps, i.e. if say 2nd argument does not need cleanup, the
1848 // state variable will never be assigned the corresponding value. However, the
1849 // value must always monotonically increase so we can use <=, >, etc.
1852 pcsEmit->EmitLDC(uArgIdx + 1);
1853 pcsEmit->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1856 void NDirectStubLinker::EmitCheckForArgCleanup(ILCodeStream* pcsEmit, UINT uArgIdx, ArgCleanupBranchKind branchKind, ILCodeLabel* pSkipCleanupLabel)
1858 STANDARD_VM_CONTRACT;
1862 // See EmitSetArgMarshalIndex.
1863 pcsEmit->EmitLDLOC(m_dwArgMarshalIndexLocalNum);
1864 pcsEmit->EmitLDC(uArgIdx);
1868 case BranchIfMarshaled:
1870 // we branch to the label if the argument has been marshaled
1871 pcsEmit->EmitBGT(pSkipCleanupLabel);
1875 case BranchIfNotMarshaled:
1877 // we branch to the label if the argument has not been marshaled
1878 pcsEmit->EmitBLE(pSkipCleanupLabel);
1887 int NDirectStubLinker::GetLCIDParamIdx()
1889 LIMITED_METHOD_CONTRACT;
1890 return m_iLCIDParamIdx;
1893 ILCodeStream* NDirectStubLinker::GetSetupCodeStream()
1895 LIMITED_METHOD_CONTRACT;
1899 ILCodeStream* NDirectStubLinker::GetMarshalCodeStream()
1901 LIMITED_METHOD_CONTRACT;
1902 return m_pcsMarshal;
1905 ILCodeStream* NDirectStubLinker::GetUnmarshalCodeStream()
1907 LIMITED_METHOD_CONTRACT;
1908 return m_pcsUnmarshal;
1911 ILCodeStream* NDirectStubLinker::GetReturnUnmarshalCodeStream()
1913 LIMITED_METHOD_CONTRACT;
1914 return m_pcsRetUnmarshal;
1917 ILCodeStream* NDirectStubLinker::GetDispatchCodeStream()
1919 LIMITED_METHOD_CONTRACT;
1920 return m_pcsDispatch;
1923 ILCodeStream* NDirectStubLinker::GetCleanupCodeStream()
1925 LIMITED_METHOD_CONTRACT;
1926 return m_pcsCleanup;
1929 ILCodeStream* NDirectStubLinker::GetExceptionCleanupCodeStream()
1931 LIMITED_METHOD_CONTRACT;
1932 return m_pcsExceptionCleanup;
1935 void NDirectStubLinker::AdjustTargetStackDeltaForExtraParam()
1937 LIMITED_METHOD_CONTRACT;
1939 // Compensate for the extra parameter.
1941 m_iTargetStackDelta++;
1944 void NDirectStubLinker::AdjustTargetStackDeltaForReverseInteropHRESULTSwapping()
1946 WRAPPER_NO_CONTRACT;
1948 // In the case of reverse pinvoke, we build up the 'target'
1949 // signature as if it were normal forward pinvoke and then
1950 // switch that signature (representing the native sig) with
1951 // the stub's sig (representing the managed sig). However,
1952 // as a side-effect, our calcualted target stack delta is
1955 // The only way that we support a different stack delta is
1956 // through hresult swapping. So this code "undoes" the
1957 // deltas that would have been applied in that case.
1960 if (StubHasVoidReturnType())
1963 // If the managed return type is void, undo the HRESULT
1964 // return type added to our target sig for HRESULT swapping.
1965 // No extra argument will have been added because it makes
1966 // no sense to add an extry byref void argument.
1968 m_iTargetStackDelta--;
1973 // no longer pop the extra byref argument from the stack
1975 m_iTargetStackDelta++;
1979 void NDirectStubLinker::SetInteropParamExceptionInfo(UINT resID, UINT paramIdx)
1981 LIMITED_METHOD_CONTRACT;
1983 // only keep the first one
1984 if (HasInteropParamExceptionInfo())
1989 m_ErrorResID = resID;
1990 m_ErrorParamIdx = paramIdx;
1993 bool NDirectStubLinker::HasInteropParamExceptionInfo()
1995 LIMITED_METHOD_CONTRACT;
1997 return !(((DWORD)-1 == m_ErrorResID) && ((DWORD)-1 == m_ErrorParamIdx));
2000 void NDirectStubLinker::GenerateInteropParamException(ILCodeStream* pcsEmit)
2002 STANDARD_VM_CONTRACT;
2004 pcsEmit->EmitLDC(m_ErrorResID);
2005 pcsEmit->EmitLDC(m_ErrorParamIdx);
2006 pcsEmit->EmitCALL(METHOD__STUBHELPERS__THROW_INTEROP_PARAM_EXCEPTION, 2, 0);
2008 pcsEmit->EmitLDNULL();
2009 pcsEmit->EmitTHROW();
2012 #ifdef FEATURE_COMINTEROP
2013 DWORD NDirectStubLinker::GetTargetInterfacePointerLocalNum()
2015 LIMITED_METHOD_CONTRACT;
2016 CONSISTENCY_CHECK(m_dwTargetInterfacePointerLocalNum != (DWORD)-1);
2017 return m_dwTargetInterfacePointerLocalNum;
2019 DWORD NDirectStubLinker::GetTargetEntryPointLocalNum()
2021 LIMITED_METHOD_CONTRACT;
2022 CONSISTENCY_CHECK(m_dwTargetEntryPointLocalNum != (DWORD)-1);
2023 return m_dwTargetEntryPointLocalNum;
2026 void NDirectStubLinker::EmitLoadRCWThis(ILCodeStream *pcsEmit, DWORD dwStubFlags)
2028 STANDARD_VM_CONTRACT;
2030 if (SF_IsForwardStub(dwStubFlags) &&
2031 (SF_IsWinRTCtorStub(dwStubFlags) || SF_IsWinRTStaticStub(dwStubFlags)))
2033 // WinRT ctor/static stubs make the call on the factory object instead of 'this'
2034 if (m_dwWinRTFactoryObjectLocalNum == (DWORD)-1)
2036 m_dwWinRTFactoryObjectLocalNum = NewLocal(ELEMENT_TYPE_OBJECT);
2038 // get the factory object
2039 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2040 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_OBJECT, 1, 1);
2041 m_pcsSetup->EmitSTLOC(m_dwWinRTFactoryObjectLocalNum);
2044 pcsEmit->EmitLDLOC(m_dwWinRTFactoryObjectLocalNum);
2048 pcsEmit->EmitLoadThis();
2051 #endif // FEATURE_COMINTEROP
2053 DWORD NDirectStubLinker::GetCleanupWorkListLocalNum()
2055 LIMITED_METHOD_CONTRACT;
2056 CONSISTENCY_CHECK(m_dwCleanupWorkListLocalNum != (DWORD)-1);
2057 return m_dwCleanupWorkListLocalNum;
2060 DWORD NDirectStubLinker::GetThreadLocalNum()
2062 STANDARD_VM_CONTRACT;
2064 if (m_dwThreadLocalNum == (DWORD)-1)
2066 // The local is created and initialized lazily when first asked.
2067 m_dwThreadLocalNum = NewLocal(ELEMENT_TYPE_I);
2068 m_pcsSetup->EmitCALL(METHOD__THREAD__INTERNAL_GET_CURRENT_THREAD, 0, 1);
2069 m_pcsSetup->EmitSTLOC(m_dwThreadLocalNum);
2072 return m_dwThreadLocalNum;
2075 DWORD NDirectStubLinker::GetReturnValueLocalNum()
2077 LIMITED_METHOD_CONTRACT;
2078 return m_dwRetValLocalNum;
2081 BOOL NDirectStubLinker::IsCleanupNeeded()
2083 LIMITED_METHOD_CONTRACT;
2085 return (m_fHasCleanupCode || IsCleanupWorkListSetup());
2088 BOOL NDirectStubLinker::IsExceptionCleanupNeeded()
2090 LIMITED_METHOD_CONTRACT;
2092 return m_fHasExceptionCleanupCode;
2095 void NDirectStubLinker::InitCleanupCode()
2100 PRECONDITION(NULL == m_pCleanupFinallyBeginLabel);
2104 m_pCleanupFinallyBeginLabel = NewCodeLabel();
2105 m_pcsExceptionCleanup->EmitLabel(m_pCleanupFinallyBeginLabel);
2108 void NDirectStubLinker::InitExceptionCleanupCode()
2113 PRECONDITION(NULL == m_pSkipExceptionCleanupLabel);
2119 // we want to skip the entire exception cleanup if no exception has been thrown
2120 m_pSkipExceptionCleanupLabel = NewCodeLabel();
2121 EmitCheckForArgCleanup(m_pcsExceptionCleanup, CLEANUP_INDEX_ALL_DONE, BranchIfMarshaled, m_pSkipExceptionCleanupLabel);
2124 void NDirectStubLinker::SetCleanupNeeded()
2126 WRAPPER_NO_CONTRACT;
2128 if (!m_fHasCleanupCode)
2130 m_fHasCleanupCode = TRUE;
2135 void NDirectStubLinker::SetExceptionCleanupNeeded()
2137 WRAPPER_NO_CONTRACT;
2139 if (!m_fHasExceptionCleanupCode)
2141 m_fHasExceptionCleanupCode = TRUE;
2142 InitExceptionCleanupCode();
2146 void NDirectStubLinker::NeedsCleanupList()
2148 STANDARD_VM_CONTRACT;
2150 if (!IsCleanupWorkListSetup())
2152 m_fCleanupWorkListIsSetup = TRUE;
2155 // we setup a new local that will hold the cleanup work list
2156 LocalDesc desc(MscorlibBinder::GetClass(CLASS__CLEANUP_WORK_LIST_ELEMENT));
2157 m_dwCleanupWorkListLocalNum = NewLocal(desc);
2162 BOOL NDirectStubLinker::IsCleanupWorkListSetup ()
2164 LIMITED_METHOD_CONTRACT;
2166 return m_fCleanupWorkListIsSetup;
2170 void NDirectStubLinker::LoadCleanupWorkList(ILCodeStream* pcsEmit)
2172 STANDARD_VM_CONTRACT;
2175 pcsEmit->EmitLDLOCA(GetCleanupWorkListLocalNum());
2179 void NDirectStubLinker::Begin(DWORD dwStubFlags)
2181 STANDARD_VM_CONTRACT;
2183 #ifdef FEATURE_COMINTEROP
2184 if (SF_IsWinRTHasRedirection(dwStubFlags))
2186 _ASSERTE(SF_IsForwardCOMStub(dwStubFlags));
2188 // The very first thing we need to do is check whether the call should be routed to
2189 // the marshaling stub for the corresponding projected WinRT interface. If so, we
2191 m_pcsSetup->EmitLoadThis();
2192 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2193 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__SHOULD_CALL_WINRT_INTERFACE, 2, 1);
2195 ILCodeLabel *pNoRedirection = m_pcsSetup->NewCodeLabel();
2196 m_pcsSetup->EmitBRFALSE(pNoRedirection);
2198 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(
2200 TypeHandle::Interop_ManagedToNative);
2202 CONSISTENCY_CHECK(pAdapterMD != NULL && !pAdapterMD->HasMethodInstantiation());
2204 m_pcsSetup->EmitJMP(m_pcsSetup->GetToken(pAdapterMD));
2206 m_pcsSetup->EmitLabel(pNoRedirection);
2208 #endif // FEATURE_COMINTEROP
2210 if (SF_IsForwardStub(dwStubFlags))
2213 if (SF_IsStubWithCctorTrigger(dwStubFlags))
2215 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2216 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__INIT_DECLARING_TYPE, 1, 0);
2221 #ifdef MDA_SUPPORTED
2222 if (!SF_IsNGENedStub(dwStubFlags) && MDA_GET_ASSISTANT(GcUnmanagedToManaged))
2224 EmitCallGcCollectForMDA(m_pcsSetup, dwStubFlags);
2226 #endif // MDA_SUPPORTED
2228 if (SF_IsDelegateStub(dwStubFlags))
2230 #if defined(MDA_SUPPORTED)
2231 // GC was induced (gcUnmanagedToManagedMDA), arguments have been marshaled, and we are about
2232 // to touch the UMEntryThunk and extract the delegate target from it so this is the right time
2233 // to do the collected delegate MDA check.
2235 // The call to CheckCollectedDelegateMDA is emitted regardless of whether the MDA is on at the
2236 // moment. This is to avoid having to ignore NGENed stubs without the call just as we do for
2237 // the GC MDA (callbackOncollectedDelegateMDA is turned on under managed debugger by default
2238 // so the impact would be substantial). The helper bails out fast if the MDA is not enabled.
2239 EmitLoadStubContext(m_pcsDispatch, dwStubFlags);
2240 m_pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CHECK_COLLECTED_DELEGATE_MDA, 1, 0);
2241 #endif // MDA_SUPPORTED
2244 // recover delegate object from UMEntryThunk
2246 EmitLoadStubContext(m_pcsDispatch, dwStubFlags); // load UMEntryThunk*
2248 m_pcsDispatch->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2249 m_pcsDispatch->EmitADD();
2250 m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE
2251 m_pcsDispatch->EmitLDIND_REF(); // get Delegate object
2252 m_pcsDispatch->EmitLDFLD(GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__TARGET)));
2256 m_pCleanupTryBeginLabel = NewCodeLabel();
2257 m_pcsMarshal->EmitLabel(m_pCleanupTryBeginLabel);
2260 void NDirectStubLinker::End(DWORD dwStubFlags)
2262 STANDARD_VM_CONTRACT;
2264 ILCodeStream* pcs = m_pcsUnmarshal;
2266 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(dwStubFlags)
2267 && !SF_IsFieldGetterStub(dwStubFlags)
2268 && !SF_IsFieldSetterStub(dwStubFlags);
2271 // Create a local for the return value and store the return value in it.
2273 if (IsCleanupNeeded() || hasTryCatchForHRESULT)
2275 // Save the return value if necessary, since the IL stack will be emptied when we leave a try block.
2276 LocalDesc locDescRetVal;
2277 if (SF_IsForwardStub(dwStubFlags))
2279 GetStubReturnType(&locDescRetVal);
2283 GetStubTargetReturnType(&locDescRetVal);
2286 if (!( (locDescRetVal.cbType == 1) && (locDescRetVal.ElementType[0] == ELEMENT_TYPE_VOID) ))
2288 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2289 if (SF_IsReverseStub(dwStubFlags) && StubHasVoidReturnType())
2291 // if the target returns void and we are doing HRESULT swapping, S_OK is loaded
2292 // in the unmarshal stream
2293 m_pcsUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2297 // otherwise the return value is loaded in the return unmarshal stream
2298 m_pcsRetUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2301 else if (hasTryCatchForHRESULT && (locDescRetVal.ElementType[0] != ELEMENT_TYPE_VOID))
2303 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2308 // Emit end-of-try and end-of-finally code for the try/finally
2310 if (IsCleanupNeeded())
2312 m_pCleanupFinallyEndLabel = NewCodeLabel();
2313 m_pCleanupTryEndLabel = NewCodeLabel();
2315 if (IsExceptionCleanupNeeded())
2317 // if we made it here, no exception has been thrown
2318 EmitSetArgMarshalIndex(m_pcsUnmarshal, CLEANUP_INDEX_ALL_DONE);
2321 // Emit a leave at the end of the try block. If we have an outer try/catch, we need
2322 // to leave to the beginning of the ExceptionHandler code stream, which follows the
2323 // Cleanup code stream. If we don't, we can just leave to the tail end of the
2324 // Unmarshal code stream where we'll emit our RET.
2326 ILCodeLabel* pLeaveTarget = m_pCleanupTryEndLabel;
2327 if (hasTryCatchForHRESULT)
2329 pLeaveTarget = m_pCleanupFinallyEndLabel;
2332 m_pcsUnmarshal->EmitLEAVE(pLeaveTarget);
2333 m_pcsUnmarshal->EmitLabel(m_pCleanupTryEndLabel);
2335 // Emit a call to destroy the clean-up list if needed.
2336 if (IsCleanupWorkListSetup())
2338 LoadCleanupWorkList(m_pcsCleanup);
2339 m_pcsCleanup->EmitCALL(METHOD__STUBHELPERS__DESTROY_CLEANUP_LIST, 1, 0);
2342 // Emit the endfinally.
2343 m_pcsCleanup->EmitENDFINALLY();
2344 m_pcsCleanup->EmitLabel(m_pCleanupFinallyEndLabel);
2347 #ifdef MDA_SUPPORTED
2348 if (SF_IsReverseStub(dwStubFlags) && !SF_IsNGENedStub(dwStubFlags) &&
2349 MDA_GET_ASSISTANT(GcManagedToUnmanaged))
2351 EmitCallGcCollectForMDA(pcs, dwStubFlags);
2353 #endif // MDA_SUPPORTED
2355 if (IsExceptionCleanupNeeded())
2357 m_pcsExceptionCleanup->EmitLabel(m_pSkipExceptionCleanupLabel);
2360 // Reload the return value
2361 if ((m_dwRetValLocalNum != (DWORD)-1) && !hasTryCatchForHRESULT)
2363 pcs->EmitLDLOC(m_dwRetValLocalNum);
2367 void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD)
2369 STANDARD_VM_CONTRACT;
2370 if (SF_IsForwardStub(dwStubFlags)) // managed-to-native
2373 if (SF_IsDelegateStub(dwStubFlags)) // delegate invocation
2375 // get the delegate unmanaged target - we call a helper instead of just grabbing
2376 // the _methodPtrAux field because we may need to intercept the call for host, MDA, etc.
2377 pcsEmit->EmitLoadThis();
2378 #ifdef _TARGET_64BIT_
2379 // on AMD64 GetDelegateTarget will return address of the generic stub for host when we are hosted
2380 // and update the secret argument with real target - the secret arg will be embedded in the
2381 // InlinedCallFrame by the JIT and fetched via TLS->Thread->Frame->Datum by the stub for host
2382 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
2383 #else // !_TARGET_64BIT_
2384 // we don't need to do this on x86 because stub for host is generated dynamically per target
2385 pcsEmit->EmitLDNULL();
2386 #endif // !_TARGET_64BIT_
2387 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 2, 1);
2389 else // direct invocation
2391 if (SF_IsCALLIStub(dwStubFlags)) // unmanaged CALLI
2393 // if we ever NGEN CALLI stubs, this would have to be done differently
2394 _ASSERTE(!SF_IsNGENedStub(dwStubFlags));
2396 // for managed-to-unmanaged CALLI that requires marshaling, the target is passed
2397 // as the secret argument to the stub by GenericPInvokeCalliHelper (asmhelpers.asm)
2398 EmitLoadStubContext(pcsEmit, dwStubFlags);
2400 // the secret arg has been shifted to left and ORed with 1 (see code:GenericPInvokeCalliHelper)
2401 pcsEmit->EmitLDC(1);
2402 pcsEmit->EmitSHR_UN();
2406 #ifdef FEATURE_COMINTEROP
2407 if (!SF_IsCOMStub(dwStubFlags)) // forward P/Invoke
2408 #endif // FEATURE_COMINTEROP
2410 EmitLoadStubContext(pcsEmit, dwStubFlags);
2413 // Perf: inline the helper for now
2414 //pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_NDIRECT_TARGET, 1, 1);
2415 pcsEmit->EmitLDC(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
2418 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2423 pcsEmit->EmitLDIND_I();
2425 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2430 pcsEmit->EmitLDIND_I();
2433 #ifdef FEATURE_COMINTEROP
2436 // this is a CLR -> COM call
2437 // the target has been computed by StubHelpers::GetCOMIPFromRCW
2438 pcsEmit->EmitLDLOC(m_dwTargetEntryPointLocalNum);
2440 #endif // FEATURE_COMINTEROP
2443 else // native-to-managed
2445 if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate
2447 int tokDelegate_methodPtr = pcsEmit->GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__METHOD_PTR));
2449 EmitLoadStubContext(pcsEmit, dwStubFlags);
2450 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2452 pcsEmit->EmitLDIND_I(); // Get OBJECTHANDLE
2453 pcsEmit->EmitLDIND_REF(); // Get Delegate object
2454 pcsEmit->EmitLDFLD(tokDelegate_methodPtr); // get _methodPtr
2456 #ifdef FEATURE_COMINTEROP
2457 else if (SF_IsCOMStub(dwStubFlags)) // COM -> CLR call
2459 // managed target is passed directly in the secret argument
2460 EmitLoadStubContext(pcsEmit, dwStubFlags);
2462 #endif // FEATURE_COMINTEROP
2463 else // direct reverse P/Invoke (CoreCLR hosting)
2465 EmitLoadStubContext(pcsEmit, dwStubFlags);
2466 CONSISTENCY_CHECK(0 == offsetof(UMEntryThunk, m_pManagedTarget)); // if this changes, just add back the EmitLDC/EmitADD below
2467 // pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pManagedTarget));
2468 // pcsEmit->EmitADD();
2469 pcsEmit->EmitLDIND_I(); // Get UMEntryThunk::m_pManagedTarget
2473 // For managed-to-native calls, the rest of the work is done by the JIT. It will
2474 // erect InlinedCallFrame, flip GC mode, and use the specified calling convention
2475 // to call the target. For native-to-managed calls, this is an ordinary managed
2476 // CALLI and nothing special happens.
2477 pcsEmit->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, 0, m_iTargetStackDelta);
2480 void NDirectStubLinker::EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwPinnedLocal)
2482 STANDARD_VM_CONTRACT;
2484 if (SF_IsForwardPInvokeStub(m_dwStubFlags) && !SF_IsForwardDelegateStub(m_dwStubFlags))
2486 // get the secret argument via intrinsic
2487 pslILEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2491 // no secret argument
2492 pslILEmit->EmitLoadNullPtr();
2495 pslILEmit->EmitLDLOC(dwPinnedLocal);
2497 pslILEmit->EmitCALL(METHOD__STUBHELPERS__LOG_PINNED_ARGUMENT, 2, 0);
2500 void NDirectStubLinker::GetCleanupFinallyOffsets(ILStubEHClause * pClause)
2505 PRECONDITION(CheckPointer(pClause));
2509 if (m_pCleanupFinallyEndLabel)
2511 _ASSERTE(m_pCleanupFinallyBeginLabel);
2512 _ASSERTE(m_pCleanupTryBeginLabel);
2513 _ASSERTE(m_pCleanupTryEndLabel);
2515 pClause->kind = ILStubEHClause::kFinally;
2516 pClause->dwTryBeginOffset = (DWORD)m_pCleanupTryBeginLabel->GetCodeOffset();
2517 pClause->cbTryLength = (DWORD)m_pCleanupTryEndLabel->GetCodeOffset() - pClause->dwTryBeginOffset;
2518 pClause->dwHandlerBeginOffset = (DWORD)m_pCleanupFinallyBeginLabel->GetCodeOffset();
2519 pClause->cbHandlerLength = (DWORD)m_pCleanupFinallyEndLabel->GetCodeOffset() - pClause->dwHandlerBeginOffset;
2523 void NDirectStubLinker::ClearCode()
2525 WRAPPER_NO_CONTRACT;
2526 ILStubLinker::ClearCode();
2528 m_pCleanupTryBeginLabel = 0;
2529 m_pCleanupTryEndLabel = 0;
2530 m_pCleanupFinallyBeginLabel = 0;
2531 m_pCleanupFinallyEndLabel = 0;
2534 #ifdef PROFILING_SUPPORTED
2535 DWORD NDirectStubLinker::EmitProfilerBeginTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2537 STANDARD_VM_CONTRACT;
2539 if (SF_IsForwardDelegateStub(dwStubFlags) || SF_IsCALLIStub(dwStubFlags))
2541 // secret argument does not contain MD nor UMEntryThunk
2542 pcsEmit->EmitLoadNullPtr();
2546 EmitLoadStubContext(pcsEmit, dwStubFlags);
2549 if (SF_IsForwardStub(dwStubFlags))
2551 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2555 // we use a null pThread to indicate reverse interop
2556 pcsEmit->EmitLDC(NULL);
2559 // In the unmanaged delegate case, we need the "this" object to retrieve the MD
2560 // in StubHelpers::ProfilerEnterCallback().
2561 if (SF_IsDelegateStub(dwStubFlags))
2563 if (SF_IsForwardStub(dwStubFlags))
2565 pcsEmit->EmitLoadThis();
2569 EmitLoadStubContext(pcsEmit, dwStubFlags); // load UMEntryThunk*
2570 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2572 pcsEmit->EmitLDIND_I(); // get OBJECTHANDLE
2573 pcsEmit->EmitLDIND_REF(); // get Delegate object
2578 pcsEmit->EmitLDC(NULL);
2580 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_BEGIN_TRANSITION_CALLBACK, 3, 1);
2582 // Store the MD for StubHelpers::ProfilerLeaveCallback().
2583 DWORD dwMethodDescLocalNum = pcsEmit->NewLocal(ELEMENT_TYPE_I);
2584 pcsEmit->EmitSTLOC(dwMethodDescLocalNum);
2585 return dwMethodDescLocalNum;
2588 void NDirectStubLinker::EmitProfilerEndTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags, DWORD dwMethodDescLocalNum)
2590 STANDARD_VM_CONTRACT;
2592 pcsEmit->EmitLDLOC(dwMethodDescLocalNum);
2593 if (SF_IsReverseStub(dwStubFlags))
2595 // we use a null pThread to indicate reverse interop
2596 pcsEmit->EmitLDC(NULL);
2600 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2602 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_END_TRANSITION_CALLBACK, 2, 0);
2604 #endif // PROFILING_SUPPPORTED
2607 void NDirectStubLinker::EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNum, bool fIsByref, DWORD dwStubFlags)
2609 STANDARD_VM_CONTRACT;
2611 pcsEmit->EmitLDLOC(dwLocalNum);
2613 if (SF_IsDelegateStub(dwStubFlags))
2615 pcsEmit->EmitLoadNullPtr();
2616 pcsEmit->EmitLoadThis();
2618 else if (SF_IsCALLIStub(dwStubFlags))
2620 pcsEmit->EmitLoadNullPtr();
2621 pcsEmit->EmitLDNULL();
2625 // P/Invoke, CLR->COM
2626 EmitLoadStubContext(pcsEmit, dwStubFlags);
2627 pcsEmit->EmitLDNULL();
2632 // StubHelpers.ValidateByref(byref, pMD, pThis)
2633 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 3, 0);
2637 // StubHelpers.ValidateObject(obj, pMD, pThis)
2638 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 3, 0);
2642 void NDirectStubLinker::EmitObjectValidation(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2644 STANDARD_VM_CONTRACT;
2646 // generate validation callouts for pinned locals
2647 CQuickBytes qbLocalSig;
2648 DWORD cbSig = GetLocalSigSize();
2650 qbLocalSig.AllocThrows(cbSig);
2651 PCOR_SIGNATURE pSig = (PCOR_SIGNATURE)qbLocalSig.Ptr();
2653 GetLocalSig(pSig, cbSig);
2654 SigPointer ptr(pSig, cbSig);
2656 IfFailThrow(ptr.GetData(NULL)); // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
2659 IfFailThrow(ptr.GetData(&numLocals));
2661 for (ULONG i = 0; i < numLocals; i++)
2664 IfFailThrow(ptr.PeekByte(&modifier));
2665 if (modifier == ELEMENT_TYPE_PINNED)
2667 IfFailThrow(ptr.GetByte(NULL));
2668 IfFailThrow(ptr.PeekByte(&modifier));
2669 EmitValidateLocal(pcsEmit, i, (modifier == ELEMENT_TYPE_BYREF), dwStubFlags);
2672 IfFailThrow(ptr.SkipExactlyOne());
2675 #endif // VERIFY_HEAP
2677 // Loads the 'secret argument' passed to the stub.
2678 void NDirectStubLinker::EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2680 STANDARD_VM_CONTRACT;
2682 CONSISTENCY_CHECK(!SF_IsForwardDelegateStub(dwStubFlags));
2683 CONSISTENCY_CHECK(!SF_IsFieldGetterStub(dwStubFlags) && !SF_IsFieldSetterStub(dwStubFlags));
2685 #ifdef FEATURE_COMINTEROP
2686 if (SF_IsWinRTDelegateStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags))
2688 // we have the delegate 'this' but we need the EEImpl/Instantiated 'Invoke' MD pointer
2689 // (Delegate.GetInvokeMethod does not return exact instantiated MD so we call our own helper)
2690 pcsEmit->EmitLoadThis();
2691 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_INVOKE_METHOD, 1, 1);
2694 #endif // FEATURE_COMINTEROP
2696 // get the secret argument via intrinsic
2697 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2701 #ifdef MDA_SUPPORTED
2702 void NDirectStubLinker::EmitCallGcCollectForMDA(ILCodeStream *pcsEmit, DWORD dwStubFlags)
2704 STANDARD_VM_CONTRACT;
2706 ILCodeLabel *pSkipGcLabel = NULL;
2708 if (SF_IsForwardPInvokeStub(dwStubFlags) &&
2709 !SF_IsDelegateStub(dwStubFlags) &&
2710 !SF_IsCALLIStub(dwStubFlags))
2712 // don't call GC if this is a QCall
2713 EmitLoadStubContext(pcsEmit, dwStubFlags);
2714 pcsEmit->EmitCALL(METHOD__STUBHELPERS__IS_QCALL, 1, 1);
2716 pSkipGcLabel = pcsEmit->NewCodeLabel();
2717 pcsEmit->EmitBRTRUE(pSkipGcLabel);
2720 pcsEmit->EmitCALL(METHOD__STUBHELPERS__TRIGGER_GC_FOR_MDA, 0, 0);
2722 if (pSkipGcLabel != NULL)
2724 pcsEmit->EmitLabel(pSkipGcLabel);
2727 #endif // MDA_SUPPORTED
2729 #ifdef FEATURE_COMINTEROP
2731 class DispatchStubState : public StubState // For CLR-to-COM late-bound/eventing calls
2738 WRAPPER_NO_CONTRACT;
2741 void SetLastError(BOOL fSetLastError)
2743 LIMITED_METHOD_CONTRACT;
2745 CONSISTENCY_CHECK(!fSetLastError);
2748 void BeginEmit(DWORD dwStubFlags)
2750 LIMITED_METHOD_CONTRACT;
2752 CONSISTENCY_CHECK(SF_IsCOMStub(dwStubFlags));
2753 m_dwStubFlags = dwStubFlags;
2756 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
2762 PRECONDITION(CheckPointer(pInfo));
2767 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
2772 PRECONDITION(CheckPointer(pInfo));
2776 if (SF_IsCOMLateBoundStub(m_dwStubFlags) && pInfo->GetDispWrapperType() != 0)
2778 m_lateBoundFlags |= ComPlusCallInfo::kRequiresArgumentWrapping;
2782 void MarshalLCID(int argIdx)
2784 LIMITED_METHOD_CONTRACT;
2787 #ifdef FEATURE_COMINTEROP
2788 void MarshalHiddenLengthArgument(MarshalInfo *, BOOL)
2790 LIMITED_METHOD_CONTRACT;
2792 void MarshalFactoryReturn()
2794 LIMITED_METHOD_CONTRACT;
2797 #endif // FEATURE_COMINTEROP
2799 void EmitInvokeTarget(MethodDesc *pStubMD)
2801 LIMITED_METHOD_CONTRACT;
2802 UNREACHABLE_MSG("Should never come to DispatchStubState::EmitInvokeTarget");
2805 void FinishEmit(MethodDesc *pMD)
2807 STANDARD_VM_CONTRACT;
2809 // set flags directly on the interop MD
2810 _ASSERTE(pMD->IsComPlusCall());
2812 ((ComPlusCallMethodDesc *)pMD)->SetLateBoundFlags(m_lateBoundFlags);
2816 DWORD m_dwStubFlags;
2817 BYTE m_lateBoundFlags; // ComPlusCallMethodDesc::Flags
2820 #endif // FEATURE_COMINTEROP
2823 void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)
2833 // initialize data members
2835 m_pModule = pModule;
2836 m_callConv = (CorPinvokeMap)0;
2837 SetBestFitMapping (TRUE);
2838 SetThrowOnUnmappableChar (FALSE);
2839 SetLinkFlags (nlfNone);
2840 SetCharSet (nltAnsi);
2843 // assembly/type level m_bestFit & m_bThrowOnUnmappableChar
2845 BOOL bThrowOnUnmappableChar;
2849 EEClass::GetBestFitMapping(pMT, &bBestFit, &bThrowOnUnmappableChar);
2853 ReadBestFitCustomAttribute(m_pModule->GetMDImport(), mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
2856 SetBestFitMapping (bBestFit);
2857 SetThrowOnUnmappableChar (bThrowOnUnmappableChar);
2860 void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
2870 PreInit(pMD->GetModule(), pMD->GetMethodTable());
2871 SetIsStatic (pMD->IsStatic());
2872 m_sig = pMD->GetSignature();
2873 if (pMD->IsEEImpl())
2875 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
2876 SetIsDelegateInterop(TRUE);
2880 PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2881 MethodDesc* pMD, LPCUTF8 *pLibName, LPCUTF8 *pEntryPointName, ThrowOnError throwOnError)
2891 DllImportInit(pMD, pLibName, pEntryPointName);
2897 PInvokeStaticSigInfo::PInvokeStaticSigInfo(MethodDesc* pMD, ThrowOnError throwOnError)
2905 PRECONDITION(CheckPointer(pMD));
2911 MethodTable * pMT = pMD->GetMethodTable();
2913 if (!pMT->IsDelegate())
2915 DllImportInit(pMD, NULL, NULL);
2919 // initialize data members to defaults
2922 // System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
2925 CorPinvokeMap callConv = (CorPinvokeMap)0;
2927 HRESULT hRESULT = pMT->GetMDImport()->GetCustomAttributeByName(
2928 pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, (const VOID **)(&pData), (ULONG *)&cData);
2929 IfFailThrow(hRESULT);
2932 CustomAttributeParser ca(pData, cData);
2935 args[0].InitEnum(SERIALIZATION_TYPE_I4, (ULONG)m_callConv);
2937 IfFailGo(ParseKnownCaArgs(ca, args, lengthof(args)));
2939 enum UnmanagedFunctionPointerNamedArgs
2943 MDA_ThrowOnUnmappableChar,
2948 CaNamedArg namedArgs[MDA_Last];
2949 namedArgs[MDA_CharSet].InitI4FieldEnum("CharSet", "System.Runtime.InteropServices.CharSet", (ULONG)GetCharSet());
2950 namedArgs[MDA_BestFitMapping].InitBoolField("BestFitMapping", (ULONG)GetBestFitMapping());
2951 namedArgs[MDA_ThrowOnUnmappableChar].InitBoolField("ThrowOnUnmappableChar", (ULONG)GetThrowOnUnmappableChar());
2952 namedArgs[MDA_SetLastError].InitBoolField("SetLastError", 0);
2954 IfFailGo(ParseKnownCaNamedArgs(ca, namedArgs, lengthof(namedArgs)));
2956 callConv = (CorPinvokeMap)(args[0].val.u4 << 8);
2957 CorNativeLinkType nlt = (CorNativeLinkType)0;
2959 // XXX Tue 07/19/2005
2960 // Keep in sync with the handling of CorPInvokeMap in
2961 // PInvokeStaticSigInfo::DllImportInit.
2962 switch( namedArgs[MDA_CharSet].val.u4 )
2966 nlt = nltAnsi; break;
2968 case nltAuto: // Since Win9x isn't supported anymore, nltAuto always represents unicode strings.
2969 nlt = nltUnicode; break;
2971 hr = E_FAIL; goto ErrExit;
2974 SetBestFitMapping (namedArgs[MDA_BestFitMapping].val.u1);
2975 SetThrowOnUnmappableChar (namedArgs[MDA_ThrowOnUnmappableChar].val.u1);
2976 if (namedArgs[MDA_SetLastError].val.u1)
2977 SetLinkFlags ((CorNativeLinkFlags)(nlfLastError | GetLinkFlags()));
2983 SetError(IDS_EE_NDIRECT_BADNATL);
2985 InitCallConv(callConv, pMD->IsVarArg());
2991 PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2992 Signature sig, Module* pModule, ThrowOnError throwOnError)
3000 PRECONDITION(CheckPointer(pModule));
3004 PreInit(pModule, NULL);
3006 SetIsStatic (!(MetaSig::GetCallingConvention(pModule, sig) & IMAGE_CEE_CS_CALLCONV_HASTHIS));
3007 InitCallConv((CorPinvokeMap)0, FALSE);
3013 void PInvokeStaticSigInfo::DllImportInit(MethodDesc* pMD, LPCUTF8 *ppLibName, LPCUTF8 *ppEntryPointName)
3021 PRECONDITION(CheckPointer(pMD));
3023 // These preconditions to prevent multithreaded regression
3024 // where pMD->ndirect.m_szLibName was passed in directly, cleared
3025 // by this API, then accessed on another thread before being reset here.
3026 PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
3027 PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
3031 // initialize data members to defaults
3034 // System.Runtime.InteropServices.DllImportAttribute
3035 IMDInternalImport *pInternalImport = pMD->GetMDImport();
3036 CorPinvokeMap mappingFlags = pmMaxValue;
3037 mdModuleRef modref = mdModuleRefNil;
3038 if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
3040 #if !defined(CROSSGEN_COMPILE) // IJW
3041 // The guessing heuristic has been broken with NGen for a long time since we stopped loading
3042 // images at NGen time using full LoadLibrary. The DLL references are not resolved correctly
3043 // without full LoadLibrary.
3045 // Disable the heuristic consistently during NGen so that it does not kick in by accident.
3046 if (!IsCompilationProcess())
3047 BestGuessNDirectDefaults(pMD);
3050 InitCallConv((CorPinvokeMap)0, pMD->IsVarArg());
3054 // out parameter pEntryPointName
3055 if (ppEntryPointName && *ppEntryPointName == NULL)
3056 *ppEntryPointName = pMD->GetName();
3058 // out parameter pLibName
3059 if (ppLibName != NULL)
3061 if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
3063 SetError(IDS_CLASSLOAD_BADFORMAT);
3069 InitCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask), pMD->IsVarArg());
3072 CorPinvokeMap bestFitMask = (CorPinvokeMap)(mappingFlags & pmBestFitMask);
3073 if (bestFitMask == pmBestFitEnabled)
3074 SetBestFitMapping (TRUE);
3075 else if (bestFitMask == pmBestFitDisabled)
3076 SetBestFitMapping (FALSE);
3078 // m_bThrowOnUnmappableChar
3079 CorPinvokeMap unmappableMask = (CorPinvokeMap)(mappingFlags & pmThrowOnUnmappableCharMask);
3080 if (unmappableMask == pmThrowOnUnmappableCharEnabled)
3081 SetThrowOnUnmappableChar (TRUE);
3082 else if (unmappableMask == pmThrowOnUnmappableCharDisabled)
3083 SetThrowOnUnmappableChar (FALSE);
3085 // inkFlags : CorPinvoke -> CorNativeLinkFlags
3086 if (mappingFlags & pmSupportsLastError)
3087 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfLastError));
3088 if (mappingFlags & pmNoMangle)
3089 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
3091 // XXX Tue 07/19/2005
3092 // Keep in sync with the handling of CorNativeLinkType in
3093 // PInvokeStaticSigInfo::PInvokeStaticSigInfo.
3095 // charset : CorPinvoke -> CorNativeLinkType
3096 CorPinvokeMap charSetMask = (CorPinvokeMap)(mappingFlags & (pmCharSetNotSpec | pmCharSetAnsi | pmCharSetUnicode | pmCharSetAuto));
3097 if (charSetMask == pmCharSetNotSpec || charSetMask == pmCharSetAnsi)
3099 SetCharSet (nltAnsi);
3101 else if (charSetMask == pmCharSetUnicode || charSetMask == pmCharSetAuto)
3103 // Since Win9x isn't supported anymore, pmCharSetAuto always represents unicode strings.
3104 SetCharSet (nltUnicode);
3108 SetError(IDS_EE_NDIRECT_BADNATL);
3112 #if !defined(CROSSGEN_COMPILE) // IJW
3114 // This function would work, but be unused on Unix. Ifdefing out to avoid build errors due to the unused function.
3115 #if !defined (FEATURE_PAL)
3116 static LPBYTE FollowIndirect(LPBYTE pTarget)
3123 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
3131 AVInRuntimeImplOkayHolder AVOkay;
3134 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3136 pRet = **(LPBYTE**)(pTarget + 2);
3138 #elif defined(_TARGET_AMD64_)
3139 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3141 INT64 rva = *(INT32*)(pTarget + 2);
3142 pRet = *(LPBYTE*)(pTarget + 6 + rva);
3150 EX_END_CATCH(SwallowAllExceptions);
3154 #endif // !FEATURE_PAL
3156 BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget)
3166 #if !defined(FEATURE_PAL)
3167 static LPBYTE pGetLastError = NULL;
3170 // No need to use a holder here, since no cleanup is necessary.
3171 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
3174 pGetLastError = (LPBYTE)GetProcAddress(hMod, "GetLastError");
3177 // This should never happen but better to be cautious.
3178 pGetLastError = (LPBYTE)-1;
3183 // We failed to get the module handle for kernel32.dll. This is almost impossible
3184 // however better to err on the side of caution.
3185 pGetLastError = (LPBYTE)-1;
3189 if (pTarget == pGetLastError)
3192 if (pTarget == NULL)
3195 LPBYTE pTarget2 = FollowIndirect(pTarget);
3198 // jmp [xxxx] - could be an import thunk
3199 return pTarget2 == pGetLastError;
3201 #endif // !FEATURE_PAL
3206 DWORD STDMETHODCALLTYPE FalseGetLastError()
3208 WRAPPER_NO_CONTRACT;
3210 return GetThread()->m_dwLastError;
3213 void PInvokeStaticSigInfo::BestGuessNDirectDefaults(MethodDesc* pMD)
3223 if (!pMD->IsNDirect())
3226 NDirectMethodDesc* pMDD = (NDirectMethodDesc*)pMD;
3228 if (!pMDD->IsEarlyBound())
3231 LPVOID pTarget = NULL;
3233 // NOTE: If we get inside this block, and this is a call to GetLastError,
3234 // then InitEarlyBoundNDirectTarget has not been run yet.
3235 if (pMDD->NDirectTargetIsImportThunk())
3237 // Get the unmanaged callsite.
3238 pTarget = (LPVOID)pMDD->GetModule()->GetInternalPInvokeTarget(pMDD->GetRVA());
3240 // If this is a call to GetLastError, then we haven't overwritten m_pNativeNDirectTarget yet.
3241 if (HeuristicDoesThisLookLikeAGetLastErrorCall((LPBYTE)pTarget))
3242 pTarget = (BYTE*)FalseGetLastError;
3246 pTarget = pMDD->GetNativeNDirectTarget();
3250 #endif // !CROSSGEN_COMPILE
3252 inline CorPinvokeMap GetDefaultCallConv(BOOL bIsVarArg)
3254 #ifdef PLATFORM_UNIX
3255 return pmCallConvCdecl;
3256 #else // PLATFORM_UNIX
3257 return bIsVarArg ? pmCallConvCdecl : pmCallConvStdcall;
3258 #endif // !PLATFORM_UNIX
3261 void PInvokeStaticSigInfo::InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg)
3271 // Convert WinAPI methods to either StdCall or CDecl based on if they are varargs or not.
3272 if (callConv == pmCallConvWinapi)
3273 callConv = GetDefaultCallConv(bIsVarArg);
3275 CorPinvokeMap sigCallConv = (CorPinvokeMap)0;
3276 BOOL fSuccess = MetaSig::GetUnmanagedCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
3280 SetError(IDS_EE_NDIRECT_BADNATL); //Bad metadata format
3283 // Do the same WinAPI to StdCall or CDecl for the signature calling convention as well. We need
3284 // to do this before we check to make sure the PInvoke map calling convention and the
3285 // signature calling convention match for compatibility reasons.
3286 if (sigCallConv == pmCallConvWinapi)
3287 sigCallConv = GetDefaultCallConv(bIsVarArg);
3289 if (callConv != 0 && sigCallConv != 0 && callConv != sigCallConv)
3290 SetError(IDS_EE_NDIRECT_BADNATL_CALLCONV);
3292 if (callConv == 0 && sigCallConv == 0)
3293 m_callConv = GetDefaultCallConv(bIsVarArg);
3294 else if (callConv != 0)
3295 m_callConv = callConv;
3297 m_callConv = sigCallConv;
3299 if (bIsVarArg && m_callConv != pmCallConvCdecl)
3300 SetError(IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV);
3303 void PInvokeStaticSigInfo::ReportErrors()
3314 COMPlusThrow(kTypeLoadException, m_error);
3318 //---------------------------------------------------------
3319 // Does a class or method have a NAT_L CustomAttribute?
3323 // FAILED = unknown because something failed.
3324 //---------------------------------------------------------
3326 HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs)
3334 PRECONDITION(CheckPointer(pInternalImport));
3335 PRECONDITION(TypeFromToken(token) == mdtMethodDef);
3339 // Check method flags first before trying to find the custom value
3340 if (!IsReallyMdPinvokeImpl(dwMemberAttrs))
3344 LPCSTR pszImportName;
3347 if (SUCCEEDED(pInternalImport->GetPinvokeMap(token, &mappingFlags, &pszImportName, &modref)))
3354 // Either MD or signature & module must be given.
3356 BOOL NDirect::MarshalingRequired(MethodDesc *pMD, PCCOR_SIGNATURE pSig /*= NULL*/, Module *pModule /*= NULL*/)
3361 PRECONDITION(pMD != NULL || (pSig != NULL && pModule != NULL));
3365 // As a by-product, when returning FALSE we will also set the native stack size to the MD if it's
3366 // an NDirectMethodDesc. This number is needed to link the P/Invoke (it determines the @n entry
3367 // point name suffix and affects alignment thunk generation on the Mac). If this method returns
3368 // TRUE, the stack size will be set when building the marshaling IL stub.
3369 DWORD dwStackSize = 0;
3370 CorPinvokeMap callConv = (CorPinvokeMap)0;
3374 if (pMD->IsNDirect() || pMD->IsComPlusCall())
3376 // HRESULT swapping is handled by stub
3377 if ((pMD->GetImplAttrs() & miPreserveSig) == 0)
3381 // SetLastError is handled by stub
3382 PInvokeStaticSigInfo sigInfo(pMD);
3383 if (sigInfo.GetLinkFlags() & nlfLastError)
3386 // LCID argument is handled by stub
3387 if (GetLCIDParameterIndex(pMD) != -1)
3390 // making sure that cctor has run may be handled by stub
3391 if (pMD->IsNDirect() && ((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
3394 callConv = sigInfo.GetCallConv();
3399 PREFIX_ASSUME(pMD != NULL);
3401 pSig = pMD->GetSig();
3402 pModule = pMD->GetModule();
3405 // Check to make certain that the signature only contains types that marshal trivially
3406 SigPointer ptr(pSig);
3407 IfFailThrow(ptr.GetCallingConvInfo(NULL));
3409 IfFailThrow(ptr.GetData(&numArgs));
3410 numArgs++; // +1 for return type
3412 // We'll need to parse parameter native types
3413 mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef));
3414 IMDInternalImport *pMDImport = pModule->GetMDImport();
3416 SigTypeContext emptyTypeContext;
3418 mdMethodDef methodToken = mdMethodDefNil;
3421 methodToken = pMD->GetMemberDef();
3423 CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
3425 for (ULONG i = 0; i < numArgs; i++)
3427 SigPointer arg = ptr;
3428 CorElementType type;
3429 IfFailThrow(arg.PeekElemType(&type));
3433 case ELEMENT_TYPE_PTR:
3435 IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR
3436 IfFailThrow(arg.PeekElemType(&type));
3438 if (type == ELEMENT_TYPE_VALUETYPE)
3440 if ((arg.HasCustomModifier(pModule,
3441 "Microsoft.VisualC.NeedsCopyConstructorModifier",
3442 ELEMENT_TYPE_CMOD_REQD)) ||
3443 (arg.HasCustomModifier(pModule,
3444 "System.Runtime.CompilerServices.IsCopyConstructed",
3445 ELEMENT_TYPE_CMOD_REQD)))
3450 if (i > 0) dwStackSize += sizeof(SLOT);
3454 case ELEMENT_TYPE_INTERNAL:
3456 // this check is not functional in DAC and provides no security against a malicious dump
3457 // the DAC is prepared to receive an invalid type handle
3458 #ifndef DACCESS_COMPILE
3459 if (pModule->IsSigInIL(arg.GetPtr()))
3460 THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule);
3465 case ELEMENT_TYPE_VALUETYPE:
3467 TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext);
3469 // JIT can handle internal blittable value types
3470 if (!hndArgType.IsBlittable() && !hndArgType.IsEnum())
3475 // return value is fine as long as it can be normalized to an integer
3478 CorElementType normalizedType = hndArgType.GetInternalCorElementType();
3479 if (normalizedType == ELEMENT_TYPE_VALUETYPE)
3481 // it is a structure even after normalization
3487 dwStackSize += StackElemSize(hndArgType.GetSize());
3492 case ELEMENT_TYPE_BOOLEAN:
3493 case ELEMENT_TYPE_CHAR:
3495 // Bool requires marshaling
3496 // Char may require marshaling (MARSHAL_TYPE_ANSICHAR)
3502 if (CorTypeInfo::IsPrimitiveType(type) || type == ELEMENT_TYPE_FNPTR)
3504 if (i > 0) dwStackSize += StackElemSize(CorTypeInfo::Size(type));
3508 // other non-primitive type - requires marshaling
3514 // check for explicit MarshalAs
3515 NativeTypeParamInfo paramInfo;
3517 if (pParamTokenArray[i] != mdParamDefNil)
3519 if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, ¶mInfo) ||
3520 paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT)
3522 // Presence of MarshalAs does not necessitate marshaling (it could as well be the default
3523 // for the type), but it's a good enough heuristic. We definitely don't want to duplicate
3524 // the logic from code:MarshalInfo.MarshalInfo here.
3529 IfFailThrow(ptr.SkipExactlyOne());
3532 if (!FitsInU2(dwStackSize))
3535 // do not set the stack size for varargs - the number is call site specific
3536 if (pMD != NULL && !pMD->IsVarArg())
3538 if (pMD->IsNDirect())
3540 ((NDirectMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize), callConv);
3542 #ifdef FEATURE_COMINTEROP
3543 else if (pMD->IsComPlusCall())
3545 // calling convention is always stdcall
3546 ((ComPlusCallMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize));
3548 #endif // FEATURE_COMINTEROP
3555 // factorization of CreateNDirectStubWorker
3556 static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig,
3558 CorNativeLinkType nlType,
3559 CorNativeLinkFlags nlFlags,
3560 UINT argidx, // this is used for reverse pinvoke hresult swapping
3566 UINT& nativeStackOffset,
3567 bool& fStubNeedsCOM,
3569 DEBUG_ARG(LPCUTF8 pDebugName)
3570 DEBUG_ARG(LPCUTF8 pDebugClassName)
3577 PRECONDITION(CheckPointer(params));
3578 PRECONDITION(CheckPointer(pss));
3579 PRECONDITION(CheckPointer(pMD, NULL_OK));
3583 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3585 MarshalInfo::MarshalScenario ms;
3586 #ifdef FEATURE_COMINTEROP
3587 if (SF_IsCOMStub(dwStubFlags))
3589 if (SF_IsWinRTStub(dwStubFlags))
3590 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
3592 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3595 #endif // FEATURE_COMINTEROP
3597 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3600 #ifdef FEATURE_COMINTEROP
3601 if (SF_IsWinRTCtorStub(dwStubFlags))
3603 _ASSERTE(msig.GetReturnType() == ELEMENT_TYPE_VOID);
3604 _ASSERTE(SF_IsHRESULTSwapping(dwStubFlags));
3606 pss->MarshalFactoryReturn();
3607 nativeStackOffset += sizeof(LPVOID);
3608 if (SF_IsWinRTCompositionStub(dwStubFlags))
3610 nativeStackOffset += 2 * sizeof(LPVOID);
3614 #endif // FEATURE_COMINTEROP
3615 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
3617 MarshalInfo returnInfo(msig.GetModule(),
3618 msig.GetReturnProps(),
3619 msig.GetSigTypeContext(),
3626 msig.NumFixedArgs(),
3627 SF_IsBestFit(dwStubFlags),
3628 SF_IsThrowOnUnmappableChar(dwStubFlags),
3632 DEBUG_ARG(pDebugName)
3633 DEBUG_ARG(pDebugClassName)
3637 marshalType = returnInfo.GetMarshalType();
3639 fStubNeedsCOM |= returnInfo.MarshalerRequiresCOM();
3641 #ifdef FEATURE_COMINTEROP
3642 if (marshalType == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3644 // Hidden length arrays are only valid with HRESULT swapping
3645 if (!SF_IsHRESULTSwapping(dwStubFlags))
3647 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3650 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3651 returnInfo.SetHiddenLengthParamIndex(static_cast<UINT16>(nativeArgIndex));
3653 // Inject the hidden argument so that it winds up at the end of the method signature
3654 pss->MarshalHiddenLengthArgument(&returnInfo, TRUE);
3655 nativeStackOffset += returnInfo.GetHiddenLengthParamStackSize();
3657 if (SF_IsReverseStub(dwStubFlags))
3663 if (SF_IsCOMStub(dwStubFlags))
3665 // We don't support native methods that return VARIANTs, non-blittable structs, GUIDs, or DECIMALs directly.
3666 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT ||
3667 marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS ||
3668 marshalType == MarshalInfo::MARSHAL_TYPE_GUID ||
3669 marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL)
3671 if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
3673 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3677 pss->MarshalReturn(&returnInfo, argOffset);
3680 #endif // FEATURE_COMINTEROP
3682 if (marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS
3683 || marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS
3684 || marshalType == MarshalInfo::MARSHAL_TYPE_GUID
3685 || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL
3686 #ifdef FEATURE_COMINTEROP
3687 || marshalType == MarshalInfo::MARSHAL_TYPE_DATETIME
3688 #endif // FEATURE_COMINTEROP
3691 if (SF_IsHRESULTSwapping(dwStubFlags))
3693 // V1 restriction: we could implement this but it's late in the game to do so.
3694 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3697 else if (marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY
3698 || marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET
3699 || marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
3700 #ifdef FEATURE_COMINTEROP
3701 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
3702 #endif // FEATURE_COMINTEROP
3705 // Each of these types are non-blittable and according to its managed size should be returned in a return buffer on x86 in stdcall.
3706 // However, its native size is small enough to be returned by-value.
3707 // We don't know the native type representation early enough to get this correct, so we throw an exception here.
3708 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3710 else if (IsUnsupportedTypedrefReturn(msig))
3712 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3715 #ifdef FEATURE_COMINTEROP
3716 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT && !SF_IsHRESULTSwapping(dwStubFlags))
3718 // No support for returning variants. This is a V1 restriction, due to the late date,
3719 // don't want to add the special-case code to support this in light of low demand.
3720 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NOVARIANTRETURN);
3722 #endif // FEATURE_COMINTEROP
3724 pss->MarshalReturn(&returnInfo, argOffset);
3731 static inline UINT GetStackOffsetFromStackSize(UINT stackSize, bool fThisCall)
3733 LIMITED_METHOD_CONTRACT;
3737 // -1 means that the argument is not on the stack
3738 return (stackSize >= sizeof(SLOT) ? (stackSize - sizeof(SLOT)) : (UINT)-1);
3740 #endif // _TARGET_X86_
3744 #ifdef FEATURE_COMINTEROP
3746 struct HiddenParameterInfo
3748 MarshalInfo *pManagedParam; // Managed parameter which required the hidden parameter
3749 int nativeIndex; // 0 based index into the native method signature where the hidden parameter should be injected
3752 // Get the indexes of any hidden length parameters to be marshaled for the method
3754 // At return, each value in the ppParamIndexes array is a 0 based index into the native method signature where
3755 // the length parameter for a hidden length array should be passed. The MarshalInfo objects will also be
3756 // updated such that they all have explicit marshaling information.
3758 // The caller is responsible for freeing the memory pointed to by ppParamIndexes
3759 void CheckForHiddenParameters(DWORD cParamMarshalInfo,
3760 __in_ecount(cParamMarshalInfo) MarshalInfo *pParamMarshalInfo,
3761 __out DWORD *pcHiddenNativeParameters,
3762 __out HiddenParameterInfo **ppHiddenNativeParameters)
3767 PRECONDITION(CheckPointer(pParamMarshalInfo));
3768 PRECONDITION(CheckPointer(pcHiddenNativeParameters));
3769 PRECONDITION(CheckPointer(ppHiddenNativeParameters));
3773 NewArrayHolder<HiddenParameterInfo> hiddenParamInfo(new HiddenParameterInfo[cParamMarshalInfo]);
3774 DWORD foundInfoCount = 0;
3776 for (DWORD iParam = 0; iParam < cParamMarshalInfo; ++iParam)
3778 // Look for hidden length arrays, which all require additional parameters to be added
3779 if (pParamMarshalInfo[iParam].GetMarshalType() == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3781 DWORD currentNativeIndex = iParam + foundInfoCount;
3783 // The location of the length parameter is implicitly just before the array pointer.
3784 // We'll give it our current index, and bumping the found count will push us back a slot.
3786 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3787 pParamMarshalInfo[iParam].SetHiddenLengthParamIndex(static_cast<UINT16>(currentNativeIndex));
3789 hiddenParamInfo[foundInfoCount].nativeIndex = pParamMarshalInfo[iParam].HiddenLengthParamIndex();
3790 hiddenParamInfo[foundInfoCount].pManagedParam = &(pParamMarshalInfo[iParam]);
3795 *pcHiddenNativeParameters = foundInfoCount;
3796 *ppHiddenNativeParameters = hiddenParamInfo.Extract();
3799 bool IsHiddenParameter(int nativeArgIndex,
3800 DWORD cHiddenParameters,
3801 __in_ecount(cHiddenParameters) HiddenParameterInfo *pHiddenParameters,
3802 __out HiddenParameterInfo **ppHiddenParameterInfo)
3807 PRECONDITION(cHiddenParameters == 0 || CheckPointer(pHiddenParameters));
3808 PRECONDITION(CheckPointer(ppHiddenParameterInfo));
3812 *ppHiddenParameterInfo = NULL;
3814 for (DWORD i = 0; i < cHiddenParameters; ++i)
3816 _ASSERTE(pHiddenParameters[i].nativeIndex != -1);
3817 if (pHiddenParameters[i].nativeIndex == nativeArgIndex)
3819 *ppHiddenParameterInfo = &(pHiddenParameters[i]);
3827 #endif // FEATURE_COMINTEROP
3829 //---------------------------------------------------------
3830 // Creates a new stub for a N/Direct call. Return refcount is 1.
3831 // Note that this function may now throw if it fails to create
3833 //---------------------------------------------------------
3834 static void CreateNDirectStubWorker(StubState* pss,
3835 StubSigDesc* pSigDesc,
3836 CorNativeLinkType nlType,
3837 CorNativeLinkFlags nlFlags,
3838 CorPinvokeMap unmgdCallConv,
3841 mdParamDef* pParamTokenArray,
3849 PRECONDITION(CheckPointer(pss));
3850 PRECONDITION(CheckPointer(pSigDesc));
3851 PRECONDITION(CheckPointer(pMD, NULL_OK));
3852 PRECONDITION(!pMD || pMD->IsILStub() || (0 != pMD->GetMethodTable()->IsDelegate()) == SF_IsDelegateStub(dwStubFlags));
3856 SF_ConsistencyCheck(dwStubFlags);
3859 if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
3860 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
3865 if (SF_IsCOMStub(dwStubFlags))
3867 _ASSERTE(0 == nlType);
3868 _ASSERTE(0 == nlFlags);
3869 _ASSERTE(0 == unmgdCallConv);
3873 _ASSERTE(nlType == nltAnsi || nlType == nltUnicode);
3875 Module *pModule = pSigDesc->m_pModule;
3878 // Set up signature walking objects.
3881 MetaSig msig(pSigDesc->m_sig,
3883 &pSigDesc->m_typeContext);
3885 if (SF_IsVarArgStub(dwStubFlags))
3886 msig.SetTreatAsVarArg();
3888 bool fThisCall = (unmgdCallConv == pmCallConvThiscall);
3890 pss->SetLastError(nlFlags & nlfLastError);
3892 // This has been in the product since forward P/Invoke via delegates was
3893 // introduced. It's wrong, but please keep it for backward compatibility.
3894 if (SF_IsDelegateStub(dwStubFlags))
3895 pss->SetLastError(TRUE);
3897 pss->BeginEmit(dwStubFlags);
3901 // LCID is not supported on WinRT
3902 _ASSERTE(!SF_IsWinRTStub(dwStubFlags));
3904 // The code to handle the LCID will call MarshalLCID before calling MarshalArgument
3905 // on the argument the LCID should go after. So we just bump up the index here.
3909 int numArgs = msig.NumFixedArgs();
3911 // thiscall must have at least one parameter (the "this")
3912 if (fThisCall && numArgs == 0)
3913 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
3916 // Now, emit the IL.
3921 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3924 // Marshal the return value.
3927 UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? sizeof(SLOT) : 0);
3928 bool fHasCopyCtorArgs = false;
3929 bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags);
3931 // Normally we would like this to be false so that we use the correct signature
3932 // in the IL_STUB, (i.e if it returns a value class then the signature will use that)
3933 // When this bool is true we change the return type to void and explicitly add a
3934 // return buffer argument as the first argument so as to match the native calling convention correctly.
3935 BOOL fMarshalReturnValueFirst = FALSE;
3937 BOOL fReverseWithReturnBufferArg = FALSE;
3939 // We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping!
3940 // When we are HRESULT-swapping, the managed return type is actually the type of the last parameter and not the return type.
3941 // The native return type of an HRESULT-swapped function is an HRESULT, which never uses a return-buffer argument.
3942 // Since the managed return type is actually the last parameter, we need to marshal it after the last parameter in the managed signature
3943 // to make sure we match the native signature correctly (when marshalling parameters, we add them to the native stub signature).
3944 if (!SF_IsHRESULTSwapping(dwStubFlags))
3946 bool isInstanceMethod = fStubNeedsCOM || fThisCall;
3947 // We cannot just use pSig.GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums.
3948 bool isReturnTypeValueType = msig.GetRetTypeHandleThrowing().GetVerifierCorElementType() == ELEMENT_TYPE_VALUETYPE;
3949 #if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3950 // JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer.
3951 // Therefore instead we change the signature of calli to return void and make the return buffer as first
3952 // argument. This matches the ABI i.e. return buffer is passed as first arg. So native target will get the
3953 // return buffer in correct register.
3954 // The return structure secret arg comes first, however byvalue return is processed at
3955 // the end because it could be the HRESULT-swapped argument which always comes last.
3958 // For functions with value type class, managed and unmanaged calling convention differ
3959 fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig);
3960 #elif defined(_TARGET_ARM_)
3961 fMarshalReturnValueFirst = HasRetBuffArg(&msig);
3963 // On Windows-X86, the native signature might need a return buffer when the managed doesn't (specifically when the native signature is a member function).
3964 fMarshalReturnValueFirst = HasRetBuffArg(&msig) || (isInstanceMethod && isReturnTypeValueType);
3965 #endif // UNIX_X86_ABI
3966 #elif defined(_TARGET_AMD64_)
3967 fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType;
3968 #endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3970 fReverseWithReturnBufferArg = fMarshalReturnValueFirst && SF_IsReverseStub(dwStubFlags);
3975 // Marshal the arguments
3977 MarshalInfo::MarshalScenario ms;
3978 #ifdef FEATURE_COMINTEROP
3979 if (SF_IsCOMStub(dwStubFlags))
3981 if (SF_IsWinRTStub(dwStubFlags))
3982 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
3984 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3987 #endif // FEATURE_COMINTEROP
3989 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3992 // Build up marshaling information for each of the method's parameters
3993 SIZE_T cbParamMarshalInfo;
3994 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numArgs, cbParamMarshalInfo))
3996 COMPlusThrowHR(COR_E_OVERFLOW);
3999 NewArrayHolder<BYTE> pbParamMarshalInfo(new BYTE[cbParamMarshalInfo]);
4000 MarshalInfo *pParamMarshalInfo = reinterpret_cast<MarshalInfo *>(pbParamMarshalInfo.GetValue());
4002 MetaSig paramInfoMSig(msig);
4003 for (int i = 0; i < numArgs; ++i)
4005 paramInfoMSig.NextArg();
4006 new(&(pParamMarshalInfo[i])) MarshalInfo(paramInfoMSig.GetModule(),
4007 paramInfoMSig.GetArgProps(),
4008 paramInfoMSig.GetSigTypeContext(),
4009 pParamTokenArray[i + 1],
4016 SF_IsBestFit(dwStubFlags),
4017 SF_IsThrowOnUnmappableChar(dwStubFlags),
4021 DEBUG_ARG(pSigDesc->m_pDebugName)
4022 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4026 #ifdef FEATURE_COMINTEROP
4027 // Check to see if we need to inject any additional hidden parameters
4028 DWORD cHiddenNativeParameters;
4029 NewArrayHolder<HiddenParameterInfo> pHiddenNativeParameters;
4030 CheckForHiddenParameters(numArgs, pParamMarshalInfo, &cHiddenNativeParameters, &pHiddenNativeParameters);
4032 // Hidden parameters and LCID do not mix
4033 _ASSERTE(!(cHiddenNativeParameters > 0 && iLCIDArg != -1));
4034 #endif // FEATURE_COMINTEROP
4036 // Marshal the parameters
4038 int nativeArgIndex = 0;
4040 // If we are generating a return buffer on a member function that is marked as thiscall (as opposed to being a COM method)
4041 // then we need to marshal the this parameter first and the return buffer second.
4042 // We don't need to do this for COM methods because the "this" is implied as argument 0 by the signature of the stub.
4043 if (fThisCall && fMarshalReturnValueFirst)
4047 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
4048 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
4049 nativeStackSize += info.GetNativeArgSize();
4051 fStubNeedsCOM |= info.MarshalerRequiresCOM();
4053 // make sure that the first parameter is enregisterable
4054 if (info.GetNativeArgSize() > sizeof(SLOT))
4055 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
4060 // If we're doing a native->managed call and are generating a return buffer,
4061 // we need to move all of the actual arguments over one and have the return value be the first argument (after the this pointer if applicable).
4062 if (fReverseWithReturnBufferArg)
4067 if (fMarshalReturnValueFirst)
4069 marshalType = DoMarshalReturnValue(msig,
4082 DEBUG_ARG(pSigDesc->m_pDebugName)
4083 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4086 if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE ||
4087 marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY ||
4088 marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET ||
4089 marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF ||
4090 marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
4091 #ifdef FEATURE_COMINTEROP
4092 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
4093 #endif // FEATURE_COMINTEROP
4096 // These are special non-blittable types returned by-ref in managed,
4097 // but marshaled as primitive values returned by-value in unmanaged.
4101 // This is an ordinary value type - see if it is returned by-ref.
4102 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4103 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4105 nativeStackSize += sizeof(LPVOID);
4110 while (argidx <= numArgs)
4112 #ifdef FEATURE_COMINTEROP
4113 HiddenParameterInfo *pHiddenParameter;
4114 // Check to see if we need to inject a hidden parameter
4115 if (IsHiddenParameter(nativeArgIndex, cHiddenNativeParameters, pHiddenNativeParameters, &pHiddenParameter))
4117 pss->MarshalHiddenLengthArgument(pHiddenParameter->pManagedParam, FALSE);
4118 nativeStackSize += pHiddenParameter->pManagedParam->GetHiddenLengthParamStackSize();
4120 if (SF_IsReverseStub(dwStubFlags))
4126 #endif // FEATURE_COMINTEROP
4129 // Check to see if this is the parameter after which we need to insert the LCID.
4131 if (argidx == iLCIDArg)
4133 pss->MarshalLCID(argidx);
4134 nativeStackSize += sizeof(LPVOID);
4136 if (SF_IsReverseStub(dwStubFlags))
4142 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
4144 #ifdef FEATURE_COMINTEROP
4145 // For the hidden-length array, length parameters must occur before the parameter containing the array pointer
4146 _ASSERTE(info.GetMarshalType() != MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY || nativeArgIndex > info.HiddenLengthParamIndex());
4147 #endif // FEATURE_COMINTEROP
4149 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
4150 nativeStackSize += info.GetNativeArgSize();
4152 fStubNeedsCOM |= info.MarshalerRequiresCOM();
4154 if (fThisCall && argidx == 1)
4156 // make sure that the first parameter is enregisterable
4157 if (info.GetNativeArgSize() > sizeof(SLOT))
4158 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
4161 fHasCopyCtorArgs = info.GetMarshalType() == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR ? TRUE : FALSE;
4169 // Check to see if this is the parameter after which we need to insert the LCID.
4170 if (argidx == iLCIDArg)
4172 pss->MarshalLCID(argidx);
4173 nativeStackSize += sizeof(LPVOID);
4175 if (SF_IsReverseStub(dwStubFlags))
4179 if (!fMarshalReturnValueFirst)
4181 // This could be a HRESULT-swapped argument so it must come last.
4182 marshalType = DoMarshalReturnValue(msig,
4195 DEBUG_ARG(pSigDesc->m_pDebugName)
4196 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4199 // If the return value is a SafeHandle or CriticalHandle, mark the stub method.
4200 // Interop methods that use this stub will have an implicit reliability contract
4201 // (see code:TAStackCrawlCallBack).
4202 if (!SF_IsHRESULTSwapping(dwStubFlags))
4204 if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE ||
4205 marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE)
4207 if (pMD->IsDynamicMethod())
4208 pMD->AsDynamicMethodDesc()->SetUnbreakable(true);
4213 if (SF_IsHRESULTSwapping(dwStubFlags))
4215 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
4216 nativeStackSize += sizeof(LPVOID);
4219 if (pMD->IsDynamicMethod())
4221 // Set the native stack size to the IL stub MD. It is needed for alignment
4222 // thunk generation on the Mac and stdcall name decoration on Windows.
4223 // We do not store it directly in the interop MethodDesc here because due
4224 // to sharing we come here only for the first call with given signature and
4225 // the target MD may even be NULL.
4230 _ASSERTE(nativeStackSize >= sizeof(SLOT));
4231 nativeStackSize -= sizeof(SLOT);
4233 #else // _TARGET_X86_
4235 // The algorithm to compute nativeStackSize on the fly is x86-specific.
4236 // Recompute the correct size for other platforms from the stub signature.
4238 if (SF_IsForwardStub(dwStubFlags))
4240 // It would be nice to compute the correct value for forward stubs too.
4241 // The value is only used in MarshalNative::NumParamBytes right now,
4242 // and changing what MarshalNative::NumParamBytes returns is
4243 // a potential breaking change.
4247 // native stack size is updated in code:ILStubState.SwapStubSignatures
4249 #endif // _TARGET_X86_
4251 if (!FitsInU2(nativeStackSize))
4252 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
4254 DynamicMethodDesc *pDMD = pMD->AsDynamicMethodDesc();
4256 pDMD->SetNativeStackArgSize(static_cast<WORD>(nativeStackSize));
4257 pDMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
4258 pDMD->SetStubNeedsCOMStarted(fStubNeedsCOM);
4261 // FinishEmit needs to know the native stack arg size so we call it after the number
4262 // has been set in the stub MD (code:DynamicMethodDesc.SetNativeStackArgSize)
4263 pss->FinishEmit(pMD);
4266 class NDirectStubHashBlob : public ILStubHashBlobBase
4271 WORD m_unmgdCallConv;
4272 BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob
4279 BYTE m_rgbSigAndParamData[1];
4280 // (dwParamAttr, cbNativeType) // length: number of parameters
4281 // NativeTypeBlob // length: number of parameters
4282 // BYTE m_rgbSigData[]; // length: determined by sig walk
4285 // For better performance and less memory fragmentation,
4286 // I'm using structure here to avoid allocating 3 different arrays.
4291 PCCOR_SIGNATURE pvNativeType;
4294 ILStubHashBlob* NDirect::CreateHashBlob(NDirectStubParameters* pParams)
4296 STANDARD_VM_CONTRACT;
4298 NDirectStubHashBlob* pBlob;
4300 IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport();
4302 CQuickBytes paramInfoBytes;
4303 paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens);
4304 ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr();
4305 ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens);
4307 size_t cbNativeTypeTotal = 0;
4310 // Collect information for function parameters
4312 for (int idx = 0; idx < pParams->m_nParamTokens; idx++)
4314 mdParamDef token = pParams->m_pParamTokenArray[idx];
4315 if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token)
4317 USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted
4318 LPCSTR szParamName_Ignore;
4319 IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, ¶mInfos[idx].dwParamAttr, &szParamName_Ignore));
4321 if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal)
4323 IfFailThrow(pInternalImport->GetFieldMarshal(token, ¶mInfos[idx].pvNativeType, ¶mInfos[idx].cbNativeType));
4324 cbNativeTypeTotal += paramInfos[idx].cbNativeType;
4329 SigPointer sigPtr = pParams->m_sig.CreateSigPointer();
4331 // note that ConvertToInternalSignature also resolves generics so different instantiations will get different
4332 // hash blobs for methods that have generic parameters in their signature
4333 SigBuilder sigBuilder;
4334 sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE);
4337 PVOID pSig = sigBuilder.GetSignature(&cbSig);
4340 // Build hash blob for IL stub sharing
4342 S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) +
4343 S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes
4344 S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size
4345 S_SIZE_T(cbNativeTypeTotal) + // Native type blob data
4346 S_SIZE_T(cbSig); // Signature
4348 if (cbSizeOfBlob.IsOverflow())
4349 COMPlusThrowHR(COR_E_OVERFLOW);
4351 static_assert_no_msg(nltMaxValue <= 0xFF);
4352 static_assert_no_msg(nlfMaxValue <= 0xFF);
4353 static_assert_no_msg(pmMaxValue <= 0xFFFF);
4355 NewArrayHolder<BYTE> pBytes = new BYTE[cbSizeOfBlob.Value()];
4356 // zero out the hash bytes to ensure all bit fields are deterministically set
4357 ZeroMemory(pBytes, cbSizeOfBlob.Value());
4358 pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes;
4360 pBlob->m_pModule = NULL;
4362 if (SF_IsNGENedStub(pParams->m_dwStubFlags))
4364 // don't share across modules if we are ngening the stub
4365 pBlob->m_pModule = pParams->m_pModule;
4368 pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value();
4369 pBlob->m_unmgdCallConv = static_cast<WORD>(pParams->m_unmgdCallConv);
4370 pBlob->m_nlType = static_cast<BYTE>(pParams->m_nlType);
4371 pBlob->m_nlFlags = static_cast<BYTE>(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub
4372 pBlob->m_iLCIDArg = pParams->m_iLCIDArg;
4374 pBlob->m_StubFlags = pParams->m_dwStubFlags;
4375 pBlob->m_nParams = pParams->m_nParamTokens;
4377 BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0];
4380 // Write (dwParamAttr, cbNativeType) for parameters
4382 // Note that these need to be aligned and it is why they are written before the byte blobs
4383 // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs
4385 _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0);
4386 _ASSERTE(sizeof(DWORD) == sizeof(ULONG));
4388 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4390 // We only care about In/Out/HasFieldMarshal
4391 // Other attr are about optional/default values which are not used in marshalling,
4392 // but only used in compilers
4393 *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal);
4394 pBlobParams += sizeof(DWORD);
4396 *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType;
4397 pBlobParams += sizeof(ULONG);
4401 // Write native type blob for parameters
4403 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4405 memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType);
4406 pBlobParams += paramInfos[i].cbNativeType;
4412 memcpy(pBlobParams, pSig, cbSig);
4414 // Verify that we indeed have reached the end
4415 _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value());
4417 pBytes.SuppressRelease();
4418 return (ILStubHashBlob*)pBlob;
4422 ILStubCache* NDirect::GetILStubCache(NDirectStubParameters* pParams)
4432 // Use the m_pLoaderModule instead of m_pModule
4433 // They could be different for methods on generic types.
4434 return pParams->m_pLoaderModule->GetILStubCache();
4438 MethodDesc* NDirect::GetStubMethodDesc(
4439 MethodDesc *pTargetMD,
4440 NDirectStubParameters* pParams,
4441 ILStubHashBlob* pHashParams,
4442 AllocMemTracker* pamTracker,
4443 bool& bILStubCreator,
4444 MethodDesc* pLastMD)
4446 CONTRACT(MethodDesc*)
4450 PRECONDITION(CheckPointer(pParams));
4451 PRECONDITION(!pParams->m_sig.IsEmpty());
4452 PRECONDITION(CheckPointer(pParams->m_pModule));
4453 PRECONDITION(CheckPointer(pTargetMD, NULL_OK));
4454 POSTCONDITION(CheckPointer(RETVAL));
4460 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4462 pMD = pCache->GetStubMethodDesc(pTargetMD,
4464 pParams->m_dwStubFlags,
4466 pParams->m_sig.GetRawSig(),
4467 pParams->m_sig.GetRawSigLen(),
4477 void NDirect::RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams)
4483 PRECONDITION(CheckPointer(pParams));
4484 PRECONDITION(CheckPointer(pHashParams));
4485 PRECONDITION(!pParams->m_sig.IsEmpty());
4486 PRECONDITION(CheckPointer(pParams->m_pModule));
4490 LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams));
4492 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4494 pCache->DeleteEntry(pHashParams);
4498 void NDirect::AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD)
4504 PRECONDITION(CheckPointer(pParams));
4505 PRECONDITION(!pParams->m_sig.IsEmpty());
4506 PRECONDITION(CheckPointer(pParams->m_pModule));
4510 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4512 pCache->AddMethodDescChunkWithLockTaken(pMD);
4516 // Additional factorization of CreateNDirectStub. This hoists all the metadata accesses
4517 // into one location so that we can leave CreateNDirectStubWorker to just generate the
4518 // IL. This allows us to cache a stub based on the inputs to CreateNDirectStubWorker
4519 // instead of having to generate the IL first before doing the caching.
4521 void CreateNDirectStubAccessMetadata(StubSigDesc* pSigDesc, // IN
4522 CorPinvokeMap unmgdCallConv, // IN
4523 DWORD* pdwStubFlags, // IN/OUT
4524 int* piLCIDArg, // OUT
4525 int* pNumArgs // OUT
4528 STANDARD_VM_CONTRACT;
4530 if (SF_IsCOMStub(*pdwStubFlags))
4532 _ASSERTE(0 == unmgdCallConv);
4536 if (unmgdCallConv != pmCallConvStdcall &&
4537 unmgdCallConv != pmCallConvCdecl &&
4538 unmgdCallConv != pmCallConvThiscall)
4540 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
4544 #ifdef FEATURE_COMINTEROP
4545 if (SF_IsDelegateStub(*pdwStubFlags))
4547 _ASSERTE(!SF_IsWinRTStub(*pdwStubFlags));
4548 if (pSigDesc->m_pMD->GetMethodTable()->IsProjectedFromWinRT())
4550 // We do not allow P/Invoking via WinRT delegates to better segregate WinRT
4551 // from classic interop scenarios.
4552 COMPlusThrow(kMarshalDirectiveException, IDS_EE_DELEGATEPINVOKE_WINRT);
4555 #endif // FEATURE_COMINTEROP
4557 MetaSig msig(pSigDesc->m_sig,
4558 pSigDesc->m_pModule,
4559 &pSigDesc->m_typeContext);
4561 if (SF_IsVarArgStub(*pdwStubFlags))
4562 msig.SetTreatAsVarArg();
4564 (*pNumArgs) = msig.NumFixedArgs();
4566 IMDInternalImport* pInternalImport = pSigDesc->m_pModule->GetMDImport();
4568 _ASSERTE(!SF_IsHRESULTSwapping(*pdwStubFlags));
4570 mdMethodDef md = pSigDesc->m_tkMethodDef;
4571 if (md != mdMethodDefNil)
4573 DWORD dwDescrOffset;
4575 IfFailThrow(pInternalImport->GetMethodImplProps(
4580 #ifdef FEATURE_COMINTEROP
4581 if (SF_IsWinRTStub(*pdwStubFlags))
4583 // All WinRT methods do HRESULT swapping
4584 if (IsMiPreserveSig(dwImplFlags))
4586 COMPlusThrow(kMarshalDirectiveException, IDS_EE_PRESERVESIG_WINRT);
4589 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4592 #endif // FEATURE_COMINTEROP
4593 if (SF_IsReverseStub(*pdwStubFlags))
4595 // only COM-to-CLR call supports hresult swapping in the reverse direction
4596 if (SF_IsCOMStub(*pdwStubFlags) && !IsMiPreserveSig(dwImplFlags))
4598 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4603 // fwd pinvoke, fwd com interop support hresult swapping.
4604 // delegate to an unmanaged method does not.
4605 if (!IsMiPreserveSig(dwImplFlags) && !SF_IsDelegateStub(*pdwStubFlags))
4607 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4612 if (pSigDesc->m_pMD != NULL)
4614 (*piLCIDArg) = GetLCIDParameterIndex(pSigDesc->m_pMD);
4621 // Check to see if we need to do LCID conversion.
4622 if ((*piLCIDArg) != -1 && (*piLCIDArg) > (*pNumArgs))
4624 COMPlusThrow(kIndexOutOfRangeException, IDS_EE_INVALIDLCIDPARAM);
4627 if (SF_IsCOMStub(*pdwStubFlags) && !SF_IsWinRTStaticStub(*pdwStubFlags))
4629 CONSISTENCY_CHECK(msig.HasThis());
4633 if (msig.HasThis() && !SF_IsDelegateStub(*pdwStubFlags))
4635 COMPlusThrow(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC);
4640 void NDirect::PopulateNDirectMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, BOOL throwOnError /*= TRUE*/)
4642 if (pNMD->IsSynchronized() && throwOnError)
4643 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
4645 WORD ndirectflags = 0;
4646 if (pNMD->MethodDesc::IsVarArg())
4647 ndirectflags |= NDirectMethodDesc::kVarArgs;
4649 LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
4650 new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName,
4651 (throwOnError ? PInvokeStaticSigInfo::THROW_ON_ERROR : PInvokeStaticSigInfo::NO_THROW_ON_ERROR));
4653 if (pSigInfo->GetCharSet() == nltAnsi)
4654 ndirectflags |= NDirectMethodDesc::kNativeAnsi;
4656 CorNativeLinkFlags linkflags = pSigInfo->GetLinkFlags();
4657 if (linkflags & nlfLastError)
4658 ndirectflags |= NDirectMethodDesc::kLastError;
4659 if (linkflags & nlfNoMangle)
4660 ndirectflags |= NDirectMethodDesc::kNativeNoMangle;
4662 CorPinvokeMap callConv = pSigInfo->GetCallConv();
4663 if (callConv == pmCallConvStdcall)
4664 ndirectflags |= NDirectMethodDesc::kStdCall;
4665 if (callConv == pmCallConvThiscall)
4666 ndirectflags |= NDirectMethodDesc::kThisCall;
4668 if (pNMD->GetLoaderModule()->IsSystem() && strcmp(szLibName, "QCall") == 0)
4670 ndirectflags |= NDirectMethodDesc::kIsQCall;
4674 EnsureWritablePages(&pNMD->ndirect);
4675 pNMD->ndirect.m_pszLibName.SetValueMaybeNull(szLibName);
4676 pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(szEntryPointName);
4680 if (ndirectflags & NDirectMethodDesc::kStdCall)
4682 // Compute the kStdCallWithRetBuf flag which is needed at link time for entry point mangling.
4684 ArgIterator argit(&msig);
4685 if (argit.HasRetBuffArg())
4687 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4688 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4690 ndirectflags |= NDirectMethodDesc::kStdCallWithRetBuf;
4694 #endif // _TARGET_X86_
4696 // Call this exactly ONCE per thread. Do not publish incomplete prestub flags
4697 // or you will introduce a race condition.
4698 pNMD->InterlockedSetNDirectFlags(ndirectflags);
4701 #ifdef FEATURE_COMINTEROP
4702 // Find the MethodDesc of the predefined IL stub method by either
4703 // 1) looking at redirected adapter interfaces, OR
4704 // 2) looking at special attributes for the specific interop scenario (specified by dwStubFlags).
4705 // Currently only ManagedToNativeComInteropStubAttribute is supported.
4706 // It returns NULL if no such attribute(s) can be found.
4707 // But if the attribute is found and is invalid, or something went wrong in the looking up
4708 // process, an exception will be thrown. If everything goes well, you'll get the MethodDesc
4709 // of the stub method
4710 HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, MethodDesc **ppRetStubMD)
4717 PRECONDITION(CheckPointer(pTargetMD));
4718 PRECONDITION(CheckPointer(ppRetStubMD));
4719 PRECONDITION(*ppRetStubMD == NULL);
4725 MethodTable *pTargetMT = pTargetMD->GetMethodTable();
4727 // Check if this is a redirected interface - we have static stubs in mscorlib for those.
4728 if (SF_IsForwardCOMStub(dwStubFlags) && pTargetMT->IsInterface())
4731 // Redirect generic redirected interfaces to the corresponding adapter methods in mscorlib
4732 if (pTargetMT->HasInstantiation())
4734 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(pTargetMD, TypeHandle::Interop_ManagedToNative);
4735 if (pAdapterMD != NULL)
4737 *ppRetStubMD = pAdapterMD;
4744 // Find out if we have the attribute
4749 // Support v-table forward classic COM interop calls only
4750 if (SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags) && !SF_IsWinRTStub(dwStubFlags))
4752 if (pTargetMT->HasInstantiation())
4754 // ManagedToNativeComInteropStubAttribute is not supported with generics
4758 if (pTargetMD->IsFCall())
4760 // ManagedToNativeComInteropStubAttribute is not supported on FCalls (i.e. methods on legacy
4761 // interfaces forwarded to CustomMarshalers.dll such as IEnumerable::GetEnumerator)
4764 _ASSERTE(pTargetMD->IsComPlusCall());
4766 if (pTargetMD->IsInterface())
4768 _ASSERTE(!pTargetMD->GetAssembly()->IsWinMD());
4769 hr = pTargetMD->GetMDImport()->GetCustomAttributeByName(
4770 pTargetMD->GetMemberDef(),
4771 FORWARD_INTEROP_STUB_METHOD_TYPE,
4777 // GetCustomAttributeByName returns S_FALSE when it cannot find the attribute but nothing fails...
4778 // Translate that to E_FAIL
4779 else if (hr == S_FALSE)
4784 // We are dealing with the class, use the interface MD instead
4785 // After second thought I believe we don't need to check the class MD.
4786 // We can think stubs as part of public interface, and if the interface is public,
4787 // the stubs should also be accessible
4788 MethodDesc *pInterfaceMD = pTargetMD->GetInterfaceMD();
4791 hr = FindPredefinedILStubMethod(pInterfaceMD, dwStubFlags, ppRetStubMD);
4802 // Parse the attribute
4804 CustomAttributeParser parser(pBytes, cbBytes);
4805 IfFailRet(parser.SkipProlog());
4809 IfFailRet(parser.GetNonEmptyString(&pTypeName, &cbTypeName));
4811 LPCUTF8 pMethodName;
4813 IfFailRet(parser.GetNonEmptyString(&pMethodName, &cbMethodName));
4815 StackSString typeName(SString::Utf8, pTypeName, cbTypeName);
4816 StackSString methodName(SString::Utf8, pMethodName, cbMethodName);
4819 // Retrieve the type
4821 TypeHandle stubClassType;
4822 stubClassType = TypeName::GetTypeUsingCASearchRules(typeName.GetUnicode(), pTargetMT->GetAssembly());
4824 MethodTable *pStubClassMT = stubClassType.AsMethodTable();
4826 StackSString stubClassName;
4827 pStubClassMT->_GetFullyQualifiedNameForClassNestedAware(stubClassName);
4829 StackSString targetInterfaceName;
4830 pTargetMT->_GetFullyQualifiedNameForClassNestedAware(targetInterfaceName);
4832 // Restrict to same assembly only to reduce test cost
4833 if (stubClassType.GetAssembly() != pTargetMT->GetAssembly())
4837 IDS_EE_INTEROP_STUB_CA_MUST_BE_WITHIN_SAME_ASSEMBLY,
4838 stubClassName.GetUnicode(),
4839 targetInterfaceName.GetUnicode()
4843 if (stubClassType.HasInstantiation())
4847 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_GENERIC,
4848 stubClassName.GetUnicode()
4852 if (stubClassType.IsInterface())
4856 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_INTERFACE,
4857 stubClassName.GetUnicode()
4862 // Locate the MethodDesc for the stub method
4864 MethodDesc *pStubMD = NULL;
4867 PCCOR_SIGNATURE pTargetSig = NULL;
4868 DWORD pcTargetSig = 0;
4870 SigTypeContext typeContext; // NO generics supported
4872 pTargetMD->GetSig(&pTargetSig, &pcTargetSig);
4874 MetaSig msig(pTargetSig,
4876 pTargetMD->GetModule(),
4878 _ASSERTE(msig.HasThis());
4880 SigBuilder stubSigBuilder;
4883 // Append calling Convention, NumOfArgs + 1,
4885 stubSigBuilder.AppendByte(msig.GetCallingConvention() & ~IMAGE_CEE_CS_CALLCONV_HASTHIS);
4886 stubSigBuilder.AppendData(msig.NumFixedArgs() + 1);
4889 // Append return type
4891 SigPointer pReturn = msig.GetReturnProps();
4892 LPBYTE pReturnTypeBegin = (LPBYTE)pReturn.GetPtr();
4893 IfFailThrow(pReturn.SkipExactlyOne());
4894 LPBYTE pReturnTypeEnd = (LPBYTE)pReturn.GetPtr();
4896 stubSigBuilder.AppendBlob(pReturnTypeBegin, pReturnTypeEnd - pReturnTypeBegin);
4901 stubSigBuilder.AppendElementType(ELEMENT_TYPE_CLASS);
4902 stubSigBuilder.AppendToken(pTargetMT->GetCl());
4905 // Copy rest of the arguments
4907 if (msig.NextArg() != ELEMENT_TYPE_END)
4909 SigPointer pFirstArg = msig.GetArgProps();
4910 LPBYTE pArgBegin = (LPBYTE) pFirstArg.GetPtr();
4911 LPBYTE pArgEnd = (LPBYTE) pTargetSig + pcTargetSig;
4913 stubSigBuilder.AppendBlob(pArgBegin, pArgEnd - pArgBegin);
4917 // Allocate new memory and copy over
4919 DWORD pcStubSig = 0;
4920 PCCOR_SIGNATURE pStubSig = (PCCOR_SIGNATURE) stubSigBuilder.GetSignature(&pcStubSig);
4923 // Find method using name + signature
4925 StackScratchBuffer buffer;
4926 LPCUTF8 szMethodNameUTF8 = methodName.GetUTF8(buffer);
4927 pStubMD = MemberLoader::FindMethod(stubClassType.GetMethodTable(),
4931 pTargetMT->GetModule());
4933 if (pStubMD == NULL)
4942 pTargetMD->GetMDImport(),
4945 // Unfortunately the PrettyPrintSig doesn't print 'static' when the function is static
4946 // so we need to append 'static' here. No need to localize
4947 SString signature(SString::Utf8, (LPCUTF8)"static ");
4948 signature.AppendUTF8((LPCUTF8) qbSig.Ptr());
4951 kMissingMethodException,
4952 IDS_EE_INTEROP_STUB_CA_STUB_METHOD_MISSING,
4953 signature.GetUnicode(),
4954 stubClassName.GetUnicode()
4961 // Check the Stub MD
4964 // Verify that the target interop method can call the stub method
4966 _ASSERTE(pTargetMD != NULL);
4968 StaticAccessCheckContext accessContext(pTargetMD, pTargetMT);
4970 if (!ClassLoader::CanAccess(
4973 stubClassType.GetAssembly(),
4974 pStubMD->GetAttrs(),
4978 StackSString interopMethodName(SString::Utf8, pTargetMD->GetName());
4981 kMethodAccessException,
4982 IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD,
4983 interopMethodName.GetUnicode(),
4984 methodName.GetUnicode()
4988 // The FindMethod call will make sure that it is static by matching signature.
4989 // So there is no need to check and throw
4990 _ASSERTE(pStubMD->IsStatic());
4992 *ppRetStubMD = pStubMD;
4996 #endif // FEATURE_COMINTEROP
4998 MethodDesc* CreateInteropILStub(
5000 StubSigDesc* pSigDesc,
5001 CorNativeLinkType nlType,
5002 CorNativeLinkFlags nlFlags,
5003 CorPinvokeMap unmgdCallConv,
5004 DWORD dwStubFlags, // NDirectStubFlags
5006 mdParamDef* pParamTokenArray,
5010 CONTRACT(MethodDesc*)
5014 PRECONDITION(CheckPointer(pSigDesc));
5015 POSTCONDITION(CheckPointer(RETVAL));
5020 ///////////////////////////////
5022 // MethodDesc creation
5024 ///////////////////////////////
5026 MethodDesc* pStubMD = NULL;
5028 Module* pModule = pSigDesc->m_pModule;
5029 Module* pLoaderModule = pSigDesc->m_pLoaderModule;
5030 MethodDesc* pTargetMD = pSigDesc->m_pMD;
5032 // pTargetMD may be null in the case of calli pinvoke
5033 // and vararg pinvoke.
5036 #ifdef FEATURE_COMINTEROP
5038 // Try to locate predefined IL stub either defined in user code or hardcoded in CLR
5039 // If there is one, use the pointed method as the stub.
5040 // Skip pTargetMD == NULL case for reverse interop calls
5042 if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD)))
5044 #ifndef CROSSGEN_COMPILE
5045 // We are about to execute method in pStubMD which could be in another module.
5046 // Call EnsureActive before make the call
5047 // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here
5048 pStubMD->EnsureActive();
5050 if (pStubMD->IsPreImplemented())
5051 RestoreNGENedStub(pStubMD);
5056 #endif // FEATURE_COMINTEROP
5058 // Otherwise, fall back to generating IL stub on-the-fly
5059 NDirectStubParameters params(pSigDesc->m_sig,
5060 &pSigDesc->m_typeContext,
5072 // The following two ILStubCreatorHelperHolder are to recover the status when an
5073 // exception happen during the generation of the IL stubs. We need to free the
5074 // memory allocated and restore the ILStubCache.
5076 // The following block is logically divided into two phases. The first phase is
5077 // CreateOrGet IL Stub phase which we take a domain level lock. The second phase
5078 // is IL generation phase which we take a MethodDesc level lock. Taking two locks
5079 // is mainly designed for performance.
5081 // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the
5082 // allocated memory during the creation of MethodDesc so that we are able to remove
5083 // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper
5085 // When removing IL Stub from Cache, we have a constraint that only the thread which
5086 // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will
5087 // remove it from cache if OOM occurs
5090 ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, ¶ms);
5092 // take the domain level lock
5093 ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock());
5096 // The holder will free the allocated MethodDesc and restore the ILStubCache
5097 // if exception happen.
5098 ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper);
5099 pStubMD = pCreateOrGetStubHolder->GetStubMD();
5101 ///////////////////////////////
5105 ///////////////////////////////
5108 // take the MethodDesc level locker
5109 ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5111 ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
5113 // We can release the holder for the first phase now
5114 pCreateOrGetStubHolder.SuppressRelease();
5117 // The holder will free the allocated MethodDesc and restore the ILStubCache
5118 // if exception happen. The reason to get the holder again is to
5119 ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper);
5121 if (!pEntryLock.DeadlockAwareAcquire())
5123 // the IL generation is not recursive!
5124 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5127 if (SF_IsSharedStub(params.m_dwStubFlags))
5129 // Assure that pStubMD we have now has not been destroyed by other threads
5130 pGenILHolder->GetStubMethodDesc();
5132 while (pStubMD != pGenILHolder->GetStubMD())
5134 pStubMD = pGenILHolder->GetStubMD();
5136 pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5137 pEntryLock.Assign(pEntry, FALSE);
5139 if (!pEntryLock.DeadlockAwareAcquire())
5141 // the IL generation is not recursive!
5142 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5145 pGenILHolder->GetStubMethodDesc();
5151 // We have the entry lock now, we can release the global lock
5152 pILStubLock.Release();
5154 if (pEntry->m_hrResultCode != S_FALSE)
5156 // We came in to generate the IL but someone
5157 // beat us so there's nothing to do
5161 ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
5163 CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc()));
5165 if (pResolver->IsILGenerated())
5167 // this stub already has its IL generated
5172 // Check that the stub signature and MethodDesc are compatible. The JIT
5173 // interface functions depend on this.
5177 SigPointer ptr = pSigDesc->m_sig.CreateSigPointer();
5180 IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo));
5182 BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS);
5184 // CreateNDirectStubWorker will throw an exception for these cases.
5185 BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags);
5187 if (fSigIsStatic || fCanHaveThis)
5189 CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic);
5194 ILStubGenHolder sgh(pResolver);
5196 pResolver->SetStubMethodDesc(pStubMD);
5197 pResolver->SetStubTargetMethodDesc(pTargetMD);
5199 CreateNDirectStubWorker(pss,
5209 pResolver->SetTokenLookupMap(pss->GetTokenLookupMap());
5211 pResolver->SetStubTargetMethodSig(
5212 pss->GetStubTargetMethodSig(),
5213 pss->GetStubTargetMethodSigLength());
5215 // we successfully generated the IL stub
5216 sgh.SuppressRelease();
5219 pEntry->m_hrResultCode = S_OK;
5223 // Link the MethodDesc onto the method table with the lock taken
5224 NDirect::AddMethodDescChunkWithLockTaken(¶ms, pStubMD);
5226 pGenILHolder.SuppressRelease();
5230 ilStubCreatorHelper.SuppressRelease();
5233 #if defined(_TARGET_X86_)
5234 if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg())
5236 // copy the stack arg byte count from the stub MD to the target MD - this number is computed
5237 // during stub generation and is copied to all target MDs that share the stub
5238 // (we don't set it for varargs - the number is call site specific)
5239 // also copy the "takes parameters with copy constructors" flag which is needed to generate
5240 // appropriate intercept stub
5242 WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
5243 BOOL fHasCopyCtorArgs = pStubMD->AsDynamicMethodDesc()->HasCopyCtorArgs();
5245 if (pTargetMD->IsNDirect())
5247 NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD;
5249 pTargetNMD->SetStackArgumentSize(cbStackArgSize, (CorPinvokeMap)0);
5250 pTargetNMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5252 #ifdef FEATURE_COMINTEROP
5255 if (SF_IsCOMStub(dwStubFlags))
5257 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD);
5259 if (pComInfo != NULL)
5261 pComInfo->SetStackArgumentSize(cbStackArgSize);
5262 pComInfo->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5266 #endif // FEATURE_COMINTEROP
5268 #endif // defined(_TARGET_X86_)
5273 MethodDesc* NDirect::CreateCLRToNativeILStub(
5274 StubSigDesc* pSigDesc,
5275 CorNativeLinkType nlType,
5276 CorNativeLinkFlags nlFlags,
5277 CorPinvokeMap unmgdCallConv,
5278 DWORD dwStubFlags) // NDirectStubFlags
5280 CONTRACT(MethodDesc*)
5284 PRECONDITION(CheckPointer(pSigDesc));
5285 POSTCONDITION(CheckPointer(RETVAL));
5291 int numParamTokens = 0;
5292 mdParamDef* pParamTokenArray = NULL;
5294 CreateNDirectStubAccessMetadata(pSigDesc,
5300 Module *pModule = pSigDesc->m_pModule;
5301 numParamTokens = numArgs + 1;
5302 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5303 CollateParamTokens(pModule->GetMDImport(), pSigDesc->m_tkMethodDef, numArgs, pParamTokenArray);
5305 MethodDesc *pMD = pSigDesc->m_pMD;
5307 NewHolder<ILStubState> pStubState;
5309 #ifdef FEATURE_COMINTEROP
5310 if (SF_IsCOMStub(dwStubFlags))
5312 if (SF_IsReverseStub(dwStubFlags))
5314 pStubState = new COMToCLR_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5318 pStubState = new CLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5324 pStubState = new PInvoke_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, unmgdCallConv, iLCIDArg, pMD);
5327 MethodDesc* pStubMD;
5328 pStubMD = CreateInteropILStub(
5344 #ifdef FEATURE_COMINTEROP
5345 MethodDesc* NDirect::CreateFieldAccessILStub(
5346 PCCOR_SIGNATURE szMetaSig,
5347 DWORD cbMetaSigSize,
5350 DWORD dwStubFlags, // NDirectStubFlags
5353 CONTRACT(MethodDesc*)
5357 PRECONDITION(CheckPointer(szMetaSig));
5358 PRECONDITION(CheckPointer(pModule));
5359 PRECONDITION(CheckPointer(pFD, NULL_OK));
5360 PRECONDITION(SF_IsFieldGetterStub(dwStubFlags) || SF_IsFieldSetterStub(dwStubFlags));
5361 POSTCONDITION(CheckPointer(RETVAL));
5365 int numArgs = (SF_IsFieldSetterStub(dwStubFlags) ? 1 : 0);
5366 int numParamTokens = numArgs + 1;
5368 // make sure we capture marshaling metadata
5369 mdParamDef* pParamTokenArray = (mdParamDef *)_alloca(numParamTokens * sizeof(mdParamDef));
5370 pParamTokenArray[0] = mdParamDefNil;
5371 pParamTokenArray[numArgs] = (mdParamDef)fd;
5373 // fields are never preserve-sig
5374 dwStubFlags |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
5376 // convert field signature to getter/setter signature
5377 SigBuilder sigBuilder;
5379 sigBuilder.AppendData(IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS);
5380 sigBuilder.AppendData(numArgs);
5382 if (SF_IsFieldSetterStub(dwStubFlags))
5384 // managed setter returns void
5385 sigBuilder.AppendElementType(ELEMENT_TYPE_VOID);
5388 CONSISTENCY_CHECK(*szMetaSig == IMAGE_CEE_CS_CALLCONV_FIELD);
5390 sigBuilder.AppendBlob((const PVOID)(szMetaSig + 1), cbMetaSigSize - 1);
5391 szMetaSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbMetaSigSize);
5393 StubSigDesc sigDesc(NULL, Signature(szMetaSig, cbMetaSigSize), pModule);
5396 sigDesc.m_pDebugName = pFD->GetDebugName();
5397 sigDesc.m_pDebugClassName = pFD->GetEnclosingMethodTable()->GetDebugClassName();
5400 Signature signature(szMetaSig, cbMetaSigSize);
5401 NewHolder<ILStubState> pStubState = new COMToCLRFieldAccess_ILStubState(pModule, signature, &sigDesc.m_typeContext, dwStubFlags, pFD);
5403 MethodDesc* pStubMD;
5404 pStubMD = CreateInteropILStub(
5407 (CorNativeLinkType)0,
5408 (CorNativeLinkFlags)0,
5417 #endif // FEATURE_COMINTEROP
5419 MethodDesc* NDirect::CreateCLRToNativeILStub(PInvokeStaticSigInfo* pSigInfo,
5423 STANDARD_VM_CONTRACT;
5425 StubSigDesc sigDesc(pMD, pSigInfo);
5427 if (SF_IsWinRTDelegateStub(dwStubFlags))
5429 _ASSERTE(pMD->IsEEImpl());
5431 return CreateCLRToNativeILStub(&sigDesc,
5432 (CorNativeLinkType)0,
5433 (CorNativeLinkFlags)0,
5435 (pSigInfo->GetStubFlags() | dwStubFlags) & ~NDIRECTSTUB_FL_DELEGATE);
5439 return CreateCLRToNativeILStub(&sigDesc,
5440 pSigInfo->GetCharSet(),
5441 pSigInfo->GetLinkFlags(),
5442 pSigInfo->GetCallConv(),
5443 pSigInfo->GetStubFlags() | dwStubFlags);
5447 MethodDesc* NDirect::GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags)
5449 STANDARD_VM_CONTRACT;
5451 MethodDesc* pStubMD = NULL;
5453 if (!pNMD->IsVarArgs() || SF_IsForNumParamBytes(dwStubFlags))
5455 if (pNMD->IsClassConstructorTriggeredByILStub())
5457 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
5460 pStubMD = CreateCLRToNativeILStub(
5462 dwStubFlags & ~NDIRECTSTUB_FL_FOR_NUMPARAMBYTES,
5469 MethodDesc* GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags)
5471 STANDARD_VM_CONTRACT;
5473 BOOL fGcMdaEnabled = FALSE;
5474 #ifdef MDA_SUPPORTED
5475 if (MDA_GET_ASSISTANT(GcManagedToUnmanaged) || MDA_GET_ASSISTANT(GcUnmanagedToManaged))
5477 // We never generate checks for these MDAs to NGEN'ed stubs so if they are
5478 // enabled, a new stub must be generated (the perf impact is huge anyway).
5479 fGcMdaEnabled = TRUE;
5481 #endif // MDA_SUPPORTED
5483 #ifdef FEATURE_COMINTEROP
5484 if (SF_IsReverseCOMStub(dwStubFlags))
5489 // reverse COM stubs live in a hash table
5490 StubMethodHashTable *pHash = pMD->GetLoaderModule()->GetStubMethodHashTable();
5491 return (pHash == NULL ? NULL : pHash->FindMethodDesc(pMD));
5494 #endif // FEATURE_COMINTEROP
5495 if (pMD->IsNDirect())
5497 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5498 return ((fGcMdaEnabled && !pNMD->IsQCall()) ? NULL : pNMD->ndirect.m_pStubMD.GetValueMaybeNull());
5500 #ifdef FEATURE_COMINTEROP
5501 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5503 #ifdef MDA_SUPPORTED
5504 if (MDA_GET_ASSISTANT(RaceOnRCWCleanup))
5506 // we never generate this callout to NGEN'ed stubs
5509 #endif // MDA_SUPPORTED
5514 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
5515 return (pComInfo == NULL ? NULL : pComInfo->m_pStubMD.GetValueMaybeNull());
5517 #endif // FEATURE_COMINTEROP
5518 else if (pMD->IsEEImpl())
5523 DelegateEEClass *pClass = (DelegateEEClass *)pMD->GetClass();
5524 if (SF_IsReverseStub(dwStubFlags))
5526 return pClass->m_pReverseStubMD;
5530 #ifdef FEATURE_COMINTEROP
5531 if (SF_IsWinRTDelegateStub(dwStubFlags))
5533 return pClass->m_pComPlusCallInfo->m_pStubMD.GetValueMaybeNull();
5536 #endif // FEATURE_COMINTEROP
5538 return pClass->m_pForwardStubMD;
5542 else if (pMD->IsIL())
5544 // these are currently only created at runtime, not at NGEN time
5549 UNREACHABLE_MSG("unexpected type of MethodDesc");
5553 #ifndef CROSSGEN_COMPILE
5555 PCODE NDirect::GetStubForILStub(MethodDesc* pManagedMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5561 PRECONDITION(CheckPointer(pManagedMD));
5562 POSTCONDITION(RETVAL != NULL);
5566 // pStubMD, if provided, must be preimplemented.
5567 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5569 if (NULL == *ppStubMD)
5571 PInvokeStaticSigInfo sigInfo(pManagedMD);
5572 *ppStubMD = NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pManagedMD);
5575 RETURN JitILStub(*ppStubMD);
5578 PCODE NDirect::GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5580 STANDARD_VM_CONTRACT;
5584 // pStubMD, if provided, must be preimplemented.
5585 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5587 if (NULL == *ppStubMD)
5589 PInvokeStaticSigInfo sigInfo;
5590 NDirect::PopulateNDirectMethodDesc(pNMD, &sigInfo, /* throwOnError = */ !SF_IsForNumParamBytes(dwStubFlags));
5592 *ppStubMD = NDirect::GetILStubMethodDesc(pNMD, &sigInfo, dwStubFlags);
5595 if (SF_IsForNumParamBytes(dwStubFlags))
5600 pStub = JitILStub(*ppStubMD);
5604 CONSISTENCY_CHECK(pNMD->IsVarArgs());
5607 // varargs goes through vararg NDirect stub
5609 pStub = TheVarargNDirectStub(pNMD->HasRetBuffArg());
5612 if (pNMD->IsEarlyBound())
5614 pNMD->InitEarlyBoundNDirectTarget();
5622 // NOTE: there is a race in updating this MethodDesc. We depend on all
5623 // threads getting back the same DynamicMethodDesc for a particular
5624 // NDirectMethodDesc, in that case, the locking around the actual JIT
5625 // operation will prevent the code from being jitted more than once.
5626 // By the time we get here, all threads get the same address of code
5627 // back from the JIT operation and they all just fill in the same value
5630 // In the NGEN case, all threads will get the same preimplemented code
5631 // address much like the JIT case.
5637 PCODE JitILStub(MethodDesc* pStubMD)
5639 STANDARD_VM_CONTRACT;
5641 PCODE pCode = pStubMD->GetNativeCode();
5645 ///////////////////////////////
5649 ///////////////////////////////
5652 if (pStubMD->IsDynamicMethod())
5655 // A dynamically generated IL stub
5658 pCode = pStubMD->PrepareInitialCode();
5660 _ASSERTE(pCode == pStubMD->GetNativeCode());
5665 // A static IL stub that is pointing to a static method in user assembly
5666 // Compile it and return the native code
5669 // This returns the stable entry point
5670 pCode = pStubMD->DoPrestub(NULL);
5672 _ASSERTE(pCode == pStubMD->GetStableEntryPoint());
5676 if (!pStubMD->IsDynamicMethod())
5678 // We need an entry point that can be called multiple times
5679 pCode = pStubMD->GetMultiCallableAddrOfCode();
5685 MethodDesc* RestoreNGENedStub(MethodDesc* pStubMD)
5690 PRECONDITION(CheckPointer(pStubMD));
5694 #ifdef FEATURE_PREJIT
5695 pStubMD->CheckRestore();
5697 PCODE pCode = pStubMD->GetPreImplementedCode();
5700 TADDR pFixupList = pStubMD->GetFixupList();
5701 if (pFixupList != NULL)
5703 Module* pZapModule = pStubMD->GetZapModule();
5704 _ASSERTE(pZapModule != NULL);
5705 if (!pZapModule->FixupDelayList(pFixupList))
5707 _ASSERTE(!"FixupDelayList failed");
5708 ThrowHR(COR_E_BADIMAGEFORMAT);
5712 #if defined(HAVE_GCCOVER)
5713 if (GCStress<cfg_instr_ngen>::IsEnabled())
5714 SetupGcCoverage(pStubMD, (BYTE*) pCode);
5715 #endif // HAVE_GCCOVER
5720 // We only pass a non-NULL pStubMD to GetStubForILStub() below if pStubMD is preimplemeneted.
5723 #endif // FEATURE_PREJIT
5728 PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags, MethodDesc **ppStubMD)
5734 PRECONDITION(CheckPointer(pMD));
5735 PRECONDITION(pMD->IsNDirect() || pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl() || pMD->IsIL());
5740 MethodDesc* pStubMD = NULL;
5742 pStubMD = GetStubMethodDescFromInteropMethodDesc(pMD, dwStubFlags);
5743 if (pStubMD != NULL)
5745 pStubMD = RestoreNGENedStub(pStubMD);
5748 if ((NULL == pStubMD) && (SF_IsNGENedStub(dwStubFlags)))
5750 // Return NULL -- the caller asked only for an ngened stub and
5751 // one does not exist, so don't do any more work.
5752 CONSISTENCY_CHECK(pStub == NULL);
5755 if (pMD->IsNDirect())
5757 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5758 pStub = NDirect::GetStubForILStub(pNMD, &pStubMD, dwStubFlags);
5760 #ifdef FEATURE_COMINTEROP
5762 if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5764 pStub = ComPlusCall::GetStubForILStub(pMD, &pStubMD);
5766 #endif // FEATURE_COMINTEROP
5768 if (pMD->IsEEImpl())
5770 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
5771 EEImplMethodDesc* pDelegateMD = (EEImplMethodDesc*)pMD;
5772 pStub = COMDelegate::GetStubForILStub(pDelegateMD, &pStubMD, dwStubFlags);
5777 CONSISTENCY_CHECK(SF_IsReverseStub(dwStubFlags));
5778 pStub = NDirect::GetStubForILStub(pMD, &pStubMD, dwStubFlags);
5782 UNREACHABLE_MSG("unexpected MethodDesc type");
5785 if (pStubMD != NULL && pStubMD->IsILStub() && pStubMD->AsDynamicMethodDesc()->IsStubNeedsCOMStarted())
5787 // the stub uses COM so make sure that it is started
5791 if (ppStubMD != NULL)
5792 *EnsureWritablePages(ppStubMD) = pStubMD;
5797 #ifdef FEATURE_COMINTEROP
5798 void CreateCLRToDispatchCOMStub(
5800 DWORD dwStubFlags) // NDirectStubFlags
5806 PRECONDITION(CheckPointer(pMD));
5810 _ASSERTE(SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags));
5812 // If we are dealing with a COM event call, then we need to initialize the
5813 // COM event call information.
5814 if (SF_IsCOMEventCallStub(dwStubFlags))
5816 _ASSERTE(pMD->IsComPlusCall()); // no generic COM eventing
5817 ((ComPlusCallMethodDesc *)pMD)->InitComEventCallInfo();
5820 // Get the call signature information
5821 StubSigDesc sigDesc(pMD);
5825 int numParamTokens = 0;
5826 mdParamDef* pParamTokenArray = NULL;
5828 CreateNDirectStubAccessMetadata(&sigDesc,
5834 numParamTokens = numArgs + 1;
5835 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5836 CollateParamTokens(sigDesc.m_pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray);
5838 DispatchStubState MyStubState;
5840 CreateNDirectStubWorker(&MyStubState,
5842 (CorNativeLinkType)0,
5843 (CorNativeLinkFlags)0,
5845 dwStubFlags | NDIRECTSTUB_FL_COM,
5850 _ASSERTE(pMD->IsComPlusCall()); // no generic disp-calls
5851 ((ComPlusCallMethodDesc *)pMD)->InitRetThunk();
5855 #endif // FEATURE_COMINTEROP
5858 LPVOID NDirect::NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod)
5860 // GetProcAddress cannot be called while preemptive GC is disabled.
5861 // It requires the OS to take the loader lock.
5865 PRECONDITION(CheckPointer(pMD));
5866 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
5870 g_IBCLogger.LogNDirectCodeAccess(pMD);
5872 #ifdef MDA_SUPPORTED
5873 MDA_TRIGGER_ASSISTANT(PInvokeLog, LogPInvoke(pMD, hMod));
5876 RETURN pMD->FindEntryPoint(hMod);
5879 VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget)
5887 PRECONDITION(IsNDirect());
5888 PRECONDITION(pTarget != NULL);
5892 Stub *pInterceptStub = NULL;
5897 #ifdef MDA_SUPPORTED
5898 if (!IsQCall() && MDA_GET_ASSISTANT(PInvokeStackImbalance))
5900 pInterceptStub = GenerateStubForMDA(pTarget, pInterceptStub);
5902 #endif // MDA_SUPPORTED
5905 #endif // _TARGET_X86_
5908 NDirectWriteableData* pWriteableData = GetWriteableData();
5909 EnsureWritablePages(pWriteableData);
5910 g_IBCLogger.LogNDirectCodeAccess(this);
5912 if (pInterceptStub != NULL)
5914 ndirect.m_pNativeNDirectTarget = pTarget;
5916 #if defined(_TARGET_X86_)
5917 pTarget = (PVOID)pInterceptStub->GetEntryPoint();
5919 LPVOID oldTarget = GetNDirectImportThunkGlue()->GetEntrypoint();
5920 if (FastInterlockCompareExchangePointer(&pWriteableData->m_pNDirectTarget, pTarget,
5921 oldTarget) != oldTarget)
5923 pInterceptStub->DecRef();
5926 _ASSERTE(pInterceptStub == NULL); // we don't intercept for anything else than host on !_TARGET_X86_
5931 pWriteableData->m_pNDirectTarget = pTarget;
5937 #if defined(_TARGET_X86_) && defined(MDA_SUPPORTED)
5938 EXTERN_C VOID __stdcall PInvokeStackImbalanceWorker(StackImbalanceCookie *pSICookie, DWORD dwPostESP)
5940 STATIC_CONTRACT_THROWS;
5941 STATIC_CONTRACT_GC_TRIGGERS;
5942 STATIC_CONTRACT_MODE_PREEMPTIVE; // we've already switched to preemptive
5944 // make sure we restore the original Win32 last error before leaving this function - we are
5945 // called right after returning from the P/Invoke target and the error has not been saved yet
5946 BEGIN_PRESERVE_LAST_ERROR;
5948 MdaPInvokeStackImbalance* pProbe = MDA_GET_ASSISTANT(PInvokeStackImbalance);
5950 // This MDA must be active if we generated a call to PInvokeStackImbalanceHelper
5953 pProbe->CheckStack(pSICookie, dwPostESP);
5955 END_PRESERVE_LAST_ERROR;
5957 #endif // _TARGET_X86_ && MDA_SUPPORTED
5960 // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places
5961 // if earlier loads fail and those later loads obliterate error codes.
5963 // This tracker object will keep track of the error code in accordance to priority:
5965 // low-priority: unknown error code (should never happen)
5966 // medium-priority: dll not found
5967 // high-priority: dll found but error during loading
5969 // We will overwrite the previous load's error code only if the new error code is higher priority.
5972 class LoadLibErrorTracker
5975 static const DWORD const_priorityNotFound = 10;
5976 static const DWORD const_priorityAccessDenied = 20;
5977 static const DWORD const_priorityCouldNotLoad = 99999;
5979 LoadLibErrorTracker()
5981 LIMITED_METHOD_CONTRACT;
5983 m_priorityOfLastError = 0;
5986 VOID TrackErrorCode()
5988 LIMITED_METHOD_CONTRACT;
5994 SetMessage(PAL_GetLoadLibraryError());
5997 DWORD dwLastError = GetLastError();
5999 switch (dwLastError)
6001 case ERROR_FILE_NOT_FOUND:
6002 case ERROR_PATH_NOT_FOUND:
6003 case ERROR_MOD_NOT_FOUND:
6004 case ERROR_DLL_NOT_FOUND:
6005 priority = const_priorityNotFound;
6008 // If we can't access a location, we can't know if the dll's there or if it's good.
6009 // Still, this is probably more unusual (and thus of more interest) than a dll-not-found
6010 // so give it an intermediate priority.
6011 case ERROR_ACCESS_DENIED:
6012 priority = const_priorityAccessDenied;
6014 // Assume all others are "dll found but couldn't load."
6016 priority = const_priorityCouldNotLoad;
6019 UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError));
6023 // Sets the error code to HRESULT as could not load DLL
6024 void TrackHR_CouldNotLoad(HRESULT hr)
6026 UpdateHR(const_priorityCouldNotLoad, hr);
6034 SString& GetMessage()
6039 void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath)
6041 STANDARD_VM_CONTRACT;
6043 #if defined(__APPLE__)
6044 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage());
6045 #elif defined(FEATURE_PAL)
6046 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage());
6048 HRESULT theHRESULT = GetHR();
6049 if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT))
6051 COMPlusThrow(kBadImageFormatException);
6056 GetHRMsg(theHRESULT, hrString);
6057 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString);
6059 #endif // FEATURE_PAL
6065 void UpdateHR(DWORD priority, HRESULT hr)
6067 if (priority > m_priorityOfLastError)
6070 m_priorityOfLastError = priority;
6074 void SetMessage(LPCSTR message)
6076 m_message = SString(SString::Utf8, message);
6080 DWORD m_priorityOfLastError;
6082 }; // class LoadLibErrorTracker
6084 // Load the library directly. On Unix systems, don't register it yet with PAL.
6085 // * External callers like AssemblyNative::InternalLoadUnmanagedDllFromPath() and the upcoming
6086 // System.Runtime.Interop.Marshall.LoadLibrary() need the raw system handle
6087 // * Internal callers like LoadLibraryModule() can convert this handle to a HMODULE via PAL APIs on Unix
6088 static NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker )
6090 STANDARD_VM_CONTRACT;
6092 NATIVE_LIBRARY_HANDLE hmod = NULL;
6096 if ((flags & 0xFFFFFF00) != 0
6097 #ifndef FEATURE_CORESYSTEM
6098 && NDirect::SecureLoadLibrarySupported()
6099 #endif // !FEATURE_CORESYSTEM
6102 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00);
6108 DWORD dwLastError = GetLastError();
6109 if (dwLastError != ERROR_INVALID_PARAMETER)
6111 pErrorTracker->TrackErrorCode();
6116 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF);
6118 #else // !FEATURE_PAL
6119 hmod = PAL_LoadLibraryDirect(name);
6120 #endif // !FEATURE_PAL
6124 pErrorTracker->TrackErrorCode();
6130 #if !defined(FEATURE_PAL)
6131 bool NDirect::s_fSecureLoadLibrarySupported = false;
6134 #define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a))
6135 #define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a))
6138 #define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W
6139 #define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W
6140 #else // !FEATURE_PAL
6141 #define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll")
6142 #define PLATFORM_SHARED_LIB_PREFIX_W W("")
6143 #endif // !FEATURE_PAL
6145 // The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
6146 // In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
6147 // Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
6148 #define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
6150 // DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags.
6151 // There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute,
6152 // CoreCLR's LoadLibrary implementation uses the following defaults.
6153 // Other implementations of LoadLibrary callbacks/events are free to use other default conventions.
6154 void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6156 STANDARD_VM_CONTRACT;
6158 *searchAssemblyDirectory = TRUE;
6159 *dllImportSearchPathFlags = 0;
6162 // If a module has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and return true.
6163 // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
6164 BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6166 STANDARD_VM_CONTRACT;
6168 if (pModule->HasDefaultDllImportSearchPathsAttribute())
6170 *dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
6171 *searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
6175 GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory);
6179 // If a pInvoke has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
6180 // Otherwise, if the containing assembly has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
6181 // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
6182 BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6184 STANDARD_VM_CONTRACT;
6186 if (pMD->HasDefaultDllImportSearchPathsAttribute())
6188 *dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
6189 *searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
6193 return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory);
6197 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
6202 PRECONDITION(CheckPointer(libraryPath));
6206 LoadLibErrorTracker errorTracker;
6207 const NATIVE_LIBRARY_HANDLE hmod =
6208 LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker);
6210 if (throwOnError && (hmod == nullptr))
6212 SString libraryPathSString(libraryPath);
6213 errorTracker.Throw(libraryPathSString);
6219 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
6220 BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags,
6226 PRECONDITION(CheckPointer(libraryName));
6227 PRECONDITION(CheckPointer(callingAssembly));
6231 LoadLibErrorTracker errorTracker;
6233 // First checks if a default dllImportSearchPathFlags was passed in, if so, use that value.
6234 // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute.
6235 // If so, use that value.
6236 BOOL searchAssemblyDirectory;
6237 DWORD dllImportSearchPathFlags;
6239 if (hasDllImportSearchFlags)
6241 dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6242 searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6247 GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(),
6248 &dllImportSearchPathFlags, &searchAssemblyDirectory);
6251 NATIVE_LIBRARY_HANDLE hmod =
6252 LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName);
6254 if (throwOnError && (hmod == nullptr))
6256 SString libraryPathSString(libraryName);
6257 errorTracker.Throw(libraryPathSString);
6264 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, PCWSTR wszLibName)
6266 STANDARD_VM_CONTRACT;
6268 BOOL searchAssemblyDirectory;
6269 DWORD dllImportSearchPathFlags;
6271 GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
6273 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6274 return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName);
6278 void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle)
6280 STANDARD_VM_CONTRACT;
6282 // FreeLibrary doesn't throw if the input is null.
6283 // This avoids further null propagation/check while freeing resources (ex: in finally blocks)
6288 BOOL retVal = FreeLibrary(handle);
6289 #else // !FEATURE_PAL
6290 BOOL retVal = PAL_FreeLibraryDirect(handle);
6291 #endif // !FEATURE_PAL
6294 COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException"));
6298 INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError)
6303 PRECONDITION(CheckPointer(handle));
6304 PRECONDITION(CheckPointer(symbolName));
6308 MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName);
6311 INT_PTR address = reinterpret_cast<INT_PTR>(GetProcAddress((HMODULE)handle, lpstr));
6312 if ((address == NULL) && throwOnError)
6313 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName);
6314 #else // !FEATURE_PAL
6315 INT_PTR address = reinterpret_cast<INT_PTR>(PAL_GetProcAddressDirect((NATIVE_LIBRARY_HANDLE)handle, lpstr));
6316 if ((address == NULL) && throwOnError)
6317 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName);
6318 #endif // !FEATURE_PAL
6323 #ifndef PLATFORM_UNIX
6324 BOOL IsWindowsAPISet(PCWSTR wszLibName)
6326 STANDARD_VM_CONTRACT;
6328 // This is replicating quick check from the OS implementation of api sets.
6329 return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 ||
6330 SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0;
6332 #endif // !PLATFORM_UNIX
6335 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, PCWSTR wszLibName)
6337 STANDARD_VM_CONTRACT;
6338 //Dynamic Pinvoke Support:
6339 //Check if we need to provide the host a chance to provide the unmanaged dll
6341 #ifndef PLATFORM_UNIX
6342 if (IsWindowsAPISet(wszLibName))
6344 // Prevent Overriding of Windows API sets.
6347 #endif // !PLATFORM_UNIX
6349 NATIVE_LIBRARY_HANDLE hmod = NULL;
6350 AppDomain* pDomain = GetAppDomain();
6351 CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
6352 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6354 PEFile *pManifestFile = pAssembly->GetManifestFile();
6355 PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext();
6357 //Step 0: Check if the assembly was bound using TPA.
6358 // The Binding Context can be null or an overridden TPA context
6359 if (pBindingContext == NULL)
6361 // If we do not have any binder associated, then return to the default resolution mechanism.
6365 UINT_PTR assemblyBinderID = 0;
6366 IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
6368 ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
6370 // For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call.
6371 if (AreSameBinderInstance(pCurrentBinder, pTPABinder))
6376 #ifdef FEATURE_COMINTEROP
6377 CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder();
6378 if (AreSameBinderInstance(pCurrentBinder, pWinRTBinder))
6380 // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to
6381 // types being referenced from Windows.Foundation.Winmd) or when dealing with a winmd (which is bound using WinRT binder).
6383 // For this, we should use the standard mechanism to make pinvoke call as well.
6386 #endif // FEATURE_COMINTEROP
6388 //Step 1: If the assembly was not bound using TPA,
6389 // Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll to give
6390 // The custom assembly context a chance to load the unmanaged dll.
6394 STRINGREF pUnmanagedDllName;
6395 pUnmanagedDllName = StringObject::NewString(wszLibName);
6397 GCPROTECT_BEGIN(pUnmanagedDllName);
6399 // Get the pointer to the managed assembly load context
6400 INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
6402 // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll method.
6403 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL);
6404 DECLARE_ARGHOLDER_ARRAY(args, 2);
6405 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName);
6406 args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
6409 CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
6416 // Return the AssemblyLoadContext for an assembly
6417 INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly)
6419 STANDARD_VM_CONTRACT;
6421 PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext();
6422 if (pBindingContext == NULL)
6424 // GetBindingContext() returns NULL for System.Private.CoreLib
6428 UINT_PTR assemblyBinderID = 0;
6429 IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
6431 AppDomain *pDomain = GetAppDomain();
6432 ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
6434 #ifdef FEATURE_COMINTEROP
6435 if (AreSameBinderInstance(pCurrentBinder, pDomain->GetWinRtBinder()))
6437 // No ALC associated handle with WinRT Binders.
6440 #endif // FEATURE_COMINTEROP
6442 // The code here deals with two implementations of ICLRPrivBinder interface:
6443 // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and
6444 // - CLRPrivBinderAssemblyLoadContext for custom ALCs.
6445 // in order obtain the associated ALC handle.
6446 INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext())
6447 ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext()
6448 : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
6450 return ptrManagedAssemblyLoadContext;
6454 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, PCWSTR wszLibName)
6456 STANDARD_VM_CONTRACT;
6458 NATIVE_LIBRARY_HANDLE hmod = NULL;
6459 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6460 INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly);
6462 if (ptrManagedAssemblyLoadContext == NULL)
6471 OBJECTREF AssemblyRef;
6472 } gc = { NULL, NULL };
6474 GCPROTECT_BEGIN(gc);
6476 gc.DllName = StringObject::NewString(wszLibName);
6477 gc.AssemblyRef = pAssembly->GetExposedObject();
6479 // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method
6480 // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef
6481 // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in
6482 // as an additional argument.
6483 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT);
6484 DECLARE_ARGHOLDER_ARRAY(args, 3);
6485 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName);
6486 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef);
6487 args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
6490 CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
6497 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallback(NDirectMethodDesc * pMD, LPCWSTR wszLibName)
6499 STANDARD_VM_CONTRACT;
6501 if (pMD->GetModule()->IsSystem())
6503 // Don't attempt to callback on Corelib itself.
6504 // The LoadLibrary callback stub is managed code that requires CoreLib
6508 DWORD dllImportSearchPathFlags;
6509 BOOL searchAssemblyDirectory;
6510 BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
6511 dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0;
6513 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6514 NATIVE_LIBRARY_HANDLE handle = NULL;
6519 STRINGREF libNameRef;
6520 OBJECTREF assemblyRef;
6521 } gc = { NULL, NULL };
6523 GCPROTECT_BEGIN(gc);
6525 gc.libNameRef = StringObject::NewString(wszLibName);
6526 gc.assemblyRef = pAssembly->GetExposedObject();
6528 PREPARE_NONVIRTUAL_CALLSITE(METHOD__NATIVELIBRARY__LOADLIBRARYCALLBACKSTUB);
6529 DECLARE_ARGHOLDER_ARRAY(args, 4);
6530 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef);
6531 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef);
6532 args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags);
6533 args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags);
6536 CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args);
6542 // Try to load the module alongside the assembly where the PInvoke was declared.
6543 NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6545 STANDARD_VM_CONTRACT;
6547 NATIVE_LIBRARY_HANDLE hmod = NULL;
6549 SString path = pAssembly->GetManifestFile()->GetPath();
6551 SString::Iterator lastPathSeparatorIter = path.End();
6552 if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter))
6554 lastPathSeparatorIter++;
6555 path.Truncate(lastPathSeparatorIter);
6557 path.Append(libName);
6558 hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker);
6564 // Try to load the module from the native DLL search directories
6565 NATIVE_LIBRARY_HANDLE NDirect::LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6567 STANDARD_VM_CONTRACT;
6569 NATIVE_LIBRARY_HANDLE hmod = NULL;
6570 AppDomain* pDomain = GetAppDomain();
6572 if (pDomain->HasNativeDllSearchDirectories())
6574 AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories();
6575 while (hmod == NULL && pathIter.Next())
6577 SString qualifiedPath(*(pathIter.GetPath()));
6578 qualifiedPath.Append(libName);
6579 if (!Path::IsRelative(qualifiedPath))
6581 hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker);
6590 static const int MaxVariationCount = 4;
6591 static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6593 // Supported lib name variations
6594 static auto NameFmt = W("%.0s%s%.0s");
6595 static auto PrefixNameFmt = W("%s%s%.0s");
6596 static auto NameSuffixFmt = W("%.0s%s%s");
6597 static auto PrefixNameSuffixFmt = W("%s%s%s");
6599 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6602 if (!libNameIsRelativePath)
6604 libNameVariations[varCount++] = NameFmt;
6608 // We check if the suffix is contained in the name, because on Linux it is common to append
6609 // a version number to the library name (e.g. 'libicuuc.so.57').
6610 bool containsSuffix = false;
6611 SString::CIterator it = libName.Begin();
6612 if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W))
6614 it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W);
6615 containsSuffix = it == libName.End() || *it == (WCHAR)'.';
6618 // If the path contains a path delimiter, we don't add a prefix
6619 it = libName.Begin();
6620 bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W);
6624 libNameVariations[varCount++] = NameFmt;
6627 libNameVariations[varCount++] = PrefixNameFmt;
6629 libNameVariations[varCount++] = NameSuffixFmt;
6632 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6636 libNameVariations[varCount++] = NameSuffixFmt;
6639 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6641 libNameVariations[varCount++] = NameFmt;
6644 libNameVariations[varCount++] = PrefixNameFmt;
6648 *numberOfVariations = varCount;
6650 #else // FEATURE_PAL
6651 static const int MaxVariationCount = 2;
6652 static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6654 // Supported lib name variations
6655 static auto NameFmt = W("%.0s%s%.0s");
6656 static auto NameSuffixFmt = W("%.0s%s%s");
6658 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6662 // The purpose of following code is to workaround LoadLibrary limitation:
6663 // LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario:
6664 // [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke
6665 // The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll"
6666 auto it = libName.Begin();
6667 if (!libNameIsRelativePath ||
6668 !libName.Find(it, W('.')) ||
6669 libName.EndsWith(W(".")) ||
6670 libName.EndsWithCaseInsensitive(W(".dll")) ||
6671 libName.EndsWithCaseInsensitive(W(".exe")))
6673 // Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
6674 // If the string specifies a full path, the function searches only that path for the module.
6675 // If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name.
6676 // To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.
6677 libNameVariations[varCount++] = NameFmt;
6681 libNameVariations[varCount++] = NameFmt;
6682 libNameVariations[varCount++] = NameSuffixFmt;
6685 *numberOfVariations = varCount;
6687 #endif // FEATURE_PAL
6689 // Search for the library and variants of its name in probing directories.
6691 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly,
6692 BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags,
6693 LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
6695 STANDARD_VM_CONTRACT;
6697 NATIVE_LIBRARY_HANDLE hmod = NULL;
6699 #if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX)
6700 // Try to go straight to System32 for Windows API sets. This is replicating quick check from
6701 // the OS implementation of api sets.
6702 if (IsWindowsAPISet(wszLibName))
6704 hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker);
6710 #endif // FEATURE_CORESYSTEM && !FEATURE_PAL
6712 AppDomain* pDomain = GetAppDomain();
6713 DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
6714 bool libNameIsRelativePath = Path::IsRelative(wszLibName);
6716 // P/Invokes are often declared with variations on the actual library name.
6717 // For example, it's common to leave off the extension/suffix of the library
6718 // even if it has one, or to leave off a prefix like "lib" even if it has one
6719 // (both of these are typically done to smooth over cross-platform differences).
6720 // We try to dlopen with such variations on the original.
6721 const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {};
6722 int numberOfVariations = COUNTOF(prefixSuffixCombinations);
6723 DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath);
6724 for (int i = 0; i < numberOfVariations; i++)
6726 SString currLibNameVariation;
6727 currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W);
6729 // NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path
6730 hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
6736 if (!libNameIsRelativePath)
6738 DWORD flags = loadWithAlteredPathFlags;
6739 if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
6741 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
6742 // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
6743 flags |= dllImportSearchPathFlags;
6746 hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
6752 else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
6754 hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
6761 hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker);
6768 // This may be an assembly name
6769 // Format is "fileName, assemblyDisplayName"
6770 MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName);
6771 char *szComma = strchr(szLibName, ',');
6775 // Trim white spaces
6776 while (COMCharacter::nativeIsWhiteSpace(*(++szComma)));
6779 if (SUCCEEDED(spec.Init(szComma)))
6781 // Need to perform case insensitive hashing.
6784 UTF8_TO_LOWER_CASE(szLibName, qbLC);
6785 szLibName = (LPUTF8) qbLC.Ptr();
6788 Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED);
6789 Module *pModule = pAssembly->FindModuleByName(szLibName);
6791 hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
6798 // This Method returns an instance of the PAL-Registered handle
6799 HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker)
6804 PRECONDITION( CheckPointer( pMD ) );
6808 LPCUTF8 name = pMD->GetLibName();
6809 if ( !name || !*name )
6812 PREFIX_ASSUME( name != NULL );
6813 MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
6815 ModuleHandleHolder hmod = LoadLibraryModuleViaCallback(pMD, wszLibName);
6819 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6820 #endif // FEATURE_PAL
6821 return hmod.Extract();
6824 AppDomain* pDomain = GetAppDomain();
6826 // AssemblyLoadContext is not supported in AppX mode and thus,
6827 // we should not perform PInvoke resolution via it when operating in AppX mode.
6828 if (!AppX::IsAppXProcess())
6830 hmod = LoadLibraryModuleViaHost(pMD, wszLibName);
6834 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6835 #endif // FEATURE_PAL
6836 return hmod.Extract();
6840 hmod = pDomain->FindUnmanagedImageInCache(wszLibName);
6843 return hmod.Extract();
6846 hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName);
6850 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6851 #endif // FEATURE_PAL
6853 // If we have a handle add it to the cache.
6854 pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
6855 return hmod.Extract();
6858 if (!AppX::IsAppXProcess())
6860 hmod = LoadLibraryModuleViaEvent(pMD, wszLibName);
6864 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6865 #endif // FEATURE_PAL
6866 return hmod.Extract();
6870 return hmod.Extract();
6873 //---------------------------------------------------------
6874 // Loads the DLL and finds the procaddress for an N/Direct call.
6875 //---------------------------------------------------------
6877 VOID NDirect::NDirectLink(NDirectMethodDesc *pMD)
6882 PRECONDITION(CheckPointer(pMD));
6887 // On the phone, we only allow platform assemblies to define pinvokes
6888 // unless the host has asked us otherwise.
6891 if (pMD->IsClassConstructorTriggeredAtLinkTime())
6893 pMD->GetMethodTable()->CheckRunClassInitThrowing();
6898 LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget;
6900 // Do not repeat the lookup if the QCall was hardbound during ngen
6901 if (pvTarget == NULL)
6903 pvTarget = ECall::GetQCallImpl(pMD);
6907 _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD));
6910 pMD->SetNDirectTarget(pvTarget);
6914 // Loading unmanaged dlls can trigger dllmains which certainly count as code execution!
6915 pMD->EnsureActive();
6917 LoadLibErrorTracker errorTracker;
6919 BOOL fSuccess = FALSE;
6920 HINSTANCE hmod = LoadLibraryModule( pMD, &errorTracker );
6923 LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod);
6927 #ifdef MDA_SUPPORTED
6928 MdaInvalidOverlappedToPinvoke *pOverlapCheck = MDA_GET_ASSISTANT(InvalidOverlappedToPinvoke);
6929 if (pOverlapCheck && pOverlapCheck->ShouldHook(pMD))
6931 LPVOID pNewTarget = pOverlapCheck->Register(hmod,pvTarget);
6934 pvTarget = pNewTarget;
6938 pMD->SetNDirectTarget(pvTarget);
6945 if (pMD->GetLibName() == NULL)
6946 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME);
6948 StackSString ssLibName(SString::Utf8, pMD->GetLibName());
6952 errorTracker.Throw(ssLibName);
6955 WCHAR wszEPName[50];
6956 if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0)
6958 wszEPName[0] = W('?');
6959 wszEPName[1] = W('\0');
6962 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName);
6964 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName);
6970 //---------------------------------------------------------
6972 //---------------------------------------------------------
6973 /*static*/ void NDirect::Init()
6980 INJECT_FAULT(COMPlusThrowOM());
6984 #if !defined(FEATURE_PAL)
6985 // Check if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
6986 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
6987 _ASSERTE(hMod != NULL);
6989 if (GetProcAddress(hMod, "AddDllDirectory") != NULL)
6991 // The AddDllDirectory export was added in KB2533623 together with the new flag support
6992 s_fSecureLoadLibrarySupported = true;
6994 #endif // !FEATURE_PAL
6998 //==========================================================================
6999 // This function is reached only via NDirectImportThunk. It's purpose
7000 // is to ensure that the target DLL is fully loaded and ready to run.
7002 // FUN FACTS: Though this function is actually entered in unmanaged mode,
7003 // it can reenter managed mode and throw a COM+ exception if the DLL linking
7005 //==========================================================================
7008 EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD)
7012 BEGIN_PRESERVE_LAST_ERROR;
7022 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
7023 // this function is called by CLR to native assembly stubs which are called by
7024 // managed code as a result, we need an unwind and continue handler to translate
7025 // any of our internal exceptions into managed exceptions.
7026 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
7028 if (pMD->IsEarlyBound())
7030 if (!pMD->IsZapped())
7032 // we need the MD to be populated in case we decide to build an intercept
7033 // stub to wrap the target in InitEarlyBoundNDirectTarget
7034 PInvokeStaticSigInfo sigInfo;
7035 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
7038 pMD->InitEarlyBoundNDirectTarget();
7043 // Otherwise we're in an inlined pinvoke late bound MD
7045 INDEBUG(Thread *pThread = GetThread());
7047 _ASSERTE(pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr());
7049 CONSISTENCY_CHECK(pMD->IsNDirect());
7051 // With IL stubs, we don't have to do anything but ensure the DLL is loaded.
7054 if (!pMD->IsZapped())
7056 PInvokeStaticSigInfo sigInfo;
7057 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
7061 // must have been populated at NGEN time
7062 _ASSERTE(pMD->GetLibName() != NULL);
7065 pMD->CheckRestore();
7067 NDirect::NDirectLink(pMD);
7071 ret = pMD->GetNDirectTarget();
7073 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
7074 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
7076 END_PRESERVE_LAST_ERROR;
7081 //===========================================================================
7082 // Support for Pinvoke Calli instruction
7084 //===========================================================================
7086 EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock * pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD)
7088 BEGIN_PRESERVE_LAST_ERROR;
7090 STATIC_CONTRACT_THROWS;
7091 STATIC_CONTRACT_GC_TRIGGERS;
7092 STATIC_CONTRACT_MODE_COOPERATIVE;
7093 STATIC_CONTRACT_ENTRY_POINT;
7095 MAKE_CURRENT_THREAD_AVAILABLE();
7098 Thread::ObjectRefFlush(CURRENT_THREAD);
7101 FrameWithCookie<PrestubMethodFrame> frame(pTransitionBlock, pMD);
7102 PrestubMethodFrame * pFrame = &frame;
7104 pFrame->Push(CURRENT_THREAD);
7106 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
7107 _ASSERTE(pMD == pFrame->GetFunction());
7109 GetILStubForCalli(pVASigCookie, pMD);
7111 pFrame->Pop(CURRENT_THREAD);
7113 END_PRESERVE_LAST_ERROR;
7116 EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
7118 BEGIN_PRESERVE_LAST_ERROR;
7120 STATIC_CONTRACT_THROWS;
7121 STATIC_CONTRACT_GC_TRIGGERS;
7122 STATIC_CONTRACT_MODE_COOPERATIVE;
7123 STATIC_CONTRACT_ENTRY_POINT;
7125 MAKE_CURRENT_THREAD_AVAILABLE();
7128 Thread::ObjectRefFlush(CURRENT_THREAD);
7131 FrameWithCookie<PInvokeCalliFrame> frame(pTransitionBlock, pVASigCookie, pUnmanagedTarget);
7132 PInvokeCalliFrame * pFrame = &frame;
7134 pFrame->Push(CURRENT_THREAD);
7136 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
7138 GetILStubForCalli(pVASigCookie, NULL);
7140 pFrame->Pop(CURRENT_THREAD);
7142 END_PRESERVE_LAST_ERROR;
7145 PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
7153 PRECONDITION(CheckPointer(pVASigCookie));
7154 PRECONDITION(CheckPointer(pMD, NULL_OK));
7155 POSTCONDITION(RETVAL != NULL);
7159 PCODE pTempILStub = NULL;
7161 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
7162 // this function is called by CLR to native assembly stubs which are called by
7163 // managed code as a result, we need an unwind and continue handler to translate
7164 // any of our internal exceptions into managed exceptions.
7165 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
7167 // Force a GC if the stress level is high enough
7168 GCStress<cfg_any>::MaybeTrigger();
7172 Signature signature = pVASigCookie->signature;
7173 CorPinvokeMap unmgdCallConv = pmNoMangle;
7175 DWORD dwStubFlags = NDIRECTSTUB_FL_BESTFIT;
7177 // The MethodDesc pointer may in fact be the unmanaged target, see PInvokeStubs.asm.
7178 if (pMD == NULL || (UINT_PTR)pMD & 0x1)
7181 dwStubFlags |= NDIRECTSTUB_FL_UNMANAGED_CALLI;
7183 // need to convert the CALLI signature to stub signature with managed calling convention
7184 switch (MetaSig::GetCallingConvention(pVASigCookie->pModule, pVASigCookie->signature))
7186 case IMAGE_CEE_CS_CALLCONV_C:
7187 unmgdCallConv = pmCallConvCdecl;
7189 case IMAGE_CEE_CS_CALLCONV_STDCALL:
7190 unmgdCallConv = pmCallConvStdcall;
7192 case IMAGE_CEE_CS_CALLCONV_THISCALL:
7193 unmgdCallConv = pmCallConvThiscall;
7195 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
7196 unmgdCallConv = pmCallConvFastcall;
7199 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
7202 LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
7203 PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen()));
7204 CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen());
7206 // make the stub IMAGE_CEE_CS_CALLCONV_DEFAULT
7207 *new_sig &= ~IMAGE_CEE_CS_CALLCONV_MASK;
7208 *new_sig |= IMAGE_CEE_CS_CALLCONV_DEFAULT;
7210 signature = Signature(new_sig, signature.GetRawSigLen());
7214 _ASSERTE(pMD->IsNDirect());
7215 dwStubFlags |= NDIRECTSTUB_FL_CONVSIGASVARARG;
7217 // vararg P/Invoke must be cdecl
7218 unmgdCallConv = pmCallConvCdecl;
7220 if (((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
7222 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
7227 CorNativeLinkFlags nlFlags;
7228 CorNativeLinkType nlType;
7232 PInvokeStaticSigInfo sigInfo(pMD);
7234 md = pMD->GetMemberDef();
7235 nlFlags = sigInfo.GetLinkFlags();
7236 nlType = sigInfo.GetCharSet();
7240 md = mdMethodDefNil;
7245 StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule);
7247 MethodDesc* pStubMD = NDirect::CreateCLRToNativeILStub(&sigDesc,
7253 pTempILStub = JitILStub(pStubMD);
7255 InterlockedCompareExchangeT<PCODE>(&pVASigCookie->pNDirectILStub,
7259 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
7260 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
7262 RETURN pVASigCookie->pNDirectILStub;
7265 #endif // CROSSGEN_COMPILE
7267 #endif // #ifndef DACCESS_COMPILE
7270 // Truncates a SString by first converting it to unicode and truncate it
7271 // if it is larger than size. "..." will be appended if it is truncated.
7273 void TruncateUnicodeString(SString &string, COUNT_T bufSize)
7276 if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize)
7278 _ASSERTE(bufSize / sizeof(WCHAR) > 4);
7279 string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4);
7280 string.Append(W("..."));