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 nlt = nltUnicode; break;
2970 #ifdef PLATFORM_WINDOWS
2973 nlt = nltAnsi; // We don't have a utf8 charset in metadata yet, but ANSI == UTF-8 off-Windows
2977 hr = E_FAIL; goto ErrExit;
2980 SetBestFitMapping (namedArgs[MDA_BestFitMapping].val.u1);
2981 SetThrowOnUnmappableChar (namedArgs[MDA_ThrowOnUnmappableChar].val.u1);
2982 if (namedArgs[MDA_SetLastError].val.u1)
2983 SetLinkFlags ((CorNativeLinkFlags)(nlfLastError | GetLinkFlags()));
2989 SetError(IDS_EE_NDIRECT_BADNATL);
2991 InitCallConv(callConv, pMD->IsVarArg());
2997 PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2998 Signature sig, Module* pModule, ThrowOnError throwOnError)
3006 PRECONDITION(CheckPointer(pModule));
3010 PreInit(pModule, NULL);
3012 SetIsStatic (!(MetaSig::GetCallingConvention(pModule, sig) & IMAGE_CEE_CS_CALLCONV_HASTHIS));
3013 InitCallConv((CorPinvokeMap)0, FALSE);
3019 void PInvokeStaticSigInfo::DllImportInit(MethodDesc* pMD, LPCUTF8 *ppLibName, LPCUTF8 *ppEntryPointName)
3027 PRECONDITION(CheckPointer(pMD));
3029 // These preconditions to prevent multithreaded regression
3030 // where pMD->ndirect.m_szLibName was passed in directly, cleared
3031 // by this API, then accessed on another thread before being reset here.
3032 PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
3033 PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
3037 // initialize data members to defaults
3040 // System.Runtime.InteropServices.DllImportAttribute
3041 IMDInternalImport *pInternalImport = pMD->GetMDImport();
3042 CorPinvokeMap mappingFlags = pmMaxValue;
3043 mdModuleRef modref = mdModuleRefNil;
3044 if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
3046 #if !defined(CROSSGEN_COMPILE) // IJW
3047 // The guessing heuristic has been broken with NGen for a long time since we stopped loading
3048 // images at NGen time using full LoadLibrary. The DLL references are not resolved correctly
3049 // without full LoadLibrary.
3051 // Disable the heuristic consistently during NGen so that it does not kick in by accident.
3052 if (!IsCompilationProcess())
3053 BestGuessNDirectDefaults(pMD);
3056 InitCallConv((CorPinvokeMap)0, pMD->IsVarArg());
3060 // out parameter pEntryPointName
3061 if (ppEntryPointName && *ppEntryPointName == NULL)
3062 *ppEntryPointName = pMD->GetName();
3064 // out parameter pLibName
3065 if (ppLibName != NULL)
3067 if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
3069 SetError(IDS_CLASSLOAD_BADFORMAT);
3075 InitCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask), pMD->IsVarArg());
3078 CorPinvokeMap bestFitMask = (CorPinvokeMap)(mappingFlags & pmBestFitMask);
3079 if (bestFitMask == pmBestFitEnabled)
3080 SetBestFitMapping (TRUE);
3081 else if (bestFitMask == pmBestFitDisabled)
3082 SetBestFitMapping (FALSE);
3084 // m_bThrowOnUnmappableChar
3085 CorPinvokeMap unmappableMask = (CorPinvokeMap)(mappingFlags & pmThrowOnUnmappableCharMask);
3086 if (unmappableMask == pmThrowOnUnmappableCharEnabled)
3087 SetThrowOnUnmappableChar (TRUE);
3088 else if (unmappableMask == pmThrowOnUnmappableCharDisabled)
3089 SetThrowOnUnmappableChar (FALSE);
3091 // inkFlags : CorPinvoke -> CorNativeLinkFlags
3092 if (mappingFlags & pmSupportsLastError)
3093 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfLastError));
3094 if (mappingFlags & pmNoMangle)
3095 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
3097 // Keep in sync with the handling of CorNativeLinkType in
3098 // PInvokeStaticSigInfo::PInvokeStaticSigInfo.
3100 // charset : CorPinvoke -> CorNativeLinkType
3101 CorPinvokeMap charSetMask = (CorPinvokeMap)(mappingFlags & (pmCharSetNotSpec | pmCharSetAnsi | pmCharSetUnicode | pmCharSetAuto));
3102 if (charSetMask == pmCharSetNotSpec || charSetMask == pmCharSetAnsi)
3104 SetCharSet (nltAnsi);
3106 else if (charSetMask == pmCharSetUnicode)
3108 SetCharSet (nltUnicode);
3110 else if (charSetMask == pmCharSetAuto)
3112 #ifdef PLATFORM_WINDOWS
3113 SetCharSet(nltUnicode);
3115 SetCharSet(nltAnsi); // We don't have a utf8 charset in metadata yet, but ANSI == UTF-8 off-Windows
3120 SetError(IDS_EE_NDIRECT_BADNATL);
3124 #if !defined(CROSSGEN_COMPILE) // IJW
3126 // This function would work, but be unused on Unix. Ifdefing out to avoid build errors due to the unused function.
3127 #if !defined (FEATURE_PAL)
3128 static LPBYTE FollowIndirect(LPBYTE pTarget)
3135 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
3143 AVInRuntimeImplOkayHolder AVOkay;
3146 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3148 pRet = **(LPBYTE**)(pTarget + 2);
3150 #elif defined(_TARGET_AMD64_)
3151 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3153 INT64 rva = *(INT32*)(pTarget + 2);
3154 pRet = *(LPBYTE*)(pTarget + 6 + rva);
3162 EX_END_CATCH(SwallowAllExceptions);
3166 #endif // !FEATURE_PAL
3168 BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget)
3178 #if !defined(FEATURE_PAL)
3179 static LPBYTE pGetLastError = NULL;
3182 // No need to use a holder here, since no cleanup is necessary.
3183 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
3186 pGetLastError = (LPBYTE)GetProcAddress(hMod, "GetLastError");
3189 // This should never happen but better to be cautious.
3190 pGetLastError = (LPBYTE)-1;
3195 // We failed to get the module handle for kernel32.dll. This is almost impossible
3196 // however better to err on the side of caution.
3197 pGetLastError = (LPBYTE)-1;
3201 if (pTarget == pGetLastError)
3204 if (pTarget == NULL)
3207 LPBYTE pTarget2 = FollowIndirect(pTarget);
3210 // jmp [xxxx] - could be an import thunk
3211 return pTarget2 == pGetLastError;
3213 #endif // !FEATURE_PAL
3218 DWORD STDMETHODCALLTYPE FalseGetLastError()
3220 WRAPPER_NO_CONTRACT;
3222 return GetThread()->m_dwLastError;
3225 void PInvokeStaticSigInfo::BestGuessNDirectDefaults(MethodDesc* pMD)
3235 if (!pMD->IsNDirect())
3238 NDirectMethodDesc* pMDD = (NDirectMethodDesc*)pMD;
3240 if (!pMDD->IsEarlyBound())
3243 LPVOID pTarget = NULL;
3245 // NOTE: If we get inside this block, and this is a call to GetLastError,
3246 // then InitEarlyBoundNDirectTarget has not been run yet.
3247 if (pMDD->NDirectTargetIsImportThunk())
3249 // Get the unmanaged callsite.
3250 pTarget = (LPVOID)pMDD->GetModule()->GetInternalPInvokeTarget(pMDD->GetRVA());
3252 // If this is a call to GetLastError, then we haven't overwritten m_pNativeNDirectTarget yet.
3253 if (HeuristicDoesThisLookLikeAGetLastErrorCall((LPBYTE)pTarget))
3254 pTarget = (BYTE*)FalseGetLastError;
3258 pTarget = pMDD->GetNativeNDirectTarget();
3262 #endif // !CROSSGEN_COMPILE
3264 inline CorPinvokeMap GetDefaultCallConv(BOOL bIsVarArg)
3266 #ifdef PLATFORM_UNIX
3267 return pmCallConvCdecl;
3268 #else // PLATFORM_UNIX
3269 return bIsVarArg ? pmCallConvCdecl : pmCallConvStdcall;
3270 #endif // !PLATFORM_UNIX
3273 void PInvokeStaticSigInfo::InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg)
3283 // Convert WinAPI methods to either StdCall or CDecl based on if they are varargs or not.
3284 if (callConv == pmCallConvWinapi)
3285 callConv = GetDefaultCallConv(bIsVarArg);
3287 CorPinvokeMap sigCallConv = (CorPinvokeMap)0;
3288 BOOL fSuccess = MetaSig::GetUnmanagedCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
3292 SetError(IDS_EE_NDIRECT_BADNATL); //Bad metadata format
3295 // Do the same WinAPI to StdCall or CDecl for the signature calling convention as well. We need
3296 // to do this before we check to make sure the PInvoke map calling convention and the
3297 // signature calling convention match for compatibility reasons.
3298 if (sigCallConv == pmCallConvWinapi)
3299 sigCallConv = GetDefaultCallConv(bIsVarArg);
3301 if (callConv != 0 && sigCallConv != 0 && callConv != sigCallConv)
3302 SetError(IDS_EE_NDIRECT_BADNATL_CALLCONV);
3304 if (callConv == 0 && sigCallConv == 0)
3305 m_callConv = GetDefaultCallConv(bIsVarArg);
3306 else if (callConv != 0)
3307 m_callConv = callConv;
3309 m_callConv = sigCallConv;
3311 if (bIsVarArg && m_callConv != pmCallConvCdecl)
3312 SetError(IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV);
3315 void PInvokeStaticSigInfo::ReportErrors()
3326 COMPlusThrow(kTypeLoadException, m_error);
3330 //---------------------------------------------------------
3331 // Does a class or method have a NAT_L CustomAttribute?
3335 // FAILED = unknown because something failed.
3336 //---------------------------------------------------------
3338 HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs)
3346 PRECONDITION(CheckPointer(pInternalImport));
3347 PRECONDITION(TypeFromToken(token) == mdtMethodDef);
3351 // Check method flags first before trying to find the custom value
3352 if (!IsReallyMdPinvokeImpl(dwMemberAttrs))
3356 LPCSTR pszImportName;
3359 if (SUCCEEDED(pInternalImport->GetPinvokeMap(token, &mappingFlags, &pszImportName, &modref)))
3366 // Either MD or signature & module must be given.
3368 BOOL NDirect::MarshalingRequired(MethodDesc *pMD, PCCOR_SIGNATURE pSig /*= NULL*/, Module *pModule /*= NULL*/)
3373 PRECONDITION(pMD != NULL || (pSig != NULL && pModule != NULL));
3377 // As a by-product, when returning FALSE we will also set the native stack size to the MD if it's
3378 // an NDirectMethodDesc. This number is needed to link the P/Invoke (it determines the @n entry
3379 // point name suffix and affects alignment thunk generation on the Mac). If this method returns
3380 // TRUE, the stack size will be set when building the marshaling IL stub.
3381 DWORD dwStackSize = 0;
3382 CorPinvokeMap callConv = (CorPinvokeMap)0;
3386 if (pMD->IsNDirect() || pMD->IsComPlusCall())
3388 // HRESULT swapping is handled by stub
3389 if ((pMD->GetImplAttrs() & miPreserveSig) == 0)
3393 // SetLastError is handled by stub
3394 PInvokeStaticSigInfo sigInfo(pMD);
3395 if (sigInfo.GetLinkFlags() & nlfLastError)
3398 // LCID argument is handled by stub
3399 if (GetLCIDParameterIndex(pMD) != -1)
3402 // making sure that cctor has run may be handled by stub
3403 if (pMD->IsNDirect() && ((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
3406 callConv = sigInfo.GetCallConv();
3411 PREFIX_ASSUME(pMD != NULL);
3413 pSig = pMD->GetSig();
3414 pModule = pMD->GetModule();
3417 // Check to make certain that the signature only contains types that marshal trivially
3418 SigPointer ptr(pSig);
3419 IfFailThrow(ptr.GetCallingConvInfo(NULL));
3421 IfFailThrow(ptr.GetData(&numArgs));
3422 numArgs++; // +1 for return type
3424 // We'll need to parse parameter native types
3425 mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef));
3426 IMDInternalImport *pMDImport = pModule->GetMDImport();
3428 SigTypeContext emptyTypeContext;
3430 mdMethodDef methodToken = mdMethodDefNil;
3433 methodToken = pMD->GetMemberDef();
3435 CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
3437 for (ULONG i = 0; i < numArgs; i++)
3439 SigPointer arg = ptr;
3440 CorElementType type;
3441 IfFailThrow(arg.PeekElemType(&type));
3445 case ELEMENT_TYPE_PTR:
3447 IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR
3448 IfFailThrow(arg.PeekElemType(&type));
3450 if (type == ELEMENT_TYPE_VALUETYPE)
3452 if ((arg.HasCustomModifier(pModule,
3453 "Microsoft.VisualC.NeedsCopyConstructorModifier",
3454 ELEMENT_TYPE_CMOD_REQD)) ||
3455 (arg.HasCustomModifier(pModule,
3456 "System.Runtime.CompilerServices.IsCopyConstructed",
3457 ELEMENT_TYPE_CMOD_REQD)))
3462 if (i > 0) dwStackSize += sizeof(SLOT);
3466 case ELEMENT_TYPE_INTERNAL:
3468 // this check is not functional in DAC and provides no security against a malicious dump
3469 // the DAC is prepared to receive an invalid type handle
3470 #ifndef DACCESS_COMPILE
3471 if (pModule->IsSigInIL(arg.GetPtr()))
3472 THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule);
3477 case ELEMENT_TYPE_VALUETYPE:
3479 TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext);
3481 // JIT can handle internal blittable value types
3482 if (!hndArgType.IsBlittable() && !hndArgType.IsEnum())
3487 #ifdef FEATURE_READYTORUN_COMPILER
3488 if (IsReadyToRunCompilation())
3490 if (!hndArgType.AsMethodTable()->IsLayoutInCurrentVersionBubble())
3495 // return value is fine as long as it can be normalized to an integer
3498 CorElementType normalizedType = hndArgType.GetInternalCorElementType();
3499 if (normalizedType == ELEMENT_TYPE_VALUETYPE)
3501 // it is a structure even after normalization
3507 dwStackSize += StackElemSize(hndArgType.GetSize());
3512 case ELEMENT_TYPE_BOOLEAN:
3513 case ELEMENT_TYPE_CHAR:
3515 // Bool requires marshaling
3516 // Char may require marshaling (MARSHAL_TYPE_ANSICHAR)
3522 if (CorTypeInfo::IsPrimitiveType(type) || type == ELEMENT_TYPE_FNPTR)
3524 if (i > 0) dwStackSize += StackElemSize(CorTypeInfo::Size(type));
3528 // other non-primitive type - requires marshaling
3534 // check for explicit MarshalAs
3535 NativeTypeParamInfo paramInfo;
3537 if (pParamTokenArray[i] != mdParamDefNil)
3539 if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, ¶mInfo) ||
3540 paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT)
3542 // Presence of MarshalAs does not necessitate marshaling (it could as well be the default
3543 // for the type), but it's a good enough heuristic. We definitely don't want to duplicate
3544 // the logic from code:MarshalInfo.MarshalInfo here.
3549 IfFailThrow(ptr.SkipExactlyOne());
3552 if (!FitsInU2(dwStackSize))
3555 // do not set the stack size for varargs - the number is call site specific
3556 if (pMD != NULL && !pMD->IsVarArg())
3558 if (pMD->IsNDirect())
3560 ((NDirectMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize), callConv);
3562 #ifdef FEATURE_COMINTEROP
3563 else if (pMD->IsComPlusCall())
3565 // calling convention is always stdcall
3566 ((ComPlusCallMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize));
3568 #endif // FEATURE_COMINTEROP
3575 // factorization of CreateNDirectStubWorker
3576 static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig,
3578 CorNativeLinkType nlType,
3579 CorNativeLinkFlags nlFlags,
3580 UINT argidx, // this is used for reverse pinvoke hresult swapping
3582 BOOL isInstanceMethod,
3586 UINT& nativeStackOffset,
3587 bool& fStubNeedsCOM,
3589 DEBUG_ARG(LPCUTF8 pDebugName)
3590 DEBUG_ARG(LPCUTF8 pDebugClassName)
3597 PRECONDITION(CheckPointer(params));
3598 PRECONDITION(CheckPointer(pss));
3599 PRECONDITION(CheckPointer(pMD, NULL_OK));
3603 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3605 MarshalInfo::MarshalScenario ms;
3606 #ifdef FEATURE_COMINTEROP
3607 if (SF_IsCOMStub(dwStubFlags))
3609 if (SF_IsWinRTStub(dwStubFlags))
3610 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
3612 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3615 #endif // FEATURE_COMINTEROP
3617 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3620 #ifdef FEATURE_COMINTEROP
3621 if (SF_IsWinRTCtorStub(dwStubFlags))
3623 _ASSERTE(msig.GetReturnType() == ELEMENT_TYPE_VOID);
3624 _ASSERTE(SF_IsHRESULTSwapping(dwStubFlags));
3626 pss->MarshalFactoryReturn();
3627 nativeStackOffset += sizeof(LPVOID);
3628 if (SF_IsWinRTCompositionStub(dwStubFlags))
3630 nativeStackOffset += 2 * sizeof(LPVOID);
3634 #endif // FEATURE_COMINTEROP
3635 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
3637 MarshalInfo returnInfo(msig.GetModule(),
3638 msig.GetReturnProps(),
3639 msig.GetSigTypeContext(),
3646 msig.NumFixedArgs(),
3647 SF_IsBestFit(dwStubFlags),
3648 SF_IsThrowOnUnmappableChar(dwStubFlags),
3653 DEBUG_ARG(pDebugName)
3654 DEBUG_ARG(pDebugClassName)
3658 marshalType = returnInfo.GetMarshalType();
3660 fStubNeedsCOM |= returnInfo.MarshalerRequiresCOM();
3662 #ifdef FEATURE_COMINTEROP
3663 if (marshalType == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3665 // Hidden length arrays are only valid with HRESULT swapping
3666 if (!SF_IsHRESULTSwapping(dwStubFlags))
3668 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3671 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3672 returnInfo.SetHiddenLengthParamIndex(static_cast<UINT16>(nativeArgIndex));
3674 // Inject the hidden argument so that it winds up at the end of the method signature
3675 pss->MarshalHiddenLengthArgument(&returnInfo, TRUE);
3676 nativeStackOffset += returnInfo.GetHiddenLengthParamStackSize();
3678 if (SF_IsReverseStub(dwStubFlags))
3684 if (SF_IsCOMStub(dwStubFlags))
3686 // We don't support native methods that return VARIANTs, non-blittable structs, GUIDs, or DECIMALs directly.
3687 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT ||
3688 marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS ||
3689 marshalType == MarshalInfo::MARSHAL_TYPE_GUID ||
3690 marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL)
3692 if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
3694 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3698 pss->MarshalReturn(&returnInfo, argOffset);
3701 #endif // FEATURE_COMINTEROP
3703 if (marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS
3704 || marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS
3705 || marshalType == MarshalInfo::MARSHAL_TYPE_GUID
3706 || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL
3707 #ifdef FEATURE_COMINTEROP
3708 || marshalType == MarshalInfo::MARSHAL_TYPE_DATETIME
3709 #endif // FEATURE_COMINTEROP
3712 if (SF_IsHRESULTSwapping(dwStubFlags))
3714 // V1 restriction: we could implement this but it's late in the game to do so.
3715 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3718 else if (marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY
3719 || marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET
3720 || marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
3721 #ifdef FEATURE_COMINTEROP
3722 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
3723 #endif // FEATURE_COMINTEROP
3726 // Each of these types are non-blittable and according to its managed size should be returned in a return buffer on x86 in stdcall.
3727 // However, its native size is small enough to be returned by-value.
3728 // We don't know the native type representation early enough to get this correct, so we throw an exception here.
3729 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3731 else if (IsUnsupportedTypedrefReturn(msig))
3733 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3736 #ifdef FEATURE_COMINTEROP
3737 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT && !SF_IsHRESULTSwapping(dwStubFlags))
3739 // No support for returning variants. This is a V1 restriction, due to the late date,
3740 // don't want to add the special-case code to support this in light of low demand.
3741 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NOVARIANTRETURN);
3743 #endif // FEATURE_COMINTEROP
3745 pss->MarshalReturn(&returnInfo, argOffset);
3752 static inline UINT GetStackOffsetFromStackSize(UINT stackSize, bool fThisCall)
3754 LIMITED_METHOD_CONTRACT;
3758 // -1 means that the argument is not on the stack
3759 return (stackSize >= sizeof(SLOT) ? (stackSize - sizeof(SLOT)) : (UINT)-1);
3761 #endif // _TARGET_X86_
3765 #ifdef FEATURE_COMINTEROP
3767 struct HiddenParameterInfo
3769 MarshalInfo *pManagedParam; // Managed parameter which required the hidden parameter
3770 int nativeIndex; // 0 based index into the native method signature where the hidden parameter should be injected
3773 // Get the indexes of any hidden length parameters to be marshaled for the method
3775 // At return, each value in the ppParamIndexes array is a 0 based index into the native method signature where
3776 // the length parameter for a hidden length array should be passed. The MarshalInfo objects will also be
3777 // updated such that they all have explicit marshaling information.
3779 // The caller is responsible for freeing the memory pointed to by ppParamIndexes
3780 void CheckForHiddenParameters(DWORD cParamMarshalInfo,
3781 __in_ecount(cParamMarshalInfo) MarshalInfo *pParamMarshalInfo,
3782 __out DWORD *pcHiddenNativeParameters,
3783 __out HiddenParameterInfo **ppHiddenNativeParameters)
3788 PRECONDITION(CheckPointer(pParamMarshalInfo));
3789 PRECONDITION(CheckPointer(pcHiddenNativeParameters));
3790 PRECONDITION(CheckPointer(ppHiddenNativeParameters));
3794 NewArrayHolder<HiddenParameterInfo> hiddenParamInfo(new HiddenParameterInfo[cParamMarshalInfo]);
3795 DWORD foundInfoCount = 0;
3797 for (DWORD iParam = 0; iParam < cParamMarshalInfo; ++iParam)
3799 // Look for hidden length arrays, which all require additional parameters to be added
3800 if (pParamMarshalInfo[iParam].GetMarshalType() == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3802 DWORD currentNativeIndex = iParam + foundInfoCount;
3804 // The location of the length parameter is implicitly just before the array pointer.
3805 // We'll give it our current index, and bumping the found count will push us back a slot.
3807 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3808 pParamMarshalInfo[iParam].SetHiddenLengthParamIndex(static_cast<UINT16>(currentNativeIndex));
3810 hiddenParamInfo[foundInfoCount].nativeIndex = pParamMarshalInfo[iParam].HiddenLengthParamIndex();
3811 hiddenParamInfo[foundInfoCount].pManagedParam = &(pParamMarshalInfo[iParam]);
3816 *pcHiddenNativeParameters = foundInfoCount;
3817 *ppHiddenNativeParameters = hiddenParamInfo.Extract();
3820 bool IsHiddenParameter(int nativeArgIndex,
3821 DWORD cHiddenParameters,
3822 __in_ecount(cHiddenParameters) HiddenParameterInfo *pHiddenParameters,
3823 __out HiddenParameterInfo **ppHiddenParameterInfo)
3828 PRECONDITION(cHiddenParameters == 0 || CheckPointer(pHiddenParameters));
3829 PRECONDITION(CheckPointer(ppHiddenParameterInfo));
3833 *ppHiddenParameterInfo = NULL;
3835 for (DWORD i = 0; i < cHiddenParameters; ++i)
3837 _ASSERTE(pHiddenParameters[i].nativeIndex != -1);
3838 if (pHiddenParameters[i].nativeIndex == nativeArgIndex)
3840 *ppHiddenParameterInfo = &(pHiddenParameters[i]);
3848 #endif // FEATURE_COMINTEROP
3850 //---------------------------------------------------------
3851 // Creates a new stub for a N/Direct call. Return refcount is 1.
3852 // Note that this function may now throw if it fails to create
3854 //---------------------------------------------------------
3855 static void CreateNDirectStubWorker(StubState* pss,
3856 StubSigDesc* pSigDesc,
3857 CorNativeLinkType nlType,
3858 CorNativeLinkFlags nlFlags,
3859 CorPinvokeMap unmgdCallConv,
3862 mdParamDef* pParamTokenArray,
3870 PRECONDITION(CheckPointer(pss));
3871 PRECONDITION(CheckPointer(pSigDesc));
3872 PRECONDITION(CheckPointer(pMD, NULL_OK));
3873 PRECONDITION(!pMD || pMD->IsILStub() || (0 != pMD->GetMethodTable()->IsDelegate()) == SF_IsDelegateStub(dwStubFlags));
3877 SF_ConsistencyCheck(dwStubFlags);
3880 if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
3881 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
3886 if (SF_IsCOMStub(dwStubFlags))
3888 _ASSERTE(0 == nlType);
3889 _ASSERTE(0 == nlFlags);
3890 _ASSERTE(0 == unmgdCallConv);
3894 _ASSERTE(nlType == nltAnsi || nlType == nltUnicode);
3896 Module *pModule = pSigDesc->m_pModule;
3899 // Set up signature walking objects.
3902 MetaSig msig(pSigDesc->m_sig,
3904 &pSigDesc->m_typeContext);
3906 if (SF_IsVarArgStub(dwStubFlags))
3907 msig.SetTreatAsVarArg();
3909 bool fThisCall = (unmgdCallConv == pmCallConvThiscall);
3911 pss->SetLastError(nlFlags & nlfLastError);
3913 // This has been in the product since forward P/Invoke via delegates was
3914 // introduced. It's wrong, but please keep it for backward compatibility.
3915 if (SF_IsDelegateStub(dwStubFlags))
3916 pss->SetLastError(TRUE);
3918 pss->BeginEmit(dwStubFlags);
3922 // LCID is not supported on WinRT
3923 _ASSERTE(!SF_IsWinRTStub(dwStubFlags));
3925 // The code to handle the LCID will call MarshalLCID before calling MarshalArgument
3926 // on the argument the LCID should go after. So we just bump up the index here.
3930 int numArgs = msig.NumFixedArgs();
3932 // thiscall must have at least one parameter (the "this")
3933 if (fThisCall && numArgs == 0)
3934 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
3937 // Now, emit the IL.
3942 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3945 // Marshal the return value.
3948 UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? sizeof(SLOT) : 0);
3949 bool fHasCopyCtorArgs = false;
3950 bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags);
3952 // Normally we would like this to be false so that we use the correct signature
3953 // in the IL_STUB, (i.e if it returns a value class then the signature will use that)
3954 // When this bool is true we change the return type to void and explicitly add a
3955 // return buffer argument as the first argument so as to match the native calling convention correctly.
3956 BOOL fMarshalReturnValueFirst = FALSE;
3958 BOOL fReverseWithReturnBufferArg = FALSE;
3959 bool isInstanceMethod = fStubNeedsCOM || fThisCall;
3961 // We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping!
3962 // When we are HRESULT-swapping, the managed return type is actually the type of the last parameter and not the return type.
3963 // The native return type of an HRESULT-swapped function is an HRESULT, which never uses a return-buffer argument.
3964 // Since the managed return type is actually the last parameter, we need to marshal it after the last parameter in the managed signature
3965 // to make sure we match the native signature correctly (when marshalling parameters, we add them to the native stub signature).
3966 if (!SF_IsHRESULTSwapping(dwStubFlags))
3968 // We cannot just use pSig.GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums.
3969 bool isReturnTypeValueType = msig.GetRetTypeHandleThrowing().GetVerifierCorElementType() == ELEMENT_TYPE_VALUETYPE;
3970 #if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3971 // JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer.
3972 // Therefore instead we change the signature of calli to return void and make the return buffer as first
3973 // argument. This matches the ABI i.e. return buffer is passed as first arg. So native target will get the
3974 // return buffer in correct register.
3975 // The return structure secret arg comes first, however byvalue return is processed at
3976 // the end because it could be the HRESULT-swapped argument which always comes last.
3979 // For functions with value type class, managed and unmanaged calling convention differ
3980 fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig);
3981 #elif defined(_TARGET_ARM_)
3982 fMarshalReturnValueFirst = HasRetBuffArg(&msig);
3984 // 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).
3985 fMarshalReturnValueFirst = HasRetBuffArg(&msig) || (isInstanceMethod && isReturnTypeValueType);
3986 #endif // UNIX_X86_ABI
3987 #elif defined(_TARGET_AMD64_) || defined (_TARGET_ARM64_)
3988 fMarshalReturnValueFirst = isInstanceMethod && isReturnTypeValueType;
3989 #endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3991 fReverseWithReturnBufferArg = fMarshalReturnValueFirst && SF_IsReverseStub(dwStubFlags);
3996 // Marshal the arguments
3998 MarshalInfo::MarshalScenario ms;
3999 #ifdef FEATURE_COMINTEROP
4000 if (SF_IsCOMStub(dwStubFlags))
4002 if (SF_IsWinRTStub(dwStubFlags))
4003 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
4005 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
4008 #endif // FEATURE_COMINTEROP
4010 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
4013 // Build up marshaling information for each of the method's parameters
4014 SIZE_T cbParamMarshalInfo;
4015 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numArgs, cbParamMarshalInfo))
4017 COMPlusThrowHR(COR_E_OVERFLOW);
4020 NewArrayHolder<BYTE> pbParamMarshalInfo(new BYTE[cbParamMarshalInfo]);
4021 MarshalInfo *pParamMarshalInfo = reinterpret_cast<MarshalInfo *>(pbParamMarshalInfo.GetValue());
4023 MetaSig paramInfoMSig(msig);
4024 for (int i = 0; i < numArgs; ++i)
4026 paramInfoMSig.NextArg();
4027 new(&(pParamMarshalInfo[i])) MarshalInfo(paramInfoMSig.GetModule(),
4028 paramInfoMSig.GetArgProps(),
4029 paramInfoMSig.GetSigTypeContext(),
4030 pParamTokenArray[i + 1],
4037 SF_IsBestFit(dwStubFlags),
4038 SF_IsThrowOnUnmappableChar(dwStubFlags),
4040 isInstanceMethod ? TRUE : FALSE,
4043 DEBUG_ARG(pSigDesc->m_pDebugName)
4044 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4048 #ifdef FEATURE_COMINTEROP
4049 // Check to see if we need to inject any additional hidden parameters
4050 DWORD cHiddenNativeParameters;
4051 NewArrayHolder<HiddenParameterInfo> pHiddenNativeParameters;
4052 CheckForHiddenParameters(numArgs, pParamMarshalInfo, &cHiddenNativeParameters, &pHiddenNativeParameters);
4054 // Hidden parameters and LCID do not mix
4055 _ASSERTE(!(cHiddenNativeParameters > 0 && iLCIDArg != -1));
4056 #endif // FEATURE_COMINTEROP
4058 // Marshal the parameters
4060 int nativeArgIndex = 0;
4062 // If we are generating a return buffer on a member function that is marked as thiscall (as opposed to being a COM method)
4063 // then we need to marshal the this parameter first and the return buffer second.
4064 // We don't need to do this for COM methods because the "this" is implied as argument 0 by the signature of the stub.
4065 if (fThisCall && fMarshalReturnValueFirst)
4069 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
4070 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
4071 nativeStackSize += info.GetNativeArgSize();
4073 fStubNeedsCOM |= info.MarshalerRequiresCOM();
4075 // make sure that the first parameter is enregisterable
4076 if (info.GetNativeArgSize() > sizeof(SLOT))
4077 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
4082 // If we're doing a native->managed call and are generating a return buffer,
4083 // 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).
4084 if (fReverseWithReturnBufferArg)
4089 if (fMarshalReturnValueFirst)
4091 marshalType = DoMarshalReturnValue(msig,
4104 DEBUG_ARG(pSigDesc->m_pDebugName)
4105 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4108 if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE ||
4109 marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY ||
4110 marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET ||
4111 marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF ||
4112 marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
4113 #ifdef FEATURE_COMINTEROP
4114 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
4115 #endif // FEATURE_COMINTEROP
4118 // These are special non-blittable types returned by-ref in managed,
4119 // but marshaled as primitive values returned by-value in unmanaged.
4123 // This is an ordinary value type - see if it is returned by-ref.
4124 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4125 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4127 nativeStackSize += sizeof(LPVOID);
4132 while (argidx <= numArgs)
4134 #ifdef FEATURE_COMINTEROP
4135 HiddenParameterInfo *pHiddenParameter;
4136 // Check to see if we need to inject a hidden parameter
4137 if (IsHiddenParameter(nativeArgIndex, cHiddenNativeParameters, pHiddenNativeParameters, &pHiddenParameter))
4139 pss->MarshalHiddenLengthArgument(pHiddenParameter->pManagedParam, FALSE);
4140 nativeStackSize += pHiddenParameter->pManagedParam->GetHiddenLengthParamStackSize();
4142 if (SF_IsReverseStub(dwStubFlags))
4148 #endif // FEATURE_COMINTEROP
4151 // Check to see if this is the parameter after which we need to insert the LCID.
4153 if (argidx == iLCIDArg)
4155 pss->MarshalLCID(argidx);
4156 nativeStackSize += sizeof(LPVOID);
4158 if (SF_IsReverseStub(dwStubFlags))
4164 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
4166 #ifdef FEATURE_COMINTEROP
4167 // For the hidden-length array, length parameters must occur before the parameter containing the array pointer
4168 _ASSERTE(info.GetMarshalType() != MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY || nativeArgIndex > info.HiddenLengthParamIndex());
4169 #endif // FEATURE_COMINTEROP
4171 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
4172 nativeStackSize += info.GetNativeArgSize();
4174 fStubNeedsCOM |= info.MarshalerRequiresCOM();
4176 if (fThisCall && argidx == 1)
4178 // make sure that the first parameter is enregisterable
4179 if (info.GetNativeArgSize() > sizeof(SLOT))
4180 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
4183 fHasCopyCtorArgs = info.GetMarshalType() == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR ? TRUE : FALSE;
4191 // Check to see if this is the parameter after which we need to insert the LCID.
4192 if (argidx == iLCIDArg)
4194 pss->MarshalLCID(argidx);
4195 nativeStackSize += sizeof(LPVOID);
4197 if (SF_IsReverseStub(dwStubFlags))
4201 if (!fMarshalReturnValueFirst)
4203 // This could be a HRESULT-swapped argument so it must come last.
4204 marshalType = DoMarshalReturnValue(msig,
4217 DEBUG_ARG(pSigDesc->m_pDebugName)
4218 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4221 // If the return value is a SafeHandle or CriticalHandle, mark the stub method.
4222 // Interop methods that use this stub will have an implicit reliability contract
4223 // (see code:TAStackCrawlCallBack).
4224 if (!SF_IsHRESULTSwapping(dwStubFlags))
4226 if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE ||
4227 marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE)
4229 if (pMD->IsDynamicMethod())
4230 pMD->AsDynamicMethodDesc()->SetUnbreakable(true);
4235 if (SF_IsHRESULTSwapping(dwStubFlags))
4237 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
4238 nativeStackSize += sizeof(LPVOID);
4241 if (pMD->IsDynamicMethod())
4243 // Set the native stack size to the IL stub MD. It is needed for alignment
4244 // thunk generation on the Mac and stdcall name decoration on Windows.
4245 // We do not store it directly in the interop MethodDesc here because due
4246 // to sharing we come here only for the first call with given signature and
4247 // the target MD may even be NULL.
4252 _ASSERTE(nativeStackSize >= sizeof(SLOT));
4253 nativeStackSize -= sizeof(SLOT);
4255 #else // _TARGET_X86_
4257 // The algorithm to compute nativeStackSize on the fly is x86-specific.
4258 // Recompute the correct size for other platforms from the stub signature.
4260 if (SF_IsForwardStub(dwStubFlags))
4262 // It would be nice to compute the correct value for forward stubs too.
4263 // The value is only used in MarshalNative::NumParamBytes right now,
4264 // and changing what MarshalNative::NumParamBytes returns is
4265 // a potential breaking change.
4269 // native stack size is updated in code:ILStubState.SwapStubSignatures
4271 #endif // _TARGET_X86_
4273 if (!FitsInU2(nativeStackSize))
4274 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
4276 DynamicMethodDesc *pDMD = pMD->AsDynamicMethodDesc();
4278 pDMD->SetNativeStackArgSize(static_cast<WORD>(nativeStackSize));
4279 pDMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
4280 pDMD->SetStubNeedsCOMStarted(fStubNeedsCOM);
4283 // FinishEmit needs to know the native stack arg size so we call it after the number
4284 // has been set in the stub MD (code:DynamicMethodDesc.SetNativeStackArgSize)
4285 pss->FinishEmit(pMD);
4288 class NDirectStubHashBlob : public ILStubHashBlobBase
4293 WORD m_unmgdCallConv;
4294 BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob
4301 BYTE m_rgbSigAndParamData[1];
4302 // (dwParamAttr, cbNativeType) // length: number of parameters
4303 // NativeTypeBlob // length: number of parameters
4304 // BYTE m_rgbSigData[]; // length: determined by sig walk
4307 // For better performance and less memory fragmentation,
4308 // I'm using structure here to avoid allocating 3 different arrays.
4313 PCCOR_SIGNATURE pvNativeType;
4316 ILStubHashBlob* NDirect::CreateHashBlob(NDirectStubParameters* pParams)
4318 STANDARD_VM_CONTRACT;
4320 NDirectStubHashBlob* pBlob;
4322 IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport();
4324 CQuickBytes paramInfoBytes;
4325 paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens);
4326 ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr();
4327 ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens);
4329 size_t cbNativeTypeTotal = 0;
4332 // Collect information for function parameters
4334 for (int idx = 0; idx < pParams->m_nParamTokens; idx++)
4336 mdParamDef token = pParams->m_pParamTokenArray[idx];
4337 if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token)
4339 USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted
4340 LPCSTR szParamName_Ignore;
4341 IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, ¶mInfos[idx].dwParamAttr, &szParamName_Ignore));
4343 if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal)
4345 IfFailThrow(pInternalImport->GetFieldMarshal(token, ¶mInfos[idx].pvNativeType, ¶mInfos[idx].cbNativeType));
4346 cbNativeTypeTotal += paramInfos[idx].cbNativeType;
4351 SigPointer sigPtr = pParams->m_sig.CreateSigPointer();
4353 // note that ConvertToInternalSignature also resolves generics so different instantiations will get different
4354 // hash blobs for methods that have generic parameters in their signature
4355 SigBuilder sigBuilder;
4356 sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE);
4359 PVOID pSig = sigBuilder.GetSignature(&cbSig);
4362 // Build hash blob for IL stub sharing
4364 S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) +
4365 S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes
4366 S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size
4367 S_SIZE_T(cbNativeTypeTotal) + // Native type blob data
4368 S_SIZE_T(cbSig); // Signature
4370 if (cbSizeOfBlob.IsOverflow())
4371 COMPlusThrowHR(COR_E_OVERFLOW);
4373 static_assert_no_msg(nltMaxValue <= 0xFF);
4374 static_assert_no_msg(nlfMaxValue <= 0xFF);
4375 static_assert_no_msg(pmMaxValue <= 0xFFFF);
4377 NewArrayHolder<BYTE> pBytes = new BYTE[cbSizeOfBlob.Value()];
4378 // zero out the hash bytes to ensure all bit fields are deterministically set
4379 ZeroMemory(pBytes, cbSizeOfBlob.Value());
4380 pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes;
4382 pBlob->m_pModule = NULL;
4384 if (SF_IsNGENedStub(pParams->m_dwStubFlags))
4386 // don't share across modules if we are ngening the stub
4387 pBlob->m_pModule = pParams->m_pModule;
4390 pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value();
4391 pBlob->m_unmgdCallConv = static_cast<WORD>(pParams->m_unmgdCallConv);
4392 pBlob->m_nlType = static_cast<BYTE>(pParams->m_nlType);
4393 pBlob->m_nlFlags = static_cast<BYTE>(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub
4394 pBlob->m_iLCIDArg = pParams->m_iLCIDArg;
4396 pBlob->m_StubFlags = pParams->m_dwStubFlags;
4397 pBlob->m_nParams = pParams->m_nParamTokens;
4399 BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0];
4402 // Write (dwParamAttr, cbNativeType) for parameters
4404 // Note that these need to be aligned and it is why they are written before the byte blobs
4405 // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs
4407 _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0);
4408 _ASSERTE(sizeof(DWORD) == sizeof(ULONG));
4410 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4412 // We only care about In/Out/HasFieldMarshal
4413 // Other attr are about optional/default values which are not used in marshalling,
4414 // but only used in compilers
4415 *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal);
4416 pBlobParams += sizeof(DWORD);
4418 *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType;
4419 pBlobParams += sizeof(ULONG);
4423 // Write native type blob for parameters
4425 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4427 memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType);
4428 pBlobParams += paramInfos[i].cbNativeType;
4434 memcpy(pBlobParams, pSig, cbSig);
4436 // Verify that we indeed have reached the end
4437 _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value());
4439 pBytes.SuppressRelease();
4440 return (ILStubHashBlob*)pBlob;
4444 ILStubCache* NDirect::GetILStubCache(NDirectStubParameters* pParams)
4454 // Use the m_pLoaderModule instead of m_pModule
4455 // They could be different for methods on generic types.
4456 return pParams->m_pLoaderModule->GetILStubCache();
4460 MethodDesc* NDirect::GetStubMethodDesc(
4461 MethodDesc *pTargetMD,
4462 NDirectStubParameters* pParams,
4463 ILStubHashBlob* pHashParams,
4464 AllocMemTracker* pamTracker,
4465 bool& bILStubCreator,
4466 MethodDesc* pLastMD)
4468 CONTRACT(MethodDesc*)
4472 PRECONDITION(CheckPointer(pParams));
4473 PRECONDITION(!pParams->m_sig.IsEmpty());
4474 PRECONDITION(CheckPointer(pParams->m_pModule));
4475 PRECONDITION(CheckPointer(pTargetMD, NULL_OK));
4476 POSTCONDITION(CheckPointer(RETVAL));
4482 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4484 pMD = pCache->GetStubMethodDesc(pTargetMD,
4486 pParams->m_dwStubFlags,
4488 pParams->m_sig.GetRawSig(),
4489 pParams->m_sig.GetRawSigLen(),
4499 void NDirect::RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams)
4505 PRECONDITION(CheckPointer(pParams));
4506 PRECONDITION(CheckPointer(pHashParams));
4507 PRECONDITION(!pParams->m_sig.IsEmpty());
4508 PRECONDITION(CheckPointer(pParams->m_pModule));
4512 LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams));
4514 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4516 pCache->DeleteEntry(pHashParams);
4520 void NDirect::AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD)
4526 PRECONDITION(CheckPointer(pParams));
4527 PRECONDITION(!pParams->m_sig.IsEmpty());
4528 PRECONDITION(CheckPointer(pParams->m_pModule));
4532 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4534 pCache->AddMethodDescChunkWithLockTaken(pMD);
4538 // Additional factorization of CreateNDirectStub. This hoists all the metadata accesses
4539 // into one location so that we can leave CreateNDirectStubWorker to just generate the
4540 // IL. This allows us to cache a stub based on the inputs to CreateNDirectStubWorker
4541 // instead of having to generate the IL first before doing the caching.
4543 void CreateNDirectStubAccessMetadata(StubSigDesc* pSigDesc, // IN
4544 CorPinvokeMap unmgdCallConv, // IN
4545 DWORD* pdwStubFlags, // IN/OUT
4546 int* piLCIDArg, // OUT
4547 int* pNumArgs // OUT
4550 STANDARD_VM_CONTRACT;
4552 if (SF_IsCOMStub(*pdwStubFlags))
4554 _ASSERTE(0 == unmgdCallConv);
4558 if (unmgdCallConv != pmCallConvStdcall &&
4559 unmgdCallConv != pmCallConvCdecl &&
4560 unmgdCallConv != pmCallConvThiscall)
4562 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
4566 #ifdef FEATURE_COMINTEROP
4567 if (SF_IsDelegateStub(*pdwStubFlags))
4569 _ASSERTE(!SF_IsWinRTStub(*pdwStubFlags));
4570 if (pSigDesc->m_pMD->GetMethodTable()->IsProjectedFromWinRT())
4572 // We do not allow P/Invoking via WinRT delegates to better segregate WinRT
4573 // from classic interop scenarios.
4574 COMPlusThrow(kMarshalDirectiveException, IDS_EE_DELEGATEPINVOKE_WINRT);
4577 #endif // FEATURE_COMINTEROP
4579 MetaSig msig(pSigDesc->m_sig,
4580 pSigDesc->m_pModule,
4581 &pSigDesc->m_typeContext);
4583 if (SF_IsVarArgStub(*pdwStubFlags))
4584 msig.SetTreatAsVarArg();
4586 (*pNumArgs) = msig.NumFixedArgs();
4588 IMDInternalImport* pInternalImport = pSigDesc->m_pModule->GetMDImport();
4590 _ASSERTE(!SF_IsHRESULTSwapping(*pdwStubFlags));
4592 mdMethodDef md = pSigDesc->m_tkMethodDef;
4593 if (md != mdMethodDefNil)
4595 DWORD dwDescrOffset;
4597 IfFailThrow(pInternalImport->GetMethodImplProps(
4602 #ifdef FEATURE_COMINTEROP
4603 if (SF_IsWinRTStub(*pdwStubFlags))
4605 // All WinRT methods do HRESULT swapping
4606 if (IsMiPreserveSig(dwImplFlags))
4608 COMPlusThrow(kMarshalDirectiveException, IDS_EE_PRESERVESIG_WINRT);
4611 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4614 #endif // FEATURE_COMINTEROP
4615 if (SF_IsReverseStub(*pdwStubFlags))
4617 // only COM-to-CLR call supports hresult swapping in the reverse direction
4618 if (SF_IsCOMStub(*pdwStubFlags) && !IsMiPreserveSig(dwImplFlags))
4620 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4625 // fwd pinvoke, fwd com interop support hresult swapping.
4626 // delegate to an unmanaged method does not.
4627 if (!IsMiPreserveSig(dwImplFlags) && !SF_IsDelegateStub(*pdwStubFlags))
4629 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4634 if (pSigDesc->m_pMD != NULL)
4636 (*piLCIDArg) = GetLCIDParameterIndex(pSigDesc->m_pMD);
4643 // Check to see if we need to do LCID conversion.
4644 if ((*piLCIDArg) != -1 && (*piLCIDArg) > (*pNumArgs))
4646 COMPlusThrow(kIndexOutOfRangeException, IDS_EE_INVALIDLCIDPARAM);
4649 if (SF_IsCOMStub(*pdwStubFlags) && !SF_IsWinRTStaticStub(*pdwStubFlags))
4651 CONSISTENCY_CHECK(msig.HasThis());
4655 if (msig.HasThis() && !SF_IsDelegateStub(*pdwStubFlags))
4657 COMPlusThrow(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC);
4662 void NDirect::PopulateNDirectMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, BOOL throwOnError /*= TRUE*/)
4664 if (pNMD->IsSynchronized() && throwOnError)
4665 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
4667 WORD ndirectflags = 0;
4668 if (pNMD->MethodDesc::IsVarArg())
4669 ndirectflags |= NDirectMethodDesc::kVarArgs;
4671 LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
4672 new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName,
4673 (throwOnError ? PInvokeStaticSigInfo::THROW_ON_ERROR : PInvokeStaticSigInfo::NO_THROW_ON_ERROR));
4675 if (pSigInfo->GetCharSet() == nltAnsi)
4676 ndirectflags |= NDirectMethodDesc::kNativeAnsi;
4678 CorNativeLinkFlags linkflags = pSigInfo->GetLinkFlags();
4679 if (linkflags & nlfLastError)
4680 ndirectflags |= NDirectMethodDesc::kLastError;
4681 if (linkflags & nlfNoMangle)
4682 ndirectflags |= NDirectMethodDesc::kNativeNoMangle;
4684 CorPinvokeMap callConv = pSigInfo->GetCallConv();
4685 if (callConv == pmCallConvStdcall)
4686 ndirectflags |= NDirectMethodDesc::kStdCall;
4687 if (callConv == pmCallConvThiscall)
4688 ndirectflags |= NDirectMethodDesc::kThisCall;
4690 if (pNMD->GetLoaderModule()->IsSystem() && strcmp(szLibName, "QCall") == 0)
4692 ndirectflags |= NDirectMethodDesc::kIsQCall;
4696 EnsureWritablePages(&pNMD->ndirect);
4697 pNMD->ndirect.m_pszLibName.SetValueMaybeNull(szLibName);
4698 pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(szEntryPointName);
4702 if (ndirectflags & NDirectMethodDesc::kStdCall)
4704 // Compute the kStdCallWithRetBuf flag which is needed at link time for entry point mangling.
4706 ArgIterator argit(&msig);
4707 if (argit.HasRetBuffArg())
4709 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4710 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4712 ndirectflags |= NDirectMethodDesc::kStdCallWithRetBuf;
4716 #endif // _TARGET_X86_
4718 // Call this exactly ONCE per thread. Do not publish incomplete prestub flags
4719 // or you will introduce a race condition.
4720 pNMD->InterlockedSetNDirectFlags(ndirectflags);
4723 #ifdef FEATURE_COMINTEROP
4724 // Find the MethodDesc of the predefined IL stub method by either
4725 // 1) looking at redirected adapter interfaces, OR
4726 // 2) looking at special attributes for the specific interop scenario (specified by dwStubFlags).
4727 // Currently only ManagedToNativeComInteropStubAttribute is supported.
4728 // It returns NULL if no such attribute(s) can be found.
4729 // But if the attribute is found and is invalid, or something went wrong in the looking up
4730 // process, an exception will be thrown. If everything goes well, you'll get the MethodDesc
4731 // of the stub method
4732 HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, MethodDesc **ppRetStubMD)
4739 PRECONDITION(CheckPointer(pTargetMD));
4740 PRECONDITION(CheckPointer(ppRetStubMD));
4741 PRECONDITION(*ppRetStubMD == NULL);
4747 MethodTable *pTargetMT = pTargetMD->GetMethodTable();
4749 // Check if this is a redirected interface - we have static stubs in mscorlib for those.
4750 if (SF_IsForwardCOMStub(dwStubFlags) && pTargetMT->IsInterface())
4753 // Redirect generic redirected interfaces to the corresponding adapter methods in mscorlib
4754 if (pTargetMT->HasInstantiation())
4756 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(pTargetMD, TypeHandle::Interop_ManagedToNative);
4757 if (pAdapterMD != NULL)
4759 *ppRetStubMD = pAdapterMD;
4766 // Find out if we have the attribute
4771 // Support v-table forward classic COM interop calls only
4772 if (SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags) && !SF_IsWinRTStub(dwStubFlags))
4774 if (pTargetMT->HasInstantiation())
4776 // ManagedToNativeComInteropStubAttribute is not supported with generics
4780 if (pTargetMD->IsFCall())
4782 // ManagedToNativeComInteropStubAttribute is not supported on FCalls (i.e. methods on legacy
4783 // interfaces forwarded to CustomMarshalers.dll such as IEnumerable::GetEnumerator)
4786 _ASSERTE(pTargetMD->IsComPlusCall());
4788 if (pTargetMD->IsInterface())
4790 _ASSERTE(!pTargetMD->GetAssembly()->IsWinMD());
4791 hr = pTargetMD->GetMDImport()->GetCustomAttributeByName(
4792 pTargetMD->GetMemberDef(),
4793 FORWARD_INTEROP_STUB_METHOD_TYPE,
4799 // GetCustomAttributeByName returns S_FALSE when it cannot find the attribute but nothing fails...
4800 // Translate that to E_FAIL
4801 else if (hr == S_FALSE)
4806 // We are dealing with the class, use the interface MD instead
4807 // After second thought I believe we don't need to check the class MD.
4808 // We can think stubs as part of public interface, and if the interface is public,
4809 // the stubs should also be accessible
4810 MethodDesc *pInterfaceMD = pTargetMD->GetInterfaceMD();
4813 hr = FindPredefinedILStubMethod(pInterfaceMD, dwStubFlags, ppRetStubMD);
4824 // Parse the attribute
4826 CustomAttributeParser parser(pBytes, cbBytes);
4827 IfFailRet(parser.SkipProlog());
4831 IfFailRet(parser.GetNonEmptyString(&pTypeName, &cbTypeName));
4833 LPCUTF8 pMethodName;
4835 IfFailRet(parser.GetNonEmptyString(&pMethodName, &cbMethodName));
4837 StackSString typeName(SString::Utf8, pTypeName, cbTypeName);
4838 StackSString methodName(SString::Utf8, pMethodName, cbMethodName);
4841 // Retrieve the type
4843 TypeHandle stubClassType;
4844 stubClassType = TypeName::GetTypeUsingCASearchRules(typeName.GetUnicode(), pTargetMT->GetAssembly());
4846 MethodTable *pStubClassMT = stubClassType.AsMethodTable();
4848 StackSString stubClassName;
4849 pStubClassMT->_GetFullyQualifiedNameForClassNestedAware(stubClassName);
4851 StackSString targetInterfaceName;
4852 pTargetMT->_GetFullyQualifiedNameForClassNestedAware(targetInterfaceName);
4854 // Restrict to same assembly only to reduce test cost
4855 if (stubClassType.GetAssembly() != pTargetMT->GetAssembly())
4859 IDS_EE_INTEROP_STUB_CA_MUST_BE_WITHIN_SAME_ASSEMBLY,
4860 stubClassName.GetUnicode(),
4861 targetInterfaceName.GetUnicode()
4865 if (stubClassType.HasInstantiation())
4869 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_GENERIC,
4870 stubClassName.GetUnicode()
4874 if (stubClassType.IsInterface())
4878 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_INTERFACE,
4879 stubClassName.GetUnicode()
4884 // Locate the MethodDesc for the stub method
4886 MethodDesc *pStubMD = NULL;
4889 PCCOR_SIGNATURE pTargetSig = NULL;
4890 DWORD pcTargetSig = 0;
4892 SigTypeContext typeContext; // NO generics supported
4894 pTargetMD->GetSig(&pTargetSig, &pcTargetSig);
4896 MetaSig msig(pTargetSig,
4898 pTargetMD->GetModule(),
4900 _ASSERTE(msig.HasThis());
4902 SigBuilder stubSigBuilder;
4905 // Append calling Convention, NumOfArgs + 1,
4907 stubSigBuilder.AppendByte(msig.GetCallingConvention() & ~IMAGE_CEE_CS_CALLCONV_HASTHIS);
4908 stubSigBuilder.AppendData(msig.NumFixedArgs() + 1);
4911 // Append return type
4913 SigPointer pReturn = msig.GetReturnProps();
4914 LPBYTE pReturnTypeBegin = (LPBYTE)pReturn.GetPtr();
4915 IfFailThrow(pReturn.SkipExactlyOne());
4916 LPBYTE pReturnTypeEnd = (LPBYTE)pReturn.GetPtr();
4918 stubSigBuilder.AppendBlob(pReturnTypeBegin, pReturnTypeEnd - pReturnTypeBegin);
4923 stubSigBuilder.AppendElementType(ELEMENT_TYPE_CLASS);
4924 stubSigBuilder.AppendToken(pTargetMT->GetCl());
4927 // Copy rest of the arguments
4929 if (msig.NextArg() != ELEMENT_TYPE_END)
4931 SigPointer pFirstArg = msig.GetArgProps();
4932 LPBYTE pArgBegin = (LPBYTE) pFirstArg.GetPtr();
4933 LPBYTE pArgEnd = (LPBYTE) pTargetSig + pcTargetSig;
4935 stubSigBuilder.AppendBlob(pArgBegin, pArgEnd - pArgBegin);
4939 // Allocate new memory and copy over
4941 DWORD pcStubSig = 0;
4942 PCCOR_SIGNATURE pStubSig = (PCCOR_SIGNATURE) stubSigBuilder.GetSignature(&pcStubSig);
4945 // Find method using name + signature
4947 StackScratchBuffer buffer;
4948 LPCUTF8 szMethodNameUTF8 = methodName.GetUTF8(buffer);
4949 pStubMD = MemberLoader::FindMethod(stubClassType.GetMethodTable(),
4953 pTargetMT->GetModule());
4955 if (pStubMD == NULL)
4964 pTargetMD->GetMDImport(),
4967 // Unfortunately the PrettyPrintSig doesn't print 'static' when the function is static
4968 // so we need to append 'static' here. No need to localize
4969 SString signature(SString::Utf8, (LPCUTF8)"static ");
4970 signature.AppendUTF8((LPCUTF8) qbSig.Ptr());
4973 kMissingMethodException,
4974 IDS_EE_INTEROP_STUB_CA_STUB_METHOD_MISSING,
4975 signature.GetUnicode(),
4976 stubClassName.GetUnicode()
4983 // Check the Stub MD
4986 // Verify that the target interop method can call the stub method
4988 _ASSERTE(pTargetMD != NULL);
4990 StaticAccessCheckContext accessContext(pTargetMD, pTargetMT);
4992 if (!ClassLoader::CanAccess(
4995 stubClassType.GetAssembly(),
4996 pStubMD->GetAttrs(),
5000 StackSString interopMethodName(SString::Utf8, pTargetMD->GetName());
5003 kMethodAccessException,
5004 IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD,
5005 interopMethodName.GetUnicode(),
5006 methodName.GetUnicode()
5010 // The FindMethod call will make sure that it is static by matching signature.
5011 // So there is no need to check and throw
5012 _ASSERTE(pStubMD->IsStatic());
5014 *ppRetStubMD = pStubMD;
5018 #endif // FEATURE_COMINTEROP
5020 MethodDesc* CreateInteropILStub(
5022 StubSigDesc* pSigDesc,
5023 CorNativeLinkType nlType,
5024 CorNativeLinkFlags nlFlags,
5025 CorPinvokeMap unmgdCallConv,
5026 DWORD dwStubFlags, // NDirectStubFlags
5028 mdParamDef* pParamTokenArray,
5032 CONTRACT(MethodDesc*)
5036 PRECONDITION(CheckPointer(pSigDesc));
5037 POSTCONDITION(CheckPointer(RETVAL));
5042 ///////////////////////////////
5044 // MethodDesc creation
5046 ///////////////////////////////
5048 MethodDesc* pStubMD = NULL;
5050 Module* pModule = pSigDesc->m_pModule;
5051 Module* pLoaderModule = pSigDesc->m_pLoaderModule;
5052 MethodDesc* pTargetMD = pSigDesc->m_pMD;
5054 // pTargetMD may be null in the case of calli pinvoke
5055 // and vararg pinvoke.
5058 #ifdef FEATURE_COMINTEROP
5060 // Try to locate predefined IL stub either defined in user code or hardcoded in CLR
5061 // If there is one, use the pointed method as the stub.
5062 // Skip pTargetMD == NULL case for reverse interop calls
5064 if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD)))
5066 #ifndef CROSSGEN_COMPILE
5067 // We are about to execute method in pStubMD which could be in another module.
5068 // Call EnsureActive before make the call
5069 // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here
5070 pStubMD->EnsureActive();
5072 if (pStubMD->IsPreImplemented())
5073 RestoreNGENedStub(pStubMD);
5078 #endif // FEATURE_COMINTEROP
5080 // Otherwise, fall back to generating IL stub on-the-fly
5081 NDirectStubParameters params(pSigDesc->m_sig,
5082 &pSigDesc->m_typeContext,
5094 // The following two ILStubCreatorHelperHolder are to recover the status when an
5095 // exception happen during the generation of the IL stubs. We need to free the
5096 // memory allocated and restore the ILStubCache.
5098 // The following block is logically divided into two phases. The first phase is
5099 // CreateOrGet IL Stub phase which we take a domain level lock. The second phase
5100 // is IL generation phase which we take a MethodDesc level lock. Taking two locks
5101 // is mainly designed for performance.
5103 // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the
5104 // allocated memory during the creation of MethodDesc so that we are able to remove
5105 // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper
5107 // When removing IL Stub from Cache, we have a constraint that only the thread which
5108 // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will
5109 // remove it from cache if OOM occurs
5112 ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, ¶ms);
5114 // take the domain level lock
5115 ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock());
5118 // The holder will free the allocated MethodDesc and restore the ILStubCache
5119 // if exception happen.
5120 ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper);
5121 pStubMD = pCreateOrGetStubHolder->GetStubMD();
5123 ///////////////////////////////
5127 ///////////////////////////////
5130 // take the MethodDesc level locker
5131 ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5133 ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
5135 // We can release the holder for the first phase now
5136 pCreateOrGetStubHolder.SuppressRelease();
5139 // The holder will free the allocated MethodDesc and restore the ILStubCache
5140 // if exception happen. The reason to get the holder again is to
5141 ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper);
5143 if (!pEntryLock.DeadlockAwareAcquire())
5145 // the IL generation is not recursive!
5146 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5149 if (SF_IsSharedStub(params.m_dwStubFlags))
5151 // Assure that pStubMD we have now has not been destroyed by other threads
5152 pGenILHolder->GetStubMethodDesc();
5154 while (pStubMD != pGenILHolder->GetStubMD())
5156 pStubMD = pGenILHolder->GetStubMD();
5158 pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5159 pEntryLock.Assign(pEntry, FALSE);
5161 if (!pEntryLock.DeadlockAwareAcquire())
5163 // the IL generation is not recursive!
5164 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5167 pGenILHolder->GetStubMethodDesc();
5173 // We have the entry lock now, we can release the global lock
5174 pILStubLock.Release();
5176 if (pEntry->m_hrResultCode != S_FALSE)
5178 // We came in to generate the IL but someone
5179 // beat us so there's nothing to do
5183 ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
5185 CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc()));
5187 if (pResolver->IsILGenerated())
5189 // this stub already has its IL generated
5194 // Check that the stub signature and MethodDesc are compatible. The JIT
5195 // interface functions depend on this.
5199 SigPointer ptr = pSigDesc->m_sig.CreateSigPointer();
5202 IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo));
5204 BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS);
5206 // CreateNDirectStubWorker will throw an exception for these cases.
5207 BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags);
5209 if (fSigIsStatic || fCanHaveThis)
5211 CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic);
5216 ILStubGenHolder sgh(pResolver);
5218 pResolver->SetStubMethodDesc(pStubMD);
5219 pResolver->SetStubTargetMethodDesc(pTargetMD);
5221 CreateNDirectStubWorker(pss,
5231 pResolver->SetTokenLookupMap(pss->GetTokenLookupMap());
5233 pResolver->SetStubTargetMethodSig(
5234 pss->GetStubTargetMethodSig(),
5235 pss->GetStubTargetMethodSigLength());
5237 // we successfully generated the IL stub
5238 sgh.SuppressRelease();
5241 pEntry->m_hrResultCode = S_OK;
5245 // Link the MethodDesc onto the method table with the lock taken
5246 NDirect::AddMethodDescChunkWithLockTaken(¶ms, pStubMD);
5248 pGenILHolder.SuppressRelease();
5252 ilStubCreatorHelper.SuppressRelease();
5255 #if defined(_TARGET_X86_)
5256 if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg())
5258 // copy the stack arg byte count from the stub MD to the target MD - this number is computed
5259 // during stub generation and is copied to all target MDs that share the stub
5260 // (we don't set it for varargs - the number is call site specific)
5261 // also copy the "takes parameters with copy constructors" flag which is needed to generate
5262 // appropriate intercept stub
5264 WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
5265 BOOL fHasCopyCtorArgs = pStubMD->AsDynamicMethodDesc()->HasCopyCtorArgs();
5267 if (pTargetMD->IsNDirect())
5269 NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD;
5271 pTargetNMD->SetStackArgumentSize(cbStackArgSize, (CorPinvokeMap)0);
5272 pTargetNMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5274 #ifdef FEATURE_COMINTEROP
5277 if (SF_IsCOMStub(dwStubFlags))
5279 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD);
5281 if (pComInfo != NULL)
5283 pComInfo->SetStackArgumentSize(cbStackArgSize);
5284 pComInfo->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5288 #endif // FEATURE_COMINTEROP
5290 #endif // defined(_TARGET_X86_)
5295 MethodDesc* NDirect::CreateCLRToNativeILStub(
5296 StubSigDesc* pSigDesc,
5297 CorNativeLinkType nlType,
5298 CorNativeLinkFlags nlFlags,
5299 CorPinvokeMap unmgdCallConv,
5300 DWORD dwStubFlags) // NDirectStubFlags
5302 CONTRACT(MethodDesc*)
5306 PRECONDITION(CheckPointer(pSigDesc));
5307 POSTCONDITION(CheckPointer(RETVAL));
5313 int numParamTokens = 0;
5314 mdParamDef* pParamTokenArray = NULL;
5316 CreateNDirectStubAccessMetadata(pSigDesc,
5322 Module *pModule = pSigDesc->m_pModule;
5323 numParamTokens = numArgs + 1;
5324 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5325 CollateParamTokens(pModule->GetMDImport(), pSigDesc->m_tkMethodDef, numArgs, pParamTokenArray);
5327 MethodDesc *pMD = pSigDesc->m_pMD;
5329 NewHolder<ILStubState> pStubState;
5331 #ifdef FEATURE_COMINTEROP
5332 if (SF_IsCOMStub(dwStubFlags))
5334 if (SF_IsReverseStub(dwStubFlags))
5336 pStubState = new COMToCLR_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5340 pStubState = new CLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5346 pStubState = new PInvoke_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, unmgdCallConv, iLCIDArg, pMD);
5349 MethodDesc* pStubMD;
5350 pStubMD = CreateInteropILStub(
5366 #ifdef FEATURE_COMINTEROP
5367 MethodDesc* NDirect::CreateFieldAccessILStub(
5368 PCCOR_SIGNATURE szMetaSig,
5369 DWORD cbMetaSigSize,
5372 DWORD dwStubFlags, // NDirectStubFlags
5375 CONTRACT(MethodDesc*)
5379 PRECONDITION(CheckPointer(szMetaSig));
5380 PRECONDITION(CheckPointer(pModule));
5381 PRECONDITION(CheckPointer(pFD, NULL_OK));
5382 PRECONDITION(SF_IsFieldGetterStub(dwStubFlags) || SF_IsFieldSetterStub(dwStubFlags));
5383 POSTCONDITION(CheckPointer(RETVAL));
5387 int numArgs = (SF_IsFieldSetterStub(dwStubFlags) ? 1 : 0);
5388 int numParamTokens = numArgs + 1;
5390 // make sure we capture marshaling metadata
5391 mdParamDef* pParamTokenArray = (mdParamDef *)_alloca(numParamTokens * sizeof(mdParamDef));
5392 pParamTokenArray[0] = mdParamDefNil;
5393 pParamTokenArray[numArgs] = (mdParamDef)fd;
5395 // fields are never preserve-sig
5396 dwStubFlags |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
5398 // convert field signature to getter/setter signature
5399 SigBuilder sigBuilder;
5401 sigBuilder.AppendData(IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS);
5402 sigBuilder.AppendData(numArgs);
5404 if (SF_IsFieldSetterStub(dwStubFlags))
5406 // managed setter returns void
5407 sigBuilder.AppendElementType(ELEMENT_TYPE_VOID);
5410 CONSISTENCY_CHECK(*szMetaSig == IMAGE_CEE_CS_CALLCONV_FIELD);
5412 sigBuilder.AppendBlob((const PVOID)(szMetaSig + 1), cbMetaSigSize - 1);
5413 szMetaSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbMetaSigSize);
5415 StubSigDesc sigDesc(NULL, Signature(szMetaSig, cbMetaSigSize), pModule);
5418 sigDesc.m_pDebugName = pFD->GetDebugName();
5419 sigDesc.m_pDebugClassName = pFD->GetEnclosingMethodTable()->GetDebugClassName();
5422 Signature signature(szMetaSig, cbMetaSigSize);
5423 NewHolder<ILStubState> pStubState = new COMToCLRFieldAccess_ILStubState(pModule, signature, &sigDesc.m_typeContext, dwStubFlags, pFD);
5425 MethodDesc* pStubMD;
5426 pStubMD = CreateInteropILStub(
5429 (CorNativeLinkType)0,
5430 (CorNativeLinkFlags)0,
5439 #endif // FEATURE_COMINTEROP
5441 MethodDesc* NDirect::CreateCLRToNativeILStub(PInvokeStaticSigInfo* pSigInfo,
5445 STANDARD_VM_CONTRACT;
5447 StubSigDesc sigDesc(pMD, pSigInfo);
5449 if (SF_IsWinRTDelegateStub(dwStubFlags))
5451 _ASSERTE(pMD->IsEEImpl());
5453 return CreateCLRToNativeILStub(&sigDesc,
5454 (CorNativeLinkType)0,
5455 (CorNativeLinkFlags)0,
5457 (pSigInfo->GetStubFlags() | dwStubFlags) & ~NDIRECTSTUB_FL_DELEGATE);
5461 return CreateCLRToNativeILStub(&sigDesc,
5462 pSigInfo->GetCharSet(),
5463 pSigInfo->GetLinkFlags(),
5464 pSigInfo->GetCallConv(),
5465 pSigInfo->GetStubFlags() | dwStubFlags);
5469 MethodDesc* NDirect::GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags)
5471 STANDARD_VM_CONTRACT;
5473 MethodDesc* pStubMD = NULL;
5475 if (!pNMD->IsVarArgs() || SF_IsForNumParamBytes(dwStubFlags))
5477 if (pNMD->IsClassConstructorTriggeredByILStub())
5479 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
5482 pStubMD = CreateCLRToNativeILStub(
5484 dwStubFlags & ~NDIRECTSTUB_FL_FOR_NUMPARAMBYTES,
5491 MethodDesc* GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags)
5493 STANDARD_VM_CONTRACT;
5495 BOOL fGcMdaEnabled = FALSE;
5496 #ifdef MDA_SUPPORTED
5497 if (MDA_GET_ASSISTANT(GcManagedToUnmanaged) || MDA_GET_ASSISTANT(GcUnmanagedToManaged))
5499 // We never generate checks for these MDAs to NGEN'ed stubs so if they are
5500 // enabled, a new stub must be generated (the perf impact is huge anyway).
5501 fGcMdaEnabled = TRUE;
5503 #endif // MDA_SUPPORTED
5505 #ifdef FEATURE_COMINTEROP
5506 if (SF_IsReverseCOMStub(dwStubFlags))
5511 // reverse COM stubs live in a hash table
5512 StubMethodHashTable *pHash = pMD->GetLoaderModule()->GetStubMethodHashTable();
5513 return (pHash == NULL ? NULL : pHash->FindMethodDesc(pMD));
5516 #endif // FEATURE_COMINTEROP
5517 if (pMD->IsNDirect())
5519 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5520 return ((fGcMdaEnabled && !pNMD->IsQCall()) ? NULL : pNMD->ndirect.m_pStubMD.GetValueMaybeNull());
5522 #ifdef FEATURE_COMINTEROP
5523 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5525 #ifdef MDA_SUPPORTED
5526 if (MDA_GET_ASSISTANT(RaceOnRCWCleanup))
5528 // we never generate this callout to NGEN'ed stubs
5531 #endif // MDA_SUPPORTED
5536 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
5537 return (pComInfo == NULL ? NULL : pComInfo->m_pStubMD.GetValueMaybeNull());
5539 #endif // FEATURE_COMINTEROP
5540 else if (pMD->IsEEImpl())
5545 DelegateEEClass *pClass = (DelegateEEClass *)pMD->GetClass();
5546 if (SF_IsReverseStub(dwStubFlags))
5548 return pClass->m_pReverseStubMD;
5552 #ifdef FEATURE_COMINTEROP
5553 if (SF_IsWinRTDelegateStub(dwStubFlags))
5555 return pClass->m_pComPlusCallInfo->m_pStubMD.GetValueMaybeNull();
5558 #endif // FEATURE_COMINTEROP
5560 return pClass->m_pForwardStubMD;
5564 else if (pMD->IsIL())
5566 // these are currently only created at runtime, not at NGEN time
5571 UNREACHABLE_MSG("unexpected type of MethodDesc");
5575 #ifndef CROSSGEN_COMPILE
5577 PCODE NDirect::GetStubForILStub(MethodDesc* pManagedMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5583 PRECONDITION(CheckPointer(pManagedMD));
5584 POSTCONDITION(RETVAL != NULL);
5588 // pStubMD, if provided, must be preimplemented.
5589 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5591 if (NULL == *ppStubMD)
5593 PInvokeStaticSigInfo sigInfo(pManagedMD);
5594 *ppStubMD = NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pManagedMD);
5597 RETURN JitILStub(*ppStubMD);
5600 PCODE NDirect::GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5602 STANDARD_VM_CONTRACT;
5606 // pStubMD, if provided, must be preimplemented.
5607 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5609 if (NULL == *ppStubMD)
5611 PInvokeStaticSigInfo sigInfo;
5612 NDirect::PopulateNDirectMethodDesc(pNMD, &sigInfo, /* throwOnError = */ !SF_IsForNumParamBytes(dwStubFlags));
5614 *ppStubMD = NDirect::GetILStubMethodDesc(pNMD, &sigInfo, dwStubFlags);
5617 if (SF_IsForNumParamBytes(dwStubFlags))
5622 pStub = JitILStub(*ppStubMD);
5626 CONSISTENCY_CHECK(pNMD->IsVarArgs());
5629 // varargs goes through vararg NDirect stub
5631 pStub = TheVarargNDirectStub(pNMD->HasRetBuffArg());
5634 if (pNMD->IsEarlyBound())
5636 pNMD->InitEarlyBoundNDirectTarget();
5644 // NOTE: there is a race in updating this MethodDesc. We depend on all
5645 // threads getting back the same DynamicMethodDesc for a particular
5646 // NDirectMethodDesc, in that case, the locking around the actual JIT
5647 // operation will prevent the code from being jitted more than once.
5648 // By the time we get here, all threads get the same address of code
5649 // back from the JIT operation and they all just fill in the same value
5652 // In the NGEN case, all threads will get the same preimplemented code
5653 // address much like the JIT case.
5659 PCODE JitILStub(MethodDesc* pStubMD)
5661 STANDARD_VM_CONTRACT;
5663 PCODE pCode = pStubMD->GetNativeCode();
5667 ///////////////////////////////
5671 ///////////////////////////////
5674 if (pStubMD->IsDynamicMethod())
5677 // A dynamically generated IL stub
5680 pCode = pStubMD->PrepareInitialCode();
5682 _ASSERTE(pCode == pStubMD->GetNativeCode());
5687 // A static IL stub that is pointing to a static method in user assembly
5688 // Compile it and return the native code
5691 // This returns the stable entry point
5692 pCode = pStubMD->DoPrestub(NULL);
5694 _ASSERTE(pCode == pStubMD->GetStableEntryPoint());
5698 if (!pStubMD->IsDynamicMethod())
5700 // We need an entry point that can be called multiple times
5701 pCode = pStubMD->GetMultiCallableAddrOfCode();
5707 MethodDesc* RestoreNGENedStub(MethodDesc* pStubMD)
5712 PRECONDITION(CheckPointer(pStubMD));
5716 #ifdef FEATURE_PREJIT
5717 pStubMD->CheckRestore();
5719 PCODE pCode = pStubMD->GetPreImplementedCode();
5722 TADDR pFixupList = pStubMD->GetFixupList();
5723 if (pFixupList != NULL)
5725 Module* pZapModule = pStubMD->GetZapModule();
5726 _ASSERTE(pZapModule != NULL);
5727 if (!pZapModule->FixupDelayList(pFixupList))
5729 _ASSERTE(!"FixupDelayList failed");
5730 ThrowHR(COR_E_BADIMAGEFORMAT);
5734 #if defined(HAVE_GCCOVER)
5735 if (GCStress<cfg_instr_ngen>::IsEnabled())
5736 SetupGcCoverage(pStubMD, (BYTE*) pCode);
5737 #endif // HAVE_GCCOVER
5742 // We only pass a non-NULL pStubMD to GetStubForILStub() below if pStubMD is preimplemeneted.
5745 #endif // FEATURE_PREJIT
5750 PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags, MethodDesc **ppStubMD)
5756 PRECONDITION(CheckPointer(pMD));
5757 PRECONDITION(pMD->IsNDirect() || pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl() || pMD->IsIL());
5762 MethodDesc* pStubMD = NULL;
5764 pStubMD = GetStubMethodDescFromInteropMethodDesc(pMD, dwStubFlags);
5765 if (pStubMD != NULL)
5767 pStubMD = RestoreNGENedStub(pStubMD);
5770 if ((NULL == pStubMD) && (SF_IsNGENedStub(dwStubFlags)))
5772 // Return NULL -- the caller asked only for an ngened stub and
5773 // one does not exist, so don't do any more work.
5774 CONSISTENCY_CHECK(pStub == NULL);
5777 if (pMD->IsNDirect())
5779 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5780 pStub = NDirect::GetStubForILStub(pNMD, &pStubMD, dwStubFlags);
5782 #ifdef FEATURE_COMINTEROP
5784 if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5786 pStub = ComPlusCall::GetStubForILStub(pMD, &pStubMD);
5788 #endif // FEATURE_COMINTEROP
5790 if (pMD->IsEEImpl())
5792 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
5793 EEImplMethodDesc* pDelegateMD = (EEImplMethodDesc*)pMD;
5794 pStub = COMDelegate::GetStubForILStub(pDelegateMD, &pStubMD, dwStubFlags);
5799 CONSISTENCY_CHECK(SF_IsReverseStub(dwStubFlags));
5800 pStub = NDirect::GetStubForILStub(pMD, &pStubMD, dwStubFlags);
5804 UNREACHABLE_MSG("unexpected MethodDesc type");
5807 if (pStubMD != NULL && pStubMD->IsILStub() && pStubMD->AsDynamicMethodDesc()->IsStubNeedsCOMStarted())
5809 // the stub uses COM so make sure that it is started
5813 if (ppStubMD != NULL)
5814 *EnsureWritablePages(ppStubMD) = pStubMD;
5819 #ifdef FEATURE_COMINTEROP
5820 void CreateCLRToDispatchCOMStub(
5822 DWORD dwStubFlags) // NDirectStubFlags
5828 PRECONDITION(CheckPointer(pMD));
5832 _ASSERTE(SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags));
5834 // If we are dealing with a COM event call, then we need to initialize the
5835 // COM event call information.
5836 if (SF_IsCOMEventCallStub(dwStubFlags))
5838 _ASSERTE(pMD->IsComPlusCall()); // no generic COM eventing
5839 ((ComPlusCallMethodDesc *)pMD)->InitComEventCallInfo();
5842 // Get the call signature information
5843 StubSigDesc sigDesc(pMD);
5847 int numParamTokens = 0;
5848 mdParamDef* pParamTokenArray = NULL;
5850 CreateNDirectStubAccessMetadata(&sigDesc,
5856 numParamTokens = numArgs + 1;
5857 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5858 CollateParamTokens(sigDesc.m_pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray);
5860 DispatchStubState MyStubState;
5862 CreateNDirectStubWorker(&MyStubState,
5864 (CorNativeLinkType)0,
5865 (CorNativeLinkFlags)0,
5867 dwStubFlags | NDIRECTSTUB_FL_COM,
5872 _ASSERTE(pMD->IsComPlusCall()); // no generic disp-calls
5873 ((ComPlusCallMethodDesc *)pMD)->InitRetThunk();
5877 #endif // FEATURE_COMINTEROP
5880 LPVOID NDirect::NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod)
5882 // GetProcAddress cannot be called while preemptive GC is disabled.
5883 // It requires the OS to take the loader lock.
5887 PRECONDITION(CheckPointer(pMD));
5888 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
5892 g_IBCLogger.LogNDirectCodeAccess(pMD);
5894 #ifdef MDA_SUPPORTED
5895 MDA_TRIGGER_ASSISTANT(PInvokeLog, LogPInvoke(pMD, hMod));
5898 RETURN pMD->FindEntryPoint(hMod);
5901 VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget)
5909 PRECONDITION(IsNDirect());
5910 PRECONDITION(pTarget != NULL);
5914 Stub *pInterceptStub = NULL;
5919 #ifdef MDA_SUPPORTED
5920 if (!IsQCall() && MDA_GET_ASSISTANT(PInvokeStackImbalance))
5922 pInterceptStub = GenerateStubForMDA(pTarget, pInterceptStub);
5924 #endif // MDA_SUPPORTED
5927 #endif // _TARGET_X86_
5930 NDirectWriteableData* pWriteableData = GetWriteableData();
5931 EnsureWritablePages(pWriteableData);
5932 g_IBCLogger.LogNDirectCodeAccess(this);
5934 if (pInterceptStub != NULL)
5936 ndirect.m_pNativeNDirectTarget = pTarget;
5938 #if defined(_TARGET_X86_)
5939 pTarget = (PVOID)pInterceptStub->GetEntryPoint();
5941 LPVOID oldTarget = GetNDirectImportThunkGlue()->GetEntrypoint();
5942 if (FastInterlockCompareExchangePointer(&pWriteableData->m_pNDirectTarget, pTarget,
5943 oldTarget) != oldTarget)
5945 pInterceptStub->DecRef();
5948 _ASSERTE(pInterceptStub == NULL); // we don't intercept for anything else than host on !_TARGET_X86_
5953 pWriteableData->m_pNDirectTarget = pTarget;
5959 #if defined(_TARGET_X86_) && defined(MDA_SUPPORTED)
5960 EXTERN_C VOID __stdcall PInvokeStackImbalanceWorker(StackImbalanceCookie *pSICookie, DWORD dwPostESP)
5962 STATIC_CONTRACT_THROWS;
5963 STATIC_CONTRACT_GC_TRIGGERS;
5964 STATIC_CONTRACT_MODE_PREEMPTIVE; // we've already switched to preemptive
5966 // make sure we restore the original Win32 last error before leaving this function - we are
5967 // called right after returning from the P/Invoke target and the error has not been saved yet
5968 BEGIN_PRESERVE_LAST_ERROR;
5970 MdaPInvokeStackImbalance* pProbe = MDA_GET_ASSISTANT(PInvokeStackImbalance);
5972 // This MDA must be active if we generated a call to PInvokeStackImbalanceHelper
5975 pProbe->CheckStack(pSICookie, dwPostESP);
5977 END_PRESERVE_LAST_ERROR;
5979 #endif // _TARGET_X86_ && MDA_SUPPORTED
5982 // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places
5983 // if earlier loads fail and those later loads obliterate error codes.
5985 // This tracker object will keep track of the error code in accordance to priority:
5987 // low-priority: unknown error code (should never happen)
5988 // medium-priority: dll not found
5989 // high-priority: dll found but error during loading
5991 // We will overwrite the previous load's error code only if the new error code is higher priority.
5994 class LoadLibErrorTracker
5997 static const DWORD const_priorityNotFound = 10;
5998 static const DWORD const_priorityAccessDenied = 20;
5999 static const DWORD const_priorityCouldNotLoad = 99999;
6001 LoadLibErrorTracker()
6003 LIMITED_METHOD_CONTRACT;
6005 m_priorityOfLastError = 0;
6008 VOID TrackErrorCode()
6010 LIMITED_METHOD_CONTRACT;
6016 SetMessage(PAL_GetLoadLibraryError());
6019 DWORD dwLastError = GetLastError();
6021 switch (dwLastError)
6023 case ERROR_FILE_NOT_FOUND:
6024 case ERROR_PATH_NOT_FOUND:
6025 case ERROR_MOD_NOT_FOUND:
6026 case ERROR_DLL_NOT_FOUND:
6027 priority = const_priorityNotFound;
6030 // If we can't access a location, we can't know if the dll's there or if it's good.
6031 // Still, this is probably more unusual (and thus of more interest) than a dll-not-found
6032 // so give it an intermediate priority.
6033 case ERROR_ACCESS_DENIED:
6034 priority = const_priorityAccessDenied;
6036 // Assume all others are "dll found but couldn't load."
6038 priority = const_priorityCouldNotLoad;
6041 UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError));
6045 // Sets the error code to HRESULT as could not load DLL
6046 void TrackHR_CouldNotLoad(HRESULT hr)
6048 UpdateHR(const_priorityCouldNotLoad, hr);
6056 SString& GetMessage()
6061 void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath)
6063 STANDARD_VM_CONTRACT;
6065 #if defined(__APPLE__)
6066 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage());
6067 #elif defined(FEATURE_PAL)
6068 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage());
6070 HRESULT theHRESULT = GetHR();
6071 if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT))
6073 COMPlusThrow(kBadImageFormatException);
6078 GetHRMsg(theHRESULT, hrString);
6079 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString);
6081 #endif // FEATURE_PAL
6087 void UpdateHR(DWORD priority, HRESULT hr)
6089 if (priority > m_priorityOfLastError)
6092 m_priorityOfLastError = priority;
6096 void SetMessage(LPCSTR message)
6098 m_message = SString(SString::Utf8, message);
6102 DWORD m_priorityOfLastError;
6104 }; // class LoadLibErrorTracker
6106 // Load the library directly. On Unix systems, don't register it yet with PAL.
6107 // * External callers like AssemblyNative::InternalLoadUnmanagedDllFromPath() and the upcoming
6108 // System.Runtime.Interop.Marshall.LoadLibrary() need the raw system handle
6109 // * Internal callers like LoadLibraryModule() can convert this handle to a HMODULE via PAL APIs on Unix
6110 static NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker )
6112 STANDARD_VM_CONTRACT;
6114 NATIVE_LIBRARY_HANDLE hmod = NULL;
6118 if ((flags & 0xFFFFFF00) != 0
6119 #ifndef FEATURE_CORESYSTEM
6120 && NDirect::SecureLoadLibrarySupported()
6121 #endif // !FEATURE_CORESYSTEM
6124 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00);
6130 DWORD dwLastError = GetLastError();
6131 if (dwLastError != ERROR_INVALID_PARAMETER)
6133 pErrorTracker->TrackErrorCode();
6138 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF);
6140 #else // !FEATURE_PAL
6141 hmod = PAL_LoadLibraryDirect(name);
6142 #endif // !FEATURE_PAL
6146 pErrorTracker->TrackErrorCode();
6152 #if !defined(FEATURE_PAL)
6153 bool NDirect::s_fSecureLoadLibrarySupported = false;
6156 #define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a))
6157 #define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a))
6160 #define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W
6161 #define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W
6162 #else // !FEATURE_PAL
6163 #define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll")
6164 #define PLATFORM_SHARED_LIB_PREFIX_W W("")
6165 #endif // !FEATURE_PAL
6167 // The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
6168 // In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
6169 // Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
6170 #define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
6172 // DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags.
6173 // There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute,
6174 // CoreCLR's LoadLibrary implementation uses the following defaults.
6175 // Other implementations of LoadLibrary callbacks/events are free to use other default conventions.
6176 void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6178 STANDARD_VM_CONTRACT;
6180 *searchAssemblyDirectory = TRUE;
6181 *dllImportSearchPathFlags = 0;
6184 // If a module has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and return true.
6185 // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
6186 BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6188 STANDARD_VM_CONTRACT;
6190 if (pModule->HasDefaultDllImportSearchPathsAttribute())
6192 *dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
6193 *searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
6197 GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory);
6201 // If a pInvoke has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
6202 // Otherwise, if the containing assembly has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
6203 // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
6204 BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
6206 STANDARD_VM_CONTRACT;
6208 if (pMD->HasDefaultDllImportSearchPathsAttribute())
6210 *dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
6211 *searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
6215 return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory);
6219 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
6224 PRECONDITION(CheckPointer(libraryPath));
6228 LoadLibErrorTracker errorTracker;
6229 const NATIVE_LIBRARY_HANDLE hmod =
6230 LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker);
6232 if (throwOnError && (hmod == nullptr))
6234 SString libraryPathSString(libraryPath);
6235 errorTracker.Throw(libraryPathSString);
6241 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
6242 BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags,
6248 PRECONDITION(CheckPointer(libraryName));
6249 PRECONDITION(CheckPointer(callingAssembly));
6253 LoadLibErrorTracker errorTracker;
6255 // First checks if a default dllImportSearchPathFlags was passed in, if so, use that value.
6256 // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute.
6257 // If so, use that value.
6258 BOOL searchAssemblyDirectory;
6259 DWORD dllImportSearchPathFlags;
6261 if (hasDllImportSearchFlags)
6263 dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6264 searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6269 GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(),
6270 &dllImportSearchPathFlags, &searchAssemblyDirectory);
6273 NATIVE_LIBRARY_HANDLE hmod =
6274 LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName);
6276 if (throwOnError && (hmod == nullptr))
6278 SString libraryPathSString(libraryName);
6279 errorTracker.Throw(libraryPathSString);
6286 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, PCWSTR wszLibName)
6288 STANDARD_VM_CONTRACT;
6290 BOOL searchAssemblyDirectory;
6291 DWORD dllImportSearchPathFlags;
6293 GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
6295 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6296 return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName);
6300 void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle)
6302 STANDARD_VM_CONTRACT;
6304 // FreeLibrary doesn't throw if the input is null.
6305 // This avoids further null propagation/check while freeing resources (ex: in finally blocks)
6310 BOOL retVal = FreeLibrary(handle);
6311 #else // !FEATURE_PAL
6312 BOOL retVal = PAL_FreeLibraryDirect(handle);
6313 #endif // !FEATURE_PAL
6316 COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException"));
6320 INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError)
6325 PRECONDITION(CheckPointer(handle));
6326 PRECONDITION(CheckPointer(symbolName));
6330 MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName);
6333 INT_PTR address = reinterpret_cast<INT_PTR>(GetProcAddress((HMODULE)handle, lpstr));
6334 if ((address == NULL) && throwOnError)
6335 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName);
6336 #else // !FEATURE_PAL
6337 INT_PTR address = reinterpret_cast<INT_PTR>(PAL_GetProcAddressDirect((NATIVE_LIBRARY_HANDLE)handle, lpstr));
6338 if ((address == NULL) && throwOnError)
6339 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName);
6340 #endif // !FEATURE_PAL
6345 #ifndef PLATFORM_UNIX
6346 BOOL IsWindowsAPISet(PCWSTR wszLibName)
6348 STANDARD_VM_CONTRACT;
6350 // This is replicating quick check from the OS implementation of api sets.
6351 return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 ||
6352 SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0;
6354 #endif // !PLATFORM_UNIX
6357 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, PCWSTR wszLibName)
6359 STANDARD_VM_CONTRACT;
6360 //Dynamic Pinvoke Support:
6361 //Check if we need to provide the host a chance to provide the unmanaged dll
6363 #ifndef PLATFORM_UNIX
6364 if (IsWindowsAPISet(wszLibName))
6366 // Prevent Overriding of Windows API sets.
6369 #endif // !PLATFORM_UNIX
6371 NATIVE_LIBRARY_HANDLE hmod = NULL;
6372 AppDomain* pDomain = GetAppDomain();
6373 CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
6374 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6376 PEFile *pManifestFile = pAssembly->GetManifestFile();
6377 PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext();
6379 //Step 0: Check if the assembly was bound using TPA.
6380 // The Binding Context can be null or an overridden TPA context
6381 if (pBindingContext == NULL)
6383 // If we do not have any binder associated, then return to the default resolution mechanism.
6387 UINT_PTR assemblyBinderID = 0;
6388 IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
6390 ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
6392 // For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call.
6393 if (AreSameBinderInstance(pCurrentBinder, pTPABinder))
6398 #ifdef FEATURE_COMINTEROP
6399 CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder();
6400 if (AreSameBinderInstance(pCurrentBinder, pWinRTBinder))
6402 // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to
6403 // types being referenced from Windows.Foundation.Winmd) or when dealing with a winmd (which is bound using WinRT binder).
6405 // For this, we should use the standard mechanism to make pinvoke call as well.
6408 #endif // FEATURE_COMINTEROP
6410 //Step 1: If the assembly was not bound using TPA,
6411 // Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll to give
6412 // The custom assembly context a chance to load the unmanaged dll.
6416 STRINGREF pUnmanagedDllName;
6417 pUnmanagedDllName = StringObject::NewString(wszLibName);
6419 GCPROTECT_BEGIN(pUnmanagedDllName);
6421 // Get the pointer to the managed assembly load context
6422 INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
6424 // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll method.
6425 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL);
6426 DECLARE_ARGHOLDER_ARRAY(args, 2);
6427 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName);
6428 args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
6431 CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
6438 // Return the AssemblyLoadContext for an assembly
6439 INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly)
6441 STANDARD_VM_CONTRACT;
6443 PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext();
6444 if (pBindingContext == NULL)
6446 // GetBindingContext() returns NULL for System.Private.CoreLib
6450 UINT_PTR assemblyBinderID = 0;
6451 IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
6453 AppDomain *pDomain = GetAppDomain();
6454 ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
6456 #ifdef FEATURE_COMINTEROP
6457 if (AreSameBinderInstance(pCurrentBinder, pDomain->GetWinRtBinder()))
6459 // No ALC associated handle with WinRT Binders.
6462 #endif // FEATURE_COMINTEROP
6464 // The code here deals with two implementations of ICLRPrivBinder interface:
6465 // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and
6466 // - CLRPrivBinderAssemblyLoadContext for custom ALCs.
6467 // in order obtain the associated ALC handle.
6468 INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext())
6469 ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext()
6470 : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
6472 return ptrManagedAssemblyLoadContext;
6476 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, PCWSTR wszLibName)
6478 STANDARD_VM_CONTRACT;
6480 NATIVE_LIBRARY_HANDLE hmod = NULL;
6481 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6482 INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly);
6484 if (ptrManagedAssemblyLoadContext == NULL)
6493 OBJECTREF AssemblyRef;
6494 } gc = { NULL, NULL };
6496 GCPROTECT_BEGIN(gc);
6498 gc.DllName = StringObject::NewString(wszLibName);
6499 gc.AssemblyRef = pAssembly->GetExposedObject();
6501 // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method
6502 // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef
6503 // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in
6504 // as an additional argument.
6505 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT);
6506 DECLARE_ARGHOLDER_ARRAY(args, 3);
6507 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName);
6508 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef);
6509 args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
6512 CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
6519 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallback(NDirectMethodDesc * pMD, LPCWSTR wszLibName)
6521 STANDARD_VM_CONTRACT;
6523 if (pMD->GetModule()->IsSystem())
6525 // Don't attempt to callback on Corelib itself.
6526 // The LoadLibrary callback stub is managed code that requires CoreLib
6530 DWORD dllImportSearchPathFlags;
6531 BOOL searchAssemblyDirectory;
6532 BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
6533 dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0;
6535 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6536 NATIVE_LIBRARY_HANDLE handle = NULL;
6541 STRINGREF libNameRef;
6542 OBJECTREF assemblyRef;
6543 } gc = { NULL, NULL };
6545 GCPROTECT_BEGIN(gc);
6547 gc.libNameRef = StringObject::NewString(wszLibName);
6548 gc.assemblyRef = pAssembly->GetExposedObject();
6550 PREPARE_NONVIRTUAL_CALLSITE(METHOD__NATIVELIBRARY__LOADLIBRARYCALLBACKSTUB);
6551 DECLARE_ARGHOLDER_ARRAY(args, 4);
6552 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef);
6553 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef);
6554 args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags);
6555 args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags);
6558 CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args);
6564 // Try to load the module alongside the assembly where the PInvoke was declared.
6565 NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6567 STANDARD_VM_CONTRACT;
6569 NATIVE_LIBRARY_HANDLE hmod = NULL;
6571 SString path = pAssembly->GetManifestFile()->GetPath();
6573 SString::Iterator lastPathSeparatorIter = path.End();
6574 if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter))
6576 lastPathSeparatorIter++;
6577 path.Truncate(lastPathSeparatorIter);
6579 path.Append(libName);
6580 hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker);
6586 // Try to load the module from the native DLL search directories
6587 NATIVE_LIBRARY_HANDLE NDirect::LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6589 STANDARD_VM_CONTRACT;
6591 NATIVE_LIBRARY_HANDLE hmod = NULL;
6592 AppDomain* pDomain = GetAppDomain();
6594 if (pDomain->HasNativeDllSearchDirectories())
6596 AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories();
6597 while (hmod == NULL && pathIter.Next())
6599 SString qualifiedPath(*(pathIter.GetPath()));
6600 qualifiedPath.Append(libName);
6601 if (!Path::IsRelative(qualifiedPath))
6603 hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker);
6612 static const int MaxVariationCount = 4;
6613 static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6615 // Supported lib name variations
6616 static auto NameFmt = W("%.0s%s%.0s");
6617 static auto PrefixNameFmt = W("%s%s%.0s");
6618 static auto NameSuffixFmt = W("%.0s%s%s");
6619 static auto PrefixNameSuffixFmt = W("%s%s%s");
6621 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6624 if (!libNameIsRelativePath)
6626 libNameVariations[varCount++] = NameFmt;
6630 // We check if the suffix is contained in the name, because on Linux it is common to append
6631 // a version number to the library name (e.g. 'libicuuc.so.57').
6632 bool containsSuffix = false;
6633 SString::CIterator it = libName.Begin();
6634 if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W))
6636 it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W);
6637 containsSuffix = it == libName.End() || *it == (WCHAR)'.';
6640 // If the path contains a path delimiter, we don't add a prefix
6641 it = libName.Begin();
6642 bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W);
6646 libNameVariations[varCount++] = NameFmt;
6649 libNameVariations[varCount++] = PrefixNameFmt;
6651 libNameVariations[varCount++] = NameSuffixFmt;
6654 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6658 libNameVariations[varCount++] = NameSuffixFmt;
6661 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6663 libNameVariations[varCount++] = NameFmt;
6666 libNameVariations[varCount++] = PrefixNameFmt;
6670 *numberOfVariations = varCount;
6672 #else // FEATURE_PAL
6673 static const int MaxVariationCount = 2;
6674 static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6676 // Supported lib name variations
6677 static auto NameFmt = W("%.0s%s%.0s");
6678 static auto NameSuffixFmt = W("%.0s%s%s");
6680 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6684 // The purpose of following code is to workaround LoadLibrary limitation:
6685 // LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario:
6686 // [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke
6687 // The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll"
6688 auto it = libName.Begin();
6689 if (!libNameIsRelativePath ||
6690 !libName.Find(it, W('.')) ||
6691 libName.EndsWith(W(".")) ||
6692 libName.EndsWithCaseInsensitive(W(".dll")) ||
6693 libName.EndsWithCaseInsensitive(W(".exe")))
6695 // Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
6696 // If the string specifies a full path, the function searches only that path for the module.
6697 // 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.
6698 // To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.
6699 libNameVariations[varCount++] = NameFmt;
6703 libNameVariations[varCount++] = NameFmt;
6704 libNameVariations[varCount++] = NameSuffixFmt;
6707 *numberOfVariations = varCount;
6709 #endif // FEATURE_PAL
6711 // Search for the library and variants of its name in probing directories.
6713 NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly,
6714 BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags,
6715 LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
6717 STANDARD_VM_CONTRACT;
6719 NATIVE_LIBRARY_HANDLE hmod = NULL;
6721 #if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX)
6722 // Try to go straight to System32 for Windows API sets. This is replicating quick check from
6723 // the OS implementation of api sets.
6724 if (IsWindowsAPISet(wszLibName))
6726 hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker);
6732 #endif // FEATURE_CORESYSTEM && !FEATURE_PAL
6734 AppDomain* pDomain = GetAppDomain();
6735 DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
6736 bool libNameIsRelativePath = Path::IsRelative(wszLibName);
6738 // P/Invokes are often declared with variations on the actual library name.
6739 // For example, it's common to leave off the extension/suffix of the library
6740 // even if it has one, or to leave off a prefix like "lib" even if it has one
6741 // (both of these are typically done to smooth over cross-platform differences).
6742 // We try to dlopen with such variations on the original.
6743 const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {};
6744 int numberOfVariations = COUNTOF(prefixSuffixCombinations);
6745 DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath);
6746 for (int i = 0; i < numberOfVariations; i++)
6748 SString currLibNameVariation;
6749 currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W);
6751 // NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path
6752 hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
6758 if (!libNameIsRelativePath)
6760 DWORD flags = loadWithAlteredPathFlags;
6761 if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
6763 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
6764 // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
6765 flags |= dllImportSearchPathFlags;
6768 hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
6774 else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
6776 hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
6783 hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker);
6790 // This may be an assembly name
6791 // Format is "fileName, assemblyDisplayName"
6792 MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName);
6793 char *szComma = strchr(szLibName, ',');
6797 // Trim white spaces
6798 while (COMCharacter::nativeIsWhiteSpace(*(++szComma)));
6801 if (SUCCEEDED(spec.Init(szComma)))
6803 // Need to perform case insensitive hashing.
6806 UTF8_TO_LOWER_CASE(szLibName, qbLC);
6807 szLibName = (LPUTF8) qbLC.Ptr();
6810 Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED);
6811 Module *pModule = pAssembly->FindModuleByName(szLibName);
6813 hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
6820 // This Method returns an instance of the PAL-Registered handle
6821 HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker)
6826 PRECONDITION( CheckPointer( pMD ) );
6830 LPCUTF8 name = pMD->GetLibName();
6831 if ( !name || !*name )
6834 PREFIX_ASSUME( name != NULL );
6835 MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
6837 ModuleHandleHolder hmod = LoadLibraryModuleViaCallback(pMD, wszLibName);
6841 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6842 #endif // FEATURE_PAL
6843 return hmod.Extract();
6846 AppDomain* pDomain = GetAppDomain();
6848 // AssemblyLoadContext is not supported in AppX mode and thus,
6849 // we should not perform PInvoke resolution via it when operating in AppX mode.
6850 if (!AppX::IsAppXProcess())
6852 hmod = LoadLibraryModuleViaHost(pMD, wszLibName);
6856 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6857 #endif // FEATURE_PAL
6858 return hmod.Extract();
6862 hmod = pDomain->FindUnmanagedImageInCache(wszLibName);
6865 return hmod.Extract();
6868 hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName);
6872 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6873 #endif // FEATURE_PAL
6875 // If we have a handle add it to the cache.
6876 pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
6877 return hmod.Extract();
6880 if (!AppX::IsAppXProcess())
6882 hmod = LoadLibraryModuleViaEvent(pMD, wszLibName);
6886 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6887 #endif // FEATURE_PAL
6888 return hmod.Extract();
6892 return hmod.Extract();
6895 //---------------------------------------------------------
6896 // Loads the DLL and finds the procaddress for an N/Direct call.
6897 //---------------------------------------------------------
6899 VOID NDirect::NDirectLink(NDirectMethodDesc *pMD)
6904 PRECONDITION(CheckPointer(pMD));
6909 // On the phone, we only allow platform assemblies to define pinvokes
6910 // unless the host has asked us otherwise.
6913 if (pMD->IsClassConstructorTriggeredAtLinkTime())
6915 pMD->GetMethodTable()->CheckRunClassInitThrowing();
6920 LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget;
6922 // Do not repeat the lookup if the QCall was hardbound during ngen
6923 if (pvTarget == NULL)
6925 pvTarget = ECall::GetQCallImpl(pMD);
6929 _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD));
6932 pMD->SetNDirectTarget(pvTarget);
6936 // Loading unmanaged dlls can trigger dllmains which certainly count as code execution!
6937 pMD->EnsureActive();
6939 LoadLibErrorTracker errorTracker;
6941 BOOL fSuccess = FALSE;
6942 HINSTANCE hmod = LoadLibraryModule( pMD, &errorTracker );
6945 LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod);
6949 #ifdef MDA_SUPPORTED
6950 MdaInvalidOverlappedToPinvoke *pOverlapCheck = MDA_GET_ASSISTANT(InvalidOverlappedToPinvoke);
6951 if (pOverlapCheck && pOverlapCheck->ShouldHook(pMD))
6953 LPVOID pNewTarget = pOverlapCheck->Register(hmod,pvTarget);
6956 pvTarget = pNewTarget;
6960 pMD->SetNDirectTarget(pvTarget);
6967 if (pMD->GetLibName() == NULL)
6968 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME);
6970 StackSString ssLibName(SString::Utf8, pMD->GetLibName());
6974 errorTracker.Throw(ssLibName);
6977 WCHAR wszEPName[50];
6978 if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0)
6980 wszEPName[0] = W('?');
6981 wszEPName[1] = W('\0');
6984 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName);
6986 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName);
6992 //---------------------------------------------------------
6994 //---------------------------------------------------------
6995 /*static*/ void NDirect::Init()
7002 INJECT_FAULT(COMPlusThrowOM());
7006 #if !defined(FEATURE_PAL)
7007 // Check if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
7008 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
7009 _ASSERTE(hMod != NULL);
7011 if (GetProcAddress(hMod, "AddDllDirectory") != NULL)
7013 // The AddDllDirectory export was added in KB2533623 together with the new flag support
7014 s_fSecureLoadLibrarySupported = true;
7016 #endif // !FEATURE_PAL
7020 //==========================================================================
7021 // This function is reached only via NDirectImportThunk. It's purpose
7022 // is to ensure that the target DLL is fully loaded and ready to run.
7024 // FUN FACTS: Though this function is actually entered in unmanaged mode,
7025 // it can reenter managed mode and throw a COM+ exception if the DLL linking
7027 //==========================================================================
7030 EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD)
7034 BEGIN_PRESERVE_LAST_ERROR;
7044 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
7045 // this function is called by CLR to native assembly stubs which are called by
7046 // managed code as a result, we need an unwind and continue handler to translate
7047 // any of our internal exceptions into managed exceptions.
7048 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
7050 if (pMD->IsEarlyBound())
7052 if (!pMD->IsZapped())
7054 // we need the MD to be populated in case we decide to build an intercept
7055 // stub to wrap the target in InitEarlyBoundNDirectTarget
7056 PInvokeStaticSigInfo sigInfo;
7057 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
7060 pMD->InitEarlyBoundNDirectTarget();
7065 // Otherwise we're in an inlined pinvoke late bound MD
7067 INDEBUG(Thread *pThread = GetThread());
7069 _ASSERTE(pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr());
7071 CONSISTENCY_CHECK(pMD->IsNDirect());
7073 // With IL stubs, we don't have to do anything but ensure the DLL is loaded.
7076 if (!pMD->IsZapped())
7078 PInvokeStaticSigInfo sigInfo;
7079 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
7083 // must have been populated at NGEN time
7084 _ASSERTE(pMD->GetLibName() != NULL);
7087 pMD->CheckRestore();
7089 NDirect::NDirectLink(pMD);
7093 ret = pMD->GetNDirectTarget();
7095 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
7096 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
7098 END_PRESERVE_LAST_ERROR;
7103 //===========================================================================
7104 // Support for Pinvoke Calli instruction
7106 //===========================================================================
7108 EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock * pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD)
7110 BEGIN_PRESERVE_LAST_ERROR;
7112 STATIC_CONTRACT_THROWS;
7113 STATIC_CONTRACT_GC_TRIGGERS;
7114 STATIC_CONTRACT_MODE_COOPERATIVE;
7115 STATIC_CONTRACT_ENTRY_POINT;
7117 MAKE_CURRENT_THREAD_AVAILABLE();
7120 Thread::ObjectRefFlush(CURRENT_THREAD);
7123 FrameWithCookie<PrestubMethodFrame> frame(pTransitionBlock, pMD);
7124 PrestubMethodFrame * pFrame = &frame;
7126 pFrame->Push(CURRENT_THREAD);
7128 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
7129 _ASSERTE(pMD == pFrame->GetFunction());
7131 GetILStubForCalli(pVASigCookie, pMD);
7133 pFrame->Pop(CURRENT_THREAD);
7135 END_PRESERVE_LAST_ERROR;
7138 EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
7140 BEGIN_PRESERVE_LAST_ERROR;
7142 STATIC_CONTRACT_THROWS;
7143 STATIC_CONTRACT_GC_TRIGGERS;
7144 STATIC_CONTRACT_MODE_COOPERATIVE;
7145 STATIC_CONTRACT_ENTRY_POINT;
7147 MAKE_CURRENT_THREAD_AVAILABLE();
7150 Thread::ObjectRefFlush(CURRENT_THREAD);
7153 FrameWithCookie<PInvokeCalliFrame> frame(pTransitionBlock, pVASigCookie, pUnmanagedTarget);
7154 PInvokeCalliFrame * pFrame = &frame;
7156 pFrame->Push(CURRENT_THREAD);
7158 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
7160 GetILStubForCalli(pVASigCookie, NULL);
7162 pFrame->Pop(CURRENT_THREAD);
7164 END_PRESERVE_LAST_ERROR;
7167 PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
7175 PRECONDITION(CheckPointer(pVASigCookie));
7176 PRECONDITION(CheckPointer(pMD, NULL_OK));
7177 POSTCONDITION(RETVAL != NULL);
7181 PCODE pTempILStub = NULL;
7183 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
7184 // this function is called by CLR to native assembly stubs which are called by
7185 // managed code as a result, we need an unwind and continue handler to translate
7186 // any of our internal exceptions into managed exceptions.
7187 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
7189 // Force a GC if the stress level is high enough
7190 GCStress<cfg_any>::MaybeTrigger();
7194 Signature signature = pVASigCookie->signature;
7195 CorPinvokeMap unmgdCallConv = pmNoMangle;
7197 DWORD dwStubFlags = NDIRECTSTUB_FL_BESTFIT;
7199 // The MethodDesc pointer may in fact be the unmanaged target, see PInvokeStubs.asm.
7200 if (pMD == NULL || (UINT_PTR)pMD & 0x1)
7203 dwStubFlags |= NDIRECTSTUB_FL_UNMANAGED_CALLI;
7205 // need to convert the CALLI signature to stub signature with managed calling convention
7206 switch (MetaSig::GetCallingConvention(pVASigCookie->pModule, pVASigCookie->signature))
7208 case IMAGE_CEE_CS_CALLCONV_C:
7209 unmgdCallConv = pmCallConvCdecl;
7211 case IMAGE_CEE_CS_CALLCONV_STDCALL:
7212 unmgdCallConv = pmCallConvStdcall;
7214 case IMAGE_CEE_CS_CALLCONV_THISCALL:
7215 unmgdCallConv = pmCallConvThiscall;
7217 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
7218 unmgdCallConv = pmCallConvFastcall;
7221 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
7224 LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
7225 PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen()));
7226 CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen());
7228 // make the stub IMAGE_CEE_CS_CALLCONV_DEFAULT
7229 *new_sig &= ~IMAGE_CEE_CS_CALLCONV_MASK;
7230 *new_sig |= IMAGE_CEE_CS_CALLCONV_DEFAULT;
7232 signature = Signature(new_sig, signature.GetRawSigLen());
7236 _ASSERTE(pMD->IsNDirect());
7237 dwStubFlags |= NDIRECTSTUB_FL_CONVSIGASVARARG;
7239 // vararg P/Invoke must be cdecl
7240 unmgdCallConv = pmCallConvCdecl;
7242 if (((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
7244 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
7249 CorNativeLinkFlags nlFlags;
7250 CorNativeLinkType nlType;
7254 PInvokeStaticSigInfo sigInfo(pMD);
7256 md = pMD->GetMemberDef();
7257 nlFlags = sigInfo.GetLinkFlags();
7258 nlType = sigInfo.GetCharSet();
7262 md = mdMethodDefNil;
7267 StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule);
7269 MethodDesc* pStubMD = NDirect::CreateCLRToNativeILStub(&sigDesc,
7275 pTempILStub = JitILStub(pStubMD);
7277 InterlockedCompareExchangeT<PCODE>(&pVASigCookie->pNDirectILStub,
7281 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
7282 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
7284 RETURN pVASigCookie->pNDirectILStub;
7287 #endif // CROSSGEN_COMPILE
7289 #endif // #ifndef DACCESS_COMPILE
7292 // Truncates a SString by first converting it to unicode and truncate it
7293 // if it is larger than size. "..." will be appended if it is truncated.
7295 void TruncateUnicodeString(SString &string, COUNT_T bufSize)
7298 if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize)
7300 _ASSERTE(bufSize / sizeof(WCHAR) > 4);
7301 string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4);
7302 string.Append(W("..."));