Merge pull request #14619 from briansull/emitter-cleanup
[platform/upstream/coreclr.git] / src / vm / ecall.cpp
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 // ECALL.CPP -
5 //
6 // Handles our private native calling interface.
7 //
8
9
10
11 #include "common.h"
12
13 #include "ecall.h"
14
15 #include "comdelegate.h"
16
17 #ifndef DACCESS_COMPILE
18
19 #ifdef CROSSGEN_COMPILE
20 namespace CrossGenMscorlib
21 {
22     extern const ECClass c_rgECClasses[];
23     extern const int c_nECClasses;
24 };
25 using namespace CrossGenMscorlib;
26 #else // CROSSGEN_COMPILE
27 extern const ECClass c_rgECClasses[];
28 extern const int c_nECClasses;
29 #endif // CROSSGEN_COMPILE
30
31
32 // METHOD__STRING__CTORF_XXX has to be in same order as ECall::CtorCharXxx
33 #define METHOD__STRING__CTORF_FIRST METHOD__STRING__CTORF_CHARARRAY
34 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 0 == METHOD__STRING__CTORF_CHARARRAY);
35 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 1 == METHOD__STRING__CTORF_CHARARRAY_START_LEN);
36 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 2 == METHOD__STRING__CTORF_CHAR_COUNT);
37 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 3 == METHOD__STRING__CTORF_CHARPTR);
38 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 4 == METHOD__STRING__CTORF_CHARPTR_START_LEN);
39 static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 5 == METHOD__STRING__CTORF_READONLYSPANOFCHAR);
40
41 // ECall::CtorCharXxx has to be in same order as METHOD__STRING__CTORF_XXX
42 #define ECallCtor_First ECall::CtorCharArrayManaged
43 static_assert_no_msg(ECallCtor_First + 0 == ECall::CtorCharArrayManaged);
44 static_assert_no_msg(ECallCtor_First + 1 == ECall::CtorCharArrayStartLengthManaged);
45 static_assert_no_msg(ECallCtor_First + 2 == ECall::CtorCharCountManaged);
46 static_assert_no_msg(ECallCtor_First + 3 == ECall::CtorCharPtrManaged);
47 static_assert_no_msg(ECallCtor_First + 4 == ECall::CtorCharPtrStartLengthManaged);
48 static_assert_no_msg(ECallCtor_First + 5 == ECall::CtorReadOnlySpanOfCharManaged);
49
50 #define NumberOfStringConstructors 6
51
52 void ECall::PopulateManagedStringConstructors()
53 {
54     STANDARD_VM_CONTRACT;
55
56     INDEBUG(static bool fInitialized = false);
57     _ASSERTE(!fInitialized);    // assume this method is only called once
58     _ASSERTE(g_pStringClass != NULL);
59
60     for (int i = 0; i < NumberOfStringConstructors; i++)
61     {
62         MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__STRING__CTORF_FIRST + i));
63         _ASSERTE(pMD != NULL);
64     
65         PCODE pDest = pMD->GetMultiCallableAddrOfCode();
66
67         ECall::DynamicallyAssignFCallImpl(pDest, ECallCtor_First + i);
68     }
69     INDEBUG(fInitialized = true);
70 }
71
72 static CrstStatic gFCallLock;
73
74 // This variable is used to force the compiler not to tailcall a function.
75 int FC_NO_TAILCALL;
76
77 #endif // !DACCESS_COMPILE
78
79 // To provide a quick check, this is the lowest and highest
80 // addresses of any FCALL starting address
81 GVAL_IMPL_INIT(TADDR, gLowestFCall, (TADDR)-1);
82 GVAL_IMPL(TADDR, gHighestFCall);
83
84 GARY_IMPL(PTR_ECHash, gFCallMethods, FCALL_HASH_SIZE);
85
86 inline unsigned FCallHash(PCODE pTarg) {
87     LIMITED_METHOD_DAC_CONTRACT;
88     return pTarg % FCALL_HASH_SIZE;
89 }
90
91 #ifdef DACCESS_COMPILE
92
93 GARY_IMPL(PCODE, g_FCDynamicallyAssignedImplementations,
94           ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS);
95
96 #else // !DACCESS_COMPILE
97
98 PCODE g_FCDynamicallyAssignedImplementations[ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS] = {
99     #undef DYNAMICALLY_ASSIGNED_FCALL_IMPL
100     #define DYNAMICALLY_ASSIGNED_FCALL_IMPL(id,defaultimpl) GetEEFuncEntryPoint(defaultimpl),
101     DYNAMICALLY_ASSIGNED_FCALLS()
102 };
103
104 void ECall::DynamicallyAssignFCallImpl(PCODE impl, DWORD index)
105 {
106     CONTRACTL
107     {
108         NOTHROW;
109         GC_NOTRIGGER;
110         MODE_ANY;
111     }
112     CONTRACTL_END;
113
114     _ASSERTE(index < NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS);
115     g_FCDynamicallyAssignedImplementations[index] = impl;
116 }
117
118 /*******************************************************************************/
119 static INT FindImplsIndexForClass(MethodTable* pMT)
120 {
121     CONTRACTL
122     {
123         NOTHROW;
124         GC_NOTRIGGER;
125         MODE_ANY;
126     }
127     CONTRACTL_END;
128
129     LPCUTF8 pszNamespace = 0;
130     LPCUTF8 pszName = pMT->GetFullyQualifiedNameInfo(&pszNamespace);
131
132     // Array classes get null from the above routine, but they have no ecalls.
133     if (pszName == NULL)
134         return (-1);
135
136     unsigned low  = 0;
137     unsigned high = c_nECClasses;
138
139 #ifdef _DEBUG
140     static bool checkedSort = false;
141     if (!checkedSort) {
142         checkedSort = true;
143         for (unsigned i = 1; i < high; i++)  {
144                 // Make certain list is sorted!
145             int cmp = strcmp(c_rgECClasses[i].m_szClassName, c_rgECClasses[i-1].m_szClassName);
146             if (cmp == 0)
147                 cmp = strcmp(c_rgECClasses[i].m_szNameSpace, c_rgECClasses[i-1].m_szNameSpace);
148             _ASSERTE(cmp > 0 && W("You forgot to keep ECall class names sorted"));      // Hey, you forgot to sort the new class
149         }
150     }
151 #endif // _DEBUG
152     while (high > low) {
153         unsigned mid  = (high + low) / 2;
154         int cmp = strcmp(pszName, c_rgECClasses[mid].m_szClassName);
155         if (cmp == 0)
156             cmp = strcmp(pszNamespace, c_rgECClasses[mid].m_szNameSpace);
157
158         if (cmp == 0) {
159             return(mid);
160         }
161         if (cmp > 0)
162             low = mid+1;
163         else
164             high = mid;
165     }
166
167     return (-1);
168 }
169
170 /*******************************************************************************/
171 /*  Finds the implementation for the given method desc.  */
172
173 static INT FindECIndexForMethod(MethodDesc *pMD, const LPVOID* impls)
174 {
175     CONTRACTL
176     {
177         THROWS;
178         GC_TRIGGERS;
179         MODE_ANY;
180     }
181     CONTRACTL_END;
182
183     LPCUTF8 szMethodName = pMD->GetName();
184     PCCOR_SIGNATURE pMethodSig;
185     ULONG       cbMethodSigLen;
186
187     pMD->GetSig(&pMethodSig, &cbMethodSigLen);
188     Module* pModule = pMD->GetModule();
189
190     for (ECFunc* cur = (ECFunc*)impls; !cur->IsEndOfArray(); cur = cur->NextInArray())
191     {
192         if (strcmp(cur->m_szMethodName, szMethodName) != 0)
193             continue;
194
195         if (cur->HasSignature())
196         {
197             Signature sig = MscorlibBinder::GetTargetSignature(cur->m_pMethodSig);
198
199             //@GENERICS: none of these methods belong to generic classes so there is no instantiation info to pass in
200             if (!MetaSig::CompareMethodSigs(pMethodSig, cbMethodSigLen, pModule, NULL,
201                                             sig.GetRawSig(), sig.GetRawSigLen(), MscorlibBinder::GetModule(), NULL))
202             {
203                 continue;
204             }
205         }
206
207         // We have found a match!
208         return static_cast<INT>((LPVOID*)cur - impls);
209     }
210
211     return -1;
212 }
213
214 /*******************************************************************************/
215 /* ID is formed of 2 USHORTs - class index  in high word, method index in low word.  */
216 /* class index starts at 1. id == 0 means no implementation.                    */
217
218 DWORD ECall::GetIDForMethod(MethodDesc *pMD)
219 {
220     CONTRACTL
221     {
222         THROWS;
223         GC_TRIGGERS;
224         MODE_ANY;
225     }
226     CONTRACTL_END;
227
228     // We should not go here for NGened methods
229     _ASSERTE(!pMD->IsZapped());
230
231     INT ImplsIndex = FindImplsIndexForClass(pMD->GetMethodTable());
232     if (ImplsIndex < 0)
233         return 0;
234     INT ECIndex = FindECIndexForMethod(pMD, c_rgECClasses[ImplsIndex].m_pECFunc);
235     if (ECIndex < 0)
236         return 0;
237
238     return (ImplsIndex<<16) | (ECIndex + 1);
239 }
240
241 static ECFunc *FindECFuncForID(DWORD id)
242 {
243     LIMITED_METHOD_CONTRACT;
244
245     if (id == 0)
246         return NULL;
247
248     INT ImplsIndex  = (id >> 16);
249     INT ECIndex     = (id & 0xffff) - 1;
250
251     return (ECFunc*)(c_rgECClasses[ImplsIndex].m_pECFunc + ECIndex);
252 }
253
254 static ECFunc* FindECFuncForMethod(MethodDesc* pMD)
255 {
256     CONTRACTL
257     {
258         THROWS;
259         GC_TRIGGERS;
260         MODE_ANY;
261         PRECONDITION(pMD->IsFCall());
262     }
263     CONTRACTL_END;
264
265     DWORD id = ((FCallMethodDesc *)pMD)->GetECallID();
266     if (id == 0)
267     {
268         id = ECall::GetIDForMethod(pMD);
269         
270         CONSISTENCY_CHECK_MSGF(0 != id,
271                     ("No method entry found for %s::%s.\n",
272                     pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
273             
274         // Cache the id
275         ((FCallMethodDesc *)pMD)->SetECallID(id);
276     }
277
278     return FindECFuncForID(id);
279 }
280
281 /*******************************************************************************
282 * Returns 0 if it is an ECALL,
283 * Otherwise returns the native entry point (FCALL)
284 */
285 PCODE ECall::GetFCallImpl(MethodDesc * pMD, BOOL * pfSharedOrDynamicFCallImpl /*=NULL*/)
286 {
287     CONTRACTL
288     {
289         THROWS;
290         GC_TRIGGERS;
291         MODE_ANY;
292         PRECONDITION(pMD->IsFCall());
293     }
294     CONTRACTL_END;
295
296     MethodTable * pMT = pMD->GetMethodTable();
297
298     //
299     // Delegate constructors are FCalls for which the entrypoint points to the target of the delegate
300     // We have to intercept these and set the call target to the helper COMDelegate::DelegateConstruct
301     //
302     if (pMT->IsDelegate())
303     {
304         if (pfSharedOrDynamicFCallImpl)
305             *pfSharedOrDynamicFCallImpl = TRUE;
306
307         // COMDelegate::DelegateConstruct is the only fcall used by user delegates.
308         // All the other gDelegateFuncs are only used by System.Delegate
309         _ASSERTE(pMD->IsCtor());
310
311         // We need to set up the ECFunc properly.  We don't want to use the pMD passed in,
312         // since it may disappear.  Instead, use the stable one on Delegate.  Remember
313         // that this is 1:M between the FCall and the pMDs.
314         return GetFCallImpl(MscorlibBinder::GetMethod(METHOD__DELEGATE__CONSTRUCT_DELEGATE));
315     }
316
317 #ifdef FEATURE_COMINTEROP
318     // COM imported classes have special constructors
319     if (pMT->IsComObjectType() && pMT != g_pBaseCOMObject && pMT != g_pBaseRuntimeClass)
320     {
321         if (pfSharedOrDynamicFCallImpl)
322             *pfSharedOrDynamicFCallImpl = TRUE;
323
324         // This has to be tlbimp constructor
325         _ASSERTE(pMD->IsCtor());
326         _ASSERTE(!pMT->IsProjectedFromWinRT());
327
328         // FCComCtor does not need to be in the fcall hashtable since it does not erect frame.
329         return GetEEFuncEntryPoint(FCComCtor);
330     }
331 #endif // FEATURE_COMINTEROP
332
333     if (!pMD->GetModule()->IsSystem())
334         COMPlusThrow(kSecurityException, BFA_ECALLS_MUST_BE_IN_SYS_MOD);
335
336     ECFunc* ret = FindECFuncForMethod(pMD);
337
338     // ECall is a set of tables to call functions within the EE from the classlibs.
339     // First we use the class name & namespace to find an array of function pointers for
340     // a class, then use the function name (& sometimes signature) to find the correct
341     // function pointer for your method.  Methods in the BCL will be marked as
342     // [MethodImplAttribute(MethodImplOptions.InternalCall)] and extern.
343     //
344     // You'll see this assert in several situations, almost all being the fault of whomever
345     // last touched a particular ecall or fcall method, either here or in the classlibs.
346     // However, you must also ensure you don't have stray copies of mscorlib.dll on your machine.
347     // 1) You forgot to add your class to c_rgECClasses, the list of classes w/ ecall & fcall methods.
348     // 2) You forgot to add your particular method to the ECFunc array for your class.
349     // 3) You misspelled the name of your function and/or classname.
350     // 4) The signature of the managed function doesn't match the hardcoded metadata signature
351     //    listed in your ECFunc array.  The hardcoded metadata sig is only necessary to disambiguate
352     //    overloaded ecall functions - usually you can leave it set to NULL.
353     // 5) Your copy of mscorlib.dll & mscoree.dll are out of sync - rebuild both.
354     // 6) You've loaded the wrong copy of mscorlib.dll.  In msdev's debug menu,
355     //    select the "Modules..." dialog.  Verify the path for mscorlib is right.
356     // 7) Someone mucked around with how the signatures in metasig.h are parsed, changing the
357     //    interpretation of a part of the signature (this is very rare & extremely unlikely,
358     //    but has happened at least once).
359
360     CONSISTENCY_CHECK_MSGF(ret != NULL,
361         ("Could not find an ECALL entry for %s::%s.\n"
362         "Read comment above this assert in vm/ecall.cpp\n",
363         pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
364
365     CONSISTENCY_CHECK_MSGF(!ret->IsQCall(),
366         ("%s::%s is not registered using FCFuncElement macro in ecall.cpp",
367         pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
368
369 #ifdef CROSSGEN_COMPILE
370
371     // Use the ECFunc address as a unique fake entrypoint to make the entrypoint<->MethodDesc mapping work
372     PCODE pImplementation = (PCODE)ret;
373 #ifdef _TARGET_ARM_
374     pImplementation |= THUMB_CODE;
375 #endif
376
377 #else // CROSSGEN_COMPILE
378
379     PCODE pImplementation = (PCODE)ret->m_pImplementation;
380
381     int iDynamicID = ret->DynamicID();
382     if (iDynamicID != InvalidDynamicFCallId)
383     {
384         if (pfSharedOrDynamicFCallImpl)
385             *pfSharedOrDynamicFCallImpl = TRUE;
386
387         pImplementation = g_FCDynamicallyAssignedImplementations[iDynamicID];
388         _ASSERTE(pImplementation != NULL);
389         return pImplementation;
390     }
391
392 #endif // CROSSGEN_COMPILE
393
394     // Insert the implementation into hash table if it is not there already.
395
396     CrstHolder holder(&gFCallLock);
397
398     MethodDesc * pMDinTable = ECall::MapTargetBackToMethod(pImplementation, &pImplementation);
399
400     if (pMDinTable != NULL)
401     {
402         if (pMDinTable != pMD)
403         {
404             // The fcall entrypoints has to be at unique addresses. If you get failure here, use the following steps
405             // to fix it:
406             // 1. Consider merging the offending fcalls into one fcall. Do they really do different things?
407             // 2. If it does not make sense to merge the offending fcalls into one,
408             // add FCUnique(<a random unique number here>); to one of the offending fcalls.
409
410             _ASSERTE(!"Duplicate pImplementation entries found in reverse fcall table");
411             ThrowHR(E_FAIL);
412         }
413     }
414     else
415     {
416         ECHash * pEntry = (ECHash *)(PVOID)SystemDomain::GetGlobalLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ECHash)));
417
418         pEntry->m_pImplementation = pImplementation;
419         pEntry->m_pMD = pMD;
420
421         if(gLowestFCall > pImplementation)
422             gLowestFCall = pImplementation;
423         if(gHighestFCall < pImplementation)
424             gHighestFCall = pImplementation;
425
426         // add to hash table
427         ECHash** spot = &gFCallMethods[FCallHash(pImplementation)];
428         for(;;) {
429             if (*spot == 0) {                   // found end of list
430                 *spot = pEntry;
431                 break;
432             }
433             spot = &(*spot)->m_pNext;
434         }
435     }
436
437     if (pfSharedOrDynamicFCallImpl)
438         *pfSharedOrDynamicFCallImpl = FALSE;
439
440     _ASSERTE(pImplementation != NULL);
441     return pImplementation;
442 }
443
444 BOOL ECall::IsSharedFCallImpl(PCODE pImpl)
445 {
446     LIMITED_METHOD_CONTRACT;
447
448     PCODE pNativeCode = pImpl;
449
450     return
451 #ifdef FEATURE_COMINTEROP
452         (pNativeCode == GetEEFuncEntryPoint(FCComCtor)) ||
453 #endif
454         (pNativeCode == GetEEFuncEntryPoint(COMDelegate::DelegateConstruct));
455 }
456
457 BOOL ECall::CheckUnusedECalls(SetSHash<DWORD>& usedIDs)
458 {
459     STANDARD_VM_CONTRACT;
460
461     BOOL fUnusedFCallsFound = FALSE;
462
463     INT num = c_nECClasses;
464     for (INT ImplsIndex=0; ImplsIndex < num; ImplsIndex++)
465     {
466         const ECClass * pECClass = c_rgECClasses + ImplsIndex;
467
468         BOOL fUnreferencedType = TRUE;
469         for (ECFunc* ptr = (ECFunc*)pECClass->m_pECFunc; !ptr->IsEndOfArray(); ptr = ptr->NextInArray())
470         {
471             if (ptr->DynamicID() == InvalidDynamicFCallId && !ptr->IsUnreferenced())
472             {
473                 INT ECIndex = static_cast<INT>((LPVOID*)ptr - pECClass->m_pECFunc);
474
475                 DWORD id = (ImplsIndex<<16) | (ECIndex + 1);
476
477                 if (!usedIDs.Contains(id))
478                 {
479                     printf("CheckMscorlibExtended: Unused ecall found: %s.%s::%s\n", pECClass->m_szNameSpace, c_rgECClasses[ImplsIndex].m_szClassName, ptr->m_szMethodName);
480                     fUnusedFCallsFound = TRUE;
481                     continue;
482                 }
483             }
484             fUnreferencedType = FALSE;
485         }
486
487         if (fUnreferencedType)
488         {
489             printf("CheckMscorlibExtended: Unused type found: %s.%s\n", c_rgECClasses[ImplsIndex].m_szNameSpace, c_rgECClasses[ImplsIndex].m_szClassName);
490             fUnusedFCallsFound = TRUE;
491             continue;
492         }
493     }
494
495     return !fUnusedFCallsFound;
496 }
497
498
499 #if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
500 FCIMPL1(VOID, FCComCtor, LPVOID pV)
501 {
502     FCALL_CONTRACT;
503
504     FCUnique(0x34);
505 }
506 FCIMPLEND
507 #endif // FEATURE_COMINTEROP && !CROSSGEN_COMPILE
508
509
510
511 /* static */
512 void ECall::Init()
513 {
514     CONTRACTL
515     {
516         THROWS;
517         GC_NOTRIGGER;
518         MODE_ANY;
519     }
520     CONTRACTL_END;
521
522     gFCallLock.Init(CrstFCall);
523
524     // It is important to do an explicit increment here instead of just in-place initialization
525     // so that the global optimizer cannot figure out the value and remove the side-effect that
526     // we depend on in FC_INNER_RETURN macros and other places
527     FC_NO_TAILCALL++;
528 }
529
530 LPVOID ECall::GetQCallImpl(MethodDesc * pMD)
531 {
532     CONTRACTL
533     {
534         THROWS;
535         GC_TRIGGERS;
536         MODE_ANY;
537         PRECONDITION(pMD->IsNDirect());
538     }
539     CONTRACTL_END;
540
541     DWORD id = ((NDirectMethodDesc *)pMD)->GetECallID();
542     if (id == 0)
543     {
544         id = ECall::GetIDForMethod(pMD);
545         _ASSERTE(id != 0);
546
547         // Cache the id
548         ((NDirectMethodDesc *)pMD)->SetECallID(id);
549     }
550
551     ECFunc * cur = FindECFuncForID(id);
552
553 #ifdef _DEBUG
554     CONSISTENCY_CHECK_MSGF(cur != NULL, 
555         ("%s::%s is not registered in ecall.cpp",
556         pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
557
558     CONSISTENCY_CHECK_MSGF(cur->IsQCall(), 
559         ("%s::%s is not registered using QCFuncElement macro in ecall.cpp",
560         pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
561
562     DWORD dwAttrs = pMD->GetAttrs();
563     BOOL fPublicOrProtected = IsMdPublic(dwAttrs) || IsMdFamily(dwAttrs) || IsMdFamORAssem(dwAttrs);
564
565     // SuppressUnmanagedCodeSecurityAttribute on QCalls suppresses a full demand, but there's still a link demand 
566     // for unmanaged code permission. All QCalls should be private or internal and wrapped in a managed method 
567     // to suppress this link demand.
568     CONSISTENCY_CHECK_MSGF(!fPublicOrProtected,
569         ("%s::%s has to be private or internal.",
570         pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
571 #endif
572  
573     return cur->m_pImplementation;
574 }
575
576 #endif // !DACCESS_COMPILE
577
578 MethodDesc* ECall::MapTargetBackToMethod(PCODE pTarg, PCODE * ppAdjustedEntryPoint /*=NULL*/)
579 {
580     CONTRACTL
581     {
582         NOTHROW;
583         GC_NOTRIGGER;
584         MODE_ANY;
585         HOST_NOCALLS;
586         SO_TOLERANT;
587         SUPPORTS_DAC;
588     }
589     CONTRACTL_END;
590
591     // Searching all of the entries is expensive
592     // and we are often called with pTarg == NULL so
593     // check for this value and early exit.
594
595     if (!pTarg)
596         return NULL;
597
598     // Could this possibily be an FCall?
599     if ((pTarg < gLowestFCall) || (pTarg > gHighestFCall))
600         return NULL;
601
602     ECHash * pECHash = gFCallMethods[FCallHash(pTarg)];
603     while (pECHash != NULL)
604     {
605         if (pECHash->m_pImplementation == pTarg)
606         {
607             return pECHash->m_pMD;
608         }
609         pECHash = pECHash->m_pNext;
610     }
611     return NULL;
612 }
613
614 #ifndef DACCESS_COMPILE
615
616 /* static */
617 CorInfoIntrinsics ECall::GetIntrinsicID(MethodDesc* pMD)
618 {
619     CONTRACTL
620     {
621         THROWS;
622         GC_TRIGGERS;
623         MODE_ANY;
624         SO_TOLERANT;
625         PRECONDITION(pMD->IsFCall());
626     }
627     CONTRACTL_END;
628
629     MethodTable * pMT = pMD->GetMethodTable();
630
631 #ifdef FEATURE_COMINTEROP
632     // COM imported classes have special constructors
633     if (pMT->IsComObjectType())
634     {
635         // This has to be tlbimp constructor
636         return(CORINFO_INTRINSIC_Illegal);
637     }
638 #endif // FEATURE_COMINTEROP
639
640     //
641     // Delegate constructors are FCalls for which the entrypoint points to the target of the delegate
642     // We have to intercept these and set the call target to the helper COMDelegate::DelegateConstruct
643     //
644     if (pMT->IsDelegate())
645     {
646         // COMDelegate::DelegateConstruct is the only fcall used by user delegates.
647         // All the other gDelegateFuncs are only used by System.Delegate
648         _ASSERTE(pMD->IsCtor());
649
650         return(CORINFO_INTRINSIC_Illegal);
651     }
652
653     // All intrinsic live in mscorlib.dll (FindECFuncForMethod does not work for non-mscorlib intrinsics)
654     if (!pMD->GetModule()->IsSystem())
655     {
656         return(CORINFO_INTRINSIC_Illegal);
657     }
658
659     ECFunc* info = FindECFuncForMethod(pMD);
660
661     if (info == NULL)
662         return(CORINFO_INTRINSIC_Illegal);
663
664     return info->IntrinsicID();
665 }
666
667 #ifdef _DEBUG
668
669 void FCallAssert(void*& cache, void* target)
670 {
671     STATIC_CONTRACT_NOTHROW;
672     STATIC_CONTRACT_GC_NOTRIGGER;
673     STATIC_CONTRACT_DEBUG_ONLY;
674
675     if (cache != 0)
676     {
677         return;
678     }
679
680     //
681     // Special case fcalls with 1:N mapping between implementation and methoddesc
682     //
683     if (ECall::IsSharedFCallImpl((PCODE)target))
684     {
685         cache = (void*)1;
686         return;
687     }
688
689     MethodDesc* pMD = ECall::MapTargetBackToMethod((PCODE)target);
690     if (pMD != 0)
691     {
692         return;
693     }
694
695     // Slow but only for debugging.  This is needed because in some places
696     // we call FCALLs directly from EE code.
697
698     unsigned num = c_nECClasses;
699     for (unsigned i=0; i < num; i++)
700     {
701         for (ECFunc* ptr = (ECFunc*)c_rgECClasses[i].m_pECFunc; !ptr->IsEndOfArray(); ptr = ptr->NextInArray())
702         {
703             if (ptr->m_pImplementation  == target)
704             {                
705                 cache = target;
706                 return;
707             }
708         }
709     }
710
711     // Now check the dynamically assigned table too.
712     for (unsigned i=0; i<ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS; i++)
713     {
714         if (g_FCDynamicallyAssignedImplementations[i] == (PCODE)target)
715         {
716             cache = target;
717             return;
718         }
719     }
720
721     _ASSERTE(!"Could not find FCall implemenation in ECall.cpp");
722 }
723
724 void HCallAssert(void*& cache, void* target)
725 {
726     CONTRACTL
727     {
728         SO_TOLERANT;     // STATIC_CONTRACT_DEBUG_ONLY
729         NOTHROW;
730         GC_NOTRIGGER;
731         MODE_ANY;
732         DEBUG_ONLY;
733     }
734     CONTRACTL_END;
735
736     if (cache != 0)
737         cache = ECall::MapTargetBackToMethod((PCODE)target);
738     _ASSERTE(cache == 0 || "Use FCIMPL for fcalls");
739 }
740
741 #endif // _DEBUG
742
743 #endif // !DACCESS_COMPILE
744
745 #ifdef DACCESS_COMPILE
746
747 void ECall::EnumFCallMethods()
748 {
749     SUPPORTS_DAC;
750     gLowestFCall.EnumMem();
751     gHighestFCall.EnumMem();
752     gFCallMethods.EnumMem();
753     
754     // save all ECFunc for stackwalks.
755     // TODO: we could be smarter and only save buckets referenced during stackwalks. But we 
756     // need that entire bucket so that traversals such as MethodDesc* ECall::MapTargetBackToMethod will work.
757     for (UINT i=0;i<FCALL_HASH_SIZE;i++)
758     {
759         ECHash *ecHash = gFCallMethods[i];
760         while (ecHash)
761         {
762             // If we can't read the target memory, stop immediately so we don't work
763             // with broken data.
764             if (!DacEnumMemoryRegion(dac_cast<TADDR>(ecHash), sizeof(ECHash)))
765                 break;
766             ecHash = ecHash->m_pNext;
767
768 #if defined (_DEBUG)
769             // Test hook: when testing on debug builds, we want an easy way to test that the while
770             // correctly terminates in the face of ridiculous stuff from the target.
771             if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget) == 1)
772             {
773                 // Force us to struggle on with something bad.
774                 if (!ecHash)
775                 {
776                     ecHash = (ECHash *)(((unsigned char *)&gFCallMethods[i])+1);
777                 }
778             }
779 #endif // defined (_DEBUG)
780
781         }
782     }
783 }
784
785 #endif // DACCESS_COMPILE