summaryrefslogtreecommitdiff
path: root/src/vm/stubmgr.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/stubmgr.h')
-rw-r--r--src/vm/stubmgr.h998
1 files changed, 998 insertions, 0 deletions
diff --git a/src/vm/stubmgr.h b/src/vm/stubmgr.h
new file mode 100644
index 0000000000..f0098c9d55
--- /dev/null
+++ b/src/vm/stubmgr.h
@@ -0,0 +1,998 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// StubMgr.h
+//
+
+//
+// The stub manager exists so that the debugger can accurately step through
+// the myriad stubs & wrappers which exist in the EE, without imposing undue
+// overhead on the stubs themselves.
+//
+// Each type of stub (except those which the debugger can treat as atomic operations)
+// needs to have a stub manager to represent it. The stub manager is responsible for
+// (a) identifying the stub as such, and
+// (b) tracing into the stub & reporting what the stub will call. This
+// report can consist of
+// (i) a managed code address
+// (ii) an unmanaged code address
+// (iii) another stub address
+// (iv) a "frame patch" address - that is, an address in the stub,
+// which the debugger can patch. When the patch is hit, the debugger
+// will query the topmost frame to trace itself. (Thus this is
+// a way of deferring the trace logic to the frame which the stub
+// will push.)
+//
+// The set of stub managers is extensible, but should be kept to a reasonable number
+// as they are currently linearly searched & queried for each stub.
+//
+
+
+#ifndef __stubmgr_h__
+#define __stubmgr_h__
+
+#include "simplerwlock.hpp"
+
+// When 'TraceStub' returns, it gives the address of where the 'target' is for a stub'
+// TraceType indicates what this 'target' is
+enum TraceType
+{
+ TRACE_ENTRY_STUB, // Stub goes to an unmanaged entry stub
+ TRACE_STUB, // Stub goes to another stub
+ TRACE_UNMANAGED, // Stub goes to unmanaged code
+ TRACE_MANAGED, // Stub goes to Jitted code
+ TRACE_UNJITTED_METHOD, // Is the prestub, since there is no code, the address will actually be a MethodDesc*
+
+ TRACE_FRAME_PUSH, // Don't know where stub goes, stop at address, and then ask the frame that is on the stack
+ TRACE_MGR_PUSH, // Don't know where stub goes, stop at address then call TraceManager() below to find out
+
+ TRACE_OTHER // We are going somewhere you can't step into (eg. ee helper function)
+};
+
+class StubManager;
+class SString;
+
+class DebuggerRCThread;
+
+enum StubCodeBlockKind : int;
+
+// A TraceDestination describes where code is going to call. This can be used by the Debugger's Step-In functionality
+// to skip through stubs and place a patch directly at a call's target.
+// TD are supplied by the stubmanagers.
+class TraceDestination
+{
+public:
+ friend class DebuggerRCThread;
+
+ TraceDestination() { }
+
+#ifdef _DEBUG
+ // Get a string representation of this TraceDestination
+ // Uses the supplied buffer to store the memory (or may return a string literal).
+ // This will also print the TD's arguments.
+ const WCHAR * DbgToString(SString &buffer);
+#endif
+
+ // Initialize for unmanaged code.
+ // The addr is in unmanaged code. Used for Step-in from managed to native.
+ void InitForUnmanaged(PCODE addr)
+ {
+ STATIC_CONTRACT_SO_TOLERANT;
+ this->type = TRACE_UNMANAGED;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // The addr is inside jitted code (eg, there's a JitManaged that will claim it)
+ void InitForManaged(PCODE addr)
+ {
+ STATIC_CONTRACT_SO_TOLERANT;
+ this->type = TRACE_MANAGED;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // Initialize for an unmanaged entry stub.
+ void InitForUnmanagedStub(PCODE addr)
+ {
+ STATIC_CONTRACT_SO_TOLERANT;
+ this->type = TRACE_ENTRY_STUB;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // Initialize for a stub.
+ void InitForStub(PCODE addr)
+ {
+ STATIC_CONTRACT_SO_TOLERANT;
+ this->type = TRACE_STUB;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // Init for a managed unjitted method.
+ // This will place an IL patch that will get bound when the debugger gets a Jit complete
+ // notification for this method.
+ // If pDesc is a wrapper methoddesc, we will unwrap it.
+ void InitForUnjittedMethod(MethodDesc * pDesc);
+
+ // Place a patch at the given addr, and then when it's hit,
+ // call pStubManager->TraceManager() to get the next TraceDestination.
+ void InitForManagerPush(PCODE addr, StubManager * pStubManager)
+ {
+ STATIC_CONTRACT_SO_TOLERANT;
+ this->type = TRACE_MGR_PUSH;
+ this->address = addr;
+ this->stubManager = pStubManager;
+ }
+
+ // Place a patch at the given addr, and then when it's hit
+ // call GetThread()->GetFrame()->TraceFrame() to get the next TraceDestination.
+ // This address must be safe to run a callstack at.
+ void InitForFramePush(PCODE addr)
+ {
+ this->type = TRACE_FRAME_PUSH;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // Nobody recognized the target address. We will not be able to step-in to it.
+ // This is ok if the target just calls into mscorwks (such as an Fcall) because
+ // there's no managed code to step in to, and we don't support debugging the CLR
+ // itself, so there's no native code to step into either.
+ void InitForOther(PCODE addr)
+ {
+ this->type = TRACE_OTHER;
+ this->address = addr;
+ this->stubManager = NULL;
+ }
+
+ // Accessors
+ TraceType GetTraceType() { return type; }
+ PCODE GetAddress()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(type != TRACE_UNJITTED_METHOD);
+ return address;
+ }
+ MethodDesc* GetMethodDesc()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(type == TRACE_UNJITTED_METHOD);
+ return pDesc;
+ }
+
+ StubManager * GetStubManager()
+ {
+ return stubManager;
+ }
+
+ // Expose this b/c DebuggerPatchTable::AddPatchForAddress() needs it.
+ // Ideally we'd get rid of this.
+ void Bad_SetTraceType(TraceType t)
+ {
+ this->type = t;
+ }
+private:
+ TraceType type; // The kind of code the stub is going to
+ PCODE address; // Where the stub is going
+ StubManager *stubManager; // The manager that claims this stub
+ MethodDesc *pDesc;
+};
+
+// For logging
+#ifdef LOGGING
+ void LogTraceDestination(const char * szHint, PCODE stubAddr, TraceDestination * pTrace);
+ #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint) LogTraceDestination(_stHint, stubAddr, _tracedestination)
+#else
+ #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint)
+#endif
+
+typedef VPTR(class StubManager) PTR_StubManager;
+
+class StubManager
+{
+#ifndef CROSSGEN_COMPILE
+ friend class StubManagerIterator;
+
+ VPTR_BASE_VTABLE_CLASS(StubManager)
+
+ public:
+ // Startup and shutdown the global stubmanager service.
+ static void InitializeStubManagers();
+ static void TerminateStubManagers();
+
+ // Does any sub manager recognise this EIP?
+ static BOOL IsStub(PCODE stubAddress)
+ {
+ WRAPPER_NO_CONTRACT;
+ return FindStubManager(stubAddress) != NULL;
+ }
+
+ // Find stub manager for given code address
+ static PTR_StubManager FindStubManager(PCODE stubAddress);
+
+ // Look for stubAddress, if found return TRUE, and set 'trace' to
+ static BOOL TraceStub(PCODE stubAddress, TraceDestination *trace);
+
+ // if 'trace' indicates TRACE_STUB, keep calling TraceStub on 'trace', until you get out of all stubs
+ // returns true if successfull
+ static BOOL FollowTrace(TraceDestination *trace);
+
+#ifdef DACCESS_COMPILE
+ static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+#endif
+
+ static void AddStubManager(StubManager *mgr);
+
+ // NOTE: Very important when using this. It is not thread safe, except in this very
+ // limited scenario: the thread must have the runtime suspended.
+ static void UnlinkStubManager(StubManager *mgr);
+
+#ifndef DACCESS_COMPILE
+ StubManager();
+ virtual ~StubManager();
+#endif
+
+
+#ifdef _DEBUG
+ // Debug helper to help identify stub-managers. Make it pure to force stub managers to implement it.
+ virtual const char * DbgGetName() = 0;
+#endif
+
+ // Only Stubmanagers that return 'TRACE_MGR_PUSH' as a trace type need to implement this function
+ // Fills in 'trace' (the target), and 'pRetAddr' (the method that called the stub) (this is needed
+ // as a 'fall back' so that the debugger can at least stop when the stub returns.
+ virtual BOOL TraceManager(Thread *thread, TraceDestination *trace,
+ T_CONTEXT *pContext, BYTE **pRetAddr)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(!"Default impl of TraceManager should never be called!");
+ return FALSE;
+ }
+
+ // The worker for IsStub. This calls CheckIsStub_Internal, but wraps it w/
+ // a try-catch.
+ BOOL CheckIsStub_Worker(PCODE stubStartAddress);
+
+
+
+#ifdef _DEBUG
+public:
+ //-----------------------------------------------------------------------------
+ // Debugging Stubmanager bugs is very painful. You need to figure out
+ // how you go to where you got and which stub-manager is at fault.
+ // To help with this, we track a rolling log so that we can give very
+ // informative asserts. this log is not thread-safe, but we really only expect
+ // a single stub-manager usage at a time.
+ //
+ // A stub manager for a step-in operation may be used across
+ // both the helper thread and then the managed thread doing the step-in.
+ // These threads will coordinate to have exclusive access (helper will only access
+ // when stopped; and managed thread will only access when running).
+ //
+ // It's also possible (but rare) for a single thread to have multiple step-in operations.
+ // Since that's so rare, no present need to expand our logging to support it.
+ //-----------------------------------------------------------------------------
+
+
+ static bool IsStubLoggingEnabled();
+
+ // Call to reset the log. This is used at the start of a new step-operation.
+ static void DbgBeginLog(TADDR addrCallInstruction, TADDR addrCallTarget);
+ static void DbgFinishLog();
+
+ // Log arbitrary string. This is a nop if it's outside the Begin/Finish window.
+ // We could consider making each log entry type-safe (and thus avoid the string operations).
+ static void DbgWriteLog(const CHAR *format, ...);
+
+ // Get the log as a string.
+ static void DbgGetLog(SString * pStringOut);
+
+protected:
+ // Implement log as a SString.
+ static SString * s_pDbgStubManagerLog;
+
+ static CrstStatic s_DbgLogCrst;
+
+#endif
+
+
+protected:
+
+ // Each stubmanaged implements this.
+ // This may throw, AV, etc depending on the implementation. This should not
+ // be called directly unless you know exactly what you're doing.
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress) = 0;
+
+ // The worker for TraceStub
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) = 0;
+
+#ifdef _DEBUG_IMPL
+ static BOOL IsSingleOwner(PCODE stubAddress, StubManager * pOwner);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+public:
+ // This is used by DAC to provide more information on who owns a stub.
+ virtual LPCWSTR GetStubManagerName(PCODE addr) = 0;
+#endif
+
+private:
+ SPTR_DECL(StubManager, g_pFirstManager);
+ PTR_StubManager m_pNextManager;
+
+ static CrstStatic s_StubManagerListCrst;
+#endif // !CROSSGEN_COMPILE
+};
+
+// -------------------------------------------------------
+// This just wraps the RangeList methods in a read or
+// write lock depending on the operation.
+// -------------------------------------------------------
+
+class LockedRangeList : public RangeList
+{
+ public:
+ VPTR_VTABLE_CLASS(LockedRangeList, RangeList)
+
+ LockedRangeList() : RangeList(), m_RangeListRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ ~LockedRangeList()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ protected:
+
+ virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id)
+ {
+ WRAPPER_NO_CONTRACT;
+ SimpleWriteLockHolder lh(&m_RangeListRWLock);
+ return RangeList::AddRangeWorker(start,end,id);
+ }
+
+ virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL)
+ {
+ WRAPPER_NO_CONTRACT;
+ SimpleWriteLockHolder lh(&m_RangeListRWLock);
+ RangeList::RemoveRangesWorker(id,start,end);
+ }
+
+ virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL)
+ {
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ SimpleReadLockHolder lh(&m_RangeListRWLock);
+ return RangeList::IsInRangeWorker(address, pID);
+ }
+
+ SimpleRWLock m_RangeListRWLock;
+};
+
+#ifndef CROSSGEN_COMPILE
+
+//-----------------------------------------------------------
+// Stub manager for the prestub. Although there is just one, it has
+// unique behavior so it gets its own stub manager.
+//-----------------------------------------------------------
+class ThePreStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(ThePreStubManager, StubManager)
+
+ public:
+#ifndef DACCESS_COMPILE
+ ThePreStubManager() { LIMITED_METHOD_CONTRACT; }
+#endif
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThePreStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifndef DACCESS_COMPILE
+ static void Init(void);
+#endif
+
+#ifdef DACCESS_COMPILE
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("ThePreStub"); }
+#endif
+};
+
+// -------------------------------------------------------
+// Stub manager classes for method desc prestubs & normal
+// frame-pushing, StubLinker created stubs
+// -------------------------------------------------------
+
+typedef VPTR(class PrecodeStubManager) PTR_PrecodeStubManager;
+
+class PrecodeStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(PrecodeStubManager, StubManager)
+
+ public:
+
+ SPTR_DECL(PrecodeStubManager, g_pManager);
+
+#ifdef _DEBUG
+ // Debug helper to help identify stub-managers.
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "PrecodeStubManager"; }
+#endif
+
+
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ PrecodeStubManager() {LIMITED_METHOD_CONTRACT;}
+ ~PrecodeStubManager() {WRAPPER_NO_CONTRACT;}
+#endif
+
+ public:
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+#ifndef DACCESS_COMPILE
+ virtual BOOL TraceManager(Thread *thread,
+ TraceDestination *trace,
+ CONTEXT *pContext,
+ BYTE **pRetAddr);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("MethodDescPrestub"); }
+#endif
+};
+
+// Note that this stub was written by a debugger guy, and thus when he refers to 'multicast'
+// stub, he really means multi or single cast stub. This was done b/c the same stub
+// services both types of stub.
+// Note from the debugger guy: the way to understand what this manager does is to
+// first grok EmitMulticastInvoke for the platform you're working on (right now, just x86).
+// Then return here, and understand that (for x86) the only way we know which method
+// we're going to invoke next is by inspecting EDI when we've got the debuggee stopped
+// in the stub, and so our trace frame will either (FRAME_PUSH) put a breakpoint
+// in the stub, or (if we hit the BP) examine EDI, etc, & figure out where we're going next.
+
+typedef VPTR(class StubLinkStubManager) PTR_StubLinkStubManager;
+
+class StubLinkStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(StubLinkStubManager, StubManager)
+
+ public:
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "StubLinkStubManager"; }
+#endif
+
+
+ SPTR_DECL(StubLinkStubManager, g_pManager);
+
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ StubLinkStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;}
+ ~StubLinkStubManager() {WRAPPER_NO_CONTRACT;}
+#endif
+
+ protected:
+ LockedRangeList m_rangeList;
+ public:
+ // Get dac-ized pointer to rangelist.
+ PTR_RangeList GetRangeList()
+ {
+ SUPPORTS_DAC;
+
+ TADDR addr = PTR_HOST_MEMBER_TADDR(StubLinkStubManager, this, m_rangeList);
+ return PTR_RangeList(addr);
+ }
+
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+#ifndef DACCESS_COMPILE
+ virtual BOOL TraceManager(Thread *thread,
+ TraceDestination *trace,
+ CONTEXT *pContext,
+ BYTE **pRetAddr);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("StubLinkStub"); }
+#endif
+} ;
+
+// Stub manager for thunks.
+
+typedef VPTR(class ThunkHeapStubManager) PTR_ThunkHeapStubManager;
+
+class ThunkHeapStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(ThunkHeapStubManager, StubManager)
+
+ public:
+
+ SPTR_DECL(ThunkHeapStubManager, g_pManager);
+
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ ThunkHeapStubManager() : StubManager(), m_rangeList() { LIMITED_METHOD_CONTRACT; }
+ ~ThunkHeapStubManager() {WRAPPER_NO_CONTRACT;}
+#endif
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThunkHeapStubManager"; }
+#endif
+
+ protected:
+ LockedRangeList m_rangeList;
+ public:
+ // Get dac-ized pointer to rangelist.
+ PTR_RangeList GetRangeList()
+ {
+ SUPPORTS_DAC;
+ TADDR addr = PTR_HOST_MEMBER_TADDR(ThunkHeapStubManager, this, m_rangeList);
+ return PTR_RangeList(addr);
+ }
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ private:
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("ThunkHeapStub"); }
+#endif
+};
+
+//
+// Stub manager for jump stubs created by ExecutionManager::jumpStub()
+// These are currently used only on the 64-bit targets IA64 and AMD64
+//
+typedef VPTR(class JumpStubStubManager) PTR_JumpStubStubManager;
+
+class JumpStubStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(JumpStubStubManager, StubManager)
+
+ public:
+
+ SPTR_DECL(JumpStubStubManager, g_pManager);
+
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ JumpStubStubManager() {LIMITED_METHOD_CONTRACT;}
+ ~JumpStubStubManager() {WRAPPER_NO_CONTRACT;}
+
+#endif
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "JumpStubStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("JumpStub"); }
+#endif
+};
+
+//
+// Stub manager for code sections. It forwards the query to the more appropriate
+// stub manager, or handles the query itself.
+//
+typedef VPTR(class RangeSectionStubManager) PTR_RangeSectionStubManager;
+
+class RangeSectionStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(RangeSectionStubManager, StubManager)
+
+ public:
+ SPTR_DECL(RangeSectionStubManager, g_pManager);
+
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ RangeSectionStubManager() {LIMITED_METHOD_CONTRACT;}
+ ~RangeSectionStubManager() {WRAPPER_NO_CONTRACT;}
+#endif
+
+ static StubCodeBlockKind GetStubKind(PCODE stubStartAddress);
+
+ static PCODE GetMethodThunkTarget(PCODE stubStartAddress);
+
+ public:
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "RangeSectionStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ private:
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifndef DACCESS_COMPILE
+ virtual BOOL TraceManager(Thread *thread,
+ TraceDestination *trace,
+ CONTEXT *pContext,
+ BYTE **pRetAddr);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr);
+#endif
+};
+
+//
+// This is the stub manager for IL stubs.
+//
+typedef VPTR(class ILStubManager) PTR_ILStubManager;
+
+#ifdef FEATURE_COMINTEROP
+struct ComPlusCallInfo;
+#endif // FEATURE_COMINTEROP
+
+class ILStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(ILStubManager, StubManager)
+
+ public:
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ ILStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
+ ~ILStubManager()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK; // StubManager::UnlinkStubManager uses a crst
+ }
+ CONTRACTL_END;
+ }
+#endif
+
+ public:
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ILStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ private:
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_COMINTEROP
+ static PCODE GetCOMTarget(Object *pThis, ComPlusCallInfo *pComPlusCallInfo);
+ static PCODE GetWinRTFactoryTarget(ComPlusCallMethodDesc *pCMD);
+#endif // FEATURE_COMINTEROP
+
+ virtual BOOL TraceManager(Thread *thread,
+ TraceDestination *trace,
+ CONTEXT *pContext,
+ BYTE **pRetAddr);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("ILStub"); }
+#endif
+};
+
+// This is used to recognize
+// GenericComPlusCallStub()
+// VarargPInvokeStub()
+// GenericPInvokeCalliHelper()
+typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager;
+
+class InteropDispatchStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(InteropDispatchStubManager, StubManager)
+
+ public:
+ static void Init();
+
+#ifndef DACCESS_COMPILE
+ InteropDispatchStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
+ ~InteropDispatchStubManager() {WRAPPER_NO_CONTRACT;}
+#endif
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "InteropDispatchStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+ private:
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+#ifndef DACCESS_COMPILE
+ virtual BOOL TraceManager(Thread *thread,
+ TraceDestination *trace,
+ CONTEXT *pContext,
+ BYTE **pRetAddr);
+#endif
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("InteropDispatchStub"); }
+#endif
+};
+
+//
+// Since we don't generate delegate invoke stubs at runtime on WIN64, we
+// can't use the StubLinkStubManager for these stubs. Instead, we create
+// an additional DelegateInvokeStubManager instead.
+//
+typedef VPTR(class DelegateInvokeStubManager) PTR_DelegateInvokeStubManager;
+
+class DelegateInvokeStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(DelegateInvokeStubManager, StubManager)
+
+ public:
+
+ SPTR_DECL(DelegateInvokeStubManager, g_pManager);
+
+ static void Init();
+
+#if !defined(DACCESS_COMPILE)
+ DelegateInvokeStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;}
+ ~DelegateInvokeStubManager() {WRAPPER_NO_CONTRACT;}
+#endif // DACCESS_COMPILE
+
+ BOOL AddStub(Stub* pStub);
+ void RemoveStub(Stub* pStub);
+
+#ifdef _DEBUG
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "DelegateInvokeStubManager"; }
+#endif
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+#if !defined(DACCESS_COMPILE)
+ virtual BOOL TraceManager(Thread *thread, TraceDestination *trace, CONTEXT *pContext, BYTE **pRetAddr);
+ static BOOL TraceDelegateObject(BYTE *orDel, TraceDestination *trace);
+#endif // DACCESS_COMPILE
+
+ private:
+
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace);
+
+ protected:
+ LockedRangeList m_rangeList;
+ public:
+ // Get dac-ized pointer to rangelist.
+ PTR_RangeList GetRangeList()
+ {
+ SUPPORTS_DAC;
+
+ TADDR addr = PTR_HOST_MEMBER_TADDR(DelegateInvokeStubManager, this, m_rangeList);
+ return PTR_RangeList(addr);
+ }
+
+
+#ifdef DACCESS_COMPILE
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+ protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr)
+ { LIMITED_METHOD_CONTRACT; return W("DelegateInvokeStub"); }
+#endif
+};
+
+//---------------------------------------------------------------------------------------
+//
+// This is the stub manager to help the managed debugger step into a tail call.
+// It helps the debugger trace through JIT_TailCall().
+//
+
+typedef VPTR(class TailCallStubManager) PTR_TailCallStubManager;
+
+class TailCallStubManager : public StubManager
+{
+ VPTR_VTABLE_CLASS(TailCallStubManager, StubManager)
+
+public:
+ static void Init();
+
+#if !defined(DACCESS_COMPILE)
+ TailCallStubManager() : StubManager() {WRAPPER_NO_CONTRACT;}
+ ~TailCallStubManager() {WRAPPER_NO_CONTRACT;}
+
+ virtual BOOL TraceManager(Thread * pThread, TraceDestination * pTrace, CONTEXT * pContext, BYTE ** ppRetAddr);
+
+ static bool IsTailCallStubHelper(PCODE code);
+#endif // DACCESS_COMPILE
+
+#if defined(_DEBUG)
+ virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "TailCallStubManager"; }
+#endif // _DEBUG
+
+ virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress);
+
+private:
+ virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination * pTrace);
+
+#if defined(DACCESS_COMPILE)
+ virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
+
+protected:
+ virtual LPCWSTR GetStubManagerName(PCODE addr) {LIMITED_METHOD_CONTRACT; return W("TailCallStub");}
+#endif // !DACCESS_COMPILE
+};
+
+//
+// Helpers for common value locations in stubs to make stub managers more portable
+//
+class StubManagerHelpers
+{
+public:
+ static PCODE GetReturnAddress(T_CONTEXT * pContext)
+ {
+#if defined(_TARGET_X86_)
+ return *dac_cast<PTR_PCODE>(pContext->Esp);
+#elif defined(_TARGET_AMD64_)
+ return *dac_cast<PTR_PCODE>(pContext->Rsp);
+#elif defined(_TARGET_ARM_)
+ return pContext->Lr;
+#elif defined(_TARGET_ARM64_)
+ return pContext->Lr;
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetReturnAddress");
+ return NULL;
+#endif
+ }
+
+ static PTR_Object GetThisPtr(T_CONTEXT * pContext)
+ {
+#if defined(_TARGET_X86_)
+ return dac_cast<PTR_Object>(pContext->Ecx);
+#elif defined(_TARGET_AMD64_)
+#ifdef UNIX_AMD64_ABI
+ return dac_cast<PTR_Object>(pContext->Rdi);
+#else
+ return dac_cast<PTR_Object>(pContext->Rcx);
+#endif
+#elif defined(_TARGET_ARM_)
+ return dac_cast<PTR_Object>(pContext->R0);
+#elif defined(_TARGET_ARM64_)
+ return dac_cast<PTR_Object>(pContext->X0);
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetThisPtr");
+ return NULL;
+#endif
+ }
+
+ static PCODE GetTailCallTarget(T_CONTEXT * pContext)
+ {
+#if defined(_TARGET_X86_)
+ return pContext->Eax;
+#elif defined(_TARGET_AMD64_)
+ return pContext->Rax;
+#elif defined(_TARGET_ARM_)
+ return pContext->R12;
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetTailCallTarget");
+ return NULL;
+#endif
+ }
+
+ static TADDR GetHiddenArg(T_CONTEXT * pContext)
+ {
+#if defined(_TARGET_X86_)
+ return pContext->Eax;
+#elif defined(_TARGET_AMD64_)
+ return pContext->R10;
+#elif defined(_TARGET_ARM_)
+ return pContext->R12;
+#elif defined(_TARGET_ARM64_)
+ return pContext->X12;
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetHiddenArg");
+ return NULL;
+#endif
+ }
+
+#ifndef CROSSGEN_COMPILE
+ static PCODE GetRetAddrFromMulticastILStubFrame(T_CONTEXT * pContext)
+ {
+ /*
+ Following is the callstack corresponding to context received by ILStubManager::TraceManager.
+ This function returns the return address (user code address) where control should return after all
+ delegates in multicast delegate have been executed.
+
+ StubHelpers::MulticastDebuggerTraceHelper
+ IL_STUB_MulticastDelegate_Invoke
+ UserCode which invokes multicast delegate <---
+ */
+
+#if defined(_TARGET_X86_)
+ return *((PCODE *)pContext->Ebp + 1);
+#elif defined(_TARGET_AMD64_)
+ T_CONTEXT context(*pContext);
+ Thread::VirtualUnwindCallFrame(&context);
+ Thread::VirtualUnwindCallFrame(&context);
+
+ return pContext->Rip;
+#elif defined(_TARGET_ARM_)
+ return *((PCODE *)pContext->R11 + 1);
+#elif defined(_TARGET_ARM64_)
+ return *((PCODE *)pContext->Fp + 1);
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetRetAddrFromMulticastILStubFrame");
+ return NULL;
+#endif
+ }
+#endif // !CROSSGEN_COMPILE
+
+ static TADDR GetSecondArg(T_CONTEXT * pContext)
+ {
+#if defined(_TARGET_X86_)
+ return pContext->Edx;
+#elif defined(_TARGET_AMD64_)
+#ifdef UNIX_AMD64_ABI
+ return pContext->Rsi;
+#else
+ return pContext->Rdx;
+#endif
+#elif defined(_TARGET_ARM_)
+ return pContext->R1;
+#elif defined(_TARGET_ARM_)
+ return pContext->X1;
+#else
+ PORTABILITY_ASSERT("StubManagerHelpers::GetSecondArg");
+ return NULL;
+#endif
+ }
+
+};
+
+#endif // !CROSSGEN_COMPILE
+#endif // !__stubmgr_h__