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
6#include "common.h"
7#include "stubmgr.h"
8#include "virtualcallstub.h"
9#include "dllimportcallback.h"
10#include "stubhelpers.h"
11#include "asmconstants.h"
12#ifdef FEATURE_COMINTEROP
13#include "olecontexthelpers.h"
14#endif
15
16#ifdef LOGGING
17const char *GetTType( TraceType tt)
18{
19 LIMITED_METHOD_CONTRACT;
20
21 switch( tt )
22 {
23 case TRACE_ENTRY_STUB: return "TRACE_ENTRY_STUB";
24 case TRACE_STUB: return "TRACE_STUB";
25 case TRACE_UNMANAGED: return "TRACE_UNMANAGED";
26 case TRACE_MANAGED: return "TRACE_MANAGED";
27 case TRACE_FRAME_PUSH: return "TRACE_FRAME_PUSH";
28 case TRACE_MGR_PUSH: return "TRACE_MGR_PUSH";
29 case TRACE_OTHER: return "TRACE_OTHER";
30 case TRACE_UNJITTED_METHOD: return "TRACE_UNJITTED_METHOD";
31 }
32 return "TRACE_REALLY_WACKED";
33}
34
35void LogTraceDestination(const char * szHint, PCODE stubAddr, TraceDestination * pTrace)
36{
37 LIMITED_METHOD_CONTRACT;
38 if (pTrace->GetTraceType() == TRACE_UNJITTED_METHOD)
39 {
40 MethodDesc * md = pTrace->GetMethodDesc();
41 LOG((LF_CORDB, LL_INFO10000, "'%s' yields '%s' to method 0x%p for input 0x%p.\n",
42 szHint, GetTType(pTrace->GetTraceType()),
43 md, stubAddr));
44 }
45 else
46 {
47 LOG((LF_CORDB, LL_INFO10000, "'%s' yields '%s' to address 0x%p for input 0x%p.\n",
48 szHint, GetTType(pTrace->GetTraceType()),
49 pTrace->GetAddress(), stubAddr));
50 }
51}
52#endif
53
54#ifdef _DEBUG
55// Get a string representation of this TraceDestination
56// Uses the supplied buffer to store the memory (or may return a string literal).
57const WCHAR * TraceDestination::DbgToString(SString & buffer)
58{
59 CONTRACTL
60 {
61 NOTHROW;
62 GC_NOTRIGGER;
63 MODE_ANY;
64 }
65 CONTRACTL_END;
66
67 const WCHAR * pValue = W("unknown");
68
69#ifndef DACCESS_COMPILE
70 if (!StubManager::IsStubLoggingEnabled())
71 {
72 return W("<unavailable while native-debugging>");
73 }
74 // Now that we know we're not interop-debugging, we can safely call new.
75 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
76
77
78 FAULT_NOT_FATAL();
79
80 EX_TRY
81 {
82 switch(this->type)
83 {
84 case TRACE_ENTRY_STUB:
85 buffer.Printf("TRACE_ENTRY_STUB(addr=0x%p)", GetAddress());
86 pValue = buffer.GetUnicode();
87 break;
88
89 case TRACE_STUB:
90 buffer.Printf("TRACE_STUB(addr=0x%p)", GetAddress());
91 pValue = buffer.GetUnicode();
92 break;
93
94 case TRACE_UNMANAGED:
95 buffer.Printf("TRACE_UNMANAGED(addr=0x%p)", GetAddress());
96 pValue = buffer.GetUnicode();
97 break;
98
99 case TRACE_MANAGED:
100 buffer.Printf("TRACE_MANAGED(addr=0x%p)", GetAddress());
101 pValue = buffer.GetUnicode();
102 break;
103
104 case TRACE_UNJITTED_METHOD:
105 {
106 MethodDesc * md = this->GetMethodDesc();
107 buffer.Printf("TRACE_UNJITTED_METHOD(md=0x%p, %s::%s)", md, md->m_pszDebugClassName, md->m_pszDebugMethodName);
108 pValue = buffer.GetUnicode();
109 }
110 break;
111
112 case TRACE_FRAME_PUSH:
113 buffer.Printf("TRACE_FRAME_PUSH(addr=0x%p)", GetAddress());
114 pValue = buffer.GetUnicode();
115 break;
116
117 case TRACE_MGR_PUSH:
118 buffer.Printf("TRACE_MGR_PUSH(addr=0x%p, sm=%s)", GetAddress(), this->GetStubManager()->DbgGetName());
119 pValue = buffer.GetUnicode();
120 break;
121
122 case TRACE_OTHER:
123 pValue = W("TRACE_OTHER");
124 break;
125 }
126 }
127 EX_CATCH
128 {
129 pValue = W("(OOM while printing TD)");
130 }
131 EX_END_CATCH(SwallowAllExceptions);
132#endif
133 return pValue;
134}
135#endif
136
137
138void TraceDestination::InitForUnjittedMethod(MethodDesc * pDesc)
139{
140 CONTRACTL
141 {
142 GC_NOTRIGGER;
143 NOTHROW;
144 MODE_ANY;
145
146 PRECONDITION(CheckPointer(pDesc));
147 }
148 CONTRACTL_END;
149
150 _ASSERTE(pDesc->SanityCheck());
151
152 {
153 // If this is a wrapper stub, then find the real method that it will go to and patch that.
154 // This is more than just a convenience - converted wrapper MD to real MD is required for correct behavior.
155 // Wrapper MDs look like unjitted MethodDescs. So when the debugger patches one,
156 // it won't actually bind + apply the patch (it'll wait for the jit-complete instead).
157 // But if the wrapper MD is for prejitted code, then we'll never get the Jit-complete.
158 // Thus it'll miss the patch completely.
159 if (pDesc->IsWrapperStub())
160 {
161 MethodDesc * pNewDesc = NULL;
162
163 FAULT_NOT_FATAL();
164
165
166#ifndef DACCESS_COMPILE
167 EX_TRY
168 {
169 pNewDesc = pDesc->GetExistingWrappedMethodDesc();
170 }
171 EX_CATCH
172 {
173 // In case of an error, we'll just stick w/ the original method desc.
174 } EX_END_CATCH(SwallowAllExceptions)
175#else
176 // @todo - DAC needs this too, but the method is currently not DACized.
177 // However, we don't throw here b/c the error may not be fatal.
178 // DacNotImpl();
179#endif
180
181 if (pNewDesc != NULL)
182 {
183 pDesc = pNewDesc;
184
185 LOG((LF_CORDB, LL_INFO10000, "TD::UnjittedMethod: wrapper md: %p --> %p", pDesc, pNewDesc));
186
187 }
188 }
189 }
190
191
192 this->type = TRACE_UNJITTED_METHOD;
193 this->pDesc = pDesc;
194 this->stubManager = NULL;
195}
196
197
198// Initialize statics.
199#ifdef _DEBUG
200SString * StubManager::s_pDbgStubManagerLog = NULL;
201CrstStatic StubManager::s_DbgLogCrst;
202
203#endif
204
205SPTR_IMPL(StubManager, StubManager, g_pFirstManager);
206
207CrstStatic StubManager::s_StubManagerListCrst;
208
209//-----------------------------------------------------------
210// For perf reasons, the stub managers are now kept in a two
211// tier system: all stub managers but the VirtualStubManagers
212// are in the first tier. A VirtualStubManagerManager takes
213// care of all VirtualStubManagers, and is iterated last of
214// all. It does a smarter job of looking up the owning
215// manager for virtual stubs, checking the current and shared
216// appdomains before checking the remaining managers.
217//
218// Thus, this iterator will run the regular list until it
219// hits the end, then it will check the VSMM, then it will
220// end.
221//-----------------------------------------------------------
222class StubManagerIterator
223{
224 public:
225 StubManagerIterator();
226 ~StubManagerIterator();
227
228 void Reset();
229 BOOL Next();
230 PTR_StubManager Current();
231
232 protected:
233 enum SMI_State
234 {
235 SMI_START,
236 SMI_NORMAL,
237 SMI_VIRTUALCALLSTUBMANAGER,
238 SMI_END
239 };
240
241 SMI_State m_state;
242 PTR_StubManager m_pCurMgr;
243 SimpleReadLockHolder m_lh;
244};
245
246//-----------------------------------------------------------
247// Ctor
248//-----------------------------------------------------------
249StubManagerIterator::StubManagerIterator()
250{
251 WRAPPER_NO_CONTRACT;
252 SUPPORTS_DAC;
253
254 Reset();
255}
256
257void StubManagerIterator::Reset()
258{
259 LIMITED_METHOD_DAC_CONTRACT;
260 m_pCurMgr = NULL;
261 m_state = SMI_START;
262}
263
264//-----------------------------------------------------------
265// Ctor
266//-----------------------------------------------------------
267StubManagerIterator::~StubManagerIterator()
268{
269 LIMITED_METHOD_DAC_CONTRACT;
270}
271
272//-----------------------------------------------------------
273// Move to the next element. Iterators are created at
274// start-1, so must call Next before using Current
275//-----------------------------------------------------------
276BOOL StubManagerIterator::Next()
277{
278 CONTRACTL
279 {
280 NOTHROW;
281 GC_NOTRIGGER;
282 MODE_ANY;
283#ifndef DACCESS_COMPILE
284 CAN_TAKE_LOCK; // because of m_lh.Assign()
285#else
286 CANNOT_TAKE_LOCK;
287#endif
288 }
289 CONTRACTL_END;
290
291 SUPPORTS_DAC;
292
293 do {
294 if (m_state == SMI_START) {
295 m_state = SMI_NORMAL;
296 m_pCurMgr = StubManager::g_pFirstManager;
297 }
298 else if (m_state == SMI_NORMAL) {
299 if (m_pCurMgr != NULL) {
300 m_pCurMgr = m_pCurMgr->m_pNextManager;
301 }
302 else {
303 // If we've reached the end of the regular list of stub managers, then we
304 // set the VirtualCallStubManagerManager is the current item (effectively
305 // forcing it to always be the last manager checked).
306 m_state = SMI_VIRTUALCALLSTUBMANAGER;
307 VirtualCallStubManagerManager *pVCSMMgr = VirtualCallStubManagerManager::GlobalManager();
308 m_pCurMgr = PTR_StubManager(pVCSMMgr);
309#ifndef DACCESS_COMPILE
310 m_lh.Assign(&pVCSMMgr->m_RWLock);
311#endif
312 }
313 }
314 else if (m_state == SMI_VIRTUALCALLSTUBMANAGER) {
315 m_state = SMI_END;
316 m_pCurMgr = NULL;
317#ifndef DACCESS_COMPILE
318 m_lh.Clear();
319#endif
320 }
321 } while (m_state != SMI_END && m_pCurMgr == NULL);
322
323 CONSISTENCY_CHECK(m_state == SMI_END || m_pCurMgr != NULL);
324 return (m_state != SMI_END);
325}
326
327//-----------------------------------------------------------
328// Get the current contents of the iterator
329//-----------------------------------------------------------
330PTR_StubManager StubManagerIterator::Current()
331{
332 LIMITED_METHOD_DAC_CONTRACT;
333 CONSISTENCY_CHECK(m_state != SMI_START);
334 CONSISTENCY_CHECK(m_state != SMI_END);
335 CONSISTENCY_CHECK(CheckPointer(m_pCurMgr));
336
337 return m_pCurMgr;
338}
339
340#ifndef DACCESS_COMPILE
341//-----------------------------------------------------------
342//-----------------------------------------------------------
343StubManager::StubManager()
344 : m_pNextManager(NULL)
345{
346 LIMITED_METHOD_CONTRACT;
347}
348
349//-----------------------------------------------------------
350//-----------------------------------------------------------
351StubManager::~StubManager()
352{
353 CONTRACTL {
354 NOTHROW;
355 GC_NOTRIGGER;
356 CAN_TAKE_LOCK; // StubManager::UnlinkStubManager uses a crst
357 PRECONDITION(CheckPointer(this));
358 } CONTRACTL_END;
359
360 UnlinkStubManager(this);
361}
362#endif // #ifndef DACCESS_COMPILE
363
364#ifdef _DEBUG_IMPL
365//-----------------------------------------------------------
366// Verify that the stub is owned by the given stub manager
367// and no other stub manager. If a stub is claimed by multiple managers,
368// then the wrong manager may claim ownership and improperly trace the stub.
369//-----------------------------------------------------------
370BOOL StubManager::IsSingleOwner(PCODE stubAddress, StubManager * pOwner)
371{
372 STATIC_CONTRACT_NOTHROW;
373 STATIC_CONTRACT_GC_NOTRIGGER;
374 STATIC_CONTRACT_FORBID_FAULT;
375 STATIC_CONTRACT_CAN_TAKE_LOCK; // courtesy StubManagerIterator
376
377 // ensure this stubmanager owns it.
378 _ASSERTE(pOwner != NULL);
379
380 // ensure nobody else does.
381 bool ownerFound = false;
382 int count = 0;
383 StubManagerIterator it;
384 while (it.Next())
385 {
386 // Callers would have iterated till pOwner.
387 if (!ownerFound && it.Current() != pOwner)
388 continue;
389
390 if (it.Current() == pOwner)
391 ownerFound = true;
392
393 if (it.Current()->CheckIsStub_Worker(stubAddress))
394 {
395 // If you hit this assert, you can tell what 2 stub managers are conflicting by inspecting their vtable.
396 CONSISTENCY_CHECK_MSGF((it.Current() == pOwner), ("Stub at 0x%p is owner by multiple managers (0x%p, 0x%p)",
397 (void*) stubAddress, pOwner, it.Current()));
398 count++;
399 }
400 else
401 {
402 _ASSERTE(it.Current() != pOwner);
403 }
404 }
405
406 _ASSERTE(ownerFound);
407
408 // We expect pOwner to be the only one to own this stub.
409 return (count == 1);
410}
411#endif
412
413
414
415//-----------------------------------------------------------
416//-----------------------------------------------------------
417BOOL StubManager::CheckIsStub_Worker(PCODE stubStartAddress)
418{
419 CONTRACTL
420 {
421 NOTHROW;
422 CAN_TAKE_LOCK; // CheckIsStub_Internal can enter SimpleRWLock
423 GC_NOTRIGGER;
424 SO_TOLERANT;
425 }
426 CONTRACTL_END;
427
428 SUPPORTS_DAC;
429
430 // @todo - consider having a single check for null right up front.
431 // Though this may cover bugs where stub-managers don't handle bad addresses.
432 // And someone could just as easily pass (0x01) as NULL.
433 if (stubStartAddress == NULL)
434 {
435 return FALSE;
436 }
437
438 CONTRACT_VIOLATION(SOToleranceViolation);
439 // @todo : this might not have a thread
440 // BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return FALSE);
441
442 struct Param
443 {
444 BOOL fIsStub;
445 StubManager *pThis;
446 TADDR stubStartAddress;
447 } param;
448 param.fIsStub = FALSE;
449 param.pThis = this;
450 param.stubStartAddress = stubStartAddress;
451
452 // This may be called from DAC, and DAC + non-DAC have very different
453 // exception handling.
454#ifdef DACCESS_COMPILE
455 PAL_TRY(Param *, pParam, &param)
456#else
457 Param *pParam = &param;
458 EX_TRY
459#endif
460 {
461 SUPPORTS_DAC;
462
463#ifndef DACCESS_COMPILE
464 // Use CheckIsStub_Internal may AV. That's ok.
465 AVInRuntimeImplOkayHolder AVOkay;
466#endif
467
468 // Make a Polymorphic call to derived stub manager.
469 // Try to see if this address is for a stub. If the address is
470 // completely bogus, then this might fault, so we protect it
471 // with SEH.
472 pParam->fIsStub = pParam->pThis->CheckIsStub_Internal(pParam->stubStartAddress);
473 }
474#ifdef DACCESS_COMPILE
475 PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
476#else
477 EX_CATCH
478#endif
479 {
480 LOG((LF_CORDB, LL_INFO10000, "D::GASTSI: exception indicated addr is bad.\n"));
481
482 param.fIsStub = FALSE;
483 }
484#ifdef DACCESS_COMPILE
485 PAL_ENDTRY
486#else
487 EX_END_CATCH(SwallowAllExceptions);
488#endif
489
490 //END_SO_INTOLERANT_CODE;
491
492 return param.fIsStub;
493}
494
495//-----------------------------------------------------------
496// stubAddress may be an invalid address.
497//-----------------------------------------------------------
498PTR_StubManager StubManager::FindStubManager(PCODE stubAddress)
499{
500 CONTRACTL
501 {
502 NOTHROW;
503 GC_NOTRIGGER;
504 CAN_TAKE_LOCK; // courtesy StubManagerIterator
505 }
506 CONTRACTL_END;
507
508 SUPPORTS_DAC;
509
510 StubManagerIterator it;
511 while (it.Next())
512 {
513 if (it.Current()->CheckIsStub_Worker(stubAddress))
514 {
515 _ASSERTE_IMPL(IsSingleOwner(stubAddress, it.Current()));
516 return it.Current();
517 }
518 }
519
520 return NULL;
521}
522
523//-----------------------------------------------------------
524// Given an address, figure out a TraceDestination describing where
525// the instructions at that address will eventually transfer execution to.
526//-----------------------------------------------------------
527BOOL StubManager::TraceStub(PCODE stubStartAddress, TraceDestination *trace)
528{
529 WRAPPER_NO_CONTRACT;
530
531 StubManagerIterator it;
532 while (it.Next())
533 {
534 StubManager * pCurrent = it.Current();
535 if (pCurrent->CheckIsStub_Worker(stubStartAddress))
536 {
537 LOG((LF_CORDB, LL_INFO10000,
538 "StubManager::TraceStub: addr 0x%p claimed by mgr "
539 "0x%p.\n", stubStartAddress, pCurrent));
540
541 _ASSERTE_IMPL(IsSingleOwner(stubStartAddress, pCurrent));
542
543 BOOL fValid = pCurrent->DoTraceStub(stubStartAddress, trace);
544#ifdef _DEBUG
545 if (IsStubLoggingEnabled())
546 {
547 DbgWriteLog("Doing TraceStub for Address 0x%p, claimed by '%s' (0x%p)\n", stubStartAddress, pCurrent->DbgGetName(), pCurrent);
548 if (fValid)
549 {
550 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
551 FAULT_NOT_FATAL();
552 SString buffer;
553 DbgWriteLog(" td=%S\n", trace->DbgToString(buffer));
554 }
555 else
556 {
557 DbgWriteLog(" stubmanager returned false. Does not expect to call managed code\n");
558
559 }
560 } // logging
561#endif
562 return fValid;
563 }
564 }
565
566 if (ExecutionManager::IsManagedCode(stubStartAddress))
567 {
568 trace->InitForManaged(stubStartAddress);
569
570#ifdef _DEBUG
571 DbgWriteLog("Doing TraceStub for Address 0x%p is jitted code claimed by codemanager\n", stubStartAddress);
572#endif
573
574 LOG((LF_CORDB, LL_INFO10000,
575 "StubManager::TraceStub: addr 0x%p is managed code\n",
576 stubStartAddress));
577
578 return TRUE;
579 }
580
581 LOG((LF_CORDB, LL_INFO10000,
582 "StubManager::TraceStub: addr 0x%p unknown. TRACE_OTHER...\n",
583 stubStartAddress));
584
585#ifdef _DEBUG
586 DbgWriteLog("Doing TraceStub for Address 0x%p is unknown!!!\n", stubStartAddress);
587#endif
588
589 trace->InitForOther(stubStartAddress);
590 return FALSE;
591}
592
593//-----------------------------------------------------------
594//-----------------------------------------------------------
595BOOL StubManager::FollowTrace(TraceDestination *trace)
596{
597 STATIC_CONTRACT_NOTHROW;
598 STATIC_CONTRACT_GC_NOTRIGGER;
599 STATIC_CONTRACT_FORBID_FAULT;
600
601 while (trace->GetTraceType() == TRACE_STUB)
602 {
603 LOG((LF_CORDB, LL_INFO10000,
604 "StubManager::FollowTrace: TRACE_STUB for 0x%p\n",
605 trace->GetAddress()));
606
607 if (!TraceStub(trace->GetAddress(), trace))
608 {
609 //
610 // No stub manager claimed it - it must be an EE helper or something.
611 //
612
613 trace->InitForOther(trace->GetAddress());
614 }
615 }
616
617 LOG_TRACE_DESTINATION(trace, NULL, "StubManager::FollowTrace");
618
619 return trace->GetTraceType() != TRACE_OTHER;
620}
621
622#ifndef DACCESS_COMPILE
623
624//-----------------------------------------------------------
625//-----------------------------------------------------------
626void StubManager::AddStubManager(StubManager *mgr)
627{
628 WRAPPER_NO_CONTRACT;
629 CONSISTENCY_CHECK(CheckPointer(g_pFirstManager, NULL_OK));
630 CONSISTENCY_CHECK(CheckPointer(mgr));
631
632 GCX_COOP_NO_THREAD_BROKEN();
633
634 CrstHolder ch(&s_StubManagerListCrst);
635
636 if (g_pFirstManager == NULL)
637 {
638 g_pFirstManager = mgr;
639 }
640 else
641 {
642 mgr->m_pNextManager = g_pFirstManager;
643 g_pFirstManager = mgr;
644 }
645
646 LOG((LF_CORDB, LL_EVERYTHING, "StubManager::AddStubManager - 0x%p (vptr %x%p)\n", mgr, (*(PVOID*)mgr)));
647}
648
649//-----------------------------------------------------------
650// NOTE: The runtime MUST be suspended to use this in a
651// truly safe manner.
652//-----------------------------------------------------------
653void StubManager::UnlinkStubManager(StubManager *mgr)
654{
655 STATIC_CONTRACT_GC_NOTRIGGER;
656 STATIC_CONTRACT_NOTHROW;
657 STATIC_CONTRACT_CAN_TAKE_LOCK;
658 CONSISTENCY_CHECK(CheckPointer(g_pFirstManager, NULL_OK));
659 CONSISTENCY_CHECK(CheckPointer(mgr));
660
661 CrstHolder ch(&s_StubManagerListCrst);
662
663 StubManager **m = &g_pFirstManager;
664 while (*m != NULL)
665 {
666 if (*m == mgr)
667 {
668 *m = (*m)->m_pNextManager;
669 return;
670 }
671 m = &(*m)->m_pNextManager;
672 }
673}
674
675#endif // #ifndef DACCESS_COMPILE
676
677#ifdef DACCESS_COMPILE
678
679//-----------------------------------------------------------
680//-----------------------------------------------------------
681void
682StubManager::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
683{
684 SUPPORTS_DAC;
685 // Report the global list head.
686 DacEnumMemoryRegion(DacGlobalBase() +
687 g_dacGlobals.StubManager__g_pFirstManager,
688 sizeof(TADDR));
689
690 //
691 // Report the list contents.
692 //
693
694 StubManagerIterator it;
695 while (it.Next())
696 {
697 it.Current()->DoEnumMemoryRegions(flags);
698 }
699}
700
701//-----------------------------------------------------------
702//-----------------------------------------------------------
703void
704StubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
705{
706 SUPPORTS_DAC;
707 DAC_ENUM_VTHIS();
708 EMEM_OUT(("MEM: %p StubManager base\n", dac_cast<TADDR>(this)));
709}
710
711#endif // #ifdef DACCESS_COMPILE
712
713//-----------------------------------------------------------
714// Initialize the global stub manager service.
715//-----------------------------------------------------------
716void StubManager::InitializeStubManagers()
717{
718#if !defined(DACCESS_COMPILE)
719
720#if defined(_DEBUG)
721 s_DbgLogCrst.Init(CrstDebuggerHeapLock, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
722#endif
723 s_StubManagerListCrst.Init(CrstDebuggerHeapLock, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
724
725#endif // !DACCESS_COMPILE
726}
727
728//-----------------------------------------------------------
729// Terminate the global stub manager service.
730//-----------------------------------------------------------
731void StubManager::TerminateStubManagers()
732{
733#if !defined(DACCESS_COMPILE)
734
735#if defined(_DEBUG)
736 DbgFinishLog();
737 s_DbgLogCrst.Destroy();
738#endif
739
740 s_StubManagerListCrst.Destroy();
741#endif // !DACCESS_COMPILE
742}
743
744#ifdef _DEBUG
745
746//-----------------------------------------------------------
747// Should stub-manager logging be enabled?
748//-----------------------------------------------------------
749bool StubManager::IsStubLoggingEnabled()
750{
751 // Our current logging impl uses SString, which uses new(), which can't be called
752 // on the helper thread. (B/c it may deadlock. See SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE)
753
754 // We avoid this by just not logging when native-debugging.
755 if (IsDebuggerPresent())
756 {
757 return false;
758 }
759
760 return true;
761}
762
763
764//-----------------------------------------------------------
765// Call to reset the log. This is used at the start of a new step-operation.
766// pThread is the managed thread doing the stepping.
767// It should either be the current thread or the helper thread.
768//-----------------------------------------------------------
769void StubManager::DbgBeginLog(TADDR addrCallInstruction, TADDR addrCallTarget)
770{
771#ifndef DACCESS_COMPILE
772 CONTRACTL
773 {
774 NOTHROW;
775 GC_NOTRIGGER;
776 MODE_ANY;
777 }
778 CONTRACTL_END;
779
780
781 // We can't call new() if another thread holds the heap lock and is then suspended by
782 // an interop-debugging. Since this is debug-only logging code, we'll just skip
783 // it under those cases.
784 if (!IsStubLoggingEnabled())
785 {
786 return;
787 }
788 // Now that we know we're not interop-debugging, we can safely call new.
789 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
790 FAULT_NOT_FATAL();
791
792 {
793 CrstHolder ch(&s_DbgLogCrst);
794 EX_TRY
795 {
796 if (s_pDbgStubManagerLog == NULL)
797 {
798 s_pDbgStubManagerLog = new SString();
799 }
800 s_pDbgStubManagerLog->Clear();
801 }
802 EX_CATCH
803 {
804 DbgFinishLog();
805 }
806 EX_END_CATCH(SwallowAllExceptions);
807 }
808
809 DbgWriteLog("Beginning Step-in. IP after Call instruction is at 0x%p, call target is at 0x%p\n",
810 addrCallInstruction, addrCallTarget);
811#endif
812}
813
814//-----------------------------------------------------------
815// Finish logging for this thread.
816// pThread is the managed thread doing the stepping.
817// It should either be the current thread or the helper thread.
818//-----------------------------------------------------------
819void StubManager::DbgFinishLog()
820{
821#ifndef DACCESS_COMPILE
822 CONTRACTL
823 {
824 NOTHROW;
825 GC_NOTRIGGER;
826 MODE_ANY;
827 }
828 CONTRACTL_END;
829
830 CrstHolder ch(&s_DbgLogCrst);
831
832 // Since this is just a tool for debugging, we don't care if we call new.
833 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
834 FAULT_NOT_FATAL();
835
836 delete s_pDbgStubManagerLog;
837 s_pDbgStubManagerLog = NULL;
838
839
840#endif
841}
842
843
844//-----------------------------------------------------------
845// Write an arbitrary string to the log.
846//-----------------------------------------------------------
847void StubManager::DbgWriteLog(const CHAR *format, ...)
848{
849#ifndef DACCESS_COMPILE
850 CONTRACTL
851 {
852 NOTHROW;
853 GC_NOTRIGGER;
854 MODE_ANY;
855 }
856 CONTRACTL_END;
857
858
859 if (!IsStubLoggingEnabled())
860 {
861 return;
862 }
863
864 // Since this is just a tool for debugging, we don't care if we call new.
865 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
866 FAULT_NOT_FATAL();
867
868 CrstHolder ch(&s_DbgLogCrst);
869
870 if (s_pDbgStubManagerLog == NULL)
871 {
872 return;
873 }
874
875 // Suppress asserts about lossy encoding conversion in SString::Printf
876 CHECK chk;
877 BOOL fEntered = chk.EnterAssert();
878
879 EX_TRY
880 {
881 va_list args;
882 va_start(args, format);
883 s_pDbgStubManagerLog->AppendVPrintf(format, args);
884 va_end(args);
885 }
886 EX_CATCH
887 {
888 }
889 EX_END_CATCH(SwallowAllExceptions);
890
891 if (fEntered) chk.LeaveAssert();
892#endif
893}
894
895
896
897//-----------------------------------------------------------
898// Get the log as a string.
899//-----------------------------------------------------------
900void StubManager::DbgGetLog(SString * pStringOut)
901{
902#ifndef DACCESS_COMPILE
903 CONTRACTL
904 {
905 NOTHROW;
906 GC_NOTRIGGER;
907 MODE_ANY;
908
909 PRECONDITION(CheckPointer(pStringOut));
910 }
911 CONTRACTL_END;
912
913 if (!IsStubLoggingEnabled())
914 {
915 return;
916 }
917
918 // Since this is just a tool for debugging, we don't care if we call new.
919 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
920 FAULT_NOT_FATAL();
921
922 CrstHolder ch(&s_DbgLogCrst);
923
924 if (s_pDbgStubManagerLog == NULL)
925 {
926 return;
927 }
928
929 EX_TRY
930 {
931 pStringOut->Set(*s_pDbgStubManagerLog);
932 }
933 EX_CATCH
934 {
935 }
936 EX_END_CATCH(SwallowAllExceptions);
937#endif
938}
939
940
941#endif // _DEBUG
942
943extern "C" void STDCALL ThePreStubPatchLabel(void);
944
945//-----------------------------------------------------------
946//-----------------------------------------------------------
947BOOL ThePreStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace)
948{
949 CONTRACTL
950 {
951 NOTHROW;
952 GC_NOTRIGGER;
953 MODE_ANY;
954
955 PRECONDITION(stubStartAddress != NULL);
956 PRECONDITION(CheckPointer(trace));
957 }
958 CONTRACTL_END;
959
960 //
961 // We cannot tell where the stub will end up
962 // until after the prestub worker has been run.
963 //
964
965 trace->InitForFramePush(GetEEFuncEntryPoint(ThePreStubPatchLabel));
966
967 return TRUE;
968}
969
970//-----------------------------------------------------------
971BOOL ThePreStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
972{
973 LIMITED_METHOD_DAC_CONTRACT;
974 return stubStartAddress == GetPreStubEntryPoint();
975
976}
977
978
979// -------------------------------------------------------
980// Stub manager functions & globals
981// -------------------------------------------------------
982
983SPTR_IMPL(PrecodeStubManager, PrecodeStubManager, g_pManager);
984
985#ifndef DACCESS_COMPILE
986
987/* static */
988void PrecodeStubManager::Init()
989{
990 CONTRACTL
991 {
992 THROWS;
993 GC_NOTRIGGER;
994 MODE_ANY;
995 }
996 CONTRACTL_END
997
998 g_pManager = new PrecodeStubManager();
999 StubManager::AddStubManager(g_pManager);
1000}
1001
1002#endif // #ifndef DACCESS_COMPILE
1003
1004/* static */
1005BOOL PrecodeStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1006{
1007 CONTRACTL
1008 {
1009 THROWS; // address may be bad, so we may AV.
1010 GC_NOTRIGGER;
1011 SUPPORTS_DAC;
1012 }
1013 CONTRACTL_END;
1014
1015 // Forwarded to from RangeSectionStubManager
1016 return FALSE;
1017}
1018
1019BOOL PrecodeStubManager::DoTraceStub(PCODE stubStartAddress,
1020 TraceDestination *trace)
1021{
1022 CONTRACTL
1023 {
1024 INSTANCE_CHECK;
1025 NOTHROW;
1026 GC_NOTRIGGER;
1027 MODE_ANY;
1028 FORBID_FAULT;
1029 }
1030 CONTRACTL_END
1031
1032 LOG((LF_CORDB, LL_EVERYTHING, "PrecodeStubManager::DoTraceStub called\n"));
1033
1034 MethodDesc* pMD = NULL;
1035
1036#ifdef HAS_COMPACT_ENTRYPOINTS
1037 if (MethodDescChunk::IsCompactEntryPointAtAddress(stubStartAddress))
1038 {
1039 pMD = MethodDescChunk::GetMethodDescFromCompactEntryPoint(stubStartAddress);
1040 }
1041 else
1042#endif // HAS_COMPACT_ENTRYPOINTS
1043 {
1044 Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(stubStartAddress);
1045 PREFIX_ASSUME(pPrecode != NULL);
1046
1047 switch (pPrecode->GetType())
1048 {
1049 case PRECODE_STUB:
1050 break;
1051
1052#ifdef HAS_NDIRECT_IMPORT_PRECODE
1053 case PRECODE_NDIRECT_IMPORT:
1054#ifndef DACCESS_COMPILE
1055 trace->InitForUnmanaged(GetEEFuncEntryPoint(NDirectImportThunk));
1056#else
1057 trace->InitForOther(NULL);
1058#endif
1059 LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - NDirect import");
1060 return TRUE;
1061#endif // HAS_NDIRECT_IMPORT_PRECODE
1062
1063#ifdef HAS_FIXUP_PRECODE
1064 case PRECODE_FIXUP:
1065 break;
1066#endif // HAS_FIXUP_PRECODE
1067
1068#ifdef HAS_THISPTR_RETBUF_PRECODE
1069 case PRECODE_THISPTR_RETBUF:
1070 break;
1071#endif // HAS_THISPTR_RETBUF_PRECODE
1072
1073 default:
1074 _ASSERTE_IMPL(!"DoTraceStub: Unexpected precode type");
1075 break;
1076 }
1077
1078 PCODE target = pPrecode->GetTarget();
1079
1080 // check if the method has been jitted
1081 if (!pPrecode->IsPointingToPrestub(target))
1082 {
1083 trace->InitForStub(target);
1084 LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - code");
1085 return TRUE;
1086 }
1087
1088 pMD = pPrecode->GetMethodDesc();
1089 }
1090
1091 PREFIX_ASSUME(pMD != NULL);
1092
1093 // If the method is not IL, then we patch the prestub because no one will ever change the call here at the
1094 // MethodDesc. If, however, this is an IL method, then we are at risk to have another thread backpatch the call
1095 // here, so we'd miss if we patched the prestub. Therefore, we go right to the IL method and patch IL offset 0
1096 // by using TRACE_UNJITTED_METHOD.
1097 if (!pMD->IsIL())
1098 {
1099 trace->InitForStub(GetPreStubEntryPoint());
1100 }
1101 else
1102 {
1103 trace->InitForUnjittedMethod(pMD);
1104 }
1105
1106 LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - prestub");
1107 return TRUE;
1108}
1109
1110#ifndef DACCESS_COMPILE
1111BOOL PrecodeStubManager::TraceManager(Thread *thread,
1112 TraceDestination *trace,
1113 T_CONTEXT *pContext,
1114 BYTE **pRetAddr)
1115{
1116 CONTRACTL
1117 {
1118 NOTHROW;
1119 GC_NOTRIGGER;
1120 MODE_ANY;
1121 PRECONDITION(CheckPointer(thread, NULL_OK));
1122 PRECONDITION(CheckPointer(trace));
1123 PRECONDITION(CheckPointer(pContext));
1124 PRECONDITION(CheckPointer(pRetAddr));
1125 }
1126 CONTRACTL_END;
1127
1128 _ASSERTE(!"Unexpected call to PrecodeStubManager::TraceManager");
1129 return FALSE;
1130}
1131#endif
1132
1133// -------------------------------------------------------
1134// StubLinkStubManager
1135// -------------------------------------------------------
1136
1137SPTR_IMPL(StubLinkStubManager, StubLinkStubManager, g_pManager);
1138
1139#ifndef DACCESS_COMPILE
1140
1141/* static */
1142void StubLinkStubManager::Init()
1143{
1144 CONTRACTL
1145 {
1146 THROWS;
1147 GC_NOTRIGGER;
1148 MODE_ANY;
1149 }
1150 CONTRACTL_END
1151
1152 g_pManager = new StubLinkStubManager();
1153 StubManager::AddStubManager(g_pManager);
1154}
1155
1156#endif // #ifndef DACCESS_COMPILE
1157
1158BOOL StubLinkStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1159{
1160 WRAPPER_NO_CONTRACT;
1161 SUPPORTS_DAC;
1162
1163 return GetRangeList()->IsInRange(stubStartAddress);
1164}
1165
1166
1167BOOL StubLinkStubManager::DoTraceStub(PCODE stubStartAddress,
1168 TraceDestination *trace)
1169{
1170 CONTRACTL
1171 {
1172 INSTANCE_CHECK;
1173 NOTHROW;
1174 GC_NOTRIGGER;
1175 MODE_ANY;
1176 }
1177 CONTRACTL_END
1178
1179 LOG((LF_CORDB, LL_INFO10000,
1180 "StubLinkStubManager::DoTraceStub: stubStartAddress=0x%08x\n",
1181 stubStartAddress));
1182
1183 Stub *stub = Stub::RecoverStub(stubStartAddress);
1184
1185 LOG((LF_CORDB, LL_INFO10000,
1186 "StubLinkStubManager::DoTraceStub: stub=0x%08x\n", stub));
1187
1188 //
1189 // If this is an intercept stub, we may be able to step
1190 // into the intercepted stub.
1191 //
1192 // <TODO>!!! Note that this case should not be necessary, it's just
1193 // here until I get all of the patch offsets & frame patch
1194 // methods in place.</TODO>
1195 //
1196 TADDR pRealAddr = 0;
1197 if (stub->IsIntercept())
1198 {
1199 InterceptStub *is = dac_cast<PTR_InterceptStub>(stub);
1200
1201 if (*is->GetInterceptedStub() == NULL)
1202 {
1203 pRealAddr = *is->GetRealAddr();
1204 LOG((LF_CORDB, LL_INFO10000, "StubLinkStubManager::DoTraceStub"
1205 " Intercept stub, no following stub, real addr:0x%x\n",
1206 pRealAddr));
1207 }
1208 else
1209 {
1210 stub = *is->GetInterceptedStub();
1211
1212 pRealAddr = stub->GetEntryPoint();
1213
1214 LOG((LF_CORDB, LL_INFO10000,
1215 "StubLinkStubManager::DoTraceStub: intercepted "
1216 "stub=0x%08x, ep=0x%08x\n",
1217 stub, stub->GetEntryPoint()));
1218 }
1219 _ASSERTE( pRealAddr );
1220
1221 // !!! will push a frame???
1222 return TraceStub(pRealAddr, trace);
1223 }
1224 else if (stub->IsMulticastDelegate())
1225 {
1226 LOG((LF_CORDB, LL_INFO10000,
1227 "StubLinkStubManager(MCDel)::DoTraceStub: stubStartAddress=0x%08x\n",
1228 stubStartAddress));
1229
1230 LOG((LF_CORDB, LL_INFO10000,
1231 "StubLinkStubManager(MCDel)::DoTraceStub: stub=0x%08x MGR_PUSH to entrypoint:0x%x\n", stub,
1232 stub->GetEntryPoint()));
1233
1234 // If it's a MC delegate, then we want to set a BP & do a context-ful
1235 // manager push, so that we can figure out if this call will be to a
1236 // single multicast delegate or a multi multicast delegate
1237 trace->InitForManagerPush(stubStartAddress, this);
1238
1239 return TRUE;
1240 }
1241 else if (stub->GetPatchOffset() == 0)
1242 {
1243 LOG((LF_CORDB, LL_INFO10000, "StubLinkStubManager::DoTraceStub: patch offset is 0!\n"));
1244
1245 return FALSE;
1246 }
1247 else
1248 {
1249 trace->InitForFramePush((PCODE)stub->GetPatchAddress());
1250
1251 LOG_TRACE_DESTINATION(trace, stubStartAddress, "StubLinkStubManager::DoTraceStub");
1252
1253 return TRUE;
1254 }
1255}
1256
1257#ifndef DACCESS_COMPILE
1258
1259BOOL StubLinkStubManager::TraceManager(Thread *thread,
1260 TraceDestination *trace,
1261 T_CONTEXT *pContext,
1262 BYTE **pRetAddr)
1263{
1264 CONTRACTL
1265 {
1266 INSTANCE_CHECK;
1267 THROWS;
1268 GC_TRIGGERS;
1269 MODE_ANY;
1270 INJECT_FAULT(return FALSE;);
1271 }
1272 CONTRACTL_END
1273
1274 // NOTE that we're assuming that this will be called if and ONLY if
1275 // we're examing a multicast delegate stub. Otherwise, we'll have to figure out
1276 // what we're looking iat
1277
1278 BYTE *pbDel = 0;
1279
1280 LPVOID pc = (LPVOID)GetIP(pContext);
1281
1282 *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext);
1283
1284 pbDel = (BYTE *)StubManagerHelpers::GetThisPtr(pContext);
1285
1286 LOG((LF_CORDB,LL_INFO10000, "SLSM:TM at 0x%x, retAddr is 0x%x\n", pc, (*pRetAddr)));
1287
1288 return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace);
1289}
1290
1291#endif // #ifndef DACCESS_COMPILE
1292
1293// -------------------------------------------------------
1294// Stub manager for thunks.
1295//
1296// Note, the only reason we have this stub manager is so that we can recgonize UMEntryThunks for IsTransitionStub. If it
1297// turns out that having a full-blown stub manager for these things causes problems else where, then we can just attach
1298// a range list to the thunk heap and have IsTransitionStub check that after checking with the main stub manager.
1299// -------------------------------------------------------
1300
1301SPTR_IMPL(ThunkHeapStubManager, ThunkHeapStubManager, g_pManager);
1302
1303#ifndef DACCESS_COMPILE
1304
1305/* static */
1306void ThunkHeapStubManager::Init()
1307{
1308 CONTRACTL
1309 {
1310 THROWS;
1311 GC_NOTRIGGER;
1312 MODE_ANY;
1313 INJECT_FAULT(COMPlusThrowOM());
1314 }
1315 CONTRACTL_END;
1316
1317 g_pManager = new ThunkHeapStubManager();
1318 StubManager::AddStubManager(g_pManager);
1319}
1320
1321#endif // !DACCESS_COMPILE
1322
1323BOOL ThunkHeapStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1324{
1325 WRAPPER_NO_CONTRACT;
1326 SUPPORTS_DAC;
1327
1328 // Its a stub if its in our heaps range.
1329 return GetRangeList()->IsInRange(stubStartAddress);
1330}
1331
1332BOOL ThunkHeapStubManager::DoTraceStub(PCODE stubStartAddress,
1333 TraceDestination *trace)
1334{
1335 LIMITED_METHOD_CONTRACT;
1336 // We never trace through these stubs when stepping through managed code. The only reason we have this stub manager
1337 // is so that IsTransitionStub can recgonize UMEntryThunks.
1338 return FALSE;
1339}
1340
1341// -------------------------------------------------------
1342// JumpStub stubs
1343//
1344// Stub manager for jump stubs created by ExecutionManager::jumpStub()
1345// These are currently used only on the 64-bit targets IA64 and AMD64
1346//
1347// -------------------------------------------------------
1348
1349SPTR_IMPL(JumpStubStubManager, JumpStubStubManager, g_pManager);
1350
1351#ifndef DACCESS_COMPILE
1352/* static */
1353void JumpStubStubManager::Init()
1354{
1355 CONTRACTL
1356 {
1357 THROWS;
1358 GC_NOTRIGGER;
1359 MODE_ANY;
1360 }
1361 CONTRACTL_END
1362
1363 g_pManager = new JumpStubStubManager();
1364 StubManager::AddStubManager(g_pManager);
1365}
1366#endif // #ifndef DACCESS_COMPILE
1367
1368BOOL JumpStubStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1369{
1370 WRAPPER_NO_CONTRACT;
1371 SUPPORTS_DAC;
1372
1373 // Forwarded to from RangeSectionStubManager
1374 return FALSE;
1375}
1376
1377BOOL JumpStubStubManager::DoTraceStub(PCODE stubStartAddress,
1378 TraceDestination *trace)
1379{
1380 LIMITED_METHOD_CONTRACT;
1381
1382 PCODE jumpTarget = decodeBackToBackJump(stubStartAddress);
1383 trace->InitForStub(jumpTarget);
1384
1385 LOG_TRACE_DESTINATION(trace, stubStartAddress, "JumpStubStubManager::DoTraceStub");
1386
1387 return TRUE;
1388}
1389
1390//
1391// Stub manager for code sections. It forwards the query to the more appropriate
1392// stub manager, or handles the query itself.
1393//
1394
1395SPTR_IMPL(RangeSectionStubManager, RangeSectionStubManager, g_pManager);
1396
1397#ifndef DACCESS_COMPILE
1398/* static */
1399void RangeSectionStubManager::Init()
1400{
1401 CONTRACTL
1402 {
1403 THROWS;
1404 GC_NOTRIGGER;
1405 MODE_ANY;
1406 }
1407 CONTRACTL_END
1408
1409 g_pManager = new RangeSectionStubManager();
1410 StubManager::AddStubManager(g_pManager);
1411}
1412#endif // #ifndef DACCESS_COMPILE
1413
1414BOOL RangeSectionStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1415{
1416 WRAPPER_NO_CONTRACT;
1417 SUPPORTS_DAC;
1418
1419 switch (GetStubKind(stubStartAddress))
1420 {
1421 case STUB_CODE_BLOCK_PRECODE:
1422 case STUB_CODE_BLOCK_JUMPSTUB:
1423 case STUB_CODE_BLOCK_STUBLINK:
1424 case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK:
1425 case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK:
1426 case STUB_CODE_BLOCK_METHOD_CALL_THUNK:
1427 return TRUE;
1428 default:
1429 break;
1430 }
1431
1432 return FALSE;
1433}
1434
1435BOOL RangeSectionStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace)
1436{
1437 CONTRACTL
1438 {
1439 INSTANCE_CHECK;
1440 NOTHROW;
1441 GC_NOTRIGGER;
1442 MODE_ANY;
1443 FORBID_FAULT;
1444 }
1445 CONTRACTL_END
1446
1447 switch (GetStubKind(stubStartAddress))
1448 {
1449 case STUB_CODE_BLOCK_PRECODE:
1450 return PrecodeStubManager::g_pManager->DoTraceStub(stubStartAddress, trace);
1451
1452 case STUB_CODE_BLOCK_JUMPSTUB:
1453 return JumpStubStubManager::g_pManager->DoTraceStub(stubStartAddress, trace);
1454
1455 case STUB_CODE_BLOCK_STUBLINK:
1456 return StubLinkStubManager::g_pManager->DoTraceStub(stubStartAddress, trace);
1457
1458#ifdef FEATURE_PREJIT
1459 case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK:
1460 {
1461 PCODE pTarget = GetMethodThunkTarget(stubStartAddress);
1462 if (pTarget == ExecutionManager::FindZapModule(stubStartAddress)->
1463 GetNGenLayoutInfo()->m_pVirtualImportFixupJumpStub)
1464 {
1465#ifdef DACCESS_COMPILE
1466 DacNotImpl();
1467#else
1468 trace->InitForManagerPush(GetEEFuncEntryPoint(VirtualMethodFixupPatchLabel), this);
1469#endif
1470 }
1471 else
1472 {
1473 trace->InitForStub(pTarget);
1474 }
1475 return TRUE;
1476 }
1477#endif
1478
1479 case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK:
1480 {
1481 PCODE pTarget = GetMethodThunkTarget(stubStartAddress);
1482 if (pTarget != ExecutionManager::FindZapModule(stubStartAddress)->
1483 GetNGenLayoutInfo()->m_pExternalMethodFixupJumpStub)
1484 {
1485 trace->InitForStub(pTarget);
1486 return TRUE;
1487 }
1488 }
1489
1490 __fallthrough;
1491
1492 case STUB_CODE_BLOCK_METHOD_CALL_THUNK:
1493#ifdef DACCESS_COMPILE
1494 DacNotImpl();
1495#else
1496 trace->InitForManagerPush(GetEEFuncEntryPoint(ExternalMethodFixupPatchLabel), this);
1497#endif
1498 return TRUE;
1499
1500 default:
1501 break;
1502 }
1503
1504 return FALSE;
1505}
1506
1507#ifndef DACCESS_COMPILE
1508BOOL RangeSectionStubManager::TraceManager(Thread *thread,
1509 TraceDestination *trace,
1510 CONTEXT *pContext,
1511 BYTE **pRetAddr)
1512{
1513 CONTRACTL
1514 {
1515 NOTHROW;
1516 GC_NOTRIGGER;
1517 MODE_ANY;
1518 }
1519 CONTRACTL_END;
1520
1521 // Both virtual and external import thunks have the same structure. We can use
1522 // common code to handle them.
1523 _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(VirtualMethodFixupPatchLabel)
1524 || GetIP(pContext) == GetEEFuncEntryPoint(ExternalMethodFixupPatchLabel));
1525
1526 *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext);
1527
1528 PCODE target = StubManagerHelpers::GetTailCallTarget(pContext);
1529 trace->InitForStub(target);
1530 return TRUE;
1531}
1532#endif
1533
1534PCODE RangeSectionStubManager::GetMethodThunkTarget(PCODE stubStartAddress)
1535{
1536 WRAPPER_NO_CONTRACT;
1537
1538#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
1539 return rel32Decode(stubStartAddress+1);
1540#elif defined(_TARGET_ARM_)
1541 TADDR pInstr = PCODEToPINSTR(stubStartAddress);
1542 return *dac_cast<PTR_PCODE>(pInstr + 2 * sizeof(DWORD));
1543#else
1544 PORTABILITY_ASSERT("RangeSectionStubManager::GetMethodThunkTarget");
1545 return NULL;
1546#endif
1547}
1548
1549#ifdef DACCESS_COMPILE
1550LPCWSTR RangeSectionStubManager::GetStubManagerName(PCODE addr)
1551{
1552 WRAPPER_NO_CONTRACT;
1553
1554 switch (GetStubKind(addr))
1555 {
1556 case STUB_CODE_BLOCK_PRECODE:
1557 return W("MethodDescPrestub");
1558
1559 case STUB_CODE_BLOCK_JUMPSTUB:
1560 return W("JumpStub");
1561
1562 case STUB_CODE_BLOCK_STUBLINK:
1563 return W("StubLinkStub");
1564
1565 case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK:
1566 return W("VirtualMethodThunk");
1567
1568 case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK:
1569 return W("ExternalMethodThunk");
1570
1571 case STUB_CODE_BLOCK_METHOD_CALL_THUNK:
1572 return W("MethodCallThunk");
1573
1574 default:
1575 break;
1576 }
1577
1578 return W("UnknownRangeSectionStub");
1579}
1580#endif // DACCESS_COMPILE
1581
1582StubCodeBlockKind
1583RangeSectionStubManager::GetStubKind(PCODE stubStartAddress)
1584{
1585 CONTRACTL
1586 {
1587 NOTHROW;
1588 GC_NOTRIGGER;
1589 SO_TOLERANT;
1590 MODE_ANY;
1591 }
1592 CONTRACTL_END;
1593
1594 RangeSection * pRS = ExecutionManager::FindCodeRange(stubStartAddress, ExecutionManager::ScanReaderLock);
1595 if (pRS == NULL)
1596 return STUB_CODE_BLOCK_UNKNOWN;
1597
1598 return pRS->pjit->GetStubCodeBlockKind(pRS, stubStartAddress);
1599}
1600
1601//
1602// This is the stub manager for IL stubs.
1603//
1604
1605#ifndef DACCESS_COMPILE
1606
1607/* static */
1608void ILStubManager::Init()
1609{
1610 CONTRACTL
1611 {
1612 THROWS;
1613 GC_NOTRIGGER;
1614 MODE_ANY;
1615 }
1616 CONTRACTL_END
1617
1618 StubManager::AddStubManager(new ILStubManager());
1619}
1620
1621#endif // #ifndef DACCESS_COMPILE
1622
1623BOOL ILStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1624{
1625 WRAPPER_NO_CONTRACT;
1626 SUPPORTS_DAC;
1627
1628 MethodDesc *pMD = ExecutionManager::GetCodeMethodDesc(stubStartAddress);
1629
1630 return (pMD != NULL) && pMD->IsILStub();
1631}
1632
1633BOOL ILStubManager::DoTraceStub(PCODE stubStartAddress,
1634 TraceDestination *trace)
1635{
1636 LIMITED_METHOD_CONTRACT;
1637
1638 LOG((LF_CORDB, LL_EVERYTHING, "ILStubManager::DoTraceStub called\n"));
1639
1640#ifndef DACCESS_COMPILE
1641
1642 PCODE traceDestination = NULL;
1643
1644#ifdef FEATURE_MULTICASTSTUB_AS_IL
1645 MethodDesc* pStubMD = ExecutionManager::GetCodeMethodDesc(stubStartAddress);
1646 if (pStubMD != NULL && pStubMD->AsDynamicMethodDesc()->IsMulticastStub())
1647 {
1648 traceDestination = GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper);
1649 }
1650 else
1651#endif // FEATURE_MULTICASTSTUB_AS_IL
1652 {
1653 // This call is going out to unmanaged code, either through pinvoke or COM interop.
1654 traceDestination = stubStartAddress;
1655 }
1656
1657 trace->InitForManagerPush(traceDestination, this);
1658 LOG_TRACE_DESTINATION(trace, traceDestination, "ILStubManager::DoTraceStub");
1659
1660 return TRUE;
1661
1662#else // !DACCESS_COMPILE
1663 trace->InitForOther(NULL);
1664 return FALSE;
1665
1666#endif // !DACCESS_COMPILE
1667}
1668
1669#ifndef DACCESS_COMPILE
1670#ifdef FEATURE_COMINTEROP
1671PCODE ILStubManager::GetCOMTarget(Object *pThis, ComPlusCallInfo *pComPlusCallInfo)
1672{
1673 CONTRACTL
1674 {
1675 THROWS;
1676 GC_TRIGGERS;
1677 MODE_COOPERATIVE;
1678 }
1679 CONTRACTL_END;
1680
1681 // calculate the target interface pointer
1682 SafeComHolder<IUnknown> pUnk;
1683
1684 OBJECTREF oref = ObjectToOBJECTREF(pThis);
1685 GCPROTECT_BEGIN(oref);
1686 pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pComPlusCallInfo->m_pInterfaceMT);
1687 GCPROTECT_END();
1688
1689 LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk;
1690
1691 PCODE target = (PCODE)lpVtbl[pComPlusCallInfo->m_cachedComSlot];
1692 return target;
1693}
1694
1695// This function should return the same result as StubHelpers::GetWinRTFactoryObject followed by
1696// ILStubManager::GetCOMTarget. The difference is that it does not allocate managed memory, so it
1697// does not trigger GC.
1698//
1699// The reason why GC (and potentially a stack walk for other purposes, such as exception handling)
1700// would be problematic is that we are stopped at the first instruction of an IL stub which is
1701// not a GC-safe point. Technically, the function still has the GC_TRIGGERS contract but we should
1702// not see GC in practice here without allocating.
1703//
1704// Note that the GC suspension logic detects the debugger-is-attached-at-GC-unsafe-point case and
1705// will back off and retry. This means that it suffices to ensure that this thread does not trigger
1706// GC, allocations on other threads will wait and not cause major trouble.
1707PCODE ILStubManager::GetWinRTFactoryTarget(ComPlusCallMethodDesc *pCMD)
1708{
1709 CONTRACTL
1710 {
1711 THROWS;
1712 GC_TRIGGERS;
1713 MODE_COOPERATIVE;
1714 }
1715 CONTRACTL_END;
1716
1717 MethodTable *pMT = pCMD->GetMethodTable();
1718
1719 // GetComClassFactory could load types and trigger GC, get class name manually
1720 InlineSString<DEFAULT_NONSTACK_CLASSNAME_SIZE> ssClassName;
1721 pMT->_GetFullyQualifiedNameForClass(ssClassName);
1722
1723 IID iid;
1724 pCMD->m_pComPlusCallInfo->m_pInterfaceMT->GetGuid(&iid, FALSE, FALSE);
1725
1726 SafeComHolder<IInspectable> pFactory;
1727 {
1728 GCX_PREEMP();
1729 if (SUCCEEDED(RoGetActivationFactory(WinRtStringRef(ssClassName.GetUnicode(), ssClassName.GetCount()), iid, &pFactory)))
1730 {
1731 LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pFactory;
1732 return (PCODE)lpVtbl[pCMD->m_pComPlusCallInfo->m_cachedComSlot];
1733 }
1734 }
1735
1736 return NULL;
1737}
1738#endif // FEATURE_COMINTEROP
1739
1740#ifndef CROSSGEN_COMPILE
1741BOOL ILStubManager::TraceManager(Thread *thread,
1742 TraceDestination *trace,
1743 T_CONTEXT *pContext,
1744 BYTE **pRetAddr)
1745{
1746 // See code:ILStubCache.CreateNewMethodDesc for the code that sets flags on stub MDs
1747
1748 PCODE stubIP = GetIP(pContext);
1749 *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext);
1750
1751#ifdef FEATURE_MULTICASTSTUB_AS_IL
1752 if (stubIP == GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper))
1753 {
1754 stubIP = (PCODE)*pRetAddr;
1755 *pRetAddr = (BYTE*)StubManagerHelpers::GetRetAddrFromMulticastILStubFrame(pContext);
1756 }
1757#endif
1758
1759 DynamicMethodDesc *pStubMD = Entry2MethodDesc(stubIP, NULL)->AsDynamicMethodDesc();
1760
1761 TADDR arg = StubManagerHelpers::GetHiddenArg(pContext);
1762
1763 Object * pThis = StubManagerHelpers::GetThisPtr(pContext);
1764
1765 // See code:ILStubCache.CreateNewMethodDesc for the code that sets flags on stub MDs
1766 PCODE target;
1767
1768#ifdef FEATURE_MULTICASTSTUB_AS_IL
1769 if(pStubMD->IsMulticastStub())
1770 {
1771 _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper));
1772
1773 int delegateCount = (int)StubManagerHelpers::GetSecondArg(pContext);
1774
1775 int totalDelegateCount = (int)*(size_t*)((BYTE*)pThis + DelegateObject::GetOffsetOfInvocationCount());
1776
1777 if (delegateCount == totalDelegateCount)
1778 {
1779 LOG((LF_CORDB, LL_INFO1000, "MF::TF: Executed all stubs, should return\n"));
1780 // We've executed all the stubs, so we should return
1781 return FALSE;
1782 }
1783 else
1784 {
1785 // We're going to execute stub delegateCount next, so go and grab it.
1786 BYTE *pbDelInvocationList = *(BYTE **)((BYTE*)pThis + DelegateObject::GetOffsetOfInvocationList());
1787
1788 BYTE* pbDel = *(BYTE**)( ((ArrayBase *)pbDelInvocationList)->GetDataPtr() +
1789 ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount);
1790
1791 _ASSERTE(pbDel);
1792 return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace);
1793 }
1794
1795 }
1796 else
1797#endif // FEATURE_MULTICASTSTUB_AS_IL
1798 if (pStubMD->IsReverseStub())
1799 {
1800 if (pStubMD->IsStatic())
1801 {
1802 // This is reverse P/Invoke stub, the argument is UMEntryThunk
1803 UMEntryThunk *pEntryThunk = (UMEntryThunk *)arg;
1804 target = pEntryThunk->GetManagedTarget();
1805
1806 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Reverse P/Invoke case 0x%x\n", target));
1807 }
1808 else
1809 {
1810 // This is COM-to-CLR stub, the argument is the target
1811 target = (PCODE)arg;
1812 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: COM-to-CLR case 0x%x\n", target));
1813 }
1814 trace->InitForManaged(target);
1815 }
1816 else if (pStubMD->IsDelegateStub())
1817 {
1818 // This is forward delegate P/Invoke stub, the argument is undefined
1819 DelegateObject *pDel = (DelegateObject *)pThis;
1820 target = pDel->GetMethodPtrAux();
1821
1822 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward delegate P/Invoke case 0x%x\n", target));
1823 trace->InitForUnmanaged(target);
1824 }
1825 else if (pStubMD->IsCALLIStub())
1826 {
1827 // This is unmanaged CALLI stub, the argument is the target
1828 target = (PCODE)arg;
1829
1830 // The value is mangled on 64-bit
1831#ifdef _TARGET_AMD64_
1832 target = target >> 1; // call target is encoded as (addr << 1) | 1
1833#endif // _TARGET_AMD64_
1834
1835 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Unmanaged CALLI case 0x%x\n", target));
1836 trace->InitForUnmanaged(target);
1837 }
1838#ifdef FEATURE_COMINTEROP
1839 else if (pStubMD->IsDelegateCOMStub())
1840 {
1841 // This is a delegate, but the target is COM.
1842 DelegateObject *pDel = (DelegateObject *)pThis;
1843 DelegateEEClass *pClass = (DelegateEEClass *)pDel->GetMethodTable()->GetClass();
1844
1845 target = GetCOMTarget(pThis, pClass->m_pComPlusCallInfo);
1846
1847 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM via delegate case 0x%x\n", target));
1848 trace->InitForUnmanaged(target);
1849 }
1850#endif // FEATURE_COMINTEROP
1851 else
1852 {
1853 // This is either direct forward P/Invoke or a CLR-to-COM call, the argument is MD
1854 MethodDesc *pMD = (MethodDesc *)arg;
1855
1856 if (pMD->IsNDirect())
1857 {
1858 target = (PCODE)((NDirectMethodDesc *)pMD)->GetNativeNDirectTarget();
1859 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward P/Invoke case 0x%x\n", target));
1860 trace->InitForUnmanaged(target);
1861 }
1862#ifdef FEATURE_COMINTEROP
1863 else
1864 {
1865 _ASSERTE(pMD->IsComPlusCall());
1866 ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)pMD;
1867
1868 if (pCMD->IsStatic() || pCMD->IsCtor())
1869 {
1870 // pThis is not the object we'll be calling, we need to get the factory object instead
1871 MethodTable *pMTOfTypeToCreate = pCMD->GetMethodTable();
1872 pThis = OBJECTREFToObject(GetAppDomain()->LookupWinRTFactoryObject(pMTOfTypeToCreate, GetCurrentCtxCookie()));
1873
1874 if (pThis == NULL)
1875 {
1876 // If we don't have an RCW of the factory object yet, don't create it. We would
1877 // risk triggering GC which is not safe here because the IL stub is not at a GC
1878 // safe point. Instead, query WinRT directly and release the factory immediately.
1879 target = GetWinRTFactoryTarget(pCMD);
1880
1881 if (target != NULL)
1882 {
1883 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM WinRT factory RCW-does-not-exist-yet case 0x%x\n", target));
1884 trace->InitForUnmanaged(target);
1885 }
1886 }
1887 }
1888
1889 if (pThis != NULL)
1890 {
1891 target = GetCOMTarget(pThis, pCMD->m_pComPlusCallInfo);
1892
1893 LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM case 0x%x\n", target));
1894 trace->InitForUnmanaged(target);
1895 }
1896 }
1897#endif // FEATURE_COMINTEROP
1898 }
1899
1900 return TRUE;
1901}
1902#endif // !CROSSGEN_COMPILE
1903#endif //!DACCESS_COMPILE
1904
1905// This is used to recognize GenericComPlusCallStub, VarargPInvokeStub, and GenericPInvokeCalliHelper.
1906
1907#ifndef DACCESS_COMPILE
1908
1909/* static */
1910void InteropDispatchStubManager::Init()
1911{
1912 CONTRACTL
1913 {
1914 THROWS;
1915 GC_NOTRIGGER;
1916 MODE_ANY;
1917 }
1918 CONTRACTL_END
1919
1920 StubManager::AddStubManager(new InteropDispatchStubManager());
1921}
1922
1923#endif // #ifndef DACCESS_COMPILE
1924
1925PCODE TheGenericComplusCallStub(); // clrtocom.cpp
1926
1927#ifndef DACCESS_COMPILE
1928static BOOL IsVarargPInvokeStub(PCODE stubStartAddress)
1929{
1930 LIMITED_METHOD_CONTRACT;
1931
1932 if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub))
1933 return TRUE;
1934
1935#if !defined(_TARGET_X86_) && !defined(_TARGET_ARM64_)
1936 if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg))
1937 return TRUE;
1938#endif
1939
1940 return FALSE;
1941}
1942#endif // #ifndef DACCESS_COMPILE
1943
1944BOOL InteropDispatchStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
1945{
1946 WRAPPER_NO_CONTRACT;
1947 //@dbgtodo dharvey implement DAC suport
1948
1949#ifndef DACCESS_COMPILE
1950#ifdef FEATURE_COMINTEROP
1951 if (stubStartAddress == GetEEFuncEntryPoint(GenericComPlusCallStub))
1952 {
1953 return true;
1954 }
1955#endif // FEATURE_COMINTEROP
1956
1957 if (IsVarargPInvokeStub(stubStartAddress))
1958 {
1959 return true;
1960 }
1961
1962 if (stubStartAddress == GetEEFuncEntryPoint(GenericPInvokeCalliHelper))
1963 {
1964 return true;
1965 }
1966
1967#endif // !DACCESS_COMPILE
1968 return false;
1969}
1970
1971BOOL InteropDispatchStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace)
1972{
1973 LIMITED_METHOD_CONTRACT;
1974
1975 LOG((LF_CORDB, LL_EVERYTHING, "InteropDispatchStubManager::DoTraceStub called\n"));
1976
1977#ifndef DACCESS_COMPILE
1978 _ASSERTE(CheckIsStub_Internal(stubStartAddress));
1979
1980 trace->InitForManagerPush(stubStartAddress, this);
1981
1982 LOG_TRACE_DESTINATION(trace, stubStartAddress, "InteropDispatchStubManager::DoTraceStub");
1983
1984 return TRUE;
1985
1986#else // !DACCESS_COMPILE
1987 trace->InitForOther(NULL);
1988 return FALSE;
1989
1990#endif // !DACCESS_COMPILE
1991}
1992
1993#ifndef DACCESS_COMPILE
1994
1995BOOL InteropDispatchStubManager::TraceManager(Thread *thread,
1996 TraceDestination *trace,
1997 T_CONTEXT *pContext,
1998 BYTE **pRetAddr)
1999{
2000 CONTRACTL
2001 {
2002 THROWS;
2003 GC_TRIGGERS;
2004 MODE_COOPERATIVE;
2005 }
2006 CONTRACTL_END;
2007
2008 *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext);
2009
2010 TADDR arg = StubManagerHelpers::GetHiddenArg(pContext);
2011
2012 // IL stub may not exist at this point so we init directly for the target (TODO?)
2013
2014 if (IsVarargPInvokeStub(GetIP(pContext)))
2015 {
2016 NDirectMethodDesc *pNMD = (NDirectMethodDesc *)arg;
2017 _ASSERTE(pNMD->IsNDirect());
2018 PCODE target = (PCODE)pNMD->GetNDirectTarget();
2019
2020 LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Vararg P/Invoke case 0x%x\n", target));
2021 trace->InitForUnmanaged(target);
2022 }
2023 else if (GetIP(pContext) == GetEEFuncEntryPoint(GenericPInvokeCalliHelper))
2024 {
2025 PCODE target = (PCODE)arg;
2026 LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Unmanaged CALLI case 0x%x\n", target));
2027 trace->InitForUnmanaged(target);
2028 }
2029#ifdef FEATURE_COMINTEROP
2030 else
2031 {
2032 ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)arg;
2033 _ASSERTE(pCMD->IsComPlusCall());
2034
2035 Object * pThis = StubManagerHelpers::GetThisPtr(pContext);
2036
2037 {
2038 if (!pCMD->m_pComPlusCallInfo->m_pInterfaceMT->IsComEventItfType() && (pCMD->m_pComPlusCallInfo->m_pILStub != NULL))
2039 {
2040 // Early-bound CLR->COM call - continue in the IL stub
2041 trace->InitForStub(pCMD->m_pComPlusCallInfo->m_pILStub);
2042 }
2043 else
2044 {
2045 // Late-bound CLR->COM call - continue in target's IDispatch::Invoke
2046 OBJECTREF oref = ObjectToOBJECTREF(pThis);
2047 GCPROTECT_BEGIN(oref);
2048
2049 MethodTable *pItfMT = pCMD->m_pComPlusCallInfo->m_pInterfaceMT;
2050 _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch);
2051
2052 SafeComHolder<IUnknown> pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pItfMT);
2053 LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk;
2054
2055 PCODE target = (PCODE)lpVtbl[6]; // DISPATCH_INVOKE_SLOT;
2056 LOG((LF_CORDB, LL_INFO10000, "CPSM::TraceManager: CLR-to-COM late-bound case 0x%x\n", target));
2057 trace->InitForUnmanaged(target);
2058
2059 GCPROTECT_END();
2060 }
2061 }
2062 }
2063#endif // FEATURE_COMINTEROP
2064
2065 return TRUE;
2066}
2067#endif //!DACCESS_COMPILE
2068
2069//
2070// Since we don't generate delegate invoke stubs at runtime on IA64, we
2071// can't use the StubLinkStubManager for these stubs. Instead, we create
2072// an additional DelegateInvokeStubManager instead.
2073//
2074SPTR_IMPL(DelegateInvokeStubManager, DelegateInvokeStubManager, g_pManager);
2075
2076#ifndef DACCESS_COMPILE
2077
2078// static
2079void DelegateInvokeStubManager::Init()
2080{
2081 CONTRACTL
2082 {
2083 THROWS;
2084 GC_NOTRIGGER;
2085 MODE_ANY;
2086 }
2087 CONTRACTL_END
2088
2089 g_pManager = new DelegateInvokeStubManager();
2090 StubManager::AddStubManager(g_pManager);
2091}
2092
2093BOOL DelegateInvokeStubManager::AddStub(Stub* pStub)
2094{
2095 WRAPPER_NO_CONTRACT;
2096 PCODE start = pStub->GetEntryPoint();
2097
2098 // We don't really care about the size here. We only stop in these stubs at the first instruction,
2099 // so we'll never be asked to claim an address in the middle of a stub.
2100 return GetRangeList()->AddRange((BYTE *)start, (BYTE *)start + 1, (LPVOID)start);
2101}
2102
2103void DelegateInvokeStubManager::RemoveStub(Stub* pStub)
2104{
2105 WRAPPER_NO_CONTRACT;
2106 PCODE start = pStub->GetEntryPoint();
2107
2108 // We don't really care about the size here. We only stop in these stubs at the first instruction,
2109 // so we'll never be asked to claim an address in the middle of a stub.
2110 GetRangeList()->RemoveRanges((LPVOID)start);
2111}
2112
2113#endif
2114
2115BOOL DelegateInvokeStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
2116{
2117 LIMITED_METHOD_DAC_CONTRACT;
2118
2119 bool fIsStub = false;
2120
2121#ifndef DACCESS_COMPILE
2122#ifndef _TARGET_X86_
2123 fIsStub = fIsStub || (stubStartAddress == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub));
2124#endif
2125#endif // !DACCESS_COMPILE
2126
2127 fIsStub = fIsStub || GetRangeList()->IsInRange(stubStartAddress);
2128
2129 return fIsStub;
2130}
2131
2132BOOL DelegateInvokeStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace)
2133{
2134 LIMITED_METHOD_CONTRACT;
2135
2136 LOG((LF_CORDB, LL_EVERYTHING, "DelegateInvokeStubManager::DoTraceStub called\n"));
2137
2138 _ASSERTE(CheckIsStub_Internal(stubStartAddress));
2139
2140 // If it's a MC delegate, then we want to set a BP & do a context-ful
2141 // manager push, so that we can figure out if this call will be to a
2142 // single multicast delegate or a multi multicast delegate
2143 trace->InitForManagerPush(stubStartAddress, this);
2144
2145 LOG_TRACE_DESTINATION(trace, stubStartAddress, "DelegateInvokeStubManager::DoTraceStub");
2146
2147 return TRUE;
2148}
2149
2150#if !defined(DACCESS_COMPILE)
2151
2152BOOL DelegateInvokeStubManager::TraceManager(Thread *thread, TraceDestination *trace,
2153 T_CONTEXT *pContext, BYTE **pRetAddr)
2154{
2155 CONTRACTL
2156 {
2157 MODE_COOPERATIVE;
2158 }
2159 CONTRACTL_END;
2160
2161 PCODE destAddr;
2162
2163 PCODE pc;
2164 pc = ::GetIP(pContext);
2165
2166 BYTE* pThis;
2167 pThis = NULL;
2168
2169 // Retrieve the this pointer from the context.
2170#if defined(_TARGET_X86_)
2171 (*pRetAddr) = *(BYTE **)(size_t)(pContext->Esp);
2172
2173 pThis = (BYTE*)(size_t)(pContext->Ecx);
2174
2175 destAddr = *(PCODE*)(pThis + DelegateObject::GetOffsetOfMethodPtrAux());
2176
2177#elif defined(_TARGET_AMD64_)
2178
2179 // <TODO>
2180 // We need to check whether the following is the correct return address.
2181 // </TODO>
2182 (*pRetAddr) = *(BYTE **)(size_t)(pContext->Rsp);
2183
2184 LOG((LF_CORDB, LL_INFO10000, "DISM:TM at 0x%p, retAddr is 0x%p\n", pc, (*pRetAddr)));
2185
2186 DELEGATEREF orDelegate;
2187 if (GetEEFuncEntryPoint(SinglecastDelegateInvokeStub) == pc)
2188 {
2189 LOG((LF_CORDB, LL_INFO10000, "DISM::TraceManager: isSingle\n"));
2190
2191 orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext));
2192
2193 // _methodPtr is where we are going to next. However, in ngen cases, we may have a shuffle thunk
2194 // burned into the ngen image, in which case the shuffle thunk is not added to the range list of
2195 // the DelegateInvokeStubManager. So we use _methodPtrAux as a fallback.
2196 destAddr = orDelegate->GetMethodPtr();
2197 if (StubManager::TraceStub(destAddr, trace))
2198 {
2199 LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n", destAddr));
2200 LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: 1, result type: %d\n", trace->GetTraceType()));
2201 return TRUE;
2202 }
2203 }
2204 else
2205 {
2206 // We get here if we are stopped at the beginning of a shuffle thunk.
2207 // The next address we are going to is _methodPtrAux.
2208 Stub* pStub = Stub::RecoverStub(pc);
2209
2210 // We use the patch offset field to indicate whether the stub has a hidden return buffer argument.
2211 // This field is set in SetupShuffleThunk().
2212 if (pStub->GetPatchOffset() != 0)
2213 {
2214 // This stub has a hidden return buffer argument.
2215 orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetSecondArg(pContext));
2216 }
2217 else
2218 {
2219 orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext));
2220 }
2221 }
2222
2223 destAddr = orDelegate->GetMethodPtrAux();
2224#elif defined(_TARGET_ARM_)
2225 (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr);
2226 pThis = (BYTE*)(size_t)(pContext->R0);
2227
2228 // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a
2229 // shuffle thunk (destination in _methodPtrAux).
2230 int offsetOfNextDest;
2231 if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub))
2232 offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr();
2233 else
2234 offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux();
2235 destAddr = *(PCODE*)(pThis + offsetOfNextDest);
2236#elif defined(_TARGET_ARM64_)
2237 (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr);
2238 pThis = (BYTE*)(size_t)(pContext->X0);
2239
2240 // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a
2241 // shuffle thunk (destination in _methodPtrAux).
2242 int offsetOfNextDest;
2243 if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub))
2244 offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr();
2245 else
2246 offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux();
2247 destAddr = *(PCODE*)(pThis + offsetOfNextDest);
2248#else
2249 PORTABILITY_ASSERT("DelegateInvokeStubManager::TraceManager");
2250 destAddr = NULL;
2251#endif
2252
2253 LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n", destAddr));
2254
2255 BOOL res = StubManager::TraceStub(destAddr, trace);
2256 LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: %d, result type: %d\n", res, trace->GetTraceType()));
2257
2258 return res;
2259}
2260
2261// static
2262BOOL DelegateInvokeStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *trace)
2263{
2264 CONTRACTL
2265 {
2266 NOTHROW;
2267 GC_NOTRIGGER;
2268 MODE_ANY;
2269 }
2270 CONTRACTL_END;
2271 BYTE **ppbDest = NULL;
2272 // If we got here, then we're here b/c we're at the start of a delegate stub
2273 // need to figure out the kind of delegates we are dealing with
2274
2275 BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList());
2276
2277 LOG((LF_CORDB,LL_INFO10000, "DISM::TMI: invocationList: 0x%x\n", pbDelInvocationList));
2278
2279 if (pbDelInvocationList == NULL)
2280 {
2281 // null invocationList can be one of the following:
2282 // Instance closed, Instance open non-virt, Instance open virtual, Static closed, Static opened, Unmanaged FtnPtr
2283 // Instance open virtual is complex and we need to figure out what to do (TODO).
2284 // For the others the logic is the following:
2285 // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux
2286
2287 ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux());
2288
2289 if (*ppbDest == NULL)
2290 {
2291 ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr());
2292
2293 if (*ppbDest == NULL)
2294 {
2295 // it's not looking good, bail out
2296 LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n"));
2297 return FALSE;
2298 }
2299
2300 }
2301
2302 LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%x *ppbDest:0x%x\n", ppbDest, *ppbDest));
2303
2304 BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace);
2305
2306 LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n", res, trace->GetTraceType()));
2307
2308 return res;
2309 }
2310
2311 // invocationList is not null, so it can be one of the following:
2312 // Multicast, Static closed (special sig), Secure
2313
2314 // rule out the static with special sig
2315 BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount());
2316
2317 if (!pbCount)
2318 {
2319 // it's a static closed, the target lives in _methodAuxPtr
2320 ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux());
2321
2322 if (*ppbDest == NULL)
2323 {
2324 // it's not looking good, bail out
2325 LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n"));
2326 return FALSE;
2327 }
2328
2329 LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%x *ppbDest:0x%x\n", ppbDest, *ppbDest));
2330
2331 BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace);
2332
2333 LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n", res, trace->GetTraceType()));
2334
2335 return res;
2336 }
2337
2338 MethodTable *pType = *(MethodTable**)pbDelInvocationList;
2339 if (pType->IsDelegate())
2340 {
2341 // this is a secure deelgate. The target is hidden inside this field, so recurse in and pray...
2342 return TraceDelegateObject(pbDelInvocationList, trace);
2343 }
2344
2345 // Otherwise, we're going for the first invoke of the multi case.
2346 // In order to go to the correct spot, we have just have to fish out
2347 // slot 0 of the invocation list, and figure out where that's going to,
2348 // then put a breakpoint there...
2349 pbDel = *(BYTE**)(((ArrayBase *)pbDelInvocationList)->GetDataPtr());
2350 return TraceDelegateObject(pbDel, trace);
2351}
2352
2353#endif // DACCESS_COMPILE
2354
2355
2356#if !defined(DACCESS_COMPILE)
2357
2358// static
2359void TailCallStubManager::Init()
2360{
2361 CONTRACTL
2362 {
2363 THROWS;
2364 GC_NOTRIGGER;
2365 MODE_ANY;
2366 }
2367 CONTRACTL_END
2368
2369 StubManager::AddStubManager(new TailCallStubManager());
2370}
2371
2372bool TailCallStubManager::IsTailCallStubHelper(PCODE code)
2373{
2374 LIMITED_METHOD_CONTRACT;
2375
2376 return code == GetEEFuncEntryPoint(JIT_TailCall);
2377}
2378
2379#endif // !DACCESS_COMPILED
2380
2381BOOL TailCallStubManager::CheckIsStub_Internal(PCODE stubStartAddress)
2382{
2383 LIMITED_METHOD_DAC_CONTRACT;
2384
2385 bool fIsStub = false;
2386
2387#if !defined(DACCESS_COMPILE)
2388 fIsStub = IsTailCallStubHelper(stubStartAddress);
2389#endif // !DACCESS_COMPILE
2390
2391 return fIsStub;
2392}
2393
2394#if !defined(DACCESS_COMPILE)
2395
2396#if defined(_TARGET_X86_)
2397EXTERN_C void STDCALL JIT_TailCallLeave();
2398EXTERN_C void STDCALL JIT_TailCallVSDLeave();
2399#endif // _TARGET_X86_
2400
2401BOOL TailCallStubManager::TraceManager(Thread * pThread,
2402 TraceDestination * pTrace,
2403 T_CONTEXT * pContext,
2404 BYTE ** ppRetAddr)
2405{
2406 WRAPPER_NO_CONTRACT;
2407#if defined(_TARGET_X86_)
2408 TADDR esp = GetSP(pContext);
2409 TADDR ebp = GetFP(pContext);
2410
2411 // Check if we are stopped at the beginning of JIT_TailCall().
2412 if (GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCall))
2413 {
2414 // There are two cases in JIT_TailCall(). The first one is a normal tail call.
2415 // The second one is a tail call to a virtual method.
2416 *ppRetAddr = *(reinterpret_cast<BYTE **>(ebp + sizeof(SIZE_T)));
2417
2418 // Check whether this is a VSD tail call.
2419 SIZE_T flags = *(reinterpret_cast<SIZE_T *>(esp + JIT_TailCall_StackOffsetToFlags));
2420 if (flags & 0x2)
2421 {
2422 // This is a VSD tail call.
2423 pTrace->InitForManagerPush(GetEEFuncEntryPoint(JIT_TailCallVSDLeave), this);
2424 return TRUE;
2425 }
2426 else
2427 {
2428 // This is not a VSD tail call.
2429 pTrace->InitForManagerPush(GetEEFuncEntryPoint(JIT_TailCallLeave), this);
2430 return TRUE;
2431 }
2432 }
2433 else
2434 {
2435 if (GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCallLeave))
2436 {
2437 // This is the simple case. The tail call goes directly to the target. There won't be an
2438 // explicit frame on the stack. We should be right at the return instruction which branches to
2439 // the call target. The return address is stored in the second leafmost stack slot.
2440 *ppRetAddr = *(reinterpret_cast<BYTE **>(esp + sizeof(SIZE_T)));
2441 }
2442 else
2443 {
2444 _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCallVSDLeave));
2445
2446 // This is the VSD case. The tail call goes through a assembly helper function which sets up
2447 // and tears down an explicit frame. In this case, the return address is at the same place
2448 // as on entry to JIT_TailCall().
2449 *ppRetAddr = *(reinterpret_cast<BYTE **>(ebp + sizeof(SIZE_T)));
2450 }
2451
2452 // In both cases, the target address is stored in the leafmost stack slot.
2453 pTrace->InitForStub((PCODE)*reinterpret_cast<SIZE_T *>(esp));
2454 return TRUE;
2455 }
2456
2457#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM_)
2458
2459 _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCall));
2460
2461 // The target address is the second argument
2462#ifdef _TARGET_AMD64_
2463 PCODE target = (PCODE)pContext->Rdx;
2464#else
2465 PCODE target = (PCODE)pContext->R1;
2466#endif
2467 *ppRetAddr = reinterpret_cast<BYTE *>(target);
2468 pTrace->InitForStub(target);
2469 return TRUE;
2470
2471#else // !_TARGET_X86_ && !_TARGET_AMD64_ && !_TARGET_ARM_
2472
2473 _ASSERTE(!"TCSM::TM - TailCallStubManager should not be necessary on this platform");
2474 return FALSE;
2475
2476#endif // _TARGET_X86_ || _TARGET_AMD64_
2477}
2478
2479#endif // !DACCESS_COMPILE
2480
2481BOOL TailCallStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace)
2482{
2483 WRAPPER_NO_CONTRACT;
2484
2485 LOG((LF_CORDB, LL_EVERYTHING, "TailCallStubManager::DoTraceStub called\n"));
2486
2487 BOOL fResult = FALSE;
2488
2489 // Make sure we are stopped at the beginning of JIT_TailCall().
2490 _ASSERTE(CheckIsStub_Internal(stubStartAddress));
2491 trace->InitForManagerPush(stubStartAddress, this);
2492 fResult = TRUE;
2493
2494 LOG_TRACE_DESTINATION(trace, stubStartAddress, "TailCallStubManager::DoTraceStub");
2495 return fResult;
2496}
2497
2498
2499#ifdef DACCESS_COMPILE
2500
2501void
2502PrecodeStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2503{
2504 SUPPORTS_DAC;
2505 WRAPPER_NO_CONTRACT;
2506 DAC_ENUM_VTHIS();
2507 EMEM_OUT(("MEM: %p PrecodeStubManager\n", dac_cast<TADDR>(this)));
2508}
2509
2510void
2511StubLinkStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2512{
2513 SUPPORTS_DAC;
2514 WRAPPER_NO_CONTRACT;
2515 DAC_ENUM_VTHIS();
2516 EMEM_OUT(("MEM: %p StubLinkStubManager\n", dac_cast<TADDR>(this)));
2517 GetRangeList()->EnumMemoryRegions(flags);
2518}
2519
2520void
2521ThunkHeapStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2522{
2523 SUPPORTS_DAC;
2524 WRAPPER_NO_CONTRACT;
2525 DAC_ENUM_VTHIS();
2526 EMEM_OUT(("MEM: %p ThunkHeapStubManager\n", dac_cast<TADDR>(this)));
2527 GetRangeList()->EnumMemoryRegions(flags);
2528}
2529
2530void
2531JumpStubStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2532{
2533 SUPPORTS_DAC;
2534 WRAPPER_NO_CONTRACT;
2535 DAC_ENUM_VTHIS();
2536 EMEM_OUT(("MEM: %p JumpStubStubManager\n", dac_cast<TADDR>(this)));
2537}
2538
2539#ifdef FEATURE_PREJIT
2540void
2541RangeSectionStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2542{
2543 SUPPORTS_DAC;
2544 WRAPPER_NO_CONTRACT;
2545 DAC_ENUM_VTHIS();
2546 EMEM_OUT(("MEM: %p RangeSectionStubManager\n", dac_cast<TADDR>(this)));
2547}
2548#endif
2549
2550void
2551ILStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2552{
2553 SUPPORTS_DAC;
2554 WRAPPER_NO_CONTRACT;
2555 DAC_ENUM_VTHIS();
2556 EMEM_OUT(("MEM: %p ILStubManager\n", dac_cast<TADDR>(this)));
2557}
2558
2559void
2560InteropDispatchStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2561{
2562 SUPPORTS_DAC;
2563 WRAPPER_NO_CONTRACT;
2564 DAC_ENUM_VTHIS();
2565 EMEM_OUT(("MEM: %p InteropDispatchStubManager\n", dac_cast<TADDR>(this)));
2566}
2567
2568void
2569DelegateInvokeStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2570{
2571 SUPPORTS_DAC;
2572 WRAPPER_NO_CONTRACT;
2573 DAC_ENUM_VTHIS();
2574 EMEM_OUT(("MEM: %p DelegateInvokeStubManager\n", dac_cast<TADDR>(this)));
2575 GetRangeList()->EnumMemoryRegions(flags);
2576}
2577
2578void
2579VirtualCallStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2580{
2581 SUPPORTS_DAC;
2582 WRAPPER_NO_CONTRACT;
2583 DAC_ENUM_VTHIS();
2584 EMEM_OUT(("MEM: %p VirtualCallStubManager\n", dac_cast<TADDR>(this)));
2585 GetLookupRangeList()->EnumMemoryRegions(flags);
2586 GetResolveRangeList()->EnumMemoryRegions(flags);
2587 GetDispatchRangeList()->EnumMemoryRegions(flags);
2588 GetCacheEntryRangeList()->EnumMemoryRegions(flags);
2589}
2590
2591void TailCallStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2592{
2593 SUPPORTS_DAC;
2594 WRAPPER_NO_CONTRACT;
2595 DAC_ENUM_VTHIS();
2596 EMEM_OUT(("MEM: %p TailCallStubManager\n", dac_cast<TADDR>(this)));
2597}
2598
2599#endif // #ifdef DACCESS_COMPILE
2600
2601