diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 4e8f7e1f7959be..8fcc1cced7d439 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -94,7 +94,6 @@ DEFINE_DACVAR(PTR_StubLinkStubManager, StubLinkStubManager__g_pManager, StubLink DEFINE_DACVAR(PTR_ThunkHeapStubManager, ThunkHeapStubManager__g_pManager, ThunkHeapStubManager::g_pManager) DEFINE_DACVAR(PTR_JumpStubStubManager, JumpStubStubManager__g_pManager, JumpStubStubManager::g_pManager) DEFINE_DACVAR(PTR_RangeSectionStubManager, RangeSectionStubManager__g_pManager, RangeSectionStubManager::g_pManager) -DEFINE_DACVAR(PTR_DelegateInvokeStubManager, DelegateInvokeStubManager__g_pManager, DelegateInvokeStubManager::g_pManager) DEFINE_DACVAR(PTR_VirtualCallStubManagerManager, VirtualCallStubManagerManager__g_pManager, VirtualCallStubManagerManager::g_pManager) DEFINE_DACVAR(PTR_CallCountingStubManager, CallCountingStubManager__g_pManager, CallCountingStubManager::g_pManager) diff --git a/src/coreclr/inc/vptr_list.h b/src/coreclr/inc/vptr_list.h index 4683dd86e65129..718c7edf7afbf2 100644 --- a/src/coreclr/inc/vptr_list.h +++ b/src/coreclr/inc/vptr_list.h @@ -34,7 +34,6 @@ VPTR_CLASS(JumpStubStubManager) VPTR_CLASS(RangeSectionStubManager) VPTR_CLASS(ILStubManager) VPTR_CLASS(InteropDispatchStubManager) -VPTR_CLASS(DelegateInvokeStubManager) #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) VPTR_CLASS(TailCallStubManager) #endif diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index b210c25ed382f6..ae6de5be009110 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -956,7 +956,6 @@ void SystemDomain::Attach() // Initialize stub managers PrecodeStubManager::Init(); - DelegateInvokeStubManager::Init(); JumpStubStubManager::Init(); RangeSectionStubManager::Init(); ILStubManager::Init(); diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp index ebe0e686d4bdfb..3670c7e8446bda 100644 --- a/src/coreclr/vm/array.cpp +++ b/src/coreclr/vm/array.cpp @@ -1124,7 +1124,7 @@ void GenerateArrayOpScript(ArrayMethodDesc *pMD, ArrayOpScript *paos) //--------------------------------------------------------- class ArrayStubCache : public StubCacheBase { - virtual void CompileStub(const BYTE *pRawStub, + virtual DWORD CompileStub(const BYTE *pRawStub, StubLinker *psl); virtual UINT Length(const BYTE *pRawStub); @@ -1169,12 +1169,13 @@ Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD) return pArrayOpStub; } -void ArrayStubCache::CompileStub(const BYTE *pRawStub, +DWORD ArrayStubCache::CompileStub(const BYTE *pRawStub, StubLinker *psl) { STANDARD_VM_CONTRACT; ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub); + return NEWSTUB_FL_NONE; } UINT ArrayStubCache::Length(const BYTE *pRawStub) diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index faf9483aa45ab9..9c4d864fb6ec3b 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -146,12 +146,16 @@ void EEClass::Destruct(MethodTable * pOwningMT) if (pDelegateEEClass->m_pStaticCallStub) { + // Collect data to remove stub entry from StubManager if + // stub is deleted. + BYTE* entry = (BYTE*)pDelegateEEClass->m_pStaticCallStub->GetEntryPoint(); + UINT length = pDelegateEEClass->m_pStaticCallStub->GetNumCodeBytes(); + ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pStaticCallStub, sizeof(Stub)); BOOL fStubDeleted = stubWriterHolder.GetRW()->DecRef(); - if (fStubDeleted) { - DelegateInvokeStubManager::g_pManager->RemoveStub(pDelegateEEClass->m_pStaticCallStub); + StubLinkStubManager::g_pManager->RemoveStubRange(entry, length); } } if (pDelegateEEClass->m_pInstRetBuffCallStub) diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 4f693ccae82748..f910e27fbf9424 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -37,7 +37,6 @@ class COMDelegate // friend VOID CPUSTUBLINKER::EmitMulticastInvoke(...); // friend VOID CPUSTUBLINKER::EmitShuffleThunk(...); friend class CPUSTUBLINKER; - friend class DelegateInvokeStubManager; friend BOOL MulticastFrame::TraceFrame(Thread *thread, BOOL fromPatch, TraceDestination *trace, REGDISPLAY *regs); @@ -213,12 +212,13 @@ class ShuffleThunkCache : public StubCacheBase // Compile a static delegate shufflethunk. Always returns // STANDALONE since we don't interpret these things. //--------------------------------------------------------- - virtual void CompileStub(const BYTE *pRawStub, + virtual DWORD CompileStub(const BYTE *pRawStub, StubLinker *pstublinker) { STANDARD_VM_CONTRACT; ((CPUSTUBLINKER*)pstublinker)->EmitShuffleThunk((ShuffleEntry*)pRawStub); + return NEWSTUB_FL_THUNK; } //--------------------------------------------------------- @@ -234,19 +234,6 @@ class ShuffleThunkCache : public StubCacheBase } return sizeof(ShuffleEntry) * (UINT)(1 + (pse - (ShuffleEntry*)pRawStub)); } - - virtual void AddStub(const BYTE* pRawStub, Stub* pNewStub) - { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - DelegateInvokeStubManager::g_pManager->AddStub(pNewStub); - } }; #endif // _COMDELEGATE_H_ diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index fbdafacc16d2e5..4cccc1e6dfc429 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1925,7 +1925,7 @@ BOOL MulticastFrame::TraceFrame(Thread *thread, BOOL fromPatch, ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount); _ASSERTE(pbDel); - return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace); + return StubLinkStubManager::TraceDelegateObject(pbDel, trace); } #endif // !DACCESS_COMPILE } diff --git a/src/coreclr/vm/stubcache.cpp b/src/coreclr/vm/stubcache.cpp index 8a415001ef1a59..44e093a9c1a431 100644 --- a/src/coreclr/vm/stubcache.cpp +++ b/src/coreclr/vm/stubcache.cpp @@ -108,14 +108,14 @@ Stub *StubCacheBase::Canonicalize(const BYTE * pRawStub) // Couldn't find it, let's try to compile it. CPUSTUBLINKER sl; CPUSTUBLINKER *psl = &sl; - CompileStub(pRawStub, psl); + DWORD linkFlags = CompileStub(pRawStub, psl); // Append the raw stub to the native stub // and link up the stub. CodeLabel *plabel = psl->EmitNewCodeLabel(); psl->EmitBytes(pRawStub, Length(pRawStub)); StubHolder pstub; - pstub = psl->Link(m_heap); + pstub = psl->Link(m_heap, linkFlags); UINT32 offset = psl->GetLabelOffset(plabel); if (offset > 0xffff) diff --git a/src/coreclr/vm/stubcache.h b/src/coreclr/vm/stubcache.h index 5954851173d19c..d35110c76b018a 100644 --- a/src/coreclr/vm/stubcache.h +++ b/src/coreclr/vm/stubcache.h @@ -67,13 +67,9 @@ class StubCacheBase : private CClosedHashBase // This method should compile into the provided stublinker (but // not call the Link method.) // - // It should return the chosen compilation mode. - // - // If the method fails for some reason, it should return - // INTERPRETED so that the EE can fall back on the already - // created ML code. + // It can return flags that will be passed to StubLinker::Link(). //--------------------------------------------------------- - virtual void CompileStub(const BYTE *pRawStub, + virtual DWORD CompileStub(const BYTE *pRawStub, StubLinker *psl) = 0; //--------------------------------------------------------- diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index db228659c39774..bd66bad8cd630a 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -2231,6 +2231,8 @@ void Stub::SetupStub(int numCodeBytes, DWORD flags m_numCodeBytesAndFlags |= EXTERNAL_ENTRY_BIT; if ((flags & NEWSTUB_FL_INSTANTIATING_METHOD) != 0) m_numCodeBytesAndFlags |= INSTANTIATING_STUB_BIT; + if ((flags & NEWSTUB_FL_THUNK) != 0) + m_numCodeBytesAndFlags |= THUNK_BIT; } #ifdef STUBLINKER_GENERATES_UNWIND_INFO diff --git a/src/coreclr/vm/stublink.h b/src/coreclr/vm/stublink.h index 8b6a73325df37a..9151e48fe05606 100644 --- a/src/coreclr/vm/stublink.h +++ b/src/coreclr/vm/stublink.h @@ -439,7 +439,8 @@ enum NewStubFlags NEWSTUB_FL_INSTANTIATING_METHOD = 0x00000001, NEWSTUB_FL_MULTICAST = 0x00000002, NEWSTUB_FL_EXTERNAL = 0x00000004, - NEWSTUB_FL_LOADERHEAP = 0x00000008 + NEWSTUB_FL_LOADERHEAP = 0x00000008, + NEWSTUB_FL_THUNK = 0x00000010 }; @@ -464,6 +465,7 @@ class Stub LOADER_HEAP_BIT = 0x20000000, INSTANTIATING_STUB_BIT = 0x10000000, UNWIND_INFO_BIT = 0x08000000, + THUNK_BIT = 0x04000000, CODEBYTES_MASK = UNWIND_INFO_BIT - 1, MAX_CODEBYTES = CODEBYTES_MASK + 1, @@ -513,6 +515,15 @@ class Stub return (m_numCodeBytesAndFlags & INSTANTIATING_STUB_BIT) != 0; } + //------------------------------------------------------------------- + // Used by the debugger to help step through stubs + //------------------------------------------------------------------- + BOOL IsManagedThunk() + { + LIMITED_METHOD_CONTRACT; + return (m_numCodeBytesAndFlags & THUNK_BIT) != 0; + } + //------------------------------------------------------------------- // For stubs which execute user code, a patch offset needs to be set // to tell the debugger how far into the stub code the debugger has diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index ee757227c72a19..d6258ac5fd5911 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1137,7 +1137,7 @@ SPTR_IMPL(StubLinkStubManager, StubLinkStubManager, g_pManager); #ifndef DACCESS_COMPILE -/* static */ +// static void StubLinkStubManager::Init() { CONTRACTL @@ -1152,16 +1152,113 @@ void StubLinkStubManager::Init() StubManager::AddStubManager(g_pManager); } -#endif // #ifndef DACCESS_COMPILE +// static +BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *trace) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; -BOOL StubLinkStubManager::CheckIsStub_Internal(PCODE stubStartAddress) + BYTE **ppbDest; + + // If we got here, then we're here b/c we're at the start of a delegate stub + // need to figure out the kind of delegates we are dealing with. + BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); + + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: invocationList: %p\n", pbDelInvocationList)); + + if (pbDelInvocationList == NULL) + { + // A null invocationList can be one of the following: + // - Instance closed, Instance open non-virt, Instance open virtual, Static closed, Static opened, Unmanaged FtnPtr + // - Instance open virtual is complex and we need to figure out what to do (TODO). + // For the others the logic is the following: + // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux + + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); + if (*ppbDest == NULL) + { + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr()); + + if (*ppbDest == NULL) + { + // it's not looking good, bail out + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: can't trace into it\n")); + return FALSE; + } + } + + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: ppbDest: %p *ppbDest:%p\n", ppbDest, *ppbDest)); + + BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); + + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %s, result type: %d\n", (res ? "true" : "false"), trace->GetTraceType())); + + return res; + } + + // invocationList is not null, so it can be one of the following: + // Multicast, Static closed (special sig), Secure + + // rule out the static with special sig + BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount()); + if (pbCount == NULL) + { + // it's a static closed, the target lives in _methodAuxPtr + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); + + if (*ppbDest == NULL) + { + // it's not looking good, bail out + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: can't trace into it\n")); + return FALSE; + } + + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: ppbDest: %p *ppbDest:%p\n", ppbDest, *ppbDest)); + + BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); + + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %d, result type: %d\n", (res ? "true" : "false"), trace->GetTraceType())); + + return res; + } + + MethodTable *pType = *(MethodTable**)pbDelInvocationList; + if (pType->IsDelegate()) + { + // this is a secure delegate. The target is hidden inside this field, so recurse. + return TraceDelegateObject(pbDelInvocationList, trace); + } + + // Otherwise, we're going for the first invoke of the multi case. + // In order to go to the correct spot, we have just have to fish out + // slot 0 of the invocation list, and figure out where that's going to, + // then put a breakpoint there. + pbDel = *(BYTE**)(((ArrayBase *)pbDelInvocationList)->GetDataPtr()); + return TraceDelegateObject(pbDel, trace); +} + +#endif // DACCESS_COMPILE + +void StubLinkStubManager::RemoveStubRange(BYTE* start, UINT length) { WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; + _ASSERTE(start != NULL && length > 0); - return GetRangeList()->IsInRange(stubStartAddress); + BYTE* end = start + length; + GetRangeList()->RemoveRanges((LPVOID)start, start, end); } +BOOL StubLinkStubManager::CheckIsStub_Internal(PCODE stubStartAddress) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return GetRangeList()->IsInRange(stubStartAddress) ? TRUE : FALSE; +} BOOL StubLinkStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) @@ -1208,6 +1305,12 @@ BOOL StubLinkStubManager::DoTraceStub(PCODE stubStartAddress, LOG_TRACE_DESTINATION(trace, stubStartAddress, "StubLinkStubManager(InstantiatingMethod)::DoTraceStub"); return TRUE; } + else if (stub->IsManagedThunk()) + { + trace->InitForManagerPush(stubStartAddress, this); + LOG_TRACE_DESTINATION(trace, stubStartAddress, "StubLinkStubManager(ManagedThunk)::DoTraceStub"); + return TRUE; + } else if (stub->GetPatchOffset() != 0) { // The patch offset is currently only non-zero in x86 non-IL delegate scenarios. @@ -1249,6 +1352,103 @@ static PCODE GetStubTarget(PTR_MethodDesc pTargetMD) return targetCode.GetNativeCode(); } +// Managed thunks are often implemented in terms of Delegates +// so the below utilizes Delegate internals. +static BOOL TraceManagedThunk( + TraceDestination *trace, + T_CONTEXT *pContext, + BYTE **pRetAddr) +{ + CONTRACTL + { + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PCODE destAddr; + PCODE pc = ::GetIP(pContext); + + // Retrieve the this pointer from the context. +#if defined(TARGET_X86) + (*pRetAddr) = *(BYTE **)(size_t)(pContext->Esp); + + BYTE* pThis = (BYTE*)(size_t)(pContext->Ecx); + + destAddr = *(PCODE*)(pThis + DelegateObject::GetOffsetOfMethodPtrAux()); + +#elif defined(TARGET_AMD64) + + // + // We need to check whether the following is the correct return address. + // + (*pRetAddr) = *(BYTE **)(size_t)(pContext->Rsp); + + LOG((LF_CORDB, LL_INFO10000, "TraceManagedThunk: at %p, retAddr is %p\n", pc, (*pRetAddr))); + + DELEGATEREF orDelegate; + if (GetEEFuncEntryPoint(SinglecastDelegateInvokeStub) == pc) + { + LOG((LF_CORDB, LL_INFO10000, "TraceManagedThunk: isSingle\n")); + + orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); + destAddr = orDelegate->GetMethodPtr(); + if (StubManager::TraceStub(destAddr, trace)) + { + LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: ppbDest: %p\n", destAddr)); + LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: res: 1, result type: %d\n", trace->GetTraceType())); + return TRUE; + } + } + else + { + // We get here if we are stopped at the beginning of a shuffle thunk. + // The next address we are going to is _methodPtrAux. + Stub* pStub = Stub::RecoverStub(pc); + + // We use the patch offset field to indicate whether the stub has a hidden return buffer argument. + // This field is set in SetupShuffleThunk(). + orDelegate = (pStub->GetPatchOffset() != 0) + ? (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetSecondArg(pContext)) // This stub has a hidden return buffer argument. + : (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); + } + + destAddr = orDelegate->GetMethodPtrAux(); + +#elif defined(TARGET_ARM) + (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); + BYTE* pThis = (BYTE*)(size_t)(pContext->R0); + + // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a + // shuffle thunk (destination in _methodPtrAux). + int offsetOfNextDest = (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) + ? DelegateObject::GetOffsetOfMethodPtr() + : DelegateObject::GetOffsetOfMethodPtrAux(); + destAddr = *(PCODE*)(pThis + offsetOfNextDest); + +#elif defined(TARGET_ARM64) + (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); + BYTE* pThis = (BYTE*)(size_t)(pContext->X0); + + // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a + // shuffle thunk (destination in _methodPtrAux). + int offsetOfNextDest = (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) + ? DelegateObject::GetOffsetOfMethodPtr() + : DelegateObject::GetOffsetOfMethodPtrAux(); + destAddr = *(PCODE*)(pThis + offsetOfNextDest); + +#else + PORTABILITY_ASSERT("TraceManagedThunk"); + destAddr = NULL; +#endif + + LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: ppbDest: %p\n", destAddr)); + + BOOL res = StubManager::TraceStub(destAddr, trace); + LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: res: %d, result type: %d\n", res, trace->GetTraceType())); + + return res; +} + BOOL StubLinkStubManager::TraceManager(Thread *thread, TraceDestination *trace, T_CONTEXT *pContext, @@ -1290,7 +1490,12 @@ BOOL StubLinkStubManager::TraceManager(Thread *thread, { LOG((LF_CORDB,LL_INFO10000, "SLSM:TM MultiCastDelegate\n")); BYTE *pbDel = (BYTE *)StubManagerHelpers::GetThisPtr(pContext); - return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace); + return TraceDelegateObject(pbDel, trace); + } + else if (stub->IsManagedThunk()) + { + LOG((LF_CORDB,LL_INFO10000, "SLSM:TM ManagedThunk\n")); + return TraceManagedThunk(trace, pContext, pRetAddr); } // Runtime bug if we get here. Did we make a change in StubLinkStubManager::DoTraceStub() that @@ -1721,7 +1926,7 @@ BOOL ILStubManager::TraceManager(Thread *thread, ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount); _ASSERTE(pbDel); - return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace); + return StubLinkStubManager::TraceDelegateObject(pbDel, trace); } } else @@ -1987,293 +2192,6 @@ BOOL InteropDispatchStubManager::TraceManager(Thread *thread, } #endif //!DACCESS_COMPILE -// -// Since we don't generate delegate invoke stubs at runtime on IA64, we -// can't use the StubLinkStubManager for these stubs. Instead, we create -// an additional DelegateInvokeStubManager instead. -// -SPTR_IMPL(DelegateInvokeStubManager, DelegateInvokeStubManager, g_pManager); - -#ifndef DACCESS_COMPILE - -// static -void DelegateInvokeStubManager::Init() -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END - - g_pManager = new DelegateInvokeStubManager(); - StubManager::AddStubManager(g_pManager); -} - -BOOL DelegateInvokeStubManager::AddStub(Stub* pStub) -{ - WRAPPER_NO_CONTRACT; - PCODE start = pStub->GetEntryPoint(); - - // We don't really care about the size here. We only stop in these stubs at the first instruction, - // so we'll never be asked to claim an address in the middle of a stub. - return GetRangeList()->AddRange((BYTE *)start, (BYTE *)start + 1, (LPVOID)start); -} - -void DelegateInvokeStubManager::RemoveStub(Stub* pStub) -{ - WRAPPER_NO_CONTRACT; - PCODE start = pStub->GetEntryPoint(); - - // We don't really care about the size here. We only stop in these stubs at the first instruction, - // so we'll never be asked to claim an address in the middle of a stub. - GetRangeList()->RemoveRanges((LPVOID)start); -} - -#endif - -BOOL DelegateInvokeStubManager::CheckIsStub_Internal(PCODE stubStartAddress) -{ - LIMITED_METHOD_DAC_CONTRACT; - - bool fIsStub = false; - -#ifndef DACCESS_COMPILE -#ifndef TARGET_X86 - fIsStub = fIsStub || (stubStartAddress == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)); -#endif -#endif // !DACCESS_COMPILE - - fIsStub = fIsStub || GetRangeList()->IsInRange(stubStartAddress); - - return fIsStub ? TRUE : FALSE; -} - -BOOL DelegateInvokeStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) -{ - LIMITED_METHOD_CONTRACT; - - LOG((LF_CORDB, LL_EVERYTHING, "DelegateInvokeStubManager::DoTraceStub called\n")); - - _ASSERTE(CheckIsStub_Internal(stubStartAddress)); - - // If it's a MC delegate, then we want to set a BP & do a context-ful - // manager push, so that we can figure out if this call will be to a - // single multicast delegate or a multi multicast delegate - trace->InitForManagerPush(stubStartAddress, this); - - LOG_TRACE_DESTINATION(trace, stubStartAddress, "DelegateInvokeStubManager::DoTraceStub"); - - return TRUE; -} - -#if !defined(DACCESS_COMPILE) - -BOOL DelegateInvokeStubManager::TraceManager(Thread *thread, TraceDestination *trace, - T_CONTEXT *pContext, BYTE **pRetAddr) -{ - CONTRACTL - { - MODE_COOPERATIVE; - } - CONTRACTL_END; - - PCODE destAddr; - - PCODE pc; - pc = ::GetIP(pContext); - - BYTE* pThis; - pThis = NULL; - - // Retrieve the this pointer from the context. -#if defined(TARGET_X86) - (*pRetAddr) = *(BYTE **)(size_t)(pContext->Esp); - - pThis = (BYTE*)(size_t)(pContext->Ecx); - - destAddr = *(PCODE*)(pThis + DelegateObject::GetOffsetOfMethodPtrAux()); - -#elif defined(TARGET_AMD64) - - // - // We need to check whether the following is the correct return address. - // - (*pRetAddr) = *(BYTE **)(size_t)(pContext->Rsp); - - LOG((LF_CORDB, LL_INFO10000, "DISM:TM at 0x%p, retAddr is 0x%p\n", pc, (*pRetAddr))); - - DELEGATEREF orDelegate; - if (GetEEFuncEntryPoint(SinglecastDelegateInvokeStub) == pc) - { - LOG((LF_CORDB, LL_INFO10000, "DISM::TraceManager: isSingle\n")); - - orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); - - // _methodPtr is where we are going to next. However, in ngen cases, we may have a shuffle thunk - // burned into the ngen image, in which case the shuffle thunk is not added to the range list of - // the DelegateInvokeStubManager. So we use _methodPtrAux as a fallback. - destAddr = orDelegate->GetMethodPtr(); - if (StubManager::TraceStub(destAddr, trace)) - { - LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n", destAddr)); - LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: 1, result type: %d\n", trace->GetTraceType())); - return TRUE; - } - } - else - { - // We get here if we are stopped at the beginning of a shuffle thunk. - // The next address we are going to is _methodPtrAux. - Stub* pStub = Stub::RecoverStub(pc); - - // We use the patch offset field to indicate whether the stub has a hidden return buffer argument. - // This field is set in SetupShuffleThunk(). - if (pStub->GetPatchOffset() != 0) - { - // This stub has a hidden return buffer argument. - orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetSecondArg(pContext)); - } - else - { - orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); - } - } - - destAddr = orDelegate->GetMethodPtrAux(); -#elif defined(TARGET_ARM) - (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); - pThis = (BYTE*)(size_t)(pContext->R0); - - // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a - // shuffle thunk (destination in _methodPtrAux). - int offsetOfNextDest; - if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) - offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr(); - else - offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux(); - destAddr = *(PCODE*)(pThis + offsetOfNextDest); -#elif defined(TARGET_ARM64) - (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); - pThis = (BYTE*)(size_t)(pContext->X0); - - // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a - // shuffle thunk (destination in _methodPtrAux). - int offsetOfNextDest; - if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) - offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr(); - else - offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux(); - destAddr = *(PCODE*)(pThis + offsetOfNextDest); -#else - PORTABILITY_ASSERT("DelegateInvokeStubManager::TraceManager"); - destAddr = NULL; -#endif - - LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n", destAddr)); - - BOOL res = StubManager::TraceStub(destAddr, trace); - LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: %d, result type: %d\n", res, trace->GetTraceType())); - - return res; -} - -// static -BOOL DelegateInvokeStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *trace) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - BYTE **ppbDest = NULL; - // If we got here, then we're here b/c we're at the start of a delegate stub - // need to figure out the kind of delegates we are dealing with - - BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); - - LOG((LF_CORDB,LL_INFO10000, "DISM::TMI: invocationList: 0x%p\n", pbDelInvocationList)); - - if (pbDelInvocationList == NULL) - { - // null invocationList can be one of the following: - // Instance closed, Instance open non-virt, Instance open virtual, Static closed, Static opened, Unmanaged FtnPtr - // Instance open virtual is complex and we need to figure out what to do (TODO). - // For the others the logic is the following: - // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux - - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); - - if (*ppbDest == NULL) - { - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr()); - - if (*ppbDest == NULL) - { - // it's not looking good, bail out - LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n")); - return FALSE; - } - - } - - LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%p *ppbDest:0x%p\n", ppbDest, *ppbDest)); - - BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); - - LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n", res, trace->GetTraceType())); - - return res; - } - - // invocationList is not null, so it can be one of the following: - // Multicast, Static closed (special sig), Secure - - // rule out the static with special sig - BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount()); - - if (!pbCount) - { - // it's a static closed, the target lives in _methodAuxPtr - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); - - if (*ppbDest == NULL) - { - // it's not looking good, bail out - LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n")); - return FALSE; - } - - LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%p *ppbDest:0x%p\n", ppbDest, *ppbDest)); - - BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); - - LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n", res, trace->GetTraceType())); - - return res; - } - - MethodTable *pType = *(MethodTable**)pbDelInvocationList; - if (pType->IsDelegate()) - { - // this is a secure deelgate. The target is hidden inside this field, so recurse in and pray... - return TraceDelegateObject(pbDelInvocationList, trace); - } - - // Otherwise, we're going for the first invoke of the multi case. - // In order to go to the correct spot, we have just have to fish out - // slot 0 of the invocation list, and figure out where that's going to, - // then put a breakpoint there... - pbDel = *(BYTE**)(((ArrayBase *)pbDelInvocationList)->GetDataPtr()); - return TraceDelegateObject(pbDel, trace); -} - -#endif // DACCESS_COMPILE - - #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) #if !defined(DACCESS_COMPILE) @@ -2464,16 +2382,6 @@ InteropDispatchStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) EMEM_OUT(("MEM: %p InteropDispatchStubManager\n", dac_cast(this))); } -void -DelegateInvokeStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) -{ - SUPPORTS_DAC; - WRAPPER_NO_CONTRACT; - DAC_ENUM_VTHIS(); - EMEM_OUT(("MEM: %p DelegateInvokeStubManager\n", dac_cast(this))); - GetRangeList()->EnumMemoryRegions(flags); -} - void VirtualCallStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) { diff --git a/src/coreclr/vm/stubmgr.h b/src/coreclr/vm/stubmgr.h index c4c80d0b17db61..6064939dc21f0d 100644 --- a/src/coreclr/vm/stubmgr.h +++ b/src/coreclr/vm/stubmgr.h @@ -446,10 +446,14 @@ class StubLinkStubManager : public StubManager static void Init(); -#ifndef DACCESS_COMPILE +#if !defined(DACCESS_COMPILE) + static BOOL TraceDelegateObject(BYTE *orDel, TraceDestination *trace); +#endif // DACCESS_COMPILE + +#if !defined(DACCESS_COMPILE) StubLinkStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;} ~StubLinkStubManager() {WRAPPER_NO_CONTRACT;} -#endif +#endif // DACCESS_COMPILE protected: LockedRangeList m_rangeList; @@ -463,6 +467,7 @@ class StubLinkStubManager : public StubManager return PTR_RangeList(addr); } + void RemoveStubRange(BYTE* start, UINT length); virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); @@ -719,68 +724,6 @@ class InteropDispatchStubManager : public StubManager #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, T_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 -}; - #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) //--------------------------------------------------------------------------------------- //