1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
18 #include "dllimport.h"
20 #include "siginfo.hpp"
21 #include "callconvbuilder.hpp"
22 #include "comdelegate.h"
26 #include "comutilnative.h"
28 #include "asmconstants.h"
29 #include "customattribute.h"
30 #include "ilstubcache.h"
31 #include "typeparse.h"
32 #include "typestring.h"
33 #include "sigbuilder.h"
34 #include "sigformat.h"
36 #include "fieldmarshaler.h"
37 #include "pinvokeoverride.h"
38 #include "nativelibrary.h"
39 #include "interoplibinterface.h"
41 #include <formattype.h>
42 #include "../md/compiler/custattr.h"
44 #ifdef FEATURE_COMINTEROP
45 #include "runtimecallablewrapper.h"
46 #include "clrtocomcall.h"
47 #endif // FEATURE_COMINTEROP
51 #endif // FEATURE_PREJIT
53 #ifdef TIZEN_ASAN_ENVIRONMENT
54 #include <tizenasanenv.h>
55 #endif // TIZEN_ASAN_ENVIRONMENT
57 #include "eventtrace.h"
61 void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx)
63 LIMITED_METHOD_CONTRACT;
64 if (pClause->kind == ILStubEHClause::kNone)
70 CorExceptionFlag flags;
71 switch (pClause->kind)
73 case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break;
74 case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break;
76 UNREACHABLE_MSG("unexpected ILStubEHClause kind");
78 _ASSERTE(idx < nClauses);
79 pEHSect->Fat.Clauses[idx].Flags = flags;
80 pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset;
81 pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength;
82 pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset;
83 pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength;
84 pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken;
87 VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo)
89 LIMITED_METHOD_CONTRACT;
90 pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat);
91 pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses);
94 AppendEHClause(nClauses, pEHSect, pOne, &curIdx);
95 AppendEHClause(nClauses, pEHSect, pTwo, &curIdx);
99 StubSigDesc::StubSigDesc(MethodDesc *pMD)
106 PRECONDITION(pMD != NULL);
112 m_sig = pMD->GetSignature();
113 m_pModule = pMD->GetModule(); // Used for token resolution.
115 m_tkMethodDef = pMD->GetMemberDef();
116 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
117 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
119 INDEBUG(InitDebugNames());
122 StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule)
129 PRECONDITION(!sig.IsEmpty());
130 PRECONDITION(pModule != NULL);
141 m_tkMethodDef = pMD->GetMemberDef();
142 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
143 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
147 m_tkMethodDef = mdMethodDefNil;
148 m_pLoaderModule = m_pModule;
151 INDEBUG(InitDebugNames());
154 StubSigDesc::StubSigDesc(MethodTable* pMT, const Signature& sig, Module* pModule)
161 PRECONDITION(!sig.IsEmpty());
162 PRECONDITION(pModule != NULL);
171 m_tkMethodDef = mdMethodDefNil;
175 SigTypeContext::InitTypeContext(pMT, &m_typeContext);
176 m_pLoaderModule = pMT->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
180 m_pLoaderModule = m_pModule;
183 INDEBUG(InitDebugNames());
186 StubSigDesc::StubSigDesc(const Signature& sig, Module* pModule)
193 PRECONDITION(!sig.IsEmpty());
194 PRECONDITION(pModule != NULL);
202 m_tkMethodDef = mdMethodDefNil;
203 m_pLoaderModule = m_pModule;
205 INDEBUG(InitDebugNames());
208 #ifndef DACCESS_COMPILE
213 virtual void SetLastError(BOOL fSetLastError) = 0;
214 virtual void BeginEmit(DWORD dwStubFlags) = 0;
215 virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) = 0;
216 virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset) = 0;
217 virtual void MarshalLCID(int argIdx) = 0;
218 virtual void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc) = 0;
220 virtual void EmitInvokeTarget(MethodDesc *pStubMD) = 0;
222 virtual void FinishEmit(MethodDesc* pMD) = 0;
226 LIMITED_METHOD_CONTRACT;
230 class ILStubState : public StubState
236 const Signature &signature,
237 SigTypeContext* pTypeContext,
240 MethodDesc* pTargetMD)
241 : m_slIL(dwStubFlags, pStubModule, signature, pTypeContext, pTargetMD, iLCIDParamIdx)
242 , m_dwStubFlags(dwStubFlags)
244 STANDARD_VM_CONTRACT;
250 void SetLastError(BOOL fSetLastError)
252 LIMITED_METHOD_CONTRACT;
254 m_fSetLastError = fSetLastError;
257 // We use three stub linkers to generate IL stubs. The pre linker is the main one. It does all the marshaling and
258 // then calls the target method. The post return linker is only used to unmarshal the return value after we return
259 // from the target method. The post linker handles all the unmarshaling for by ref arguments and clean-up. It
260 // also checks if we should throw an exception etc.
262 // Currently, we have two "emittable" ILCodeLabel's. The first one is at the beginning of the pre linker. This
263 // label is used to emit code to declare and initialize clean-up flags. Each argument which requires clean-up
264 // emits one flag. This flag is set only after the marshaling is done, and it is checked before we do any clean-up
267 // The second "emittable" ILCodeLabel is at the beginning of the post linker. It is used to emit code which is
268 // not safe to run in the case of an exception. The rest of the post linker is wrapped in a finally, and it contains
269 // with the necessary clean-up which should be executed in both normal and exception cases.
270 void BeginEmit(DWORD dwStubFlags)
273 m_slIL.Begin(dwStubFlags);
274 _ASSERTE(m_dwStubFlags == dwStubFlags);
277 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
283 PRECONDITION(CheckPointer(pInfo));
287 pInfo->GenerateReturnIL(&m_slIL, argOffset,
288 SF_IsForwardStub(m_dwStubFlags),
289 SF_IsFieldGetterStub(m_dwStubFlags),
290 SF_IsHRESULTSwapping(m_dwStubFlags));
293 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
298 PRECONDITION(CheckPointer(pInfo));
302 pInfo->GenerateArgumentIL(&m_slIL, argOffset, nativeStackOffset, SF_IsForwardStub(m_dwStubFlags));
305 void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc)
310 PRECONDITION(CheckPointer(pInfo));
314 pInfo->GenerateFieldIL(&m_slIL, managedOffset, nativeOffset, pFieldDesc);
317 #ifdef FEATURE_COMINTEROP
318 static void EmitInterfaceClearNative(ILCodeStream* pcsEmit, DWORD dwLocalNum)
320 STANDARD_VM_CONTRACT;
322 ILCodeLabel *pSkipClearNativeLabel = pcsEmit->NewCodeLabel();
323 pcsEmit->EmitLDLOC(dwLocalNum);
324 pcsEmit->EmitBRFALSE(pSkipClearNativeLabel);
325 pcsEmit->EmitLDLOC(dwLocalNum);
326 pcsEmit->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
327 pcsEmit->EmitLabel(pSkipClearNativeLabel);
329 #endif // FEATURE_COMINTEROP
331 void MarshalLCID(int argIdx)
333 STANDARD_VM_CONTRACT;
335 ILCodeStream* pcs = m_slIL.GetDispatchCodeStream();
337 if (SF_IsReverseStub(m_dwStubFlags))
339 if ((m_slIL.GetStubTargetCallingConv() & IMAGE_CEE_CS_CALLCONV_HASTHIS) == IMAGE_CEE_CS_CALLCONV_HASTHIS)
341 // the arg number will be incremented by LDARG if we are in an instance method
342 _ASSERTE(argIdx > 0);
346 // call CultureInfo.get_CurrentCulture()
347 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
349 // save the current culture
350 LocalDesc locDescCulture(CoreLibBinder::GetClass(CLASS__CULTURE_INFO));
351 DWORD dwCultureLocalNum = pcs->NewLocal(locDescCulture);
353 pcs->EmitSTLOC(dwCultureLocalNum);
355 // set a new one based on the LCID passed from unmanaged
356 pcs->EmitLDARG(argIdx);
358 // call CultureInfo..ctor(lcid)
359 // call CultureInfo.set_CurrentCulture(culture)
360 pcs->EmitNEWOBJ(METHOD__CULTURE_INFO__INT_CTOR, 1);
361 pcs->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
363 // and restore the current one after the call
364 m_slIL.SetCleanupNeeded();
365 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
367 // call CultureInfo.set_CurrentCulture(original_culture)
368 pcsCleanup->EmitLDLOC(dwCultureLocalNum);
369 pcsCleanup->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
373 if (SF_IsCOMStub(m_dwStubFlags))
375 // We used to get LCID from current thread's culture here. The code
376 // was replaced by the hardcoded LCID_ENGLISH_US as requested by VSTO.
377 pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
381 // call CultureInfo.get_CurrentCulture()
382 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
384 //call CultureInfo.get_LCID(this)
385 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_ID, 1, 1);
389 // add the extra arg to the unmanaged signature
390 LocalDesc locDescNative(ELEMENT_TYPE_I4);
391 pcs->SetStubTargetArgType(&locDescNative, false);
394 void SwapStubSignatures(MethodDesc* pStubMD)
396 STANDARD_VM_CONTRACT;
399 // Since the stub handles native-to-managed transitions, we have to swap the
400 // stub-state-calculated stub target sig with the stub sig itself. This is
401 // because the stub target sig represents the native signature and the stub
402 // sig represents the managed signature.
404 // The first step is to convert the managed signature to a module-independent
405 // signature and then pass it off to SetStubTargetMethodSig. Note that the
406 // ILStubResolver will copy the sig, so we only need to make a temporary copy
409 SigBuilder sigBuilder;
412 SigPointer sigPtr(pStubMD->GetSig());
413 sigPtr.ConvertToInternalSignature(pStubMD->GetModule(), NULL, &sigBuilder);
417 // The second step is to reset the sig on the stub MethodDesc to be the
418 // stub-state-calculated stub target sig.
422 // make a domain-local copy of the sig so that this state can outlive the
423 // compile time state.
426 PCCOR_SIGNATURE pNewSig;
428 cbNewSig = GetStubTargetMethodSigLength();
429 pNewSig = (PCCOR_SIGNATURE)(void *)pStubMD->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig));
431 memcpyNoGCRefs((void *)pNewSig, GetStubTargetMethodSig(), cbNewSig);
433 pStubMD->AsDynamicMethodDesc()->SetStoredMethodSig(pNewSig, cbNewSig);
435 SigPointer sigPtr(pNewSig, cbNewSig);
436 uint32_t callConvInfo;
437 IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo));
439 if (callConvInfo & CORINFO_CALLCONV_HASTHIS)
441 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags &= ~mdStatic;
442 pStubMD->ClearStatic();
446 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags |= mdStatic;
447 pStubMD->SetStatic();
451 // we store the real managed argument stack size in the stub MethodDesc on non-X86
452 UINT stackSize = pStubMD->SizeOfNativeArgStack();
454 if (!FitsInU2(stackSize))
455 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
457 pStubMD->AsDynamicMethodDesc()->SetNativeStackArgSize(static_cast<WORD>(stackSize));
461 DWORD cbTempModuleIndependentSigLength;
462 BYTE * pTempModuleIndependentSig = (BYTE *)sigBuilder.GetSignature(&cbTempModuleIndependentSigLength);
465 SetStubTargetMethodSig(pTempModuleIndependentSig,
466 cbTempModuleIndependentSigLength);
469 void EmitInvokeTarget(MethodDesc *pStubMD)
471 STANDARD_VM_CONTRACT;
473 m_slIL.DoNDirect(m_slIL.GetDispatchCodeStream(), m_dwStubFlags, pStubMD);
476 virtual void EmitExceptionHandler(LocalDesc* pNativeReturnType, LocalDesc* pManagedReturnType,
477 ILCodeLabel** ppTryBeginLabel, ILCodeLabel** ppTryEndAndCatchBeginLabel, ILCodeLabel** ppCatchEndAndReturnLabel)
479 #ifdef FEATURE_COMINTEROP
480 STANDARD_VM_CONTRACT;
482 ILCodeStream* pcsExceptionHandler = m_slIL.NewCodeStream(ILStubLinker::kExceptionHandler);
483 *ppTryEndAndCatchBeginLabel = pcsExceptionHandler->NewCodeLabel(); // try ends at the same place the catch begins
484 *ppCatchEndAndReturnLabel = pcsExceptionHandler->NewCodeLabel(); // catch ends at the same place we resume afterwards
486 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
487 pcsExceptionHandler->EmitLabel(*ppTryEndAndCatchBeginLabel);
489 BYTE nativeReturnElemType = pNativeReturnType->ElementType[0]; // return type of the stub
490 BYTE managedReturnElemType = pManagedReturnType->ElementType[0]; // return type of the mananged target
492 bool returnTheHRESULT = SF_IsHRESULTSwapping(m_dwStubFlags) ||
493 (managedReturnElemType == ELEMENT_TYPE_I4) ||
494 (managedReturnElemType == ELEMENT_TYPE_U4);
496 DWORD retvalLocalNum = m_slIL.GetReturnValueLocalNum();
497 BinderMethodID getHRForException;
498 getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION;
500 pcsExceptionHandler->EmitCALL(getHRForException,
501 0, // WARNING: This method takes 1 input arg, the exception object. But the ILStubLinker
502 // has no knowledge that the exception object is on the stack (because it is
503 // unaware that we've just entered a catch block), so we lie and claim that we
504 // don't take any input arguments.
507 switch (nativeReturnElemType)
510 UNREACHABLE_MSG("Unexpected element type found on native return type.");
512 case ELEMENT_TYPE_VOID:
513 _ASSERTE(retvalLocalNum == (DWORD)-1);
514 pcsExceptionHandler->EmitPOP();
516 case ELEMENT_TYPE_I4:
517 case ELEMENT_TYPE_U4:
519 if (!returnTheHRESULT)
521 pcsExceptionHandler->EmitPOP();
522 pcsExceptionHandler->EmitLDC(0);
523 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
525 _ASSERTE(retvalLocalNum != (DWORD)-1);
526 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
529 case ELEMENT_TYPE_R4:
530 pcsExceptionHandler->EmitPOP();
531 pcsExceptionHandler->EmitLDC_R4(CLR_NAN_32);
532 _ASSERTE(retvalLocalNum != (DWORD)-1);
533 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
535 case ELEMENT_TYPE_R8:
536 pcsExceptionHandler->EmitPOP();
537 pcsExceptionHandler->EmitLDC_R8(CLR_NAN_64);
538 _ASSERTE(retvalLocalNum != (DWORD)-1);
539 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
541 case ELEMENT_TYPE_INTERNAL:
543 TypeHandle returnTypeHnd = pNativeReturnType->InternalToken;
544 CONSISTENCY_CHECK(returnTypeHnd.IsValueType());
545 _ASSERTE(retvalLocalNum != (DWORD)-1);
546 pcsExceptionHandler->EmitLDLOCA(retvalLocalNum);
547 pcsExceptionHandler->EmitINITOBJ(m_slIL.GetDispatchCodeStream()->GetToken(returnTypeHnd));
550 case ELEMENT_TYPE_BOOLEAN:
551 case ELEMENT_TYPE_CHAR:
552 case ELEMENT_TYPE_I1:
553 case ELEMENT_TYPE_U1:
554 case ELEMENT_TYPE_I2:
555 case ELEMENT_TYPE_U2:
556 case ELEMENT_TYPE_I8:
557 case ELEMENT_TYPE_U8:
560 pcsExceptionHandler->EmitPOP();
561 pcsExceptionHandler->EmitLDC(0);
562 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
563 _ASSERTE(retvalLocalNum != (DWORD)-1);
564 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
568 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
569 pcsExceptionHandler->EmitLabel(*ppCatchEndAndReturnLabel);
570 if (nativeReturnElemType != ELEMENT_TYPE_VOID)
572 _ASSERTE(retvalLocalNum != (DWORD)-1);
573 pcsExceptionHandler->EmitLDLOC(retvalLocalNum);
575 pcsExceptionHandler->EmitRET();
576 #endif // FEATURE_COMINTEROP
579 #ifndef DACCESS_COMPILE
580 void FinishEmit(MethodDesc* pStubMD)
582 STANDARD_VM_CONTRACT;
584 ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
585 ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
586 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
588 if (SF_IsHRESULTSwapping(m_dwStubFlags) && m_slIL.StubHasVoidReturnType())
590 // if the return type is void, but we're doing HRESULT swapping, we
591 // need to set the return type here. Otherwise, the return value
592 // marshaler will do this.
593 pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // HRESULT
595 if (SF_IsReverseStub(m_dwStubFlags))
597 // reverse interop needs to seed the return value if the
598 // managed function returns void but we're doing hresult
600 pcsUnmarshal->EmitLDC(S_OK);
604 LocalDesc nativeReturnType;
605 LocalDesc managedReturnType;
606 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(m_dwStubFlags)
607 && !SF_IsFieldGetterStub(m_dwStubFlags)
608 && !SF_IsFieldSetterStub(m_dwStubFlags);
610 bool hasTryCatchExceptionHandler = hasTryCatchForHRESULT || SF_IsStructMarshalStub(m_dwStubFlags);
612 #ifdef FEATURE_COMINTEROP
613 if (hasTryCatchForHRESULT)
615 m_slIL.GetStubTargetReturnType(&nativeReturnType);
616 m_slIL.GetStubReturnType(&managedReturnType);
618 #endif // FEATURE_COMINTEROP
620 // Don't touch target signatures from this point on otherwise it messes up the
621 // cache in ILStubState::GetStubTargetMethodSig.
625 // The native and local signatures should not have any tokens.
626 // All token references should have been converted to
627 // ELEMENT_TYPE_INTERNAL.
629 // Note that MetaSig::GetReturnType and NextArg will normalize
630 // ELEMENT_TYPE_INTERNAL back to CLASS or VALUETYPE.
632 // <TODO> need to recursively check ELEMENT_TYPE_FNPTR signatures </TODO>
634 SigTypeContext typeContext; // this is an empty type context: COM calls are guaranteed to not be generics.
636 GetStubTargetMethodSig(),
637 GetStubTargetMethodSigLength(),
638 CoreLibBinder::GetModule(),
642 IfFailThrow(nsig.GetReturnProps().PeekElemType(&type));
643 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
645 while (ELEMENT_TYPE_END != (type = nsig.NextArg()))
647 IfFailThrow(nsig.GetArgProps().PeekElemType(&type));
648 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
654 // The profiler helpers below must be called immediately before and after the call to the target.
655 // The debugger trace call helpers are invoked from StubRareDisableWorker
658 #if defined(PROFILING_SUPPORTED)
659 DWORD dwMethodDescLocalNum = (DWORD)-1;
661 // Notify the profiler of call out of the runtime
662 if (!SF_IsReverseCOMStub(m_dwStubFlags) && !SF_IsStructMarshalStub(m_dwStubFlags) && (CORProfilerTrackTransitions() || (!IsReadyToRunCompilation() && SF_IsNGENedStubForProfiling(m_dwStubFlags))))
664 dwMethodDescLocalNum = m_slIL.EmitProfilerBeginTransitionCallback(pcsDispatch, m_dwStubFlags);
665 _ASSERTE(dwMethodDescLocalNum != (DWORD)-1);
667 #endif // PROFILING_SUPPORTED
669 // For CoreClr, clear the last error before calling the target that returns last error.
670 // There isn't always a way to know the function have failed without checking last error,
671 // in particular on Unix.
672 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
674 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CLEAR_LAST_ERROR, 0, 0);
677 // Invoke the target (calli, call method, call delegate, get/set field, etc.)
678 EmitInvokeTarget(pStubMD);
680 // Saving last error must be the first thing we do after returning from the target
681 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
683 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__SET_LAST_ERROR, 0, 0);
686 #if defined(TARGET_X86)
687 if (SF_IsForwardDelegateStub(m_dwStubFlags))
689 // the delegate may have an intercept stub attached to its sync block so we should
690 // prevent it from being garbage collected when the call is in progress
691 pcsDispatch->EmitLoadThis();
692 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
694 #endif // defined(TARGET_X86)
697 if (SF_IsForwardStub(m_dwStubFlags) && g_pConfig->InteropValidatePinnedObjects())
699 // call StubHelpers.ValidateObject/StubHelpers.ValidateByref on pinned locals
700 m_slIL.EmitObjectValidation(pcsDispatch, m_dwStubFlags);
702 #endif // VERIFY_HEAP
704 #if defined(PROFILING_SUPPORTED)
705 // Notify the profiler of return back into the runtime
706 if (dwMethodDescLocalNum != (DWORD)-1)
708 m_slIL.EmitProfilerEndTransitionCallback(pcsDispatch, m_dwStubFlags, dwMethodDescLocalNum);
710 #endif // PROFILING_SUPPORTED
712 #ifdef FEATURE_COMINTEROP
713 if (SF_IsForwardCOMStub(m_dwStubFlags))
715 // Make sure that the RCW stays alive for the duration of the call. Note that if we do HRESULT
716 // swapping, we'll pass 'this' to GetCOMHRExceptionObject after returning from the target so
717 // GC.KeepAlive is not necessary.
718 if (!SF_IsHRESULTSwapping(m_dwStubFlags))
720 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
721 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
724 #endif // FEATURE_COMINTEROP
726 if (SF_IsHRESULTSwapping(m_dwStubFlags))
728 if (SF_IsForwardStub(m_dwStubFlags))
730 ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
732 pcsDispatch->EmitDUP();
733 pcsDispatch->EmitLDC(0); // Compare against S_OK (i.e. 0).
734 pcsDispatch->EmitBGE(pSkipThrowLabel);
736 #ifdef FEATURE_COMINTEROP
737 if (SF_IsCOMStub(m_dwStubFlags))
739 m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
740 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
742 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT, 3, 1);
745 #endif // FEATURE_COMINTEROP
747 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_HR_EXCEPTION_OBJECT, 1, 1);
750 pcsDispatch->EmitTHROW();
751 pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
752 pcsDispatch->EmitLabel(pSkipThrowLabel);
753 pcsDispatch->EmitPOP();
757 if (SF_IsCheckPendingException(m_dwStubFlags)
758 && SF_IsForwardStub(m_dwStubFlags))
760 ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
762 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_PENDING_EXCEPTION_OBJECT, 0, 1);
763 pcsDispatch->EmitDUP();
764 pcsDispatch->EmitBRFALSE(pSkipThrowLabel);
765 pcsDispatch->EmitTHROW();
766 pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
767 pcsDispatch->EmitLabel(pSkipThrowLabel);
768 pcsDispatch->EmitPOP();
771 m_slIL.End(m_dwStubFlags);
772 if (!hasTryCatchForHRESULT) // we will 'leave' the try scope and then 'ret' from outside
774 pcsUnmarshal->EmitRET();
777 CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB);
779 if (m_slIL.HasInteropParamExceptionInfo())
781 // This code will not use the secret parameter, so we do not
782 // tell the JIT to bother with it.
784 m_slIL.GenerateInteropParamException(pcsMarshal);
786 else if (SF_IsFieldGetterStub(m_dwStubFlags) || SF_IsFieldSetterStub(m_dwStubFlags))
788 // Field access stubs are not shared and do not use the secret parameter.
790 else if (SF_IsStructMarshalStub(m_dwStubFlags))
792 // Struct marshal stubs don't actually call anything so they do not need the secrect parameter.
795 else if (SF_IsForwardDelegateStub(m_dwStubFlags))
797 // Forward delegate stubs get all the context they need in 'this' so they
798 // don't use the secret parameter. Except for AMD64 where we use the secret
799 // argument to pass the real target to the stub-for-host.
801 #endif // !HOST_64BIT
804 // All other IL stubs will need to use the secret parameter.
805 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM);
808 if (SF_IsReverseStub(m_dwStubFlags))
810 SwapStubSignatures(pStubMD);
813 ILCodeLabel* pTryBeginLabel = nullptr;
814 ILCodeLabel* pTryEndAndCatchBeginLabel = nullptr;
815 ILCodeLabel* pCatchEndLabel = nullptr;
816 if (hasTryCatchExceptionHandler)
818 EmitExceptionHandler(&nativeReturnType, &managedReturnType, &pTryBeginLabel, &pTryEndAndCatchBeginLabel, &pCatchEndLabel);
827 cbCode = m_slIL.Link(&maxStack);
828 cbSig = m_slIL.GetLocalSigSize();
830 ILStubResolver * pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
831 COR_ILMETHOD_DECODER * pILHeader = pResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
832 pbBuffer = (BYTE *)pILHeader->Code;
833 pbLocalSig = (BYTE *)pILHeader->LocalVarSig;
834 _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
836 ILStubEHClause cleanupTryFinally{};
837 m_slIL.GetCleanupFinallyOffsets(&cleanupTryFinally);
839 ILStubEHClause tryCatchClause{};
840 if (hasTryCatchExceptionHandler)
842 tryCatchClause.kind = ILStubEHClause::kTypedCatch;
843 tryCatchClause.dwTryBeginOffset = pTryBeginLabel != nullptr ? (DWORD)pTryBeginLabel->GetCodeOffset() : 0;
844 tryCatchClause.dwHandlerBeginOffset = ((DWORD)pTryEndAndCatchBeginLabel->GetCodeOffset());
845 tryCatchClause.cbTryLength = tryCatchClause.dwHandlerBeginOffset - tryCatchClause.dwTryBeginOffset;
846 tryCatchClause.cbHandlerLength = ((DWORD)pCatchEndLabel->GetCodeOffset()) - tryCatchClause.dwHandlerBeginOffset;
847 tryCatchClause.dwTypeToken = pcsMarshal->GetToken(g_pObjectClass);
852 if (tryCatchClause.cbHandlerLength != 0)
855 if (cleanupTryFinally.cbHandlerLength != 0)
860 COR_ILMETHOD_SECT_EH* pEHSect = pResolver->AllocEHSect(nEHClauses);
861 PopulateEHSect(pEHSect, nEHClauses, &cleanupTryFinally, &tryCatchClause);
864 m_slIL.GenerateCode(pbBuffer, cbCode);
865 m_slIL.GetLocalSig(pbLocalSig, cbSig);
867 pResolver->SetJitFlags(jitFlags);
870 LOG((LF_STUBS, LL_INFO1000, "---------------------------------------------------------------------\n"));
871 LOG((LF_STUBS, LL_INFO1000, "NDirect IL stub dump: %s::%s\n", pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
872 if (LoggingEnabled() && LoggingOn(LF_STUBS, LL_INFO1000))
874 CQuickBytes qbManaged;
877 PCCOR_SIGNATURE pManagedSig;
880 IMDInternalImport* pIMDI = CoreLibBinder::GetModule()->GetMDImport();
882 pStubMD->GetSig(&pManagedSig, &cManagedSig);
884 PrettyPrintSig(pManagedSig, cManagedSig, "*", &qbManaged, pStubMD->GetMDImport(), NULL);
885 PrettyPrintSig(pbLocalSig, cbSig, NULL, &qbLocal, pIMDI, NULL);
887 LOG((LF_STUBS, LL_INFO1000, "incoming managed sig: %p: %s\n", pManagedSig, qbManaged.Ptr()));
888 LOG((LF_STUBS, LL_INFO1000, "locals sig: %p: %s\n", pbLocalSig+1, qbLocal.Ptr()));
890 if (cleanupTryFinally.cbHandlerLength != 0)
892 LOG((LF_STUBS, LL_INFO1000, "try_begin: 0x%04x try_end: 0x%04x finally_begin: 0x%04x finally_end: 0x%04x \n",
893 cleanupTryFinally.dwTryBeginOffset, cleanupTryFinally.dwTryBeginOffset + cleanupTryFinally.cbTryLength,
894 cleanupTryFinally.dwHandlerBeginOffset, cleanupTryFinally.dwHandlerBeginOffset + cleanupTryFinally.cbHandlerLength));
896 if (tryCatchClause.cbHandlerLength != 0)
898 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",
899 tryCatchClause.dwTryBeginOffset, tryCatchClause.dwTryBeginOffset + tryCatchClause.cbTryLength,
900 tryCatchClause.dwHandlerBeginOffset, tryCatchClause.dwHandlerBeginOffset + tryCatchClause.cbHandlerLength,
901 tryCatchClause.dwTypeToken));
904 LogILStubFlags(LF_STUBS, LL_INFO1000, m_dwStubFlags);
906 m_slIL.LogILStub(jitFlags);
908 LOG((LF_STUBS, LL_INFO1000, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"));
912 // Publish ETW events for IL stubs
915 // If the category and the event is enabled...
916 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ILStubGenerated))
918 EtwOnILStubGenerated(
933 // Truncates a SString by first converting it to unicode and truncate it
934 // if it is larger than size. "..." will be appended if it is truncated.
936 void TruncateUnicodeString(SString &string, COUNT_T bufSize)
939 if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize)
941 _ASSERTE(bufSize / sizeof(WCHAR) > 4);
942 string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4);
943 string.Append(W("..."));
947 //---------------------------------------------------------------------------------------
950 EtwOnILStubGenerated(
951 MethodDesc * pStubMD,
952 PCCOR_SIGNATURE pbLocalSig,
954 CORJIT_FLAGS jitFlags,
955 ILStubEHClause * pConvertToHRTryCatchBounds,
956 ILStubEHClause * pCleanupTryFinallyBounds,
960 STANDARD_VM_CONTRACT;
963 // Interop Method Information
965 MethodDesc *pTargetMD = m_slIL.GetTargetMD();
966 SString strNamespaceOrClassName, strMethodName, strMethodSignature;
967 UINT64 uModuleId = 0;
971 pTargetMD->GetMethodInfoWithNewSig(strNamespaceOrClassName, strMethodName, strMethodSignature);
972 uModuleId = (UINT64)pTargetMD->GetModule()->GetAddrModuleID();
976 // Stub Method Signature
978 SString stubNamespaceOrClassName, stubMethodName, stubMethodSignature;
979 pStubMD->GetMethodInfoWithNewSig(stubNamespaceOrClassName, stubMethodName, stubMethodSignature);
981 IMDInternalImport *pStubImport = pStubMD->GetModule()->GetMDImport();
984 PrettyPrintSig(pbLocalSig, (DWORD)cbSig, NULL, &qbLocal, pStubImport, NULL);
986 SString strLocalSig(SString::Utf8, (LPCUTF8)qbLocal.Ptr());
991 SString strNativeSignature(SString::Utf8);
992 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
994 // Reverse interop. Use StubSignature
995 strNativeSignature = stubMethodSignature;
999 // Forward interop. Use StubTarget siganture
1000 PCCOR_SIGNATURE pCallTargetSig = GetStubTargetMethodSig();
1001 DWORD cCallTargetSig = GetStubTargetMethodSigLength();
1003 CQuickBytes qbCallTargetSig;
1005 PrettyPrintSig(pCallTargetSig, cCallTargetSig, "", &qbCallTargetSig, pStubImport, NULL);
1007 strNativeSignature.SetUTF8((LPCUTF8)qbCallTargetSig.Ptr());
1011 // Dump IL stub code
1013 SString strILStubCode;
1014 strILStubCode.Preallocate(4096); // Preallocate 4K bytes to avoid unnecessary growth
1016 SString codeSizeFormat;
1017 codeSizeFormat.LoadResource(CCompRC::Optional, IDS_EE_INTEROP_CODE_SIZE_COMMENT);
1018 strILStubCode.AppendPrintf(W("// %s\t%d (0x%04x)\n"), codeSizeFormat.GetUnicode(), cbCode, cbCode);
1019 strILStubCode.AppendPrintf(W(".maxstack %d \n"), maxStack);
1020 strILStubCode.AppendPrintf(W(".locals %s\n"), strLocalSig.GetUnicode());
1022 m_slIL.LogILStub(jitFlags, &strILStubCode);
1024 if (pConvertToHRTryCatchBounds->cbTryLength != 0 && pConvertToHRTryCatchBounds->cbHandlerLength != 0)
1026 strILStubCode.AppendPrintf(
1027 W(".try IL_%04x to IL_%04x catch handler IL_%04x to IL_%04x\n"),
1028 pConvertToHRTryCatchBounds->dwTryBeginOffset,
1029 pConvertToHRTryCatchBounds->dwTryBeginOffset + pConvertToHRTryCatchBounds->cbTryLength,
1030 pConvertToHRTryCatchBounds->dwHandlerBeginOffset,
1031 pConvertToHRTryCatchBounds->dwHandlerBeginOffset + pConvertToHRTryCatchBounds->cbHandlerLength);
1034 if (pCleanupTryFinallyBounds->cbTryLength != 0 && pCleanupTryFinallyBounds->cbHandlerLength != 0)
1036 strILStubCode.AppendPrintf(
1037 W(".try IL_%04x to IL_%04x finally handler IL_%04x to IL_%04x\n"),
1038 pCleanupTryFinallyBounds->dwTryBeginOffset,
1039 pCleanupTryFinallyBounds->dwTryBeginOffset + pCleanupTryFinallyBounds->cbTryLength,
1040 pCleanupTryFinallyBounds->dwHandlerBeginOffset,
1041 pCleanupTryFinallyBounds->dwHandlerBeginOffset + pCleanupTryFinallyBounds->cbHandlerLength);
1048 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
1049 dwFlags |= ETW_IL_STUB_FLAGS_REVERSE_INTEROP;
1050 #ifdef FEATURE_COMINTEROP
1051 if (m_dwStubFlags & NDIRECTSTUB_FL_COM)
1052 dwFlags |= ETW_IL_STUB_FLAGS_COM_INTEROP;
1053 #endif // FEATURE_COMINTEROP
1054 if (m_dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUB)
1055 dwFlags |= ETW_IL_STUB_FLAGS_NGENED_STUB;
1056 if (m_dwStubFlags & NDIRECTSTUB_FL_DELEGATE)
1057 dwFlags |= ETW_IL_STUB_FLAGS_DELEGATE;
1058 if (m_dwStubFlags & NDIRECTSTUB_FL_CONVSIGASVARARG)
1059 dwFlags |= ETW_IL_STUB_FLAGS_VARARG;
1060 if (m_dwStubFlags & NDIRECTSTUB_FL_UNMANAGED_CALLI)
1061 dwFlags |= ETW_IL_STUB_FLAGS_UNMANAGED_CALLI;
1062 if (m_dwStubFlags & NDIRECTSTUB_FL_STRUCT_MARSHAL)
1063 dwFlags |= ETW_IL_STUB_FLAGS_STRUCT_MARSHAL;
1067 dwToken = pTargetMD->GetMemberDef();
1071 // Truncate string fields. Make sure the whole event is less than 64KB
1073 TruncateUnicodeString(strNamespaceOrClassName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1074 TruncateUnicodeString(strMethodName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1075 TruncateUnicodeString(strMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1076 TruncateUnicodeString(strNativeSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1077 TruncateUnicodeString(stubMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1078 TruncateUnicodeString(strILStubCode, ETW_IL_STUB_EVENT_CODE_STRING_FIELD_MAXSIZE);
1083 FireEtwILStubGenerated(
1084 GetClrInstanceId(), // ClrInstanceId
1085 uModuleId, // ModuleIdentifier
1086 (UINT64)pStubMD, // StubMethodIdentifier
1087 dwFlags, // StubFlags
1088 dwToken, // ManagedInteropMethodToken
1089 strNamespaceOrClassName.GetUnicode(), // ManagedInteropMethodNamespace
1090 strMethodName.GetUnicode(), // ManagedInteropMethodName
1091 strMethodSignature.GetUnicode(), // ManagedInteropMethodSignature
1092 strNativeSignature.GetUnicode(), // NativeSignature
1093 stubMethodSignature.GetUnicode(), // StubMethodSigature
1094 strILStubCode.GetUnicode() // StubMethodILCode
1096 } // EtwOnILStubGenerated
1097 #endif // DACCESS_COMPILE
1100 //---------------------------------------------------------------------------------------
1102 static inline void LogOneFlag(DWORD flags, DWORD flag, LPCSTR str, DWORD facility, DWORD level)
1104 LIMITED_METHOD_CONTRACT;
1107 LOG((facility, level, str));
1111 static void LogILStubFlags(DWORD facility, DWORD level, DWORD dwStubFlags)
1113 LIMITED_METHOD_CONTRACT;
1114 LOG((facility, level, "dwStubFlags: 0x%08x\n", dwStubFlags));
1115 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_CONVSIGASVARARG, " NDIRECTSTUB_FL_CONVSIGASVARARG\n", facility, level);
1116 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_BESTFIT, " NDIRECTSTUB_FL_BESTFIT\n", facility, level);
1117 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR, " NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR\n", facility, level);
1118 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUB, " NDIRECTSTUB_FL_NGENEDSTUB\n", facility, level);
1119 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DELEGATE, " NDIRECTSTUB_FL_DELEGATE\n", facility, level);
1120 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DOHRESULTSWAPPING, " NDIRECTSTUB_FL_DOHRESULTSWAPPING\n", facility, level);
1121 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_REVERSE_INTEROP, " NDIRECTSTUB_FL_REVERSE_INTEROP\n", facility, level);
1122 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_STRUCT_MARSHAL, " NDIRECTSTUB_FL_STRUCT_MARSHAL\n", facility, level);
1123 #ifdef FEATURE_COMINTEROP
1124 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_COM, " NDIRECTSTUB_FL_COM\n", facility, level);
1125 #endif // FEATURE_COMINTEROP
1126 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING, " NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING\n", facility, level);
1127 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL, " NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL\n", facility, level);
1128 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_UNMANAGED_CALLI, " NDIRECTSTUB_FL_UNMANAGED_CALLI\n", facility, level);
1129 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_TRIGGERCCTOR, " NDIRECTSTUB_FL_TRIGGERCCTOR\n", facility, level);
1130 #ifdef FEATURE_COMINTEROP
1131 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDGETTER, " NDIRECTSTUB_FL_FIELDGETTER\n", facility, level);
1132 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDSETTER, " NDIRECTSTUB_FL_FIELDSETTER\n", facility, level);
1133 #endif // FEATURE_COMINTEROP
1136 // no need to log the internal flags, let's just assert what we expect to see...
1138 CONSISTENCY_CHECK(!SF_IsCOMLateBoundStub(dwStubFlags));
1139 CONSISTENCY_CHECK(!SF_IsCOMEventCallStub(dwStubFlags));
1142 NDIRECTSTUB_FL_CONVSIGASVARARG |
1143 NDIRECTSTUB_FL_BESTFIT |
1144 NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR |
1145 NDIRECTSTUB_FL_NGENEDSTUB |
1146 NDIRECTSTUB_FL_DELEGATE |
1147 NDIRECTSTUB_FL_DOHRESULTSWAPPING |
1148 NDIRECTSTUB_FL_REVERSE_INTEROP |
1149 NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING |
1150 NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL |
1151 NDIRECTSTUB_FL_UNMANAGED_CALLI |
1152 NDIRECTSTUB_FL_STRUCT_MARSHAL |
1153 NDIRECTSTUB_FL_TRIGGERCCTOR |
1154 #ifdef FEATURE_COMINTEROP
1155 NDIRECTSTUB_FL_COM |
1156 NDIRECTSTUB_FL_COMLATEBOUND | // internal
1157 NDIRECTSTUB_FL_COMEVENTCALL | // internal
1158 NDIRECTSTUB_FL_FIELDGETTER |
1159 NDIRECTSTUB_FL_FIELDSETTER |
1160 #endif // FEATURE_COMINTEROP
1163 DWORD dwUnknownFlags = dwStubFlags & ~dwKnownMask;
1164 if (0 != dwUnknownFlags)
1166 LOG((facility, level, "UNKNOWN FLAGS: 0x%08x\n", dwUnknownFlags));
1171 PCCOR_SIGNATURE GetStubTargetMethodSig()
1173 CONTRACT(PCCOR_SIGNATURE)
1176 POSTCONDITION(CheckPointer(RETVAL, NULL_NOT_OK));
1182 if (!m_qbNativeFnSigBuffer.Size())
1184 DWORD cb = m_slIL.GetStubTargetMethodSigSize();
1185 pb = (BYTE *)m_qbNativeFnSigBuffer.AllocThrows(cb);
1187 m_slIL.GetStubTargetMethodSig(pb, cb);
1191 pb = (BYTE*)m_qbNativeFnSigBuffer.Ptr();
1198 GetStubTargetMethodSigLength()
1200 WRAPPER_NO_CONTRACT;
1202 return m_slIL.GetStubTargetMethodSigSize();
1205 void SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig)
1207 WRAPPER_NO_CONTRACT;
1209 m_slIL.SetStubTargetMethodSig(pSig, cSig);
1210 m_qbNativeFnSigBuffer.Shrink(0);
1213 TokenLookupMap* GetTokenLookupMap() { WRAPPER_NO_CONTRACT; return m_slIL.GetTokenLookupMap(); }
1215 DWORD GetFlags() const { return m_dwStubFlags; }
1218 CQuickBytes m_qbNativeFnSigBuffer;
1219 NDirectStubLinker m_slIL;
1220 BOOL m_fSetLastError;
1221 DWORD m_dwStubFlags;
1224 class StructMarshal_ILStubState : public ILStubState
1228 StructMarshal_ILStubState(MethodTable* pMT, const Signature& signature, SigTypeContext* pTypeContext, DWORD dwStubFlags)
1234 -1 /* We have no LCID parameter */,
1236 m_nativeSize(pMT->GetNativeSize())
1238 LIMITED_METHOD_CONTRACT;
1242 void BeginEmit(DWORD dwStubFlags)
1244 STANDARD_VM_CONTRACT;
1246 ILStubState::BeginEmit(dwStubFlags);
1248 ILCodeStream* pcsSetup = m_slIL.GetSetupCodeStream();
1249 ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
1250 ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
1251 ILCodeStream* pcsCleanup = m_slIL.GetCleanupCodeStream();
1253 pMarshalStartLabel = pcsSetup->NewCodeLabel();
1254 pCatchTrampolineStartLabel = pcsSetup->NewCodeLabel();
1255 pCatchTrampolineEndLabel = pcsSetup->NewCodeLabel();
1256 pUnmarshalStartLabel = pcsSetup->NewCodeLabel();
1257 pCleanupStartLabel = pcsSetup->NewCodeLabel();
1258 pReturnLabel = pcsSetup->NewCodeLabel();
1260 dwExceptionDispatchInfoLocal = pcsSetup->NewLocal(CoreLibBinder::GetClass(CLASS__EXCEPTION_DISPATCH_INFO));
1261 pcsSetup->EmitLDNULL();
1262 pcsSetup->EmitSTLOC(dwExceptionDispatchInfoLocal);
1264 pcsMarshal->EmitLabel(pMarshalStartLabel);
1265 pcsUnmarshal->EmitLabel(pUnmarshalStartLabel);
1266 pcsCleanup->EmitLabel(pCleanupStartLabel);
1268 // Initialize the native structure's memory so we can do a partial cleanup
1269 // if marshalling fails.
1270 pcsMarshal->EmitLDARG(StructMarshalStubs::NATIVE_STRUCT_ARGIDX);
1271 pcsMarshal->EmitLDC(0);
1272 pcsMarshal->EmitLDC(m_nativeSize);
1273 pcsMarshal->EmitINITBLK();
1276 void FinishEmit(MethodDesc* pStubMD)
1278 STANDARD_VM_CONTRACT;
1280 ILCodeStream* pcsSetup = m_slIL.GetSetupCodeStream();
1281 ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
1282 ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
1283 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
1284 ILCodeStream* pcsCleanup = m_slIL.GetCleanupCodeStream();
1286 pcsSetup->EmitNOP("// marshal operation jump table {");
1287 pcsSetup->EmitLDARG(StructMarshalStubs::OPERATION_ARGIDX);
1288 pcsSetup->EmitLDC(StructMarshalStubs::MarshalOperation::Marshal);
1289 pcsSetup->EmitBEQ(pMarshalStartLabel);
1290 pcsSetup->EmitLDARG(StructMarshalStubs::OPERATION_ARGIDX);
1291 pcsSetup->EmitLDC(StructMarshalStubs::MarshalOperation::Unmarshal);
1292 pcsSetup->EmitBEQ(pUnmarshalStartLabel);
1293 pcsSetup->EmitLDARG(StructMarshalStubs::OPERATION_ARGIDX);
1294 pcsSetup->EmitLDC(StructMarshalStubs::MarshalOperation::Cleanup);
1295 pcsSetup->EmitBEQ(pCleanupStartLabel);
1296 pcsSetup->EmitNOP("// } marshal operation jump table");
1298 // Clear native memory after release so we don't leave anything dangling.
1299 pcsCleanup->EmitLDARG(StructMarshalStubs::NATIVE_STRUCT_ARGIDX);
1300 pcsCleanup->EmitLDC(0);
1301 pcsCleanup->EmitLDC(m_nativeSize);
1302 pcsCleanup->EmitINITBLK();
1304 pcsMarshal->EmitLEAVE(pReturnLabel);
1305 pcsMarshal->EmitLabel(pCatchTrampolineStartLabel);
1306 // WARNING: The ILStubLinker has no knowledge that the exception object is on the stack
1308 // unaware that we've just entered a catch block), so we lie about the number of arguments
1309 // (say the method takes one less) to rebalance the stack.
1310 pcsMarshal->EmitCALL(METHOD__EXCEPTION_DISPATCH_INFO__CAPTURE, 0, 1);
1311 pcsMarshal->EmitSTLOC(dwExceptionDispatchInfoLocal);
1312 pcsMarshal->EmitLEAVE(pCleanupStartLabel);
1313 pcsMarshal->EmitLabel(pCatchTrampolineEndLabel);
1315 pcsDispatch->EmitLabel(pReturnLabel);
1316 pcsDispatch->EmitRET();
1318 pcsUnmarshal->EmitRET();
1320 pcsCleanup->EmitLDLOC(dwExceptionDispatchInfoLocal);
1321 pcsCleanup->EmitBRFALSE(pReturnLabel);
1322 pcsCleanup->EmitLDLOC(dwExceptionDispatchInfoLocal);
1323 pcsCleanup->EmitCALL(METHOD__EXCEPTION_DISPATCH_INFO__THROW, 0, 0);
1324 pcsCleanup->EmitRET();
1326 ILStubState::FinishEmit(pStubMD);
1329 virtual void EmitExceptionHandler(LocalDesc* pNativeReturnType, LocalDesc* pManagedReturnType,
1330 ILCodeLabel** ppTryBeginLabel, ILCodeLabel** ppTryEndCatchBeginLabel, ILCodeLabel** ppCatchEndLabel)
1332 *ppTryBeginLabel = pMarshalStartLabel;
1333 *ppTryEndCatchBeginLabel = pCatchTrampolineStartLabel;
1334 *ppCatchEndLabel = pCatchTrampolineEndLabel;
1338 ILCodeLabel* pMarshalStartLabel = nullptr;
1339 ILCodeLabel* pCatchTrampolineStartLabel = nullptr;
1340 ILCodeLabel* pCatchTrampolineEndLabel = nullptr;
1341 ILCodeLabel* pUnmarshalStartLabel = nullptr;
1342 ILCodeLabel* pCleanupStartLabel = nullptr;
1343 ILCodeLabel* pReturnLabel = nullptr;
1344 DWORD dwExceptionDispatchInfoLocal;
1346 UINT32 m_nativeSize;
1349 class PInvoke_ILStubState : public ILStubState
1353 PInvoke_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1354 CorInfoCallConvExtension unmgdCallConv, int iLCIDParamIdx, MethodDesc* pTargetMD)
1359 UpdateStubFlags(dwStubFlags, pTargetMD),
1363 STANDARD_VM_CONTRACT;
1364 m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags));
1368 static DWORD UpdateStubFlags(DWORD dwStubFlags, MethodDesc* pTargetMD)
1370 if (TargetHasThis(dwStubFlags))
1372 dwStubFlags |= NDIRECTSTUB_FL_TARGET_HAS_THIS;
1374 if (StubHasThis(dwStubFlags))
1376 dwStubFlags |= NDIRECTSTUB_FL_STUB_HAS_THIS;
1378 if ((dwStubFlags & NDIRECTSTUB_FL_SUPPRESSGCTRANSITION) == 0
1379 && TargetSuppressGCTransition(dwStubFlags, pTargetMD))
1381 dwStubFlags |= NDIRECTSTUB_FL_SUPPRESSGCTRANSITION;
1383 if (HasCheckForPendingException(pTargetMD))
1385 dwStubFlags |= NDIRECTSTUB_FL_CHECK_PENDING_EXCEPTION;
1390 static BOOL TargetHasThis(DWORD dwStubFlags)
1393 // in reverse pinvoke on delegate, the managed target will
1394 // have a 'this' pointer, but the unmanaged signature does
1397 return SF_IsReverseDelegateStub(dwStubFlags);
1400 static BOOL StubHasThis(DWORD dwStubFlags)
1403 // in forward pinvoke on a delegate, the stub will have a
1404 // 'this' pointer, but the unmanaged target will not.
1406 return SF_IsForwardDelegateStub(dwStubFlags);
1409 static BOOL TargetSuppressGCTransition(DWORD dwStubFlags, MethodDesc* pTargetMD)
1411 return SF_IsForwardStub(dwStubFlags) && pTargetMD && pTargetMD->ShouldSuppressGCTransition();
1414 static BOOL HasCheckForPendingException(MethodDesc* pTargetMD)
1416 #ifdef CROSSGEN_COMPILE
1419 if (pTargetMD == NULL || !pTargetMD->IsNDirect())
1422 auto pNMD = (NDirectMethodDesc*)pTargetMD;
1423 if (!Interop::ShouldCheckForPendingException(pNMD))
1427 #endif // !CROSSGEN_COMPILE
1431 #ifdef FEATURE_COMINTEROP
1432 class CLRToCOM_ILStubState : public ILStubState
1436 CLRToCOM_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1437 int iLCIDParamIdx, MethodDesc* pTargetMD)
1442 dwStubFlags | NDIRECTSTUB_FL_STUB_HAS_THIS | NDIRECTSTUB_FL_TARGET_HAS_THIS,
1446 STANDARD_VM_CONTRACT;
1448 if (SF_IsForwardStub(dwStubFlags))
1450 m_slIL.SetCallingConvention(CorInfoCallConvExtension::Stdcall, SF_IsVarArgStub(dwStubFlags));
1454 void BeginEmit(DWORD dwStubFlags) // CLR to COM IL
1456 STANDARD_VM_CONTRACT;
1458 ILStubState::BeginEmit(dwStubFlags);
1460 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
1462 // add the 'this' COM IP parameter to the target CALLI
1463 m_slIL.GetMarshalCodeStream()->SetStubTargetArgType(ELEMENT_TYPE_I, false);
1465 // convert 'this' to COM IP and the target method entry point
1466 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
1468 m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
1470 pcsDispatch->EmitLDLOCA(m_slIL.GetTargetEntryPointLocalNum());
1472 DWORD dwIPRequiresCleanupLocalNum = pcsDispatch->NewLocal(ELEMENT_TYPE_BOOLEAN);
1473 pcsDispatch->EmitLDLOCA(dwIPRequiresCleanupLocalNum);
1475 // StubHelpers.GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease)
1476 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW, 4, 1);
1478 // save it because we'll need it to compute the CALLI target and release it
1479 pcsDispatch->EmitDUP();
1480 pcsDispatch->EmitSTLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1482 // make sure it's Release()'ed after the call
1483 m_slIL.SetCleanupNeeded();
1484 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
1486 ILCodeLabel *pSkipThisCleanup = pcsCleanup->NewCodeLabel();
1488 // and if it requires cleanup (i.e. it's not taken from the RCW cache)
1489 pcsCleanup->EmitLDLOC(dwIPRequiresCleanupLocalNum);
1490 pcsCleanup->EmitBRFALSE(pSkipThisCleanup);
1492 pcsCleanup->EmitLDLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1493 pcsCleanup->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
1494 pcsCleanup->EmitLabel(pSkipThisCleanup);
1498 class COMToCLR_ILStubState : public ILStubState
1502 COMToCLR_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1503 int iLCIDParamIdx, MethodDesc* pTargetMD)
1508 dwStubFlags | NDIRECTSTUB_FL_STUB_HAS_THIS | NDIRECTSTUB_FL_TARGET_HAS_THIS,
1512 STANDARD_VM_CONTRACT;
1515 void BeginEmit(DWORD dwStubFlags) // COM to CLR IL
1517 STANDARD_VM_CONTRACT;
1519 ILStubState::BeginEmit(dwStubFlags);
1521 m_slIL.GetDispatchCodeStream()->EmitLoadThis();
1525 class COMToCLRFieldAccess_ILStubState : public COMToCLR_ILStubState
1529 COMToCLRFieldAccess_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext,
1530 DWORD dwStubFlags, FieldDesc* pFD)
1531 : COMToCLR_ILStubState(
1539 STANDARD_VM_CONTRACT;
1541 _ASSERTE(pFD != NULL);
1545 void EmitInvokeTarget(MethodDesc *pStubMD)
1547 STANDARD_VM_CONTRACT;
1549 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
1551 if (SF_IsFieldGetterStub(m_dwStubFlags))
1553 pcsDispatch->EmitLDFLD(pcsDispatch->GetToken(m_pFD));
1557 CONSISTENCY_CHECK(SF_IsFieldSetterStub(m_dwStubFlags));
1558 pcsDispatch->EmitSTFLD(pcsDispatch->GetToken(m_pFD));
1565 #endif // FEATURE_COMINTEROP
1567 ILStubLinkerFlags GetILStubLinkerFlagsForNDirectStubFlags(NDirectStubFlags flags)
1569 DWORD result = ILSTUB_LINKER_FLAG_NONE;
1570 if (!SF_IsCOMStub(flags))
1572 result |= ILSTUB_LINKER_FLAG_NDIRECT;
1574 if (SF_IsReverseStub(flags))
1576 result |= ILSTUB_LINKER_FLAG_REVERSE;
1578 if (flags & NDIRECTSTUB_FL_SUPPRESSGCTRANSITION)
1580 result |= ILSTUB_LINKER_FLAG_SUPPRESSGCTRANSITION;
1582 if (flags & NDIRECTSTUB_FL_STUB_HAS_THIS)
1584 result |= ILSTUB_LINKER_FLAG_STUB_HAS_THIS;
1586 if (flags & NDIRECTSTUB_FL_TARGET_HAS_THIS)
1588 result |= ILSTUB_LINKER_FLAG_TARGET_HAS_THIS;
1590 return (ILStubLinkerFlags)result;
1593 NDirectStubLinker::NDirectStubLinker(
1596 const Signature &signature,
1597 SigTypeContext *pTypeContext,
1598 MethodDesc* pTargetMD,
1600 : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, GetILStubLinkerFlagsForNDirectStubFlags((NDirectStubFlags)dwStubFlags)),
1601 m_pCleanupFinallyBeginLabel(NULL),
1602 m_pCleanupFinallyEndLabel(NULL),
1603 m_pSkipExceptionCleanupLabel(NULL),
1604 m_fHasCleanupCode(FALSE),
1605 m_fHasExceptionCleanupCode(FALSE),
1606 m_fCleanupWorkListIsSetup(FALSE),
1607 m_targetHasThis((dwStubFlags & NDIRECTSTUB_FL_TARGET_HAS_THIS) != 0),
1608 m_dwThreadLocalNum(-1),
1609 m_dwCleanupWorkListLocalNum(-1),
1610 m_dwRetValLocalNum(-1),
1612 m_ErrorParamIdx(-1),
1613 m_iLCIDParamIdx(iLCIDParamIdx),
1614 m_dwStubFlags(dwStubFlags)
1616 STANDARD_VM_CONTRACT;
1619 m_pcsSetup = NewCodeStream(ILStubLinker::kSetup); // do any one-time setup work
1620 m_pcsMarshal = NewCodeStream(ILStubLinker::kMarshal); // marshals arguments
1621 m_pcsDispatch = NewCodeStream(ILStubLinker::kDispatch); // sets up arguments and makes call
1622 m_pcsRetUnmarshal = NewCodeStream(ILStubLinker::kReturnUnmarshal); // unmarshals return value
1623 m_pcsUnmarshal = NewCodeStream(ILStubLinker::kUnmarshal); // unmarshals arguments
1624 m_pcsExceptionCleanup = NewCodeStream(ILStubLinker::kExceptionCleanup); // MAY NOT THROW: goes in a finally and does exception-only cleanup
1625 m_pcsCleanup = NewCodeStream(ILStubLinker::kCleanup); // MAY NOT THROW: goes in a finally and does unconditional cleanup
1629 m_dwArgMarshalIndexLocalNum = NewLocal(ELEMENT_TYPE_I4);
1630 m_pcsMarshal->EmitLDC(0);
1631 m_pcsMarshal->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1633 #ifdef FEATURE_COMINTEROP
1635 // Forward COM interop needs a local to hold target interface pointer
1637 if (SF_IsForwardCOMStub(m_dwStubFlags))
1639 m_dwTargetEntryPointLocalNum = NewLocal(ELEMENT_TYPE_I);
1640 m_dwTargetInterfacePointerLocalNum = NewLocal(ELEMENT_TYPE_I);
1641 m_pcsSetup->EmitLoadNullPtr();
1642 m_pcsSetup->EmitSTLOC(m_dwTargetInterfacePointerLocalNum);
1644 #endif // FEATURE_COMINTEROP
1647 void NDirectStubLinker::SetCallingConvention(CorInfoCallConvExtension unmngCallConv, BOOL fIsVarArg)
1649 LIMITED_METHOD_CONTRACT;
1651 #if !defined(TARGET_X86)
1654 // The JIT has to use a different calling convention for unmanaged vararg targets on 64-bit and ARM:
1655 // any float values must be duplicated in the corresponding general-purpose registers.
1656 SetStubTargetCallingConv(IMAGE_CEE_CS_CALLCONV_NATIVEVARARG);
1659 #endif // !TARGET_X86
1661 SetStubTargetCallingConv(unmngCallConv);
1665 void NDirectStubLinker::EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx)
1667 WRAPPER_NO_CONTRACT;
1670 // This sets our state local variable that tracks the progress of the stub execution.
1671 // In the finally block we test this variable to see what cleanup we need to do. The
1672 // variable starts with the value of 0 and is assigned the following values as the
1675 // CLEANUP_INDEX_ARG0_MARSHAL + 1 - 1st argument marshaled
1676 // CLEANUP_INDEX_ARG0_MARSHAL + 2 - 2nd argument marshaled
1678 // CLEANUP_INDEX_ARG0_MARSHAL + n - nth argument marshaled
1679 // CLEANUP_INDEX_RETVAL_UNMARSHAL + 1 - return value unmarshaled
1680 // CLEANUP_INDEX_ARG0_UNMARSHAL + 1 - 1st argument unmarshaled
1681 // CLEANUP_INDEX_ARG0_UNMARSHAL + 2 - 2nd argument unmarshaled
1683 // CLEANUP_INDEX_ARG0_UNMARSHAL + n - nth argument unmarshaled
1684 // CLEANUP_INDEX_ALL_DONE + 1 - ran to completion, no exception thrown
1686 // Note: There may be gaps, i.e. if say 2nd argument does not need cleanup, the
1687 // state variable will never be assigned the corresponding value. However, the
1688 // value must always monotonically increase so we can use <=, >, etc.
1691 pcsEmit->EmitLDC(uArgIdx + 1);
1692 pcsEmit->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1695 void NDirectStubLinker::EmitCheckForArgCleanup(ILCodeStream* pcsEmit, UINT uArgIdx, ArgCleanupBranchKind branchKind, ILCodeLabel* pSkipCleanupLabel)
1697 STANDARD_VM_CONTRACT;
1701 // See EmitSetArgMarshalIndex.
1702 pcsEmit->EmitLDLOC(m_dwArgMarshalIndexLocalNum);
1703 pcsEmit->EmitLDC(uArgIdx);
1707 case BranchIfMarshaled:
1709 // we branch to the label if the argument has been marshaled
1710 pcsEmit->EmitBGT(pSkipCleanupLabel);
1714 case BranchIfNotMarshaled:
1716 // we branch to the label if the argument has not been marshaled
1717 pcsEmit->EmitBLE(pSkipCleanupLabel);
1726 int NDirectStubLinker::GetLCIDParamIdx()
1728 LIMITED_METHOD_CONTRACT;
1729 return m_iLCIDParamIdx;
1732 ILCodeStream* NDirectStubLinker::GetSetupCodeStream()
1734 LIMITED_METHOD_CONTRACT;
1738 ILCodeStream* NDirectStubLinker::GetMarshalCodeStream()
1740 LIMITED_METHOD_CONTRACT;
1741 return m_pcsMarshal;
1744 ILCodeStream* NDirectStubLinker::GetUnmarshalCodeStream()
1746 LIMITED_METHOD_CONTRACT;
1747 return m_pcsUnmarshal;
1750 ILCodeStream* NDirectStubLinker::GetReturnUnmarshalCodeStream()
1752 LIMITED_METHOD_CONTRACT;
1753 return m_pcsRetUnmarshal;
1756 ILCodeStream* NDirectStubLinker::GetDispatchCodeStream()
1758 LIMITED_METHOD_CONTRACT;
1759 return m_pcsDispatch;
1762 ILCodeStream* NDirectStubLinker::GetCleanupCodeStream()
1764 LIMITED_METHOD_CONTRACT;
1765 return m_pcsCleanup;
1768 ILCodeStream* NDirectStubLinker::GetExceptionCleanupCodeStream()
1770 LIMITED_METHOD_CONTRACT;
1771 return m_pcsExceptionCleanup;
1774 void NDirectStubLinker::SetInteropParamExceptionInfo(UINT resID, UINT paramIdx)
1776 LIMITED_METHOD_CONTRACT;
1778 // only keep the first one
1779 if (HasInteropParamExceptionInfo())
1784 m_ErrorResID = resID;
1785 m_ErrorParamIdx = paramIdx;
1788 bool NDirectStubLinker::HasInteropParamExceptionInfo()
1790 LIMITED_METHOD_CONTRACT;
1792 return !(((DWORD)-1 == m_ErrorResID) && ((DWORD)-1 == m_ErrorParamIdx));
1795 void NDirectStubLinker::GenerateInteropParamException(ILCodeStream* pcsEmit)
1797 STANDARD_VM_CONTRACT;
1799 pcsEmit->EmitLDC(m_ErrorResID);
1800 pcsEmit->EmitLDC(m_ErrorParamIdx);
1801 pcsEmit->EmitCALL(METHOD__STUBHELPERS__THROW_INTEROP_PARAM_EXCEPTION, 2, 0);
1803 pcsEmit->EmitLDNULL();
1804 pcsEmit->EmitTHROW();
1807 #ifdef FEATURE_COMINTEROP
1808 DWORD NDirectStubLinker::GetTargetInterfacePointerLocalNum()
1810 LIMITED_METHOD_CONTRACT;
1811 CONSISTENCY_CHECK(m_dwTargetInterfacePointerLocalNum != (DWORD)-1);
1812 return m_dwTargetInterfacePointerLocalNum;
1814 DWORD NDirectStubLinker::GetTargetEntryPointLocalNum()
1816 LIMITED_METHOD_CONTRACT;
1817 CONSISTENCY_CHECK(m_dwTargetEntryPointLocalNum != (DWORD)-1);
1818 return m_dwTargetEntryPointLocalNum;
1821 void NDirectStubLinker::EmitLoadRCWThis(ILCodeStream *pcsEmit, DWORD dwStubFlags)
1823 STANDARD_VM_CONTRACT;
1825 pcsEmit->EmitLoadThis();
1827 #endif // FEATURE_COMINTEROP
1829 DWORD NDirectStubLinker::GetCleanupWorkListLocalNum()
1831 LIMITED_METHOD_CONTRACT;
1832 CONSISTENCY_CHECK(m_dwCleanupWorkListLocalNum != (DWORD)-1);
1833 return m_dwCleanupWorkListLocalNum;
1836 DWORD NDirectStubLinker::GetThreadLocalNum()
1838 STANDARD_VM_CONTRACT;
1840 if (m_dwThreadLocalNum == (DWORD)-1)
1842 // The local is created and initialized lazily when first asked.
1843 m_dwThreadLocalNum = NewLocal(ELEMENT_TYPE_I);
1844 m_pcsSetup->EmitCALL(METHOD__THREAD__INTERNAL_GET_CURRENT_THREAD, 0, 1);
1845 m_pcsSetup->EmitSTLOC(m_dwThreadLocalNum);
1848 return m_dwThreadLocalNum;
1851 DWORD NDirectStubLinker::GetReturnValueLocalNum()
1853 LIMITED_METHOD_CONTRACT;
1854 return m_dwRetValLocalNum;
1857 BOOL NDirectStubLinker::IsCleanupNeeded()
1859 LIMITED_METHOD_CONTRACT;
1861 return (m_fHasCleanupCode || IsCleanupWorkListSetup());
1864 BOOL NDirectStubLinker::IsExceptionCleanupNeeded()
1866 LIMITED_METHOD_CONTRACT;
1868 return m_fHasExceptionCleanupCode;
1871 void NDirectStubLinker::InitCleanupCode()
1876 PRECONDITION(NULL == m_pCleanupFinallyBeginLabel);
1880 m_pCleanupFinallyBeginLabel = NewCodeLabel();
1881 m_pcsExceptionCleanup->EmitLabel(m_pCleanupFinallyBeginLabel);
1884 void NDirectStubLinker::InitExceptionCleanupCode()
1889 PRECONDITION(NULL == m_pSkipExceptionCleanupLabel);
1895 // we want to skip the entire exception cleanup if no exception has been thrown
1896 m_pSkipExceptionCleanupLabel = NewCodeLabel();
1897 EmitCheckForArgCleanup(m_pcsExceptionCleanup, CLEANUP_INDEX_ALL_DONE, BranchIfMarshaled, m_pSkipExceptionCleanupLabel);
1900 void NDirectStubLinker::SetCleanupNeeded()
1902 WRAPPER_NO_CONTRACT;
1904 if (!m_fHasCleanupCode)
1906 m_fHasCleanupCode = TRUE;
1911 void NDirectStubLinker::SetExceptionCleanupNeeded()
1913 WRAPPER_NO_CONTRACT;
1915 if (!m_fHasExceptionCleanupCode)
1917 m_fHasExceptionCleanupCode = TRUE;
1918 InitExceptionCleanupCode();
1922 void NDirectStubLinker::NeedsCleanupList()
1924 STANDARD_VM_CONTRACT;
1926 if (!IsCleanupWorkListSetup())
1928 m_fCleanupWorkListIsSetup = TRUE;
1931 // we setup a new local that will hold the cleanup work list
1932 LocalDesc desc(CoreLibBinder::GetClass(CLASS__CLEANUP_WORK_LIST_ELEMENT));
1933 m_dwCleanupWorkListLocalNum = NewLocal(desc);
1938 BOOL NDirectStubLinker::IsCleanupWorkListSetup ()
1940 LIMITED_METHOD_CONTRACT;
1942 return m_fCleanupWorkListIsSetup;
1946 void NDirectStubLinker::LoadCleanupWorkList(ILCodeStream* pcsEmit)
1948 STANDARD_VM_CONTRACT;
1950 if (SF_IsStructMarshalStub(m_dwStubFlags))
1952 pcsEmit->EmitLDARG(StructMarshalStubs::CLEANUP_WORK_LIST_ARGIDX);
1957 pcsEmit->EmitLDLOCA(GetCleanupWorkListLocalNum());
1962 void NDirectStubLinker::Begin(DWORD dwStubFlags)
1964 STANDARD_VM_CONTRACT;
1966 if (SF_IsForwardStub(dwStubFlags))
1969 if (SF_IsStubWithCctorTrigger(dwStubFlags))
1971 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
1972 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__INIT_DECLARING_TYPE, 1, 0);
1977 if (SF_IsDelegateStub(dwStubFlags))
1980 // recover delegate object from UMEntryThunk
1982 EmitLoadStubContext(m_pcsDispatch, dwStubFlags); // load UMEntryThunk*
1984 m_pcsDispatch->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
1985 m_pcsDispatch->EmitADD();
1986 m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE
1987 m_pcsDispatch->EmitLDIND_REF(); // get Delegate object
1988 m_pcsDispatch->EmitLDFLD(GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__TARGET)));
1992 m_pCleanupTryBeginLabel = NewCodeLabel();
1993 m_pcsMarshal->EmitLabel(m_pCleanupTryBeginLabel);
1996 void NDirectStubLinker::End(DWORD dwStubFlags)
1998 STANDARD_VM_CONTRACT;
2000 ILCodeStream* pcs = m_pcsUnmarshal;
2002 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(dwStubFlags)
2003 && !SF_IsFieldGetterStub(dwStubFlags)
2004 && !SF_IsFieldSetterStub(dwStubFlags);
2007 // Create a local for the return value and store the return value in it.
2009 if ((IsCleanupNeeded() || hasTryCatchForHRESULT) && !SF_IsStructMarshalStub(dwStubFlags))
2011 // Save the return value if necessary, since the IL stack will be emptied when we leave a try block.
2012 LocalDesc locDescRetVal;
2013 if (SF_IsForwardStub(dwStubFlags))
2015 GetStubReturnType(&locDescRetVal);
2019 GetStubTargetReturnType(&locDescRetVal);
2022 if (!( (locDescRetVal.cbType == 1) && (locDescRetVal.ElementType[0] == ELEMENT_TYPE_VOID) ))
2024 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2025 if (SF_IsReverseStub(dwStubFlags) && StubHasVoidReturnType())
2027 // if the target returns void and we are doing HRESULT swapping, S_OK is loaded
2028 // in the unmarshal stream
2029 m_pcsUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2033 // otherwise the return value is loaded in the return unmarshal stream
2034 m_pcsRetUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2037 else if (hasTryCatchForHRESULT && (locDescRetVal.ElementType[0] != ELEMENT_TYPE_VOID))
2039 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2044 // Emit end-of-try and end-of-finally code for the try/finally
2046 if (IsCleanupNeeded() && !SF_IsStructMarshalStub(dwStubFlags))
2048 m_pCleanupFinallyEndLabel = NewCodeLabel();
2049 m_pCleanupTryEndLabel = NewCodeLabel();
2051 if (IsExceptionCleanupNeeded())
2053 // if we made it here, no exception has been thrown
2054 EmitSetArgMarshalIndex(m_pcsUnmarshal, CLEANUP_INDEX_ALL_DONE);
2057 // Emit a leave at the end of the try block. If we have an outer try/catch, we need
2058 // to leave to the beginning of the ExceptionHandler code stream, which follows the
2059 // Cleanup code stream. If we don't, we can just leave to the tail end of the
2060 // Unmarshal code stream where we'll emit our RET.
2062 ILCodeLabel* pLeaveTarget = m_pCleanupTryEndLabel;
2063 if (hasTryCatchForHRESULT)
2065 pLeaveTarget = m_pCleanupFinallyEndLabel;
2068 m_pcsUnmarshal->EmitLEAVE(pLeaveTarget);
2069 m_pcsUnmarshal->EmitLabel(m_pCleanupTryEndLabel);
2071 // Emit a call to destroy the clean-up list if needed.
2072 if (IsCleanupWorkListSetup())
2074 LoadCleanupWorkList(m_pcsCleanup);
2075 m_pcsCleanup->EmitCALL(METHOD__STUBHELPERS__DESTROY_CLEANUP_LIST, 1, 0);
2078 // Emit the endfinally.
2079 m_pcsCleanup->EmitENDFINALLY();
2080 m_pcsCleanup->EmitLabel(m_pCleanupFinallyEndLabel);
2083 if (IsExceptionCleanupNeeded())
2085 m_pcsExceptionCleanup->EmitLabel(m_pSkipExceptionCleanupLabel);
2088 // Reload the return value
2089 if ((m_dwRetValLocalNum != (DWORD)-1) && !hasTryCatchForHRESULT && !SF_IsStructMarshalStub(dwStubFlags))
2091 pcs->EmitLDLOC(m_dwRetValLocalNum);
2095 void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD)
2097 STANDARD_VM_CONTRACT;
2099 if (SF_IsStructMarshalStub(dwStubFlags))
2101 // Struct marshal stubs do not call anything, so this is a no-op
2105 if (SF_IsForwardStub(dwStubFlags)) // managed-to-native
2108 if (SF_IsDelegateStub(dwStubFlags)) // delegate invocation
2110 // get the delegate unmanaged target - we call a helper instead of just grabbing
2111 // the _methodPtrAux field because we may need to intercept the call for host, etc.
2112 pcsEmit->EmitLoadThis();
2114 // on AMD64 GetDelegateTarget will return address of the generic stub for host when we are hosted
2115 // and update the secret argument with real target - the secret arg will be embedded in the
2116 // InlinedCallFrame by the JIT and fetched via TLS->Thread->Frame->Datum by the stub for host
2117 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
2118 #else // !TARGET_64BIT
2119 // we don't need to do this on x86 because stub for host is generated dynamically per target
2120 pcsEmit->EmitLDNULL();
2121 #endif // !TARGET_64BIT
2122 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 2, 1);
2124 else // direct invocation
2126 if (SF_IsCALLIStub(dwStubFlags)) // unmanaged CALLI
2128 // if we ever NGEN CALLI stubs, this would have to be done differently
2129 _ASSERTE(!SF_IsNGENedStub(dwStubFlags));
2131 // for managed-to-unmanaged CALLI that requires marshaling, the target is passed
2132 // as the secret argument to the stub by GenericPInvokeCalliHelper (asmhelpers.asm)
2133 EmitLoadStubContext(pcsEmit, dwStubFlags);
2135 // the secret arg has been shifted to left and ORed with 1 (see code:GenericPInvokeCalliHelper)
2136 pcsEmit->EmitLDC(1);
2137 pcsEmit->EmitSHR_UN();
2141 #ifdef FEATURE_COMINTEROP
2142 if (!SF_IsCOMStub(dwStubFlags)) // forward P/Invoke
2143 #endif // FEATURE_COMINTEROP
2145 EmitLoadStubContext(pcsEmit, dwStubFlags);
2146 // pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_NDIRECT_TARGET, 1, 1);
2148 #ifdef _DEBUG // There are five more pointer values for _DEBUG
2149 _ASSERTE(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData) == sizeof(void*) * 8 + 8);
2150 pcsEmit->EmitLDC(TARGET_POINTER_SIZE * 8 + 8); // offsetof(NDirectMethodDesc, ndirect.m_pWriteableData)
2152 _ASSERTE(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData) == sizeof(void*) * 3 + 8);
2153 pcsEmit->EmitLDC(TARGET_POINTER_SIZE * 3 + 8); // offsetof(NDirectMethodDesc, ndirect.m_pWriteableData)
2157 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2162 pcsEmit->EmitLDIND_I();
2164 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2169 pcsEmit->EmitLDIND_I();
2171 #ifdef FEATURE_COMINTEROP
2174 // this is a CLR -> COM call
2175 // the target has been computed by StubHelpers::GetCOMIPFromRCW
2176 pcsEmit->EmitLDLOC(m_dwTargetEntryPointLocalNum);
2178 #endif // FEATURE_COMINTEROP
2181 else // native-to-managed
2183 if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate
2185 int tokDelegate_methodPtr = pcsEmit->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR));
2187 EmitLoadStubContext(pcsEmit, dwStubFlags);
2188 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2190 pcsEmit->EmitLDIND_I(); // Get OBJECTHANDLE
2191 pcsEmit->EmitLDIND_REF(); // Get Delegate object
2192 pcsEmit->EmitLDFLD(tokDelegate_methodPtr); // get _methodPtr
2194 #ifdef FEATURE_COMINTEROP
2195 else if (SF_IsCOMStub(dwStubFlags)) // COM -> CLR call
2197 // managed target is passed directly in the secret argument
2198 EmitLoadStubContext(pcsEmit, dwStubFlags);
2200 #endif // FEATURE_COMINTEROP
2201 else // direct reverse P/Invoke (CoreCLR hosting)
2203 EmitLoadStubContext(pcsEmit, dwStubFlags);
2204 CONSISTENCY_CHECK(0 == offsetof(UMEntryThunk, m_pManagedTarget)); // if this changes, just add back the EmitLDC/EmitADD below
2205 // pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pManagedTarget));
2206 // pcsEmit->EmitADD();
2207 pcsEmit->EmitLDIND_I(); // Get UMEntryThunk::m_pManagedTarget
2211 // For managed-to-native calls, the rest of the work is done by the JIT. It will
2212 // erect InlinedCallFrame, flip GC mode, and use the specified calling convention
2213 // to call the target. For native-to-managed calls, this is an ordinary managed
2214 // CALLI and nothing special happens.
2215 pcsEmit->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, 0, m_iTargetStackDelta);
2218 void NDirectStubLinker::EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwPinnedLocal)
2220 STANDARD_VM_CONTRACT;
2222 if (SF_IsForwardPInvokeStub(m_dwStubFlags) && !SF_IsForwardDelegateStub(m_dwStubFlags))
2224 // get the secret argument via intrinsic
2225 pslILEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2229 // no secret argument
2230 pslILEmit->EmitLoadNullPtr();
2233 pslILEmit->EmitLDLOC(dwPinnedLocal);
2235 pslILEmit->EmitCALL(METHOD__STUBHELPERS__LOG_PINNED_ARGUMENT, 2, 0);
2238 #ifndef DACCESS_COMPILE
2239 void NDirectStubLinker::GetCleanupFinallyOffsets(ILStubEHClause * pClause)
2244 PRECONDITION(CheckPointer(pClause));
2248 if (m_pCleanupFinallyEndLabel)
2250 _ASSERTE(m_pCleanupFinallyBeginLabel);
2251 _ASSERTE(m_pCleanupTryBeginLabel);
2252 _ASSERTE(m_pCleanupTryEndLabel);
2254 pClause->kind = ILStubEHClause::kFinally;
2255 pClause->dwTryBeginOffset = (DWORD)m_pCleanupTryBeginLabel->GetCodeOffset();
2256 pClause->cbTryLength = (DWORD)m_pCleanupTryEndLabel->GetCodeOffset() - pClause->dwTryBeginOffset;
2257 pClause->dwHandlerBeginOffset = (DWORD)m_pCleanupFinallyBeginLabel->GetCodeOffset();
2258 pClause->cbHandlerLength = (DWORD)m_pCleanupFinallyEndLabel->GetCodeOffset() - pClause->dwHandlerBeginOffset;
2261 #endif // DACCESS_COMPILE
2263 void NDirectStubLinker::ClearCode()
2265 WRAPPER_NO_CONTRACT;
2266 ILStubLinker::ClearCode();
2268 m_pCleanupTryBeginLabel = 0;
2269 m_pCleanupTryEndLabel = 0;
2270 m_pCleanupFinallyBeginLabel = 0;
2271 m_pCleanupFinallyEndLabel = 0;
2274 #ifdef PROFILING_SUPPORTED
2275 DWORD NDirectStubLinker::EmitProfilerBeginTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2277 STANDARD_VM_CONTRACT;
2279 if (SF_IsForwardDelegateStub(dwStubFlags) || SF_IsCALLIStub(dwStubFlags))
2281 // secret argument does not contain MD nor UMEntryThunk
2282 pcsEmit->EmitLoadNullPtr();
2286 EmitLoadStubContext(pcsEmit, dwStubFlags);
2289 if (SF_IsForwardStub(dwStubFlags))
2291 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2295 // we use a null pThread to indicate reverse interop
2296 pcsEmit->EmitLoadNullPtr();
2299 // In the unmanaged delegate case, we need the "this" object to retrieve the MD
2300 // in StubHelpers::ProfilerEnterCallback().
2301 if (SF_IsDelegateStub(dwStubFlags))
2303 if (SF_IsForwardStub(dwStubFlags))
2305 pcsEmit->EmitLoadThis();
2309 EmitLoadStubContext(pcsEmit, dwStubFlags); // load UMEntryThunk*
2310 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2312 pcsEmit->EmitLDIND_I(); // get OBJECTHANDLE
2313 pcsEmit->EmitLDIND_REF(); // get Delegate object
2318 pcsEmit->EmitLDNULL();
2321 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_BEGIN_TRANSITION_CALLBACK, 3, 1);
2323 // Store the MD for StubHelpers::ProfilerLeaveCallback().
2324 DWORD dwMethodDescLocalNum = pcsEmit->NewLocal(ELEMENT_TYPE_I);
2325 pcsEmit->EmitSTLOC(dwMethodDescLocalNum);
2326 return dwMethodDescLocalNum;
2329 void NDirectStubLinker::EmitProfilerEndTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags, DWORD dwMethodDescLocalNum)
2331 STANDARD_VM_CONTRACT;
2333 pcsEmit->EmitLDLOC(dwMethodDescLocalNum);
2334 if (SF_IsReverseStub(dwStubFlags))
2336 // we use a null pThread to indicate reverse interop
2337 pcsEmit->EmitLoadNullPtr();
2341 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2343 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_END_TRANSITION_CALLBACK, 2, 0);
2345 #endif // PROFILING_SUPPPORTED
2348 void NDirectStubLinker::EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNum, bool fIsByref, DWORD dwStubFlags)
2350 STANDARD_VM_CONTRACT;
2352 pcsEmit->EmitLDLOC(dwLocalNum);
2354 if (SF_IsDelegateStub(dwStubFlags))
2356 pcsEmit->EmitLoadNullPtr();
2357 pcsEmit->EmitLoadThis();
2359 else if (SF_IsCALLIStub(dwStubFlags))
2361 pcsEmit->EmitLoadNullPtr();
2362 pcsEmit->EmitLDNULL();
2366 // P/Invoke, CLR->COM
2367 EmitLoadStubContext(pcsEmit, dwStubFlags);
2368 pcsEmit->EmitLDNULL();
2373 // StubHelpers.ValidateByref(byref, pMD, pThis)
2374 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 3, 0);
2378 // StubHelpers.ValidateObject(obj, pMD, pThis)
2379 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 3, 0);
2383 void NDirectStubLinker::EmitObjectValidation(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2385 STANDARD_VM_CONTRACT;
2387 // generate validation callouts for pinned locals
2388 CQuickBytes qbLocalSig;
2389 DWORD cbSig = GetLocalSigSize();
2391 qbLocalSig.AllocThrows(cbSig);
2392 PCOR_SIGNATURE pSig = (PCOR_SIGNATURE)qbLocalSig.Ptr();
2394 GetLocalSig(pSig, cbSig);
2395 SigPointer ptr(pSig, cbSig);
2397 IfFailThrow(ptr.GetData(NULL)); // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
2400 IfFailThrow(ptr.GetData(&numLocals));
2402 for (uint32_t i = 0; i < numLocals; i++)
2405 IfFailThrow(ptr.PeekByte(&modifier));
2406 if (modifier == ELEMENT_TYPE_PINNED)
2408 IfFailThrow(ptr.GetByte(NULL));
2409 IfFailThrow(ptr.PeekByte(&modifier));
2410 EmitValidateLocal(pcsEmit, i, (modifier == ELEMENT_TYPE_BYREF), dwStubFlags);
2413 IfFailThrow(ptr.SkipExactlyOne());
2416 #endif // VERIFY_HEAP
2418 // Loads the 'secret argument' passed to the stub.
2419 void NDirectStubLinker::EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2421 STANDARD_VM_CONTRACT;
2423 CONSISTENCY_CHECK(!SF_IsForwardDelegateStub(dwStubFlags));
2424 CONSISTENCY_CHECK(!SF_IsFieldGetterStub(dwStubFlags) && !SF_IsFieldSetterStub(dwStubFlags));
2425 // get the secret argument via intrinsic
2426 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2429 #ifdef FEATURE_COMINTEROP
2431 class DispatchStubState : public StubState // For CLR-to-COM late-bound/eventing calls
2438 WRAPPER_NO_CONTRACT;
2441 void SetLastError(BOOL fSetLastError)
2443 LIMITED_METHOD_CONTRACT;
2445 CONSISTENCY_CHECK(!fSetLastError);
2448 void BeginEmit(DWORD dwStubFlags)
2450 LIMITED_METHOD_CONTRACT;
2452 CONSISTENCY_CHECK(SF_IsCOMStub(dwStubFlags));
2453 m_dwStubFlags = dwStubFlags;
2456 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
2462 PRECONDITION(CheckPointer(pInfo));
2467 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
2472 PRECONDITION(CheckPointer(pInfo));
2476 if (SF_IsCOMLateBoundStub(m_dwStubFlags) && pInfo->GetDispWrapperType() != 0)
2478 m_lateBoundFlags |= ComPlusCallInfo::kRequiresArgumentWrapping;
2482 void MarshalLCID(int argIdx)
2484 LIMITED_METHOD_CONTRACT;
2487 void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc)
2489 LIMITED_METHOD_CONTRACT;
2493 #ifdef FEATURE_COMINTEROP
2494 void MarshalHiddenLengthArgument(MarshalInfo *, BOOL)
2496 LIMITED_METHOD_CONTRACT;
2498 void MarshalFactoryReturn()
2500 LIMITED_METHOD_CONTRACT;
2503 #endif // FEATURE_COMINTEROP
2505 void EmitInvokeTarget(MethodDesc *pStubMD)
2507 LIMITED_METHOD_CONTRACT;
2508 UNREACHABLE_MSG("Should never come to DispatchStubState::EmitInvokeTarget");
2511 void FinishEmit(MethodDesc *pMD)
2513 STANDARD_VM_CONTRACT;
2515 // set flags directly on the interop MD
2516 _ASSERTE(pMD->IsComPlusCall());
2518 ((ComPlusCallMethodDesc *)pMD)->SetLateBoundFlags(m_lateBoundFlags);
2522 DWORD m_dwStubFlags;
2523 BYTE m_lateBoundFlags; // ComPlusCallMethodDesc::Flags
2526 #endif // FEATURE_COMINTEROP
2530 // Use CorInfoCallConvExtension::Managed as a sentinel represent a user-provided WinApi calling convention.
2531 constexpr CorInfoCallConvExtension CallConvWinApiSentinel = CorInfoCallConvExtension::Managed;
2533 // Returns the unmanaged calling convention for callConv or CallConvWinApiSentinel
2534 // if the calling convention is not provided or WinApi.
2535 CorInfoCallConvExtension GetCallConvValueForPInvokeCallConv(CorPinvokeMap callConv)
2537 LIMITED_METHOD_CONTRACT;
2542 case pmCallConvWinapi:
2543 return CallConvWinApiSentinel;
2544 case pmCallConvCdecl:
2545 return CorInfoCallConvExtension::C;
2546 case pmCallConvStdcall:
2547 return CorInfoCallConvExtension::Stdcall;
2548 case pmCallConvThiscall:
2549 return CorInfoCallConvExtension::Thiscall;
2550 case pmCallConvFastcall:
2551 return CorInfoCallConvExtension::Fastcall;
2553 _ASSERTE_MSG(false, "Invalid PInvoke callconv.");
2554 return CallConvWinApiSentinel;
2558 // Convert a CorNativeLinkType into an unambiguous usable value.
2559 HRESULT RemapLinkType(_In_ CorNativeLinkType value, _Out_ CorNativeLinkType* nlt)
2561 LIMITED_METHOD_CONTRACT;
2562 _ASSERTE(nlt != NULL);
2564 // Handle case where the value is not in the defined enumeration.
2565 if ((int)value == 0)
2577 #ifdef TARGET_WINDOWS
2580 *nlt = nltAnsi; // We don't have a utf8 charset in metadata so ANSI == UTF-8 off-Windows
2584 return E_INVALIDARG;
2587 // Validate we remapped to a usable value.
2588 _ASSERTE(*nlt == nltAnsi || *nlt == nltUnicode);
2592 HRESULT ParseCallingConventionFromAttributeConstructor(_Inout_ CustomAttributeParser& ca, _Out_ CorInfoCallConvExtension* callConv)
2594 LIMITED_METHOD_CONTRACT;
2595 _ASSERTE(callConv != NULL);
2598 callConvArg.InitEnum(SERIALIZATION_TYPE_I4, (ULONG)0);
2599 HRESULT hr = ParseKnownCaArgs(ca, &callConvArg, 1);
2603 *callConv = GetCallConvValueForPInvokeCallConv((CorPinvokeMap)(callConvArg.val.u4 << 8));
2609 void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)
2619 // initialize data members
2621 m_pModule = pModule;
2622 m_callConv = CallConvWinApiSentinel;
2623 SetBestFitMapping (TRUE);
2624 SetThrowOnUnmappableChar (FALSE);
2625 SetLinkFlags (nlfNone);
2626 SetCharSet (nltAnsi);
2628 // assembly/type level m_bestFit & m_bThrowOnUnmappableChar
2630 BOOL bThrowOnUnmappableChar;
2634 EEClass::GetBestFitMapping(pMT, &bBestFit, &bThrowOnUnmappableChar);
2638 ReadBestFitCustomAttribute(m_pModule, mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
2641 SetBestFitMapping (bBestFit);
2642 SetThrowOnUnmappableChar (bThrowOnUnmappableChar);
2645 void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
2655 PreInit(pMD->GetModule(), pMD->GetMethodTable());
2656 m_sig = pMD->GetSignature();
2657 if (pMD->IsEEImpl())
2659 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
2660 SetIsDelegateInterop(TRUE);
2664 PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2665 _In_ MethodDesc* pMD,
2666 _Outptr_opt_ LPCUTF8* pLibName,
2667 _Outptr_opt_ LPCUTF8* pEntryPointName)
2672 PRECONDITION(CheckPointer(pMD));
2676 DllImportInit(pMD, pLibName, pEntryPointName);
2679 PInvokeStaticSigInfo::PInvokeStaticSigInfo(_In_ MethodDesc* pMD)
2684 PRECONDITION(CheckPointer(pMD));
2690 MethodTable * pMT = pMD->GetMethodTable();
2692 if (!pMT->IsDelegate())
2694 DllImportInit(pMD, NULL, NULL);
2698 // initialize data members to defaults
2701 // System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
2704 CorInfoCallConvExtension callConv = CallConvWinApiSentinel;
2706 hr = pMT->GetCustomAttribute(
2707 WellKnownAttribute::UnmanagedFunctionPointer, (const VOID **)(&pData), (ULONG *)&cData);
2711 CustomAttributeParser ca(pData, cData);
2713 IfFailGo(ParseCallingConventionFromAttributeConstructor(ca, &callConv));
2715 enum UnmanagedFunctionPointerNamedArgs
2719 MDA_ThrowOnUnmappableChar,
2724 CaNamedArg namedArgs[MDA_Last];
2725 namedArgs[MDA_CharSet].InitI4FieldEnum("CharSet", "System.Runtime.InteropServices.CharSet", (ULONG)GetCharSet());
2726 namedArgs[MDA_BestFitMapping].InitBoolField("BestFitMapping", (ULONG)GetBestFitMapping());
2727 namedArgs[MDA_ThrowOnUnmappableChar].InitBoolField("ThrowOnUnmappableChar", (ULONG)GetThrowOnUnmappableChar());
2728 namedArgs[MDA_SetLastError].InitBoolField("SetLastError", 0);
2730 IfFailGo(ParseKnownCaNamedArgs(ca, namedArgs, lengthof(namedArgs)));
2732 CorNativeLinkType nlt;
2733 IfFailGo(RemapLinkType((CorNativeLinkType)namedArgs[MDA_CharSet].val.u4, &nlt));
2736 SetBestFitMapping (namedArgs[MDA_BestFitMapping].val.u1);
2737 SetThrowOnUnmappableChar (namedArgs[MDA_ThrowOnUnmappableChar].val.u1);
2738 if (namedArgs[MDA_SetLastError].val.u1)
2739 SetLinkFlags ((CorNativeLinkFlags)(nlfLastError | GetLinkFlags()));
2742 InitCallConv(callConv, pMD->IsVarArg());
2746 ThrowError(IDS_EE_NDIRECT_BADNATL);
2749 PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2750 _In_ const Signature& sig, _In_ Module* pModule)
2756 PRECONDITION(CheckPointer(pModule));
2760 PreInit(pModule, NULL);
2762 InitCallConv(CallConvWinApiSentinel, FALSE);
2765 void PInvokeStaticSigInfo::DllImportInit(
2766 _In_ MethodDesc* pMD,
2767 _Outptr_opt_ LPCUTF8* ppLibName,
2768 _Outptr_opt_ LPCUTF8* ppEntryPointName)
2774 PRECONDITION(CheckPointer(pMD));
2776 // These preconditions to prevent multithreaded regression
2777 // where pMD->ndirect.m_szLibName was passed in directly, cleared
2778 // by this API, then accessed on another thread before being reset here.
2779 PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
2780 PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
2784 // initialize data members to defaults
2787 // System.Runtime.InteropServices.DllImportAttribute
2788 IMDInternalImport *pInternalImport = pMD->GetMDImport();
2789 CorPinvokeMap mappingFlags = pmMaxValue;
2790 mdModuleRef modref = mdModuleRefNil;
2791 if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
2793 InitCallConv(CallConvWinApiSentinel, pMD);
2797 if (ppEntryPointName && *ppEntryPointName == NULL)
2798 *ppEntryPointName = pMD->GetName();
2800 // out parameter pLibName
2801 if (ppLibName != NULL)
2803 if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
2805 ThrowError(IDS_CLASSLOAD_BADFORMAT);
2810 InitCallConv(GetCallConvValueForPInvokeCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask)), pMD);
2813 CorPinvokeMap bestFitMask = (CorPinvokeMap)(mappingFlags & pmBestFitMask);
2814 if (bestFitMask == pmBestFitEnabled)
2815 SetBestFitMapping (TRUE);
2816 else if (bestFitMask == pmBestFitDisabled)
2817 SetBestFitMapping (FALSE);
2819 // m_bThrowOnUnmappableChar
2820 CorPinvokeMap unmappableMask = (CorPinvokeMap)(mappingFlags & pmThrowOnUnmappableCharMask);
2821 if (unmappableMask == pmThrowOnUnmappableCharEnabled)
2822 SetThrowOnUnmappableChar (TRUE);
2823 else if (unmappableMask == pmThrowOnUnmappableCharDisabled)
2824 SetThrowOnUnmappableChar (FALSE);
2826 // linkFlags : CorPinvoke -> CorNativeLinkFlags
2827 if (mappingFlags & pmSupportsLastError)
2828 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfLastError));
2829 if (mappingFlags & pmNoMangle)
2830 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
2832 // charset : CorPinvoke -> CorNativeLinkType
2833 CorPinvokeMap charSetMask = (CorPinvokeMap)(mappingFlags & (pmCharSetNotSpec | pmCharSetAnsi | pmCharSetUnicode | pmCharSetAuto));
2834 CorNativeLinkType nlt = nltMaxValue; // Initialize to invalid value
2835 switch (charSetMask)
2837 case pmCharSetNotSpec:
2841 case pmCharSetUnicode:
2848 _ASSERTE("Unknown CharSet mask value");
2852 if (FAILED(RemapLinkType(nlt, &nlt)))
2853 ThrowError(IDS_EE_NDIRECT_BADNATL);
2858 #if !defined(CROSSGEN_COMPILE) // IJW
2860 // This function would work, but be unused on Unix. Ifdefing out to avoid build errors due to the unused function.
2861 #if !defined (TARGET_UNIX)
2862 static LPBYTE FollowIndirect(LPBYTE pTarget)
2869 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
2877 AVInRuntimeImplOkayHolder AVOkay;
2880 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
2882 pRet = **(LPBYTE**)(pTarget + 2);
2884 #elif defined(TARGET_AMD64)
2885 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
2887 INT64 rva = *(INT32*)(pTarget + 2);
2888 pRet = *(LPBYTE*)(pTarget + 6 + rva);
2896 EX_END_CATCH(SwallowAllExceptions);
2900 #endif // !TARGET_UNIX
2902 BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget)
2912 #if !defined(TARGET_UNIX)
2913 static LPBYTE pGetLastError = NULL;
2916 // No need to use a holder here, since no cleanup is necessary.
2917 HMODULE hMod = WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
2920 pGetLastError = (LPBYTE)GetProcAddress(hMod, "GetLastError");
2923 // This should never happen but better to be cautious.
2924 pGetLastError = (LPBYTE)-1;
2929 // We failed to get the module handle for kernel32.dll. This is almost impossible
2930 // however better to err on the side of caution.
2931 pGetLastError = (LPBYTE)-1;
2935 if (pTarget == pGetLastError)
2938 if (pTarget == NULL)
2941 LPBYTE pTarget2 = FollowIndirect(pTarget);
2944 // jmp [xxxx] - could be an import thunk
2945 return pTarget2 == pGetLastError;
2947 #endif // !TARGET_UNIX
2952 DWORD STDMETHODCALLTYPE FalseGetLastError()
2954 WRAPPER_NO_CONTRACT;
2956 return GetThread()->m_dwLastError;
2959 #endif // !CROSSGEN_COMPILE
2961 CorInfoCallConvExtension GetDefaultCallConv(BOOL bIsVarArg)
2963 return bIsVarArg ? CorInfoCallConvExtension::C : CallConv::GetDefaultUnmanagedCallingConvention();
2966 void PInvokeStaticSigInfo::InitCallConv(_In_ CorInfoCallConvExtension callConv, _In_ MethodDesc *pMD)
2971 PRECONDITION(pMD != NULL);
2975 #ifdef CROSSGEN_COMPILE
2976 _ASSERTE_MSG(!pMD->HasUnmanagedCallConvAttribute(), "UnmanagedCallConv methods are not supported in crossgen and should be rejected before getting here.");
2978 // If the calling convention has not been determined yet, check the UnmanagedCallConv attribute
2979 if (callConv == CallConvWinApiSentinel)
2981 CallConvBuilder builder;
2982 UINT errorResID = 0;
2984 // System.Runtime.InteropServices.UnmanagedCallConvAttribute
2985 HRESULT hr = CallConv::TryGetCallingConventionFromUnmanagedCallConv(pMD, &builder, &errorResID);
2988 // Use a generic error message for P/Invokes or UnmanagedFunction if no specific one was provided
2989 ThrowError(errorResID == 0 ? IDS_EE_NDIRECT_BADNATL : errorResID);
2994 callConv = builder.GetCurrentCallConv();
2995 if (builder.IsCurrentCallConvModSet(CallConvBuilder::CALL_CONV_MOD_SUPPRESSGCTRANSITION))
2997 SetShouldSuppressGCTransition(TRUE);
3001 #endif // CROSSGEN_COMPILE
3003 InitCallConv(callConv, pMD->IsVarArg());
3006 void PInvokeStaticSigInfo::InitCallConv(CorInfoCallConvExtension callConv, BOOL bIsVarArg)
3008 STANDARD_VM_CONTRACT;
3010 CallConvBuilder builder;
3012 HRESULT hr = CallConv::TryGetUnmanagedCallingConventionFromModOpt(GetScopeHandle(m_pModule), m_sig.GetRawSig(), m_sig.GetRawSigLen(), &builder, &errorResID);
3015 // Use an error message specific to P/Invokes or UnmanagedFunction for bad format.
3016 ThrowError(hr == COR_E_BADIMAGEFORMAT ? IDS_EE_NDIRECT_BADNATL : errorResID);
3019 CorInfoCallConvExtension sigCallConv = builder.GetCurrentCallConv();
3021 // Validate that either no specific calling convention is provided or that the signature calling convention
3022 // matches the DllImport calling convention.
3023 // If no calling convention is provided, then use the default calling convention for the platform.
3025 if (callConv != CallConvWinApiSentinel && sigCallConv != CallConvWinApiSentinel && callConv != sigCallConv)
3026 ThrowError(IDS_EE_NDIRECT_BADNATL_CALLCONV);
3028 if (callConv == CallConvWinApiSentinel && sigCallConv == CallConvWinApiSentinel)
3029 m_callConv = GetDefaultCallConv(bIsVarArg);
3030 else if (callConv != CallConvWinApiSentinel)
3031 m_callConv = callConv;
3033 m_callConv = sigCallConv;
3035 if (bIsVarArg && m_callConv != CorInfoCallConvExtension::C)
3036 ThrowError(IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV);
3038 _ASSERTE(m_callConv != CallConvWinApiSentinel);
3041 void PInvokeStaticSigInfo::ThrowError(WORD errorResourceID)
3048 PRECONDITION(errorResourceID != 0);
3052 COMPlusThrow(kTypeLoadException, errorResourceID);
3057 bool HasSuppressGCTransitionAttribute(_In_ MethodDesc* pMD)
3059 LIMITED_METHOD_CONTRACT;
3061 HRESULT hr = pMD->GetCustomAttribute(
3062 WellKnownAttribute::SuppressGCTransition,
3068 bool TryGetCallingConventionFromPInvokeMetadata(_In_ MethodDesc* pMD, _Out_ CorInfoCallConvExtension* callConv)
3073 PRECONDITION(pMD != NULL);
3074 PRECONDITION(pMD->IsNDirect());
3075 PRECONDITION(callConv != NULL);
3079 CorInfoCallConvExtension callConvLocal;
3080 IMDInternalImport* pInternalImport = pMD->GetMDImport();
3081 CorPinvokeMap mappingFlags = pmMaxValue;
3082 HRESULT hr = pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, NULL /*pszImportName*/, NULL /*pmrImportDLL*/);
3086 callConvLocal = GetCallConvValueForPInvokeCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask));
3087 if (callConvLocal != CallConvWinApiSentinel)
3089 *callConv = callConvLocal;
3096 bool TryGetCallingConventionFromUnmanagedFunctionPointer(_In_ MethodTable* pMT, _Out_ CorInfoCallConvExtension* callConv)
3101 PRECONDITION(pMT != NULL);
3102 PRECONDITION(pMT->IsDelegate());
3103 PRECONDITION(callConv != NULL);
3109 HRESULT hr = pMT->GetCustomAttribute(WellKnownAttribute::UnmanagedFunctionPointer, (const VOID **)(&pData), (ULONG *)&cData);
3113 _ASSERTE(cData > 0);
3114 CustomAttributeParser ca(pData, cData);
3115 CorInfoCallConvExtension callConvLocal;
3116 hr = ParseCallingConventionFromAttributeConstructor(ca, &callConvLocal);
3117 if (SUCCEEDED(hr) && callConvLocal != CallConvWinApiSentinel)
3119 *callConv = callConvLocal;
3127 void NDirect::GetCallingConvention_IgnoreErrors(_In_ MethodDesc* pMD, _Out_opt_ CorInfoCallConvExtension* callConv, _Out_opt_ bool* suppressGCTransition)
3132 PRECONDITION(pMD != NULL);
3133 PRECONDITION(pMD->IsNDirect());
3134 PRECONDITION(callConv != NULL || suppressGCTransition != NULL);
3138 if (suppressGCTransition != NULL)
3140 *suppressGCTransition = HasSuppressGCTransitionAttribute(pMD);
3142 // Caller only cares about SuppressGCTransition and we have already determined it is true.
3143 if (callConv == NULL && *suppressGCTransition)
3147 // This method intentionally does not check that any calling convention specified through
3148 // attributes match that in the signature. We just return once a non-sentinel calling
3149 // convention is found.
3150 CorInfoCallConvExtension callConvLocal;
3151 MethodTable* pMT = pMD->GetMethodTable();
3152 if (pMT->IsDelegate())
3154 if (callConv == NULL)
3157 // System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
3158 if (TryGetCallingConventionFromUnmanagedFunctionPointer(pMT, &callConvLocal))
3160 *callConv = callConvLocal;
3166 // P/Invoke metadata
3167 if (TryGetCallingConventionFromPInvokeMetadata(pMD, &callConvLocal))
3169 if (callConv != NULL)
3170 *callConv = callConvLocal;
3175 #ifdef CROSSGEN_COMPILE
3176 _ASSERTE_MSG(!pMD->HasUnmanagedCallConvAttribute(), "UnmanagedCallConv methods are not supported in crossgen and should be rejected before getting here.");
3178 // System.Runtime.InteropServices.UnmanagedCallConvAttribute
3179 CallConvBuilder unmanagedCallConvBuilder;
3180 if (CallConv::TryGetCallingConventionFromUnmanagedCallConv(pMD, &unmanagedCallConvBuilder, NULL /*errorResID*/) == S_OK)
3182 if (suppressGCTransition != NULL)
3184 (*suppressGCTransition) |= unmanagedCallConvBuilder.IsCurrentCallConvModSet(CallConvBuilder::CALL_CONV_MOD_SUPPRESSGCTRANSITION);
3187 callConvLocal = unmanagedCallConvBuilder.GetCurrentCallConv();
3188 if (callConvLocal != CallConvWinApiSentinel)
3190 if (callConv != NULL)
3191 *callConv = callConvLocal;
3196 #endif // CROSSGEN_COMPILE
3198 // Caller only cares about SuppressGCTransition - we have checked SuppressGCTransition and UnmanagedCallConv
3199 if (callConv == NULL)
3203 _ASSERTE(callConv != NULL);
3205 const Signature& sig = pMD->GetSignature();
3206 Module* module = pMD->GetModule();
3208 CallConvBuilder builder;
3212 (void)CallConv::TryGetUnmanagedCallingConventionFromModOpt(GetScopeHandle(module), sig.GetRawSig(), sig.GetRawSigLen(), &builder, &errorResID);
3213 callConvLocal = builder.GetCurrentCallConv();
3214 if (callConvLocal != CallConvWinApiSentinel)
3216 *callConv = callConvLocal;
3220 *callConv = GetDefaultCallConv(pMD->IsVarArg());
3223 //---------------------------------------------------------
3224 // Does a class or method have a NAT_L CustomAttribute?
3228 // FAILED = unknown because something failed.
3229 //---------------------------------------------------------
3231 HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs)
3239 PRECONDITION(CheckPointer(pInternalImport));
3240 PRECONDITION(TypeFromToken(token) == mdtMethodDef);
3244 // Check method flags first before trying to find the custom value
3245 if (!IsReallyMdPinvokeImpl(dwMemberAttrs))
3249 LPCSTR pszImportName;
3252 if (SUCCEEDED(pInternalImport->GetPinvokeMap(token, &mappingFlags, &pszImportName, &modref)))
3259 // Either MD or signature & module must be given.
3261 BOOL NDirect::MarshalingRequired(
3262 _In_opt_ MethodDesc* pMD,
3263 _In_opt_ PCCOR_SIGNATURE pSig,
3264 _In_opt_ Module* pModule,
3265 _In_ bool unmanagedCallersOnlyRequiresMarshalling)
3270 PRECONDITION(pMD != NULL || (pSig != NULL && pModule != NULL));
3274 // As a by-product, when returning FALSE we will also set the native stack size to the MD if it's
3275 // an NDirectMethodDesc. This number is needed to link the P/Invoke (it determines the @n entry
3276 // point name suffix and affects alignment thunk generation on the Mac). If this method returns
3277 // TRUE, the stack size will be set when building the marshaling IL stub.
3278 DWORD dwStackSize = 0;
3279 CorInfoCallConvExtension callConv = CallConv::GetDefaultUnmanagedCallingConvention();
3283 // HRESULT swapping is handled by stub
3284 if (pMD->IsNDirect() || pMD->IsComPlusCall())
3286 if (!IsMiPreserveSig(pMD->GetImplAttrs()))
3290 PInvokeStaticSigInfo sigInfo;
3291 if (!pMD->IsNDirect())
3293 new (&sigInfo) PInvokeStaticSigInfo(pMD);
3297 // A P/Invoke marked with UnmanagedCallersOnlyAttribute
3298 // doesn't technically require marshalling. However, we
3299 // don't support a DllImport with this attribute and we
3300 // error out during IL Stub generation so we indicate that
3301 // when checking if an IL Stub is needed.
3303 // Callers can indicate the check doesn't need to be performed.
3304 if (unmanagedCallersOnlyRequiresMarshalling && pMD->HasUnmanagedCallersOnlyAttribute())
3307 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
3308 // Make sure running cctor can be handled by stub
3309 if (pNMD->IsClassConstructorTriggeredByILStub())
3312 InitializeSigInfoAndPopulateNDirectMethodDesc(pNMD, &sigInfo);
3314 #ifndef CROSSGEN_COMPILE
3315 // Pending exceptions are handled by stub
3316 if (Interop::ShouldCheckForPendingException(pNMD))
3318 #endif // !CROSSGEN_COMPILE
3321 // SetLastError is handled by stub
3322 if (sigInfo.GetLinkFlags() & nlfLastError)
3325 // LCID argument is handled by stub
3326 if (GetLCIDParameterIndex(pMD) != -1)
3329 callConv = sigInfo.GetCallConv();
3334 PREFIX_ASSUME(pMD != NULL);
3336 pSig = pMD->GetSig();
3337 pModule = pMD->GetModule();
3340 // Check to make certain that the signature only contains types that marshal trivially
3341 SigPointer ptr(pSig);
3342 IfFailThrow(ptr.GetCallingConvInfo(NULL));
3344 IfFailThrow(ptr.GetData(&numArgs));
3345 numArgs++; // +1 for return type
3347 // We'll need to parse parameter native types
3348 mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef));
3349 IMDInternalImport *pMDImport = pModule->GetMDImport();
3351 SigTypeContext emptyTypeContext;
3353 mdMethodDef methodToken = mdMethodDefNil;
3356 methodToken = pMD->GetMemberDef();
3358 CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
3360 for (ULONG i = 0; i < numArgs; i++)
3362 SigPointer arg = ptr;
3363 CorElementType type;
3364 IfFailThrow(arg.PeekElemType(&type));
3368 case ELEMENT_TYPE_PTR:
3370 IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR
3371 IfFailThrow(arg.PeekElemType(&type));
3373 if (type == ELEMENT_TYPE_VALUETYPE)
3375 if ((arg.HasCustomModifier(pModule,
3376 "Microsoft.VisualC.NeedsCopyConstructorModifier",
3377 ELEMENT_TYPE_CMOD_REQD)) ||
3378 (arg.HasCustomModifier(pModule,
3379 "System.Runtime.CompilerServices.IsCopyConstructed",
3380 ELEMENT_TYPE_CMOD_REQD)))
3385 if (i > 0) dwStackSize += TARGET_POINTER_SIZE;
3389 case ELEMENT_TYPE_INTERNAL:
3391 // this check is not functional in DAC and provides no security against a malicious dump
3392 // the DAC is prepared to receive an invalid type handle
3393 #ifndef DACCESS_COMPILE
3394 if (pModule->IsSigInIL(arg.GetPtr()))
3395 THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule);
3400 case ELEMENT_TYPE_VALUETYPE:
3402 TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext);
3404 // JIT can handle internal blittable value types
3405 if (!hndArgType.IsBlittable() && !hndArgType.IsEnum())
3410 #ifdef FEATURE_READYTORUN_COMPILER
3411 if (IsReadyToRunCompilation())
3413 if (!hndArgType.AsMethodTable()->IsLayoutInCurrentVersionBubble())
3419 const bool isValueType = true;
3420 dwStackSize += StackElemSize(hndArgType.GetSize(), isValueType, hndArgType.IsFloatHfa());
3425 case ELEMENT_TYPE_BOOLEAN:
3426 case ELEMENT_TYPE_CHAR:
3428 // Bool requires marshaling
3429 // Char may require marshaling (MARSHAL_TYPE_ANSICHAR)
3435 if (CorTypeInfo::IsPrimitiveType(type) || type == ELEMENT_TYPE_FNPTR)
3440 const bool isValueType = false;
3441 const bool isFloatHfa = false;
3442 dwStackSize += StackElemSize(CorTypeInfo::Size(type), isValueType, isFloatHfa);
3447 // other non-primitive type - requires marshaling
3453 // check for explicit MarshalAs
3454 NativeTypeParamInfo paramInfo;
3456 if (pParamTokenArray[i] != mdParamDefNil)
3458 if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, ¶mInfo) ||
3459 paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT)
3461 // Presence of MarshalAs does not necessitate marshaling (it could as well be the default
3462 // for the type), but it's a good enough heuristic. We definitely don't want to duplicate
3463 // the logic from code:MarshalInfo.MarshalInfo here.
3468 IfFailThrow(ptr.SkipExactlyOne());
3471 if (!FitsInU2(dwStackSize))
3474 // do not set the stack size for varargs - the number is call site specific
3475 if (pMD != NULL && !pMD->IsVarArg())
3477 if (pMD->IsNDirect())
3479 ((NDirectMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize), callConv);
3481 #ifdef FEATURE_COMINTEROP
3482 else if (pMD->IsComPlusCall())
3484 // calling convention is always stdcall
3485 ((ComPlusCallMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize));
3487 #endif // FEATURE_COMINTEROP
3494 // factorization of CreateNDirectStubWorker
3495 static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig,
3497 CorNativeLinkType nlType,
3498 CorNativeLinkFlags nlFlags,
3499 UINT argidx, // this is used for reverse pinvoke hresult swapping
3504 UINT& nativeStackOffset,
3505 bool& fStubNeedsCOM,
3507 DEBUG_ARG(LPCUTF8 pDebugName)
3508 DEBUG_ARG(LPCUTF8 pDebugClassName)
3515 PRECONDITION(CheckPointer(params));
3516 PRECONDITION(CheckPointer(pss));
3517 PRECONDITION(CheckPointer(pMD, NULL_OK));
3521 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3523 MarshalInfo::MarshalScenario ms;
3524 #ifdef FEATURE_COMINTEROP
3525 if (SF_IsCOMStub(dwStubFlags))
3527 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3530 #endif // FEATURE_COMINTEROP
3532 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3535 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
3537 MarshalInfo returnInfo(msig.GetModule(),
3538 msig.GetReturnProps(),
3539 msig.GetSigTypeContext(),
3546 msig.NumFixedArgs(),
3547 SF_IsBestFit(dwStubFlags),
3548 SF_IsThrowOnUnmappableChar(dwStubFlags),
3552 DEBUG_ARG(pDebugName)
3553 DEBUG_ARG(pDebugClassName)
3557 marshalType = returnInfo.GetMarshalType();
3559 fStubNeedsCOM |= returnInfo.MarshalerRequiresCOM();
3561 #ifdef FEATURE_COMINTEROP
3562 if (SF_IsCOMStub(dwStubFlags))
3564 // We don't support native methods that return VARIANTs, non-blittable structs, GUIDs, or DECIMALs directly.
3565 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT ||
3566 marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS ||
3567 marshalType == MarshalInfo::MARSHAL_TYPE_GUID ||
3568 marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL)
3570 if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
3572 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3576 pss->MarshalReturn(&returnInfo, argOffset);
3579 #endif // FEATURE_COMINTEROP
3581 if (marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS
3582 || marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS
3583 || marshalType == MarshalInfo::MARSHAL_TYPE_GUID
3584 || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL
3587 if (SF_IsHRESULTSwapping(dwStubFlags))
3589 // V1 restriction: we could implement this but it's late in the game to do so.
3590 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3593 else if (marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY
3594 || marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET
3595 || marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
3596 #ifdef FEATURE_COMINTEROP
3597 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
3598 #endif // FEATURE_COMINTEROP
3601 // Each of these types are non-blittable and according to its managed size should be returned in a return buffer on x86 in stdcall.
3602 // However, its native size is small enough to be returned by-value.
3603 // We don't know the native type representation early enough to get this correct, so we throw an exception here.
3604 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3606 else if (IsUnsupportedTypedrefReturn(msig))
3608 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3611 #ifdef FEATURE_COMINTEROP
3612 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT && !SF_IsHRESULTSwapping(dwStubFlags))
3614 // No support for returning variants. This is a V1 restriction, due to the late date,
3615 // don't want to add the special-case code to support this in light of low demand.
3616 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NOVARIANTRETURN);
3618 #endif // FEATURE_COMINTEROP
3620 pss->MarshalReturn(&returnInfo, argOffset);
3627 static inline UINT GetStackOffsetFromStackSize(UINT stackSize, bool fThisCall)
3629 LIMITED_METHOD_CONTRACT;
3633 // -1 means that the argument is not on the stack
3634 return (stackSize >= TARGET_POINTER_SIZE ? (stackSize - TARGET_POINTER_SIZE) : (UINT)-1);
3636 #endif // TARGET_X86
3640 //---------------------------------------------------------
3641 // Creates a new stub for a N/Direct call. Return refcount is 1.
3642 // Note that this function may now throw if it fails to create
3644 //---------------------------------------------------------
3645 static void CreateNDirectStubWorker(StubState* pss,
3646 StubSigDesc* pSigDesc,
3647 CorNativeLinkType nlType,
3648 CorNativeLinkFlags nlFlags,
3649 CorInfoCallConvExtension unmgdCallConv,
3652 mdParamDef* pParamTokenArray,
3660 PRECONDITION(CheckPointer(pss));
3661 PRECONDITION(CheckPointer(pSigDesc));
3662 PRECONDITION(CheckPointer(pMD, NULL_OK));
3663 PRECONDITION(!pMD || pMD->IsILStub() || (0 != pMD->GetMethodTable()->IsDelegate()) == SF_IsDelegateStub(dwStubFlags));
3667 SF_ConsistencyCheck(dwStubFlags);
3670 if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
3671 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
3674 if (SF_IsCOMStub(dwStubFlags))
3676 _ASSERTE(0 == nlType);
3677 _ASSERTE(0 == nlFlags);
3678 _ASSERTE(CallConv::GetDefaultUnmanagedCallingConvention() == unmgdCallConv);
3682 _ASSERTE(nlType == nltAnsi || nlType == nltUnicode);
3684 Module *pModule = pSigDesc->m_pModule;
3687 // Set up signature walking objects.
3690 MetaSig msig(pSigDesc->m_sig,
3692 &pSigDesc->m_typeContext);
3694 if (SF_IsVarArgStub(dwStubFlags))
3695 msig.SetTreatAsVarArg();
3697 bool fThisCall = (unmgdCallConv == CorInfoCallConvExtension::Thiscall);
3699 pss->SetLastError(nlFlags & nlfLastError);
3701 // This has been in the product since forward P/Invoke via delegates was
3702 // introduced. It's wrong, but please keep it for backward compatibility.
3703 if (SF_IsDelegateStub(dwStubFlags))
3704 pss->SetLastError(TRUE);
3706 pss->BeginEmit(dwStubFlags);
3710 // The code to handle the LCID will call MarshalLCID before calling MarshalArgument
3711 // on the argument the LCID should go after. So we just bump up the index here.
3715 int numArgs = msig.NumFixedArgs();
3717 // thiscall must have at least one parameter (the "this")
3718 if (fThisCall && numArgs == 0)
3719 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
3722 // Now, emit the IL.
3727 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3730 // Marshal the return value.
3732 UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? TARGET_POINTER_SIZE : 0);
3733 bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags);
3736 // Marshal the arguments
3738 MarshalInfo::MarshalScenario ms;
3739 #ifdef FEATURE_COMINTEROP
3740 if (SF_IsCOMStub(dwStubFlags))
3742 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3745 #endif // FEATURE_COMINTEROP
3747 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3750 // Build up marshaling information for each of the method's parameters
3751 SIZE_T cbParamMarshalInfo;
3752 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numArgs, cbParamMarshalInfo))
3754 COMPlusThrowHR(COR_E_OVERFLOW);
3757 NewArrayHolder<BYTE> pbParamMarshalInfo(new BYTE[cbParamMarshalInfo]);
3758 MarshalInfo *pParamMarshalInfo = reinterpret_cast<MarshalInfo *>(pbParamMarshalInfo.GetValue());
3760 MetaSig paramInfoMSig(msig);
3761 for (int i = 0; i < numArgs; ++i)
3763 paramInfoMSig.NextArg();
3764 new(&(pParamMarshalInfo[i])) MarshalInfo(paramInfoMSig.GetModule(),
3765 paramInfoMSig.GetArgProps(),
3766 paramInfoMSig.GetSigTypeContext(),
3767 pParamTokenArray[i + 1],
3774 SF_IsBestFit(dwStubFlags),
3775 SF_IsThrowOnUnmappableChar(dwStubFlags),
3779 DEBUG_ARG(pSigDesc->m_pDebugName)
3780 DEBUG_ARG(pSigDesc->m_pDebugClassName)
3784 // Marshal the parameters
3786 int nativeArgIndex = 0;
3788 while (argidx <= numArgs)
3791 // Check to see if this is the parameter after which we need to insert the LCID.
3793 if (argidx == iLCIDArg)
3795 pss->MarshalLCID(argidx);
3796 nativeStackSize += TARGET_POINTER_SIZE;
3798 if (SF_IsReverseStub(dwStubFlags))
3804 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
3806 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
3807 nativeStackSize += info.GetNativeArgSize();
3809 fStubNeedsCOM |= info.MarshalerRequiresCOM();
3811 if (fThisCall && argidx == 1)
3813 // make sure that the first parameter is enregisterable
3814 if (info.GetNativeArgSize() > TARGET_POINTER_SIZE)
3815 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
3823 // Check to see if this is the parameter after which we need to insert the LCID.
3824 if (argidx == iLCIDArg)
3826 pss->MarshalLCID(argidx);
3827 nativeStackSize += TARGET_POINTER_SIZE;
3829 if (SF_IsReverseStub(dwStubFlags))
3833 marshalType = DoMarshalReturnValue(msig,
3845 DEBUG_ARG(pSigDesc->m_pDebugName)
3846 DEBUG_ARG(pSigDesc->m_pDebugClassName)
3849 // If the return value is a SafeHandle or CriticalHandle, mark the stub method.
3850 // Interop methods that use this stub will have an implicit reliability contract
3851 // (see code:TAStackCrawlCallBack).
3852 if (!SF_IsHRESULTSwapping(dwStubFlags))
3854 if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE ||
3855 marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE)
3857 if (pMD->IsDynamicMethod())
3858 pMD->AsDynamicMethodDesc()->SetUnbreakable(true);
3862 if (SF_IsHRESULTSwapping(dwStubFlags))
3864 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
3865 nativeStackSize += TARGET_POINTER_SIZE;
3868 if (pMD->IsDynamicMethod())
3870 // Set the native stack size to the IL stub MD. It is needed for alignment
3871 // thunk generation on the Mac and stdcall name decoration on Windows.
3872 // We do not store it directly in the interop MethodDesc here because due
3873 // to sharing we come here only for the first call with given signature and
3874 // the target MD may even be NULL.
3879 _ASSERTE(nativeStackSize >= TARGET_POINTER_SIZE);
3880 nativeStackSize -= TARGET_POINTER_SIZE;
3882 #endif // TARGET_X86
3884 nativeStackSize = ALIGN_UP(nativeStackSize, TARGET_POINTER_SIZE);
3886 if (!FitsInU2(nativeStackSize))
3887 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
3889 DynamicMethodDesc *pDMD = pMD->AsDynamicMethodDesc();
3891 pDMD->SetNativeStackArgSize(static_cast<WORD>(nativeStackSize));
3892 pDMD->SetStubNeedsCOMStarted(fStubNeedsCOM);
3895 // FinishEmit needs to know the native stack arg size so we call it after the number
3896 // has been set in the stub MD (code:DynamicMethodDesc.SetNativeStackArgSize)
3897 pss->FinishEmit(pMD);
3900 static void CreateStructStub(ILStubState* pss,
3901 StubSigDesc* pSigDesc,
3909 PRECONDITION(CheckPointer(pss));
3910 PRECONDITION(CheckPointer(pSigDesc));
3911 PRECONDITION(CheckPointer(pMD, NULL_OK));
3912 PRECONDITION(!pMD || pMD->IsILStub());
3913 PRECONDITION(SF_IsStructMarshalStub(dwStubFlags));
3917 SF_ConsistencyCheck(dwStubFlags);
3920 if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
3921 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
3924 Module* pModule = pSigDesc->m_pModule;
3927 pss->SetLastError(false);
3929 pss->BeginEmit(dwStubFlags);
3931 // Marshal the fields
3932 MarshalInfo::MarshalScenario ms = MarshalInfo::MARSHAL_SCENARIO_FIELD;
3934 EEClassNativeLayoutInfo const* pNativeLayoutInfo = pMT->GetNativeLayoutInfo();
3936 int numFields = pNativeLayoutInfo->GetNumFields();
3937 // Build up marshaling information for each of the method's parameters
3938 SIZE_T cbFieldMarshalInfo;
3939 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numFields, cbFieldMarshalInfo))
3941 COMPlusThrowHR(COR_E_OVERFLOW);
3944 CorNativeLinkType nlType = pMT->GetCharSet();
3946 NativeFieldDescriptor const* pFieldDescriptors = pNativeLayoutInfo->GetNativeFieldDescriptors();
3948 for (int i = 0; i < numFields; ++i)
3950 NativeFieldDescriptor const& nativeFieldDescriptor = pFieldDescriptors[i];
3951 PTR_FieldDesc pFD = nativeFieldDescriptor.GetFieldDesc();
3952 SigPointer fieldSig = pFD->GetSigPointer();
3953 // The first byte in a field signature is always 0x6 per ECMA 335. Skip over this byte to get to the rest of the signature for the MarshalInfo constructor.
3954 (void)fieldSig.GetByte(nullptr);
3955 SigTypeContext context(pFD, TypeHandle(pMT));
3957 MarshalInfo mlInfo(pFD->GetModule(),
3960 pFD->GetMemberDef(),
3967 SF_IsBestFit(dwStubFlags),
3968 SF_IsThrowOnUnmappableChar(dwStubFlags),
3972 DEBUG_ARG(pSigDesc->m_pDebugName)
3973 DEBUG_ARG(pSigDesc->m_pDebugClassName)
3974 DEBUG_ARG(-1 /* field */));
3976 pss->MarshalField(&mlInfo, pFD->GetOffset(), nativeFieldDescriptor.GetExternalOffset(), pFD);
3979 if (pMD->IsDynamicMethod())
3981 DynamicMethodDesc* pDMD = pMD->AsDynamicMethodDesc();
3982 pDMD->SetNativeStackArgSize(4 * TARGET_POINTER_SIZE); // The native stack arg size is constant since the signature for struct stubs is constant.
3985 // FinishEmit needs to know the native stack arg size so we call it after the number
3986 // has been set in the stub MD (code:DynamicMethodDesc.SetNativeStackArgSize)
3987 pss->FinishEmit(pMD);
3992 class NDirectStubParameters
3996 NDirectStubParameters(Signature sig,
3997 SigTypeContext* pTypeContext,
3999 Module* pLoaderModule,
4000 CorNativeLinkType nlType,
4001 CorNativeLinkFlags nlFlags,
4002 CorInfoCallConvExtension unmgdCallConv,
4003 DWORD dwStubFlags, // NDirectStubFlags
4005 mdParamDef* pParamTokenArray,
4010 m_pTypeContext(pTypeContext),
4012 m_pLoaderModule(pLoaderModule),
4013 m_pParamTokenArray(pParamTokenArray),
4014 m_unmgdCallConv(unmgdCallConv),
4017 m_dwStubFlags(dwStubFlags),
4018 m_iLCIDArg(iLCIDArg),
4019 m_nParamTokens(nParamTokens),
4022 LIMITED_METHOD_CONTRACT;
4026 SigTypeContext* m_pTypeContext;
4028 Module* m_pLoaderModule;
4029 mdParamDef* m_pParamTokenArray;
4030 CorInfoCallConvExtension m_unmgdCallConv;
4031 CorNativeLinkType m_nlType;
4032 CorNativeLinkFlags m_nlFlags;
4033 DWORD m_dwStubFlags;
4039 class NDirectStubHashBlob : public ILStubHashBlobBase
4045 WORD m_unmgdCallConv;
4046 BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob
4053 BYTE m_rgbSigAndParamData[1];
4054 // (dwParamAttr, cbNativeType) // length: number of parameters
4055 // NativeTypeBlob // length: number of parameters
4056 // BYTE m_rgbSigData[]; // length: determined by sig walk
4059 // For better performance and less memory fragmentation,
4060 // I'm using structure here to avoid allocating 3 different arrays.
4065 PCCOR_SIGNATURE pvNativeType;
4068 ILStubHashBlob* CreateHashBlob(NDirectStubParameters* pParams)
4070 STANDARD_VM_CONTRACT;
4072 NDirectStubHashBlob* pBlob;
4074 IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport();
4076 CQuickBytes paramInfoBytes;
4077 paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens);
4078 ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr();
4079 ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens);
4081 size_t cbNativeTypeTotal = 0;
4084 // Collect information for function parameters
4086 for (int idx = 0; idx < pParams->m_nParamTokens; idx++)
4088 mdParamDef token = pParams->m_pParamTokenArray[idx];
4089 if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token)
4091 USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted
4092 LPCSTR szParamName_Ignore;
4093 IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, ¶mInfos[idx].dwParamAttr, &szParamName_Ignore));
4095 if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal)
4097 IfFailThrow(pInternalImport->GetFieldMarshal(token, ¶mInfos[idx].pvNativeType, ¶mInfos[idx].cbNativeType));
4098 cbNativeTypeTotal += paramInfos[idx].cbNativeType;
4103 SigPointer sigPtr = pParams->m_sig.CreateSigPointer();
4105 // note that ConvertToInternalSignature also resolves generics so different instantiations will get different
4106 // hash blobs for methods that have generic parameters in their signature
4107 SigBuilder sigBuilder;
4108 sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE);
4111 PVOID pSig = sigBuilder.GetSignature(&cbSig);
4114 // Build hash blob for IL stub sharing
4116 S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) +
4117 S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes
4118 S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size
4119 S_SIZE_T(cbNativeTypeTotal) + // Native type blob data
4120 S_SIZE_T(cbSig); // Signature
4122 if (cbSizeOfBlob.IsOverflow())
4123 COMPlusThrowHR(COR_E_OVERFLOW);
4125 static_assert_no_msg(nltMaxValue <= 0xFF);
4126 static_assert_no_msg(nlfMaxValue <= 0xFF);
4127 static_assert_no_msg(pmMaxValue <= 0xFFFF);
4129 NewArrayHolder<BYTE> pBytes = new BYTE[cbSizeOfBlob.Value()];
4130 // zero out the hash bytes to ensure all bit fields are deterministically set
4131 ZeroMemory(pBytes, cbSizeOfBlob.Value());
4132 pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes;
4134 pBlob->m_pModule = NULL;
4136 if (SF_IsNGENedStub(pParams->m_dwStubFlags))
4138 // don't share across modules if we are ngening the stub
4139 pBlob->m_pModule = pParams->m_pModule;
4142 pBlob->m_pMT = pParams->m_pMT;
4143 pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value();
4144 pBlob->m_unmgdCallConv = static_cast<WORD>(pParams->m_unmgdCallConv);
4145 pBlob->m_nlType = static_cast<BYTE>(pParams->m_nlType);
4146 pBlob->m_nlFlags = static_cast<BYTE>(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub
4147 pBlob->m_iLCIDArg = pParams->m_iLCIDArg;
4149 pBlob->m_StubFlags = pParams->m_dwStubFlags;
4150 pBlob->m_nParams = pParams->m_nParamTokens;
4152 BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0];
4155 // Write (dwParamAttr, cbNativeType) for parameters
4157 // Note that these need to be aligned and it is why they are written before the byte blobs
4158 // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs
4160 _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0);
4161 _ASSERTE(sizeof(DWORD) == sizeof(ULONG));
4163 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4165 // We only care about In/Out/HasFieldMarshal
4166 // Other attr are about optional/default values which are not used in marshalling,
4167 // but only used in compilers
4168 *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal);
4169 pBlobParams += sizeof(DWORD);
4171 *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType;
4172 pBlobParams += sizeof(ULONG);
4176 // Write native type blob for parameters
4178 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4180 memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType);
4181 pBlobParams += paramInfos[i].cbNativeType;
4187 memcpy(pBlobParams, pSig, cbSig);
4189 // Verify that we indeed have reached the end
4190 _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value());
4192 pBytes.SuppressRelease();
4193 return (ILStubHashBlob*)pBlob;
4196 ILStubCache* GetILStubCache(NDirectStubParameters* pParams)
4206 // Use the m_pLoaderModule instead of m_pModule
4207 // They could be different for methods on generic types.
4208 return pParams->m_pLoaderModule->GetILStubCache();
4211 MethodDesc* GetStubMethodDesc(
4212 MethodDesc *pTargetMD,
4213 NDirectStubParameters* pParams,
4214 ILStubHashBlob* pHashParams,
4215 AllocMemTracker* pamTracker,
4216 bool& bILStubCreator,
4217 MethodDesc* pLastMD)
4219 CONTRACT(MethodDesc*)
4223 PRECONDITION(CheckPointer(pParams));
4224 PRECONDITION(!pParams->m_sig.IsEmpty());
4225 PRECONDITION(CheckPointer(pParams->m_pModule));
4226 PRECONDITION(CheckPointer(pTargetMD, NULL_OK));
4227 POSTCONDITION(CheckPointer(RETVAL));
4233 ILStubCache* pCache = GetILStubCache(pParams);
4235 pMD = pCache->GetStubMethodDesc(pTargetMD,
4237 pParams->m_dwStubFlags,
4239 pParams->m_sig.GetRawSig(),
4240 pParams->m_sig.GetRawSigLen(),
4248 void RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams)
4254 PRECONDITION(CheckPointer(pParams));
4255 PRECONDITION(CheckPointer(pHashParams));
4256 PRECONDITION(!pParams->m_sig.IsEmpty());
4257 PRECONDITION(CheckPointer(pParams->m_pModule));
4261 LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams));
4263 ILStubCache* pCache = GetILStubCache(pParams);
4265 pCache->DeleteEntry(pHashParams);
4268 void AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD)
4274 PRECONDITION(CheckPointer(pParams));
4275 PRECONDITION(!pParams->m_sig.IsEmpty());
4276 PRECONDITION(CheckPointer(pParams->m_pModule));
4280 ILStubCache* pCache = GetILStubCache(pParams);
4282 pCache->AddMethodDescChunkWithLockTaken(pMD);
4287 // Additional factorization of CreateNDirectStub. This hoists all the metadata accesses
4288 // into one location so that we can leave CreateNDirectStubWorker to just generate the
4289 // IL. This allows us to cache a stub based on the inputs to CreateNDirectStubWorker
4290 // instead of having to generate the IL first before doing the caching.
4292 static void CreateNDirectStubAccessMetadata(
4293 StubSigDesc* pSigDesc, // IN
4294 CorInfoCallConvExtension unmgdCallConv, // IN
4295 DWORD* pdwStubFlags, // IN/OUT
4296 int* piLCIDArg, // OUT
4297 int* pNumArgs // OUT
4300 STANDARD_VM_CONTRACT;
4302 if (SF_IsCOMStub(*pdwStubFlags))
4304 _ASSERTE(CallConv::GetDefaultUnmanagedCallingConvention() == unmgdCallConv);
4308 if (unmgdCallConv == CorInfoCallConvExtension::Managed ||
4309 unmgdCallConv == CorInfoCallConvExtension::Fastcall ||
4310 unmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction)
4312 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
4316 MetaSig msig(pSigDesc->m_sig,
4317 pSigDesc->m_pModule,
4318 &pSigDesc->m_typeContext);
4320 if (SF_IsVarArgStub(*pdwStubFlags))
4321 msig.SetTreatAsVarArg();
4323 (*pNumArgs) = msig.NumFixedArgs();
4325 IMDInternalImport* pInternalImport = pSigDesc->m_pModule->GetMDImport();
4327 _ASSERTE(!SF_IsHRESULTSwapping(*pdwStubFlags));
4329 mdMethodDef md = pSigDesc->m_tkMethodDef;
4330 if (md != mdMethodDefNil)
4332 DWORD dwDescrOffset;
4334 IfFailThrow(pInternalImport->GetMethodImplProps(
4339 if (SF_IsReverseStub(*pdwStubFlags))
4341 // only COM-to-CLR call supports hresult swapping in the reverse direction
4342 if (SF_IsCOMStub(*pdwStubFlags) && !IsMiPreserveSig(dwImplFlags))
4344 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4349 // fwd pinvoke, fwd com interop support hresult swapping.
4350 // delegate to an unmanaged method does not.
4351 if (!IsMiPreserveSig(dwImplFlags) && !SF_IsDelegateStub(*pdwStubFlags))
4353 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4360 // Check if we have a MethodDesc to query for additional data.
4361 if (pSigDesc->m_pMD != NULL)
4363 MethodDesc* pMD = pSigDesc->m_pMD;
4365 // P/Invoke marked with UnmanagedCallersOnlyAttribute is not
4366 // presently supported.
4367 if (pMD->HasUnmanagedCallersOnlyAttribute())
4368 COMPlusThrow(kNotSupportedException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
4370 // Check to see if we need to do LCID conversion.
4371 lcidArg = GetLCIDParameterIndex(pMD);
4372 if (lcidArg != -1 && lcidArg > (*pNumArgs))
4373 COMPlusThrow(kIndexOutOfRangeException, IDS_EE_INVALIDLCIDPARAM);
4376 (*piLCIDArg) = lcidArg;
4378 if (SF_IsCOMStub(*pdwStubFlags))
4380 CONSISTENCY_CHECK(msig.HasThis());
4384 if (msig.HasThis() && !SF_IsDelegateStub(*pdwStubFlags))
4386 COMPlusThrow(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC);
4393 void PopulateNDirectMethodDescImpl(
4394 _Inout_ NDirectMethodDesc* pNMD,
4395 _In_ const PInvokeStaticSigInfo& sigInfo,
4396 _In_opt_z_ LPCUTF8 libName,
4397 _In_opt_z_ LPCUTF8 entryPointName)
4402 PRECONDITION(pNMD != NULL);
4406 WORD ndirectflags = 0;
4407 if (pNMD->MethodDesc::IsVarArg())
4408 ndirectflags |= NDirectMethodDesc::kVarArgs;
4410 if (sigInfo.GetCharSet() == nltAnsi)
4411 ndirectflags |= NDirectMethodDesc::kNativeAnsi;
4413 CorNativeLinkFlags linkflags = sigInfo.GetLinkFlags();
4414 if (linkflags & nlfLastError)
4415 ndirectflags |= NDirectMethodDesc::kLastError;
4416 if (linkflags & nlfNoMangle)
4417 ndirectflags |= NDirectMethodDesc::kNativeNoMangle;
4419 CorInfoCallConvExtension callConv = sigInfo.GetCallConv();
4420 if (callConv == CorInfoCallConvExtension::Stdcall)
4421 ndirectflags |= NDirectMethodDesc::kStdCall;
4422 if (callConv == CorInfoCallConvExtension::Thiscall)
4423 ndirectflags |= NDirectMethodDesc::kThisCall;
4425 if (pNMD->GetLoaderModule()->IsSystem() && (strcmp(libName, "QCall") == 0))
4427 ndirectflags |= NDirectMethodDesc::kIsQCall;
4431 pNMD->ndirect.m_pszLibName.SetValueMaybeNull(libName);
4432 pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(entryPointName);
4435 // Do not publish incomplete prestub flags or you will introduce a race condition.
4436 pNMD->InterlockedSetNDirectFlags(ndirectflags | NDirectMethodDesc::kNDirectPopulated);
4440 void NDirect::PopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD)
4445 PRECONDITION(CheckPointer(pNMD));
4449 if (pNMD->IsSynchronized())
4450 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
4452 if (pNMD->IsPopulated())
4455 LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
4456 PInvokeStaticSigInfo sigInfo(pNMD, &szLibName, &szEntryPointName);
4457 PopulateNDirectMethodDescImpl(pNMD, sigInfo, szLibName, szEntryPointName);
4460 void NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD, _Inout_ PInvokeStaticSigInfo* pSigInfo)
4465 PRECONDITION(CheckPointer(pNMD));
4466 PRECONDITION(CheckPointer(pSigInfo));
4470 if (pNMD->IsSynchronized())
4471 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
4473 LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
4474 new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName);
4476 if (pNMD->IsPopulated())
4479 PopulateNDirectMethodDescImpl(pNMD, *pSigInfo, szLibName, szEntryPointName);
4482 #ifdef FEATURE_COMINTEROP
4483 // Find the MethodDesc of the predefined IL stub method by either
4484 // 1) looking at redirected adapter interfaces, OR
4485 // 2) looking at special attributes for the specific interop scenario (specified by dwStubFlags).
4486 // Currently only ManagedToNativeComInteropStubAttribute is supported.
4487 // It returns NULL if no such attribute(s) can be found.
4488 // But if the attribute is found and is invalid, or something went wrong in the looking up
4489 // process, an exception will be thrown. If everything goes well, you'll get the MethodDesc
4490 // of the stub method
4491 HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, MethodDesc **ppRetStubMD)
4498 PRECONDITION(CheckPointer(pTargetMD));
4499 PRECONDITION(CheckPointer(ppRetStubMD));
4500 PRECONDITION(*ppRetStubMD == NULL);
4506 MethodTable *pTargetMT = pTargetMD->GetMethodTable();
4509 // Find out if we have the attribute
4514 // Support v-table forward classic COM interop calls only
4515 if (SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags))
4517 if (pTargetMT->HasInstantiation())
4519 // ManagedToNativeComInteropStubAttribute is not supported with generics
4523 if (pTargetMD->IsFCall())
4525 // ManagedToNativeComInteropStubAttribute is not supported on FCalls (i.e. methods on legacy
4526 // interfaces forwarded to CustomMarshalers.dll such as IEnumerable::GetEnumerator)
4529 _ASSERTE(pTargetMD->IsComPlusCall());
4531 if (pTargetMD->IsInterface())
4533 hr = pTargetMD->GetCustomAttribute(
4534 WellKnownAttribute::ManagedToNativeComInteropStub,
4540 // GetCustomAttribute returns S_FALSE when it cannot find the attribute but nothing fails...
4541 // Translate that to E_FAIL
4542 else if (hr == S_FALSE)
4547 // We are dealing with the class, use the interface MD instead
4548 // After second thought I believe we don't need to check the class MD.
4549 // We can think stubs as part of public interface, and if the interface is public,
4550 // the stubs should also be accessible
4551 MethodDesc *pInterfaceMD = pTargetMD->GetInterfaceMD();
4554 hr = FindPredefinedILStubMethod(pInterfaceMD, dwStubFlags, ppRetStubMD);
4565 // Parse the attribute
4567 CustomAttributeParser parser(pBytes, cbBytes);
4568 IfFailRet(parser.SkipProlog());
4572 IfFailRet(parser.GetNonEmptyString(&pTypeName, &cbTypeName));
4574 LPCUTF8 pMethodName;
4576 IfFailRet(parser.GetNonEmptyString(&pMethodName, &cbMethodName));
4578 StackSString typeName(SString::Utf8, pTypeName, cbTypeName);
4579 StackSString methodName(SString::Utf8, pMethodName, cbMethodName);
4582 // Retrieve the type
4584 TypeHandle stubClassType;
4585 stubClassType = TypeName::GetTypeUsingCASearchRules(typeName.GetUnicode(), pTargetMT->GetAssembly());
4587 MethodTable *pStubClassMT = stubClassType.AsMethodTable();
4589 StackSString stubClassName;
4590 pStubClassMT->_GetFullyQualifiedNameForClassNestedAware(stubClassName);
4592 StackSString targetInterfaceName;
4593 pTargetMT->_GetFullyQualifiedNameForClassNestedAware(targetInterfaceName);
4595 // Restrict to same assembly only to reduce test cost
4596 if (stubClassType.GetAssembly() != pTargetMT->GetAssembly())
4600 IDS_EE_INTEROP_STUB_CA_MUST_BE_WITHIN_SAME_ASSEMBLY,
4601 stubClassName.GetUnicode(),
4602 targetInterfaceName.GetUnicode()
4606 if (stubClassType.HasInstantiation())
4610 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_GENERIC,
4611 stubClassName.GetUnicode()
4615 if (stubClassType.IsInterface())
4619 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_INTERFACE,
4620 stubClassName.GetUnicode()
4625 // Locate the MethodDesc for the stub method
4627 MethodDesc *pStubMD = NULL;
4630 PCCOR_SIGNATURE pTargetSig = NULL;
4631 DWORD pcTargetSig = 0;
4633 SigTypeContext typeContext; // NO generics supported
4635 pTargetMD->GetSig(&pTargetSig, &pcTargetSig);
4637 MetaSig msig(pTargetSig,
4639 pTargetMD->GetModule(),
4641 _ASSERTE(msig.HasThis());
4643 SigBuilder stubSigBuilder;
4646 // Append calling Convention, NumOfArgs + 1,
4648 stubSigBuilder.AppendByte(msig.GetCallingConvention() & ~IMAGE_CEE_CS_CALLCONV_HASTHIS);
4649 stubSigBuilder.AppendData(msig.NumFixedArgs() + 1);
4652 // Append return type
4654 SigPointer pReturn = msig.GetReturnProps();
4655 LPBYTE pReturnTypeBegin = (LPBYTE)pReturn.GetPtr();
4656 IfFailThrow(pReturn.SkipExactlyOne());
4657 LPBYTE pReturnTypeEnd = (LPBYTE)pReturn.GetPtr();
4659 stubSigBuilder.AppendBlob(pReturnTypeBegin, pReturnTypeEnd - pReturnTypeBegin);
4664 stubSigBuilder.AppendElementType(ELEMENT_TYPE_CLASS);
4665 stubSigBuilder.AppendToken(pTargetMT->GetCl());
4668 // Copy rest of the arguments
4670 if (msig.NextArg() != ELEMENT_TYPE_END)
4672 SigPointer pFirstArg = msig.GetArgProps();
4673 LPBYTE pArgBegin = (LPBYTE) pFirstArg.GetPtr();
4674 LPBYTE pArgEnd = (LPBYTE) pTargetSig + pcTargetSig;
4676 stubSigBuilder.AppendBlob(pArgBegin, pArgEnd - pArgBegin);
4680 // Allocate new memory and copy over
4682 DWORD pcStubSig = 0;
4683 PCCOR_SIGNATURE pStubSig = (PCCOR_SIGNATURE) stubSigBuilder.GetSignature(&pcStubSig);
4686 // Find method using name + signature
4688 StackScratchBuffer buffer;
4689 LPCUTF8 szMethodNameUTF8 = methodName.GetUTF8(buffer);
4690 pStubMD = MemberLoader::FindMethod(stubClassType.GetMethodTable(),
4694 pTargetMT->GetModule());
4696 if (pStubMD == NULL)
4705 pTargetMD->GetMDImport(),
4708 // Unfortunately the PrettyPrintSig doesn't print 'static' when the function is static
4709 // so we need to append 'static' here. No need to localize
4710 SString signature(SString::Utf8, (LPCUTF8)"static ");
4711 signature.AppendUTF8((LPCUTF8) qbSig.Ptr());
4714 kMissingMethodException,
4715 IDS_EE_INTEROP_STUB_CA_STUB_METHOD_MISSING,
4716 signature.GetUnicode(),
4717 stubClassName.GetUnicode()
4724 // Check the Stub MD
4727 // Verify that the target interop method can call the stub method
4729 _ASSERTE(pTargetMD != NULL);
4731 AccessCheckContext accessContext(pTargetMD, pTargetMT);
4733 if (!ClassLoader::CanAccess(
4736 stubClassType.GetAssembly(),
4737 pStubMD->GetAttrs(),
4741 StackSString interopMethodName(SString::Utf8, pTargetMD->GetName());
4744 kMethodAccessException,
4745 IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD,
4746 interopMethodName.GetUnicode(),
4747 methodName.GetUnicode()
4751 // The FindMethod call will make sure that it is static by matching signature.
4752 // So there is no need to check and throw
4753 _ASSERTE(pStubMD->IsStatic());
4755 *ppRetStubMD = pStubMD;
4759 #endif // FEATURE_COMINTEROP
4763 //=======================================================================
4764 // ILStubCreatorHelper
4765 // The class is used as a helper class in CreateInteropILStub. It mainly
4766 // puts two methods NDirect::GetStubMethodDesc and NDirect::RemoveILStubCacheEntry
4767 // into a holder. See CreateInteropILStub for more information
4768 //=======================================================================
4769 class ILStubCreatorHelper
4772 ILStubCreatorHelper(MethodDesc *pTargetMD,
4773 NDirectStubParameters* pParams
4775 m_pTargetMD(pTargetMD),
4778 m_bILStubCreator(false)
4780 STANDARD_VM_CONTRACT;
4781 m_pHashParams = CreateHashBlob(m_pParams);
4784 ~ILStubCreatorHelper()
4794 RemoveILStubCacheEntry();
4797 inline void GetStubMethodDesc()
4799 WRAPPER_NO_CONTRACT;
4801 m_pStubMD = ::GetStubMethodDesc(m_pTargetMD, m_pParams, m_pHashParams, &m_amTracker, m_bILStubCreator, m_pStubMD);
4804 inline void RemoveILStubCacheEntry()
4806 WRAPPER_NO_CONTRACT;
4808 if (m_bILStubCreator)
4810 ::RemoveILStubCacheEntry(m_pParams, m_pHashParams);
4811 m_bILStubCreator = false;
4815 inline MethodDesc* GetStubMD()
4817 LIMITED_METHOD_CONTRACT;
4821 inline void SuppressRelease()
4823 WRAPPER_NO_CONTRACT;
4824 m_bILStubCreator = false;
4825 m_amTracker.SuppressRelease();
4828 DEBUG_NOINLINE static void HolderEnter(ILStubCreatorHelper *pThis)
4830 WRAPPER_NO_CONTRACT;
4831 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
4832 pThis->GetStubMethodDesc();
4835 DEBUG_NOINLINE static void HolderLeave(ILStubCreatorHelper *pThis)
4837 WRAPPER_NO_CONTRACT;
4838 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
4839 pThis->RemoveILStubCacheEntry();
4843 MethodDesc* m_pTargetMD;
4844 NDirectStubParameters* m_pParams;
4845 NewArrayHolder<ILStubHashBlob> m_pHashParams;
4846 AllocMemTracker* m_pAmTracker;
4847 MethodDesc* m_pStubMD;
4848 AllocMemTracker m_amTracker;
4849 bool m_bILStubCreator; // Only the creator can remove the ILStub from the Cache
4850 }; //ILStubCreatorHelper
4852 typedef Wrapper<ILStubCreatorHelper*, ILStubCreatorHelper::HolderEnter, ILStubCreatorHelper::HolderLeave> ILStubCreatorHelperHolder;
4854 MethodDesc* CreateInteropILStub(
4856 StubSigDesc* pSigDesc,
4857 CorNativeLinkType nlType,
4858 CorNativeLinkFlags nlFlags,
4859 CorInfoCallConvExtension unmgdCallConv,
4861 mdParamDef* pParamTokenArray,
4863 bool* pGeneratedNewStub = nullptr
4866 CONTRACT(MethodDesc*)
4870 PRECONDITION(CheckPointer(pSigDesc));
4871 POSTCONDITION(CheckPointer(RETVAL));
4876 ///////////////////////////////
4878 // MethodDesc creation
4880 ///////////////////////////////
4882 MethodDesc* pStubMD = NULL;
4884 Module* pModule = pSigDesc->m_pModule;
4885 Module* pLoaderModule = pSigDesc->m_pLoaderModule;
4886 MethodDesc* pTargetMD = pSigDesc->m_pMD;
4887 MethodTable* pTargetMT = pSigDesc->m_pMT;
4889 // pTargetMD may be null in the case of calli pinvoke
4890 // and vararg pinvoke.
4893 DWORD dwStubFlags = pss->GetFlags();
4895 #ifdef FEATURE_COMINTEROP
4897 // Try to locate predefined IL stub either defined in user code or hardcoded in CLR
4898 // If there is one, use the pointed method as the stub.
4899 // Skip pTargetMD == NULL case for reverse interop calls
4901 if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD)))
4903 #ifndef CROSSGEN_COMPILE
4904 // We are about to execute method in pStubMD which could be in another module.
4905 // Call EnsureActive before make the call
4906 // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here
4907 pStubMD->EnsureActive();
4909 if (pStubMD->IsPreImplemented())
4910 RestoreNGENedStub(pStubMD);
4915 #endif // FEATURE_COMINTEROP
4917 // Otherwise, fall back to generating IL stub on-the-fly
4918 NDirectStubParameters params(pSigDesc->m_sig,
4919 &pSigDesc->m_typeContext,
4932 // The following two ILStubCreatorHelperHolder are to recover the status when an
4933 // exception happen during the generation of the IL stubs. We need to free the
4934 // memory allocated and restore the ILStubCache.
4936 // The following block is logically divided into two phases. The first phase is
4937 // CreateOrGet IL Stub phase which we take a domain level lock. The second phase
4938 // is IL generation phase which we take a MethodDesc level lock. Taking two locks
4939 // is mainly designed for performance.
4941 // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the
4942 // allocated memory during the creation of MethodDesc so that we are able to remove
4943 // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper
4945 // When removing IL Stub from Cache, we have a constraint that only the thread which
4946 // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will
4947 // remove it from cache if OOM occurs
4950 ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, ¶ms);
4952 // take the domain level lock
4953 ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock());
4956 // The holder will free the allocated MethodDesc and restore the ILStubCache
4957 // if exception happen.
4958 ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper);
4959 pStubMD = pCreateOrGetStubHolder->GetStubMD();
4961 ///////////////////////////////
4965 ///////////////////////////////
4968 // take the MethodDesc level locker
4969 ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
4971 ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
4973 // We can release the holder for the first phase now
4974 pCreateOrGetStubHolder.SuppressRelease();
4976 // We have the entry lock we need to use, so we can release the global lock.
4977 pILStubLock.Release();
4980 // The holder will free the allocated MethodDesc and restore the ILStubCache
4981 // if exception happen. The reason to get the holder again is to
4982 ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper);
4984 if (!pEntryLock.DeadlockAwareAcquire())
4986 // the IL generation is not recursive.
4987 // However, we can encounter a recursive situation when attempting to
4988 // marshal a struct containing a layout class containing another struct.
4989 // Throw an exception here instead of asserting.
4990 if (SF_IsStructMarshalStub(dwStubFlags))
4992 _ASSERTE(pSigDesc->m_pMT != nullptr);
4993 StackSString strTypeName;
4994 TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT));
4995 COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode());
4997 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5000 if (SF_IsSharedStub(params.m_dwStubFlags))
5002 // We need to re-acquire the lock in case we need to get a new pStubMD
5003 // in the case that the owner of the shared stub was destroyed.
5004 pILStubLock.Acquire();
5006 // Assure that pStubMD we have now has not been destroyed by other threads
5007 pGenILHolder->GetStubMethodDesc();
5009 while (pStubMD != pGenILHolder->GetStubMD())
5011 pStubMD = pGenILHolder->GetStubMD();
5013 pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5014 pEntryLock.Assign(pEntry, FALSE);
5016 // We have the entry lock we need to use, so we can release the global lock.
5017 pILStubLock.Release();
5019 if (!pEntryLock.DeadlockAwareAcquire())
5021 // the IL generation is not recursive.
5022 // However, we can encounter a recursive situation when attempting to
5023 // marshal a struct containing a layout class containing another struct.
5024 // Throw an exception here instead of asserting.
5025 if (SF_IsStructMarshalStub(dwStubFlags))
5027 _ASSERTE(pSigDesc->m_pMT != nullptr);
5028 StackSString strTypeName;
5029 TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT));
5030 COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode());
5032 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5035 pILStubLock.Acquire();
5037 pGenILHolder->GetStubMethodDesc();
5043 // We have the entry lock now, we can release the global lock
5044 pILStubLock.Release();
5046 _ASSERTE(pEntryLock.GetValue()->HasLock());
5048 if (pEntry->m_hrResultCode != S_FALSE)
5050 // We came in to generate the IL but someone
5051 // beat us so there's nothing to do
5055 ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
5057 CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc()));
5059 if (pResolver->IsILGenerated())
5061 // this stub already has its IL generated
5066 // Check that the stub signature and MethodDesc are compatible. The JIT
5067 // interface functions depend on this.
5071 SigPointer ptr = pSigDesc->m_sig.CreateSigPointer();
5073 uint32_t callConvInfo;
5074 IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo));
5076 BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS);
5078 // CreateNDirectStubWorker will throw an exception for these cases.
5079 BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags);
5081 if (fSigIsStatic || fCanHaveThis)
5083 CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic);
5088 ILStubGenHolder sgh(pResolver);
5090 pResolver->SetStubMethodDesc(pStubMD);
5091 pResolver->SetStubTargetMethodDesc(pTargetMD);
5093 if (SF_IsStructMarshalStub(dwStubFlags))
5095 CreateStructStub(pss, pSigDesc, pTargetMT, dwStubFlags, pStubMD);
5099 CreateNDirectStubWorker(pss,
5111 pResolver->SetTokenLookupMap(pss->GetTokenLookupMap());
5113 pResolver->SetStubTargetMethodSig(
5114 pss->GetStubTargetMethodSig(),
5115 pss->GetStubTargetMethodSigLength());
5117 // we successfully generated the IL stub
5118 sgh.SuppressRelease();
5121 if (pGeneratedNewStub)
5123 *pGeneratedNewStub = true;
5126 pEntry->m_hrResultCode = S_OK;
5130 // Link the MethodDesc onto the method table with the lock taken
5131 AddMethodDescChunkWithLockTaken(¶ms, pStubMD);
5133 pGenILHolder.SuppressRelease();
5137 ilStubCreatorHelper.SuppressRelease();
5140 #if defined(TARGET_X86)
5141 if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg())
5143 // copy the stack arg byte count from the stub MD to the target MD - this number is computed
5144 // during stub generation and is copied to all target MDs that share the stub
5145 // (we don't set it for varargs - the number is call site specific)
5146 // also copy the "takes parameters with copy constructors" flag which is needed to generate
5147 // appropriate intercept stub
5149 WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
5150 if (pTargetMD->IsNDirect())
5152 NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD;
5154 pTargetNMD->SetStackArgumentSize(cbStackArgSize, CallConv::GetDefaultUnmanagedCallingConvention());
5156 #ifdef FEATURE_COMINTEROP
5159 if (SF_IsCOMStub(dwStubFlags))
5161 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD);
5163 if (pComInfo != NULL)
5165 pComInfo->SetStackArgumentSize(cbStackArgSize);
5169 #endif // FEATURE_COMINTEROP
5171 #endif // defined(TARGET_X86)
5177 MethodDesc* NDirect::CreateCLRToNativeILStub(
5178 StubSigDesc* pSigDesc,
5179 CorNativeLinkType nlType,
5180 CorNativeLinkFlags nlFlags,
5181 CorInfoCallConvExtension unmgdCallConv,
5182 DWORD dwStubFlags) // NDirectStubFlags
5184 CONTRACT(MethodDesc*)
5188 PRECONDITION(CheckPointer(pSigDesc));
5189 POSTCONDITION(CheckPointer(RETVAL));
5195 int numParamTokens = 0;
5196 mdParamDef* pParamTokenArray = NULL;
5198 CreateNDirectStubAccessMetadata(pSigDesc,
5204 Module *pModule = pSigDesc->m_pModule;
5205 numParamTokens = numArgs + 1;
5206 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5207 CollateParamTokens(pModule->GetMDImport(), pSigDesc->m_tkMethodDef, numArgs, pParamTokenArray);
5209 MethodDesc *pMD = pSigDesc->m_pMD;
5211 NewHolder<ILStubState> pStubState;
5213 #ifdef FEATURE_COMINTEROP
5214 if (SF_IsCOMStub(dwStubFlags))
5216 if (SF_IsReverseStub(dwStubFlags))
5218 pStubState = new COMToCLR_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5222 pStubState = new CLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5228 pStubState = new PInvoke_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, unmgdCallConv, iLCIDArg, pMD);
5231 MethodDesc* pStubMD;
5232 pStubMD = CreateInteropILStub(
5245 #ifdef FEATURE_COMINTEROP
5246 MethodDesc* NDirect::CreateFieldAccessILStub(
5247 PCCOR_SIGNATURE szMetaSig,
5248 DWORD cbMetaSigSize,
5251 DWORD dwStubFlags, // NDirectStubFlags
5254 CONTRACT(MethodDesc*)
5258 PRECONDITION(CheckPointer(szMetaSig));
5259 PRECONDITION(CheckPointer(pModule));
5260 PRECONDITION(CheckPointer(pFD, NULL_OK));
5261 PRECONDITION(SF_IsFieldGetterStub(dwStubFlags) || SF_IsFieldSetterStub(dwStubFlags));
5262 POSTCONDITION(CheckPointer(RETVAL));
5266 int numArgs = (SF_IsFieldSetterStub(dwStubFlags) ? 1 : 0);
5267 int numParamTokens = numArgs + 1;
5269 // make sure we capture marshaling metadata
5270 mdParamDef* pParamTokenArray = (mdParamDef *)_alloca(numParamTokens * sizeof(mdParamDef));
5271 pParamTokenArray[0] = mdParamDefNil;
5272 pParamTokenArray[numArgs] = (mdParamDef)fd;
5274 // fields are never preserve-sig
5275 dwStubFlags |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
5277 // convert field signature to getter/setter signature
5278 SigBuilder sigBuilder;
5280 sigBuilder.AppendData(IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS);
5281 sigBuilder.AppendData(numArgs);
5283 if (SF_IsFieldSetterStub(dwStubFlags))
5285 // managed setter returns void
5286 sigBuilder.AppendElementType(ELEMENT_TYPE_VOID);
5289 CONSISTENCY_CHECK(*szMetaSig == IMAGE_CEE_CS_CALLCONV_FIELD);
5291 sigBuilder.AppendBlob((const PVOID)(szMetaSig + 1), cbMetaSigSize - 1);
5292 szMetaSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbMetaSigSize);
5294 StubSigDesc sigDesc(Signature(szMetaSig, cbMetaSigSize), pModule);
5297 sigDesc.m_pDebugName = pFD->GetDebugName();
5298 sigDesc.m_pDebugClassName = pFD->GetEnclosingMethodTable()->GetDebugClassName();
5301 Signature signature(szMetaSig, cbMetaSigSize);
5302 NewHolder<ILStubState> pStubState = new COMToCLRFieldAccess_ILStubState(pModule, signature, &sigDesc.m_typeContext, dwStubFlags, pFD);
5304 MethodDesc* pStubMD;
5305 pStubMD = CreateInteropILStub(
5308 (CorNativeLinkType)0,
5309 (CorNativeLinkFlags)0,
5310 CallConv::GetDefaultUnmanagedCallingConvention(),
5317 #endif // FEATURE_COMINTEROP
5319 #ifndef DACCESS_COMPILE
5321 MethodDesc* NDirect::CreateStructMarshalILStub(MethodTable* pMT)
5323 CONTRACT(MethodDesc*)
5327 PRECONDITION(CheckPointer(pMT));
5328 POSTCONDITION(CheckPointer(RETVAL));
5332 if (IsReadyToRunCompilation())
5334 // We don't support emitting struct marshalling IL stubs into R2R images.
5338 DWORD dwStubFlags = NDIRECTSTUB_FL_STRUCT_MARSHAL;
5340 BOOL bestFit, throwOnUnmappableChar;
5342 ReadBestFitCustomAttribute(pMT->GetModule(), pMT->GetCl(), &bestFit, &throwOnUnmappableChar);
5344 if (bestFit == TRUE)
5346 dwStubFlags |= NDIRECTSTUB_FL_BESTFIT;
5348 if (throwOnUnmappableChar == TRUE)
5350 dwStubFlags |= NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR;
5353 // ValueClass signature:
5354 // void (ref Struct managedData, native Struct* nativeData, int marshalAction, ref CleanupWorkListElement cwl)
5355 // LayoutClass signature:
5356 // void (ref byte managedData, byte* nativeData, int marshalAction, ref CleanupWorkListElement cwl)
5357 constexpr int numParamTokens = 1;
5358 mdParamDef pParamTokenArray[numParamTokens];
5359 pParamTokenArray[0] = (mdParamDef)pMT->GetCl();
5361 FunctionSigBuilder sigBuilder;
5363 sigBuilder.SetCallingConv(IMAGE_CEE_CS_CALLCONV_DEFAULT);
5364 LocalDesc returnType(ELEMENT_TYPE_VOID);
5365 sigBuilder.SetReturnType(&returnType);
5368 if (pMT->IsValueType())
5370 LocalDesc managedParameter(pMT);
5371 managedParameter.MakeByRef();
5372 sigBuilder.NewArg(&managedParameter);
5374 LocalDesc nativeValueType(TypeHandle{ pMT }.MakeNativeValueType());
5375 nativeValueType.MakePointer();
5376 sigBuilder.NewArg(&nativeValueType);
5380 LocalDesc byteRef(ELEMENT_TYPE_I1);
5381 byteRef.MakeByRef();
5382 sigBuilder.NewArg(&byteRef);
5383 LocalDesc bytePtr(ELEMENT_TYPE_I1);
5384 bytePtr.MakePointer();
5385 sigBuilder.NewArg(&bytePtr);
5388 LocalDesc i4(ELEMENT_TYPE_I4);
5389 sigBuilder.NewArg(&i4);
5391 LocalDesc cleanupWorkList(CoreLibBinder::GetClass(CLASS__CLEANUP_WORK_LIST_ELEMENT));
5392 cleanupWorkList.MakeByRef();
5393 sigBuilder.NewArg(&cleanupWorkList);
5395 DWORD cbMetaSigSize = sigBuilder.GetSigSize();
5396 AllocMemHolder<BYTE> szMetaSig(pMT->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMetaSigSize)));
5397 sigBuilder.GetSig(szMetaSig, cbMetaSigSize);
5399 StubSigDesc sigDesc(pMT, Signature(szMetaSig, cbMetaSigSize), pMT->GetModule());
5402 sigDesc.m_pDebugName = "Struct Marshalling Stub";
5403 sigDesc.m_pDebugClassName = pMT->GetDebugClassName();
5406 Signature signature(szMetaSig, cbMetaSigSize);
5408 NewHolder<ILStubState> pStubState = new StructMarshal_ILStubState(pMT, signature, &sigDesc.m_typeContext, dwStubFlags);
5410 bool generatedNewStub = false;
5412 MethodDesc* pStubMD;
5413 pStubMD = CreateInteropILStub(
5416 (CorNativeLinkType)0,
5417 (CorNativeLinkFlags)0,
5418 CorInfoCallConvExtension::Managed,
5424 if (generatedNewStub) // If we generated a new stub, we need to keep the signature we created allocated.
5426 szMetaSig.SuppressRelease();
5432 PCODE NDirect::GetEntryPointForStructMarshalStub(MethodTable* pMT)
5434 LIMITED_METHOD_CONTRACT;
5436 MethodDesc* pMD = CreateStructMarshalILStub(pMT);
5438 _ASSERTE(pMD != nullptr);
5440 return pMD->GetMultiCallableAddrOfCode();
5443 #endif // DACCESS_COMPILE
5445 MethodDesc* NDirect::CreateCLRToNativeILStub(PInvokeStaticSigInfo* pSigInfo,
5452 PRECONDITION(pSigInfo != NULL);
5456 StubSigDesc sigDesc(pMD, pSigInfo->GetSignature(), pSigInfo->GetModule());
5458 return CreateCLRToNativeILStub(&sigDesc,
5459 pSigInfo->GetCharSet(),
5460 pSigInfo->GetLinkFlags(),
5461 pSigInfo->GetCallConv(),
5462 pSigInfo->GetStubFlags() | dwStubFlags);
5465 MethodDesc* NDirect::GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags)
5470 PRECONDITION(pNMD != NULL);
5474 MethodDesc* pStubMD = NULL;
5476 if (!pNMD->IsVarArgs() || SF_IsForNumParamBytes(dwStubFlags))
5478 if (pNMD->IsClassConstructorTriggeredByILStub())
5480 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
5483 pStubMD = CreateCLRToNativeILStub(
5485 dwStubFlags & ~NDIRECTSTUB_FL_FOR_NUMPARAMBYTES,
5492 MethodDesc* GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags)
5494 STANDARD_VM_CONTRACT;
5496 #ifdef FEATURE_COMINTEROP
5497 if (SF_IsReverseCOMStub(dwStubFlags))
5499 #ifdef FEATURE_PREJIT
5500 // reverse COM stubs live in a hash table
5501 StubMethodHashTable *pHash = pMD->GetLoaderModule()->GetStubMethodHashTable();
5502 return (pHash == NULL ? NULL : pHash->FindMethodDesc(pMD));
5508 #endif // FEATURE_COMINTEROP
5509 if (pMD->IsNDirect())
5511 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5512 return pNMD->ndirect.m_pStubMD.GetValueMaybeNull();
5514 #ifdef FEATURE_COMINTEROP
5515 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5517 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
5518 return (pComInfo == NULL ? NULL : pComInfo->m_pStubMD.GetValueMaybeNull());
5520 #endif // FEATURE_COMINTEROP
5521 else if (pMD->IsEEImpl())
5523 DelegateEEClass *pClass = (DelegateEEClass *)pMD->GetClass();
5524 if (SF_IsReverseStub(dwStubFlags))
5526 return pClass->m_pReverseStubMD;
5530 return pClass->m_pForwardStubMD;
5533 else if (pMD->IsIL())
5535 // these are currently only created at runtime, not at NGEN time
5540 UNREACHABLE_MSG("unexpected type of MethodDesc");
5544 #ifndef CROSSGEN_COMPILE
5548 LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, NATIVE_LIBRARY_HANDLE hMod)
5550 // GetProcAddress cannot be called while preemptive GC is disabled.
5551 // It requires the OS to take the loader lock.
5555 PRECONDITION(CheckPointer(pMD));
5556 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
5560 g_IBCLogger.LogNDirectCodeAccess(pMD);
5562 RETURN pMD->FindEntryPoint(hMod);
5565 //---------------------------------------------------------
5566 // Loads the DLL and finds the procaddress for an N/Direct call.
5567 //---------------------------------------------------------
5568 VOID NDirectLink(NDirectMethodDesc *pMD)
5573 PRECONDITION(CheckPointer(pMD));
5577 if (pMD->IsClassConstructorTriggeredAtLinkTime())
5579 pMD->GetMethodTable()->CheckRunClassInitThrowing();
5584 LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget;
5586 // Do not repeat the lookup if the QCall was hardbound during ngen
5587 if (pvTarget == NULL)
5589 pvTarget = ECall::GetQCallImpl(pMD);
5593 _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD));
5596 pMD->SetNDirectTarget(pvTarget);
5600 // Loading unmanaged dlls can trigger dllmains which certainly count as code execution!
5601 pMD->EnsureActive();
5604 LPVOID pvTarget = (LPVOID)PInvokeOverride::GetMethodImpl(pMD->GetLibNameRaw(), pMD->GetEntrypointName());
5605 if (pvTarget != NULL)
5607 pMD->SetNDirectTarget(pvTarget);
5612 NATIVE_LIBRARY_HANDLE hmod = NativeLibrary::LoadLibraryFromMethodDesc(pMD);
5613 _ASSERTE(hmod != NULL);
5615 BOOL fSuccess = FALSE;
5616 LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod);
5619 #ifdef TIZEN_ASAN_ENVIRONMENT
5620 if (PAL_IsSanitizedLibraryDirect(hmod))
5622 pvTarget = TizenASanEnv::CreateWrapperSanitizedEntryPoint(pvTarget);
5624 #endif // TIZEN_ASAN_ENVIRONMENT
5626 pMD->SetNDirectTarget(pvTarget);
5632 StackSString ssLibName(SString::Utf8, pMD->GetLibName());
5634 WCHAR wszEPName[50];
5635 if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0)
5637 wszEPName[0] = W('?');
5638 wszEPName[1] = W('\0');
5641 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName);
5643 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName);
5649 PCODE NDirect::GetStubForILStub(MethodDesc* pManagedMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5655 PRECONDITION(CheckPointer(pManagedMD));
5656 POSTCONDITION(RETVAL != NULL);
5660 // pStubMD, if provided, must be preimplemented.
5661 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5663 if (NULL == *ppStubMD)
5665 PInvokeStaticSigInfo sigInfo(pManagedMD);
5666 *ppStubMD = NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pManagedMD);
5669 RETURN JitILStub(*ppStubMD);
5672 PCODE NDirect::GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5674 STANDARD_VM_CONTRACT;
5678 // pStubMD, if provided, must be preimplemented.
5679 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5681 if (NULL == *ppStubMD)
5683 PInvokeStaticSigInfo sigInfo;
5684 NDirect::InitializeSigInfoAndPopulateNDirectMethodDesc(pNMD, &sigInfo);
5686 *ppStubMD = NDirect::GetILStubMethodDesc(pNMD, &sigInfo, dwStubFlags);
5689 if (SF_IsForNumParamBytes(dwStubFlags))
5694 pStub = JitILStub(*ppStubMD);
5698 CONSISTENCY_CHECK(pNMD->IsVarArgs());
5701 // varargs goes through vararg NDirect stub
5703 pStub = TheVarargNDirectStub(pNMD->HasRetBuffArg());
5706 if (pNMD->IsEarlyBound())
5708 pNMD->InitEarlyBoundNDirectTarget();
5716 // NOTE: there is a race in updating this MethodDesc. We depend on all
5717 // threads getting back the same DynamicMethodDesc for a particular
5718 // NDirectMethodDesc, in that case, the locking around the actual JIT
5719 // operation will prevent the code from being jitted more than once.
5720 // By the time we get here, all threads get the same address of code
5721 // back from the JIT operation and they all just fill in the same value
5724 // In the NGEN case, all threads will get the same preimplemented code
5725 // address much like the JIT case.
5731 PCODE JitILStub(MethodDesc* pStubMD)
5733 STANDARD_VM_CONTRACT;
5735 PCODE pCode = pStubMD->GetNativeCode();
5739 ///////////////////////////////
5743 ///////////////////////////////
5746 if (pStubMD->IsDynamicMethod())
5749 // A dynamically generated IL stub
5752 pCode = pStubMD->PrepareInitialCode();
5754 _ASSERTE(pCode == pStubMD->GetNativeCode());
5759 // A static IL stub that is pointing to a static method in user assembly
5760 // Compile it and return the native code
5763 // This returns the stable entry point
5764 pCode = pStubMD->DoPrestub(NULL);
5766 _ASSERTE(pCode == pStubMD->GetStableEntryPoint());
5770 if (!pStubMD->IsDynamicMethod())
5772 // We need an entry point that can be called multiple times
5773 pCode = pStubMD->GetMultiCallableAddrOfCode();
5779 MethodDesc* RestoreNGENedStub(MethodDesc* pStubMD)
5784 PRECONDITION(CheckPointer(pStubMD));
5788 #ifdef FEATURE_PREJIT
5789 pStubMD->CheckRestore();
5791 PCODE pCode = pStubMD->GetPreImplementedCode();
5794 TADDR pFixupList = pStubMD->GetFixupList();
5795 if (pFixupList != NULL)
5797 Module* pZapModule = pStubMD->GetZapModule();
5798 _ASSERTE(pZapModule != NULL);
5799 if (!pZapModule->FixupDelayList(pFixupList))
5801 _ASSERTE(!"FixupDelayList failed");
5802 ThrowHR(COR_E_BADIMAGEFORMAT);
5806 #if defined(HAVE_GCCOVER)
5807 if (GCStress<cfg_instr_ngen>::IsEnabled())
5808 SetupGcCoverage(NativeCodeVersion(pStubMD), (BYTE*)pCode);
5809 #endif // HAVE_GCCOVER
5814 // We only pass a non-NULL pStubMD to GetStubForILStub() below if pStubMD is preimplemeneted.
5817 #endif // FEATURE_PREJIT
5822 PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags, MethodDesc **ppStubMD)
5828 PRECONDITION(CheckPointer(pMD));
5829 PRECONDITION(pMD->IsNDirect() || pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl() || pMD->IsIL());
5834 MethodDesc* pStubMD = NULL;
5836 pStubMD = GetStubMethodDescFromInteropMethodDesc(pMD, dwStubFlags);
5837 if (pStubMD != NULL)
5839 pStubMD = RestoreNGENedStub(pStubMD);
5842 if ((NULL == pStubMD) && (SF_IsNGENedStub(dwStubFlags)))
5844 // Return NULL -- the caller asked only for an ngened stub and
5845 // one does not exist, so don't do any more work.
5846 CONSISTENCY_CHECK(pStub == NULL);
5849 if (pMD->IsNDirect())
5851 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5852 pStub = NDirect::GetStubForILStub(pNMD, &pStubMD, dwStubFlags);
5854 #ifdef FEATURE_COMINTEROP
5856 if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5858 pStub = ComPlusCall::GetStubForILStub(pMD, &pStubMD);
5860 #endif // FEATURE_COMINTEROP
5862 if (pMD->IsEEImpl())
5864 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
5865 EEImplMethodDesc* pDelegateMD = (EEImplMethodDesc*)pMD;
5866 pStub = COMDelegate::GetStubForILStub(pDelegateMD, &pStubMD, dwStubFlags);
5871 CONSISTENCY_CHECK(SF_IsReverseStub(dwStubFlags));
5872 pStub = NDirect::GetStubForILStub(pMD, &pStubMD, dwStubFlags);
5876 UNREACHABLE_MSG("unexpected MethodDesc type");
5879 if (pStubMD != NULL && pStubMD->IsILStub() && pStubMD->AsDynamicMethodDesc()->IsStubNeedsCOMStarted())
5881 // the stub uses COM so make sure that it is started
5885 if (ppStubMD != NULL)
5886 *ppStubMD = pStubMD;
5891 #ifdef FEATURE_COMINTEROP
5892 void CreateCLRToDispatchCOMStub(
5894 DWORD dwStubFlags) // NDirectStubFlags
5900 PRECONDITION(CheckPointer(pMD));
5904 _ASSERTE(SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags));
5906 // If we are dealing with a COM event call, then we need to initialize the
5907 // COM event call information.
5908 if (SF_IsCOMEventCallStub(dwStubFlags))
5910 _ASSERTE(pMD->IsComPlusCall()); // no generic COM eventing
5911 ((ComPlusCallMethodDesc *)pMD)->InitComEventCallInfo();
5914 // Get the call signature information
5915 StubSigDesc sigDesc(pMD);
5919 int numParamTokens = 0;
5920 mdParamDef* pParamTokenArray = NULL;
5922 CreateNDirectStubAccessMetadata(&sigDesc,
5923 CallConv::GetDefaultUnmanagedCallingConvention(),
5928 numParamTokens = numArgs + 1;
5929 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5930 CollateParamTokens(sigDesc.m_pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray);
5932 DispatchStubState MyStubState;
5934 CreateNDirectStubWorker(&MyStubState,
5936 (CorNativeLinkType)0,
5937 (CorNativeLinkFlags)0,
5938 CallConv::GetDefaultUnmanagedCallingConvention(),
5939 dwStubFlags | NDIRECTSTUB_FL_COM,
5944 _ASSERTE(pMD->IsComPlusCall()); // no generic disp-calls
5945 ((ComPlusCallMethodDesc *)pMD)->InitRetThunk();
5949 #endif // FEATURE_COMINTEROP
5951 VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget)
5959 PRECONDITION(IsNDirect());
5960 PRECONDITION(pTarget != NULL);
5964 NDirectWriteableData* pWriteableData = GetWriteableData();
5965 g_IBCLogger.LogNDirectCodeAccess(this);
5966 pWriteableData->m_pNDirectTarget = pTarget;
5969 void MarshalStructViaILStub(MethodDesc* pStubMD, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */)
5976 PRECONDITION(CheckPointer(pStubMD));
5982 PtrToArgSlot(pManagedData),
5983 PtrToArgSlot(pNativeData),
5984 (ARG_SLOT)operation,
5985 PtrToArgSlot(ppCleanupWorkList)
5988 MethodDescCallSite callSite(pStubMD);
5990 callSite.Call(args);
5993 void MarshalStructViaILStubCode(PCODE pStubCode, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */)
6000 PRECONDITION(pStubCode != NULL);
6004 PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pStubCode);
6005 DECLARE_ARGHOLDER_ARRAY(args, 4);
6006 args[ARGNUM_0] = PTR_TO_ARGHOLDER(pManagedData);
6007 args[ARGNUM_1] = PTR_TO_ARGHOLDER(pNativeData);
6008 args[ARGNUM_2] = DWORD_TO_ARGHOLDER(operation);
6009 args[ARGNUM_3] = PTR_TO_ARGHOLDER(ppCleanupWorkList);
6011 CALL_MANAGED_METHOD_NORET(args);
6015 //==========================================================================
6016 // This function is reached only via NDirectImportThunk. It's purpose
6017 // is to ensure that the target DLL is fully loaded and ready to run.
6019 // FUN FACTS: Though this function is actually entered in unmanaged mode,
6020 // it can reenter managed mode and throw a COM+ exception if the DLL linking
6022 //==========================================================================
6023 EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD)
6027 BEGIN_PRESERVE_LAST_ERROR;
6037 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
6038 // this function is called by CLR to native assembly stubs which are called by
6039 // managed code as a result, we need an unwind and continue handler to translate
6040 // any of our internal exceptions into managed exceptions.
6041 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6043 if (pMD->IsEarlyBound())
6045 if (!pMD->IsZapped())
6047 // we need the MD to be populated in case we decide to build an intercept
6048 // stub to wrap the target in InitEarlyBoundNDirectTarget
6049 NDirect::PopulateNDirectMethodDesc(pMD);
6052 pMD->InitEarlyBoundNDirectTarget();
6057 // Otherwise we're in an inlined pinvoke late bound MD
6059 INDEBUG(Thread *pThread = GetThread());
6061 _ASSERTE(pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()
6062 || pMD->ShouldSuppressGCTransition());
6064 CONSISTENCY_CHECK(pMD->IsNDirect());
6066 // With IL stubs, we don't have to do anything but ensure the DLL is loaded.
6069 if (!pMD->IsZapped())
6071 NDirect::PopulateNDirectMethodDesc(pMD);
6075 // must have been populated at NGEN time
6076 _ASSERTE(pMD->GetLibName() != NULL);
6079 pMD->CheckRestore();
6085 ret = pMD->GetNDirectTarget();
6087 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
6088 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
6090 END_PRESERVE_LAST_ERROR;
6095 //===========================================================================
6096 // Support for Pinvoke Calli instruction
6098 //===========================================================================
6100 EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock * pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD)
6102 BEGIN_PRESERVE_LAST_ERROR;
6104 STATIC_CONTRACT_THROWS;
6105 STATIC_CONTRACT_GC_TRIGGERS;
6106 STATIC_CONTRACT_MODE_COOPERATIVE;
6107 STATIC_CONTRACT_ENTRY_POINT;
6109 MAKE_CURRENT_THREAD_AVAILABLE();
6112 Thread::ObjectRefFlush(CURRENT_THREAD);
6115 FrameWithCookie<PrestubMethodFrame> frame(pTransitionBlock, pMD);
6116 PrestubMethodFrame * pFrame = &frame;
6118 pFrame->Push(CURRENT_THREAD);
6120 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
6121 _ASSERTE(pMD == pFrame->GetFunction());
6123 GetILStubForCalli(pVASigCookie, pMD);
6125 pFrame->Pop(CURRENT_THREAD);
6127 END_PRESERVE_LAST_ERROR;
6130 EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
6132 BEGIN_PRESERVE_LAST_ERROR;
6134 STATIC_CONTRACT_THROWS;
6135 STATIC_CONTRACT_GC_TRIGGERS;
6136 STATIC_CONTRACT_MODE_COOPERATIVE;
6137 STATIC_CONTRACT_ENTRY_POINT;
6139 MAKE_CURRENT_THREAD_AVAILABLE();
6142 Thread::ObjectRefFlush(CURRENT_THREAD);
6145 FrameWithCookie<PInvokeCalliFrame> frame(pTransitionBlock, pVASigCookie, pUnmanagedTarget);
6146 PInvokeCalliFrame * pFrame = &frame;
6148 pFrame->Push(CURRENT_THREAD);
6150 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
6152 GetILStubForCalli(pVASigCookie, NULL);
6154 pFrame->Pop(CURRENT_THREAD);
6156 END_PRESERVE_LAST_ERROR;
6159 PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
6167 PRECONDITION(CheckPointer(pVASigCookie));
6168 PRECONDITION(CheckPointer(pMD, NULL_OK));
6169 POSTCONDITION(RETVAL != NULL);
6173 PCODE pTempILStub = NULL;
6175 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
6176 // this function is called by CLR to native assembly stubs which are called by
6177 // managed code as a result, we need an unwind and continue handler to translate
6178 // any of our internal exceptions into managed exceptions.
6179 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6181 // Force a GC if the stress level is high enough
6182 GCStress<cfg_any>::MaybeTrigger();
6186 Signature signature = pVASigCookie->signature;
6187 CorInfoCallConvExtension unmgdCallConv = CorInfoCallConvExtension::Managed;
6189 DWORD dwStubFlags = NDIRECTSTUB_FL_BESTFIT;
6191 // The MethodDesc pointer may in fact be the unmanaged target, see PInvokeStubs.asm.
6192 if (pMD == NULL || (UINT_PTR)pMD & 0x1)
6195 dwStubFlags |= NDIRECTSTUB_FL_UNMANAGED_CALLI;
6197 // need to convert the CALLI signature to stub signature with managed calling convention
6198 BYTE callConv = MetaSig::GetCallingConvention(signature);
6200 // Unmanaged calling convention indicates modopt should be read
6201 if (callConv != IMAGE_CEE_CS_CALLCONV_UNMANAGED)
6203 unmgdCallConv = (CorInfoCallConvExtension)callConv;
6207 CallConvBuilder builder;
6209 HRESULT hr = CallConv::TryGetUnmanagedCallingConventionFromModOpt(GetScopeHandle(pVASigCookie->pModule), signature.GetRawSig(), signature.GetRawSigLen(), &builder, &errorResID);
6211 COMPlusThrowHR(hr, errorResID);
6213 unmgdCallConv = builder.GetCurrentCallConv();
6214 if (unmgdCallConv == CallConvBuilder::UnsetValue)
6216 unmgdCallConv = CallConv::GetDefaultUnmanagedCallingConvention();
6219 if (builder.IsCurrentCallConvModSet(CallConvBuilder::CALL_CONV_MOD_SUPPRESSGCTRANSITION))
6221 dwStubFlags |= NDIRECTSTUB_FL_SUPPRESSGCTRANSITION;
6225 LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
6226 PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen()));
6227 CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen());
6229 // make the stub IMAGE_CEE_CS_CALLCONV_DEFAULT
6230 *new_sig &= ~IMAGE_CEE_CS_CALLCONV_MASK;
6231 *new_sig |= IMAGE_CEE_CS_CALLCONV_DEFAULT;
6233 signature = Signature(new_sig, signature.GetRawSigLen());
6237 _ASSERTE(pMD->IsNDirect());
6238 dwStubFlags |= NDIRECTSTUB_FL_CONVSIGASVARARG;
6240 // vararg P/Invoke must be cdecl
6241 unmgdCallConv = CorInfoCallConvExtension::C;
6243 if (((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
6245 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
6250 CorNativeLinkFlags nlFlags;
6251 CorNativeLinkType nlType;
6255 PInvokeStaticSigInfo sigInfo(pMD);
6257 md = pMD->GetMemberDef();
6258 nlFlags = sigInfo.GetLinkFlags();
6259 nlType = sigInfo.GetCharSet();
6263 md = mdMethodDefNil;
6268 StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule);
6270 MethodDesc* pStubMD = NDirect::CreateCLRToNativeILStub(&sigDesc,
6276 pTempILStub = JitILStub(pStubMD);
6278 InterlockedCompareExchangeT<PCODE>(&pVASigCookie->pNDirectILStub,
6282 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
6283 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
6285 RETURN pVASigCookie->pNDirectILStub;
6288 #endif // CROSSGEN_COMPILE
6290 #endif // #ifndef DACCESS_COMPILE