1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// File: DllImport.cpp
6//
7
8//
9// P/Invoke support.
10//
11
12
13#include "common.h"
14
15#include "vars.hpp"
16#include "stublink.h"
17#include "threads.h"
18#include "excep.h"
19#include "dllimport.h"
20#include "method.hpp"
21#include "siginfo.hpp"
22#include "comdelegate.h"
23#include "ceeload.h"
24#include "mlinfo.h"
25#include "eeconfig.h"
26#include "comutilnative.h"
27#include "corhost.h"
28#include "asmconstants.h"
29#include "mdaassistants.h"
30#include "customattribute.h"
31#include "ilstubcache.h"
32#include "typeparse.h"
33#include "sigbuilder.h"
34#include "sigformat.h"
35#include "strongnameholders.h"
36#include "ecall.h"
37
38#include <formattype.h>
39#include "../md/compiler/custattr.h"
40
41#ifdef FEATURE_COMINTEROP
42#include "runtimecallablewrapper.h"
43#include "clrtocomcall.h"
44#endif // FEATURE_COMINTEROP
45
46#ifdef FEATURE_PREJIT
47#include "compile.h"
48#endif // FEATURE_PREJIT
49
50#include "eventtrace.h"
51#include "clr/fs/path.h"
52using namespace clr::fs;
53
54// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
55// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
56// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
57#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
58
59// remove when we get an updated SDK
60#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
61#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
62
63void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx)
64{
65 LIMITED_METHOD_CONTRACT;
66 if (pClause->kind == ILStubEHClause::kNone)
67 return;
68
69 int idx = *pCurIdx;
70 *pCurIdx = idx + 1;
71
72 CorExceptionFlag flags;
73 switch (pClause->kind)
74 {
75 case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break;
76 case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break;
77 default:
78 UNREACHABLE_MSG("unexpected ILStubEHClause kind");
79 }
80 _ASSERTE(idx < nClauses);
81 pEHSect->Fat.Clauses[idx].Flags = flags;
82 pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset;
83 pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength;
84 pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset;
85 pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength;
86 pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken;
87}
88
89VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo)
90{
91 LIMITED_METHOD_CONTRACT;
92 pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat);
93 pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses);
94
95 int curIdx = 0;
96 AppendEHClause(nClauses, pEHSect, pOne, &curIdx);
97 AppendEHClause(nClauses, pEHSect, pTwo, &curIdx);
98}
99
100StubSigDesc::StubSigDesc(MethodDesc *pMD, PInvokeStaticSigInfo* pSigInfo /*= NULL*/)
101{
102 CONTRACTL
103 {
104 NOTHROW;
105 GC_NOTRIGGER;
106 SUPPORTS_DAC;
107 }
108 CONTRACTL_END
109
110 m_pMD = pMD;
111 if (pSigInfo != NULL)
112 {
113 m_sig = pSigInfo->GetSignature();
114 m_pModule = pSigInfo->GetModule();
115 }
116 else
117 {
118 _ASSERTE(pMD != NULL);
119 m_sig = pMD->GetSignature();
120 m_pModule = pMD->GetModule(); // Used for token resolution.
121 }
122
123 if (pMD != NULL)
124 {
125 m_tkMethodDef = pMD->GetMemberDef();
126 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
127 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
128 }
129 else
130 {
131 m_tkMethodDef = mdMethodDefNil;
132 m_pLoaderModule = m_pModule;
133 }
134
135 INDEBUG(InitDebugNames());
136}
137
138StubSigDesc::StubSigDesc(MethodDesc *pMD, Signature sig, Module *pModule)
139{
140 CONTRACTL
141 {
142 NOTHROW;
143 GC_NOTRIGGER;
144 SUPPORTS_DAC;
145 PRECONDITION(!sig.IsEmpty());
146 PRECONDITION(pModule != NULL);
147 }
148 CONTRACTL_END
149
150 m_pMD = pMD;
151 m_sig = sig;
152 m_pModule = pModule;
153
154 if (pMD != NULL)
155 {
156 m_tkMethodDef = pMD->GetMemberDef();
157 SigTypeContext::InitTypeContext(pMD, &m_typeContext);
158 m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation.
159 }
160 else
161 {
162 m_tkMethodDef = mdMethodDefNil;
163 m_pLoaderModule = m_pModule;
164 }
165
166 INDEBUG(InitDebugNames());
167}
168
169#ifndef DACCESS_COMPILE
170
171class StubState
172{
173public:
174 virtual void SetLastError(BOOL fSetLastError) = 0;
175 virtual void BeginEmit(DWORD dwStubFlags) = 0;
176 virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) = 0;
177 virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset) = 0;
178 virtual void MarshalLCID(int argIdx) = 0;
179
180#ifdef FEATURE_COMINTEROP
181 virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray) = 0;
182 virtual void MarshalFactoryReturn() = 0;
183#endif // FEATURE_COMINTEROP
184
185 virtual void EmitInvokeTarget(MethodDesc *pStubMD) = 0;
186
187 virtual void FinishEmit(MethodDesc* pMD) = 0;
188
189 virtual ~StubState()
190 {
191 LIMITED_METHOD_CONTRACT;
192 }
193};
194
195class ILStubState : public StubState
196{
197protected:
198
199 ILStubState(
200 Module* pStubModule,
201 const Signature &signature,
202 SigTypeContext* pTypeContext,
203 BOOL fTargetHasThis,
204 BOOL fStubHasThis,
205 DWORD dwStubFlags,
206 int iLCIDParamIdx,
207 MethodDesc* pTargetMD)
208 : m_slIL(dwStubFlags, pStubModule, signature, pTypeContext, pTargetMD, iLCIDParamIdx, fTargetHasThis, fStubHasThis)
209 {
210 STANDARD_VM_CONTRACT;
211
212 m_fSetLastError = 0;
213 }
214
215public:
216 void SetLastError(BOOL fSetLastError)
217 {
218 LIMITED_METHOD_CONTRACT;
219
220 m_fSetLastError = fSetLastError;
221 }
222
223 // We use three stub linkers to generate IL stubs. The pre linker is the main one. It does all the marshaling and
224 // then calls the target method. The post return linker is only used to unmarshal the return value after we return
225 // from the target method. The post linker handles all the unmarshaling for by ref arguments and clean-up. It
226 // also checks if we should throw an exception etc.
227 //
228 // Currently, we have two "emittable" ILCodeLabel's. The first one is at the beginning of the pre linker. This
229 // label is used to emit code to declare and initialize clean-up flags. Each argument which requires clean-up
230 // emits one flag. This flag is set only after the marshaling is done, and it is checked before we do any clean-up
231 // in the finally.
232 //
233 // The second "emittable" ILCodeLabel is at the beginning of the post linker. It is used to emit code which is
234 // not safe to run in the case of an exception. The rest of the post linker is wrapped in a finally, and it contains
235 // with the necessary clean-up which should be executed in both normal and exception cases.
236 void BeginEmit(DWORD dwStubFlags)
237 {
238 WRAPPER_NO_CONTRACT;
239 m_slIL.Begin(dwStubFlags);
240 m_dwStubFlags = dwStubFlags;
241 }
242
243 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
244 {
245 CONTRACTL
246 {
247 STANDARD_VM_CHECK;
248
249 PRECONDITION(CheckPointer(pInfo));
250 }
251 CONTRACTL_END;
252
253 pInfo->GenerateReturnIL(&m_slIL, argOffset,
254 SF_IsForwardStub(m_dwStubFlags),
255 SF_IsFieldGetterStub(m_dwStubFlags),
256 SF_IsHRESULTSwapping(m_dwStubFlags));
257 }
258
259 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
260 {
261 CONTRACTL
262 {
263 STANDARD_VM_CHECK;
264 PRECONDITION(CheckPointer(pInfo));
265 }
266 CONTRACTL_END;
267
268 pInfo->GenerateArgumentIL(&m_slIL, argOffset, nativeStackOffset, SF_IsForwardStub(m_dwStubFlags));
269 }
270
271#ifdef FEATURE_COMINTEROP
272 // Marshal the hidden length parameter for the managed parameter in pInfo
273 virtual void MarshalHiddenLengthArgument(MarshalInfo *pInfo, BOOL isForReturnArray)
274 {
275 STANDARD_VM_CONTRACT;
276
277 pInfo->MarshalHiddenLengthArgument(&m_slIL, SF_IsForwardStub(m_dwStubFlags), isForReturnArray);
278
279 if (SF_IsReverseStub(m_dwStubFlags))
280 {
281 // Hidden length arguments appear explicitly in the native signature
282 // however, they are not in the managed signature.
283 m_slIL.AdjustTargetStackDeltaForExtraParam();
284 }
285 }
286
287 void MarshalFactoryReturn()
288 {
289 CONTRACTL
290 {
291 STANDARD_VM_CHECK;
292 PRECONDITION(SF_IsCOMStub(m_dwStubFlags));
293 PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
294 }
295 CONTRACTL_END;
296
297 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
298 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
299 ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
300 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
301
302 /*
303 * SETUP
304 */
305
306 // create a local to hold the returned pUnk and initialize to 0 in case the factory fails
307 // and we try to release it during cleanup
308 LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
309 DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
310 pcsSetup->EmitLoadNullPtr();
311 pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
312
313 DWORD dwInnerIInspectableLocalNum = -1;
314 DWORD dwOuterIInspectableLocalNum = -1;
315 if (SF_IsWinRTCompositionStub(m_dwStubFlags))
316 {
317 // Create locals to store the outer and inner IInspectable values and initialize to null
318 // Note that we do this in the setup stream so that we're guaranteed to have a null-initialized
319 // value in the cleanup stream
320 LocalDesc locDescOuterIInspectable(ELEMENT_TYPE_I);
321 dwOuterIInspectableLocalNum = pcsSetup->NewLocal(locDescOuterIInspectable);
322 pcsSetup->EmitLoadNullPtr();
323 pcsSetup->EmitSTLOC(dwOuterIInspectableLocalNum);
324 LocalDesc locDescInnerIInspectable(ELEMENT_TYPE_I);
325 dwInnerIInspectableLocalNum = pcsSetup->NewLocal(locDescInnerIInspectable);
326 pcsSetup->EmitLoadNullPtr();
327 pcsSetup->EmitSTLOC(dwInnerIInspectableLocalNum);
328 }
329
330 /*
331 * DISPATCH
332 */
333
334 // For composition factories, add the two extra params
335 if (SF_IsWinRTCompositionStub(m_dwStubFlags))
336 {
337 // Get outer IInspectable. The helper will return NULL if this is the "top-level" constructor,
338 // and the appropriate outer pointer otherwise.
339 pcsDispatch->EmitLoadThis();
340 m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
341 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_OUTER_INSPECTABLE, 2, 1);
342 pcsDispatch->EmitSTLOC(dwOuterIInspectableLocalNum);
343
344 // load the outer IInspectable (3rd last argument)
345 pcsDispatch->SetStubTargetArgType(ELEMENT_TYPE_I, false);
346 pcsDispatch->EmitLDLOC(dwOuterIInspectableLocalNum);
347
348 // pass pointer to where inner non-delegating IInspectable should be stored (2nd last argument)
349 LocalDesc locDescInnerPtr(ELEMENT_TYPE_I);
350 locDescInnerPtr.MakeByRef();
351 pcsDispatch->SetStubTargetArgType(&locDescInnerPtr, false);
352 pcsDispatch->EmitLDLOCA(dwInnerIInspectableLocalNum);
353 }
354
355 // pass pointer to the local to the factory method (last argument)
356 locDescFactoryRetVal.MakeByRef();
357 pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
358 pcsDispatch->EmitLDLOCA(dwFactoryRetValLocalNum);
359
360 /*
361 * UNMARSHAL
362 */
363
364 // Mark that the factory method has succesfully returned and so cleanup will be necessary after
365 // this point.
366 m_slIL.EmitSetArgMarshalIndex(pcsUnmarshal, NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL);
367
368 // associate the 'this' RCW with one of the returned interface pointers
369 pcsUnmarshal->EmitLoadThis();
370
371 // now we need to find the right interface pointer to load
372 if (dwInnerIInspectableLocalNum != -1)
373 {
374 // We may have a composition scenario
375 ILCodeLabel* pNonCompositionLabel = pcsUnmarshal->NewCodeLabel();
376 ILCodeLabel* pLoadedLabel = pcsUnmarshal->NewCodeLabel();
377
378 // Did we pass an outer IInspectable?
379 pcsUnmarshal->EmitLDLOC(dwOuterIInspectableLocalNum);
380 pcsUnmarshal->EmitBRFALSE(pNonCompositionLabel);
381
382 // yes, this is a composition scenario
383 {
384 // ignore the delegating interface pointer (will be released in cleanup below) - we can
385 // re-create it by QI'ing the non-delegating one.
386 // Note that using this could be useful in the future (avoids an extra QueryInterface call)
387 // Just load the non-delegating interface pointer
388 pcsUnmarshal->EmitLDLOCA(dwInnerIInspectableLocalNum);
389 pcsUnmarshal->EmitBR(pLoadedLabel);
390 }
391 // else, no this is a non-composition scenario
392 {
393 pcsUnmarshal->EmitLabel(pNonCompositionLabel);
394
395 // ignore the non-delegating interface pointer (which should be null, but will regardless get
396 // cleaned up below in the event the factory doesn't follow the pattern properly).
397 // Just load the regular delegating interface pointer
398 pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
399 }
400
401 pcsUnmarshal->EmitLabel(pLoadedLabel);
402 }
403 else
404 {
405 // Definitely can't be a composition scenario - use the only pointer we have
406 pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
407 }
408
409 pcsUnmarshal->EmitCALL(METHOD__MARSHAL__INITIALIZE_WRAPPER_FOR_WINRT, 2, 0);
410
411 /*
412 * CLEANUP
413 */
414
415 // release the returned interface pointer in the finally block
416 m_slIL.SetCleanupNeeded();
417
418 ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
419
420 m_slIL.EmitCheckForArgCleanup(pcsCleanup,
421 NDirectStubLinker::CLEANUP_INDEX_RETVAL_UNMARSHAL,
422 NDirectStubLinker::BranchIfNotMarshaled,
423 pSkipCleanupLabel);
424
425 EmitInterfaceClearNative(pcsCleanup, dwFactoryRetValLocalNum);
426
427 // Note that it's a no-op to pass NULL to Clear_Native, so we call it even though we don't
428 // know if we assigned to the inner/outer IInspectable
429 if (dwInnerIInspectableLocalNum != -1)
430 {
431 EmitInterfaceClearNative(pcsCleanup, dwInnerIInspectableLocalNum);
432 }
433 if (dwOuterIInspectableLocalNum != -1)
434 {
435 EmitInterfaceClearNative(pcsCleanup, dwOuterIInspectableLocalNum);
436 }
437
438 pcsCleanup->EmitLabel(pSkipCleanupLabel);
439 }
440
441 static void EmitInterfaceClearNative(ILCodeStream* pcsEmit, DWORD dwLocalNum)
442 {
443 STANDARD_VM_CONTRACT;
444
445 ILCodeLabel *pSkipClearNativeLabel = pcsEmit->NewCodeLabel();
446 pcsEmit->EmitLDLOC(dwLocalNum);
447 pcsEmit->EmitBRFALSE(pSkipClearNativeLabel);
448 pcsEmit->EmitLDLOC(dwLocalNum);
449 pcsEmit->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
450 pcsEmit->EmitLabel(pSkipClearNativeLabel);
451 }
452
453#endif // FEATURE_COMINTEROP
454
455 void MarshalLCID(int argIdx)
456 {
457 STANDARD_VM_CONTRACT;
458
459 ILCodeStream* pcs = m_slIL.GetDispatchCodeStream();
460
461#ifdef FEATURE_USE_LCID
462 if (SF_IsReverseStub(m_dwStubFlags))
463 {
464 if ((m_slIL.GetStubTargetCallingConv() & IMAGE_CEE_CS_CALLCONV_HASTHIS) == IMAGE_CEE_CS_CALLCONV_HASTHIS)
465 {
466 // the arg number will be incremented by LDARG if we are in an instance method
467 _ASSERTE(argIdx > 0);
468 argIdx--;
469 }
470
471 // call CultureInfo.get_CurrentCulture()
472 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
473
474 // save the current culture
475 LocalDesc locDescCulture(MscorlibBinder::GetClass(CLASS__CULTURE_INFO));
476 DWORD dwCultureLocalNum = pcs->NewLocal(locDescCulture);
477
478 pcs->EmitSTLOC(dwCultureLocalNum);
479
480 // set a new one based on the LCID passed from unmanaged
481 pcs->EmitLDARG(argIdx);
482
483 // call CultureInfo..ctor(lcid)
484 // call CultureInfo.set_CurrentCulture(culture)
485 pcs->EmitNEWOBJ(METHOD__CULTURE_INFO__INT_CTOR, 1);
486 pcs->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
487
488 // and restore the current one after the call
489 m_slIL.SetCleanupNeeded();
490 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
491
492 // call CultureInfo.set_CurrentCulture(original_culture)
493 pcsCleanup->EmitLDLOC(dwCultureLocalNum);
494 pcsCleanup->EmitCALL(METHOD__CULTURE_INFO__SET_CURRENT_CULTURE, 1, 0);
495 }
496 else
497 {
498 if (SF_IsCOMStub(m_dwStubFlags))
499 {
500 // We used to get LCID from current thread's culture here. The code
501 // was replaced by the hardcoded LCID_ENGLISH_US as requested by VSTO.
502 pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
503 }
504 else
505 {
506 // call CultureInfo.get_CurrentCulture()
507 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_CURRENT_CULTURE, 0, 1);
508
509 //call CultureInfo.get_LCID(this)
510 pcs->EmitCALL(METHOD__CULTURE_INFO__GET_ID, 1, 1);
511 }
512 }
513#else // FEATURE_USE_LCID
514 if (SF_IsForwardStub(m_dwStubFlags))
515 {
516 pcs->EmitLDC(0x0409); // LCID_ENGLISH_US
517 }
518#endif // FEATURE_USE_LCID
519
520 // add the extra arg to the unmanaged signature
521 LocalDesc locDescNative(ELEMENT_TYPE_I4);
522 pcs->SetStubTargetArgType(&locDescNative, false);
523
524 if (SF_IsReverseStub(m_dwStubFlags))
525 {
526 // reverse the effect of SetStubTargetArgType on the stack delta
527 // (the LCID argument is explicitly passed from unmanaged but does not
528 // show up in the managed signature in any way)
529 m_slIL.AdjustTargetStackDeltaForExtraParam();
530 }
531
532 }
533
534 void SwapStubSignatures(MethodDesc* pStubMD)
535 {
536 STANDARD_VM_CONTRACT;
537
538 //
539 // Since the stub handles native-to-managed transitions, we have to swap the
540 // stub-state-calculated stub target sig with the stub sig itself. This is
541 // because the stub target sig represents the native signature and the stub
542 // sig represents the managed signature.
543 //
544 // The first step is to convert the managed signature to a module-independent
545 // signature and then pass it off to SetStubTargetMethodSig. Note that the
546 // ILStubResolver will copy the sig, so we only need to make a temporary copy
547 // of it.
548 //
549 SigBuilder sigBuilder;
550
551 {
552 SigPointer sigPtr(pStubMD->GetSig());
553 sigPtr.ConvertToInternalSignature(pStubMD->GetModule(), NULL, &sigBuilder);
554 }
555
556 //
557 // The second step is to reset the sig on the stub MethodDesc to be the
558 // stub-state-calculated stub target sig.
559 //
560 {
561 //
562 // make a domain-local copy of the sig so that this state can outlive the
563 // compile time state.
564 //
565 DWORD cbNewSig;
566 PCCOR_SIGNATURE pNewSig;
567
568 cbNewSig = GetStubTargetMethodSigLength();
569 pNewSig = (PCCOR_SIGNATURE)(void *)pStubMD->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig));
570
571 memcpyNoGCRefs((void *)pNewSig, GetStubTargetMethodSig(), cbNewSig);
572
573 pStubMD->AsDynamicMethodDesc()->SetStoredMethodSig(pNewSig, cbNewSig);
574
575 SigPointer sigPtr(pNewSig, cbNewSig);
576 ULONG callConvInfo;
577 IfFailThrow(sigPtr.GetCallingConvInfo(&callConvInfo));
578
579 if (callConvInfo & CORINFO_CALLCONV_HASTHIS)
580 {
581 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags &= ~mdStatic;
582 pStubMD->ClearStatic();
583 }
584 else
585 {
586 ((PTR_DynamicMethodDesc)pStubMD)->m_dwExtendedFlags |= mdStatic;
587 pStubMD->SetStatic();
588 }
589
590#ifndef _TARGET_X86_
591 // we store the real managed argument stack size in the stub MethodDesc on non-X86
592 UINT stackSize = pStubMD->SizeOfNativeArgStack();
593
594 if (!FitsInU2(stackSize))
595 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
596
597 pStubMD->AsDynamicMethodDesc()->SetNativeStackArgSize(static_cast<WORD>(stackSize));
598#endif // _TARGET_X86_
599 }
600
601 DWORD cbTempModuleIndependentSigLength;
602 BYTE * pTempModuleIndependentSig = (BYTE *)sigBuilder.GetSignature(&cbTempModuleIndependentSigLength);
603
604 // Finish it
605 SetStubTargetMethodSig(pTempModuleIndependentSig,
606 cbTempModuleIndependentSigLength);
607 }
608
609 void EmitInvokeTarget(MethodDesc *pStubMD)
610 {
611 STANDARD_VM_CONTRACT;
612
613 m_slIL.DoNDirect(m_slIL.GetDispatchCodeStream(), m_dwStubFlags, pStubMD);
614 }
615
616#ifdef FEATURE_COMINTEROP
617 void EmitExceptionHandler(LocalDesc* pNativeReturnType, LocalDesc* pManagedReturnType,
618 ILCodeLabel** ppTryEndAndCatchBeginLabel, ILCodeLabel ** ppCatchEndAndReturnLabel)
619 {
620 STANDARD_VM_CONTRACT;
621
622 ILCodeStream* pcsExceptionHandler = m_slIL.NewCodeStream(ILStubLinker::kExceptionHandler);
623 *ppTryEndAndCatchBeginLabel = pcsExceptionHandler->NewCodeLabel();
624 *ppCatchEndAndReturnLabel = pcsExceptionHandler->NewCodeLabel();
625
626 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
627 pcsExceptionHandler->EmitLabel(*ppTryEndAndCatchBeginLabel);
628
629 BYTE nativeReturnElemType = pNativeReturnType->ElementType[0]; // return type of the stub
630 BYTE managedReturnElemType = pManagedReturnType->ElementType[0]; // return type of the mananged target
631
632 bool returnTheHRESULT = SF_IsHRESULTSwapping(m_dwStubFlags) ||
633 (managedReturnElemType == ELEMENT_TYPE_I4) ||
634 (managedReturnElemType == ELEMENT_TYPE_U4);
635
636#ifdef MDA_SUPPORTED
637 if (!returnTheHRESULT)
638 {
639 MdaExceptionSwallowedOnCallFromCom* mda = MDA_GET_ASSISTANT(ExceptionSwallowedOnCallFromCom);
640 if (mda)
641 {
642 // on the stack: exception object, but the stub linker doesn't know it
643 pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
644 pcsExceptionHandler->EmitCALL(METHOD__STUBHELPERS__TRIGGER_EXCEPTION_SWALLOWED_MDA,
645 1, // WARNING: This method takes 2 input args, the exception object and the stub context.
646 // But the ILStubLinker has no knowledge that the exception object is on the
647 // stack (because it is unaware that we've just entered a catch block), so we
648 // lie and claim that we only take one input argument.
649 1); // returns the exception object back
650 }
651 }
652#endif // MDA_SUPPORTED
653
654 DWORD retvalLocalNum = m_slIL.GetReturnValueLocalNum();
655 BinderMethodID getHRForException;
656 if (SF_IsWinRTStub(m_dwStubFlags))
657 {
658 getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION_WINRT;
659 }
660 else
661 {
662 getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION;
663 }
664
665 pcsExceptionHandler->EmitCALL(getHRForException,
666 0, // WARNING: This method takes 1 input arg, the exception object. But the ILStubLinker
667 // has no knowledge that the exception object is on the stack (because it is
668 // unaware that we've just entered a catch block), so we lie and claim that we
669 // don't take any input arguments.
670 1);
671
672 switch (nativeReturnElemType)
673 {
674 default:
675 UNREACHABLE_MSG("Unexpected element type found on native return type.");
676 break;
677 case ELEMENT_TYPE_VOID:
678 _ASSERTE(retvalLocalNum == (DWORD)-1);
679 pcsExceptionHandler->EmitPOP();
680 break;
681 case ELEMENT_TYPE_I4:
682 case ELEMENT_TYPE_U4:
683 {
684 if (!returnTheHRESULT)
685 {
686 pcsExceptionHandler->EmitPOP();
687 pcsExceptionHandler->EmitLDC(0);
688 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
689 }
690 _ASSERTE(retvalLocalNum != (DWORD)-1);
691 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
692 }
693 break;
694 case ELEMENT_TYPE_R4:
695 pcsExceptionHandler->EmitPOP();
696 pcsExceptionHandler->EmitLDC_R4(CLR_NAN_32);
697 _ASSERTE(retvalLocalNum != (DWORD)-1);
698 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
699 break;
700 case ELEMENT_TYPE_R8:
701 pcsExceptionHandler->EmitPOP();
702 pcsExceptionHandler->EmitLDC_R8(CLR_NAN_64);
703 _ASSERTE(retvalLocalNum != (DWORD)-1);
704 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
705 break;
706 case ELEMENT_TYPE_INTERNAL:
707 {
708 TypeHandle returnTypeHnd = pNativeReturnType->InternalToken;
709 CONSISTENCY_CHECK(returnTypeHnd.IsValueType());
710 _ASSERTE(retvalLocalNum != (DWORD)-1);
711 pcsExceptionHandler->EmitLDLOCA(retvalLocalNum);
712 pcsExceptionHandler->EmitINITOBJ(m_slIL.GetDispatchCodeStream()->GetToken(returnTypeHnd));
713 }
714 break;
715 case ELEMENT_TYPE_BOOLEAN:
716 case ELEMENT_TYPE_CHAR:
717 case ELEMENT_TYPE_I1:
718 case ELEMENT_TYPE_U1:
719 case ELEMENT_TYPE_I2:
720 case ELEMENT_TYPE_U2:
721 case ELEMENT_TYPE_I8:
722 case ELEMENT_TYPE_U8:
723 case ELEMENT_TYPE_I:
724 case ELEMENT_TYPE_U:
725 pcsExceptionHandler->EmitPOP();
726 pcsExceptionHandler->EmitLDC(0);
727 pcsExceptionHandler->EmitCONV_T((CorElementType)nativeReturnElemType);
728 _ASSERTE(retvalLocalNum != (DWORD)-1);
729 pcsExceptionHandler->EmitSTLOC(retvalLocalNum);
730 break;
731 }
732
733 pcsExceptionHandler->EmitLEAVE(*ppCatchEndAndReturnLabel);
734 pcsExceptionHandler->EmitLabel(*ppCatchEndAndReturnLabel);
735 if (nativeReturnElemType != ELEMENT_TYPE_VOID)
736 {
737 _ASSERTE(retvalLocalNum != (DWORD)-1);
738 pcsExceptionHandler->EmitLDLOC(retvalLocalNum);
739 }
740 pcsExceptionHandler->EmitRET();
741 }
742#endif // FEATURE_COMINTEROP
743
744 void FinishEmit(MethodDesc* pStubMD)
745 {
746 STANDARD_VM_CONTRACT;
747
748 ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream();
749 ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream();
750 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
751
752 if (SF_IsHRESULTSwapping(m_dwStubFlags) && m_slIL.StubHasVoidReturnType())
753 {
754 // if the return type is void, but we're doing HRESULT swapping, we
755 // need to set the return type here. Otherwise, the return value
756 // marshaler will do this.
757 pcsMarshal->SetStubTargetReturnType(ELEMENT_TYPE_I4); // HRESULT
758
759 if (SF_IsReverseStub(m_dwStubFlags))
760 {
761 // reverse interop needs to seed the return value if the
762 // managed function returns void but we're doing hresult
763 // swapping.
764 pcsUnmarshal->EmitLDC(S_OK);
765 }
766 }
767
768 LocalDesc nativeReturnType;
769 LocalDesc managedReturnType;
770 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(m_dwStubFlags)
771 && !SF_IsFieldGetterStub(m_dwStubFlags)
772 && !SF_IsFieldSetterStub(m_dwStubFlags);
773
774#ifdef FEATURE_COMINTEROP
775 if (hasTryCatchForHRESULT)
776 {
777 m_slIL.GetStubTargetReturnType(&nativeReturnType);
778 m_slIL.GetStubReturnType(&managedReturnType);
779 }
780#endif // FEATURE_COMINTEROP
781
782 if (SF_IsHRESULTSwapping(m_dwStubFlags) && SF_IsReverseStub(m_dwStubFlags))
783 {
784 m_slIL.AdjustTargetStackDeltaForReverseInteropHRESULTSwapping();
785 }
786
787 if (SF_IsForwardCOMStub(m_dwStubFlags))
788 {
789 // Compensate for the 'this' parameter.
790 m_slIL.AdjustTargetStackDeltaForExtraParam();
791 }
792
793 // Don't touch target signatures from this point on otherwise it messes up the
794 // cache in ILStubState::GetStubTargetMethodSig.
795
796#ifdef _DEBUG
797 {
798 // The native and local signatures should not have any tokens.
799 // All token references should have been converted to
800 // ELEMENT_TYPE_INTERNAL.
801 //
802 // Note that MetaSig::GetReturnType and NextArg will normalize
803 // ELEMENT_TYPE_INTERNAL back to CLASS or VALUETYPE.
804 //
805 // <TODO> need to recursively check ELEMENT_TYPE_FNPTR signatures </TODO>
806
807 SigTypeContext typeContext; // this is an empty type context: COM calls are guaranteed to not be generics.
808 MetaSig nsig(
809 GetStubTargetMethodSig(),
810 GetStubTargetMethodSigLength(),
811 MscorlibBinder::GetModule(),
812 &typeContext);
813
814 CorElementType type;
815 IfFailThrow(nsig.GetReturnProps().PeekElemType(&type));
816 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
817
818 while (ELEMENT_TYPE_END != (type = nsig.NextArg()))
819 {
820 IfFailThrow(nsig.GetArgProps().PeekElemType(&type));
821 CONSISTENCY_CHECK(ELEMENT_TYPE_CLASS != type && ELEMENT_TYPE_VALUETYPE != type);
822 }
823 }
824#endif // _DEBUG
825
826#ifdef FEATURE_COMINTEROP
827 if (SF_IsForwardCOMStub(m_dwStubFlags))
828 {
829#if defined(MDA_SUPPORTED)
830 // We won't use this NGEN'ed stub if RaceOnRCWCleanup is enabled at run-time
831 if (!SF_IsNGENedStub(m_dwStubFlags))
832 {
833 // This code may change the type of the frame we use, so it has to be run before the code below where we
834 // retrieve the stack arg size based on the frame type.
835 MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
836 if (mda)
837 {
838 // Here we have to register the RCW of the "this" object to the RCWStack and schedule the clean-up for it.
839 // Emit a call to StubHelpers::StubRegisterRCW() and StubHelpers::StubUnregisterRCW() to do this.
840 m_slIL.EmitLoadRCWThis(pcsMarshal, m_dwStubFlags);
841 pcsMarshal->EmitCALL(METHOD__STUBHELPERS__STUB_REGISTER_RCW, 1, 0);
842
843 // We use an extra local to track whether we need to unregister the RCW on cleanup
844 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
845 DWORD dwRCWRegisteredLocalNum = pcsSetup->NewLocal(ELEMENT_TYPE_BOOLEAN);
846 pcsSetup->EmitLDC(0);
847 pcsSetup->EmitSTLOC(dwRCWRegisteredLocalNum);
848
849 pcsMarshal->EmitLDC(1);
850 pcsMarshal->EmitSTLOC(dwRCWRegisteredLocalNum);
851
852 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
853 ILCodeLabel *pSkipCleanupLabel = pcsCleanup->NewCodeLabel();
854
855 m_slIL.SetCleanupNeeded();
856 pcsCleanup->EmitLDLOC(dwRCWRegisteredLocalNum);
857 pcsCleanup->EmitBRFALSE(pSkipCleanupLabel);
858
859 m_slIL.EmitLoadRCWThis(pcsCleanup, m_dwStubFlags);
860 pcsCleanup->EmitCALL(METHOD__STUBHELPERS__STUB_UNREGISTER_RCW, 1, 0);
861
862 pcsCleanup->EmitLabel(pSkipCleanupLabel);
863 }
864 }
865#endif // MDA_SUPPORTED
866 }
867#endif // FEATURE_COMINTEROP
868
869 // <NOTE>
870 // The profiler helpers below must be called immediately before and after the call to the target.
871 // The debugger trace call helpers are invoked from StubRareDisableWorker
872 // </NOTE>
873
874#if defined(PROFILING_SUPPORTED)
875 DWORD dwMethodDescLocalNum = -1;
876
877 // Notify the profiler of call out of the runtime
878 if (!SF_IsReverseCOMStub(m_dwStubFlags) && (CORProfilerTrackTransitions() || SF_IsNGENedStubForProfiling(m_dwStubFlags)))
879 {
880 dwMethodDescLocalNum = m_slIL.EmitProfilerBeginTransitionCallback(pcsDispatch, m_dwStubFlags);
881 _ASSERTE(dwMethodDescLocalNum != -1);
882 }
883#endif // PROFILING_SUPPORTED
884
885#ifdef MDA_SUPPORTED
886 if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
887 MDA_GET_ASSISTANT(GcManagedToUnmanaged))
888 {
889 m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
890 }
891#endif // MDA_SUPPORTED
892
893 // For CoreClr, clear the last error before calling the target that returns last error.
894 // There isn't always a way to know the function have failed without checking last error,
895 // in particular on Unix.
896 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
897 {
898 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CLEAR_LAST_ERROR, 0, 0);
899 }
900
901 // Invoke the target (calli, call method, call delegate, get/set field, etc.)
902 EmitInvokeTarget(pStubMD);
903
904 // Saving last error must be the first thing we do after returning from the target
905 if (m_fSetLastError && SF_IsForwardStub(m_dwStubFlags))
906 {
907 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__SET_LAST_ERROR, 0, 0);
908 }
909
910#if defined(_TARGET_X86_)
911 if (SF_IsForwardDelegateStub(m_dwStubFlags))
912 {
913 // the delegate may have an intercept stub attached to its sync block so we should
914 // prevent it from being garbage collected when the call is in progress
915 pcsDispatch->EmitLoadThis();
916 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
917 }
918#endif // defined(_TARGET_X86_)
919
920#ifdef MDA_SUPPORTED
921 if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsNGENedStub(m_dwStubFlags) &&
922 MDA_GET_ASSISTANT(GcUnmanagedToManaged))
923 {
924 m_slIL.EmitCallGcCollectForMDA(pcsDispatch, m_dwStubFlags);
925 }
926#endif // MDA_SUPPORTED
927
928#ifdef VERIFY_HEAP
929 if (SF_IsForwardStub(m_dwStubFlags) && g_pConfig->InteropValidatePinnedObjects())
930 {
931 // call StubHelpers.ValidateObject/StubHelpers.ValidateByref on pinned locals
932 m_slIL.EmitObjectValidation(pcsDispatch, m_dwStubFlags);
933 }
934#endif // VERIFY_HEAP
935
936#if defined(PROFILING_SUPPORTED)
937 // Notify the profiler of return back into the runtime
938 if (dwMethodDescLocalNum != -1)
939 {
940 m_slIL.EmitProfilerEndTransitionCallback(pcsDispatch, m_dwStubFlags, dwMethodDescLocalNum);
941 }
942#endif // PROFILING_SUPPORTED
943
944#ifdef FEATURE_COMINTEROP
945 if (SF_IsForwardCOMStub(m_dwStubFlags))
946 {
947 // Make sure that the RCW stays alive for the duration of the call. Note that if we do HRESULT
948 // swapping, we'll pass 'this' to GetCOMHRExceptionObject after returning from the target so
949 // GC.KeepAlive is not necessary.
950 if (!SF_IsHRESULTSwapping(m_dwStubFlags))
951 {
952 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
953 pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
954 }
955 }
956#endif // FEATURE_COMINTEROP
957
958 if (SF_IsHRESULTSwapping(m_dwStubFlags))
959 {
960 if (SF_IsForwardStub(m_dwStubFlags))
961 {
962 ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
963
964 pcsDispatch->EmitDUP();
965 pcsDispatch->EmitLDC(0);
966 pcsDispatch->EmitBGE(pSkipThrowLabel);
967
968#ifdef FEATURE_COMINTEROP
969 if (SF_IsCOMStub(m_dwStubFlags))
970 {
971 m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
972 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
973
974 if (SF_IsWinRTStub(m_dwStubFlags))
975 {
976 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT_WINRT, 3, 1);
977 }
978 else
979 {
980 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT, 3, 1);
981 }
982 }
983 else
984#endif // FEATURE_COMINTEROP
985 {
986 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_HR_EXCEPTION_OBJECT, 1, 1);
987 }
988
989 pcsDispatch->EmitTHROW();
990 pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
991 pcsDispatch->EmitLabel(pSkipThrowLabel);
992 pcsDispatch->EmitPOP();
993 }
994 }
995
996 m_slIL.End(m_dwStubFlags);
997 if (!hasTryCatchForHRESULT) // we will 'leave' the try scope and then 'ret' from outside
998 {
999 pcsUnmarshal->EmitRET();
1000 }
1001
1002 CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB);
1003
1004 if (m_slIL.HasInteropParamExceptionInfo())
1005 {
1006 // This code will not use the secret parameter, so we do not
1007 // tell the JIT to bother with it.
1008 m_slIL.ClearCode();
1009 m_slIL.GenerateInteropParamException(pcsMarshal);
1010 }
1011 else if (SF_IsFieldGetterStub(m_dwStubFlags) || SF_IsFieldSetterStub(m_dwStubFlags))
1012 {
1013 // Field access stubs are not shared and do not use the secret parameter.
1014 }
1015#ifndef _WIN64
1016 else if (SF_IsForwardDelegateStub(m_dwStubFlags) ||
1017 (SF_IsForwardCOMStub(m_dwStubFlags) && SF_IsWinRTDelegateStub(m_dwStubFlags)))
1018 {
1019 // Forward delegate stubs get all the context they need in 'this' so they
1020 // don't use the secret parameter. Except for AMD64 where we use the secret
1021 // argument to pass the real target to the stub-for-host.
1022 }
1023#endif // !_WIN64
1024 else
1025 {
1026 // All other IL stubs will need to use the secret parameter.
1027 jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM);
1028 }
1029
1030 if (SF_IsReverseStub(m_dwStubFlags))
1031 {
1032 SwapStubSignatures(pStubMD);
1033 }
1034
1035 ILCodeLabel* pTryEndAndCatchBeginLabel = NULL; // try ends at the same place the catch begins
1036 ILCodeLabel* pCatchEndAndReturnLabel = NULL; // catch ends at the same place we resume afterwards
1037#ifdef FEATURE_COMINTEROP
1038 if (hasTryCatchForHRESULT)
1039 {
1040 EmitExceptionHandler(&nativeReturnType, &managedReturnType, &pTryEndAndCatchBeginLabel, &pCatchEndAndReturnLabel);
1041 }
1042#endif // FEATURE_COMINTEROP
1043
1044 UINT maxStack;
1045 size_t cbCode;
1046 DWORD cbSig;
1047 BYTE * pbBuffer;
1048 BYTE * pbLocalSig;
1049
1050 cbCode = m_slIL.Link(&maxStack);
1051 cbSig = m_slIL.GetLocalSigSize();
1052
1053 ILStubResolver * pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
1054 COR_ILMETHOD_DECODER * pILHeader = pResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
1055 pbBuffer = (BYTE *)pILHeader->Code;
1056 pbLocalSig = (BYTE *)pILHeader->LocalVarSig;
1057 _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
1058
1059 ILStubEHClause cleanupTryFinally = { 0 };
1060 ILStubEHClause convertToHRTryCatch = { 0 };
1061 m_slIL.GetCleanupFinallyOffsets(&cleanupTryFinally);
1062
1063#ifdef FEATURE_COMINTEROP
1064 if (hasTryCatchForHRESULT)
1065 {
1066 convertToHRTryCatch.kind = ILStubEHClause::kTypedCatch;
1067 convertToHRTryCatch.dwTryBeginOffset = 0;
1068 convertToHRTryCatch.dwHandlerBeginOffset = ((DWORD)pTryEndAndCatchBeginLabel->GetCodeOffset());
1069 convertToHRTryCatch.cbTryLength = convertToHRTryCatch.dwHandlerBeginOffset - convertToHRTryCatch.dwTryBeginOffset;
1070 convertToHRTryCatch.cbHandlerLength = ((DWORD)pCatchEndAndReturnLabel->GetCodeOffset()) - convertToHRTryCatch.dwHandlerBeginOffset;
1071 convertToHRTryCatch.dwTypeToken = pcsDispatch->GetToken(g_pObjectClass);
1072 }
1073#endif // FEATURE_COMINTEROP
1074
1075 int nEHClauses = 0;
1076
1077 if (convertToHRTryCatch.cbHandlerLength != 0)
1078 nEHClauses++;
1079
1080 if (cleanupTryFinally.cbHandlerLength != 0)
1081 nEHClauses++;
1082
1083 if (nEHClauses > 0)
1084 {
1085 COR_ILMETHOD_SECT_EH* pEHSect = pResolver->AllocEHSect(nEHClauses);
1086 PopulateEHSect(pEHSect, nEHClauses, &cleanupTryFinally, &convertToHRTryCatch);
1087 }
1088
1089 m_slIL.GenerateCode(pbBuffer, cbCode);
1090 m_slIL.GetLocalSig(pbLocalSig, cbSig);
1091
1092 pResolver->SetJitFlags(jitFlags);
1093
1094#ifdef LOGGING
1095 LOG((LF_STUBS, LL_INFO1000, "---------------------------------------------------------------------\n"));
1096 LOG((LF_STUBS, LL_INFO1000, "NDirect IL stub dump: %s::%s\n", pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
1097 if (LoggingEnabled() && LoggingOn(LF_STUBS, LL_INFO1000))
1098 {
1099 CQuickBytes qbManaged;
1100 CQuickBytes qbLocal;
1101
1102 PCCOR_SIGNATURE pManagedSig;
1103 ULONG cManagedSig;
1104
1105 IMDInternalImport* pIMDI = MscorlibBinder::GetModule()->GetMDImport();
1106
1107 pStubMD->GetSig(&pManagedSig, &cManagedSig);
1108
1109 PrettyPrintSig(pManagedSig, cManagedSig, "*", &qbManaged, pStubMD->GetMDImport(), NULL);
1110 PrettyPrintSig(pbLocalSig, cbSig, NULL, &qbLocal, pIMDI, NULL);
1111
1112 LOG((LF_STUBS, LL_INFO1000, "incoming managed sig: %p: %s\n", pManagedSig, qbManaged.Ptr()));
1113 LOG((LF_STUBS, LL_INFO1000, "locals sig: %p: %s\n", pbLocalSig+1, qbLocal.Ptr()));
1114
1115 if (cleanupTryFinally.cbHandlerLength != 0)
1116 {
1117 LOG((LF_STUBS, LL_INFO1000, "try_begin: 0x%04x try_end: 0x%04x finally_begin: 0x%04x finally_end: 0x%04x \n",
1118 cleanupTryFinally.dwTryBeginOffset, cleanupTryFinally.dwTryBeginOffset + cleanupTryFinally.cbTryLength,
1119 cleanupTryFinally.dwHandlerBeginOffset, cleanupTryFinally.dwHandlerBeginOffset + cleanupTryFinally.cbHandlerLength));
1120 }
1121 if (convertToHRTryCatch.cbHandlerLength != 0)
1122 {
1123 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",
1124 convertToHRTryCatch.dwTryBeginOffset, convertToHRTryCatch.dwTryBeginOffset + convertToHRTryCatch.cbTryLength,
1125 convertToHRTryCatch.dwHandlerBeginOffset, convertToHRTryCatch.dwHandlerBeginOffset + convertToHRTryCatch.cbHandlerLength,
1126 convertToHRTryCatch.dwTypeToken));
1127 }
1128
1129 LogILStubFlags(LF_STUBS, LL_INFO1000, m_dwStubFlags);
1130
1131 m_slIL.LogILStub(jitFlags);
1132 }
1133 LOG((LF_STUBS, LL_INFO1000, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"));
1134#endif // LOGGING
1135
1136 //
1137 // Publish ETW events for IL stubs
1138 //
1139
1140 // If the category and the event is enabled...
1141 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ILStubGenerated))
1142 {
1143 EtwOnILStubGenerated(
1144 pStubMD,
1145 pbLocalSig,
1146 cbSig,
1147 jitFlags,
1148 &convertToHRTryCatch,
1149 &cleanupTryFinally,
1150 maxStack,
1151 (DWORD)cbCode
1152 );
1153 }
1154
1155 }
1156
1157 //---------------------------------------------------------------------------------------
1158 //
1159 void
1160 EtwOnILStubGenerated(
1161 MethodDesc * pStubMD,
1162 PCCOR_SIGNATURE pbLocalSig,
1163 DWORD cbSig,
1164 CORJIT_FLAGS jitFlags,
1165 ILStubEHClause * pConvertToHRTryCatchBounds,
1166 ILStubEHClause * pCleanupTryFinallyBounds,
1167 DWORD maxStack,
1168 DWORD cbCode)
1169 {
1170 STANDARD_VM_CONTRACT;
1171
1172 //
1173 // Interop Method Information
1174 //
1175 MethodDesc *pTargetMD = m_slIL.GetTargetMD();
1176 SString strNamespaceOrClassName, strMethodName, strMethodSignature;
1177 UINT64 uModuleId = 0;
1178
1179 if (pTargetMD)
1180 {
1181 pTargetMD->GetMethodInfoWithNewSig(strNamespaceOrClassName, strMethodName, strMethodSignature);
1182 uModuleId = (UINT64)pTargetMD->GetModule()->GetAddrModuleID();
1183 }
1184
1185 //
1186 // Stub Method Signature
1187 //
1188 SString stubNamespaceOrClassName, stubMethodName, stubMethodSignature;
1189 pStubMD->GetMethodInfoWithNewSig(stubNamespaceOrClassName, stubMethodName, stubMethodSignature);
1190
1191 IMDInternalImport *pStubImport = pStubMD->GetModule()->GetMDImport();
1192
1193 CQuickBytes qbLocal;
1194 PrettyPrintSig(pbLocalSig, (DWORD)cbSig, NULL, &qbLocal, pStubImport, NULL);
1195
1196 SString strLocalSig(SString::Utf8, (LPCUTF8)qbLocal.Ptr());
1197
1198 //
1199 // Native Signature
1200 //
1201 SString strNativeSignature(SString::Utf8);
1202 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
1203 {
1204 // Reverse interop. Use StubSignature
1205 strNativeSignature = stubMethodSignature;
1206 }
1207 else
1208 {
1209 // Forward interop. Use StubTarget siganture
1210 PCCOR_SIGNATURE pCallTargetSig = GetStubTargetMethodSig();
1211 DWORD cCallTargetSig = GetStubTargetMethodSigLength();
1212
1213 CQuickBytes qbCallTargetSig;
1214
1215 PrettyPrintSig(pCallTargetSig, cCallTargetSig, "", &qbCallTargetSig, pStubImport, NULL);
1216
1217 strNativeSignature.SetUTF8((LPCUTF8)qbCallTargetSig.Ptr());
1218 }
1219
1220 //
1221 // Dump IL stub code
1222 //
1223 SString strILStubCode;
1224 strILStubCode.Preallocate(4096); // Preallocate 4K bytes to avoid unnecessary growth
1225
1226 SString codeSizeFormat;
1227 codeSizeFormat.LoadResource(CCompRC::Optional, IDS_EE_INTEROP_CODE_SIZE_COMMENT);
1228 strILStubCode.AppendPrintf(W("// %s\t%d (0x%04x)\n"), codeSizeFormat.GetUnicode(), cbCode, cbCode);
1229 strILStubCode.AppendPrintf(W(".maxstack %d \n"), maxStack);
1230 strILStubCode.AppendPrintf(W(".locals %s\n"), strLocalSig.GetUnicode());
1231
1232 m_slIL.LogILStub(jitFlags, &strILStubCode);
1233
1234 if (pConvertToHRTryCatchBounds->cbTryLength != 0 && pConvertToHRTryCatchBounds->cbHandlerLength != 0)
1235 {
1236 strILStubCode.AppendPrintf(
1237 W(".try IL_%04x to IL_%04x catch handler IL_%04x to IL_%04x\n"),
1238 pConvertToHRTryCatchBounds->dwTryBeginOffset,
1239 pConvertToHRTryCatchBounds->dwTryBeginOffset + pConvertToHRTryCatchBounds->cbTryLength,
1240 pConvertToHRTryCatchBounds->dwHandlerBeginOffset,
1241 pConvertToHRTryCatchBounds->dwHandlerBeginOffset + pConvertToHRTryCatchBounds->cbHandlerLength);
1242 }
1243
1244 if (pCleanupTryFinallyBounds->cbTryLength != 0 && pCleanupTryFinallyBounds->cbHandlerLength != 0)
1245 {
1246 strILStubCode.AppendPrintf(
1247 W(".try IL_%04x to IL_%04x finally handler IL_%04x to IL_%04x\n"),
1248 pCleanupTryFinallyBounds->dwTryBeginOffset,
1249 pCleanupTryFinallyBounds->dwTryBeginOffset + pCleanupTryFinallyBounds->cbTryLength,
1250 pCleanupTryFinallyBounds->dwHandlerBeginOffset,
1251 pCleanupTryFinallyBounds->dwHandlerBeginOffset + pCleanupTryFinallyBounds->cbHandlerLength);
1252 }
1253
1254 //
1255 // Fire the event
1256 //
1257 DWORD dwFlags = 0;
1258 if (m_dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)
1259 dwFlags |= ETW_IL_STUB_FLAGS_REVERSE_INTEROP;
1260#ifdef FEATURE_COMINTEROP
1261 if (m_dwStubFlags & NDIRECTSTUB_FL_COM)
1262 dwFlags |= ETW_IL_STUB_FLAGS_COM_INTEROP;
1263#endif // FEATURE_COMINTEROP
1264 if (m_dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUB)
1265 dwFlags |= ETW_IL_STUB_FLAGS_NGENED_STUB;
1266 if (m_dwStubFlags & NDIRECTSTUB_FL_DELEGATE)
1267 dwFlags |= ETW_IL_STUB_FLAGS_DELEGATE;
1268 if (m_dwStubFlags & NDIRECTSTUB_FL_CONVSIGASVARARG)
1269 dwFlags |= ETW_IL_STUB_FLAGS_VARARG;
1270 if (m_dwStubFlags & NDIRECTSTUB_FL_UNMANAGED_CALLI)
1271 dwFlags |= ETW_IL_STUB_FLAGS_UNMANAGED_CALLI;
1272
1273 DWORD dwToken = 0;
1274 if (pTargetMD)
1275 dwToken = pTargetMD->GetMemberDef();
1276
1277
1278 //
1279 // Truncate string fields. Make sure the whole event is less than 64KB
1280 //
1281 TruncateUnicodeString(strNamespaceOrClassName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1282 TruncateUnicodeString(strMethodName, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1283 TruncateUnicodeString(strMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1284 TruncateUnicodeString(strNativeSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1285 TruncateUnicodeString(stubMethodSignature, ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE);
1286 TruncateUnicodeString(strILStubCode, ETW_IL_STUB_EVENT_CODE_STRING_FIELD_MAXSIZE);
1287
1288 //
1289 // Fire ETW event
1290 //
1291 FireEtwILStubGenerated(
1292 GetClrInstanceId(), // ClrInstanceId
1293 uModuleId, // ModuleIdentifier
1294 (UINT64)pStubMD, // StubMethodIdentifier
1295 dwFlags, // StubFlags
1296 dwToken, // ManagedInteropMethodToken
1297 strNamespaceOrClassName.GetUnicode(), // ManagedInteropMethodNamespace
1298 strMethodName.GetUnicode(), // ManagedInteropMethodName
1299 strMethodSignature.GetUnicode(), // ManagedInteropMethodSignature
1300 strNativeSignature.GetUnicode(), // NativeSignature
1301 stubMethodSignature.GetUnicode(), // StubMethodSigature
1302 strILStubCode.GetUnicode() // StubMethodILCode
1303 );
1304 } // EtwOnILStubGenerated
1305
1306#ifdef LOGGING
1307 //---------------------------------------------------------------------------------------
1308 //
1309 static inline void LogOneFlag(DWORD flags, DWORD flag, LPCSTR str, DWORD facility, DWORD level)
1310 {
1311 LIMITED_METHOD_CONTRACT;
1312 if (flags & flag)
1313 {
1314 LOG((facility, level, str));
1315 }
1316 }
1317
1318 static void LogILStubFlags(DWORD facility, DWORD level, DWORD dwStubFlags)
1319 {
1320 LIMITED_METHOD_CONTRACT;
1321 LOG((facility, level, "dwStubFlags: 0x%08x\n", dwStubFlags));
1322 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_CONVSIGASVARARG, " NDIRECTSTUB_FL_CONVSIGASVARARG\n", facility, level);
1323 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_BESTFIT, " NDIRECTSTUB_FL_BESTFIT\n", facility, level);
1324 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR, " NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR\n", facility, level);
1325 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUB, " NDIRECTSTUB_FL_NGENEDSTUB\n", facility, level);
1326 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DELEGATE, " NDIRECTSTUB_FL_DELEGATE\n", facility, level);
1327 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_DOHRESULTSWAPPING, " NDIRECTSTUB_FL_DOHRESULTSWAPPING\n", facility, level);
1328 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_REVERSE_INTEROP, " NDIRECTSTUB_FL_REVERSE_INTEROP\n", facility, level);
1329#ifdef FEATURE_COMINTEROP
1330 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_COM, " NDIRECTSTUB_FL_COM\n", facility, level);
1331#endif // FEATURE_COMINTEROP
1332 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING, " NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING\n", facility, level);
1333 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL, " NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL\n", facility, level);
1334 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_UNMANAGED_CALLI, " NDIRECTSTUB_FL_UNMANAGED_CALLI\n", facility, level);
1335 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_TRIGGERCCTOR, " NDIRECTSTUB_FL_TRIGGERCCTOR\n", facility, level);
1336#ifdef FEATURE_COMINTEROP
1337 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDGETTER, " NDIRECTSTUB_FL_FIELDGETTER\n", facility, level);
1338 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_FIELDSETTER, " NDIRECTSTUB_FL_FIELDSETTER\n", facility, level);
1339 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRT, " NDIRECTSTUB_FL_WINRT\n", facility, level);
1340 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTDELEGATE, " NDIRECTSTUB_FL_WINRTDELEGATE\n", facility, level);
1341 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSHAREDGENERIC, " NDIRECTSTUB_FL_WINRTSHAREDGENERIC\n", facility, level);
1342 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCTOR, " NDIRECTSTUB_FL_WINRTCTOR\n", facility, level);
1343 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTCOMPOSITION, " NDIRECTSTUB_FL_WINRTCOMPOSITION\n", facility, level);
1344 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTSTATIC, " NDIRECTSTUB_FL_WINRTSTATIC\n", facility, level);
1345 LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_WINRTHASREDIRECTION, " NDIRECTSTUB_FL_WINRTHASREDIRECTION\n", facility, level);
1346#endif // FEATURE_COMINTEROP
1347
1348 //
1349 // no need to log the internal flags, let's just assert what we expect to see...
1350 //
1351 CONSISTENCY_CHECK(!SF_IsCOMLateBoundStub(dwStubFlags));
1352 CONSISTENCY_CHECK(!SF_IsCOMEventCallStub(dwStubFlags));
1353
1354 DWORD dwKnownMask =
1355 NDIRECTSTUB_FL_CONVSIGASVARARG |
1356 NDIRECTSTUB_FL_BESTFIT |
1357 NDIRECTSTUB_FL_THROWONUNMAPPABLECHAR |
1358 NDIRECTSTUB_FL_NGENEDSTUB |
1359 NDIRECTSTUB_FL_DELEGATE |
1360 NDIRECTSTUB_FL_DOHRESULTSWAPPING |
1361 NDIRECTSTUB_FL_REVERSE_INTEROP |
1362 NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING |
1363 NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL |
1364 NDIRECTSTUB_FL_UNMANAGED_CALLI |
1365 NDIRECTSTUB_FL_TRIGGERCCTOR |
1366#ifdef FEATURE_COMINTEROP
1367 NDIRECTSTUB_FL_COM |
1368 NDIRECTSTUB_FL_COMLATEBOUND | // internal
1369 NDIRECTSTUB_FL_COMEVENTCALL | // internal
1370 NDIRECTSTUB_FL_FIELDGETTER |
1371 NDIRECTSTUB_FL_FIELDSETTER |
1372 NDIRECTSTUB_FL_WINRT |
1373 NDIRECTSTUB_FL_WINRTDELEGATE |
1374 NDIRECTSTUB_FL_WINRTCTOR |
1375 NDIRECTSTUB_FL_WINRTCOMPOSITION |
1376 NDIRECTSTUB_FL_WINRTSTATIC |
1377 NDIRECTSTUB_FL_WINRTHASREDIRECTION |
1378#endif // FEATURE_COMINTEROP
1379 NULL;
1380
1381 DWORD dwUnknownFlags = dwStubFlags & ~dwKnownMask;
1382 if (0 != dwUnknownFlags)
1383 {
1384 LOG((facility, level, "UNKNOWN FLAGS: 0x%08x\n", dwUnknownFlags));
1385 }
1386 }
1387#endif // LOGGING
1388
1389 PCCOR_SIGNATURE GetStubTargetMethodSig()
1390 {
1391 CONTRACT(PCCOR_SIGNATURE)
1392 {
1393 STANDARD_VM_CHECK;
1394 POSTCONDITION(CheckPointer(RETVAL, NULL_NOT_OK));
1395 }
1396 CONTRACT_END;
1397
1398 BYTE *pb;
1399
1400 if (!m_qbNativeFnSigBuffer.Size())
1401 {
1402 DWORD cb = m_slIL.GetStubTargetMethodSigSize();
1403 pb = (BYTE *)m_qbNativeFnSigBuffer.AllocThrows(cb);
1404
1405 m_slIL.GetStubTargetMethodSig(pb, cb);
1406 }
1407 else
1408 {
1409 pb = (BYTE*)m_qbNativeFnSigBuffer.Ptr();
1410 }
1411
1412 RETURN pb;
1413 }
1414
1415 DWORD
1416 GetStubTargetMethodSigLength()
1417 {
1418 WRAPPER_NO_CONTRACT;
1419
1420 return m_slIL.GetStubTargetMethodSigSize();
1421 }
1422
1423 void SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig)
1424 {
1425 WRAPPER_NO_CONTRACT;
1426
1427 m_slIL.SetStubTargetMethodSig(pSig, cSig);
1428 m_qbNativeFnSigBuffer.Shrink(0);
1429 }
1430
1431 TokenLookupMap* GetTokenLookupMap() { WRAPPER_NO_CONTRACT; return m_slIL.GetTokenLookupMap(); }
1432
1433protected:
1434 CQuickBytes m_qbNativeFnSigBuffer;
1435 NDirectStubLinker m_slIL;
1436 BOOL m_fSetLastError;
1437 DWORD m_dwStubFlags;
1438};
1439
1440
1441class PInvoke_ILStubState : public ILStubState
1442{
1443public:
1444
1445 PInvoke_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1446 CorPinvokeMap unmgdCallConv, int iLCIDParamIdx, MethodDesc* pTargetMD)
1447 : ILStubState(
1448 pStubModule,
1449 signature,
1450 pTypeContext,
1451 TargetHasThis(dwStubFlags),
1452 StubHasThis(dwStubFlags),
1453 dwStubFlags,
1454 iLCIDParamIdx,
1455 pTargetMD)
1456 {
1457 STANDARD_VM_CONTRACT;
1458
1459 if (SF_IsForwardStub(dwStubFlags))
1460 {
1461 m_slIL.SetCallingConvention(unmgdCallConv, SF_IsVarArgStub(dwStubFlags));
1462 }
1463 }
1464
1465private:
1466 static BOOL TargetHasThis(DWORD dwStubFlags)
1467 {
1468 //
1469 // in reverse pinvoke on delegate, the managed target will
1470 // have a 'this' pointer, but the unmanaged signature does
1471 // not.
1472 //
1473 return SF_IsReverseDelegateStub(dwStubFlags);
1474 }
1475
1476 static BOOL StubHasThis(DWORD dwStubFlags)
1477 {
1478 //
1479 // in forward pinvoke on a delegate, the stub will have a
1480 // 'this' pointer, but the unmanaged target will not.
1481 //
1482 return SF_IsForwardDelegateStub(dwStubFlags);
1483 }
1484};
1485
1486#ifdef FEATURE_COMINTEROP
1487class CLRToCOM_ILStubState : public ILStubState
1488{
1489public:
1490
1491 CLRToCOM_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1492 int iLCIDParamIdx, MethodDesc* pTargetMD)
1493 : ILStubState(
1494 pStubModule,
1495 signature,
1496 pTypeContext,
1497 TRUE,
1498 !SF_IsWinRTStaticStub(dwStubFlags), // fStubHasThis
1499 dwStubFlags,
1500 iLCIDParamIdx,
1501 pTargetMD)
1502 {
1503 STANDARD_VM_CONTRACT;
1504
1505 if (SF_IsForwardStub(dwStubFlags))
1506 {
1507 m_slIL.SetCallingConvention(pmCallConvStdcall, SF_IsVarArgStub(dwStubFlags));
1508 }
1509 }
1510
1511 void BeginEmit(DWORD dwStubFlags) // CLR to COM IL
1512 {
1513 STANDARD_VM_CONTRACT;
1514
1515 ILStubState::BeginEmit(dwStubFlags);
1516
1517 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
1518
1519 // add the 'this' COM IP parameter to the target CALLI
1520 m_slIL.GetMarshalCodeStream()->SetStubTargetArgType(ELEMENT_TYPE_I, false);
1521
1522 // convert 'this' to COM IP and the target method entry point
1523 m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
1524
1525#ifdef _TARGET_64BIT_
1526 if (SF_IsWinRTDelegateStub(m_dwStubFlags))
1527 {
1528 // write the stub context (EEImplMethodDesc representing the Invoke)
1529 // into the secret arg so it shows up in the InlinedCallFrame and can
1530 // be used by stub for host
1531
1532 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
1533 m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
1534 pcsDispatch->EmitSTIND_I();
1535 pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
1536 }
1537 else
1538#endif // _TARGET_64BIT_
1539 {
1540 m_slIL.EmitLoadStubContext(pcsDispatch, dwStubFlags);
1541 }
1542
1543 pcsDispatch->EmitLDLOCA(m_slIL.GetTargetEntryPointLocalNum());
1544
1545 BinderMethodID getCOMIPMethod;
1546 bool fDoPostCallIPCleanup = true;
1547
1548 if (SF_IsWinRTStub(dwStubFlags))
1549 {
1550 // WinRT uses optimized helpers
1551 if (SF_IsWinRTSharedGenericStub(dwStubFlags))
1552 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_SHARED_GENERIC;
1553 else if (SF_IsWinRTDelegateStub(dwStubFlags))
1554 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT_DELEGATE;
1555 else
1556 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW_WINRT;
1557
1558 // GetCOMIPFromRCW_WinRT, GetCOMIPFromRCW_WinRTSharedGeneric, and GetCOMIPFromRCW_WinRTDelegate
1559 // always cache the COM interface pointer so no post-call cleanup is needed
1560 fDoPostCallIPCleanup = false;
1561 }
1562 else
1563 {
1564 // classic COM interop uses the non-optimized helper
1565 getCOMIPMethod = METHOD__STUBHELPERS__GET_COM_IP_FROM_RCW;
1566 }
1567
1568 DWORD dwIPRequiresCleanupLocalNum = (DWORD)-1;
1569 if (fDoPostCallIPCleanup)
1570 {
1571 dwIPRequiresCleanupLocalNum = pcsDispatch->NewLocal(ELEMENT_TYPE_BOOLEAN);
1572 pcsDispatch->EmitLDLOCA(dwIPRequiresCleanupLocalNum);
1573
1574 // StubHelpers.GetCOMIPFromRCW(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget, out bool pfNeedsRelease)
1575 pcsDispatch->EmitCALL(getCOMIPMethod, 4, 1);
1576 }
1577 else
1578 {
1579 // StubHelpers.GetCOMIPFromRCW_WinRT*(object objSrc, IntPtr pCPCMD, out IntPtr ppTarget)
1580 pcsDispatch->EmitCALL(getCOMIPMethod, 3, 1);
1581 }
1582
1583
1584 // save it because we'll need it to compute the CALLI target and release it
1585 pcsDispatch->EmitDUP();
1586 pcsDispatch->EmitSTLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1587
1588 if (fDoPostCallIPCleanup)
1589 {
1590 // make sure it's Release()'ed after the call
1591 m_slIL.SetCleanupNeeded();
1592 ILCodeStream *pcsCleanup = m_slIL.GetCleanupCodeStream();
1593
1594 ILCodeLabel *pSkipThisCleanup = pcsCleanup->NewCodeLabel();
1595
1596 // and if it requires cleanup (i.e. it's not taken from the RCW cache)
1597 pcsCleanup->EmitLDLOC(dwIPRequiresCleanupLocalNum);
1598 pcsCleanup->EmitBRFALSE(pSkipThisCleanup);
1599
1600 pcsCleanup->EmitLDLOC(m_slIL.GetTargetInterfacePointerLocalNum());
1601 pcsCleanup->EmitCALL(METHOD__INTERFACEMARSHALER__CLEAR_NATIVE, 1, 0);
1602 pcsCleanup->EmitLabel(pSkipThisCleanup);
1603 }
1604 }
1605};
1606
1607class COMToCLR_ILStubState : public ILStubState
1608{
1609public:
1610
1611 COMToCLR_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags,
1612 int iLCIDParamIdx, MethodDesc* pTargetMD)
1613 : ILStubState(
1614 pStubModule,
1615 signature,
1616 pTypeContext,
1617 TRUE,
1618 TRUE,
1619 dwStubFlags,
1620 iLCIDParamIdx,
1621 pTargetMD)
1622 {
1623 STANDARD_VM_CONTRACT;
1624 }
1625
1626 void BeginEmit(DWORD dwStubFlags) // COM to CLR IL
1627 {
1628 STANDARD_VM_CONTRACT;
1629
1630 ILStubState::BeginEmit(dwStubFlags);
1631
1632 if (SF_IsWinRTStaticStub(dwStubFlags))
1633 {
1634 // we are not loading 'this' because the target is static
1635 m_slIL.AdjustTargetStackDeltaForExtraParam();
1636 }
1637 else
1638 {
1639 // load this
1640 m_slIL.GetDispatchCodeStream()->EmitLoadThis();
1641 }
1642 }
1643
1644 void MarshalFactoryReturn()
1645 {
1646 CONTRACTL
1647 {
1648 STANDARD_VM_CHECK;
1649 PRECONDITION(SF_IsWinRTCtorStub(m_dwStubFlags));
1650 }
1651 CONTRACTL_END;
1652
1653 ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream();
1654 ILCodeStream *pcsDispatch = m_slIL.GetDispatchCodeStream();
1655 ILCodeStream *pcsUnmarshal = m_slIL.GetReturnUnmarshalCodeStream();
1656 ILCodeStream *pcsExCleanup = m_slIL.GetExceptionCleanupCodeStream();
1657
1658 LocalDesc locDescFactoryRetVal(ELEMENT_TYPE_I);
1659 DWORD dwFactoryRetValLocalNum = pcsSetup->NewLocal(locDescFactoryRetVal);
1660 pcsSetup->EmitLoadNullPtr();
1661 pcsSetup->EmitSTLOC(dwFactoryRetValLocalNum);
1662
1663 locDescFactoryRetVal.MakeByRef();
1664
1665 // expect one additional argument - pointer to a location that receives the created instance
1666 DWORD dwRetValArgNum = pcsDispatch->SetStubTargetArgType(&locDescFactoryRetVal, false);
1667 m_slIL.AdjustTargetStackDeltaForExtraParam();
1668
1669 // convert 'this' to an interface pointer corresponding to the default interface of this class
1670 pcsUnmarshal->EmitLoadThis();
1671 pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
1672 pcsUnmarshal->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_RETURN_VALUE, 2, 1);
1673 pcsUnmarshal->EmitSTLOC(dwFactoryRetValLocalNum);
1674
1675 // assign it to the location pointed to by the argument
1676 pcsUnmarshal->EmitLDARG(dwRetValArgNum);
1677 pcsUnmarshal->EmitLDLOC(dwFactoryRetValLocalNum);
1678 pcsUnmarshal->EmitSTIND_I();
1679
1680 // on exception, we want to release the IInspectable's and assign NULL to output locations
1681 m_slIL.SetExceptionCleanupNeeded();
1682
1683 EmitInterfaceClearNative(pcsExCleanup, dwFactoryRetValLocalNum);
1684
1685 // *retVal = NULL
1686 pcsExCleanup->EmitLDARG(dwRetValArgNum);
1687 pcsExCleanup->EmitLoadNullPtr();
1688 pcsExCleanup->EmitSTIND_I();
1689
1690 }
1691};
1692
1693class COMToCLRFieldAccess_ILStubState : public COMToCLR_ILStubState
1694{
1695public:
1696
1697 COMToCLRFieldAccess_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext,
1698 DWORD dwStubFlags, FieldDesc* pFD)
1699 : COMToCLR_ILStubState(
1700 pStubModule,
1701 signature,
1702 pTypeContext,
1703 dwStubFlags,
1704 -1,
1705 NULL)
1706 {
1707 STANDARD_VM_CONTRACT;
1708
1709 _ASSERTE(pFD != NULL);
1710 m_pFD = pFD;
1711 }
1712
1713 void EmitInvokeTarget(MethodDesc *pStubMD)
1714 {
1715 STANDARD_VM_CONTRACT;
1716
1717 ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream();
1718
1719 if (SF_IsFieldGetterStub(m_dwStubFlags))
1720 {
1721 pcsDispatch->EmitLDFLD(pcsDispatch->GetToken(m_pFD));
1722 }
1723 else
1724 {
1725 CONSISTENCY_CHECK(SF_IsFieldSetterStub(m_dwStubFlags));
1726 pcsDispatch->EmitSTFLD(pcsDispatch->GetToken(m_pFD));
1727 }
1728 }
1729
1730protected:
1731 FieldDesc *m_pFD;
1732};
1733#endif // FEATURE_COMINTEROP
1734
1735
1736NDirectStubLinker::NDirectStubLinker(
1737 DWORD dwStubFlags,
1738 Module* pModule,
1739 const Signature &signature,
1740 SigTypeContext *pTypeContext,
1741 MethodDesc* pTargetMD,
1742 int iLCIDParamIdx,
1743 BOOL fTargetHasThis,
1744 BOOL fStubHasThis)
1745 : ILStubLinker(pModule, signature, pTypeContext, pTargetMD, fTargetHasThis, fStubHasThis, !SF_IsCOMStub(dwStubFlags)),
1746 m_pCleanupFinallyBeginLabel(NULL),
1747 m_pCleanupFinallyEndLabel(NULL),
1748 m_pSkipExceptionCleanupLabel(NULL),
1749#ifdef FEATURE_COMINTEROP
1750 m_dwWinRTFactoryObjectLocalNum(-1),
1751#endif // FEATURE_COMINTEROP
1752 m_fHasCleanupCode(FALSE),
1753 m_fHasExceptionCleanupCode(FALSE),
1754 m_fCleanupWorkListIsSetup(FALSE),
1755 m_dwThreadLocalNum(-1),
1756 m_dwCleanupWorkListLocalNum(-1),
1757 m_dwRetValLocalNum(-1),
1758 m_ErrorResID(-1),
1759 m_ErrorParamIdx(-1),
1760 m_iLCIDParamIdx(iLCIDParamIdx),
1761 m_dwStubFlags(dwStubFlags)
1762{
1763 STANDARD_VM_CONTRACT;
1764
1765
1766 m_pcsSetup = NewCodeStream(ILStubLinker::kSetup); // do any one-time setup work
1767 m_pcsMarshal = NewCodeStream(ILStubLinker::kMarshal); // marshals arguments
1768 m_pcsDispatch = NewCodeStream(ILStubLinker::kDispatch); // sets up arguments and makes call
1769 m_pcsRetUnmarshal = NewCodeStream(ILStubLinker::kReturnUnmarshal); // unmarshals return value
1770 m_pcsUnmarshal = NewCodeStream(ILStubLinker::kUnmarshal); // unmarshals arguments
1771 m_pcsExceptionCleanup = NewCodeStream(ILStubLinker::kExceptionCleanup); // MAY NOT THROW: goes in a finally and does exception-only cleanup
1772 m_pcsCleanup = NewCodeStream(ILStubLinker::kCleanup); // MAY NOT THROW: goes in a finally and does unconditional cleanup
1773
1774 //
1775 // Add locals
1776 m_dwArgMarshalIndexLocalNum = NewLocal(ELEMENT_TYPE_I4);
1777 m_pcsMarshal->EmitLDC(0);
1778 m_pcsMarshal->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1779
1780#ifdef FEATURE_COMINTEROP
1781 //
1782 // Forward COM interop needs a local to hold target interface pointer
1783 //
1784 if (SF_IsForwardCOMStub(m_dwStubFlags))
1785 {
1786 m_dwTargetEntryPointLocalNum = NewLocal(ELEMENT_TYPE_I);
1787 m_dwTargetInterfacePointerLocalNum = NewLocal(ELEMENT_TYPE_I);
1788 m_pcsSetup->EmitLoadNullPtr();
1789 m_pcsSetup->EmitSTLOC(m_dwTargetInterfacePointerLocalNum);
1790 }
1791#endif // FEATURE_COMINTEROP
1792}
1793
1794void NDirectStubLinker::SetCallingConvention(CorPinvokeMap unmngCallConv, BOOL fIsVarArg)
1795{
1796 LIMITED_METHOD_CONTRACT;
1797 ULONG uNativeCallingConv = 0;
1798
1799#if !defined(_TARGET_X86_)
1800 if (fIsVarArg)
1801 {
1802 // The JIT has to use a different calling convention for unmanaged vararg targets on 64-bit and ARM:
1803 // any float values must be duplicated in the corresponding general-purpose registers.
1804 uNativeCallingConv = CORINFO_CALLCONV_NATIVEVARARG;
1805 }
1806 else
1807#endif // !_TARGET_X86_
1808 {
1809 switch (unmngCallConv)
1810 {
1811 case pmCallConvCdecl:
1812 uNativeCallingConv = CORINFO_CALLCONV_C;
1813 break;
1814 case pmCallConvStdcall:
1815 uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
1816 break;
1817 case pmCallConvThiscall:
1818 uNativeCallingConv = CORINFO_CALLCONV_THISCALL;
1819 break;
1820 default:
1821 _ASSERTE(!"Invalid calling convention.");
1822 uNativeCallingConv = CORINFO_CALLCONV_STDCALL;
1823 break;
1824 }
1825 }
1826
1827 SetStubTargetCallingConv((CorCallingConvention)uNativeCallingConv);
1828}
1829
1830void NDirectStubLinker::EmitSetArgMarshalIndex(ILCodeStream* pcsEmit, UINT uArgIdx)
1831{
1832 WRAPPER_NO_CONTRACT;
1833
1834 //
1835 // This sets our state local variable that tracks the progress of the stub execution.
1836 // In the finally block we test this variable to see what cleanup we need to do. The
1837 // variable starts with the value of 0 and is assigned the following values as the
1838 // stub executes:
1839 //
1840 // CLEANUP_INDEX_ARG0_MARSHAL + 1 - 1st argument marshaled
1841 // CLEANUP_INDEX_ARG0_MARSHAL + 2 - 2nd argument marshaled
1842 // ...
1843 // CLEANUP_INDEX_ARG0_MARSHAL + n - nth argument marshaled
1844 // CLEANUP_INDEX_RETVAL_UNMARSHAL + 1 - return value unmarshaled
1845 // CLEANUP_INDEX_ARG0_UNMARSHAL + 1 - 1st argument unmarshaled
1846 // CLEANUP_INDEX_ARG0_UNMARSHAL + 2 - 2nd argument unmarshaled
1847 // ...
1848 // CLEANUP_INDEX_ARG0_UNMARSHAL + n - nth argument unmarshaled
1849 // CLEANUP_INDEX_ALL_DONE + 1 - ran to completion, no exception thrown
1850 //
1851 // Note: There may be gaps, i.e. if say 2nd argument does not need cleanup, the
1852 // state variable will never be assigned the corresponding value. However, the
1853 // value must always monotonically increase so we can use <=, >, etc.
1854 //
1855
1856 pcsEmit->EmitLDC(uArgIdx + 1);
1857 pcsEmit->EmitSTLOC(m_dwArgMarshalIndexLocalNum);
1858}
1859
1860void NDirectStubLinker::EmitCheckForArgCleanup(ILCodeStream* pcsEmit, UINT uArgIdx, ArgCleanupBranchKind branchKind, ILCodeLabel* pSkipCleanupLabel)
1861{
1862 STANDARD_VM_CONTRACT;
1863
1864 SetCleanupNeeded();
1865
1866 // See EmitSetArgMarshalIndex.
1867 pcsEmit->EmitLDLOC(m_dwArgMarshalIndexLocalNum);
1868 pcsEmit->EmitLDC(uArgIdx);
1869
1870 switch (branchKind)
1871 {
1872 case BranchIfMarshaled:
1873 {
1874 // we branch to the label if the argument has been marshaled
1875 pcsEmit->EmitBGT(pSkipCleanupLabel);
1876 break;
1877 }
1878
1879 case BranchIfNotMarshaled:
1880 {
1881 // we branch to the label if the argument has not been marshaled
1882 pcsEmit->EmitBLE(pSkipCleanupLabel);
1883 break;
1884 }
1885
1886 default:
1887 UNREACHABLE();
1888 }
1889}
1890
1891int NDirectStubLinker::GetLCIDParamIdx()
1892{
1893 LIMITED_METHOD_CONTRACT;
1894 return m_iLCIDParamIdx;
1895}
1896
1897ILCodeStream* NDirectStubLinker::GetSetupCodeStream()
1898{
1899 LIMITED_METHOD_CONTRACT;
1900 return m_pcsSetup;
1901}
1902
1903ILCodeStream* NDirectStubLinker::GetMarshalCodeStream()
1904{
1905 LIMITED_METHOD_CONTRACT;
1906 return m_pcsMarshal;
1907}
1908
1909ILCodeStream* NDirectStubLinker::GetUnmarshalCodeStream()
1910{
1911 LIMITED_METHOD_CONTRACT;
1912 return m_pcsUnmarshal;
1913}
1914
1915ILCodeStream* NDirectStubLinker::GetReturnUnmarshalCodeStream()
1916{
1917 LIMITED_METHOD_CONTRACT;
1918 return m_pcsRetUnmarshal;
1919}
1920
1921ILCodeStream* NDirectStubLinker::GetDispatchCodeStream()
1922{
1923 LIMITED_METHOD_CONTRACT;
1924 return m_pcsDispatch;
1925}
1926
1927ILCodeStream* NDirectStubLinker::GetCleanupCodeStream()
1928{
1929 LIMITED_METHOD_CONTRACT;
1930 return m_pcsCleanup;
1931}
1932
1933ILCodeStream* NDirectStubLinker::GetExceptionCleanupCodeStream()
1934{
1935 LIMITED_METHOD_CONTRACT;
1936 return m_pcsExceptionCleanup;
1937}
1938
1939void NDirectStubLinker::AdjustTargetStackDeltaForExtraParam()
1940{
1941 LIMITED_METHOD_CONTRACT;
1942 //
1943 // Compensate for the extra parameter.
1944 //
1945 m_iTargetStackDelta++;
1946}
1947
1948void NDirectStubLinker::AdjustTargetStackDeltaForReverseInteropHRESULTSwapping()
1949{
1950 WRAPPER_NO_CONTRACT;
1951 //
1952 // In the case of reverse pinvoke, we build up the 'target'
1953 // signature as if it were normal forward pinvoke and then
1954 // switch that signature (representing the native sig) with
1955 // the stub's sig (representing the managed sig). However,
1956 // as a side-effect, our calcualted target stack delta is
1957 // wrong.
1958 //
1959 // The only way that we support a different stack delta is
1960 // through hresult swapping. So this code "undoes" the
1961 // deltas that would have been applied in that case.
1962 //
1963
1964 if (StubHasVoidReturnType())
1965 {
1966 //
1967 // If the managed return type is void, undo the HRESULT
1968 // return type added to our target sig for HRESULT swapping.
1969 // No extra argument will have been added because it makes
1970 // no sense to add an extry byref void argument.
1971 //
1972 m_iTargetStackDelta--;
1973 }
1974 else
1975 {
1976 //
1977 // no longer pop the extra byref argument from the stack
1978 //
1979 m_iTargetStackDelta++;
1980 }
1981}
1982
1983void NDirectStubLinker::SetInteropParamExceptionInfo(UINT resID, UINT paramIdx)
1984{
1985 LIMITED_METHOD_CONTRACT;
1986
1987 // only keep the first one
1988 if (HasInteropParamExceptionInfo())
1989 {
1990 return;
1991 }
1992
1993 m_ErrorResID = resID;
1994 m_ErrorParamIdx = paramIdx;
1995}
1996
1997bool NDirectStubLinker::HasInteropParamExceptionInfo()
1998{
1999 LIMITED_METHOD_CONTRACT;
2000
2001 return !(((DWORD)-1 == m_ErrorResID) && ((DWORD)-1 == m_ErrorParamIdx));
2002}
2003
2004void NDirectStubLinker::GenerateInteropParamException(ILCodeStream* pcsEmit)
2005{
2006 STANDARD_VM_CONTRACT;
2007
2008 pcsEmit->EmitLDC(m_ErrorResID);
2009 pcsEmit->EmitLDC(m_ErrorParamIdx);
2010 pcsEmit->EmitCALL(METHOD__STUBHELPERS__THROW_INTEROP_PARAM_EXCEPTION, 2, 0);
2011
2012 pcsEmit->EmitLDNULL();
2013 pcsEmit->EmitTHROW();
2014}
2015
2016#ifdef FEATURE_COMINTEROP
2017DWORD NDirectStubLinker::GetTargetInterfacePointerLocalNum()
2018{
2019 LIMITED_METHOD_CONTRACT;
2020 CONSISTENCY_CHECK(m_dwTargetInterfacePointerLocalNum != (DWORD)-1);
2021 return m_dwTargetInterfacePointerLocalNum;
2022}
2023DWORD NDirectStubLinker::GetTargetEntryPointLocalNum()
2024{
2025 LIMITED_METHOD_CONTRACT;
2026 CONSISTENCY_CHECK(m_dwTargetEntryPointLocalNum != (DWORD)-1);
2027 return m_dwTargetEntryPointLocalNum;
2028}
2029
2030void NDirectStubLinker::EmitLoadRCWThis(ILCodeStream *pcsEmit, DWORD dwStubFlags)
2031{
2032 STANDARD_VM_CONTRACT;
2033
2034 if (SF_IsForwardStub(dwStubFlags) &&
2035 (SF_IsWinRTCtorStub(dwStubFlags) || SF_IsWinRTStaticStub(dwStubFlags)))
2036 {
2037 // WinRT ctor/static stubs make the call on the factory object instead of 'this'
2038 if (m_dwWinRTFactoryObjectLocalNum == (DWORD)-1)
2039 {
2040 m_dwWinRTFactoryObjectLocalNum = NewLocal(ELEMENT_TYPE_OBJECT);
2041
2042 // get the factory object
2043 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2044 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__GET_WINRT_FACTORY_OBJECT, 1, 1);
2045 m_pcsSetup->EmitSTLOC(m_dwWinRTFactoryObjectLocalNum);
2046 }
2047
2048 pcsEmit->EmitLDLOC(m_dwWinRTFactoryObjectLocalNum);
2049 }
2050 else
2051 {
2052 pcsEmit->EmitLoadThis();
2053 }
2054}
2055#endif // FEATURE_COMINTEROP
2056
2057DWORD NDirectStubLinker::GetCleanupWorkListLocalNum()
2058{
2059 LIMITED_METHOD_CONTRACT;
2060 CONSISTENCY_CHECK(m_dwCleanupWorkListLocalNum != (DWORD)-1);
2061 return m_dwCleanupWorkListLocalNum;
2062}
2063
2064DWORD NDirectStubLinker::GetThreadLocalNum()
2065{
2066 STANDARD_VM_CONTRACT;
2067
2068 if (m_dwThreadLocalNum == (DWORD)-1)
2069 {
2070 // The local is created and initialized lazily when first asked.
2071 m_dwThreadLocalNum = NewLocal(ELEMENT_TYPE_I);
2072 m_pcsSetup->EmitCALL(METHOD__THREAD__INTERNAL_GET_CURRENT_THREAD, 0, 1);
2073 m_pcsSetup->EmitSTLOC(m_dwThreadLocalNum);
2074 }
2075
2076 return m_dwThreadLocalNum;
2077}
2078
2079DWORD NDirectStubLinker::GetReturnValueLocalNum()
2080{
2081 LIMITED_METHOD_CONTRACT;
2082 return m_dwRetValLocalNum;
2083}
2084
2085BOOL NDirectStubLinker::IsCleanupNeeded()
2086{
2087 LIMITED_METHOD_CONTRACT;
2088
2089 return (m_fHasCleanupCode || IsCleanupWorkListSetup());
2090}
2091
2092BOOL NDirectStubLinker::IsExceptionCleanupNeeded()
2093{
2094 LIMITED_METHOD_CONTRACT;
2095
2096 return m_fHasExceptionCleanupCode;
2097}
2098
2099void NDirectStubLinker::InitCleanupCode()
2100{
2101 CONTRACTL
2102 {
2103 STANDARD_VM_CHECK;
2104 PRECONDITION(NULL == m_pCleanupFinallyBeginLabel);
2105 }
2106 CONTRACTL_END;
2107
2108 m_pCleanupFinallyBeginLabel = NewCodeLabel();
2109 m_pcsExceptionCleanup->EmitLabel(m_pCleanupFinallyBeginLabel);
2110}
2111
2112void NDirectStubLinker::InitExceptionCleanupCode()
2113{
2114 CONTRACTL
2115 {
2116 STANDARD_VM_CHECK;
2117 PRECONDITION(NULL == m_pSkipExceptionCleanupLabel);
2118 }
2119 CONTRACTL_END;
2120
2121 SetCleanupNeeded();
2122
2123 // we want to skip the entire exception cleanup if no exception has been thrown
2124 m_pSkipExceptionCleanupLabel = NewCodeLabel();
2125 EmitCheckForArgCleanup(m_pcsExceptionCleanup, CLEANUP_INDEX_ALL_DONE, BranchIfMarshaled, m_pSkipExceptionCleanupLabel);
2126}
2127
2128void NDirectStubLinker::SetCleanupNeeded()
2129{
2130 WRAPPER_NO_CONTRACT;
2131
2132 if (!m_fHasCleanupCode)
2133 {
2134 m_fHasCleanupCode = TRUE;
2135 InitCleanupCode();
2136 }
2137}
2138
2139void NDirectStubLinker::SetExceptionCleanupNeeded()
2140{
2141 WRAPPER_NO_CONTRACT;
2142
2143 if (!m_fHasExceptionCleanupCode)
2144 {
2145 m_fHasExceptionCleanupCode = TRUE;
2146 InitExceptionCleanupCode();
2147 }
2148}
2149
2150void NDirectStubLinker::NeedsCleanupList()
2151{
2152 STANDARD_VM_CONTRACT;
2153
2154 if (!IsCleanupWorkListSetup())
2155 {
2156 m_fCleanupWorkListIsSetup = TRUE;
2157 SetCleanupNeeded();
2158
2159 // we setup a new local that will hold the cleanup work list
2160 LocalDesc desc(MscorlibBinder::GetClass(CLASS__CLEANUP_WORK_LIST_ELEMENT));
2161 m_dwCleanupWorkListLocalNum = NewLocal(desc);
2162 }
2163}
2164
2165
2166BOOL NDirectStubLinker::IsCleanupWorkListSetup ()
2167{
2168 LIMITED_METHOD_CONTRACT;
2169
2170 return m_fCleanupWorkListIsSetup;
2171}
2172
2173
2174void NDirectStubLinker::LoadCleanupWorkList(ILCodeStream* pcsEmit)
2175{
2176 STANDARD_VM_CONTRACT;
2177
2178 NeedsCleanupList();
2179 pcsEmit->EmitLDLOCA(GetCleanupWorkListLocalNum());
2180}
2181
2182
2183void NDirectStubLinker::Begin(DWORD dwStubFlags)
2184{
2185 STANDARD_VM_CONTRACT;
2186
2187#ifdef FEATURE_COMINTEROP
2188 if (SF_IsWinRTHasRedirection(dwStubFlags))
2189 {
2190 _ASSERTE(SF_IsForwardCOMStub(dwStubFlags));
2191
2192 // The very first thing we need to do is check whether the call should be routed to
2193 // the marshaling stub for the corresponding projected WinRT interface. If so, we
2194 // tail-call there.
2195 m_pcsSetup->EmitLoadThis();
2196 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2197 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__SHOULD_CALL_WINRT_INTERFACE, 2, 1);
2198
2199 ILCodeLabel *pNoRedirection = m_pcsSetup->NewCodeLabel();
2200 m_pcsSetup->EmitBRFALSE(pNoRedirection);
2201
2202 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(
2203 GetTargetMD(),
2204 TypeHandle::Interop_ManagedToNative);
2205
2206 CONSISTENCY_CHECK(pAdapterMD != NULL && !pAdapterMD->HasMethodInstantiation());
2207
2208 m_pcsSetup->EmitJMP(m_pcsSetup->GetToken(pAdapterMD));
2209
2210 m_pcsSetup->EmitLabel(pNoRedirection);
2211 }
2212#endif // FEATURE_COMINTEROP
2213
2214 if (SF_IsForwardStub(dwStubFlags))
2215 {
2216
2217 if (SF_IsStubWithCctorTrigger(dwStubFlags))
2218 {
2219 EmitLoadStubContext(m_pcsSetup, dwStubFlags);
2220 m_pcsSetup->EmitCALL(METHOD__STUBHELPERS__INIT_DECLARING_TYPE, 1, 0);
2221 }
2222 }
2223 else
2224 {
2225#ifdef MDA_SUPPORTED
2226 if (!SF_IsNGENedStub(dwStubFlags) && MDA_GET_ASSISTANT(GcUnmanagedToManaged))
2227 {
2228 EmitCallGcCollectForMDA(m_pcsSetup, dwStubFlags);
2229 }
2230#endif // MDA_SUPPORTED
2231
2232 if (SF_IsDelegateStub(dwStubFlags))
2233 {
2234#if defined(MDA_SUPPORTED)
2235 // GC was induced (gcUnmanagedToManagedMDA), arguments have been marshaled, and we are about
2236 // to touch the UMEntryThunk and extract the delegate target from it so this is the right time
2237 // to do the collected delegate MDA check.
2238
2239 // The call to CheckCollectedDelegateMDA is emitted regardless of whether the MDA is on at the
2240 // moment. This is to avoid having to ignore NGENed stubs without the call just as we do for
2241 // the GC MDA (callbackOncollectedDelegateMDA is turned on under managed debugger by default
2242 // so the impact would be substantial). The helper bails out fast if the MDA is not enabled.
2243 EmitLoadStubContext(m_pcsDispatch, dwStubFlags);
2244 m_pcsDispatch->EmitCALL(METHOD__STUBHELPERS__CHECK_COLLECTED_DELEGATE_MDA, 1, 0);
2245#endif // MDA_SUPPORTED
2246
2247 //
2248 // recover delegate object from UMEntryThunk
2249
2250 EmitLoadStubContext(m_pcsDispatch, dwStubFlags); // load UMEntryThunk*
2251
2252 m_pcsDispatch->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2253 m_pcsDispatch->EmitADD();
2254 m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE
2255 m_pcsDispatch->EmitLDIND_REF(); // get Delegate object
2256 m_pcsDispatch->EmitLDFLD(GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__TARGET)));
2257 }
2258 }
2259
2260 m_pCleanupTryBeginLabel = NewCodeLabel();
2261 m_pcsMarshal->EmitLabel(m_pCleanupTryBeginLabel);
2262}
2263
2264void NDirectStubLinker::End(DWORD dwStubFlags)
2265{
2266 STANDARD_VM_CONTRACT;
2267
2268 ILCodeStream* pcs = m_pcsUnmarshal;
2269
2270 bool hasTryCatchForHRESULT = SF_IsReverseCOMStub(dwStubFlags)
2271 && !SF_IsFieldGetterStub(dwStubFlags)
2272 && !SF_IsFieldSetterStub(dwStubFlags);
2273
2274 //
2275 // Create a local for the return value and store the return value in it.
2276 //
2277 if (IsCleanupNeeded() || hasTryCatchForHRESULT)
2278 {
2279 // Save the return value if necessary, since the IL stack will be emptied when we leave a try block.
2280 LocalDesc locDescRetVal;
2281 if (SF_IsForwardStub(dwStubFlags))
2282 {
2283 GetStubReturnType(&locDescRetVal);
2284 }
2285 else
2286 {
2287 GetStubTargetReturnType(&locDescRetVal);
2288 }
2289
2290 if (!( (locDescRetVal.cbType == 1) && (locDescRetVal.ElementType[0] == ELEMENT_TYPE_VOID) ))
2291 {
2292 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2293 if (SF_IsReverseStub(dwStubFlags) && StubHasVoidReturnType())
2294 {
2295 // if the target returns void and we are doing HRESULT swapping, S_OK is loaded
2296 // in the unmarshal stream
2297 m_pcsUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2298 }
2299 else
2300 {
2301 // otherwise the return value is loaded in the return unmarshal stream
2302 m_pcsRetUnmarshal->EmitSTLOC(m_dwRetValLocalNum);
2303 }
2304 }
2305 else if (hasTryCatchForHRESULT && (locDescRetVal.ElementType[0] != ELEMENT_TYPE_VOID))
2306 {
2307 m_dwRetValLocalNum = m_pcsRetUnmarshal->NewLocal(locDescRetVal);
2308 }
2309 }
2310
2311 //
2312 // Emit end-of-try and end-of-finally code for the try/finally
2313 //
2314 if (IsCleanupNeeded())
2315 {
2316 m_pCleanupFinallyEndLabel = NewCodeLabel();
2317 m_pCleanupTryEndLabel = NewCodeLabel();
2318
2319 if (IsExceptionCleanupNeeded())
2320 {
2321 // if we made it here, no exception has been thrown
2322 EmitSetArgMarshalIndex(m_pcsUnmarshal, CLEANUP_INDEX_ALL_DONE);
2323 }
2324
2325 // Emit a leave at the end of the try block. If we have an outer try/catch, we need
2326 // to leave to the beginning of the ExceptionHandler code stream, which follows the
2327 // Cleanup code stream. If we don't, we can just leave to the tail end of the
2328 // Unmarshal code stream where we'll emit our RET.
2329
2330 ILCodeLabel* pLeaveTarget = m_pCleanupTryEndLabel;
2331 if (hasTryCatchForHRESULT)
2332 {
2333 pLeaveTarget = m_pCleanupFinallyEndLabel;
2334 }
2335
2336 m_pcsUnmarshal->EmitLEAVE(pLeaveTarget);
2337 m_pcsUnmarshal->EmitLabel(m_pCleanupTryEndLabel);
2338
2339 // Emit a call to destroy the clean-up list if needed.
2340 if (IsCleanupWorkListSetup())
2341 {
2342 LoadCleanupWorkList(m_pcsCleanup);
2343 m_pcsCleanup->EmitCALL(METHOD__STUBHELPERS__DESTROY_CLEANUP_LIST, 1, 0);
2344 }
2345
2346 // Emit the endfinally.
2347 m_pcsCleanup->EmitENDFINALLY();
2348 m_pcsCleanup->EmitLabel(m_pCleanupFinallyEndLabel);
2349 }
2350
2351#ifdef MDA_SUPPORTED
2352 if (SF_IsReverseStub(dwStubFlags) && !SF_IsNGENedStub(dwStubFlags) &&
2353 MDA_GET_ASSISTANT(GcManagedToUnmanaged))
2354 {
2355 EmitCallGcCollectForMDA(pcs, dwStubFlags);
2356 }
2357#endif // MDA_SUPPORTED
2358
2359 if (IsExceptionCleanupNeeded())
2360 {
2361 m_pcsExceptionCleanup->EmitLabel(m_pSkipExceptionCleanupLabel);
2362 }
2363
2364 // Reload the return value
2365 if ((m_dwRetValLocalNum != (DWORD)-1) && !hasTryCatchForHRESULT)
2366 {
2367 pcs->EmitLDLOC(m_dwRetValLocalNum);
2368 }
2369}
2370
2371void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, MethodDesc * pStubMD)
2372{
2373 STANDARD_VM_CONTRACT;
2374 if (SF_IsForwardStub(dwStubFlags)) // managed-to-native
2375 {
2376
2377 if (SF_IsDelegateStub(dwStubFlags)) // delegate invocation
2378 {
2379 // get the delegate unmanaged target - we call a helper instead of just grabbing
2380 // the _methodPtrAux field because we may need to intercept the call for host, MDA, etc.
2381 pcsEmit->EmitLoadThis();
2382#ifdef _TARGET_64BIT_
2383 // on AMD64 GetDelegateTarget will return address of the generic stub for host when we are hosted
2384 // and update the secret argument with real target - the secret arg will be embedded in the
2385 // InlinedCallFrame by the JIT and fetched via TLS->Thread->Frame->Datum by the stub for host
2386 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT_ADDR, 0, 1);
2387#else // !_TARGET_64BIT_
2388 // we don't need to do this on x86 because stub for host is generated dynamically per target
2389 pcsEmit->EmitLDNULL();
2390#endif // !_TARGET_64BIT_
2391 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_TARGET, 2, 1);
2392 }
2393 else // direct invocation
2394 {
2395 if (SF_IsCALLIStub(dwStubFlags)) // unmanaged CALLI
2396 {
2397 // if we ever NGEN CALLI stubs, this would have to be done differently
2398 _ASSERTE(!SF_IsNGENedStub(dwStubFlags));
2399
2400 // for managed-to-unmanaged CALLI that requires marshaling, the target is passed
2401 // as the secret argument to the stub by GenericPInvokeCalliHelper (asmhelpers.asm)
2402 EmitLoadStubContext(pcsEmit, dwStubFlags);
2403#ifdef _WIN64
2404 // the secret arg has been shifted to left and ORed with 1 (see code:GenericPInvokeCalliHelper)
2405 pcsEmit->EmitLDC(1);
2406 pcsEmit->EmitSHR_UN();
2407#endif
2408 }
2409 else
2410#ifdef FEATURE_COMINTEROP
2411 if (!SF_IsCOMStub(dwStubFlags)) // forward P/Invoke
2412#endif // FEATURE_COMINTEROP
2413 {
2414 EmitLoadStubContext(pcsEmit, dwStubFlags);
2415
2416 {
2417 // Perf: inline the helper for now
2418 //pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_NDIRECT_TARGET, 1, 1);
2419 pcsEmit->EmitLDC(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
2420 pcsEmit->EmitADD();
2421
2422 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2423 {
2424 pcsEmit->EmitDUP();
2425 }
2426
2427 pcsEmit->EmitLDIND_I();
2428
2429 if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
2430 {
2431 pcsEmit->EmitADD();
2432 }
2433
2434 pcsEmit->EmitLDIND_I();
2435 }
2436 }
2437#ifdef FEATURE_COMINTEROP
2438 else
2439 {
2440 // this is a CLR -> COM call
2441 // the target has been computed by StubHelpers::GetCOMIPFromRCW
2442 pcsEmit->EmitLDLOC(m_dwTargetEntryPointLocalNum);
2443 }
2444#endif // FEATURE_COMINTEROP
2445 }
2446 }
2447 else // native-to-managed
2448 {
2449 if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate
2450 {
2451 int tokDelegate_methodPtr = pcsEmit->GetToken(MscorlibBinder::GetField(FIELD__DELEGATE__METHOD_PTR));
2452
2453 EmitLoadStubContext(pcsEmit, dwStubFlags);
2454 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2455 pcsEmit->EmitADD();
2456 pcsEmit->EmitLDIND_I(); // Get OBJECTHANDLE
2457 pcsEmit->EmitLDIND_REF(); // Get Delegate object
2458 pcsEmit->EmitLDFLD(tokDelegate_methodPtr); // get _methodPtr
2459 }
2460#ifdef FEATURE_COMINTEROP
2461 else if (SF_IsCOMStub(dwStubFlags)) // COM -> CLR call
2462 {
2463 // managed target is passed directly in the secret argument
2464 EmitLoadStubContext(pcsEmit, dwStubFlags);
2465 }
2466#endif // FEATURE_COMINTEROP
2467 else // direct reverse P/Invoke (CoreCLR hosting)
2468 {
2469 EmitLoadStubContext(pcsEmit, dwStubFlags);
2470 CONSISTENCY_CHECK(0 == offsetof(UMEntryThunk, m_pManagedTarget)); // if this changes, just add back the EmitLDC/EmitADD below
2471 // pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pManagedTarget));
2472 // pcsEmit->EmitADD();
2473 pcsEmit->EmitLDIND_I(); // Get UMEntryThunk::m_pManagedTarget
2474 }
2475 }
2476
2477 // For managed-to-native calls, the rest of the work is done by the JIT. It will
2478 // erect InlinedCallFrame, flip GC mode, and use the specified calling convention
2479 // to call the target. For native-to-managed calls, this is an ordinary managed
2480 // CALLI and nothing special happens.
2481 pcsEmit->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, 0, m_iTargetStackDelta);
2482}
2483
2484void NDirectStubLinker::EmitLogNativeArgument(ILCodeStream* pslILEmit, DWORD dwPinnedLocal)
2485{
2486 STANDARD_VM_CONTRACT;
2487
2488 if (SF_IsForwardPInvokeStub(m_dwStubFlags) && !SF_IsForwardDelegateStub(m_dwStubFlags))
2489 {
2490 // get the secret argument via intrinsic
2491 pslILEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2492 }
2493 else
2494 {
2495 // no secret argument
2496 pslILEmit->EmitLoadNullPtr();
2497 }
2498
2499 pslILEmit->EmitLDLOC(dwPinnedLocal);
2500
2501 pslILEmit->EmitCALL(METHOD__STUBHELPERS__LOG_PINNED_ARGUMENT, 2, 0);
2502}
2503
2504void NDirectStubLinker::GetCleanupFinallyOffsets(ILStubEHClause * pClause)
2505{
2506 CONTRACTL
2507 {
2508 STANDARD_VM_CHECK;
2509 PRECONDITION(CheckPointer(pClause));
2510 }
2511 CONTRACTL_END;
2512
2513 if (m_pCleanupFinallyEndLabel)
2514 {
2515 _ASSERTE(m_pCleanupFinallyBeginLabel);
2516 _ASSERTE(m_pCleanupTryBeginLabel);
2517 _ASSERTE(m_pCleanupTryEndLabel);
2518
2519 pClause->kind = ILStubEHClause::kFinally;
2520 pClause->dwTryBeginOffset = (DWORD)m_pCleanupTryBeginLabel->GetCodeOffset();
2521 pClause->cbTryLength = (DWORD)m_pCleanupTryEndLabel->GetCodeOffset() - pClause->dwTryBeginOffset;
2522 pClause->dwHandlerBeginOffset = (DWORD)m_pCleanupFinallyBeginLabel->GetCodeOffset();
2523 pClause->cbHandlerLength = (DWORD)m_pCleanupFinallyEndLabel->GetCodeOffset() - pClause->dwHandlerBeginOffset;
2524 }
2525}
2526
2527void NDirectStubLinker::ClearCode()
2528{
2529 WRAPPER_NO_CONTRACT;
2530 ILStubLinker::ClearCode();
2531
2532 m_pCleanupTryBeginLabel = 0;
2533 m_pCleanupTryEndLabel = 0;
2534 m_pCleanupFinallyBeginLabel = 0;
2535 m_pCleanupFinallyEndLabel = 0;
2536}
2537
2538#ifdef PROFILING_SUPPORTED
2539DWORD NDirectStubLinker::EmitProfilerBeginTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2540{
2541 STANDARD_VM_CONTRACT;
2542
2543 if (SF_IsForwardDelegateStub(dwStubFlags) || SF_IsCALLIStub(dwStubFlags))
2544 {
2545 // secret argument does not contain MD nor UMEntryThunk
2546 pcsEmit->EmitLoadNullPtr();
2547 }
2548 else
2549 {
2550 EmitLoadStubContext(pcsEmit, dwStubFlags);
2551 }
2552
2553 if (SF_IsForwardStub(dwStubFlags))
2554 {
2555 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2556 }
2557 else
2558 {
2559 // we use a null pThread to indicate reverse interop
2560 pcsEmit->EmitLDC(NULL);
2561 }
2562
2563 // In the unmanaged delegate case, we need the "this" object to retrieve the MD
2564 // in StubHelpers::ProfilerEnterCallback().
2565 if (SF_IsDelegateStub(dwStubFlags))
2566 {
2567 if (SF_IsForwardStub(dwStubFlags))
2568 {
2569 pcsEmit->EmitLoadThis();
2570 }
2571 else
2572 {
2573 EmitLoadStubContext(pcsEmit, dwStubFlags); // load UMEntryThunk*
2574 pcsEmit->EmitLDC(offsetof(UMEntryThunk, m_pObjectHandle));
2575 pcsEmit->EmitADD();
2576 pcsEmit->EmitLDIND_I(); // get OBJECTHANDLE
2577 pcsEmit->EmitLDIND_REF(); // get Delegate object
2578 }
2579 }
2580 else
2581 {
2582 pcsEmit->EmitLDC(NULL);
2583 }
2584 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_BEGIN_TRANSITION_CALLBACK, 3, 1);
2585
2586 // Store the MD for StubHelpers::ProfilerLeaveCallback().
2587 DWORD dwMethodDescLocalNum = pcsEmit->NewLocal(ELEMENT_TYPE_I);
2588 pcsEmit->EmitSTLOC(dwMethodDescLocalNum);
2589 return dwMethodDescLocalNum;
2590}
2591
2592void NDirectStubLinker::EmitProfilerEndTransitionCallback(ILCodeStream* pcsEmit, DWORD dwStubFlags, DWORD dwMethodDescLocalNum)
2593{
2594 STANDARD_VM_CONTRACT;
2595
2596 pcsEmit->EmitLDLOC(dwMethodDescLocalNum);
2597 if (SF_IsReverseStub(dwStubFlags))
2598 {
2599 // we use a null pThread to indicate reverse interop
2600 pcsEmit->EmitLDC(NULL);
2601 }
2602 else
2603 {
2604 pcsEmit->EmitLDLOC(GetThreadLocalNum());
2605 }
2606 pcsEmit->EmitCALL(METHOD__STUBHELPERS__PROFILER_END_TRANSITION_CALLBACK, 2, 0);
2607}
2608#endif // PROFILING_SUPPPORTED
2609
2610#ifdef VERIFY_HEAP
2611void NDirectStubLinker::EmitValidateLocal(ILCodeStream* pcsEmit, DWORD dwLocalNum, bool fIsByref, DWORD dwStubFlags)
2612{
2613 STANDARD_VM_CONTRACT;
2614
2615 pcsEmit->EmitLDLOC(dwLocalNum);
2616
2617 if (SF_IsDelegateStub(dwStubFlags))
2618 {
2619 pcsEmit->EmitLoadNullPtr();
2620 pcsEmit->EmitLoadThis();
2621 }
2622 else if (SF_IsCALLIStub(dwStubFlags))
2623 {
2624 pcsEmit->EmitLoadNullPtr();
2625 pcsEmit->EmitLDNULL();
2626 }
2627 else
2628 {
2629 // P/Invoke, CLR->COM
2630 EmitLoadStubContext(pcsEmit, dwStubFlags);
2631 pcsEmit->EmitLDNULL();
2632 }
2633
2634 if (fIsByref)
2635 {
2636 // StubHelpers.ValidateByref(byref, pMD, pThis)
2637 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_BYREF, 3, 0);
2638 }
2639 else
2640 {
2641 // StubHelpers.ValidateObject(obj, pMD, pThis)
2642 pcsEmit->EmitCALL(METHOD__STUBHELPERS__VALIDATE_OBJECT, 3, 0);
2643 }
2644}
2645
2646void NDirectStubLinker::EmitObjectValidation(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2647{
2648 STANDARD_VM_CONTRACT;
2649
2650 // generate validation callouts for pinned locals
2651 CQuickBytes qbLocalSig;
2652 DWORD cbSig = GetLocalSigSize();
2653
2654 qbLocalSig.AllocThrows(cbSig);
2655 PCOR_SIGNATURE pSig = (PCOR_SIGNATURE)qbLocalSig.Ptr();
2656
2657 GetLocalSig(pSig, cbSig);
2658 SigPointer ptr(pSig, cbSig);
2659
2660 IfFailThrow(ptr.GetData(NULL)); // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
2661
2662 ULONG numLocals;
2663 IfFailThrow(ptr.GetData(&numLocals));
2664
2665 for (ULONG i = 0; i < numLocals; i++)
2666 {
2667 BYTE modifier;
2668 IfFailThrow(ptr.PeekByte(&modifier));
2669 if (modifier == ELEMENT_TYPE_PINNED)
2670 {
2671 IfFailThrow(ptr.GetByte(NULL));
2672 IfFailThrow(ptr.PeekByte(&modifier));
2673 EmitValidateLocal(pcsEmit, i, (modifier == ELEMENT_TYPE_BYREF), dwStubFlags);
2674 }
2675
2676 IfFailThrow(ptr.SkipExactlyOne());
2677 }
2678}
2679#endif // VERIFY_HEAP
2680
2681// Loads the 'secret argument' passed to the stub.
2682void NDirectStubLinker::EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubFlags)
2683{
2684 STANDARD_VM_CONTRACT;
2685
2686 CONSISTENCY_CHECK(!SF_IsForwardDelegateStub(dwStubFlags));
2687 CONSISTENCY_CHECK(!SF_IsFieldGetterStub(dwStubFlags) && !SF_IsFieldSetterStub(dwStubFlags));
2688
2689#ifdef FEATURE_COMINTEROP
2690 if (SF_IsWinRTDelegateStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags))
2691 {
2692 // we have the delegate 'this' but we need the EEImpl/Instantiated 'Invoke' MD pointer
2693 // (Delegate.GetInvokeMethod does not return exact instantiated MD so we call our own helper)
2694 pcsEmit->EmitLoadThis();
2695 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_DELEGATE_INVOKE_METHOD, 1, 1);
2696 }
2697 else
2698#endif // FEATURE_COMINTEROP
2699 {
2700 // get the secret argument via intrinsic
2701 pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1);
2702 }
2703}
2704
2705#ifdef MDA_SUPPORTED
2706void NDirectStubLinker::EmitCallGcCollectForMDA(ILCodeStream *pcsEmit, DWORD dwStubFlags)
2707{
2708 STANDARD_VM_CONTRACT;
2709
2710 ILCodeLabel *pSkipGcLabel = NULL;
2711
2712 if (SF_IsForwardPInvokeStub(dwStubFlags) &&
2713 !SF_IsDelegateStub(dwStubFlags) &&
2714 !SF_IsCALLIStub(dwStubFlags))
2715 {
2716 // don't call GC if this is a QCall
2717 EmitLoadStubContext(pcsEmit, dwStubFlags);
2718 pcsEmit->EmitCALL(METHOD__STUBHELPERS__IS_QCALL, 1, 1);
2719
2720 pSkipGcLabel = pcsEmit->NewCodeLabel();
2721 pcsEmit->EmitBRTRUE(pSkipGcLabel);
2722 }
2723
2724 pcsEmit->EmitCALL(METHOD__STUBHELPERS__TRIGGER_GC_FOR_MDA, 0, 0);
2725
2726 if (pSkipGcLabel != NULL)
2727 {
2728 pcsEmit->EmitLabel(pSkipGcLabel);
2729 }
2730}
2731#endif // MDA_SUPPORTED
2732
2733#ifdef FEATURE_COMINTEROP
2734
2735class DispatchStubState : public StubState // For CLR-to-COM late-bound/eventing calls
2736{
2737public:
2738 DispatchStubState()
2739 : m_dwStubFlags(0),
2740 m_lateBoundFlags(0)
2741 {
2742 WRAPPER_NO_CONTRACT;
2743 }
2744
2745 void SetLastError(BOOL fSetLastError)
2746 {
2747 LIMITED_METHOD_CONTRACT;
2748
2749 CONSISTENCY_CHECK(!fSetLastError);
2750 }
2751
2752 void BeginEmit(DWORD dwStubFlags)
2753 {
2754 LIMITED_METHOD_CONTRACT;
2755
2756 CONSISTENCY_CHECK(SF_IsCOMStub(dwStubFlags));
2757 m_dwStubFlags = dwStubFlags;
2758 }
2759
2760 void MarshalReturn(MarshalInfo* pInfo, int argOffset)
2761 {
2762 CONTRACTL
2763 {
2764 STANDARD_VM_CHECK;
2765
2766 PRECONDITION(CheckPointer(pInfo));
2767 }
2768 CONTRACTL_END;
2769 }
2770
2771 void MarshalArgument(MarshalInfo* pInfo, int argOffset, UINT nativeStackOffset)
2772 {
2773 CONTRACTL
2774 {
2775 STANDARD_VM_CHECK;
2776 PRECONDITION(CheckPointer(pInfo));
2777 }
2778 CONTRACTL_END;
2779
2780 if (SF_IsCOMLateBoundStub(m_dwStubFlags) && pInfo->GetDispWrapperType() != 0)
2781 {
2782 m_lateBoundFlags |= ComPlusCallInfo::kRequiresArgumentWrapping;
2783 }
2784 }
2785
2786 void MarshalLCID(int argIdx)
2787 {
2788 LIMITED_METHOD_CONTRACT;
2789 }
2790
2791#ifdef FEATURE_COMINTEROP
2792 void MarshalHiddenLengthArgument(MarshalInfo *, BOOL)
2793 {
2794 LIMITED_METHOD_CONTRACT;
2795 }
2796 void MarshalFactoryReturn()
2797 {
2798 LIMITED_METHOD_CONTRACT;
2799 UNREACHABLE();
2800 }
2801#endif // FEATURE_COMINTEROP
2802
2803 void EmitInvokeTarget(MethodDesc *pStubMD)
2804 {
2805 LIMITED_METHOD_CONTRACT;
2806 UNREACHABLE_MSG("Should never come to DispatchStubState::EmitInvokeTarget");
2807 }
2808
2809 void FinishEmit(MethodDesc *pMD)
2810 {
2811 STANDARD_VM_CONTRACT;
2812
2813 // set flags directly on the interop MD
2814 _ASSERTE(pMD->IsComPlusCall());
2815
2816 ((ComPlusCallMethodDesc *)pMD)->SetLateBoundFlags(m_lateBoundFlags);
2817 }
2818
2819protected:
2820 DWORD m_dwStubFlags;
2821 BYTE m_lateBoundFlags; // ComPlusCallMethodDesc::Flags
2822};
2823
2824#endif // FEATURE_COMINTEROP
2825
2826
2827void PInvokeStaticSigInfo::PreInit(Module* pModule, MethodTable * pMT)
2828{
2829 CONTRACTL
2830 {
2831 THROWS;
2832 GC_NOTRIGGER;
2833 MODE_ANY;
2834 }
2835 CONTRACTL_END;
2836
2837 // initialize data members
2838 m_wFlags = 0;
2839 m_pModule = pModule;
2840 m_callConv = (CorPinvokeMap)0;
2841 SetBestFitMapping (TRUE);
2842 SetThrowOnUnmappableChar (FALSE);
2843 SetLinkFlags (nlfNone);
2844 SetCharSet (nltAnsi);
2845 m_error = 0;
2846
2847 // assembly/type level m_bestFit & m_bThrowOnUnmappableChar
2848 BOOL bBestFit;
2849 BOOL bThrowOnUnmappableChar;
2850
2851 if (pMT != NULL)
2852 {
2853 EEClass::GetBestFitMapping(pMT, &bBestFit, &bThrowOnUnmappableChar);
2854 }
2855 else
2856 {
2857 ReadBestFitCustomAttribute(m_pModule->GetMDImport(), mdTypeDefNil, &bBestFit, &bThrowOnUnmappableChar);
2858 }
2859
2860 SetBestFitMapping (bBestFit);
2861 SetThrowOnUnmappableChar (bThrowOnUnmappableChar);
2862}
2863
2864void PInvokeStaticSigInfo::PreInit(MethodDesc* pMD)
2865{
2866 CONTRACTL
2867 {
2868 THROWS;
2869 GC_NOTRIGGER;
2870 MODE_ANY;
2871 }
2872 CONTRACTL_END;
2873
2874 PreInit(pMD->GetModule(), pMD->GetMethodTable());
2875 SetIsStatic (pMD->IsStatic());
2876 m_sig = pMD->GetSignature();
2877 if (pMD->IsEEImpl())
2878 {
2879 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
2880 SetIsDelegateInterop(TRUE);
2881 }
2882}
2883
2884PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2885 MethodDesc* pMD, LPCUTF8 *pLibName, LPCUTF8 *pEntryPointName, ThrowOnError throwOnError)
2886{
2887 CONTRACTL
2888 {
2889 THROWS;
2890 GC_NOTRIGGER;
2891 MODE_ANY;
2892 }
2893 CONTRACTL_END;
2894
2895 DllImportInit(pMD, pLibName, pEntryPointName);
2896
2897 if (throwOnError)
2898 ReportErrors();
2899}
2900
2901PInvokeStaticSigInfo::PInvokeStaticSigInfo(MethodDesc* pMD, ThrowOnError throwOnError)
2902{
2903 CONTRACTL
2904 {
2905 THROWS;
2906 GC_NOTRIGGER;
2907 MODE_ANY;
2908
2909 PRECONDITION(CheckPointer(pMD));
2910 }
2911 CONTRACTL_END;
2912
2913 HRESULT hr = S_OK;
2914
2915 MethodTable * pMT = pMD->GetMethodTable();
2916
2917 if (!pMT->IsDelegate())
2918 {
2919 DllImportInit(pMD, NULL, NULL);
2920 return;
2921 }
2922
2923 // initialize data members to defaults
2924 PreInit(pMD);
2925
2926 // System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
2927 BYTE* pData = NULL;
2928 LONG cData = 0;
2929 CorPinvokeMap callConv = (CorPinvokeMap)0;
2930
2931 HRESULT hRESULT = pMT->GetMDImport()->GetCustomAttributeByName(
2932 pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, (const VOID **)(&pData), (ULONG *)&cData);
2933 IfFailThrow(hRESULT);
2934 if (cData != 0)
2935 {
2936 CustomAttributeParser ca(pData, cData);
2937
2938 CaArg args[1];
2939 args[0].InitEnum(SERIALIZATION_TYPE_I4, (ULONG)m_callConv);
2940
2941 IfFailGo(ParseKnownCaArgs(ca, args, lengthof(args)));
2942
2943 enum UnmanagedFunctionPointerNamedArgs
2944 {
2945 MDA_CharSet,
2946 MDA_BestFitMapping,
2947 MDA_ThrowOnUnmappableChar,
2948 MDA_SetLastError,
2949 MDA_Last,
2950 };
2951
2952 CaNamedArg namedArgs[MDA_Last];
2953 namedArgs[MDA_CharSet].InitI4FieldEnum("CharSet", "System.Runtime.InteropServices.CharSet", (ULONG)GetCharSet());
2954 namedArgs[MDA_BestFitMapping].InitBoolField("BestFitMapping", (ULONG)GetBestFitMapping());
2955 namedArgs[MDA_ThrowOnUnmappableChar].InitBoolField("ThrowOnUnmappableChar", (ULONG)GetThrowOnUnmappableChar());
2956 namedArgs[MDA_SetLastError].InitBoolField("SetLastError", 0);
2957
2958 IfFailGo(ParseKnownCaNamedArgs(ca, namedArgs, lengthof(namedArgs)));
2959
2960 callConv = (CorPinvokeMap)(args[0].val.u4 << 8);
2961 CorNativeLinkType nlt = (CorNativeLinkType)0;
2962
2963 // XXX Tue 07/19/2005
2964 // Keep in sync with the handling of CorPInvokeMap in
2965 // PInvokeStaticSigInfo::DllImportInit.
2966 switch( namedArgs[MDA_CharSet].val.u4 )
2967 {
2968 case 0:
2969 case nltAnsi:
2970 nlt = nltAnsi; break;
2971 case nltUnicode:
2972 case nltAuto: // Since Win9x isn't supported anymore, nltAuto always represents unicode strings.
2973 nlt = nltUnicode; break;
2974 default:
2975 hr = E_FAIL; goto ErrExit;
2976 }
2977 SetCharSet ( nlt );
2978 SetBestFitMapping (namedArgs[MDA_BestFitMapping].val.u1);
2979 SetThrowOnUnmappableChar (namedArgs[MDA_ThrowOnUnmappableChar].val.u1);
2980 if (namedArgs[MDA_SetLastError].val.u1)
2981 SetLinkFlags ((CorNativeLinkFlags)(nlfLastError | GetLinkFlags()));
2982 }
2983
2984
2985ErrExit:
2986 if (hr != S_OK)
2987 SetError(IDS_EE_NDIRECT_BADNATL);
2988
2989 InitCallConv(callConv, pMD->IsVarArg());
2990
2991 if (throwOnError)
2992 ReportErrors();
2993}
2994
2995PInvokeStaticSigInfo::PInvokeStaticSigInfo(
2996 Signature sig, Module* pModule, ThrowOnError throwOnError)
2997{
2998 CONTRACTL
2999 {
3000 THROWS;
3001 GC_NOTRIGGER;
3002 MODE_ANY;
3003
3004 PRECONDITION(CheckPointer(pModule));
3005 }
3006 CONTRACTL_END;
3007
3008 PreInit(pModule, NULL);
3009 m_sig = sig;
3010 SetIsStatic (!(MetaSig::GetCallingConvention(pModule, sig) & IMAGE_CEE_CS_CALLCONV_HASTHIS));
3011 InitCallConv((CorPinvokeMap)0, FALSE);
3012
3013 if (throwOnError)
3014 ReportErrors();
3015}
3016
3017void PInvokeStaticSigInfo::DllImportInit(MethodDesc* pMD, LPCUTF8 *ppLibName, LPCUTF8 *ppEntryPointName)
3018{
3019 CONTRACTL
3020 {
3021 THROWS;
3022 GC_NOTRIGGER;
3023 MODE_ANY;
3024
3025 PRECONDITION(CheckPointer(pMD));
3026
3027 // These preconditions to prevent multithreaded regression
3028 // where pMD->ndirect.m_szLibName was passed in directly, cleared
3029 // by this API, then accessed on another thread before being reset here.
3030 PRECONDITION(CheckPointer(ppLibName, NULL_OK) && (!ppLibName || *ppLibName == NULL));
3031 PRECONDITION(CheckPointer(ppEntryPointName, NULL_OK) && (!ppEntryPointName || *ppEntryPointName == NULL));
3032 }
3033 CONTRACTL_END;
3034
3035 // initialize data members to defaults
3036 PreInit(pMD);
3037
3038 // System.Runtime.InteropServices.DllImportAttribute
3039 IMDInternalImport *pInternalImport = pMD->GetMDImport();
3040 CorPinvokeMap mappingFlags = pmMaxValue;
3041 mdModuleRef modref = mdModuleRefNil;
3042 if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
3043 {
3044#if !defined(CROSSGEN_COMPILE) // IJW
3045 // The guessing heuristic has been broken with NGen for a long time since we stopped loading
3046 // images at NGen time using full LoadLibrary. The DLL references are not resolved correctly
3047 // without full LoadLibrary.
3048 //
3049 // Disable the heuristic consistently during NGen so that it does not kick in by accident.
3050 if (!IsCompilationProcess())
3051 BestGuessNDirectDefaults(pMD);
3052#endif
3053
3054 InitCallConv((CorPinvokeMap)0, pMD->IsVarArg());
3055 return;
3056 }
3057
3058 // out parameter pEntryPointName
3059 if (ppEntryPointName && *ppEntryPointName == NULL)
3060 *ppEntryPointName = pMD->GetName();
3061
3062 // out parameter pLibName
3063 if (ppLibName != NULL)
3064 {
3065 if (FAILED(pInternalImport->GetModuleRefProps(modref, ppLibName)))
3066 {
3067 SetError(IDS_CLASSLOAD_BADFORMAT);
3068 return;
3069 }
3070 }
3071
3072 // m_callConv
3073 InitCallConv((CorPinvokeMap)(mappingFlags & pmCallConvMask), pMD->IsVarArg());
3074
3075 // m_bestFit
3076 CorPinvokeMap bestFitMask = (CorPinvokeMap)(mappingFlags & pmBestFitMask);
3077 if (bestFitMask == pmBestFitEnabled)
3078 SetBestFitMapping (TRUE);
3079 else if (bestFitMask == pmBestFitDisabled)
3080 SetBestFitMapping (FALSE);
3081
3082 // m_bThrowOnUnmappableChar
3083 CorPinvokeMap unmappableMask = (CorPinvokeMap)(mappingFlags & pmThrowOnUnmappableCharMask);
3084 if (unmappableMask == pmThrowOnUnmappableCharEnabled)
3085 SetThrowOnUnmappableChar (TRUE);
3086 else if (unmappableMask == pmThrowOnUnmappableCharDisabled)
3087 SetThrowOnUnmappableChar (FALSE);
3088
3089 // inkFlags : CorPinvoke -> CorNativeLinkFlags
3090 if (mappingFlags & pmSupportsLastError)
3091 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfLastError));
3092 if (mappingFlags & pmNoMangle)
3093 SetLinkFlags ((CorNativeLinkFlags)(GetLinkFlags() | nlfNoMangle));
3094
3095 // XXX Tue 07/19/2005
3096 // Keep in sync with the handling of CorNativeLinkType in
3097 // PInvokeStaticSigInfo::PInvokeStaticSigInfo.
3098
3099 // charset : CorPinvoke -> CorNativeLinkType
3100 CorPinvokeMap charSetMask = (CorPinvokeMap)(mappingFlags & (pmCharSetNotSpec | pmCharSetAnsi | pmCharSetUnicode | pmCharSetAuto));
3101 if (charSetMask == pmCharSetNotSpec || charSetMask == pmCharSetAnsi)
3102 {
3103 SetCharSet (nltAnsi);
3104 }
3105 else if (charSetMask == pmCharSetUnicode || charSetMask == pmCharSetAuto)
3106 {
3107 // Since Win9x isn't supported anymore, pmCharSetAuto always represents unicode strings.
3108 SetCharSet (nltUnicode);
3109 }
3110 else
3111 {
3112 SetError(IDS_EE_NDIRECT_BADNATL);
3113 }
3114}
3115
3116#if !defined(CROSSGEN_COMPILE) // IJW
3117
3118// This function would work, but be unused on Unix. Ifdefing out to avoid build errors due to the unused function.
3119#if !defined (FEATURE_PAL)
3120static LPBYTE FollowIndirect(LPBYTE pTarget)
3121{
3122 CONTRACT(LPBYTE)
3123 {
3124 NOTHROW;
3125 GC_NOTRIGGER;
3126 MODE_ANY;
3127 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
3128 }
3129 CONTRACT_END;
3130
3131 LPBYTE pRet = NULL;
3132
3133 EX_TRY
3134 {
3135 AVInRuntimeImplOkayHolder AVOkay;
3136
3137#ifdef _TARGET_X86_
3138 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3139 {
3140 pRet = **(LPBYTE**)(pTarget + 2);
3141 }
3142#elif defined(_TARGET_AMD64_)
3143 if (pTarget != NULL && !(pTarget[0] != 0xff || pTarget[1] != 0x25))
3144 {
3145 INT64 rva = *(INT32*)(pTarget + 2);
3146 pRet = *(LPBYTE*)(pTarget + 6 + rva);
3147 }
3148#endif
3149 }
3150 EX_CATCH
3151 {
3152 // Catch AVs here.
3153 }
3154 EX_END_CATCH(SwallowAllExceptions);
3155
3156 RETURN pRet;
3157}
3158#endif // !FEATURE_PAL
3159
3160BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget)
3161{
3162 CONTRACTL
3163 {
3164 NOTHROW;
3165 GC_NOTRIGGER;
3166 MODE_ANY;
3167 }
3168 CONTRACTL_END;
3169
3170#if !defined(FEATURE_PAL)
3171 static LPBYTE pGetLastError = NULL;
3172 if (!pGetLastError)
3173 {
3174 // No need to use a holder here, since no cleanup is necessary.
3175 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
3176 if (hMod)
3177 {
3178 pGetLastError = (LPBYTE)GetProcAddress(hMod, "GetLastError");
3179 if (!pGetLastError)
3180 {
3181 // This should never happen but better to be cautious.
3182 pGetLastError = (LPBYTE)-1;
3183 }
3184 }
3185 else
3186 {
3187 // We failed to get the module handle for kernel32.dll. This is almost impossible
3188 // however better to err on the side of caution.
3189 pGetLastError = (LPBYTE)-1;
3190 }
3191 }
3192
3193 if (pTarget == pGetLastError)
3194 return TRUE;
3195
3196 if (pTarget == NULL)
3197 return FALSE;
3198
3199 LPBYTE pTarget2 = FollowIndirect(pTarget);
3200 if (pTarget2)
3201 {
3202 // jmp [xxxx] - could be an import thunk
3203 return pTarget2 == pGetLastError;
3204 }
3205#endif // !FEATURE_PAL
3206
3207 return FALSE;
3208}
3209
3210DWORD STDMETHODCALLTYPE FalseGetLastError()
3211{
3212 WRAPPER_NO_CONTRACT;
3213
3214 return GetThread()->m_dwLastError;
3215}
3216
3217void PInvokeStaticSigInfo::BestGuessNDirectDefaults(MethodDesc* pMD)
3218{
3219 CONTRACTL
3220 {
3221 NOTHROW;
3222 GC_NOTRIGGER;
3223 MODE_ANY;
3224 }
3225 CONTRACTL_END;
3226
3227 if (!pMD->IsNDirect())
3228 return;
3229
3230 NDirectMethodDesc* pMDD = (NDirectMethodDesc*)pMD;
3231
3232 if (!pMDD->IsEarlyBound())
3233 return;
3234
3235 LPVOID pTarget = NULL;
3236
3237 // NOTE: If we get inside this block, and this is a call to GetLastError,
3238 // then InitEarlyBoundNDirectTarget has not been run yet.
3239 if (pMDD->NDirectTargetIsImportThunk())
3240 {
3241 // Get the unmanaged callsite.
3242 pTarget = (LPVOID)pMDD->GetModule()->GetInternalPInvokeTarget(pMDD->GetRVA());
3243
3244 // If this is a call to GetLastError, then we haven't overwritten m_pNativeNDirectTarget yet.
3245 if (HeuristicDoesThisLookLikeAGetLastErrorCall((LPBYTE)pTarget))
3246 pTarget = (BYTE*)FalseGetLastError;
3247 }
3248 else
3249 {
3250 pTarget = pMDD->GetNativeNDirectTarget();
3251 }
3252}
3253
3254#endif // !CROSSGEN_COMPILE
3255
3256inline CorPinvokeMap GetDefaultCallConv(BOOL bIsVarArg)
3257{
3258#ifdef PLATFORM_UNIX
3259 return pmCallConvCdecl;
3260#else // PLATFORM_UNIX
3261 return bIsVarArg ? pmCallConvCdecl : pmCallConvStdcall;
3262#endif // !PLATFORM_UNIX
3263}
3264
3265void PInvokeStaticSigInfo::InitCallConv(CorPinvokeMap callConv, BOOL bIsVarArg)
3266{
3267 CONTRACTL
3268 {
3269 NOTHROW;
3270 GC_NOTRIGGER;
3271 MODE_ANY;
3272 }
3273 CONTRACTL_END
3274
3275 // Convert WinAPI methods to either StdCall or CDecl based on if they are varargs or not.
3276 if (callConv == pmCallConvWinapi)
3277 callConv = GetDefaultCallConv(bIsVarArg);
3278
3279 CorPinvokeMap sigCallConv = (CorPinvokeMap)0;
3280 BOOL fSuccess = MetaSig::GetUnmanagedCallingConvention(m_pModule, m_sig.GetRawSig(), m_sig.GetRawSigLen(), &sigCallConv);
3281
3282 if (!fSuccess)
3283 {
3284 SetError(IDS_EE_NDIRECT_BADNATL); //Bad metadata format
3285 }
3286
3287 // Do the same WinAPI to StdCall or CDecl for the signature calling convention as well. We need
3288 // to do this before we check to make sure the PInvoke map calling convention and the
3289 // signature calling convention match for compatibility reasons.
3290 if (sigCallConv == pmCallConvWinapi)
3291 sigCallConv = GetDefaultCallConv(bIsVarArg);
3292
3293 if (callConv != 0 && sigCallConv != 0 && callConv != sigCallConv)
3294 SetError(IDS_EE_NDIRECT_BADNATL_CALLCONV);
3295
3296 if (callConv == 0 && sigCallConv == 0)
3297 m_callConv = GetDefaultCallConv(bIsVarArg);
3298 else if (callConv != 0)
3299 m_callConv = callConv;
3300 else
3301 m_callConv = sigCallConv;
3302
3303 if (bIsVarArg && m_callConv != pmCallConvCdecl)
3304 SetError(IDS_EE_NDIRECT_BADNATL_VARARGS_CALLCONV);
3305}
3306
3307void PInvokeStaticSigInfo::ReportErrors()
3308{
3309 CONTRACTL
3310 {
3311 THROWS;
3312 GC_NOTRIGGER;
3313 MODE_ANY;
3314 }
3315 CONTRACTL_END;
3316
3317 if (m_error != 0)
3318 COMPlusThrow(kTypeLoadException, m_error);
3319}
3320
3321
3322//---------------------------------------------------------
3323// Does a class or method have a NAT_L CustomAttribute?
3324//
3325// S_OK = yes
3326// S_FALSE = no
3327// FAILED = unknown because something failed.
3328//---------------------------------------------------------
3329/*static*/
3330HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs)
3331{
3332 CONTRACTL
3333 {
3334 NOTHROW;
3335 GC_NOTRIGGER;
3336 MODE_ANY;
3337
3338 PRECONDITION(CheckPointer(pInternalImport));
3339 PRECONDITION(TypeFromToken(token) == mdtMethodDef);
3340 }
3341 CONTRACTL_END;
3342
3343 // Check method flags first before trying to find the custom value
3344 if (!IsReallyMdPinvokeImpl(dwMemberAttrs))
3345 return S_FALSE;
3346
3347 DWORD mappingFlags;
3348 LPCSTR pszImportName;
3349 mdModuleRef modref;
3350
3351 if (SUCCEEDED(pInternalImport->GetPinvokeMap(token, &mappingFlags, &pszImportName, &modref)))
3352 return S_OK;
3353
3354 return S_FALSE;
3355}
3356
3357
3358// Either MD or signature & module must be given.
3359/*static*/
3360BOOL NDirect::MarshalingRequired(MethodDesc *pMD, PCCOR_SIGNATURE pSig /*= NULL*/, Module *pModule /*= NULL*/)
3361{
3362 CONTRACTL
3363 {
3364 STANDARD_VM_CHECK;
3365 PRECONDITION(pMD != NULL || (pSig != NULL && pModule != NULL));
3366 }
3367 CONTRACTL_END;
3368
3369 // As a by-product, when returning FALSE we will also set the native stack size to the MD if it's
3370 // an NDirectMethodDesc. This number is needed to link the P/Invoke (it determines the @n entry
3371 // point name suffix and affects alignment thunk generation on the Mac). If this method returns
3372 // TRUE, the stack size will be set when building the marshaling IL stub.
3373 DWORD dwStackSize = 0;
3374 CorPinvokeMap callConv = (CorPinvokeMap)0;
3375
3376 if (pMD != NULL)
3377 {
3378 if (pMD->IsNDirect() || pMD->IsComPlusCall())
3379 {
3380 // HRESULT swapping is handled by stub
3381 if ((pMD->GetImplAttrs() & miPreserveSig) == 0)
3382 return TRUE;
3383 }
3384
3385 // SetLastError is handled by stub
3386 PInvokeStaticSigInfo sigInfo(pMD);
3387 if (sigInfo.GetLinkFlags() & nlfLastError)
3388 return TRUE;
3389
3390 // LCID argument is handled by stub
3391 if (GetLCIDParameterIndex(pMD) != -1)
3392 return TRUE;
3393
3394 // making sure that cctor has run may be handled by stub
3395 if (pMD->IsNDirect() && ((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
3396 return TRUE;
3397
3398 callConv = sigInfo.GetCallConv();
3399 }
3400
3401 if (pSig == NULL)
3402 {
3403 PREFIX_ASSUME(pMD != NULL);
3404
3405 pSig = pMD->GetSig();
3406 pModule = pMD->GetModule();
3407 }
3408
3409 // Check to make certain that the signature only contains types that marshal trivially
3410 SigPointer ptr(pSig);
3411 IfFailThrow(ptr.GetCallingConvInfo(NULL));
3412 ULONG numArgs;
3413 IfFailThrow(ptr.GetData(&numArgs));
3414 numArgs++; // +1 for return type
3415
3416 // We'll need to parse parameter native types
3417 mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef));
3418 IMDInternalImport *pMDImport = pModule->GetMDImport();
3419
3420 SigTypeContext emptyTypeContext;
3421
3422 mdMethodDef methodToken = mdMethodDefNil;
3423 if (pMD != NULL)
3424 {
3425 methodToken = pMD->GetMemberDef();
3426 }
3427 CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
3428
3429 for (ULONG i = 0; i < numArgs; i++)
3430 {
3431 SigPointer arg = ptr;
3432 CorElementType type;
3433 IfFailThrow(arg.PeekElemType(&type));
3434
3435 switch (type)
3436 {
3437 case ELEMENT_TYPE_PTR:
3438 {
3439 IfFailThrow(arg.GetElemType(NULL)); // skip ELEMENT_TYPE_PTR
3440 IfFailThrow(arg.PeekElemType(&type));
3441
3442 if (type == ELEMENT_TYPE_VALUETYPE)
3443 {
3444 if ((arg.HasCustomModifier(pModule,
3445 "Microsoft.VisualC.NeedsCopyConstructorModifier",
3446 ELEMENT_TYPE_CMOD_REQD)) ||
3447 (arg.HasCustomModifier(pModule,
3448 "System.Runtime.CompilerServices.IsCopyConstructed",
3449 ELEMENT_TYPE_CMOD_REQD)))
3450 {
3451 return TRUE;
3452 }
3453 }
3454 if (i > 0) dwStackSize += sizeof(SLOT);
3455 break;
3456 }
3457
3458 case ELEMENT_TYPE_INTERNAL:
3459
3460 // this check is not functional in DAC and provides no security against a malicious dump
3461 // the DAC is prepared to receive an invalid type handle
3462#ifndef DACCESS_COMPILE
3463 if (pModule->IsSigInIL(arg.GetPtr()))
3464 THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule);
3465#endif
3466
3467 /* Fall thru */
3468
3469 case ELEMENT_TYPE_VALUETYPE:
3470 {
3471 TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext);
3472
3473 // JIT can handle internal blittable value types
3474 if (!hndArgType.IsBlittable() && !hndArgType.IsEnum())
3475 {
3476 return TRUE;
3477 }
3478
3479 // return value is fine as long as it can be normalized to an integer
3480 if (i == 0)
3481 {
3482 CorElementType normalizedType = hndArgType.GetInternalCorElementType();
3483 if (normalizedType == ELEMENT_TYPE_VALUETYPE)
3484 {
3485 // it is a structure even after normalization
3486 return TRUE;
3487 }
3488 }
3489 else
3490 {
3491 dwStackSize += StackElemSize(hndArgType.GetSize());
3492 }
3493 break;
3494 }
3495
3496 case ELEMENT_TYPE_BOOLEAN:
3497 case ELEMENT_TYPE_CHAR:
3498 {
3499 // Bool requires marshaling
3500 // Char may require marshaling (MARSHAL_TYPE_ANSICHAR)
3501 return TRUE;
3502 }
3503
3504 default:
3505 {
3506 if (CorTypeInfo::IsPrimitiveType(type) || type == ELEMENT_TYPE_FNPTR)
3507 {
3508 if (i > 0) dwStackSize += StackElemSize(CorTypeInfo::Size(type));
3509 }
3510 else
3511 {
3512 // other non-primitive type - requires marshaling
3513 return TRUE;
3514 }
3515 }
3516 }
3517
3518 // check for explicit MarshalAs
3519 NativeTypeParamInfo paramInfo;
3520
3521 if (pParamTokenArray[i] != mdParamDefNil)
3522 {
3523 if (!ParseNativeTypeInfo(pParamTokenArray[i], pMDImport, &paramInfo) ||
3524 paramInfo.m_NativeType != NATIVE_TYPE_DEFAULT)
3525 {
3526 // Presence of MarshalAs does not necessitate marshaling (it could as well be the default
3527 // for the type), but it's a good enough heuristic. We definitely don't want to duplicate
3528 // the logic from code:MarshalInfo.MarshalInfo here.
3529 return TRUE;
3530 }
3531 }
3532
3533 IfFailThrow(ptr.SkipExactlyOne());
3534 }
3535
3536 if (!FitsInU2(dwStackSize))
3537 return TRUE;
3538
3539 // do not set the stack size for varargs - the number is call site specific
3540 if (pMD != NULL && !pMD->IsVarArg())
3541 {
3542 if (pMD->IsNDirect())
3543 {
3544 ((NDirectMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize), callConv);
3545 }
3546#ifdef FEATURE_COMINTEROP
3547 else if (pMD->IsComPlusCall())
3548 {
3549 // calling convention is always stdcall
3550 ((ComPlusCallMethodDesc *)pMD)->SetStackArgumentSize(static_cast<WORD>(dwStackSize));
3551 }
3552#endif // FEATURE_COMINTEROP
3553 }
3554
3555 return FALSE;
3556}
3557
3558
3559// factorization of CreateNDirectStubWorker
3560static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig,
3561 mdParamDef* params,
3562 CorNativeLinkType nlType,
3563 CorNativeLinkFlags nlFlags,
3564 UINT argidx, // this is used for reverse pinvoke hresult swapping
3565 StubState* pss,
3566 BOOL fThisCall,
3567 int argOffset,
3568 DWORD dwStubFlags,
3569 MethodDesc *pMD,
3570 UINT& nativeStackOffset,
3571 bool& fStubNeedsCOM,
3572 int nativeArgIndex
3573 DEBUG_ARG(LPCUTF8 pDebugName)
3574 DEBUG_ARG(LPCUTF8 pDebugClassName)
3575 )
3576{
3577 CONTRACTL
3578 {
3579 STANDARD_VM_CHECK;
3580
3581 PRECONDITION(CheckPointer(params));
3582 PRECONDITION(CheckPointer(pss));
3583 PRECONDITION(CheckPointer(pMD, NULL_OK));
3584 }
3585 CONTRACTL_END;
3586
3587 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3588
3589 MarshalInfo::MarshalScenario ms;
3590#ifdef FEATURE_COMINTEROP
3591 if (SF_IsCOMStub(dwStubFlags))
3592 {
3593 if (SF_IsWinRTStub(dwStubFlags))
3594 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
3595 else
3596 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
3597 }
3598 else
3599#endif // FEATURE_COMINTEROP
3600 {
3601 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
3602 }
3603
3604#ifdef FEATURE_COMINTEROP
3605 if (SF_IsWinRTCtorStub(dwStubFlags))
3606 {
3607 _ASSERTE(msig.GetReturnType() == ELEMENT_TYPE_VOID);
3608 _ASSERTE(SF_IsHRESULTSwapping(dwStubFlags));
3609
3610 pss->MarshalFactoryReturn();
3611 nativeStackOffset += sizeof(LPVOID);
3612 if (SF_IsWinRTCompositionStub(dwStubFlags))
3613 {
3614 nativeStackOffset += 2 * sizeof(LPVOID);
3615 }
3616 }
3617 else
3618#endif // FEATURE_COMINTEROP
3619 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
3620 {
3621 MarshalInfo returnInfo(msig.GetModule(),
3622 msig.GetReturnProps(),
3623 msig.GetSigTypeContext(),
3624 params[0],
3625 ms,
3626 nlType,
3627 nlFlags,
3628 FALSE,
3629 argidx,
3630 msig.NumFixedArgs(),
3631 SF_IsBestFit(dwStubFlags),
3632 SF_IsThrowOnUnmappableChar(dwStubFlags),
3633 TRUE,
3634 pMD,
3635 TRUE
3636 DEBUG_ARG(pDebugName)
3637 DEBUG_ARG(pDebugClassName)
3638 DEBUG_ARG(0)
3639 );
3640
3641 marshalType = returnInfo.GetMarshalType();
3642
3643 fStubNeedsCOM |= returnInfo.MarshalerRequiresCOM();
3644
3645#ifdef FEATURE_COMINTEROP
3646 if (marshalType == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3647 {
3648 // Hidden length arrays are only valid with HRESULT swapping
3649 if (!SF_IsHRESULTSwapping(dwStubFlags))
3650 {
3651 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3652 }
3653
3654 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3655 returnInfo.SetHiddenLengthParamIndex(static_cast<UINT16>(nativeArgIndex));
3656
3657 // Inject the hidden argument so that it winds up at the end of the method signature
3658 pss->MarshalHiddenLengthArgument(&returnInfo, TRUE);
3659 nativeStackOffset += returnInfo.GetHiddenLengthParamStackSize();
3660
3661 if (SF_IsReverseStub(dwStubFlags))
3662 {
3663 ++argOffset;
3664 }
3665 }
3666
3667 if (SF_IsCOMStub(dwStubFlags))
3668 {
3669 if (marshalType == MarshalInfo::MARSHAL_TYPE_VALUECLASS ||
3670 marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS ||
3671 marshalType == MarshalInfo::MARSHAL_TYPE_GUID ||
3672 marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL)
3673 {
3674#ifndef _TARGET_X86_
3675 // We cannot optimize marshalType to MARSHAL_TYPE_GENERIC_* because the JIT works with exact types
3676 // and would refuse to compile the stub if it implicitly converted between scalars and value types (also see
3677 // code:MarshalInfo.MarhalInfo where we do the optimization on x86). We want to throw only if the structure
3678 // is too big to be returned in registers.
3679 if (marshalType != MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS ||
3680 IsUnmanagedValueTypeReturnedByRef(returnInfo.GetNativeArgSize()))
3681#endif // _TARGET_X86_
3682 {
3683 if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
3684 {
3685 // Note that this limitation is very likely not needed anymore and could be lifted if we care.
3686 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3687 }
3688 }
3689
3690 pss->MarshalReturn(&returnInfo, argOffset);
3691 }
3692 else
3693 {
3694 // We don't support native methods that return VARIANTs directly.
3695 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT)
3696 {
3697 if (!SF_IsHRESULTSwapping(dwStubFlags) && !SF_IsCOMLateBoundStub(dwStubFlags))
3698 {
3699 COMPlusThrow(kMarshalDirectiveException, IDS_EE_COM_UNSUPPORTED_SIG);
3700 }
3701 }
3702
3703 pss->MarshalReturn(&returnInfo, argOffset);
3704 }
3705 }
3706 else
3707#endif // FEATURE_COMINTEROP
3708 {
3709 if (marshalType > MarshalInfo::MARSHAL_TYPE_DOUBLE && IsUnsupportedValueTypeReturn(msig))
3710 {
3711 if (marshalType == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASS
3712 || marshalType == MarshalInfo::MARSHAL_TYPE_GUID
3713 || marshalType == MarshalInfo::MARSHAL_TYPE_DECIMAL
3714#ifdef FEATURE_COMINTEROP
3715 || marshalType == MarshalInfo::MARSHAL_TYPE_DATETIME
3716#endif // FEATURE_COMINTEROP
3717 )
3718 {
3719 if (SF_IsHRESULTSwapping(dwStubFlags))
3720 {
3721 // V1 restriction: we could implement this but it's late in the game to do so.
3722 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3723 }
3724 }
3725 else if (marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF)
3726 {
3727 COMPlusThrow(kMarshalDirectiveException, IDS_EE_BADMARSHAL_HANDLEREFRESTRICTION);
3728 }
3729 else
3730 {
3731 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_UNSUPPORTED_SIG);
3732 }
3733 }
3734
3735#ifdef FEATURE_COMINTEROP
3736 if (marshalType == MarshalInfo::MARSHAL_TYPE_OBJECT && !SF_IsHRESULTSwapping(dwStubFlags))
3737 {
3738 // No support for returning variants. This is a V1 restriction, due to the late date,
3739 // don't want to add the special-case code to support this in light of low demand.
3740 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NOVARIANTRETURN);
3741 }
3742#endif // FEATURE_COMINTEROP
3743
3744 pss->MarshalReturn(&returnInfo, argOffset);
3745 }
3746 }
3747
3748 return marshalType;
3749}
3750
3751static inline UINT GetStackOffsetFromStackSize(UINT stackSize, bool fThisCall)
3752{
3753 LIMITED_METHOD_CONTRACT;
3754#ifdef _TARGET_X86_
3755 if (fThisCall)
3756 {
3757 // -1 means that the argument is not on the stack
3758 return (stackSize >= sizeof(SLOT) ? (stackSize - sizeof(SLOT)) : (UINT)-1);
3759 }
3760#endif // _TARGET_X86_
3761 return stackSize;
3762}
3763
3764#ifdef FEATURE_COMINTEROP
3765
3766struct HiddenParameterInfo
3767{
3768 MarshalInfo *pManagedParam; // Managed parameter which required the hidden parameter
3769 int nativeIndex; // 0 based index into the native method signature where the hidden parameter should be injected
3770};
3771
3772// Get the indexes of any hidden length parameters to be marshaled for the method
3773//
3774// At return, each value in the ppParamIndexes array is a 0 based index into the native method signature where
3775// the length parameter for a hidden length array should be passed. The MarshalInfo objects will also be
3776// updated such that they all have explicit marshaling information.
3777//
3778// The caller is responsible for freeing the memory pointed to by ppParamIndexes
3779void CheckForHiddenParameters(DWORD cParamMarshalInfo,
3780 __in_ecount(cParamMarshalInfo) MarshalInfo *pParamMarshalInfo,
3781 __out DWORD *pcHiddenNativeParameters,
3782 __out HiddenParameterInfo **ppHiddenNativeParameters)
3783{
3784 CONTRACTL
3785 {
3786 STANDARD_VM_CHECK;
3787 PRECONDITION(CheckPointer(pParamMarshalInfo));
3788 PRECONDITION(CheckPointer(pcHiddenNativeParameters));
3789 PRECONDITION(CheckPointer(ppHiddenNativeParameters));
3790 }
3791 CONTRACTL_END;
3792
3793 NewArrayHolder<HiddenParameterInfo> hiddenParamInfo(new HiddenParameterInfo[cParamMarshalInfo]);
3794 DWORD foundInfoCount = 0;
3795
3796 for (DWORD iParam = 0; iParam < cParamMarshalInfo; ++iParam)
3797 {
3798 // Look for hidden length arrays, which all require additional parameters to be added
3799 if (pParamMarshalInfo[iParam].GetMarshalType() == MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY)
3800 {
3801 DWORD currentNativeIndex = iParam + foundInfoCount;
3802
3803 // The location of the length parameter is implicitly just before the array pointer.
3804 // We'll give it our current index, and bumping the found count will push us back a slot.
3805
3806 // We should be safe to cast here - giant signatures will fail to marashal later with IDS_EE_SIGTOOCOMPLEX
3807 pParamMarshalInfo[iParam].SetHiddenLengthParamIndex(static_cast<UINT16>(currentNativeIndex));
3808
3809 hiddenParamInfo[foundInfoCount].nativeIndex = pParamMarshalInfo[iParam].HiddenLengthParamIndex();
3810 hiddenParamInfo[foundInfoCount].pManagedParam = &(pParamMarshalInfo[iParam]);
3811 ++foundInfoCount;
3812 }
3813 }
3814
3815 *pcHiddenNativeParameters = foundInfoCount;
3816 *ppHiddenNativeParameters = hiddenParamInfo.Extract();
3817}
3818
3819bool IsHiddenParameter(int nativeArgIndex,
3820 DWORD cHiddenParameters,
3821 __in_ecount(cHiddenParameters) HiddenParameterInfo *pHiddenParameters,
3822 __out HiddenParameterInfo **ppHiddenParameterInfo)
3823{
3824 CONTRACTL
3825 {
3826 STANDARD_VM_CHECK;
3827 PRECONDITION(cHiddenParameters == 0 || CheckPointer(pHiddenParameters));
3828 PRECONDITION(CheckPointer(ppHiddenParameterInfo));
3829 }
3830 CONTRACTL_END;
3831
3832 *ppHiddenParameterInfo = NULL;
3833
3834 for (DWORD i = 0; i < cHiddenParameters; ++i)
3835 {
3836 _ASSERTE(pHiddenParameters[i].nativeIndex != -1);
3837 if (pHiddenParameters[i].nativeIndex == nativeArgIndex)
3838 {
3839 *ppHiddenParameterInfo = &(pHiddenParameters[i]);
3840 return true;
3841 }
3842 }
3843
3844 return false;
3845}
3846
3847#endif // FEATURE_COMINTEROP
3848
3849//---------------------------------------------------------
3850// Creates a new stub for a N/Direct call. Return refcount is 1.
3851// Note that this function may now throw if it fails to create
3852// a stub.
3853//---------------------------------------------------------
3854static void CreateNDirectStubWorker(StubState* pss,
3855 StubSigDesc* pSigDesc,
3856 CorNativeLinkType nlType,
3857 CorNativeLinkFlags nlFlags,
3858 CorPinvokeMap unmgdCallConv,
3859 DWORD dwStubFlags,
3860 MethodDesc *pMD,
3861 mdParamDef* pParamTokenArray,
3862 int iLCIDArg
3863 )
3864{
3865 CONTRACTL
3866 {
3867 STANDARD_VM_CHECK;
3868
3869 PRECONDITION(CheckPointer(pss));
3870 PRECONDITION(CheckPointer(pSigDesc));
3871 PRECONDITION(CheckPointer(pMD, NULL_OK));
3872 PRECONDITION(!pMD || pMD->IsILStub() || (0 != pMD->GetMethodTable()->IsDelegate()) == SF_IsDelegateStub(dwStubFlags));
3873 }
3874 CONTRACTL_END;
3875
3876 SF_ConsistencyCheck(dwStubFlags);
3877
3878#ifdef _DEBUG
3879 if (g_pConfig->ShouldBreakOnInteropStubSetup(pSigDesc->m_pDebugName))
3880 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInteropStubSetup: '%s' ", pSigDesc->m_pDebugName));
3881#endif // _DEBUG
3882
3883 Stub* pstub = NULL;
3884
3885 if (SF_IsCOMStub(dwStubFlags))
3886 {
3887 _ASSERTE(0 == nlType);
3888 _ASSERTE(0 == nlFlags);
3889 _ASSERTE(0 == unmgdCallConv);
3890 }
3891 else
3892 {
3893 _ASSERTE(nlType == nltAnsi || nlType == nltUnicode);
3894 }
3895 Module *pModule = pSigDesc->m_pModule;
3896
3897 //
3898 // Set up signature walking objects.
3899 //
3900
3901 MetaSig msig(pSigDesc->m_sig,
3902 pModule,
3903 &pSigDesc->m_typeContext);
3904
3905 if (SF_IsVarArgStub(dwStubFlags))
3906 msig.SetTreatAsVarArg();
3907
3908 bool fThisCall = (unmgdCallConv == pmCallConvThiscall);
3909
3910 pss->SetLastError(nlFlags & nlfLastError);
3911
3912 // This has been in the product since forward P/Invoke via delegates was
3913 // introduced. It's wrong, but please keep it for backward compatibility.
3914 if (SF_IsDelegateStub(dwStubFlags))
3915 pss->SetLastError(TRUE);
3916
3917 pss->BeginEmit(dwStubFlags);
3918
3919 if (-1 != iLCIDArg)
3920 {
3921 // LCID is not supported on WinRT
3922 _ASSERTE(!SF_IsWinRTStub(dwStubFlags));
3923
3924 // The code to handle the LCID will call MarshalLCID before calling MarshalArgument
3925 // on the argument the LCID should go after. So we just bump up the index here.
3926 iLCIDArg++;
3927 }
3928
3929 int numArgs = msig.NumFixedArgs();
3930
3931 // thiscall must have at least one parameter (the "this")
3932 if (fThisCall && numArgs == 0)
3933 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
3934
3935 //
3936 // Now, emit the IL.
3937 //
3938
3939 int argOffset = 0;
3940
3941 MarshalInfo::MarshalType marshalType = (MarshalInfo::MarshalType) 0xcccccccc;
3942
3943 //
3944 // Marshal the return value.
3945 //
3946
3947 UINT nativeStackSize = (SF_IsCOMStub(dwStubFlags) ? sizeof(SLOT) : 0);
3948 bool fHasCopyCtorArgs = false;
3949 bool fStubNeedsCOM = SF_IsCOMStub(dwStubFlags);
3950
3951 // Normally we would like this to be false so that we use the correct signature
3952 // in the IL_STUB, (i.e if it returns a value class then the signature will use that)
3953 // When this bool is true we change the return type to void and explicitly add a
3954 // return buffer argument as the first argument.
3955 BOOL fMarshalReturnValueFirst = false;
3956
3957 // We can only change fMarshalReturnValueFirst to true when we are NOT doing HRESULT-swapping!
3958 //
3959 if (!SF_IsHRESULTSwapping(dwStubFlags))
3960 {
3961
3962#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3963 // JIT32 has problems in generating code for pinvoke ILStubs which do a return in return buffer.
3964 // Therefore instead we change the signature of calli to return void and make the return buffer as first
3965 // argument. This matches the ABI i.e. return buffer is passed as first arg. So native target will get the
3966 // return buffer in correct register.
3967 // The return structure secret arg comes first, however byvalue return is processed at
3968 // the end because it could be the HRESULT-swapped argument which always comes last.
3969
3970#ifdef UNIX_X86_ABI
3971 // For functions with value type class, managed and unmanaged calling convention differ
3972 fMarshalReturnValueFirst = HasRetBuffArgUnmanagedFixup(&msig);
3973#else // UNIX_X86_ABI
3974 fMarshalReturnValueFirst = HasRetBuffArg(&msig);
3975#endif // UNIX_X86_ABI
3976
3977#endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
3978
3979 }
3980
3981 if (fMarshalReturnValueFirst)
3982 {
3983 marshalType = DoMarshalReturnValue(msig,
3984 pParamTokenArray,
3985 nlType,
3986 nlFlags,
3987 0,
3988 pss,
3989 fThisCall,
3990 argOffset,
3991 dwStubFlags,
3992 pMD,
3993 nativeStackSize,
3994 fStubNeedsCOM,
3995 0
3996 DEBUG_ARG(pSigDesc->m_pDebugName)
3997 DEBUG_ARG(pSigDesc->m_pDebugClassName)
3998 );
3999
4000 if (marshalType == MarshalInfo::MARSHAL_TYPE_DATE ||
4001 marshalType == MarshalInfo::MARSHAL_TYPE_CURRENCY ||
4002 marshalType == MarshalInfo::MARSHAL_TYPE_ARRAYWITHOFFSET ||
4003 marshalType == MarshalInfo::MARSHAL_TYPE_HANDLEREF ||
4004 marshalType == MarshalInfo::MARSHAL_TYPE_ARGITERATOR
4005#ifdef FEATURE_COMINTEROP
4006 || marshalType == MarshalInfo::MARSHAL_TYPE_OLECOLOR
4007#endif // FEATURE_COMINTEROP
4008 )
4009 {
4010 // These are special non-blittable types returned by-ref in managed,
4011 // but marshaled as primitive values returned by-value in unmanaged.
4012 }
4013 else
4014 {
4015 // This is an ordinary value type - see if it is returned by-ref.
4016 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4017 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4018 {
4019 nativeStackSize += sizeof(LPVOID);
4020 }
4021 }
4022 }
4023
4024 //
4025 // Marshal the arguments
4026 //
4027 MarshalInfo::MarshalScenario ms;
4028#ifdef FEATURE_COMINTEROP
4029 if (SF_IsCOMStub(dwStubFlags))
4030 {
4031 if (SF_IsWinRTStub(dwStubFlags))
4032 ms = MarshalInfo::MARSHAL_SCENARIO_WINRT;
4033 else
4034 ms = MarshalInfo::MARSHAL_SCENARIO_COMINTEROP;
4035 }
4036 else
4037#endif // FEATURE_COMINTEROP
4038 {
4039 ms = MarshalInfo::MARSHAL_SCENARIO_NDIRECT;
4040 }
4041
4042 // Build up marshaling information for each of the method's parameters
4043 SIZE_T cbParamMarshalInfo;
4044 if (!ClrSafeInt<SIZE_T>::multiply(sizeof(MarshalInfo), numArgs, cbParamMarshalInfo))
4045 {
4046 COMPlusThrowHR(COR_E_OVERFLOW);
4047 }
4048
4049 NewArrayHolder<BYTE> pbParamMarshalInfo(new BYTE[cbParamMarshalInfo]);
4050 MarshalInfo *pParamMarshalInfo = reinterpret_cast<MarshalInfo *>(pbParamMarshalInfo.GetValue());
4051
4052 MetaSig paramInfoMSig(msig);
4053 for (int i = 0; i < numArgs; ++i)
4054 {
4055 paramInfoMSig.NextArg();
4056 new(&(pParamMarshalInfo[i])) MarshalInfo(paramInfoMSig.GetModule(),
4057 paramInfoMSig.GetArgProps(),
4058 paramInfoMSig.GetSigTypeContext(),
4059 pParamTokenArray[i + 1],
4060 ms,
4061 nlType,
4062 nlFlags,
4063 TRUE,
4064 i + 1,
4065 numArgs,
4066 SF_IsBestFit(dwStubFlags),
4067 SF_IsThrowOnUnmappableChar(dwStubFlags),
4068 TRUE,
4069 pMD,
4070 TRUE
4071 DEBUG_ARG(pSigDesc->m_pDebugName)
4072 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4073 DEBUG_ARG(i + 1));
4074 }
4075
4076#ifdef FEATURE_COMINTEROP
4077 // Check to see if we need to inject any additional hidden parameters
4078 DWORD cHiddenNativeParameters;
4079 NewArrayHolder<HiddenParameterInfo> pHiddenNativeParameters;
4080 CheckForHiddenParameters(numArgs, pParamMarshalInfo, &cHiddenNativeParameters, &pHiddenNativeParameters);
4081
4082 // Hidden parameters and LCID do not mix
4083 _ASSERTE(!(cHiddenNativeParameters > 0 && iLCIDArg != -1));
4084#endif // FEATURE_COMINTEROP
4085
4086 // Marshal the parameters
4087 int argidx = 1;
4088 int nativeArgIndex = 0;
4089 while (argidx <= numArgs)
4090 {
4091#ifdef FEATURE_COMINTEROP
4092 HiddenParameterInfo *pHiddenParameter;
4093 // Check to see if we need to inject a hidden parameter
4094 if (IsHiddenParameter(nativeArgIndex, cHiddenNativeParameters, pHiddenNativeParameters, &pHiddenParameter))
4095 {
4096 pss->MarshalHiddenLengthArgument(pHiddenParameter->pManagedParam, FALSE);
4097 nativeStackSize += pHiddenParameter->pManagedParam->GetHiddenLengthParamStackSize();
4098
4099 if (SF_IsReverseStub(dwStubFlags))
4100 {
4101 ++argOffset;
4102 }
4103 }
4104 else
4105#endif // FEATURE_COMINTEROP
4106 {
4107 //
4108 // Check to see if this is the parameter after which we need to insert the LCID.
4109 //
4110 if (argidx == iLCIDArg)
4111 {
4112 pss->MarshalLCID(argidx);
4113 nativeStackSize += sizeof(LPVOID);
4114
4115 if (SF_IsReverseStub(dwStubFlags))
4116 argOffset++;
4117 }
4118
4119 msig.NextArg();
4120
4121 MarshalInfo &info = pParamMarshalInfo[argidx - 1];
4122
4123#ifdef FEATURE_COMINTEROP
4124 // For the hidden-length array, length parameters must occur before the parameter containing the array pointer
4125 _ASSERTE(info.GetMarshalType() != MarshalInfo::MARSHAL_TYPE_HIDDENLENGTHARRAY || nativeArgIndex > info.HiddenLengthParamIndex());
4126#endif // FEATURE_COMINTEROP
4127
4128 pss->MarshalArgument(&info, argOffset, GetStackOffsetFromStackSize(nativeStackSize, fThisCall));
4129 nativeStackSize += info.GetNativeArgSize();
4130
4131 fStubNeedsCOM |= info.MarshalerRequiresCOM();
4132
4133 if (fThisCall && argidx == 1)
4134 {
4135 // make sure that the first parameter is enregisterable
4136 if (info.GetNativeArgSize() > sizeof(SLOT))
4137 COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
4138 }
4139
4140
4141 argidx++;
4142 }
4143
4144 ++nativeArgIndex;
4145 }
4146
4147 // Check to see if this is the parameter after which we need to insert the LCID.
4148 if (argidx == iLCIDArg)
4149 {
4150 pss->MarshalLCID(argidx);
4151 nativeStackSize += sizeof(LPVOID);
4152
4153 if (SF_IsReverseStub(dwStubFlags))
4154 argOffset++;
4155 }
4156
4157 if (!fMarshalReturnValueFirst)
4158 {
4159 // This could be a HRESULT-swapped argument so it must come last.
4160 marshalType = DoMarshalReturnValue(msig,
4161 pParamTokenArray,
4162 nlType,
4163 nlFlags,
4164 argidx,
4165 pss,
4166 fThisCall,
4167 argOffset,
4168 dwStubFlags,
4169 pMD,
4170 nativeStackSize,
4171 fStubNeedsCOM,
4172 nativeArgIndex
4173 DEBUG_ARG(pSigDesc->m_pDebugName)
4174 DEBUG_ARG(pSigDesc->m_pDebugClassName)
4175 );
4176
4177 // If the return value is a SafeHandle or CriticalHandle, mark the stub method.
4178 // Interop methods that use this stub will have an implicit reliability contract
4179 // (see code:TAStackCrawlCallBack).
4180 if (!SF_IsHRESULTSwapping(dwStubFlags))
4181 {
4182 if (marshalType == MarshalInfo::MARSHAL_TYPE_SAFEHANDLE ||
4183 marshalType == MarshalInfo::MARSHAL_TYPE_CRITICALHANDLE)
4184 {
4185 if (pMD->IsDynamicMethod())
4186 pMD->AsDynamicMethodDesc()->SetUnbreakable(true);
4187 }
4188 }
4189 }
4190
4191 if (SF_IsHRESULTSwapping(dwStubFlags))
4192 {
4193 if (msig.GetReturnType() != ELEMENT_TYPE_VOID)
4194 nativeStackSize += sizeof(LPVOID);
4195 }
4196
4197 if (pMD->IsDynamicMethod())
4198 {
4199 // Set the native stack size to the IL stub MD. It is needed for alignment
4200 // thunk generation on the Mac and stdcall name decoration on Windows.
4201 // We do not store it directly in the interop MethodDesc here because due
4202 // to sharing we come here only for the first call with given signature and
4203 // the target MD may even be NULL.
4204
4205#ifdef _TARGET_X86_
4206 if (fThisCall)
4207 {
4208 _ASSERTE(nativeStackSize >= sizeof(SLOT));
4209 nativeStackSize -= sizeof(SLOT);
4210 }
4211#else // _TARGET_X86_
4212 //
4213 // The algorithm to compute nativeStackSize on the fly is x86-specific.
4214 // Recompute the correct size for other platforms from the stub signature.
4215 //
4216 if (SF_IsForwardStub(dwStubFlags))
4217 {
4218 // It would be nice to compute the correct value for forward stubs too.
4219 // The value is only used in MarshalNative::NumParamBytes right now,
4220 // and changing what MarshalNative::NumParamBytes returns is
4221 // a potential breaking change.
4222 }
4223 else
4224 {
4225 // native stack size is updated in code:ILStubState.SwapStubSignatures
4226 }
4227#endif // _TARGET_X86_
4228
4229 if (!FitsInU2(nativeStackSize))
4230 COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX);
4231
4232 DynamicMethodDesc *pDMD = pMD->AsDynamicMethodDesc();
4233
4234 pDMD->SetNativeStackArgSize(static_cast<WORD>(nativeStackSize));
4235 pDMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
4236 pDMD->SetStubNeedsCOMStarted(fStubNeedsCOM);
4237 }
4238
4239 // FinishEmit needs to know the native stack arg size so we call it after the number
4240 // has been set in the stub MD (code:DynamicMethodDesc.SetNativeStackArgSize)
4241 pss->FinishEmit(pMD);
4242}
4243
4244class NDirectStubHashBlob : public ILStubHashBlobBase
4245{
4246public:
4247 Module* m_pModule;
4248
4249 WORD m_unmgdCallConv;
4250 BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob
4251 BYTE m_nlFlags;
4252
4253 DWORD m_StubFlags;
4254
4255 INT32 m_iLCIDArg;
4256 INT32 m_nParams;
4257 BYTE m_rgbSigAndParamData[1];
4258 // (dwParamAttr, cbNativeType) // length: number of parameters
4259 // NativeTypeBlob // length: number of parameters
4260 // BYTE m_rgbSigData[]; // length: determined by sig walk
4261};
4262
4263// For better performance and less memory fragmentation,
4264// I'm using structure here to avoid allocating 3 different arrays.
4265struct ParamInfo
4266{
4267 DWORD dwParamAttr;
4268 ULONG cbNativeType;
4269 PCCOR_SIGNATURE pvNativeType;
4270};
4271
4272ILStubHashBlob* NDirect::CreateHashBlob(NDirectStubParameters* pParams)
4273{
4274 STANDARD_VM_CONTRACT;
4275
4276 NDirectStubHashBlob* pBlob;
4277
4278 IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport();
4279
4280 CQuickBytes paramInfoBytes;
4281 paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens);
4282 ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr();
4283 ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens);
4284
4285 size_t cbNativeTypeTotal = 0;
4286
4287 //
4288 // Collect information for function parameters
4289 //
4290 for (int idx = 0; idx < pParams->m_nParamTokens; idx++)
4291 {
4292 mdParamDef token = pParams->m_pParamTokenArray[idx];
4293 if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token)
4294 {
4295 USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted
4296 LPCSTR szParamName_Ignore;
4297 IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, &paramInfos[idx].dwParamAttr, &szParamName_Ignore));
4298
4299 if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal)
4300 {
4301 IfFailThrow(pInternalImport->GetFieldMarshal(token, &paramInfos[idx].pvNativeType, &paramInfos[idx].cbNativeType));
4302 cbNativeTypeTotal += paramInfos[idx].cbNativeType;
4303 }
4304 }
4305 }
4306
4307 SigPointer sigPtr = pParams->m_sig.CreateSigPointer();
4308
4309 // note that ConvertToInternalSignature also resolves generics so different instantiations will get different
4310 // hash blobs for methods that have generic parameters in their signature
4311 SigBuilder sigBuilder;
4312 sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE);
4313
4314 DWORD cbSig;
4315 PVOID pSig = sigBuilder.GetSignature(&cbSig);
4316
4317 //
4318 // Build hash blob for IL stub sharing
4319 //
4320 S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) +
4321 S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes
4322 S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size
4323 S_SIZE_T(cbNativeTypeTotal) + // Native type blob data
4324 S_SIZE_T(cbSig); // Signature
4325
4326 if (cbSizeOfBlob.IsOverflow())
4327 COMPlusThrowHR(COR_E_OVERFLOW);
4328
4329 static_assert_no_msg(nltMaxValue <= 0xFF);
4330 static_assert_no_msg(nlfMaxValue <= 0xFF);
4331 static_assert_no_msg(pmMaxValue <= 0xFFFF);
4332
4333 NewArrayHolder<BYTE> pBytes = new BYTE[cbSizeOfBlob.Value()];
4334 // zero out the hash bytes to ensure all bit fields are deterministically set
4335 ZeroMemory(pBytes, cbSizeOfBlob.Value());
4336 pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes;
4337
4338 pBlob->m_pModule = NULL;
4339
4340 if (SF_IsNGENedStub(pParams->m_dwStubFlags))
4341 {
4342 // don't share across modules if we are ngening the stub
4343 pBlob->m_pModule = pParams->m_pModule;
4344 }
4345
4346 pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value();
4347 pBlob->m_unmgdCallConv = static_cast<WORD>(pParams->m_unmgdCallConv);
4348 pBlob->m_nlType = static_cast<BYTE>(pParams->m_nlType);
4349 pBlob->m_nlFlags = static_cast<BYTE>(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub
4350 pBlob->m_iLCIDArg = pParams->m_iLCIDArg;
4351
4352 pBlob->m_StubFlags = pParams->m_dwStubFlags;
4353 pBlob->m_nParams = pParams->m_nParamTokens;
4354
4355 BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0];
4356
4357 //
4358 // Write (dwParamAttr, cbNativeType) for parameters
4359 //
4360 // Note that these need to be aligned and it is why they are written before the byte blobs
4361 // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs
4362 //
4363 _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0);
4364 _ASSERTE(sizeof(DWORD) == sizeof(ULONG));
4365
4366 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4367 {
4368 // We only care about In/Out/HasFieldMarshal
4369 // Other attr are about optional/default values which are not used in marshalling,
4370 // but only used in compilers
4371 *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal);
4372 pBlobParams += sizeof(DWORD);
4373
4374 *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType;
4375 pBlobParams += sizeof(ULONG);
4376 }
4377
4378 //
4379 // Write native type blob for parameters
4380 //
4381 for (int i = 0; i < pParams->m_nParamTokens; ++i)
4382 {
4383 memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType);
4384 pBlobParams += paramInfos[i].cbNativeType;
4385 }
4386
4387 //
4388 // Copy signature
4389 //
4390 memcpy(pBlobParams, pSig, cbSig);
4391
4392 // Verify that we indeed have reached the end
4393 _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value());
4394
4395 pBytes.SuppressRelease();
4396 return (ILStubHashBlob*)pBlob;
4397}
4398
4399// static inline
4400ILStubCache* NDirect::GetILStubCache(NDirectStubParameters* pParams)
4401{
4402 CONTRACTL
4403 {
4404 THROWS;
4405 GC_NOTRIGGER;
4406 MODE_ANY;
4407 }
4408 CONTRACTL_END;
4409
4410 // Use the m_pLoaderModule instead of m_pModule
4411 // They could be different for methods on generic types.
4412 return pParams->m_pLoaderModule->GetILStubCache();
4413}
4414
4415// static
4416MethodDesc* NDirect::GetStubMethodDesc(
4417 MethodDesc *pTargetMD,
4418 NDirectStubParameters* pParams,
4419 ILStubHashBlob* pHashParams,
4420 AllocMemTracker* pamTracker,
4421 bool& bILStubCreator,
4422 MethodDesc* pLastMD)
4423{
4424 CONTRACT(MethodDesc*)
4425 {
4426 STANDARD_VM_CHECK;
4427
4428 PRECONDITION(CheckPointer(pParams));
4429 PRECONDITION(!pParams->m_sig.IsEmpty());
4430 PRECONDITION(CheckPointer(pParams->m_pModule));
4431 PRECONDITION(CheckPointer(pTargetMD, NULL_OK));
4432 POSTCONDITION(CheckPointer(RETVAL));
4433 }
4434 CONTRACT_END;
4435
4436 MethodDesc* pMD;
4437
4438 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4439
4440 pMD = pCache->GetStubMethodDesc(pTargetMD,
4441 pHashParams,
4442 pParams->m_dwStubFlags,
4443 pParams->m_pModule,
4444 pParams->m_sig.GetRawSig(),
4445 pParams->m_sig.GetRawSigLen(),
4446 pamTracker,
4447 bILStubCreator,
4448 pLastMD);
4449
4450 RETURN pMD;
4451}
4452
4453
4454// static
4455void NDirect::RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams)
4456{
4457 CONTRACTL
4458 {
4459 STANDARD_VM_CHECK;
4460
4461 PRECONDITION(CheckPointer(pParams));
4462 PRECONDITION(CheckPointer(pHashParams));
4463 PRECONDITION(!pParams->m_sig.IsEmpty());
4464 PRECONDITION(CheckPointer(pParams->m_pModule));
4465 }
4466 CONTRACTL_END;
4467
4468 LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams));
4469
4470 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4471
4472 pCache->DeleteEntry(pHashParams);
4473}
4474
4475// static
4476void NDirect::AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD)
4477{
4478 CONTRACTL
4479 {
4480 STANDARD_VM_CHECK;
4481
4482 PRECONDITION(CheckPointer(pParams));
4483 PRECONDITION(!pParams->m_sig.IsEmpty());
4484 PRECONDITION(CheckPointer(pParams->m_pModule));
4485 }
4486 CONTRACTL_END;
4487
4488 ILStubCache* pCache = NDirect::GetILStubCache(pParams);
4489
4490 pCache->AddMethodDescChunkWithLockTaken(pMD);
4491}
4492
4493//
4494// Additional factorization of CreateNDirectStub. This hoists all the metadata accesses
4495// into one location so that we can leave CreateNDirectStubWorker to just generate the
4496// IL. This allows us to cache a stub based on the inputs to CreateNDirectStubWorker
4497// instead of having to generate the IL first before doing the caching.
4498//
4499void CreateNDirectStubAccessMetadata(StubSigDesc* pSigDesc, // IN
4500 CorPinvokeMap unmgdCallConv, // IN
4501 DWORD* pdwStubFlags, // IN/OUT
4502 int* piLCIDArg, // OUT
4503 int* pNumArgs // OUT
4504 )
4505{
4506 STANDARD_VM_CONTRACT;
4507
4508 if (SF_IsCOMStub(*pdwStubFlags))
4509 {
4510 _ASSERTE(0 == unmgdCallConv);
4511 }
4512 else
4513 {
4514 if (unmgdCallConv != pmCallConvStdcall &&
4515 unmgdCallConv != pmCallConvCdecl &&
4516 unmgdCallConv != pmCallConvThiscall)
4517 {
4518 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
4519 }
4520 }
4521
4522#ifdef FEATURE_COMINTEROP
4523 if (SF_IsDelegateStub(*pdwStubFlags))
4524 {
4525 _ASSERTE(!SF_IsWinRTStub(*pdwStubFlags));
4526 if (pSigDesc->m_pMD->GetMethodTable()->IsProjectedFromWinRT())
4527 {
4528 // We do not allow P/Invoking via WinRT delegates to better segregate WinRT
4529 // from classic interop scenarios.
4530 COMPlusThrow(kMarshalDirectiveException, IDS_EE_DELEGATEPINVOKE_WINRT);
4531 }
4532 }
4533#endif // FEATURE_COMINTEROP
4534
4535 MetaSig msig(pSigDesc->m_sig,
4536 pSigDesc->m_pModule,
4537 &pSigDesc->m_typeContext);
4538
4539 if (SF_IsVarArgStub(*pdwStubFlags))
4540 msig.SetTreatAsVarArg();
4541
4542 (*pNumArgs) = msig.NumFixedArgs();
4543
4544 IMDInternalImport* pInternalImport = pSigDesc->m_pModule->GetMDImport();
4545
4546 _ASSERTE(!SF_IsHRESULTSwapping(*pdwStubFlags));
4547
4548 mdMethodDef md = pSigDesc->m_tkMethodDef;
4549 if (md != mdMethodDefNil)
4550 {
4551 DWORD dwDescrOffset;
4552 DWORD dwImplFlags;
4553 IfFailThrow(pInternalImport->GetMethodImplProps(
4554 md,
4555 &dwDescrOffset,
4556 &dwImplFlags));
4557
4558#ifdef FEATURE_COMINTEROP
4559 if (SF_IsWinRTStub(*pdwStubFlags))
4560 {
4561 // All WinRT methods do HRESULT swapping
4562 if (IsMiPreserveSig(dwImplFlags))
4563 {
4564 COMPlusThrow(kMarshalDirectiveException, IDS_EE_PRESERVESIG_WINRT);
4565 }
4566
4567 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4568 }
4569 else
4570#endif // FEATURE_COMINTEROP
4571 if (SF_IsReverseStub(*pdwStubFlags))
4572 {
4573 // only COM-to-CLR call supports hresult swapping in the reverse direction
4574 if (SF_IsCOMStub(*pdwStubFlags) && !IsMiPreserveSig(dwImplFlags))
4575 {
4576 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4577 }
4578 }
4579 else
4580 {
4581 // fwd pinvoke, fwd com interop support hresult swapping.
4582 // delegate to an unmanaged method does not.
4583 if (!IsMiPreserveSig(dwImplFlags) && !SF_IsDelegateStub(*pdwStubFlags))
4584 {
4585 (*pdwStubFlags) |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
4586 }
4587 }
4588 }
4589
4590 if (pSigDesc->m_pMD != NULL)
4591 {
4592 (*piLCIDArg) = GetLCIDParameterIndex(pSigDesc->m_pMD);
4593 }
4594 else
4595 {
4596 (*piLCIDArg) = -1;
4597 }
4598
4599 // Check to see if we need to do LCID conversion.
4600 if ((*piLCIDArg) != -1 && (*piLCIDArg) > (*pNumArgs))
4601 {
4602 COMPlusThrow(kIndexOutOfRangeException, IDS_EE_INVALIDLCIDPARAM);
4603 }
4604
4605 if (SF_IsCOMStub(*pdwStubFlags) && !SF_IsWinRTStaticStub(*pdwStubFlags))
4606 {
4607 CONSISTENCY_CHECK(msig.HasThis());
4608 }
4609 else
4610 {
4611 if (msig.HasThis() && !SF_IsDelegateStub(*pdwStubFlags))
4612 {
4613 COMPlusThrow(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC);
4614 }
4615 }
4616}
4617
4618void NDirect::PopulateNDirectMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, BOOL throwOnError /*= TRUE*/)
4619{
4620 if (pNMD->IsSynchronized() && throwOnError)
4621 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
4622
4623 WORD ndirectflags = 0;
4624 if (pNMD->MethodDesc::IsVarArg())
4625 ndirectflags |= NDirectMethodDesc::kVarArgs;
4626
4627 LPCUTF8 szLibName = NULL, szEntryPointName = NULL;
4628 new (pSigInfo) PInvokeStaticSigInfo(pNMD, &szLibName, &szEntryPointName,
4629 (throwOnError ? PInvokeStaticSigInfo::THROW_ON_ERROR : PInvokeStaticSigInfo::NO_THROW_ON_ERROR));
4630
4631 if (pSigInfo->GetCharSet() == nltAnsi)
4632 ndirectflags |= NDirectMethodDesc::kNativeAnsi;
4633
4634 CorNativeLinkFlags linkflags = pSigInfo->GetLinkFlags();
4635 if (linkflags & nlfLastError)
4636 ndirectflags |= NDirectMethodDesc::kLastError;
4637 if (linkflags & nlfNoMangle)
4638 ndirectflags |= NDirectMethodDesc::kNativeNoMangle;
4639
4640 CorPinvokeMap callConv = pSigInfo->GetCallConv();
4641 if (callConv == pmCallConvStdcall)
4642 ndirectflags |= NDirectMethodDesc::kStdCall;
4643 if (callConv == pmCallConvThiscall)
4644 ndirectflags |= NDirectMethodDesc::kThisCall;
4645
4646 if (pNMD->GetLoaderModule()->IsSystem() && strcmp(szLibName, "QCall") == 0)
4647 {
4648 ndirectflags |= NDirectMethodDesc::kIsQCall;
4649 }
4650 else
4651 {
4652 EnsureWritablePages(&pNMD->ndirect);
4653 pNMD->ndirect.m_pszLibName.SetValueMaybeNull(szLibName);
4654 pNMD->ndirect.m_pszEntrypointName.SetValueMaybeNull(szEntryPointName);
4655 }
4656
4657#ifdef _TARGET_X86_
4658 if (ndirectflags & NDirectMethodDesc::kStdCall)
4659 {
4660 // Compute the kStdCallWithRetBuf flag which is needed at link time for entry point mangling.
4661 MetaSig msig(pNMD);
4662 ArgIterator argit(&msig);
4663 if (argit.HasRetBuffArg())
4664 {
4665 MethodTable *pRetMT = msig.GetRetTypeHandleThrowing().AsMethodTable();
4666 if (IsUnmanagedValueTypeReturnedByRef(pRetMT->GetNativeSize()))
4667 {
4668 ndirectflags |= NDirectMethodDesc::kStdCallWithRetBuf;
4669 }
4670 }
4671 }
4672#endif // _TARGET_X86_
4673
4674 // Call this exactly ONCE per thread. Do not publish incomplete prestub flags
4675 // or you will introduce a race condition.
4676 pNMD->InterlockedSetNDirectFlags(ndirectflags);
4677}
4678
4679#ifdef FEATURE_COMINTEROP
4680// Find the MethodDesc of the predefined IL stub method by either
4681// 1) looking at redirected adapter interfaces, OR
4682// 2) looking at special attributes for the specific interop scenario (specified by dwStubFlags).
4683// Currently only ManagedToNativeComInteropStubAttribute is supported.
4684// It returns NULL if no such attribute(s) can be found.
4685// But if the attribute is found and is invalid, or something went wrong in the looking up
4686// process, an exception will be thrown. If everything goes well, you'll get the MethodDesc
4687// of the stub method
4688HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, MethodDesc **ppRetStubMD)
4689{
4690 CONTRACT(HRESULT)
4691 {
4692 THROWS;
4693 GC_TRIGGERS;
4694 MODE_ANY;
4695 PRECONDITION(CheckPointer(pTargetMD));
4696 PRECONDITION(CheckPointer(ppRetStubMD));
4697 PRECONDITION(*ppRetStubMD == NULL);
4698 }
4699 CONTRACT_END;
4700
4701 HRESULT hr;
4702
4703 MethodTable *pTargetMT = pTargetMD->GetMethodTable();
4704
4705 // Check if this is a redirected interface - we have static stubs in mscorlib for those.
4706 if (SF_IsForwardCOMStub(dwStubFlags) && pTargetMT->IsInterface())
4707 {
4708
4709 // Redirect generic redirected interfaces to the corresponding adapter methods in mscorlib
4710 if (pTargetMT->HasInstantiation())
4711 {
4712 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterfaceMethod(pTargetMD, TypeHandle::Interop_ManagedToNative);
4713 if (pAdapterMD != NULL)
4714 {
4715 *ppRetStubMD = pAdapterMD;
4716 return S_OK;
4717 }
4718 }
4719 }
4720
4721 //
4722 // Find out if we have the attribute
4723 //
4724 const void *pBytes;
4725 ULONG cbBytes;
4726
4727 // Support v-table forward classic COM interop calls only
4728 if (SF_IsCOMStub(dwStubFlags) && SF_IsForwardStub(dwStubFlags) && !SF_IsWinRTStub(dwStubFlags))
4729 {
4730 if (pTargetMT->HasInstantiation())
4731 {
4732 // ManagedToNativeComInteropStubAttribute is not supported with generics
4733 return E_FAIL;
4734 }
4735
4736 if (pTargetMD->IsFCall())
4737 {
4738 // ManagedToNativeComInteropStubAttribute is not supported on FCalls (i.e. methods on legacy
4739 // interfaces forwarded to CustomMarshalers.dll such as IEnumerable::GetEnumerator)
4740 return E_FAIL;
4741 }
4742 _ASSERTE(pTargetMD->IsComPlusCall());
4743
4744 if (pTargetMD->IsInterface())
4745 {
4746 _ASSERTE(!pTargetMD->GetAssembly()->IsWinMD());
4747 hr = pTargetMD->GetMDImport()->GetCustomAttributeByName(
4748 pTargetMD->GetMemberDef(),
4749 FORWARD_INTEROP_STUB_METHOD_TYPE,
4750 &pBytes,
4751 &cbBytes);
4752
4753 if (FAILED(hr))
4754 RETURN hr;
4755 // GetCustomAttributeByName returns S_FALSE when it cannot find the attribute but nothing fails...
4756 // Translate that to E_FAIL
4757 else if (hr == S_FALSE)
4758 RETURN E_FAIL;
4759 }
4760 else
4761 {
4762 // We are dealing with the class, use the interface MD instead
4763 // After second thought I believe we don't need to check the class MD.
4764 // We can think stubs as part of public interface, and if the interface is public,
4765 // the stubs should also be accessible
4766 MethodDesc *pInterfaceMD = pTargetMD->GetInterfaceMD();
4767 if (pInterfaceMD)
4768 {
4769 hr = FindPredefinedILStubMethod(pInterfaceMD, dwStubFlags, ppRetStubMD);
4770 RETURN hr;
4771 }
4772 else
4773 RETURN E_FAIL;
4774 }
4775 }
4776 else
4777 RETURN E_FAIL;
4778
4779 //
4780 // Parse the attribute
4781 //
4782 CustomAttributeParser parser(pBytes, cbBytes);
4783 IfFailRet(parser.SkipProlog());
4784
4785 LPCUTF8 pTypeName;
4786 ULONG cbTypeName;
4787 IfFailRet(parser.GetNonEmptyString(&pTypeName, &cbTypeName));
4788
4789 LPCUTF8 pMethodName;
4790 ULONG cbMethodName;
4791 IfFailRet(parser.GetNonEmptyString(&pMethodName, &cbMethodName));
4792
4793 StackSString typeName(SString::Utf8, pTypeName, cbTypeName);
4794 StackSString methodName(SString::Utf8, pMethodName, cbMethodName);
4795
4796 //
4797 // Retrieve the type
4798 //
4799 TypeHandle stubClassType;
4800 stubClassType = TypeName::GetTypeUsingCASearchRules(typeName.GetUnicode(), pTargetMT->GetAssembly());
4801
4802 MethodTable *pStubClassMT = stubClassType.AsMethodTable();
4803
4804 StackSString stubClassName;
4805 pStubClassMT->_GetFullyQualifiedNameForClassNestedAware(stubClassName);
4806
4807 StackSString targetInterfaceName;
4808 pTargetMT->_GetFullyQualifiedNameForClassNestedAware(targetInterfaceName);
4809
4810 // Restrict to same assembly only to reduce test cost
4811 if (stubClassType.GetAssembly() != pTargetMT->GetAssembly())
4812 {
4813 COMPlusThrow(
4814 kArgumentException,
4815 IDS_EE_INTEROP_STUB_CA_MUST_BE_WITHIN_SAME_ASSEMBLY,
4816 stubClassName.GetUnicode(),
4817 targetInterfaceName.GetUnicode()
4818 );
4819 }
4820
4821 if (stubClassType.HasInstantiation())
4822 {
4823 COMPlusThrow(
4824 kArgumentException,
4825 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_GENERIC,
4826 stubClassName.GetUnicode()
4827 );
4828 }
4829
4830 if (stubClassType.IsInterface())
4831 {
4832 COMPlusThrow(
4833 kArgumentException,
4834 IDS_EE_INTEROP_STUB_CA_STUB_CLASS_MUST_NOT_BE_INTERFACE,
4835 stubClassName.GetUnicode()
4836 );
4837 }
4838
4839 //
4840 // Locate the MethodDesc for the stub method
4841 //
4842 MethodDesc *pStubMD = NULL;
4843
4844 {
4845 PCCOR_SIGNATURE pTargetSig = NULL;
4846 DWORD pcTargetSig = 0;
4847
4848 SigTypeContext typeContext; // NO generics supported
4849
4850 pTargetMD->GetSig(&pTargetSig, &pcTargetSig);
4851
4852 MetaSig msig(pTargetSig,
4853 pcTargetSig,
4854 pTargetMD->GetModule(),
4855 &typeContext);
4856 _ASSERTE(msig.HasThis());
4857
4858 SigBuilder stubSigBuilder;
4859
4860 //
4861 // Append calling Convention, NumOfArgs + 1,
4862 //
4863 stubSigBuilder.AppendByte(msig.GetCallingConvention() & ~IMAGE_CEE_CS_CALLCONV_HASTHIS);
4864 stubSigBuilder.AppendData(msig.NumFixedArgs() + 1);
4865
4866 //
4867 // Append return type
4868 //
4869 SigPointer pReturn = msig.GetReturnProps();
4870 LPBYTE pReturnTypeBegin = (LPBYTE)pReturn.GetPtr();
4871 IfFailThrow(pReturn.SkipExactlyOne());
4872 LPBYTE pReturnTypeEnd = (LPBYTE)pReturn.GetPtr();
4873
4874 stubSigBuilder.AppendBlob(pReturnTypeBegin, pReturnTypeEnd - pReturnTypeBegin);
4875
4876 //
4877 // Append 'this'
4878 //
4879 stubSigBuilder.AppendElementType(ELEMENT_TYPE_CLASS);
4880 stubSigBuilder.AppendToken(pTargetMT->GetCl());
4881
4882 //
4883 // Copy rest of the arguments
4884 //
4885 if (msig.NextArg() != ELEMENT_TYPE_END)
4886 {
4887 SigPointer pFirstArg = msig.GetArgProps();
4888 LPBYTE pArgBegin = (LPBYTE) pFirstArg.GetPtr();
4889 LPBYTE pArgEnd = (LPBYTE) pTargetSig + pcTargetSig;
4890
4891 stubSigBuilder.AppendBlob(pArgBegin, pArgEnd - pArgBegin);
4892 }
4893
4894 //
4895 // Allocate new memory and copy over
4896 //
4897 DWORD pcStubSig = 0;
4898 PCCOR_SIGNATURE pStubSig = (PCCOR_SIGNATURE) stubSigBuilder.GetSignature(&pcStubSig);
4899
4900 //
4901 // Find method using name + signature
4902 //
4903 StackScratchBuffer buffer;
4904 LPCUTF8 szMethodNameUTF8 = methodName.GetUTF8(buffer);
4905 pStubMD = MemberLoader::FindMethod(stubClassType.GetMethodTable(),
4906 szMethodNameUTF8,
4907 pStubSig,
4908 pcStubSig,
4909 pTargetMT->GetModule());
4910
4911 if (pStubMD == NULL)
4912 {
4913 CQuickBytes qbSig;
4914
4915 PrettyPrintSig(
4916 pStubSig,
4917 pcStubSig,
4918 szMethodNameUTF8,
4919 &qbSig,
4920 pTargetMD->GetMDImport(),
4921 NULL);
4922
4923 // Unfortunately the PrettyPrintSig doesn't print 'static' when the function is static
4924 // so we need to append 'static' here. No need to localize
4925 SString signature(SString::Utf8, (LPCUTF8)"static ");
4926 signature.AppendUTF8((LPCUTF8) qbSig.Ptr());
4927
4928 COMPlusThrow(
4929 kMissingMethodException,
4930 IDS_EE_INTEROP_STUB_CA_STUB_METHOD_MISSING,
4931 signature.GetUnicode(),
4932 stubClassName.GetUnicode()
4933 );
4934
4935 }
4936 }
4937
4938 //
4939 // Check the Stub MD
4940 //
4941
4942 // Verify that the target interop method can call the stub method
4943
4944 _ASSERTE(pTargetMD != NULL);
4945
4946 StaticAccessCheckContext accessContext(pTargetMD, pTargetMT);
4947
4948 if (!ClassLoader::CanAccess(
4949 &accessContext,
4950 pStubClassMT,
4951 stubClassType.GetAssembly(),
4952 pStubMD->GetAttrs(),
4953 pStubMD,
4954 NULL))
4955 {
4956 StackSString interopMethodName(SString::Utf8, pTargetMD->GetName());
4957
4958 COMPlusThrow(
4959 kMethodAccessException,
4960 IDS_EE_INTEROP_STUB_CA_NO_ACCESS_TO_STUB_METHOD,
4961 interopMethodName.GetUnicode(),
4962 methodName.GetUnicode()
4963 );
4964 }
4965
4966 // The FindMethod call will make sure that it is static by matching signature.
4967 // So there is no need to check and throw
4968 _ASSERTE(pStubMD->IsStatic());
4969
4970 *ppRetStubMD = pStubMD;
4971
4972 RETURN S_OK;
4973}
4974#endif // FEATURE_COMINTEROP
4975
4976MethodDesc* CreateInteropILStub(
4977 ILStubState* pss,
4978 StubSigDesc* pSigDesc,
4979 CorNativeLinkType nlType,
4980 CorNativeLinkFlags nlFlags,
4981 CorPinvokeMap unmgdCallConv,
4982 DWORD dwStubFlags, // NDirectStubFlags
4983 int nParamTokens,
4984 mdParamDef* pParamTokenArray,
4985 int iLCIDArg
4986 )
4987{
4988 CONTRACT(MethodDesc*)
4989 {
4990 STANDARD_VM_CHECK;
4991
4992 PRECONDITION(CheckPointer(pSigDesc));
4993 POSTCONDITION(CheckPointer(RETVAL));
4994 }
4995 CONTRACT_END;
4996
4997
4998 ///////////////////////////////
4999 //
5000 // MethodDesc creation
5001 //
5002 ///////////////////////////////
5003
5004 MethodDesc* pStubMD = NULL;
5005
5006 Module* pModule = pSigDesc->m_pModule;
5007 Module* pLoaderModule = pSigDesc->m_pLoaderModule;
5008 MethodDesc* pTargetMD = pSigDesc->m_pMD;
5009 //
5010 // pTargetMD may be null in the case of calli pinvoke
5011 // and vararg pinvoke.
5012 //
5013
5014#ifdef FEATURE_COMINTEROP
5015 //
5016 // Try to locate predefined IL stub either defined in user code or hardcoded in CLR
5017 // If there is one, use the pointed method as the stub.
5018 // Skip pTargetMD == NULL case for reverse interop calls
5019 //
5020 if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD)))
5021 {
5022#ifndef CROSSGEN_COMPILE
5023 // We are about to execute method in pStubMD which could be in another module.
5024 // Call EnsureActive before make the call
5025 // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here
5026 pStubMD->EnsureActive();
5027
5028 if (pStubMD->IsPreImplemented())
5029 RestoreNGENedStub(pStubMD);
5030#endif
5031
5032 RETURN pStubMD;
5033 }
5034#endif // FEATURE_COMINTEROP
5035
5036 // Otherwise, fall back to generating IL stub on-the-fly
5037 NDirectStubParameters params(pSigDesc->m_sig,
5038 &pSigDesc->m_typeContext,
5039 pModule,
5040 pLoaderModule,
5041 nlType,
5042 nlFlags,
5043 unmgdCallConv,
5044 dwStubFlags,
5045 nParamTokens,
5046 pParamTokenArray,
5047 iLCIDArg
5048 );
5049
5050 // The following two ILStubCreatorHelperHolder are to recover the status when an
5051 // exception happen during the generation of the IL stubs. We need to free the
5052 // memory allocated and restore the ILStubCache.
5053 //
5054 // The following block is logically divided into two phases. The first phase is
5055 // CreateOrGet IL Stub phase which we take a domain level lock. The second phase
5056 // is IL generation phase which we take a MethodDesc level lock. Taking two locks
5057 // is mainly designed for performance.
5058 //
5059 // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the
5060 // allocated memory during the creation of MethodDesc so that we are able to remove
5061 // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper
5062
5063 // When removing IL Stub from Cache, we have a constraint that only the thread which
5064 // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will
5065 // remove it from cache if OOM occurs
5066
5067 {
5068 ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, &params);
5069
5070 // take the domain level lock
5071 ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock());
5072
5073 {
5074 // The holder will free the allocated MethodDesc and restore the ILStubCache
5075 // if exception happen.
5076 ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper);
5077 pStubMD = pCreateOrGetStubHolder->GetStubMD();
5078
5079 ///////////////////////////////
5080 //
5081 // IL generation
5082 //
5083 ///////////////////////////////
5084
5085 {
5086 // take the MethodDesc level locker
5087 ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5088
5089 ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
5090
5091 // We can release the holder for the first phase now
5092 pCreateOrGetStubHolder.SuppressRelease();
5093
5094 {
5095 // The holder will free the allocated MethodDesc and restore the ILStubCache
5096 // if exception happen. The reason to get the holder again is to
5097 ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper);
5098
5099 if (!pEntryLock.DeadlockAwareAcquire())
5100 {
5101 // the IL generation is not recursive!
5102 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5103 }
5104
5105 if (SF_IsSharedStub(params.m_dwStubFlags))
5106 {
5107 // Assure that pStubMD we have now has not been destroyed by other threads
5108 pGenILHolder->GetStubMethodDesc();
5109
5110 while (pStubMD != pGenILHolder->GetStubMD())
5111 {
5112 pStubMD = pGenILHolder->GetStubMD();
5113
5114 pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock"));
5115 pEntryLock.Assign(pEntry, FALSE);
5116
5117 if (!pEntryLock.DeadlockAwareAcquire())
5118 {
5119 // the IL generation is not recursive!
5120 UNREACHABLE_MSG("unexpected deadlock in IL stub generation!");
5121 }
5122
5123 pGenILHolder->GetStubMethodDesc();
5124 }
5125 }
5126
5127 for (;;)
5128 {
5129 // We have the entry lock now, we can release the global lock
5130 pILStubLock.Release();
5131
5132 if (pEntry->m_hrResultCode != S_FALSE)
5133 {
5134 // We came in to generate the IL but someone
5135 // beat us so there's nothing to do
5136 break;
5137 }
5138
5139 ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver();
5140
5141 CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc()));
5142
5143 if (pResolver->IsILGenerated())
5144 {
5145 // this stub already has its IL generated
5146 break;
5147 }
5148
5149 //
5150 // Check that the stub signature and MethodDesc are compatible. The JIT
5151 // interface functions depend on this.
5152 //
5153
5154 {
5155 SigPointer ptr = pSigDesc->m_sig.CreateSigPointer();
5156
5157 ULONG callConvInfo;
5158 IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo));
5159
5160 BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS);
5161
5162 // CreateNDirectStubWorker will throw an exception for these cases.
5163 BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags);
5164
5165 if (fSigIsStatic || fCanHaveThis)
5166 {
5167 CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic);
5168 }
5169 }
5170
5171 {
5172 ILStubGenHolder sgh(pResolver);
5173
5174 pResolver->SetStubMethodDesc(pStubMD);
5175 pResolver->SetStubTargetMethodDesc(pTargetMD);
5176
5177 CreateNDirectStubWorker(pss,
5178 pSigDesc,
5179 nlType,
5180 nlFlags,
5181 unmgdCallConv,
5182 dwStubFlags,
5183 pStubMD,
5184 pParamTokenArray,
5185 iLCIDArg);
5186
5187 pResolver->SetTokenLookupMap(pss->GetTokenLookupMap());
5188
5189 pResolver->SetStubTargetMethodSig(
5190 pss->GetStubTargetMethodSig(),
5191 pss->GetStubTargetMethodSigLength());
5192
5193 // we successfully generated the IL stub
5194 sgh.SuppressRelease();
5195 }
5196
5197 pEntry->m_hrResultCode = S_OK;
5198 break;
5199 }
5200
5201 // Link the MethodDesc onto the method table with the lock taken
5202 NDirect::AddMethodDescChunkWithLockTaken(&params, pStubMD);
5203
5204 pGenILHolder.SuppressRelease();
5205 }
5206 }
5207 }
5208 ilStubCreatorHelper.SuppressRelease();
5209 }
5210
5211#if defined(_TARGET_X86_)
5212 if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg())
5213 {
5214 // copy the stack arg byte count from the stub MD to the target MD - this number is computed
5215 // during stub generation and is copied to all target MDs that share the stub
5216 // (we don't set it for varargs - the number is call site specific)
5217 // also copy the "takes parameters with copy constructors" flag which is needed to generate
5218 // appropriate intercept stub
5219
5220 WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
5221 BOOL fHasCopyCtorArgs = pStubMD->AsDynamicMethodDesc()->HasCopyCtorArgs();
5222
5223 if (pTargetMD->IsNDirect())
5224 {
5225 NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD;
5226
5227 pTargetNMD->SetStackArgumentSize(cbStackArgSize, (CorPinvokeMap)0);
5228 pTargetNMD->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5229 }
5230#ifdef FEATURE_COMINTEROP
5231 else
5232 {
5233 if (SF_IsCOMStub(dwStubFlags))
5234 {
5235 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD);
5236
5237 if (pComInfo != NULL)
5238 {
5239 pComInfo->SetStackArgumentSize(cbStackArgSize);
5240 pComInfo->SetHasCopyCtorArgs(fHasCopyCtorArgs);
5241 }
5242 }
5243 }
5244#endif // FEATURE_COMINTEROP
5245 }
5246#endif // defined(_TARGET_X86_)
5247
5248 RETURN pStubMD;
5249}
5250
5251MethodDesc* NDirect::CreateCLRToNativeILStub(
5252 StubSigDesc* pSigDesc,
5253 CorNativeLinkType nlType,
5254 CorNativeLinkFlags nlFlags,
5255 CorPinvokeMap unmgdCallConv,
5256 DWORD dwStubFlags) // NDirectStubFlags
5257{
5258 CONTRACT(MethodDesc*)
5259 {
5260 STANDARD_VM_CHECK;
5261
5262 PRECONDITION(CheckPointer(pSigDesc));
5263 POSTCONDITION(CheckPointer(RETVAL));
5264 }
5265 CONTRACT_END;
5266
5267 int iLCIDArg = 0;
5268 int numArgs = 0;
5269 int numParamTokens = 0;
5270 mdParamDef* pParamTokenArray = NULL;
5271
5272 CreateNDirectStubAccessMetadata(pSigDesc,
5273 unmgdCallConv,
5274 &dwStubFlags,
5275 &iLCIDArg,
5276 &numArgs);
5277
5278 Module *pModule = pSigDesc->m_pModule;
5279 numParamTokens = numArgs + 1;
5280 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5281 CollateParamTokens(pModule->GetMDImport(), pSigDesc->m_tkMethodDef, numArgs, pParamTokenArray);
5282
5283 MethodDesc *pMD = pSigDesc->m_pMD;
5284
5285 NewHolder<ILStubState> pStubState;
5286
5287#ifdef FEATURE_COMINTEROP
5288 if (SF_IsCOMStub(dwStubFlags))
5289 {
5290 if (SF_IsReverseStub(dwStubFlags))
5291 {
5292 pStubState = new COMToCLR_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5293 }
5294 else
5295 {
5296 pStubState = new CLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD);
5297 }
5298 }
5299 else
5300#endif
5301 {
5302 pStubState = new PInvoke_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, unmgdCallConv, iLCIDArg, pMD);
5303 }
5304
5305 MethodDesc* pStubMD;
5306 pStubMD = CreateInteropILStub(
5307 pStubState,
5308 pSigDesc,
5309 nlType,
5310 nlFlags,
5311 unmgdCallConv,
5312 dwStubFlags,
5313 numParamTokens,
5314 pParamTokenArray,
5315 iLCIDArg);
5316
5317
5318
5319 RETURN pStubMD;
5320}
5321
5322#ifdef FEATURE_COMINTEROP
5323MethodDesc* NDirect::CreateFieldAccessILStub(
5324 PCCOR_SIGNATURE szMetaSig,
5325 DWORD cbMetaSigSize,
5326 Module* pModule,
5327 mdFieldDef fd,
5328 DWORD dwStubFlags, // NDirectStubFlags
5329 FieldDesc* pFD)
5330{
5331 CONTRACT(MethodDesc*)
5332 {
5333 STANDARD_VM_CHECK;
5334
5335 PRECONDITION(CheckPointer(szMetaSig));
5336 PRECONDITION(CheckPointer(pModule));
5337 PRECONDITION(CheckPointer(pFD, NULL_OK));
5338 PRECONDITION(SF_IsFieldGetterStub(dwStubFlags) || SF_IsFieldSetterStub(dwStubFlags));
5339 POSTCONDITION(CheckPointer(RETVAL));
5340 }
5341 CONTRACT_END;
5342
5343 int numArgs = (SF_IsFieldSetterStub(dwStubFlags) ? 1 : 0);
5344 int numParamTokens = numArgs + 1;
5345
5346 // make sure we capture marshaling metadata
5347 mdParamDef* pParamTokenArray = (mdParamDef *)_alloca(numParamTokens * sizeof(mdParamDef));
5348 pParamTokenArray[0] = mdParamDefNil;
5349 pParamTokenArray[numArgs] = (mdParamDef)fd;
5350
5351 // fields are never preserve-sig
5352 dwStubFlags |= NDIRECTSTUB_FL_DOHRESULTSWAPPING;
5353
5354 // convert field signature to getter/setter signature
5355 SigBuilder sigBuilder;
5356
5357 sigBuilder.AppendData(IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS);
5358 sigBuilder.AppendData(numArgs);
5359
5360 if (SF_IsFieldSetterStub(dwStubFlags))
5361 {
5362 // managed setter returns void
5363 sigBuilder.AppendElementType(ELEMENT_TYPE_VOID);
5364 }
5365
5366 CONSISTENCY_CHECK(*szMetaSig == IMAGE_CEE_CS_CALLCONV_FIELD);
5367
5368 sigBuilder.AppendBlob((const PVOID)(szMetaSig + 1), cbMetaSigSize - 1);
5369 szMetaSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbMetaSigSize);
5370
5371 StubSigDesc sigDesc(NULL, Signature(szMetaSig, cbMetaSigSize), pModule);
5372
5373#ifdef _DEBUG
5374 sigDesc.m_pDebugName = pFD->GetDebugName();
5375 sigDesc.m_pDebugClassName = pFD->GetEnclosingMethodTable()->GetDebugClassName();
5376#endif // _DEBUG
5377
5378 Signature signature(szMetaSig, cbMetaSigSize);
5379 NewHolder<ILStubState> pStubState = new COMToCLRFieldAccess_ILStubState(pModule, signature, &sigDesc.m_typeContext, dwStubFlags, pFD);
5380
5381 MethodDesc* pStubMD;
5382 pStubMD = CreateInteropILStub(
5383 pStubState,
5384 &sigDesc,
5385 (CorNativeLinkType)0,
5386 (CorNativeLinkFlags)0,
5387 (CorPinvokeMap)0,
5388 dwStubFlags,
5389 numParamTokens,
5390 pParamTokenArray,
5391 -1);
5392
5393 RETURN pStubMD;
5394}
5395#endif // FEATURE_COMINTEROP
5396
5397MethodDesc* NDirect::CreateCLRToNativeILStub(PInvokeStaticSigInfo* pSigInfo,
5398 DWORD dwStubFlags,
5399 MethodDesc* pMD)
5400{
5401 STANDARD_VM_CONTRACT;
5402
5403 StubSigDesc sigDesc(pMD, pSigInfo);
5404
5405 if (SF_IsWinRTDelegateStub(dwStubFlags))
5406 {
5407 _ASSERTE(pMD->IsEEImpl());
5408
5409 return CreateCLRToNativeILStub(&sigDesc,
5410 (CorNativeLinkType)0,
5411 (CorNativeLinkFlags)0,
5412 (CorPinvokeMap)0,
5413 (pSigInfo->GetStubFlags() | dwStubFlags) & ~NDIRECTSTUB_FL_DELEGATE);
5414 }
5415 else
5416 {
5417 return CreateCLRToNativeILStub(&sigDesc,
5418 pSigInfo->GetCharSet(),
5419 pSigInfo->GetLinkFlags(),
5420 pSigInfo->GetCallConv(),
5421 pSigInfo->GetStubFlags() | dwStubFlags);
5422 }
5423}
5424
5425MethodDesc* NDirect::GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwStubFlags)
5426{
5427 STANDARD_VM_CONTRACT;
5428
5429 MethodDesc* pStubMD = NULL;
5430
5431 if (!pNMD->IsVarArgs() || SF_IsForNumParamBytes(dwStubFlags))
5432 {
5433 if (pNMD->IsClassConstructorTriggeredByILStub())
5434 {
5435 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
5436 }
5437
5438 pStubMD = CreateCLRToNativeILStub(
5439 pSigInfo,
5440 dwStubFlags & ~NDIRECTSTUB_FL_FOR_NUMPARAMBYTES,
5441 pNMD);
5442 }
5443
5444 return pStubMD;
5445}
5446
5447MethodDesc* GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags)
5448{
5449 STANDARD_VM_CONTRACT;
5450
5451 BOOL fGcMdaEnabled = FALSE;
5452#ifdef MDA_SUPPORTED
5453 if (MDA_GET_ASSISTANT(GcManagedToUnmanaged) || MDA_GET_ASSISTANT(GcUnmanagedToManaged))
5454 {
5455 // We never generate checks for these MDAs to NGEN'ed stubs so if they are
5456 // enabled, a new stub must be generated (the perf impact is huge anyway).
5457 fGcMdaEnabled = TRUE;
5458 }
5459#endif // MDA_SUPPORTED
5460
5461#ifdef FEATURE_COMINTEROP
5462 if (SF_IsReverseCOMStub(dwStubFlags))
5463 {
5464 if (fGcMdaEnabled)
5465 return NULL;
5466
5467 // reverse COM stubs live in a hash table
5468 StubMethodHashTable *pHash = pMD->GetLoaderModule()->GetStubMethodHashTable();
5469 return (pHash == NULL ? NULL : pHash->FindMethodDesc(pMD));
5470 }
5471 else
5472#endif // FEATURE_COMINTEROP
5473 if (pMD->IsNDirect())
5474 {
5475 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5476 return ((fGcMdaEnabled && !pNMD->IsQCall()) ? NULL : pNMD->ndirect.m_pStubMD.GetValueMaybeNull());
5477 }
5478#ifdef FEATURE_COMINTEROP
5479 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5480 {
5481#ifdef MDA_SUPPORTED
5482 if (MDA_GET_ASSISTANT(RaceOnRCWCleanup))
5483 {
5484 // we never generate this callout to NGEN'ed stubs
5485 return NULL;
5486 }
5487#endif // MDA_SUPPORTED
5488
5489 if (fGcMdaEnabled)
5490 return NULL;
5491
5492 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
5493 return (pComInfo == NULL ? NULL : pComInfo->m_pStubMD.GetValueMaybeNull());
5494 }
5495#endif // FEATURE_COMINTEROP
5496 else if (pMD->IsEEImpl())
5497 {
5498 if (fGcMdaEnabled)
5499 return NULL;
5500
5501 DelegateEEClass *pClass = (DelegateEEClass *)pMD->GetClass();
5502 if (SF_IsReverseStub(dwStubFlags))
5503 {
5504 return pClass->m_pReverseStubMD;
5505 }
5506 else
5507 {
5508#ifdef FEATURE_COMINTEROP
5509 if (SF_IsWinRTDelegateStub(dwStubFlags))
5510 {
5511 return pClass->m_pComPlusCallInfo->m_pStubMD.GetValueMaybeNull();
5512 }
5513 else
5514#endif // FEATURE_COMINTEROP
5515 {
5516 return pClass->m_pForwardStubMD;
5517 }
5518 }
5519 }
5520 else if (pMD->IsIL())
5521 {
5522 // these are currently only created at runtime, not at NGEN time
5523 return NULL;
5524 }
5525 else
5526 {
5527 UNREACHABLE_MSG("unexpected type of MethodDesc");
5528 }
5529}
5530
5531#ifndef CROSSGEN_COMPILE
5532
5533PCODE NDirect::GetStubForILStub(MethodDesc* pManagedMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5534{
5535 CONTRACT(PCODE)
5536 {
5537 STANDARD_VM_CHECK;
5538
5539 PRECONDITION(CheckPointer(pManagedMD));
5540 POSTCONDITION(RETVAL != NULL);
5541 }
5542 CONTRACT_END;
5543
5544 // pStubMD, if provided, must be preimplemented.
5545 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5546
5547 if (NULL == *ppStubMD)
5548 {
5549 PInvokeStaticSigInfo sigInfo(pManagedMD);
5550 *ppStubMD = NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pManagedMD);
5551 }
5552
5553 RETURN JitILStub(*ppStubMD);
5554}
5555
5556PCODE NDirect::GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
5557{
5558 STANDARD_VM_CONTRACT;
5559
5560 PCODE pStub = NULL;
5561
5562 // pStubMD, if provided, must be preimplemented.
5563 CONSISTENCY_CHECK( (*ppStubMD == NULL) || (*ppStubMD)->IsPreImplemented() );
5564
5565 if (NULL == *ppStubMD)
5566 {
5567 PInvokeStaticSigInfo sigInfo;
5568 NDirect::PopulateNDirectMethodDesc(pNMD, &sigInfo, /* throwOnError = */ !SF_IsForNumParamBytes(dwStubFlags));
5569
5570 *ppStubMD = NDirect::GetILStubMethodDesc(pNMD, &sigInfo, dwStubFlags);
5571 }
5572
5573 if (SF_IsForNumParamBytes(dwStubFlags))
5574 return NULL;
5575
5576 if (*ppStubMD)
5577 {
5578 pStub = JitILStub(*ppStubMD);
5579 }
5580 else
5581 {
5582 CONSISTENCY_CHECK(pNMD->IsVarArgs());
5583
5584 //
5585 // varargs goes through vararg NDirect stub
5586 //
5587 pStub = TheVarargNDirectStub(pNMD->HasRetBuffArg());
5588 }
5589
5590 if (pNMD->IsEarlyBound())
5591 {
5592 pNMD->InitEarlyBoundNDirectTarget();
5593 }
5594 else
5595 {
5596 NDirectLink(pNMD);
5597 }
5598
5599 //
5600 // NOTE: there is a race in updating this MethodDesc. We depend on all
5601 // threads getting back the same DynamicMethodDesc for a particular
5602 // NDirectMethodDesc, in that case, the locking around the actual JIT
5603 // operation will prevent the code from being jitted more than once.
5604 // By the time we get here, all threads get the same address of code
5605 // back from the JIT operation and they all just fill in the same value
5606 // here.
5607 //
5608 // In the NGEN case, all threads will get the same preimplemented code
5609 // address much like the JIT case.
5610 //
5611
5612 return pStub;
5613}
5614
5615PCODE JitILStub(MethodDesc* pStubMD)
5616{
5617 STANDARD_VM_CONTRACT;
5618
5619 PCODE pCode = pStubMD->GetNativeCode();
5620
5621 if (pCode == NULL)
5622 {
5623 ///////////////////////////////
5624 //
5625 // Code generation
5626 //
5627 ///////////////////////////////
5628
5629
5630 if (pStubMD->IsDynamicMethod())
5631 {
5632 //
5633 // A dynamically generated IL stub
5634 //
5635
5636 pCode = pStubMD->PrepareInitialCode();
5637
5638 _ASSERTE(pCode == pStubMD->GetNativeCode());
5639 }
5640 else
5641 {
5642 //
5643 // A static IL stub that is pointing to a static method in user assembly
5644 // Compile it and return the native code
5645 //
5646
5647 // This returns the stable entry point
5648 pCode = pStubMD->DoPrestub(NULL);
5649
5650 _ASSERTE(pCode == pStubMD->GetStableEntryPoint());
5651 }
5652 }
5653
5654 if (!pStubMD->IsDynamicMethod())
5655 {
5656 // We need an entry point that can be called multiple times
5657 pCode = pStubMD->GetMultiCallableAddrOfCode();
5658 }
5659
5660 return pCode;
5661}
5662
5663MethodDesc* RestoreNGENedStub(MethodDesc* pStubMD)
5664{
5665 CONTRACTL
5666 {
5667 STANDARD_VM_CHECK;
5668 PRECONDITION(CheckPointer(pStubMD));
5669 }
5670 CONTRACTL_END;
5671
5672#ifdef FEATURE_PREJIT
5673 pStubMD->CheckRestore();
5674
5675 PCODE pCode = pStubMD->GetPreImplementedCode();
5676 if (pCode != NULL)
5677 {
5678 TADDR pFixupList = pStubMD->GetFixupList();
5679 if (pFixupList != NULL)
5680 {
5681 Module* pZapModule = pStubMD->GetZapModule();
5682 _ASSERTE(pZapModule != NULL);
5683 if (!pZapModule->FixupDelayList(pFixupList))
5684 {
5685 _ASSERTE(!"FixupDelayList failed");
5686 ThrowHR(COR_E_BADIMAGEFORMAT);
5687 }
5688 }
5689
5690#if defined(HAVE_GCCOVER)
5691 if (GCStress<cfg_instr_ngen>::IsEnabled())
5692 SetupGcCoverage(pStubMD, (BYTE*) pCode);
5693#endif // HAVE_GCCOVER
5694
5695 }
5696 else
5697 {
5698 // We only pass a non-NULL pStubMD to GetStubForILStub() below if pStubMD is preimplemeneted.
5699 pStubMD = NULL;
5700 }
5701#endif // FEATURE_PREJIT
5702
5703 return pStubMD;
5704}
5705
5706PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags, MethodDesc **ppStubMD)
5707{
5708 CONTRACT(PCODE)
5709 {
5710 STANDARD_VM_CHECK;
5711
5712 PRECONDITION(CheckPointer(pMD));
5713 PRECONDITION(pMD->IsNDirect() || pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl() || pMD->IsIL());
5714 }
5715 CONTRACT_END;
5716
5717 PCODE pStub = NULL;
5718 MethodDesc* pStubMD = NULL;
5719
5720 pStubMD = GetStubMethodDescFromInteropMethodDesc(pMD, dwStubFlags);
5721 if (pStubMD != NULL)
5722 {
5723 pStubMD = RestoreNGENedStub(pStubMD);
5724 }
5725
5726 if ((NULL == pStubMD) && (SF_IsNGENedStub(dwStubFlags)))
5727 {
5728 // Return NULL -- the caller asked only for an ngened stub and
5729 // one does not exist, so don't do any more work.
5730 CONSISTENCY_CHECK(pStub == NULL);
5731 }
5732 else
5733 if (pMD->IsNDirect())
5734 {
5735 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
5736 pStub = NDirect::GetStubForILStub(pNMD, &pStubMD, dwStubFlags);
5737 }
5738#ifdef FEATURE_COMINTEROP
5739 else
5740 if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
5741 {
5742 pStub = ComPlusCall::GetStubForILStub(pMD, &pStubMD);
5743 }
5744#endif // FEATURE_COMINTEROP
5745 else
5746 if (pMD->IsEEImpl())
5747 {
5748 CONSISTENCY_CHECK(pMD->GetMethodTable()->IsDelegate());
5749 EEImplMethodDesc* pDelegateMD = (EEImplMethodDesc*)pMD;
5750 pStub = COMDelegate::GetStubForILStub(pDelegateMD, &pStubMD, dwStubFlags);
5751 }
5752 else
5753 if (pMD->IsIL())
5754 {
5755 CONSISTENCY_CHECK(SF_IsReverseStub(dwStubFlags));
5756 pStub = NDirect::GetStubForILStub(pMD, &pStubMD, dwStubFlags);
5757 }
5758 else
5759 {
5760 UNREACHABLE_MSG("unexpected MethodDesc type");
5761 }
5762
5763 if (pStubMD != NULL && pStubMD->IsILStub() && pStubMD->AsDynamicMethodDesc()->IsStubNeedsCOMStarted())
5764 {
5765 // the stub uses COM so make sure that it is started
5766 EnsureComStarted();
5767 }
5768
5769 if (ppStubMD != NULL)
5770 *EnsureWritablePages(ppStubMD) = pStubMD;
5771
5772 RETURN pStub;
5773}
5774
5775#ifdef FEATURE_COMINTEROP
5776void CreateCLRToDispatchCOMStub(
5777 MethodDesc * pMD,
5778 DWORD dwStubFlags) // NDirectStubFlags
5779{
5780 CONTRACTL
5781 {
5782 STANDARD_VM_CHECK;
5783
5784 PRECONDITION(CheckPointer(pMD));
5785 }
5786 CONTRACTL_END;
5787
5788 _ASSERTE(SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags));
5789
5790 // If we are dealing with a COM event call, then we need to initialize the
5791 // COM event call information.
5792 if (SF_IsCOMEventCallStub(dwStubFlags))
5793 {
5794 _ASSERTE(pMD->IsComPlusCall()); // no generic COM eventing
5795 ((ComPlusCallMethodDesc *)pMD)->InitComEventCallInfo();
5796 }
5797
5798 // Get the call signature information
5799 StubSigDesc sigDesc(pMD);
5800
5801 int iLCIDArg = 0;
5802 int numArgs = 0;
5803 int numParamTokens = 0;
5804 mdParamDef* pParamTokenArray = NULL;
5805
5806 CreateNDirectStubAccessMetadata(&sigDesc,
5807 (CorPinvokeMap)0,
5808 &dwStubFlags,
5809 &iLCIDArg,
5810 &numArgs);
5811
5812 numParamTokens = numArgs + 1;
5813 pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
5814 CollateParamTokens(sigDesc.m_pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray);
5815
5816 DispatchStubState MyStubState;
5817
5818 CreateNDirectStubWorker(&MyStubState,
5819 &sigDesc,
5820 (CorNativeLinkType)0,
5821 (CorNativeLinkFlags)0,
5822 (CorPinvokeMap)0,
5823 dwStubFlags | NDIRECTSTUB_FL_COM,
5824 pMD,
5825 pParamTokenArray,
5826 iLCIDArg);
5827
5828 _ASSERTE(pMD->IsComPlusCall()); // no generic disp-calls
5829 ((ComPlusCallMethodDesc *)pMD)->InitRetThunk();
5830}
5831
5832
5833#endif // FEATURE_COMINTEROP
5834
5835/*static*/
5836LPVOID NDirect::NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod)
5837{
5838 // GetProcAddress cannot be called while preemptive GC is disabled.
5839 // It requires the OS to take the loader lock.
5840 CONTRACT(LPVOID)
5841 {
5842 STANDARD_VM_CHECK;
5843 PRECONDITION(CheckPointer(pMD));
5844 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
5845 }
5846 CONTRACT_END;
5847
5848 g_IBCLogger.LogNDirectCodeAccess(pMD);
5849
5850#ifdef MDA_SUPPORTED
5851 MDA_TRIGGER_ASSISTANT(PInvokeLog, LogPInvoke(pMD, hMod));
5852#endif
5853
5854 RETURN pMD->FindEntryPoint(hMod);
5855}
5856
5857VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget)
5858{
5859 CONTRACTL
5860 {
5861 THROWS;
5862 GC_TRIGGERS;
5863 MODE_ANY;
5864
5865 PRECONDITION(IsNDirect());
5866 PRECONDITION(pTarget != NULL);
5867 }
5868 CONTRACTL_END;
5869
5870 Stub *pInterceptStub = NULL;
5871
5872#ifdef _TARGET_X86_
5873
5874
5875#ifdef MDA_SUPPORTED
5876 if (!IsQCall() && MDA_GET_ASSISTANT(PInvokeStackImbalance))
5877 {
5878 pInterceptStub = GenerateStubForMDA(pTarget, pInterceptStub);
5879 }
5880#endif // MDA_SUPPORTED
5881
5882
5883#endif // _TARGET_X86_
5884
5885
5886 NDirectWriteableData* pWriteableData = GetWriteableData();
5887 EnsureWritablePages(pWriteableData);
5888 g_IBCLogger.LogNDirectCodeAccess(this);
5889
5890 if (pInterceptStub != NULL)
5891 {
5892 ndirect.m_pNativeNDirectTarget = pTarget;
5893
5894#if defined(_TARGET_X86_)
5895 pTarget = (PVOID)pInterceptStub->GetEntryPoint();
5896
5897 LPVOID oldTarget = GetNDirectImportThunkGlue()->GetEntrypoint();
5898 if (FastInterlockCompareExchangePointer(&pWriteableData->m_pNDirectTarget, pTarget,
5899 oldTarget) != oldTarget)
5900 {
5901 pInterceptStub->DecRef();
5902 }
5903#else
5904 _ASSERTE(pInterceptStub == NULL); // we don't intercept for anything else than host on !_TARGET_X86_
5905#endif
5906 }
5907 else
5908 {
5909 pWriteableData->m_pNDirectTarget = pTarget;
5910 }
5911}
5912
5913
5914
5915#if defined(_TARGET_X86_) && defined(MDA_SUPPORTED)
5916EXTERN_C VOID __stdcall PInvokeStackImbalanceWorker(StackImbalanceCookie *pSICookie, DWORD dwPostESP)
5917{
5918 STATIC_CONTRACT_THROWS;
5919 STATIC_CONTRACT_GC_TRIGGERS;
5920 STATIC_CONTRACT_MODE_PREEMPTIVE; // we've already switched to preemptive
5921
5922 // make sure we restore the original Win32 last error before leaving this function - we are
5923 // called right after returning from the P/Invoke target and the error has not been saved yet
5924 BEGIN_PRESERVE_LAST_ERROR;
5925
5926 MdaPInvokeStackImbalance* pProbe = MDA_GET_ASSISTANT(PInvokeStackImbalance);
5927
5928 // This MDA must be active if we generated a call to PInvokeStackImbalanceHelper
5929 _ASSERTE(pProbe);
5930
5931 pProbe->CheckStack(pSICookie, dwPostESP);
5932
5933 END_PRESERVE_LAST_ERROR;
5934}
5935#endif // _TARGET_X86_ && MDA_SUPPORTED
5936
5937
5938// Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places
5939// if earlier loads fail and those later loads obliterate error codes.
5940//
5941// This tracker object will keep track of the error code in accordance to priority:
5942//
5943// low-priority: unknown error code (should never happen)
5944// medium-priority: dll not found
5945// high-priority: dll found but error during loading
5946//
5947// We will overwrite the previous load's error code only if the new error code is higher priority.
5948//
5949
5950class LoadLibErrorTracker
5951{
5952private:
5953 static const DWORD const_priorityNotFound = 10;
5954 static const DWORD const_priorityAccessDenied = 20;
5955 static const DWORD const_priorityCouldNotLoad = 99999;
5956public:
5957 LoadLibErrorTracker()
5958 {
5959 LIMITED_METHOD_CONTRACT;
5960 m_hr = E_FAIL;
5961 m_priorityOfLastError = 0;
5962 }
5963
5964 VOID TrackErrorCode()
5965 {
5966 LIMITED_METHOD_CONTRACT;
5967
5968 DWORD priority;
5969
5970#ifdef FEATURE_PAL
5971
5972 SetMessage(PAL_GetLoadLibraryError());
5973#else
5974
5975 DWORD dwLastError = GetLastError();
5976
5977 switch (dwLastError)
5978 {
5979 case ERROR_FILE_NOT_FOUND:
5980 case ERROR_PATH_NOT_FOUND:
5981 case ERROR_MOD_NOT_FOUND:
5982 case ERROR_DLL_NOT_FOUND:
5983 priority = const_priorityNotFound;
5984 break;
5985
5986 // If we can't access a location, we can't know if the dll's there or if it's good.
5987 // Still, this is probably more unusual (and thus of more interest) than a dll-not-found
5988 // so give it an intermediate priority.
5989 case ERROR_ACCESS_DENIED:
5990 priority = const_priorityAccessDenied;
5991
5992 // Assume all others are "dll found but couldn't load."
5993 default:
5994 priority = const_priorityCouldNotLoad;
5995 break;
5996 }
5997 UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError));
5998#endif
5999 }
6000
6001 // Sets the error code to HRESULT as could not load DLL
6002 void TrackHR_CouldNotLoad(HRESULT hr)
6003 {
6004 UpdateHR(const_priorityCouldNotLoad, hr);
6005 }
6006
6007 HRESULT GetHR()
6008 {
6009 return m_hr;
6010 }
6011
6012 SString& GetMessage()
6013 {
6014 return m_message;
6015 }
6016
6017 void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath)
6018 {
6019 STANDARD_VM_CONTRACT;
6020
6021#if defined(__APPLE__)
6022 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage());
6023#elif defined(FEATURE_PAL)
6024 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage());
6025#else // __APPLE__
6026 HRESULT theHRESULT = GetHR();
6027 if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT))
6028 {
6029 COMPlusThrow(kBadImageFormatException);
6030 }
6031 else
6032 {
6033 SString hrString;
6034 GetHRMsg(theHRESULT, hrString);
6035 COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString);
6036 }
6037#endif // FEATURE_PAL
6038
6039 __UNREACHABLE();
6040 }
6041
6042private:
6043 void UpdateHR(DWORD priority, HRESULT hr)
6044 {
6045 if (priority > m_priorityOfLastError)
6046 {
6047 m_hr = hr;
6048 m_priorityOfLastError = priority;
6049 }
6050 }
6051
6052 void SetMessage(LPCSTR message)
6053 {
6054 m_message = SString(SString::Utf8, message);
6055 }
6056
6057 HRESULT m_hr;
6058 DWORD m_priorityOfLastError;
6059 SString m_message;
6060}; // class LoadLibErrorTracker
6061
6062// Load the library directly. On Unix systems, don't register it yet with PAL.
6063// * External callers like AssemblyNative::InternalLoadUnmanagedDllFromPath() and the upcoming
6064// System.Runtime.Interop.Marshall.LoadLibrary() need the raw system handle
6065// * Internal callers like LoadLibraryModule() can convert this handle to a HMODULE via PAL APIs on Unix
6066static NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker )
6067{
6068 STANDARD_VM_CONTRACT;
6069
6070 NATIVE_LIBRARY_HANDLE hmod = NULL;
6071
6072#ifndef FEATURE_PAL
6073
6074 if ((flags & 0xFFFFFF00) != 0
6075#ifndef FEATURE_CORESYSTEM
6076 && NDirect::SecureLoadLibrarySupported()
6077#endif // !FEATURE_CORESYSTEM
6078 )
6079 {
6080 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00);
6081 if (hmod != NULL)
6082 {
6083 return hmod;
6084 }
6085
6086 DWORD dwLastError = GetLastError();
6087 if (dwLastError != ERROR_INVALID_PARAMETER)
6088 {
6089 pErrorTracker->TrackErrorCode();
6090 return hmod;
6091 }
6092 }
6093
6094 hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF);
6095
6096#else // !FEATURE_PAL
6097 hmod = PAL_LoadLibraryDirect(name);
6098#endif // !FEATURE_PAL
6099
6100 if (hmod == NULL)
6101 {
6102 pErrorTracker->TrackErrorCode();
6103 }
6104
6105 return hmod;
6106}
6107
6108#if !defined(FEATURE_PAL)
6109bool NDirect::s_fSecureLoadLibrarySupported = false;
6110#endif
6111
6112#define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a))
6113#define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a))
6114
6115#ifdef FEATURE_PAL
6116#define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W
6117#define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W
6118#else // !FEATURE_PAL
6119#define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll")
6120#define PLATFORM_SHARED_LIB_PREFIX_W W("")
6121#endif // !FEATURE_PAL
6122
6123// static
6124NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
6125{
6126 CONTRACTL
6127 {
6128 STANDARD_VM_CHECK;
6129 PRECONDITION(CheckPointer(libraryPath));
6130 }
6131 CONTRACTL_END;
6132
6133 LoadLibErrorTracker errorTracker;
6134 const NATIVE_LIBRARY_HANDLE hmod =
6135 LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker);
6136
6137 if (throwOnError && (hmod == nullptr))
6138 {
6139 SString libraryPathSString(libraryPath);
6140 errorTracker.Throw(libraryPathSString);
6141 }
6142 return hmod;
6143}
6144
6145// static
6146NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
6147 BOOL hasDllImportSearchFlag, DWORD dllImportSearchFlag,
6148 BOOL throwOnError)
6149{
6150 CONTRACTL
6151 {
6152 STANDARD_VM_CHECK;
6153 PRECONDITION(CheckPointer(libraryName));
6154 PRECONDITION(CheckPointer(callingAssembly));
6155 }
6156 CONTRACTL_END;
6157
6158 LoadLibErrorTracker errorTracker;
6159
6160 // First checks if a default DllImportSearchPathFlag was passed in, if so, use that value.
6161 // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute. If so, use that value.
6162 BOOL searchAssemblyDirectory = TRUE;
6163 DWORD dllImportSearchPathFlag = 0;
6164
6165 if (hasDllImportSearchFlag)
6166 {
6167 dllImportSearchPathFlag = dllImportSearchFlag & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6168 searchAssemblyDirectory = dllImportSearchFlag & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
6169
6170 }
6171 else
6172 {
6173 Module * pModule = callingAssembly->GetManifestModule();
6174
6175 if (pModule->HasDefaultDllImportSearchPathsAttribute())
6176 {
6177 dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
6178 searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
6179 }
6180 }
6181
6182 NATIVE_LIBRARY_HANDLE hmod =
6183 LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, &errorTracker, libraryName);
6184
6185 if (throwOnError && (hmod == nullptr))
6186 {
6187 SString libraryPathSString(libraryName);
6188 errorTracker.Throw(libraryPathSString);
6189 }
6190
6191 return hmod;
6192}
6193
6194// static
6195NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, PCWSTR wszLibName)
6196{
6197 STANDARD_VM_CONTRACT;
6198
6199 // First checks if the method has DefaultDllImportSearchPathsAttribute. If so, use that value.
6200 // Otherwise checks if the assembly has the attribute. If so, use that value.
6201 BOOL searchAssemblyDirectory = TRUE;
6202 DWORD dllImportSearchPathFlag = 0;
6203
6204 if (pMD->HasDefaultDllImportSearchPathsAttribute())
6205 {
6206 dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
6207 searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
6208 }
6209 else
6210 {
6211 Module * pModule = pMD->GetModule();
6212
6213 if (pModule->HasDefaultDllImportSearchPathsAttribute())
6214 {
6215 dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
6216 searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
6217 }
6218 }
6219
6220 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6221 return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, pErrorTracker, wszLibName);
6222}
6223
6224// static
6225void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle)
6226{
6227 STANDARD_VM_CONTRACT;
6228
6229 // FreeLibrary doesn't throw if the input is null.
6230 // This avoids further null propagation/check while freeing resources (ex: in finally blocks)
6231 if (handle == NULL)
6232 return;
6233
6234#ifndef FEATURE_PAL
6235 BOOL retVal = FreeLibrary(handle);
6236#else // !FEATURE_PAL
6237 BOOL retVal = PAL_FreeLibraryDirect(handle);
6238#endif // !FEATURE_PAL
6239
6240 if (retVal == 0)
6241 COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException"));
6242}
6243
6244//static
6245INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError)
6246{
6247 CONTRACTL
6248 {
6249 STANDARD_VM_CHECK;
6250 PRECONDITION(CheckPointer(handle));
6251 PRECONDITION(CheckPointer(symbolName));
6252 }
6253 CONTRACTL_END;
6254
6255 MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName);
6256
6257#ifndef FEATURE_PAL
6258 INT_PTR address = reinterpret_cast<INT_PTR>(GetProcAddress((HMODULE)handle, lpstr));
6259 if ((address == NULL) && throwOnError)
6260 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName);
6261#else // !FEATURE_PAL
6262 INT_PTR address = reinterpret_cast<INT_PTR>(PAL_GetProcAddressDirect((NATIVE_LIBRARY_HANDLE)handle, lpstr));
6263 if ((address == NULL) && throwOnError)
6264 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName);
6265#endif // !FEATURE_PAL
6266
6267 return address;
6268}
6269
6270// static
6271NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName)
6272{
6273 STANDARD_VM_CONTRACT;
6274 //Dynamic Pinvoke Support:
6275 //Check if we need to provide the host a chance to provide the unmanaged dll
6276
6277#ifndef PLATFORM_UNIX
6278 // Prevent Overriding of Windows API sets.
6279 // This is replicating quick check from the OS implementation of api sets.
6280 if (SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0)
6281 {
6282 return NULL;
6283 }
6284#endif
6285
6286 LPVOID hmod = NULL;
6287 CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
6288 Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
6289
6290 PEFile *pManifestFile = pAssembly->GetManifestFile();
6291 PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext();
6292
6293 //Step 0: Check if the assembly was bound using TPA.
6294 // The Binding Context can be null or an overridden TPA context
6295 if (pBindingContext == NULL)
6296 {
6297 // If we do not have any binder associated, then return to the default resolution mechanism.
6298 return NULL;
6299 }
6300
6301 UINT_PTR assemblyBinderID = 0;
6302 IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
6303
6304 ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
6305
6306 // For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call.
6307 if (AreSameBinderInstance(pCurrentBinder, pTPABinder))
6308 {
6309 return NULL;
6310 }
6311
6312#ifdef FEATURE_COMINTEROP
6313 CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder();
6314 if (AreSameBinderInstance(pCurrentBinder, pWinRTBinder))
6315 {
6316 // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to
6317 // types being referenced from Windows.Foundation.Winmd) or when dealing with a winmd (which is bound using WinRT binder).
6318 //
6319 // For this, we should use the standard mechanism to make pinvoke call as well.
6320 return NULL;
6321 }
6322#endif // FEATURE_COMINTEROP
6323
6324 //Step 1: If the assembly was not bound using TPA,
6325 // Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll to give
6326 // The custom assembly context a chance to load the unmanaged dll.
6327
6328 GCX_COOP();
6329
6330 STRINGREF pUnmanagedDllName;
6331 pUnmanagedDllName = StringObject::NewString(wszLibName);
6332
6333 GCPROTECT_BEGIN(pUnmanagedDllName);
6334
6335 // Get the pointer to the managed assembly load context
6336 INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
6337
6338 // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnamanagedDll method.
6339 PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL);
6340 DECLARE_ARGHOLDER_ARRAY(args, 2);
6341 args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName);
6342 args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
6343
6344 // Make the call
6345 CALL_MANAGED_METHOD(hmod,LPVOID,args);
6346
6347 GCPROTECT_END();
6348
6349 return (NATIVE_LIBRARY_HANDLE)hmod;
6350}
6351
6352// Try to load the module alongside the assembly where the PInvoke was declared.
6353NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6354{
6355 STANDARD_VM_CONTRACT;
6356
6357 NATIVE_LIBRARY_HANDLE hmod = NULL;
6358
6359 SString path = pAssembly->GetManifestFile()->GetPath();
6360
6361 SString::Iterator lastPathSeparatorIter = path.End();
6362 if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter))
6363 {
6364 lastPathSeparatorIter++;
6365 path.Truncate(lastPathSeparatorIter);
6366
6367 path.Append(libName);
6368 hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker);
6369 }
6370
6371 return hmod;
6372}
6373
6374// Try to load the module from the native DLL search directories
6375NATIVE_LIBRARY_HANDLE NDirect::LoadFromNativeDllSearchDirectories(AppDomain* pDomain, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
6376{
6377 STANDARD_VM_CONTRACT;
6378
6379 NATIVE_LIBRARY_HANDLE hmod = NULL;
6380
6381 if (pDomain->HasNativeDllSearchDirectories())
6382 {
6383 AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories();
6384 while (hmod == NULL && pathIter.Next())
6385 {
6386 SString qualifiedPath(*(pathIter.GetPath()));
6387 qualifiedPath.Append(libName);
6388 if (!Path::IsRelative(qualifiedPath))
6389 {
6390 hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker);
6391 }
6392 }
6393 }
6394
6395 return hmod;
6396}
6397
6398#ifdef FEATURE_PAL
6399static const int MaxVariationCount = 4;
6400static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6401{
6402 // Supported lib name variations
6403 static auto NameFmt = W("%.0s%s%.0s");
6404 static auto PrefixNameFmt = W("%s%s%.0s");
6405 static auto NameSuffixFmt = W("%.0s%s%s");
6406 static auto PrefixNameSuffixFmt = W("%s%s%s");
6407
6408 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6409
6410 int varCount = 0;
6411 if (!libNameIsRelativePath)
6412 {
6413 libNameVariations[varCount++] = NameFmt;
6414 }
6415 else
6416 {
6417 // We check if the suffix is contained in the name, because on Linux it is common to append
6418 // a version number to the library name (e.g. 'libicuuc.so.57').
6419 bool containsSuffix = false;
6420 SString::CIterator it = libName.Begin();
6421 if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W))
6422 {
6423 it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W);
6424 containsSuffix = it == libName.End() || *it == (WCHAR)'.';
6425 }
6426
6427 // If the path contains a path delimiter, we don't add a prefix
6428 it = libName.Begin();
6429 bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W);
6430
6431 if (containsSuffix)
6432 {
6433 libNameVariations[varCount++] = NameFmt;
6434
6435 if (!containsDelim)
6436 libNameVariations[varCount++] = PrefixNameFmt;
6437
6438 libNameVariations[varCount++] = NameSuffixFmt;
6439
6440 if (!containsDelim)
6441 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6442 }
6443 else
6444 {
6445 libNameVariations[varCount++] = NameSuffixFmt;
6446
6447 if (!containsDelim)
6448 libNameVariations[varCount++] = PrefixNameSuffixFmt;
6449
6450 libNameVariations[varCount++] = NameFmt;
6451
6452 if (!containsDelim)
6453 libNameVariations[varCount++] = PrefixNameFmt;
6454 }
6455 }
6456
6457 *numberOfVariations = varCount;
6458}
6459#else // FEATURE_PAL
6460static const int MaxVariationCount = 2;
6461static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
6462{
6463 // Supported lib name variations
6464 static auto NameFmt = W("%.0s%s%.0s");
6465 static auto NameSuffixFmt = W("%.0s%s%s");
6466
6467 _ASSERTE(*numberOfVariations >= MaxVariationCount);
6468
6469 int varCount = 0;
6470
6471 // The purpose of following code is to workaround LoadLibrary limitation:
6472 // LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario:
6473 // [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke
6474 // The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll"
6475 auto it = libName.Begin();
6476 if (!libNameIsRelativePath ||
6477 !libName.Find(it, W('.')) ||
6478 libName.EndsWith(W(".")) ||
6479 libName.EndsWithCaseInsensitive(W(".dll")) ||
6480 libName.EndsWithCaseInsensitive(W(".exe")))
6481 {
6482 // Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
6483 // If the string specifies a full path, the function searches only that path for the module.
6484 // If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name.
6485 // To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.
6486 libNameVariations[varCount++] = NameFmt;
6487 }
6488 else
6489 {
6490 libNameVariations[varCount++] = NameFmt;
6491 libNameVariations[varCount++] = NameSuffixFmt;
6492 }
6493
6494 *numberOfVariations = varCount;
6495}
6496#endif // FEATURE_PAL
6497
6498// Search for the library and variants of its name in probing directories.
6499//static
6500NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly,
6501 BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag,
6502 LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
6503{
6504 STANDARD_VM_CONTRACT;
6505
6506 NATIVE_LIBRARY_HANDLE hmod = NULL;
6507
6508#if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX)
6509 // Try to go straight to System32 for Windows API sets. This is replicating quick check from
6510 // the OS implementation of api sets.
6511 if (SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0)
6512 {
6513 hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker);
6514 if (hmod != NULL)
6515 {
6516 return hmod;
6517 }
6518 }
6519#endif // FEATURE_CORESYSTEM && !FEATURE_PAL
6520
6521 AppDomain* pDomain = GetAppDomain();
6522 DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
6523 bool libNameIsRelativePath = Path::IsRelative(wszLibName);
6524
6525 // P/Invokes are often declared with variations on the actual library name.
6526 // For example, it's common to leave off the extension/suffix of the library
6527 // even if it has one, or to leave off a prefix like "lib" even if it has one
6528 // (both of these are typically done to smooth over cross-platform differences).
6529 // We try to dlopen with such variations on the original.
6530 const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {};
6531 int numberOfVariations = COUNTOF(prefixSuffixCombinations);
6532 DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath);
6533 for (int i = 0; i < numberOfVariations; i++)
6534 {
6535 SString currLibNameVariation;
6536 currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W);
6537
6538 // NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path
6539 hmod = LoadFromNativeDllSearchDirectories(pDomain, currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
6540 if (hmod != NULL)
6541 {
6542 return hmod;
6543 }
6544
6545 if (!libNameIsRelativePath)
6546 {
6547 DWORD flags = loadWithAlteredPathFlags;
6548 if ((dllImportSearchPathFlag & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
6549 {
6550 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
6551 // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
6552 flags |= dllImportSearchPathFlag;
6553 }
6554
6555 hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
6556 if (hmod != NULL)
6557 {
6558 return hmod;
6559 }
6560 }
6561 else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
6562 {
6563 hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
6564 if (hmod != NULL)
6565 {
6566 return hmod;
6567 }
6568 }
6569
6570 hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlag, pErrorTracker);
6571 if (hmod != NULL)
6572 {
6573 return hmod;
6574 }
6575 }
6576
6577 // This may be an assembly name
6578 // Format is "fileName, assemblyDisplayName"
6579 MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName);
6580 char *szComma = strchr(szLibName, ',');
6581 if (szComma)
6582 {
6583 *szComma = '\0';
6584 // Trim white spaces
6585 while (COMCharacter::nativeIsWhiteSpace(*(++szComma)));
6586
6587 AssemblySpec spec;
6588 if (SUCCEEDED(spec.Init(szComma)))
6589 {
6590 // Need to perform case insensitive hashing.
6591 CQuickBytes qbLC;
6592 {
6593 UTF8_TO_LOWER_CASE(szLibName, qbLC);
6594 szLibName = (LPUTF8) qbLC.Ptr();
6595 }
6596
6597 Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED);
6598 Module *pModule = pAssembly->FindModuleByName(szLibName);
6599
6600 hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
6601 }
6602 }
6603
6604 return hmod;
6605}
6606
6607// This Method returns an instance of the PAL-Registered handle
6608HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker)
6609{
6610 CONTRACTL
6611 {
6612 STANDARD_VM_CHECK;
6613 PRECONDITION( CheckPointer( pMD ) );
6614 }
6615 CONTRACTL_END;
6616
6617 LPCUTF8 name = pMD->GetLibName();
6618 if ( !name || !*name )
6619 return NULL;
6620
6621 ModuleHandleHolder hmod;
6622
6623 PREFIX_ASSUME( name != NULL );
6624 MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
6625
6626 AppDomain* pDomain = GetAppDomain();
6627
6628 // AssemblyLoadContext is not supported in AppX mode and thus,
6629 // we should not perform PInvoke resolution via it when operating in
6630 // AppX mode.
6631 if (!AppX::IsAppXProcess())
6632 {
6633 hmod = LoadLibraryModuleViaHost(pMD, pDomain, wszLibName);
6634 if (hmod != NULL)
6635 {
6636#ifdef FEATURE_PAL
6637 // Register the system library handle with PAL and get a PAL library handle
6638 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6639#endif // FEATURE_PAL
6640 return hmod.Extract();
6641 }
6642 }
6643
6644 hmod = pDomain->FindUnmanagedImageInCache(wszLibName);
6645 if (hmod != NULL)
6646 {
6647 return hmod.Extract();
6648 }
6649
6650#ifdef FEATURE_PAL
6651 // In the PAL version of CoreCLR, the CLR module itself exports the functionality
6652 // that the Windows version obtains from kernel32 and friends. In order to avoid
6653 // picking up the wrong instance, we perform this redirection first.
6654 // This is also true for CoreSystem builds, where mscorlib p/invokes are forwarded through coreclr
6655 // itself so we can control CoreSystem library/API name re-mapping from one central location.
6656 if (SString::_wcsicmp(wszLibName, MAIN_CLR_MODULE_NAME_W) == 0)
6657 {
6658 hmod = GetCLRModule();
6659 }
6660#endif // FEATURE_PAL
6661
6662 if (hmod == NULL)
6663 {
6664 hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName);
6665 if (hmod != NULL)
6666 {
6667#ifdef FEATURE_PAL
6668 // Register the system library handle with PAL and get a PAL library handle
6669 hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
6670#endif // FEATURE_PAL
6671 }
6672 }
6673
6674 if (hmod != NULL)
6675 {
6676 // If we have a handle add it to the cache.
6677 pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
6678 }
6679
6680 return hmod.Extract();
6681}
6682
6683//---------------------------------------------------------
6684// Loads the DLL and finds the procaddress for an N/Direct call.
6685//---------------------------------------------------------
6686/* static */
6687VOID NDirect::NDirectLink(NDirectMethodDesc *pMD)
6688{
6689 CONTRACTL
6690 {
6691 STANDARD_VM_CHECK;
6692 PRECONDITION(CheckPointer(pMD));
6693 }
6694 CONTRACTL_END;
6695
6696 //
6697 // On the phone, we only allow platform assemblies to define pinvokes
6698 // unless the host has asked us otherwise.
6699 //
6700
6701 if (pMD->IsClassConstructorTriggeredAtLinkTime())
6702 {
6703 pMD->GetMethodTable()->CheckRunClassInitThrowing();
6704 }
6705
6706 if (pMD->IsQCall())
6707 {
6708 LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget;
6709
6710 // Do not repeat the lookup if the QCall was hardbound during ngen
6711 if (pvTarget == NULL)
6712 {
6713 pvTarget = ECall::GetQCallImpl(pMD);
6714 }
6715 else
6716 {
6717 _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD));
6718 }
6719
6720 pMD->SetNDirectTarget(pvTarget);
6721 return;
6722 }
6723
6724 // Loading unmanaged dlls can trigger dllmains which certainly count as code execution!
6725 pMD->EnsureActive();
6726
6727 LoadLibErrorTracker errorTracker;
6728
6729 BOOL fSuccess = FALSE;
6730 HINSTANCE hmod = LoadLibraryModule( pMD, &errorTracker );
6731 if ( hmod )
6732 {
6733 LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod);
6734 if (pvTarget)
6735 {
6736
6737#ifdef MDA_SUPPORTED
6738 MdaInvalidOverlappedToPinvoke *pOverlapCheck = MDA_GET_ASSISTANT(InvalidOverlappedToPinvoke);
6739 if (pOverlapCheck && pOverlapCheck->ShouldHook(pMD))
6740 {
6741 LPVOID pNewTarget = pOverlapCheck->Register(hmod,pvTarget);
6742 if (pNewTarget)
6743 {
6744 pvTarget = pNewTarget;
6745 }
6746 }
6747#endif
6748 pMD->SetNDirectTarget(pvTarget);
6749 fSuccess = TRUE;
6750 }
6751 }
6752
6753 if (!fSuccess)
6754 {
6755 if (pMD->GetLibName() == NULL)
6756 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME);
6757
6758 StackSString ssLibName(SString::Utf8, pMD->GetLibName());
6759
6760 if (!hmod)
6761 {
6762 errorTracker.Throw(ssLibName);
6763 }
6764
6765 WCHAR wszEPName[50];
6766 if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0)
6767 {
6768 wszEPName[0] = W('?');
6769 wszEPName[1] = W('\0');
6770 }
6771#ifdef FEATURE_PAL
6772 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName);
6773#else
6774 COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName);
6775#endif
6776 }
6777}
6778
6779
6780//---------------------------------------------------------
6781// One-time init
6782//---------------------------------------------------------
6783/*static*/ void NDirect::Init()
6784{
6785 CONTRACTL
6786 {
6787 THROWS;
6788 GC_TRIGGERS;
6789 MODE_ANY;
6790 INJECT_FAULT(COMPlusThrowOM());
6791 }
6792 CONTRACTL_END;
6793
6794#if !defined(FEATURE_PAL)
6795 // Check if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
6796 HMODULE hMod = CLRGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
6797 _ASSERTE(hMod != NULL);
6798
6799 if (GetProcAddress(hMod, "AddDllDirectory") != NULL)
6800 {
6801 // The AddDllDirectory export was added in KB2533623 together with the new flag support
6802 s_fSecureLoadLibrarySupported = true;
6803 }
6804#endif // !FEATURE_PAL
6805}
6806
6807
6808//==========================================================================
6809// This function is reached only via NDirectImportThunk. It's purpose
6810// is to ensure that the target DLL is fully loaded and ready to run.
6811//
6812// FUN FACTS: Though this function is actually entered in unmanaged mode,
6813// it can reenter managed mode and throw a COM+ exception if the DLL linking
6814// fails.
6815//==========================================================================
6816
6817
6818EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD)
6819{
6820 LPVOID ret = NULL;
6821
6822 BEGIN_PRESERVE_LAST_ERROR;
6823
6824 CONTRACTL
6825 {
6826 THROWS;
6827 GC_TRIGGERS;
6828 MODE_PREEMPTIVE;
6829 SO_TOLERANT;
6830 }
6831 CONTRACTL_END;
6832
6833 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
6834 // this function is called by CLR to native assembly stubs which are called by
6835 // managed code as a result, we need an unwind and continue handler to translate
6836 // any of our internal exceptions into managed exceptions.
6837 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6838
6839 if (pMD->IsEarlyBound())
6840 {
6841 if (!pMD->IsZapped())
6842 {
6843 // we need the MD to be populated in case we decide to build an intercept
6844 // stub to wrap the target in InitEarlyBoundNDirectTarget
6845 PInvokeStaticSigInfo sigInfo;
6846 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
6847 }
6848
6849 pMD->InitEarlyBoundNDirectTarget();
6850 }
6851 else
6852 {
6853 //
6854 // Otherwise we're in an inlined pinvoke late bound MD
6855 //
6856 INDEBUG(Thread *pThread = GetThread());
6857 {
6858 _ASSERTE(pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr());
6859
6860 CONSISTENCY_CHECK(pMD->IsNDirect());
6861 //
6862 // With IL stubs, we don't have to do anything but ensure the DLL is loaded.
6863 //
6864
6865 if (!pMD->IsZapped())
6866 {
6867 PInvokeStaticSigInfo sigInfo;
6868 NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo);
6869 }
6870 else
6871 {
6872 // must have been populated at NGEN time
6873 _ASSERTE(pMD->GetLibName() != NULL);
6874 }
6875
6876 pMD->CheckRestore();
6877
6878 NDirect::NDirectLink(pMD);
6879 }
6880 }
6881
6882 ret = pMD->GetNDirectTarget();
6883
6884 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
6885 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
6886
6887 END_PRESERVE_LAST_ERROR;
6888
6889 return ret;
6890}
6891
6892//===========================================================================
6893// Support for Pinvoke Calli instruction
6894//
6895//===========================================================================
6896
6897EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock * pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD)
6898{
6899 BEGIN_PRESERVE_LAST_ERROR;
6900
6901 STATIC_CONTRACT_THROWS;
6902 STATIC_CONTRACT_GC_TRIGGERS;
6903 STATIC_CONTRACT_MODE_COOPERATIVE;
6904 STATIC_CONTRACT_ENTRY_POINT;
6905
6906 MAKE_CURRENT_THREAD_AVAILABLE();
6907
6908#ifdef _DEBUG
6909 Thread::ObjectRefFlush(CURRENT_THREAD);
6910#endif
6911
6912 FrameWithCookie<PrestubMethodFrame> frame(pTransitionBlock, pMD);
6913 PrestubMethodFrame * pFrame = &frame;
6914
6915 pFrame->Push(CURRENT_THREAD);
6916
6917 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
6918 _ASSERTE(pMD == pFrame->GetFunction());
6919
6920 GetILStubForCalli(pVASigCookie, pMD);
6921
6922 pFrame->Pop(CURRENT_THREAD);
6923
6924 END_PRESERVE_LAST_ERROR;
6925}
6926
6927EXTERN_C void STDCALL GenericPInvokeCalliStubWorker(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
6928{
6929 BEGIN_PRESERVE_LAST_ERROR;
6930
6931 STATIC_CONTRACT_THROWS;
6932 STATIC_CONTRACT_GC_TRIGGERS;
6933 STATIC_CONTRACT_MODE_COOPERATIVE;
6934 STATIC_CONTRACT_ENTRY_POINT;
6935
6936 MAKE_CURRENT_THREAD_AVAILABLE();
6937
6938#ifdef _DEBUG
6939 Thread::ObjectRefFlush(CURRENT_THREAD);
6940#endif
6941
6942 FrameWithCookie<PInvokeCalliFrame> frame(pTransitionBlock, pVASigCookie, pUnmanagedTarget);
6943 PInvokeCalliFrame * pFrame = &frame;
6944
6945 pFrame->Push(CURRENT_THREAD);
6946
6947 _ASSERTE(pVASigCookie == pFrame->GetVASigCookie());
6948
6949 GetILStubForCalli(pVASigCookie, NULL);
6950
6951 pFrame->Pop(CURRENT_THREAD);
6952
6953 END_PRESERVE_LAST_ERROR;
6954}
6955
6956PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
6957{
6958 CONTRACT(PCODE)
6959 {
6960 THROWS;
6961 GC_TRIGGERS;
6962 ENTRY_POINT;
6963 MODE_ANY;
6964 PRECONDITION(CheckPointer(pVASigCookie));
6965 PRECONDITION(CheckPointer(pMD, NULL_OK));
6966 POSTCONDITION(RETVAL != NULL);
6967 }
6968 CONTRACT_END;
6969
6970 PCODE pTempILStub = NULL;
6971
6972 INSTALL_MANAGED_EXCEPTION_DISPATCHER;
6973 // this function is called by CLR to native assembly stubs which are called by
6974 // managed code as a result, we need an unwind and continue handler to translate
6975 // any of our internal exceptions into managed exceptions.
6976 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6977
6978 // Force a GC if the stress level is high enough
6979 GCStress<cfg_any>::MaybeTrigger();
6980
6981 GCX_PREEMP();
6982
6983 Signature signature = pVASigCookie->signature;
6984 CorPinvokeMap unmgdCallConv = pmNoMangle;
6985
6986 DWORD dwStubFlags = NDIRECTSTUB_FL_BESTFIT;
6987
6988 // The MethodDesc pointer may in fact be the unmanaged target, see PInvokeStubs.asm.
6989 if (pMD == NULL || (UINT_PTR)pMD & 0x1)
6990 {
6991 pMD = NULL;
6992 dwStubFlags |= NDIRECTSTUB_FL_UNMANAGED_CALLI;
6993
6994 // need to convert the CALLI signature to stub signature with managed calling convention
6995 switch (MetaSig::GetCallingConvention(pVASigCookie->pModule, pVASigCookie->signature))
6996 {
6997 case IMAGE_CEE_CS_CALLCONV_C:
6998 unmgdCallConv = pmCallConvCdecl;
6999 break;
7000 case IMAGE_CEE_CS_CALLCONV_STDCALL:
7001 unmgdCallConv = pmCallConvStdcall;
7002 break;
7003 case IMAGE_CEE_CS_CALLCONV_THISCALL:
7004 unmgdCallConv = pmCallConvThiscall;
7005 break;
7006 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
7007 unmgdCallConv = pmCallConvFastcall;
7008 break;
7009 default:
7010 COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV);
7011 }
7012
7013 LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap();
7014 PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen()));
7015 CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen());
7016
7017 // make the stub IMAGE_CEE_CS_CALLCONV_DEFAULT
7018 *new_sig &= ~IMAGE_CEE_CS_CALLCONV_MASK;
7019 *new_sig |= IMAGE_CEE_CS_CALLCONV_DEFAULT;
7020
7021 signature = Signature(new_sig, signature.GetRawSigLen());
7022 }
7023 else
7024 {
7025 _ASSERTE(pMD->IsNDirect());
7026 dwStubFlags |= NDIRECTSTUB_FL_CONVSIGASVARARG;
7027
7028 // vararg P/Invoke must be cdecl
7029 unmgdCallConv = pmCallConvCdecl;
7030
7031 if (((NDirectMethodDesc *)pMD)->IsClassConstructorTriggeredByILStub())
7032 {
7033 dwStubFlags |= NDIRECTSTUB_FL_TRIGGERCCTOR;
7034 }
7035 }
7036
7037 mdMethodDef md;
7038 CorNativeLinkFlags nlFlags;
7039 CorNativeLinkType nlType;
7040
7041 if (pMD != NULL)
7042 {
7043 PInvokeStaticSigInfo sigInfo(pMD);
7044
7045 md = pMD->GetMemberDef();
7046 nlFlags = sigInfo.GetLinkFlags();
7047 nlType = sigInfo.GetCharSet();
7048 }
7049 else
7050 {
7051 md = mdMethodDefNil;
7052 nlFlags = nlfNone;
7053 nlType = nltAnsi;
7054 }
7055
7056 StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule);
7057
7058 MethodDesc* pStubMD = NDirect::CreateCLRToNativeILStub(&sigDesc,
7059 nlType,
7060 nlFlags,
7061 unmgdCallConv,
7062 dwStubFlags);
7063
7064 pTempILStub = JitILStub(pStubMD);
7065
7066 InterlockedCompareExchangeT<PCODE>(&pVASigCookie->pNDirectILStub,
7067 pTempILStub,
7068 NULL);
7069
7070 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
7071 UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
7072
7073 RETURN pVASigCookie->pNDirectILStub;
7074}
7075
7076#endif // CROSSGEN_COMPILE
7077
7078#endif // #ifndef DACCESS_COMPILE
7079
7080//
7081// Truncates a SString by first converting it to unicode and truncate it
7082// if it is larger than size. "..." will be appended if it is truncated.
7083//
7084void TruncateUnicodeString(SString &string, COUNT_T bufSize)
7085{
7086 string.Normalize();
7087 if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize)
7088 {
7089 _ASSERTE(bufSize / sizeof(WCHAR) > 4);
7090 string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4);
7091 string.Append(W("..."));
7092 }
7093}
7094