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