2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 // The stub manager exists so that the debugger can accurately step through
10 // the myriad stubs & wrappers which exist in the EE, without imposing undue
11 // overhead on the stubs themselves.
13 // Each type of stub (except those which the debugger can treat as atomic operations)
14 // needs to have a stub manager to represent it. The stub manager is responsible for
15 // (a) identifying the stub as such, and
16 // (b) tracing into the stub & reporting what the stub will call. This
17 // report can consist of
18 // (i) a managed code address
19 // (ii) an unmanaged code address
20 // (iii) another stub address
21 // (iv) a "frame patch" address - that is, an address in the stub,
22 // which the debugger can patch. When the patch is hit, the debugger
23 // will query the topmost frame to trace itself. (Thus this is
24 // a way of deferring the trace logic to the frame which the stub
27 // The set of stub managers is extensible, but should be kept to a reasonable number
28 // as they are currently linearly searched & queried for each stub.
35 #include "simplerwlock.hpp"
37 // When 'TraceStub' returns, it gives the address of where the 'target' is for a stub'
38 // TraceType indicates what this 'target' is
41 TRACE_ENTRY_STUB, // Stub goes to an unmanaged entry stub
42 TRACE_STUB, // Stub goes to another stub
43 TRACE_UNMANAGED, // Stub goes to unmanaged code
44 TRACE_MANAGED, // Stub goes to Jitted code
45 TRACE_UNJITTED_METHOD, // Is the prestub, since there is no code, the address will actually be a MethodDesc*
47 TRACE_FRAME_PUSH, // Don't know where stub goes, stop at address, and then ask the frame that is on the stack
48 TRACE_MGR_PUSH, // Don't know where stub goes, stop at address then call TraceManager() below to find out
50 TRACE_OTHER // We are going somewhere you can't step into (eg. ee helper function)
56 class DebuggerRCThread;
58 enum StubCodeBlockKind : int;
60 // A TraceDestination describes where code is going to call. This can be used by the Debugger's Step-In functionality
61 // to skip through stubs and place a patch directly at a call's target.
62 // TD are supplied by the stubmanagers.
63 class TraceDestination
66 friend class DebuggerRCThread;
68 TraceDestination() { }
71 // Get a string representation of this TraceDestination
72 // Uses the supplied buffer to store the memory (or may return a string literal).
73 // This will also print the TD's arguments.
74 const WCHAR * DbgToString(SString &buffer);
77 // Initialize for unmanaged code.
78 // The addr is in unmanaged code. Used for Step-in from managed to native.
79 void InitForUnmanaged(PCODE addr)
81 STATIC_CONTRACT_SO_TOLERANT;
82 this->type = TRACE_UNMANAGED;
84 this->stubManager = NULL;
87 // The addr is inside jitted code (eg, there's a JitManaged that will claim it)
88 void InitForManaged(PCODE addr)
90 STATIC_CONTRACT_SO_TOLERANT;
91 this->type = TRACE_MANAGED;
93 this->stubManager = NULL;
96 // Initialize for an unmanaged entry stub.
97 void InitForUnmanagedStub(PCODE addr)
99 STATIC_CONTRACT_SO_TOLERANT;
100 this->type = TRACE_ENTRY_STUB;
101 this->address = addr;
102 this->stubManager = NULL;
105 // Initialize for a stub.
106 void InitForStub(PCODE addr)
108 STATIC_CONTRACT_SO_TOLERANT;
109 this->type = TRACE_STUB;
110 this->address = addr;
111 this->stubManager = NULL;
114 // Init for a managed unjitted method.
115 // This will place an IL patch that will get bound when the debugger gets a Jit complete
116 // notification for this method.
117 // If pDesc is a wrapper methoddesc, we will unwrap it.
118 void InitForUnjittedMethod(MethodDesc * pDesc);
120 // Place a patch at the given addr, and then when it's hit,
121 // call pStubManager->TraceManager() to get the next TraceDestination.
122 void InitForManagerPush(PCODE addr, StubManager * pStubManager)
124 STATIC_CONTRACT_SO_TOLERANT;
125 this->type = TRACE_MGR_PUSH;
126 this->address = addr;
127 this->stubManager = pStubManager;
130 // Place a patch at the given addr, and then when it's hit
131 // call GetThread()->GetFrame()->TraceFrame() to get the next TraceDestination.
132 // This address must be safe to run a callstack at.
133 void InitForFramePush(PCODE addr)
135 this->type = TRACE_FRAME_PUSH;
136 this->address = addr;
137 this->stubManager = NULL;
140 // Nobody recognized the target address. We will not be able to step-in to it.
141 // This is ok if the target just calls into mscorwks (such as an Fcall) because
142 // there's no managed code to step in to, and we don't support debugging the CLR
143 // itself, so there's no native code to step into either.
144 void InitForOther(PCODE addr)
146 this->type = TRACE_OTHER;
147 this->address = addr;
148 this->stubManager = NULL;
152 TraceType GetTraceType() { return type; }
155 LIMITED_METHOD_CONTRACT;
156 _ASSERTE(type != TRACE_UNJITTED_METHOD);
159 MethodDesc* GetMethodDesc()
161 LIMITED_METHOD_CONTRACT;
162 _ASSERTE(type == TRACE_UNJITTED_METHOD);
166 StubManager * GetStubManager()
171 // Expose this b/c DebuggerPatchTable::AddPatchForAddress() needs it.
172 // Ideally we'd get rid of this.
173 void Bad_SetTraceType(TraceType t)
178 TraceType type; // The kind of code the stub is going to
179 PCODE address; // Where the stub is going
180 StubManager *stubManager; // The manager that claims this stub
186 void LogTraceDestination(const char * szHint, PCODE stubAddr, TraceDestination * pTrace);
187 #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint) LogTraceDestination(_stHint, stubAddr, _tracedestination)
189 #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint)
192 typedef VPTR(class StubManager) PTR_StubManager;
196 friend class StubManagerIterator;
198 VPTR_BASE_VTABLE_CLASS(StubManager)
201 // Startup and shutdown the global stubmanager service.
202 static void InitializeStubManagers();
203 static void TerminateStubManagers();
205 // Does any sub manager recognise this EIP?
206 static BOOL IsStub(PCODE stubAddress)
209 return FindStubManager(stubAddress) != NULL;
212 // Find stub manager for given code address
213 static PTR_StubManager FindStubManager(PCODE stubAddress);
215 // Look for stubAddress, if found return TRUE, and set 'trace' to
216 static BOOL TraceStub(PCODE stubAddress, TraceDestination *trace);
218 // if 'trace' indicates TRACE_STUB, keep calling TraceStub on 'trace', until you get out of all stubs
219 // returns true if successfull
220 static BOOL FollowTrace(TraceDestination *trace);
222 #ifdef DACCESS_COMPILE
223 static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
226 static void AddStubManager(StubManager *mgr);
228 // NOTE: Very important when using this. It is not thread safe, except in this very
229 // limited scenario: the thread must have the runtime suspended.
230 static void UnlinkStubManager(StubManager *mgr);
232 #ifndef DACCESS_COMPILE
234 virtual ~StubManager();
239 // Debug helper to help identify stub-managers. Make it pure to force stub managers to implement it.
240 virtual const char * DbgGetName() = 0;
243 // Only Stubmanagers that return 'TRACE_MGR_PUSH' as a trace type need to implement this function
244 // Fills in 'trace' (the target), and 'pRetAddr' (the method that called the stub) (this is needed
245 // as a 'fall back' so that the debugger can at least stop when the stub returns.
246 virtual BOOL TraceManager(Thread *thread, TraceDestination *trace,
247 T_CONTEXT *pContext, BYTE **pRetAddr)
249 LIMITED_METHOD_CONTRACT;
251 _ASSERTE(!"Default impl of TraceManager should never be called!");
255 // The worker for IsStub. This calls CheckIsStub_Internal, but wraps it w/
257 BOOL CheckIsStub_Worker(PCODE stubStartAddress);
263 //-----------------------------------------------------------------------------
264 // Debugging Stubmanager bugs is very painful. You need to figure out
265 // how you go to where you got and which stub-manager is at fault.
266 // To help with this, we track a rolling log so that we can give very
267 // informative asserts. this log is not thread-safe, but we really only expect
268 // a single stub-manager usage at a time.
270 // A stub manager for a step-in operation may be used across
271 // both the helper thread and then the managed thread doing the step-in.
272 // These threads will coordinate to have exclusive access (helper will only access
273 // when stopped; and managed thread will only access when running).
275 // It's also possible (but rare) for a single thread to have multiple step-in operations.
276 // Since that's so rare, no present need to expand our logging to support it.
277 //-----------------------------------------------------------------------------
280 static bool IsStubLoggingEnabled();
282 // Call to reset the log. This is used at the start of a new step-operation.
283 static void DbgBeginLog(TADDR addrCallInstruction, TADDR addrCallTarget);
284 static void DbgFinishLog();
286 // Log arbitrary string. This is a nop if it's outside the Begin/Finish window.
287 // We could consider making each log entry type-safe (and thus avoid the string operations).
288 static void DbgWriteLog(const CHAR *format, ...);
290 // Get the log as a string.
291 static void DbgGetLog(SString * pStringOut);
294 // Implement log as a SString.
295 static SString * s_pDbgStubManagerLog;
297 static CrstStatic s_DbgLogCrst;
304 // Each stubmanaged implements this.
305 // This may throw, AV, etc depending on the implementation. This should not
306 // be called directly unless you know exactly what you're doing.
307 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress) = 0;
309 // The worker for TraceStub
310 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) = 0;
313 static BOOL IsSingleOwner(PCODE stubAddress, StubManager * pOwner);
316 #ifdef DACCESS_COMPILE
317 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
320 // This is used by DAC to provide more information on who owns a stub.
321 virtual LPCWSTR GetStubManagerName(PCODE addr) = 0;
325 SPTR_DECL(StubManager, g_pFirstManager);
326 PTR_StubManager m_pNextManager;
328 static CrstStatic s_StubManagerListCrst;
331 // -------------------------------------------------------
332 // This just wraps the RangeList methods in a read or
333 // write lock depending on the operation.
334 // -------------------------------------------------------
336 class LockedRangeList : public RangeList
339 VPTR_VTABLE_CLASS(LockedRangeList, RangeList)
341 LockedRangeList() : RangeList(), m_RangeListRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT)
343 LIMITED_METHOD_CONTRACT;
348 LIMITED_METHOD_CONTRACT;
353 virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id)
356 SimpleWriteLockHolder lh(&m_RangeListRWLock);
357 return RangeList::AddRangeWorker(start,end,id);
360 virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL)
363 SimpleWriteLockHolder lh(&m_RangeListRWLock);
364 RangeList::RemoveRangesWorker(id,start,end);
367 virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL)
371 SimpleReadLockHolder lh(&m_RangeListRWLock);
372 return RangeList::IsInRangeWorker(address, pID);
375 SimpleRWLock m_RangeListRWLock;
378 //-----------------------------------------------------------
379 // Stub manager for the prestub. Although there is just one, it has
380 // unique behavior so it gets its own stub manager.
381 //-----------------------------------------------------------
382 class ThePreStubManager : public StubManager
384 VPTR_VTABLE_CLASS(ThePreStubManager, StubManager)
387 #ifndef DACCESS_COMPILE
388 ThePreStubManager() { LIMITED_METHOD_CONTRACT; }
392 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThePreStubManager"; }
395 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
397 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
399 #ifndef DACCESS_COMPILE
400 static void Init(void);
403 #ifdef DACCESS_COMPILE
405 virtual LPCWSTR GetStubManagerName(PCODE addr)
406 { LIMITED_METHOD_CONTRACT; return W("ThePreStub"); }
410 // -------------------------------------------------------
411 // Stub manager classes for method desc prestubs & normal
412 // frame-pushing, StubLinker created stubs
413 // -------------------------------------------------------
415 typedef VPTR(class PrecodeStubManager) PTR_PrecodeStubManager;
417 class PrecodeStubManager : public StubManager
419 VPTR_VTABLE_CLASS(PrecodeStubManager, StubManager)
423 SPTR_DECL(PrecodeStubManager, g_pManager);
426 // Debug helper to help identify stub-managers.
427 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "PrecodeStubManager"; }
433 #ifndef DACCESS_COMPILE
434 PrecodeStubManager() {LIMITED_METHOD_CONTRACT;}
435 ~PrecodeStubManager() {WRAPPER_NO_CONTRACT;}
439 static BOOL IsPrecodeByAsm(PCODE stubStartAddress);
441 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
443 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
444 #ifndef DACCESS_COMPILE
445 virtual BOOL TraceManager(Thread *thread,
446 TraceDestination *trace,
451 #ifdef DACCESS_COMPILE
452 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
455 virtual LPCWSTR GetStubManagerName(PCODE addr)
456 { LIMITED_METHOD_CONTRACT; return W("MethodDescPrestub"); }
460 // Note that this stub was written by a debugger guy, and thus when he refers to 'multicast'
461 // stub, he really means multi or single cast stub. This was done b/c the same stub
462 // services both types of stub.
463 // Note from the debugger guy: the way to understand what this manager does is to
464 // first grok EmitMulticastInvoke for the platform you're working on (right now, just x86).
465 // Then return here, and understand that (for x86) the only way we know which method
466 // we're going to invoke next is by inspecting EDI when we've got the debuggee stopped
467 // in the stub, and so our trace frame will either (FRAME_PUSH) put a breakpoint
468 // in the stub, or (if we hit the BP) examine EDI, etc, & figure out where we're going next.
470 typedef VPTR(class StubLinkStubManager) PTR_StubLinkStubManager;
472 class StubLinkStubManager : public StubManager
474 VPTR_VTABLE_CLASS(StubLinkStubManager, StubManager)
479 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "StubLinkStubManager"; }
483 SPTR_DECL(StubLinkStubManager, g_pManager);
487 #ifndef DACCESS_COMPILE
488 StubLinkStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;}
489 ~StubLinkStubManager() {WRAPPER_NO_CONTRACT;}
493 LockedRangeList m_rangeList;
495 // Get dac-ized pointer to rangelist.
496 PTR_RangeList GetRangeList()
500 TADDR addr = PTR_HOST_MEMBER_TADDR(StubLinkStubManager, this, m_rangeList);
501 return PTR_RangeList(addr);
505 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
507 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
508 #ifndef DACCESS_COMPILE
509 virtual BOOL TraceManager(Thread *thread,
510 TraceDestination *trace,
515 #ifdef DACCESS_COMPILE
516 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
519 virtual LPCWSTR GetStubManagerName(PCODE addr)
520 { LIMITED_METHOD_CONTRACT; return W("StubLinkStub"); }
524 // Stub manager for thunks.
526 typedef VPTR(class ThunkHeapStubManager) PTR_ThunkHeapStubManager;
528 class ThunkHeapStubManager : public StubManager
530 VPTR_VTABLE_CLASS(ThunkHeapStubManager, StubManager)
534 SPTR_DECL(ThunkHeapStubManager, g_pManager);
538 #ifndef DACCESS_COMPILE
539 ThunkHeapStubManager() : StubManager(), m_rangeList() { LIMITED_METHOD_CONTRACT; }
540 ~ThunkHeapStubManager() {WRAPPER_NO_CONTRACT;}
544 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThunkHeapStubManager"; }
548 LockedRangeList m_rangeList;
550 // Get dac-ized pointer to rangelist.
551 PTR_RangeList GetRangeList()
554 TADDR addr = PTR_HOST_MEMBER_TADDR(ThunkHeapStubManager, this, m_rangeList);
555 return PTR_RangeList(addr);
557 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
560 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
562 #ifdef DACCESS_COMPILE
563 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
566 virtual LPCWSTR GetStubManagerName(PCODE addr)
567 { LIMITED_METHOD_CONTRACT; return W("ThunkHeapStub"); }
572 // Stub manager for jump stubs created by ExecutionManager::jumpStub()
573 // These are currently used only on the 64-bit targets IA64 and AMD64
575 typedef VPTR(class JumpStubStubManager) PTR_JumpStubStubManager;
577 class JumpStubStubManager : public StubManager
579 VPTR_VTABLE_CLASS(JumpStubStubManager, StubManager)
583 SPTR_DECL(JumpStubStubManager, g_pManager);
587 #ifndef DACCESS_COMPILE
588 JumpStubStubManager() {LIMITED_METHOD_CONTRACT;}
589 ~JumpStubStubManager() {WRAPPER_NO_CONTRACT;}
594 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "JumpStubStubManager"; }
597 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
599 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
601 #ifdef DACCESS_COMPILE
602 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
605 virtual LPCWSTR GetStubManagerName(PCODE addr)
606 { LIMITED_METHOD_CONTRACT; return W("JumpStub"); }
611 // Stub manager for code sections. It forwards the query to the more appropriate
612 // stub manager, or handles the query itself.
614 typedef VPTR(class RangeSectionStubManager) PTR_RangeSectionStubManager;
616 class RangeSectionStubManager : public StubManager
618 VPTR_VTABLE_CLASS(RangeSectionStubManager, StubManager)
621 SPTR_DECL(RangeSectionStubManager, g_pManager);
625 #ifndef DACCESS_COMPILE
626 RangeSectionStubManager() {LIMITED_METHOD_CONTRACT;}
627 ~RangeSectionStubManager() {WRAPPER_NO_CONTRACT;}
630 static StubCodeBlockKind GetStubKind(PCODE stubStartAddress);
632 static PCODE GetMethodThunkTarget(PCODE stubStartAddress);
636 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "RangeSectionStubManager"; }
639 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
643 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
645 #ifndef DACCESS_COMPILE
646 virtual BOOL TraceManager(Thread *thread,
647 TraceDestination *trace,
652 #ifdef DACCESS_COMPILE
653 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
656 virtual LPCWSTR GetStubManagerName(PCODE addr);
661 // This is the stub manager for IL stubs.
663 typedef VPTR(class ILStubManager) PTR_ILStubManager;
665 #ifdef FEATURE_COMINTEROP
666 struct ComPlusCallInfo;
667 #endif // FEATURE_COMINTEROP
669 class ILStubManager : public StubManager
671 VPTR_VTABLE_CLASS(ILStubManager, StubManager)
676 #ifndef DACCESS_COMPILE
677 ILStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
684 CAN_TAKE_LOCK; // StubManager::UnlinkStubManager uses a crst
693 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ILStubManager"; }
696 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
700 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
702 #ifndef DACCESS_COMPILE
703 #ifdef FEATURE_COMINTEROP
704 static PCODE GetCOMTarget(Object *pThis, ComPlusCallInfo *pComPlusCallInfo);
705 static PCODE GetWinRTFactoryTarget(ComPlusCallMethodDesc *pCMD);
706 #endif // FEATURE_COMINTEROP
708 virtual BOOL TraceManager(Thread *thread,
709 TraceDestination *trace,
714 #ifdef DACCESS_COMPILE
715 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
718 virtual LPCWSTR GetStubManagerName(PCODE addr)
719 { LIMITED_METHOD_CONTRACT; return W("ILStub"); }
723 // This is used to recognize
724 // GenericComPlusCallStub()
725 // VarargPInvokeStub()
726 // GenericPInvokeCalliHelper()
727 typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager;
729 class InteropDispatchStubManager : public StubManager
731 VPTR_VTABLE_CLASS(InteropDispatchStubManager, StubManager)
736 #ifndef DACCESS_COMPILE
737 InteropDispatchStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
738 ~InteropDispatchStubManager() {WRAPPER_NO_CONTRACT;}
742 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "InteropDispatchStubManager"; }
745 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
749 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
751 #ifndef DACCESS_COMPILE
752 virtual BOOL TraceManager(Thread *thread,
753 TraceDestination *trace,
758 #ifdef DACCESS_COMPILE
759 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
762 virtual LPCWSTR GetStubManagerName(PCODE addr)
763 { LIMITED_METHOD_CONTRACT; return W("InteropDispatchStub"); }
768 // Since we don't generate delegate invoke stubs at runtime on WIN64, we
769 // can't use the StubLinkStubManager for these stubs. Instead, we create
770 // an additional DelegateInvokeStubManager instead.
772 typedef VPTR(class DelegateInvokeStubManager) PTR_DelegateInvokeStubManager;
774 class DelegateInvokeStubManager : public StubManager
776 VPTR_VTABLE_CLASS(DelegateInvokeStubManager, StubManager)
780 SPTR_DECL(DelegateInvokeStubManager, g_pManager);
784 #if !defined(DACCESS_COMPILE)
785 DelegateInvokeStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;}
786 ~DelegateInvokeStubManager() {WRAPPER_NO_CONTRACT;}
787 #endif // DACCESS_COMPILE
789 BOOL AddStub(Stub* pStub);
790 void RemoveStub(Stub* pStub);
793 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "DelegateInvokeStubManager"; }
796 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
798 #if !defined(DACCESS_COMPILE)
799 virtual BOOL TraceManager(Thread *thread, TraceDestination *trace, CONTEXT *pContext, BYTE **pRetAddr);
800 static BOOL TraceDelegateObject(BYTE *orDel, TraceDestination *trace);
801 #endif // DACCESS_COMPILE
805 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
808 LockedRangeList m_rangeList;
810 // Get dac-ized pointer to rangelist.
811 PTR_RangeList GetRangeList()
815 TADDR addr = PTR_HOST_MEMBER_TADDR(DelegateInvokeStubManager, this, m_rangeList);
816 return PTR_RangeList(addr);
820 #ifdef DACCESS_COMPILE
821 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
824 virtual LPCWSTR GetStubManagerName(PCODE addr)
825 { LIMITED_METHOD_CONTRACT; return W("DelegateInvokeStub"); }
829 //---------------------------------------------------------------------------------------
831 // This is the stub manager to help the managed debugger step into a tail call.
832 // It helps the debugger trace through JIT_TailCall().
835 typedef VPTR(class TailCallStubManager) PTR_TailCallStubManager;
837 class TailCallStubManager : public StubManager
839 VPTR_VTABLE_CLASS(TailCallStubManager, StubManager)
844 #if !defined(DACCESS_COMPILE)
845 TailCallStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
846 ~TailCallStubManager() {WRAPPER_NO_CONTRACT;}
848 virtual BOOL TraceManager(Thread * pThread, TraceDestination * pTrace, CONTEXT * pContext, BYTE ** ppRetAddr);
850 static bool IsTailCallStubHelper(PCODE code);
851 #endif // DACCESS_COMPILE
854 virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "TailCallStubManager"; }
857 virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
860 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination * pTrace);
862 #if defined(DACCESS_COMPILE)
863 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
866 virtual LPCWSTR GetStubManagerName(PCODE addr) {LIMITED_METHOD_CONTRACT; return W("TailCallStub");}
867 #endif // !DACCESS_COMPILE
871 // Helpers for common value locations in stubs to make stub managers more portable
873 class StubManagerHelpers
876 static PCODE GetReturnAddress(T_CONTEXT * pContext)
878 #if defined(_TARGET_X86_)
879 return *dac_cast<PTR_PCODE>(pContext->Esp);
880 #elif defined(_TARGET_AMD64_)
881 return *dac_cast<PTR_PCODE>(pContext->Rsp);
882 #elif defined(_TARGET_ARM_)
884 #elif defined(_TARGET_ARM64_)
887 PORTABILITY_ASSERT("StubManagerHelpers::GetReturnAddress");
892 static PTR_Object GetThisPtr(T_CONTEXT * pContext)
894 #if defined(_TARGET_X86_)
895 return dac_cast<PTR_Object>(pContext->Ecx);
896 #elif defined(_TARGET_AMD64_)
897 #ifdef UNIX_AMD64_ABI
898 return dac_cast<PTR_Object>(pContext->Rdi);
900 return dac_cast<PTR_Object>(pContext->Rcx);
902 #elif defined(_TARGET_ARM_)
903 return dac_cast<PTR_Object>(pContext->R0);
904 #elif defined(_TARGET_ARM64_)
905 return dac_cast<PTR_Object>(pContext->X0);
907 PORTABILITY_ASSERT("StubManagerHelpers::GetThisPtr");
912 static PCODE GetTailCallTarget(T_CONTEXT * pContext)
914 #if defined(_TARGET_X86_)
915 return pContext->Eax;
916 #elif defined(_TARGET_AMD64_)
917 return pContext->Rax;
918 #elif defined(_TARGET_ARM_)
919 return pContext->R12;
921 PORTABILITY_ASSERT("StubManagerHelpers::GetTailCallTarget");
926 static TADDR GetHiddenArg(T_CONTEXT * pContext)
928 #if defined(_TARGET_X86_)
929 return pContext->Eax;
930 #elif defined(_TARGET_AMD64_)
931 return pContext->R10;
932 #elif defined(_TARGET_ARM_)
933 return pContext->R12;
934 #elif defined(_TARGET_ARM64_)
935 return pContext->X15;
937 PORTABILITY_ASSERT("StubManagerHelpers::GetHiddenArg");
942 #ifndef CROSSGEN_COMPILE
943 static PCODE GetRetAddrFromMulticastILStubFrame(T_CONTEXT * pContext)
946 Following is the callstack corresponding to context received by ILStubManager::TraceManager.
947 This function returns the return address (user code address) where control should return after all
948 delegates in multicast delegate have been executed.
950 StubHelpers::MulticastDebuggerTraceHelper
951 IL_STUB_MulticastDelegate_Invoke
952 UserCode which invokes multicast delegate <---
955 #if defined(_TARGET_X86_)
956 return *((PCODE *)pContext->Ebp + 1);
957 #elif defined(_TARGET_AMD64_)
958 T_CONTEXT context(*pContext);
959 Thread::VirtualUnwindCallFrame(&context);
960 Thread::VirtualUnwindCallFrame(&context);
962 return pContext->Rip;
963 #elif defined(_TARGET_ARM_)
964 return *((PCODE *)pContext->R11 + 1);
965 #elif defined(_TARGET_ARM64_)
966 return *((PCODE *)pContext->Fp + 1);
968 PORTABILITY_ASSERT("StubManagerHelpers::GetRetAddrFromMulticastILStubFrame");
972 #endif // !CROSSGEN_COMPILE
974 static TADDR GetSecondArg(T_CONTEXT * pContext)
976 #if defined(_TARGET_X86_)
977 return pContext->Edx;
978 #elif defined(_TARGET_AMD64_)
979 #ifdef UNIX_AMD64_ABI
980 return pContext->Rsi;
982 return pContext->Rdx;
984 #elif defined(_TARGET_ARM_)
986 #elif defined(_TARGET_ARM_)
989 PORTABILITY_ASSERT("StubManagerHelpers::GetSecondArg");