summaryrefslogtreecommitdiff
path: root/src/vm/frames.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/frames.cpp')
-rw-r--r--src/vm/frames.cpp2233
1 files changed, 2233 insertions, 0 deletions
diff --git a/src/vm/frames.cpp b/src/vm/frames.cpp
new file mode 100644
index 0000000000..ec7e7be63c
--- /dev/null
+++ b/src/vm/frames.cpp
@@ -0,0 +1,2233 @@
+// 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.
+// FRAMES.CPP
+
+
+
+#include "common.h"
+#include "log.h"
+#include "frames.h"
+#include "threads.h"
+#include "object.h"
+#include "method.hpp"
+#include "class.h"
+#include "excep.h"
+#include "security.h"
+#include "stublink.h"
+#include "fieldmarshaler.h"
+#include "objecthandle.h"
+#include "siginfo.hpp"
+#include "gc.h"
+#include "dllimportcallback.h"
+#include "stackwalk.h"
+#include "dbginterface.h"
+#include "gms.h"
+#include "eeconfig.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "ecall.h"
+#include "clsload.hpp"
+#include "cgensys.h"
+#include "virtualcallstub.h"
+#include "mdaassistants.h"
+#include "dllimport.h"
+#include "gcrefmap.h"
+#include "asmconstants.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "comtoclrcall.h"
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_INTERPRETER
+#include "interpreter.h"
+#endif // FEATURE_INTERPRETER
+
+#include "argdestination.h"
+
+#if CHECK_APP_DOMAIN_LEAKS
+#define CHECK_APP_DOMAIN GC_CALL_CHECK_APP_DOMAIN
+#else
+#define CHECK_APP_DOMAIN 0
+#endif
+
+//-----------------------------------------------------------------------
+#if _DEBUG
+//-----------------------------------------------------------------------
+
+#ifndef DACCESS_COMPILE
+
+unsigned dbgStubCtr = 0;
+unsigned dbgStubTrip = 0xFFFFFFFF;
+
+void Frame::Log() {
+ WRAPPER_NO_CONTRACT;
+
+ if (!LoggingOn(LF_STUBS, LL_INFO1000000))
+ return;
+
+ dbgStubCtr++;
+ if (dbgStubCtr > dbgStubTrip) {
+ dbgStubCtr++; // basicly a nop to put a breakpoint on.
+ }
+
+ MethodDesc* method = GetFunction();
+
+#ifdef _TARGET_X86_
+ if (GetVTablePtr() == UMThkCallFrame::GetMethodFrameVPtr())
+ method = ((UMThkCallFrame*) this)->GetUMEntryThunk()->GetMethod();
+#endif
+
+ STRESS_LOG3(LF_STUBS, LL_INFO1000000, "STUBS: In Stub with Frame %p assoc Method %pM FrameType = %pV\n", this, method, *((void**) this));
+
+ char buff[64];
+ const char* frameType;
+ if (GetVTablePtr() == PrestubMethodFrame::GetMethodFrameVPtr())
+ frameType = "PreStub";
+#ifdef _TARGET_X86_
+ else if (GetVTablePtr() == UMThkCallFrame::GetMethodFrameVPtr())
+ frameType = "UMThkCallFrame";
+#endif
+ else if (GetVTablePtr() == PInvokeCalliFrame::GetMethodFrameVPtr())
+ {
+ sprintf_s(buff, COUNTOF(buff), "PInvoke CALLI target" FMT_ADDR,
+ DBG_ADDR(((PInvokeCalliFrame*)this)->GetPInvokeCalliTarget()));
+ frameType = buff;
+ }
+ else if (GetVTablePtr() == StubDispatchFrame::GetMethodFrameVPtr())
+ frameType = "StubDispatch";
+ else if (GetVTablePtr() == ExternalMethodFrame::GetMethodFrameVPtr())
+ frameType = "ExternalMethod";
+ else
+ frameType = "Unknown";
+
+ if (method != 0)
+ LOG((LF_STUBS, LL_INFO1000000,
+ "IN %s Stub Method = %s::%s SIG %s ESP of return" FMT_ADDR "\n",
+ frameType,
+ method->m_pszDebugClassName,
+ method->m_pszDebugMethodName,
+ method->m_pszDebugMethodSignature,
+ DBG_ADDR(GetReturnAddressPtr())));
+ else
+ LOG((LF_STUBS, LL_INFO1000000,
+ "IN %s Stub Method UNKNOWN ESP of return" FMT_ADDR "\n",
+ frameType,
+ DBG_ADDR(GetReturnAddressPtr()) ));
+
+ _ASSERTE(GetThread()->PreemptiveGCDisabled());
+}
+
+//-----------------------------------------------------------------------
+// This function is used to log transitions in either direction
+// between unmanaged code and CLR/managed code.
+// This is typically done in a stub that sets up a Frame, which is
+// passed as an argument to this function.
+
+void __stdcall Frame::LogTransition(Frame* frame)
+{
+
+ CONTRACTL {
+ DEBUG_ONLY;
+ NOTHROW;
+ ENTRY_POINT;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ BEGIN_ENTRYPOINT_VOIDRET;
+
+#ifdef _TARGET_X86_
+ // On x86, StubLinkerCPU::EmitMethodStubProlog calls Frame::LogTransition
+ // but the caller of EmitMethodStubProlog sets the GSCookie later on.
+ // So the cookie is not initialized by the point we get here.
+#else
+ _ASSERTE(*frame->GetGSCookiePtr() == GetProcessGSCookie());
+#endif
+
+ if (Frame::ShouldLogTransitions())
+ frame->Log();
+
+ END_ENTRYPOINT_VOIDRET;
+} // void Frame::Log()
+
+#endif // #ifndef DACCESS_COMPILE
+
+//-----------------------------------------------------------------------
+#endif // _DEBUG
+//-----------------------------------------------------------------------
+
+
+// TODO [DAVBR]: For the full fix for VsWhidbey 450273, all the below
+// may be uncommented once isLegalManagedCodeCaller works properly
+// with non-return address inputs, and with non-DEBUG builds
+#if 0
+//-----------------------------------------------------------------------
+// returns TRUE if retAddr, is a return address that can call managed code
+
+bool isLegalManagedCodeCaller(PCODE retAddr) {
+ WRAPPER_NO_CONTRACT;
+#ifdef _TARGET_X86_
+
+ // we expect to be called from JITTED code or from special code sites inside
+ // mscorwks like callDescr which we have put a NOP (0x90) so we know that they
+ // are specially blessed.
+ if (!ExecutionManager::IsManagedCode(retAddr) &&
+ (
+#ifdef DACCESS_COMPILE
+ !(PTR_BYTE(retAddr).IsValid()) ||
+#endif
+ ((*PTR_BYTE(retAddr) != 0x90) &&
+ (*PTR_BYTE(retAddr) != 0xcc))))
+ {
+ LOG((LF_GC, LL_INFO10, "Bad caller to managed code: retAddr=0x%08x, *retAddr=0x%x\n",
+ retAddr, *(BYTE*)PTR_BYTE(retAddr)));
+
+ return false;
+ }
+
+ // it better be a return address of some kind
+ TADDR dummy;
+ if (isRetAddr(retAddr, &dummy))
+ return true;
+
+#ifndef DACCESS_COMPILE
+#ifdef DEBUGGING_SUPPORTED
+ // The debugger could have dropped an INT3 on the instruction that made the call
+ // Calls can be 2 to 7 bytes long
+ if (CORDebuggerAttached()) {
+ PTR_BYTE ptr = PTR_BYTE(retAddr);
+ for (int i = -2; i >= -7; --i)
+ if (ptr[i] == 0xCC)
+ return true;
+ return false;
+ }
+#endif // DEBUGGING_SUPPORTED
+#endif // #ifndef DACCESS_COMPILE
+
+ _ASSERTE(!"Bad return address on stack");
+ return false;
+#else // _TARGET_X86_
+ return true;
+#endif // _TARGET_X86_
+}
+#endif //0
+
+
+//-----------------------------------------------------------------------
+// Count of the number of frame types
+const size_t FRAME_TYPES_COUNT =
+#define FRAME_TYPE_NAME(frameType) +1
+#include "frames.h"
+;
+
+#if defined (_DEBUG_IMPL) // _DEBUG and !DAC
+
+//-----------------------------------------------------------------------
+// Implementation of the global table of names. On the DAC side, just the global pointer.
+// On the runtime side, the array of names.
+ #define FRAME_TYPE_NAME(x) {x::GetMethodFrameVPtr(), #x} ,
+ static FrameTypeName FrameTypeNameTable[] = {
+ #include "frames.h"
+ };
+
+
+/* static */
+PTR_CSTR Frame::GetFrameTypeName(TADDR vtbl)
+{
+ LIMITED_METHOD_CONTRACT;
+ for (size_t i=0; i<FRAME_TYPES_COUNT; ++i)
+ {
+ if (vtbl == FrameTypeNameTable[(int)i].vtbl)
+ {
+ return FrameTypeNameTable[(int)i].name;
+ }
+ }
+
+ return NULL;
+} // char* Frame::FrameTypeName()
+
+
+//-----------------------------------------------------------------------
+
+
+void Frame::LogFrame(
+ int LF, // Log facility for this call.
+ int LL) // Log Level for this call.
+{
+ char buf[32];
+ const char *pFrameType;
+ pFrameType = GetFrameTypeName();
+
+ if (pFrameType == NULL)
+ {
+ pFrameType = GetFrameTypeName(GetVTablePtr());
+ }
+
+ if (pFrameType == NULL)
+ {
+ _ASSERTE(!"New Frame type needs to be added to FrameTypeName()");
+ // Pointer is up to 17chars + vtbl@ = 22 chars
+ sprintf_s(buf, COUNTOF(buf), "vtbl@%p", GetVTablePtr());
+ pFrameType = buf;
+ }
+
+ LOG((LF, LL, "FRAME: addr:%p, next:%p, type:%s\n",
+ this, m_Next, pFrameType));
+} // void Frame::LogFrame()
+
+void Frame::LogFrameChain(
+ int LF, // Log facility for this call.
+ int LL) // Log Level for this call.
+{
+ if (!LoggingOn(LF, LL))
+ return;
+
+ Frame *pFrame = this;
+ while (pFrame != FRAME_TOP)
+ {
+ pFrame->LogFrame(LF, LL);
+ pFrame = pFrame->m_Next;
+ }
+} // void Frame::LogFrameChain()
+
+//-----------------------------------------------------------------------
+#endif // _DEBUG_IMPL
+//-----------------------------------------------------------------------
+
+#ifndef DACCESS_COMPILE
+
+// This hashtable contains the vtable value of every Frame type.
+static PtrHashMap* s_pFrameVTables = NULL;
+
+// static
+void Frame::Init()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ // create a table big enough for all the frame types, not in asynchronous mode, and with no lock owner
+ s_pFrameVTables = ::new PtrHashMap;
+ s_pFrameVTables->Init(2 * FRAME_TYPES_COUNT, FALSE, &g_lockTrustMeIAmThreadSafe);
+#define FRAME_TYPE_NAME(frameType) \
+ s_pFrameVTables->InsertValue(frameType::GetMethodFrameVPtr(), \
+ (LPVOID) frameType::GetMethodFrameVPtr());
+#include "frames.h"
+
+} // void Frame::Init()
+
+// static
+void Frame::Term()
+{
+ LIMITED_METHOD_CONTRACT;
+ delete s_pFrameVTables;
+ s_pFrameVTables = NULL;
+}
+
+#endif // DACCESS_COMPILE
+
+// Returns true if the Frame's VTablePtr is valid
+
+// static
+bool Frame::HasValidVTablePtr(Frame * pFrame)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (pFrame == NULL || pFrame == FRAME_TOP)
+ return false;
+
+#ifndef DACCESS_COMPILE
+ TADDR vptr = pFrame->GetVTablePtr();
+ //
+ // Helper MethodFrame,GCFrame,DebuggerSecurityCodeMarkFrame are the most
+ // common frame types, explicitly check for them.
+ //
+ if (vptr == HelperMethodFrame::GetMethodFrameVPtr())
+ return true;
+
+ if (vptr == GCFrame::GetMethodFrameVPtr())
+ return true;
+
+ if (vptr == DebuggerSecurityCodeMarkFrame::GetMethodFrameVPtr())
+ return true;
+
+ //
+ // otherwise consult the hashtable
+ //
+ if (s_pFrameVTables->LookupValue(vptr, (LPVOID) vptr) == (LPVOID) INVALIDENTRY)
+ return false;
+#endif
+
+ return true;
+}
+
+// Returns the location of the expected GSCookie,
+// Return NULL if the frame's vtable pointer is corrupt
+//
+// Note that Frame::GetGSCookiePtr is a virtual method,
+// and so it cannot be used without first checking if
+// the vtable is valid.
+
+// static
+PTR_GSCookie Frame::SafeGetGSCookiePtr(Frame * pFrame)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(pFrame != FRAME_TOP);
+
+ if (Frame::HasValidVTablePtr(pFrame))
+ return pFrame->GetGSCookiePtr();
+ else
+ return NULL;
+}
+
+//-----------------------------------------------------------------------
+#ifndef DACCESS_COMPILE
+//-----------------------------------------------------------------------
+// Link and Unlink this frame.
+//-----------------------------------------------------------------------
+
+VOID Frame::Push()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ Push(GetThread());
+}
+
+VOID Frame::Push(Thread *pThread)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(*GetGSCookiePtr() == GetProcessGSCookie());
+
+ m_Next = pThread->GetFrame();
+
+ // PAGE_SIZE is used to relax the assert for cases where two Frames are
+ // declared in the same source function. We cannot predict the order
+ // in which the C compiler will lay them out in the stack frame.
+ // So PAGE_SIZE is a guess of the maximum stack frame size of any method
+ // with multiple Frames in mscorwks.dll
+ _ASSERTE(((m_Next == FRAME_TOP) ||
+ (PBYTE(m_Next) + (2 * PAGE_SIZE)) > PBYTE(this)) &&
+ "Pushing a frame out of order ?");
+
+ _ASSERTE(// If AssertOnFailFast is set, the test expects to do stack overrun
+ // corruptions. In that case, the Frame chain may be corrupted,
+ // and the rest of the assert is not valid.
+ // Note that the corrupted Frame chain will be detected
+ // during stack-walking.
+ !g_pConfig->fAssertOnFailFast() ||
+ (m_Next == FRAME_TOP) ||
+ (*m_Next->GetGSCookiePtr() == GetProcessGSCookie()));
+
+ pThread->SetFrame(this);
+}
+
+VOID Frame::Pop()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ Pop(GetThread());
+}
+
+VOID Frame::Pop(Thread *pThread)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pThread->GetFrame() == this && "Popping a frame out of order ?");
+ _ASSERTE(*GetGSCookiePtr() == GetProcessGSCookie());
+ _ASSERTE(// If AssertOnFailFast is set, the test expects to do stack overrun
+ // corruptions. In that case, the Frame chain may be corrupted,
+ // and the rest of the assert is not valid.
+ // Note that the corrupted Frame chain will be detected
+ // during stack-walking.
+ !g_pConfig->fAssertOnFailFast() ||
+ (m_Next == FRAME_TOP) ||
+ (*m_Next->GetGSCookiePtr() == GetProcessGSCookie()));
+
+ pThread->SetFrame(m_Next);
+ m_Next = NULL;
+}
+
+#if defined(FEATURE_PAL) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+void Frame::PopIfChained()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ if (m_Next != NULL)
+ {
+ GCX_COOP();
+ // When the frame is destroyed, make sure it is no longer in the
+ // frame chain managed by the Thread.
+ Pop();
+ }
+}
+#endif // FEATURE_PAL && !DACCESS_COMPILE && !CROSSGEN_COMPILE
+
+//-----------------------------------------------------------------------
+#endif // #ifndef DACCESS_COMPILE
+//---------------------------------------------------------------
+// Get the extra param for shared generic code.
+//---------------------------------------------------------------
+PTR_VOID TransitionFrame::GetParamTypeArg()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ // This gets called while creating stack traces during exception handling.
+ // Using the ArgIterator constructor calls ArgIterator::Init which calls GetInitialOfsAdjust
+ // which calls SizeOfArgStack, which thinks it may load value types.
+ // However all these will have previously been loaded.
+ //
+ // I'm not entirely convinced this is the best places to put this: CrawlFrame::GetExactGenericArgsToken
+ // may be another option.
+ ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
+
+ MethodDesc *pFunction = GetFunction();
+ _ASSERTE (pFunction->RequiresInstArg());
+
+ MetaSig msig(pFunction);
+ ArgIterator argit (&msig);
+
+ INT offs = argit.GetParamTypeArgOffset();
+
+ TADDR taParamTypeArg = *PTR_TADDR(GetTransitionBlock() + offs);
+ return PTR_VOID(taParamTypeArg);
+}
+
+TADDR TransitionFrame::GetAddrOfThis()
+{
+ WRAPPER_NO_CONTRACT;
+ return GetTransitionBlock() + ArgIterator::GetThisOffset();
+}
+
+VASigCookie * TransitionFrame::GetVASigCookie()
+{
+#if defined(_TARGET_X86_)
+ LIMITED_METHOD_CONTRACT;
+ return dac_cast<PTR_VASigCookie>(
+ *dac_cast<PTR_TADDR>(GetTransitionBlock() +
+ sizeof(TransitionBlock)));
+#else
+ WRAPPER_NO_CONTRACT;
+ MetaSig msig(GetFunction());
+ ArgIterator argit(&msig);
+ return PTR_VASigCookie(
+ *dac_cast<PTR_TADDR>(GetTransitionBlock() + argit.GetVASigCookieOffset()));
+#endif
+}
+
+#ifndef DACCESS_COMPILE
+PrestubMethodFrame::PrestubMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD)
+ : FramedMethodFrame(pTransitionBlock, pMD)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL PrestubMethodFrame::TraceFrame(Thread *thread, BOOL fromPatch,
+ TraceDestination *trace, REGDISPLAY *regs)
+{
+ WRAPPER_NO_CONTRACT;
+
+ //
+ // We want to set a frame patch, unless we're already at the
+ // frame patch, in which case we'll trace stable entrypoint which
+ // should be set by now.
+ //
+
+ if (fromPatch)
+ {
+ trace->InitForStub(GetFunction()->GetStableEntryPoint());
+ }
+ else
+ {
+ trace->InitForStub(GetPreStubEntryPoint());
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "PrestubMethodFrame::TraceFrame: ip=" FMT_ADDR "\n", DBG_ADDR(trace->GetAddress()) ));
+
+ return TRUE;
+}
+
+#ifndef DACCESS_COMPILE
+//-----------------------------------------------------------------------
+// A rather specialized routine for the exclusive use of StubDispatch.
+//-----------------------------------------------------------------------
+StubDispatchFrame::StubDispatchFrame(TransitionBlock * pTransitionBlock)
+ : FramedMethodFrame(pTransitionBlock, NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pRepresentativeMT = NULL;
+ m_representativeSlot = 0;
+
+ m_pZapModule = NULL;
+ m_pIndirection = NULL;
+
+ m_pGCRefMap = NULL;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+MethodDesc* StubDispatchFrame::GetFunction()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ MethodDesc * pMD = m_pMD;
+
+ if (m_pMD == NULL)
+ {
+ if (m_pRepresentativeMT != NULL)
+ {
+ pMD = m_pRepresentativeMT->GetMethodDescForSlot(m_representativeSlot);
+#ifndef DACCESS_COMPILE
+ m_pMD = pMD;
+#endif
+ }
+ }
+
+ return pMD;
+}
+
+static PTR_BYTE FindGCRefMap(PTR_Module pZapModule, TADDR ptr)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ PEImageLayout *pNativeImage = pZapModule->GetNativeOrReadyToRunImage();
+
+ RVA rva = pNativeImage->GetDataRva(ptr);
+
+ PTR_CORCOMPILE_IMPORT_SECTION pImportSection = pZapModule->GetImportSectionForRVA(rva);
+ if (pImportSection == NULL)
+ return NULL;
+
+ COUNT_T index = (rva - pImportSection->Section.VirtualAddress) / pImportSection->EntrySize;
+
+ PTR_BYTE pGCRefMap = dac_cast<PTR_BYTE>(pNativeImage->GetRvaData(pImportSection->AuxiliaryData));
+ _ASSERTE(pGCRefMap != NULL);
+
+ // GCRefMap starts with lookup index to limit size of linear scan that follows.
+ PTR_BYTE p = pGCRefMap + dac_cast<PTR_DWORD>(pGCRefMap)[index / GCREFMAP_LOOKUP_STRIDE];
+ COUNT_T remaining = index % GCREFMAP_LOOKUP_STRIDE;
+
+ while (remaining > 0)
+ {
+ while ((*p & 0x80) != 0)
+ p++;
+ p++;
+
+ remaining--;
+ }
+
+ return p;
+}
+
+PTR_BYTE StubDispatchFrame::GetGCRefMap()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ PTR_BYTE pGCRefMap = m_pGCRefMap;
+
+ if (pGCRefMap == NULL)
+ {
+ if (m_pIndirection != NULL)
+ {
+ if (m_pZapModule == NULL)
+ {
+ m_pZapModule = ExecutionManager::FindModuleForGCRefMap(m_pIndirection);
+ }
+
+ if (m_pZapModule != NULL)
+ {
+ pGCRefMap = FindGCRefMap(m_pZapModule, m_pIndirection);
+ }
+
+#ifndef DACCESS_COMPILE
+ if (pGCRefMap != NULL)
+ {
+ m_pGCRefMap = pGCRefMap;
+ }
+ else
+ {
+ // Clear the indirection to avoid retrying
+ m_pIndirection = NULL;
+ }
+#endif
+ }
+ }
+
+ return pGCRefMap;
+}
+
+void StubDispatchFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ FramedMethodFrame::GcScanRoots(fn, sc);
+
+ PTR_BYTE pGCRefMap = GetGCRefMap();
+ if (pGCRefMap != NULL)
+ {
+ PromoteCallerStackUsingGCRefMap(fn, sc, pGCRefMap);
+ }
+ else
+ {
+ PromoteCallerStack(fn, sc);
+ }
+}
+
+BOOL StubDispatchFrame::TraceFrame(Thread *thread, BOOL fromPatch,
+ TraceDestination *trace, REGDISPLAY *regs)
+{
+ WRAPPER_NO_CONTRACT;
+
+ //
+ // We want to set a frame patch, unless we're already at the
+ // frame patch, in which case we'll trace stable entrypoint which
+ // should be set by now.
+ //
+
+ if (fromPatch)
+ {
+ trace->InitForStub(GetFunction()->GetStableEntryPoint());
+ }
+ else
+ {
+ trace->InitForStub(GetPreStubEntryPoint());
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "StubDispatchFrame::TraceFrame: ip=" FMT_ADDR "\n", DBG_ADDR(trace->GetAddress()) ));
+
+ return TRUE;
+}
+
+Frame::Interception StubDispatchFrame::GetInterception()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return INTERCEPTION_NONE;
+}
+
+#ifndef DACCESS_COMPILE
+ExternalMethodFrame::ExternalMethodFrame(TransitionBlock * pTransitionBlock)
+ : FramedMethodFrame(pTransitionBlock, NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pIndirection = NULL;
+ m_pZapModule = NULL;
+
+ m_pGCRefMap = NULL;
+}
+#endif // !DACCESS_COMPILE
+
+void ExternalMethodFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ FramedMethodFrame::GcScanRoots(fn, sc);
+ PromoteCallerStackUsingGCRefMap(fn, sc, GetGCRefMap());
+}
+
+PTR_BYTE ExternalMethodFrame::GetGCRefMap()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ PTR_BYTE pGCRefMap = m_pGCRefMap;
+
+ if (pGCRefMap == NULL)
+ {
+ if (m_pIndirection != NULL)
+ {
+ pGCRefMap = FindGCRefMap(m_pZapModule, m_pIndirection);
+#ifndef DACCESS_COMPILE
+ m_pGCRefMap = pGCRefMap;
+#endif
+ }
+ }
+
+ _ASSERTE(pGCRefMap != NULL);
+ return pGCRefMap;
+}
+
+Frame::Interception ExternalMethodFrame::GetInterception()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return INTERCEPTION_NONE;
+}
+
+Frame::Interception PrestubMethodFrame::GetInterception()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ //
+ // The only direct kind of interception done by the prestub
+ // is class initialization.
+ //
+
+ return INTERCEPTION_PRESTUB;
+}
+
+#ifdef FEATURE_READYTORUN
+
+#ifndef DACCESS_COMPILE
+DynamicHelperFrame::DynamicHelperFrame(TransitionBlock * pTransitionBlock, int dynamicHelperFrameFlags)
+ : FramedMethodFrame(pTransitionBlock, NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_dynamicHelperFrameFlags = dynamicHelperFrameFlags;
+}
+#endif // !DACCESS_COMPILE
+
+void DynamicHelperFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ FramedMethodFrame::GcScanRoots(fn, sc);
+
+ PTR_PTR_Object pArgumentRegisters = dac_cast<PTR_PTR_Object>(GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters());
+
+ if (m_dynamicHelperFrameFlags & DynamicHelperFrameFlags_ObjectArg)
+ {
+ TADDR pArgument = GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters();
+#ifdef _TARGET_X86_
+ // x86 is special as always
+ pArgument += offsetof(ArgumentRegisters, ECX);
+#endif
+ (*fn)(dac_cast<PTR_PTR_Object>(pArgument), sc, CHECK_APP_DOMAIN);
+ }
+
+ if (m_dynamicHelperFrameFlags & DynamicHelperFrameFlags_ObjectArg2)
+ {
+ TADDR pArgument = GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters();
+#ifdef _TARGET_X86_
+ // x86 is special as always
+ pArgument += offsetof(ArgumentRegisters, EDX);
+#else
+ pArgument += sizeof(TADDR);
+#endif
+ (*fn)(dac_cast<PTR_PTR_Object>(pArgument), sc, CHECK_APP_DOMAIN);
+ }
+}
+
+#endif // FEATURE_READYTORUN
+
+
+#ifndef DACCESS_COMPILE
+
+#ifdef FEATURE_COMINTEROP
+//-----------------------------------------------------------------------
+// A rather specialized routine for the exclusive use of the COM PreStub.
+//-----------------------------------------------------------------------
+VOID
+ComPrestubMethodFrame::Init()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Initializes the frame's VPTR. This assumes C++ puts the vptr
+ // at offset 0 for a class not using MI, but this is no different
+ // than the assumption that COM Classic makes.
+ *((TADDR*)this) = GetMethodFrameVPtr();
+ *GetGSCookiePtr() = GetProcessGSCookie();
+}
+#endif // FEATURE_COMINTEROP
+
+//-----------------------------------------------------------------------
+// GCFrames
+//-----------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+// This constructor pushes a new GCFrame on the frame chain.
+//--------------------------------------------------------------------
+GCFrame::GCFrame(OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ Init(GetThread(), pObjRefs, numObjRefs, maybeInterior);
+}
+
+GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ Init(pThread, pObjRefs, numObjRefs, maybeInterior);
+}
+
+void GCFrame::Init(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+#ifdef USE_CHECKED_OBJECTREFS
+ if (!maybeInterior) {
+ UINT i;
+ for(i = 0; i < numObjRefs; i++)
+ Thread::ObjectRefProtected(&pObjRefs[i]);
+
+ for (i = 0; i < numObjRefs; i++) {
+ pObjRefs[i].Validate();
+ }
+ }
+
+#if 0 // We'll want to restore this goodness check at some time. For now, the fact that we use
+ // this as temporary backstops in our loader exception conversions means we're highly
+ // exposed to infinite stack recursion should the loader be invoked during a stackwalk.
+ // So we'll do without.
+
+ if (g_pConfig->GetGCStressLevel() != 0 && IsProtectedByGCFrame(pObjRefs)) {
+ _ASSERTE(!"This objectref is already protected by a GCFrame. Protecting it twice will corrupt the GC.");
+ }
+#endif
+
+#endif
+
+ m_pObjRefs = pObjRefs;
+ m_numObjRefs = numObjRefs;
+ m_pCurThread = pThread;
+ m_MaybeInterior = maybeInterior;
+
+ Frame::Push(m_pCurThread);
+}
+
+
+//
+// GCFrame Object Scanning
+//
+// This handles scanning/promotion of GC objects that were
+// protected by the programmer explicitly protecting it in a GC Frame
+// via the GCPROTECTBEGIN / GCPROTECTEND facility...
+//
+
+#endif // !DACCESS_COMPILE
+
+void GCFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ PTR_PTR_Object pRefs = dac_cast<PTR_PTR_Object>(m_pObjRefs);
+
+ for (UINT i = 0;i < m_numObjRefs; i++) {
+
+ LOG((LF_GC, INFO3, "GC Protection Frame Promoting" FMT_ADDR "to",
+ DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(m_pObjRefs[i])) ));
+ if (m_MaybeInterior)
+ PromoteCarefully(fn, pRefs + i, sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
+ else
+ (*fn)(pRefs + i, sc, 0);
+ LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(m_pObjRefs[i])) ));
+ }
+}
+
+#ifdef FEATURE_REMOTING
+#include "objectclone.h"
+void GCSafeCollectionFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ PTR_GCSafeCollection collection = dac_cast<PTR_GCSafeCollection>(m_pCollection);
+ collection->ReportGCRefs(fn, sc);
+}
+
+#ifndef DACCESS_COMPILE
+GCSafeCollectionFrame::GCSafeCollectionFrame(void *collection)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(collection != NULL);
+ m_pCollection = collection;
+
+ Frame::Push();
+}
+
+VOID GCSafeCollectionFrame::Pop()
+{
+ LIMITED_METHOD_CONTRACT;
+ Frame::Pop();
+}
+#endif // !DACCESS_COMPILE
+#endif // FEATURE_REMOTING
+
+#ifndef DACCESS_COMPILE
+//--------------------------------------------------------------------
+// Pops the GCFrame and cancels the GC protection.
+//--------------------------------------------------------------------
+VOID GCFrame::Pop()
+{
+ WRAPPER_NO_CONTRACT;
+
+ Frame::Pop(m_pCurThread);
+#ifdef _DEBUG
+ m_pCurThread->EnableStressHeap();
+ for(UINT i = 0; i < m_numObjRefs; i++)
+ Thread::ObjectRefNew(&m_pObjRefs[i]); // Unprotect them
+#endif
+}
+
+#ifdef FEATURE_INTERPRETER
+// Methods of IntepreterFrame.
+InterpreterFrame::InterpreterFrame(Interpreter* interp)
+ : Frame(), m_interp(interp)
+{
+ Push();
+}
+
+
+MethodDesc* InterpreterFrame::GetFunction()
+{
+ return m_interp->GetMethodDesc();
+}
+
+void InterpreterFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ return m_interp->GCScanRoots(fn, sc);
+}
+
+#endif // FEATURE_INTERPRETER
+
+#if defined(_DEBUG) && !defined (DACCESS_COMPILE)
+
+struct IsProtectedByGCFrameStruct
+{
+ OBJECTREF *ppObjectRef;
+ UINT count;
+};
+
+static StackWalkAction IsProtectedByGCFrameStackWalkFramesCallback(
+ CrawlFrame *pCF,
+ VOID *pData
+)
+{
+ DEBUG_ONLY_FUNCTION;
+ WRAPPER_NO_CONTRACT;
+
+ IsProtectedByGCFrameStruct *pd = (IsProtectedByGCFrameStruct*)pData;
+ Frame *pFrame = pCF->GetFrame();
+ if (pFrame) {
+ if (pFrame->Protects(pd->ppObjectRef)) {
+ pd->count++;
+ }
+ }
+ return SWA_CONTINUE;
+}
+
+BOOL IsProtectedByGCFrame(OBJECTREF *ppObjectRef)
+{
+ DEBUG_ONLY_FUNCTION;
+ WRAPPER_NO_CONTRACT;
+
+ // Just report TRUE if GCStress is not on. This satisfies the asserts that use this
+ // code without the cost of actually determining it.
+ if (!GCStress<cfg_any>::IsEnabled())
+ return TRUE;
+
+ if (ppObjectRef == NULL) {
+ return TRUE;
+ }
+
+ CONTRACT_VIOLATION(ThrowsViolation);
+ ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE ();
+ IsProtectedByGCFrameStruct d = {ppObjectRef, 0};
+ GetThread()->StackWalkFrames(IsProtectedByGCFrameStackWalkFramesCallback, &d);
+ if (d.count > 1) {
+ _ASSERTE(!"Multiple GCFrames protecting the same pointer. This will cause GC corruption!");
+ }
+ return d.count != 0;
+}
+#endif // _DEBUG
+
+#endif //!DACCESS_COMPILE
+
+#ifdef FEATURE_HIJACK
+
+void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ ReturnKind returnKind = m_Thread->GetHijackReturnKind();
+ _ASSERTE(IsValidReturnKind(returnKind));
+
+ int regNo = 0;
+ bool moreRegisters = false;
+
+ do
+ {
+ ReturnKind r = ExtractRegReturnKind(returnKind, regNo, moreRegisters);
+ PTR_PTR_Object objPtr = dac_cast<PTR_PTR_Object>(&m_Args->ReturnValue[regNo]);
+
+ switch (r)
+ {
+#ifdef _TARGET_X86_
+ case RT_Float: // Fall through
+#endif
+ case RT_Scalar:
+ // nothing to report
+ break;
+
+ case RT_Object:
+ LOG((LF_GC, INFO3, "Hijack Frame Promoting Object" FMT_ADDR "to",
+ DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+ (*fn)(objPtr, sc, CHECK_APP_DOMAIN);
+ LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+ break;
+
+ case RT_ByRef:
+ LOG((LF_GC, INFO3, "Hijack Frame Carefully Promoting pointer" FMT_ADDR "to",
+ DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+ PromoteCarefully(fn, objPtr, sc, GC_CALL_INTERIOR | GC_CALL_CHECK_APP_DOMAIN);
+ LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+ break;
+
+ default:
+ _ASSERTE(!"Impossible two bit encoding");
+ }
+
+ regNo++;
+ } while (moreRegisters);
+}
+
+#endif // FEATURE_HIJACK
+
+void ProtectByRefsFrame::GcScanRoots(promote_func *fn, ScanContext *sc)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ ByRefInfo *pByRefInfos = m_brInfo;
+ while (pByRefInfos)
+ {
+ if (!CorIsPrimitiveType(pByRefInfos->typ))
+ {
+ TADDR pData = PTR_HOST_MEMBER_TADDR(ByRefInfo, pByRefInfos, data);
+
+ if (pByRefInfos->typeHandle.IsValueType())
+ {
+ ReportPointersFromValueType(fn, sc, pByRefInfos->typeHandle.GetMethodTable(), PTR_VOID(pData));
+ }
+ else
+ {
+ PTR_PTR_Object ppObject = PTR_PTR_Object(pData);
+
+ LOG((LF_GC, INFO3, "ProtectByRefs Frame Promoting" FMT_ADDR "to ", DBG_ADDR(*ppObject)));
+
+ (*fn)(ppObject, sc, CHECK_APP_DOMAIN);
+
+ LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(*ppObject) ));
+ }
+ }
+ pByRefInfos = pByRefInfos->pNext;
+ }
+}
+
+void ProtectValueClassFrame::GcScanRoots(promote_func *fn, ScanContext *sc)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ ValueClassInfo *pVCInfo = m_pVCInfo;
+ while (pVCInfo != NULL)
+ {
+ _ASSERTE(pVCInfo->pMT->IsValueType());
+ ReportPointersFromValueType(fn, sc, pVCInfo->pMT, pVCInfo->pData);
+ pVCInfo = pVCInfo->pNext;
+ }
+}
+
+//
+// Promote Caller Stack
+//
+//
+
+void TransitionFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // I believe this is the contract:
+ //CONTRACTL
+ //{
+ // INSTANCE_CHECK;
+ // NOTHROW;
+ // GC_NOTRIGGER;
+ // FORBID_FAULT;
+ // MODE_ANY;
+ //}
+ //CONTRACTL_END
+
+ MethodDesc *pFunction;
+
+ LOG((LF_GC, INFO3, " Promoting method caller Arguments\n" ));
+
+ // We're going to have to look at the signature to determine
+ // which arguments a are pointers....First we need the function
+ pFunction = GetFunction();
+ if (pFunction == NULL)
+ return;
+
+ // Now get the signature...
+ Signature callSignature = pFunction->GetSignature();
+ if (callSignature.IsEmpty())
+ {
+ return;
+ }
+
+ //If not "vararg" calling convention, assume "default" calling convention
+ if (!MetaSig::IsVarArg(pFunction->GetModule(), callSignature))
+ {
+ MetaSig msig(pFunction);
+ PromoteCallerStackHelper (fn, sc, pFunction, &msig);
+ }
+ else
+ {
+ VASigCookie *varArgSig = GetVASigCookie();
+
+ //Note: no instantiations needed for varargs
+ MetaSig msig(varArgSig->signature,
+ varArgSig->pModule,
+ NULL);
+ PromoteCallerStackHelper (fn, sc, pFunction, &msig);
+ }
+}
+
+void TransitionFrame::PromoteCallerStackHelper(promote_func* fn, ScanContext* sc,
+ MethodDesc *pFunction, MetaSig *pmsig)
+{
+ WRAPPER_NO_CONTRACT;
+ // I believe this is the contract:
+ //CONTRACTL
+ //{
+ // INSTANCE_CHECK;
+ // NOTHROW;
+ // GC_NOTRIGGER;
+ // FORBID_FAULT;
+ // MODE_ANY;
+ //}
+ //CONTRACTL_END
+
+ ArgIterator argit(pmsig);
+
+ TADDR pTransitionBlock = GetTransitionBlock();
+
+ // promote 'this' for non-static methods
+ if (argit.HasThis() && pFunction != NULL)
+ {
+ BOOL interior = pFunction->GetMethodTable()->IsValueType() && !pFunction->IsUnboxingStub();
+
+ PTR_PTR_VOID pThis = dac_cast<PTR_PTR_VOID>(pTransitionBlock + argit.GetThisOffset());
+ LOG((LF_GC, INFO3,
+ " 'this' Argument at " FMT_ADDR "promoted from" FMT_ADDR "\n",
+ DBG_ADDR(pThis), DBG_ADDR(*pThis) ));
+
+ if (interior)
+ PromoteCarefully(fn, PTR_PTR_Object(pThis), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
+ else
+ (fn)(PTR_PTR_Object(pThis), sc, CHECK_APP_DOMAIN);
+ }
+
+ if (argit.HasRetBuffArg())
+ {
+ PTR_PTR_VOID pRetBuffArg = dac_cast<PTR_PTR_VOID>(pTransitionBlock + argit.GetRetBuffArgOffset());
+ LOG((LF_GC, INFO3, " ret buf Argument promoted from" FMT_ADDR "\n", DBG_ADDR(*pRetBuffArg) ));
+ PromoteCarefully(fn, PTR_PTR_Object(pRetBuffArg), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
+ }
+
+ int argOffset;
+ while ((argOffset = argit.GetNextOffset()) != TransitionBlock::InvalidOffset)
+ {
+ ArgDestination argDest(dac_cast<PTR_VOID>(pTransitionBlock), argOffset, argit.GetArgLocDescForStructInRegs());
+ pmsig->GcScanRoots(&argDest, fn, sc);
+ }
+}
+
+#ifdef _TARGET_X86_
+UINT TransitionFrame::CbStackPopUsingGCRefMap(PTR_BYTE pGCRefMap)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ GCRefMapDecoder decoder(pGCRefMap);
+ return decoder.ReadStackPop() * sizeof(TADDR);
+}
+#endif
+
+void TransitionFrame::PromoteCallerStackUsingGCRefMap(promote_func* fn, ScanContext* sc, PTR_BYTE pGCRefMap)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GCRefMapDecoder decoder(pGCRefMap);
+
+#ifdef _TARGET_X86_
+ // Skip StackPop
+ decoder.ReadStackPop();
+#endif
+
+ TADDR pTransitionBlock = GetTransitionBlock();
+
+ while (!decoder.AtEnd())
+ {
+ int pos = decoder.CurrentPos();
+ int token = decoder.ReadToken();
+
+ int ofs;
+
+#ifdef _TARGET_X86_
+ ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
+ (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
+ (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
+#else
+ ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * sizeof(TADDR);
+#endif
+
+ PTR_TADDR ppObj = dac_cast<PTR_TADDR>(pTransitionBlock + ofs);
+
+ switch (token)
+ {
+ case GCREFMAP_SKIP:
+ break;
+ case GCREFMAP_REF:
+ fn(dac_cast<PTR_PTR_Object>(ppObj), sc, CHECK_APP_DOMAIN);
+ break;
+ case GCREFMAP_INTERIOR:
+ PromoteCarefully(fn, dac_cast<PTR_PTR_Object>(ppObj), sc, GC_CALL_INTERIOR | GC_CALL_CHECK_APP_DOMAIN);
+ break;
+ case GCREFMAP_METHOD_PARAM:
+ if (sc->promotion)
+ {
+#ifndef DACCESS_COMPILE
+ MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(*ppObj);
+ if (pMDReal != NULL)
+ GcReportLoaderAllocator(fn, sc, pMDReal->GetLoaderAllocator());
+#endif
+ }
+ break;
+ case GCREFMAP_TYPE_PARAM:
+ if (sc->promotion)
+ {
+#ifndef DACCESS_COMPILE
+ MethodTable *pMTReal = dac_cast<PTR_MethodTable>(*ppObj);
+ if (pMTReal != NULL)
+ GcReportLoaderAllocator(fn, sc, pMTReal->GetLoaderAllocator());
+#endif
+ }
+ break;
+ case GCREFMAP_VASIG_COOKIE:
+ {
+ VASigCookie *varArgSig = dac_cast<PTR_VASigCookie>(*ppObj);
+
+ //Note: no instantiations needed for varargs
+ MetaSig msig(varArgSig->signature,
+ varArgSig->pModule,
+ NULL);
+ PromoteCallerStackHelper (fn, sc, NULL, &msig);
+ }
+ break;
+ default:
+ _ASSERTE(!"Unknown GCREFMAP token");
+ break;
+ }
+ }
+}
+
+void PInvokeCalliFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_GC, INFO3, " Promoting CALLI caller Arguments\n" ));
+
+ // get the signature
+ VASigCookie *varArgSig = GetVASigCookie();
+ if (varArgSig->signature.IsEmpty())
+ {
+ return;
+ }
+
+ // no instantiations needed for varargs
+ MetaSig msig(varArgSig->signature,
+ varArgSig->pModule,
+ NULL);
+ PromoteCallerStackHelper(fn, sc, NULL, &msig);
+}
+
+#ifndef DACCESS_COMPILE
+PInvokeCalliFrame::PInvokeCalliFrame(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
+ : FramedMethodFrame(pTransitionBlock, NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pVASigCookie = pVASigCookie;
+ m_pUnmanagedTarget = pUnmanagedTarget;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+#ifdef FEATURE_COMINTEROP
+
+#ifndef DACCESS_COMPILE
+ComPlusMethodFrame::ComPlusMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD)
+ : FramedMethodFrame(pTransitionBlock, pMD)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+//virtual
+void ComPlusMethodFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // ComPlusMethodFrame is only used in the event call / late bound call code path where we do not have IL stub
+ // so we need to promote the arguments and return value manually.
+
+ FramedMethodFrame::GcScanRoots(fn, sc);
+ PromoteCallerStack(fn, sc);
+
+ MetaSig::RETURNTYPE returnType = GetFunction()->ReturnsObject();
+
+ // Promote the returned object
+ if(returnType == MetaSig::RETOBJ)
+ (*fn)(GetReturnObjectPtr(), sc, CHECK_APP_DOMAIN);
+ else if (returnType == MetaSig::RETBYREF)
+ PromoteCarefully(fn, GetReturnObjectPtr(), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
+}
+#endif // FEATURE_COMINTEROP
+
+#if defined (_DEBUG) && !defined (DACCESS_COMPILE)
+// For IsProtectedByGCFrame, we need to know whether a given object ref is protected
+// by a ComPlusMethodFrame or a ComMethodFrame. Since GCScanRoots for those frames are
+// quite complicated, we don't want to duplicate their logic so we call GCScanRoots with
+// IsObjRefProtected (a fake promote function) and an extended ScanContext to do the checking.
+
+struct IsObjRefProtectedScanContext : public ScanContext
+{
+ OBJECTREF * oref_to_check;
+ BOOL oref_protected;
+ IsObjRefProtectedScanContext (OBJECTREF * oref)
+ {
+ thread_under_crawl = GetThread ();
+ promotion = TRUE;
+ oref_to_check = oref;
+ oref_protected = FALSE;
+ }
+};
+
+void IsObjRefProtected (Object** ppObj, ScanContext* sc, uint32_t)
+{
+ LIMITED_METHOD_CONTRACT;
+ IsObjRefProtectedScanContext * orefProtectedSc = (IsObjRefProtectedScanContext *)sc;
+ if (ppObj == (Object **)(orefProtectedSc->oref_to_check))
+ orefProtectedSc->oref_protected = TRUE;
+}
+
+BOOL TransitionFrame::Protects(OBJECTREF * ppORef)
+{
+ WRAPPER_NO_CONTRACT;
+ IsObjRefProtectedScanContext sc (ppORef);
+ // Set the stack limit for the scan to the SP of the managed frame above the transition frame
+ sc.stack_limit = GetSP();
+ GcScanRoots (IsObjRefProtected, &sc);
+ return sc.oref_protected;
+}
+#endif //defined (_DEBUG) && !defined (DACCESS_COMPILE)
+
+//+----------------------------------------------------------------------------
+//
+// Method: TPMethodFrame::GcScanRoots public
+//
+// Synopsis: GC protects arguments on the stack
+//
+
+//
+//+----------------------------------------------------------------------------
+#ifdef FEATURE_REMOTING
+
+#ifndef DACCESS_COMPILE
+TPMethodFrame::TPMethodFrame(TransitionBlock * pTransitionBlock)
+ : FramedMethodFrame(pTransitionBlock, NULL)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+void TPMethodFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Delegate to FramedMethodFrame
+ FramedMethodFrame::GcScanRoots(fn, sc);
+ FramedMethodFrame::PromoteCallerStack(fn, sc);
+
+ MetaSig::RETURNTYPE returnType = GetFunction()->ReturnsObject();
+
+ // Promote the returned object
+ if(returnType == MetaSig::RETOBJ)
+ {
+ (*fn)(GetReturnObjectPtr(), sc, CHECK_APP_DOMAIN);
+ }
+ else if (returnType == MetaSig::RETBYREF)
+ {
+ PromoteCarefully(fn, GetReturnObjectPtr(), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
+ }
+
+ return;
+}
+
+//+----------------------------------------------------------------------------
+//
+// Method: TPMethodFrame::TraceFrame public
+//
+// Synopsis: Return where the frame will execute next - the result is filled
+// into the given "trace" structure. The frame is responsible for
+// detecting where it is in its execution lifetime.
+//
+//
+
+//
+//+----------------------------------------------------------------------------
+BOOL TPMethodFrame::TraceFrame(Thread *thread, BOOL fromPatch,
+ TraceDestination *trace, REGDISPLAY *regs)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // We want to set a frame patch, unless we're already at the
+ // frame patch, in which case we'll trace stable entrypoint which
+ // should be set by now.
+
+ if (fromPatch)
+ {
+ trace->InitForStub(GetFunction()->GetStableEntryPoint());
+ }
+ else
+ {
+#ifdef DACCESS_COMPILE
+ DacNotImpl();
+#else
+ trace->InitForStub(GetEEFuncEntryPoint(TransparentProxyStubPatchLabel));
+#endif
+ }
+ return TRUE;
+
+}
+#endif // FEATURE_REMOTING
+
+#ifdef FEATURE_COMINTEROP
+
+#ifdef _TARGET_X86_
+// Return the # of stack bytes pushed by the unmanaged caller.
+UINT ComMethodFrame::GetNumCallerStackBytes()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ ComCallMethodDesc* pCMD = PTR_ComCallMethodDesc((TADDR)GetDatum());
+ PREFIX_ASSUME(pCMD != NULL);
+ // assumes __stdcall
+ // compute the callee pop stack bytes
+ return pCMD->GetNumStackBytes();
+}
+#endif // _TARGET_X86_
+
+#ifndef DACCESS_COMPILE
+void ComMethodFrame::DoSecondPassHandlerCleanup(Frame * pCurFrame)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Find ComMethodFrame, noting any ContextTransitionFrame along the way
+
+ while ((pCurFrame != FRAME_TOP) &&
+ (pCurFrame->GetVTablePtr() != ComMethodFrame::GetMethodFrameVPtr()))
+ {
+ if (pCurFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr())
+ {
+ // If there is a context transition before we find a ComMethodFrame, do nothing. Expect that
+ // the AD transition code will perform the corresponding work after it pops its context
+ // transition frame and before it rethrows the exception.
+ return;
+ }
+ pCurFrame = pCurFrame->PtrNextFrame();
+ }
+
+ if (pCurFrame == FRAME_TOP)
+ return;
+
+ ComMethodFrame * pComMethodFrame = (ComMethodFrame *)pCurFrame;
+
+ _ASSERTE(pComMethodFrame != NULL);
+ Thread * pThread = GetThread();
+ GCX_COOP_THREAD_EXISTS(pThread);
+ // Unwind the frames till the entry frame (which was ComMethodFrame)
+ pCurFrame = pThread->GetFrame();
+ while ((pCurFrame != NULL) && (pCurFrame <= pComMethodFrame))
+ {
+ pCurFrame->ExceptionUnwind();
+ pCurFrame = pCurFrame->PtrNextFrame();
+ }
+
+ // At this point, pCurFrame would be the ComMethodFrame's predecessor frame
+ // that we need to reset to.
+ _ASSERTE((pCurFrame != NULL) && (pComMethodFrame->PtrNextFrame() == pCurFrame));
+ pThread->SetFrame(pCurFrame);
+}
+#endif // !DACCESS_COMPILE
+
+#endif // FEATURE_COMINTEROP
+
+
+#ifdef _TARGET_X86_
+
+PTR_UMEntryThunk UMThkCallFrame::GetUMEntryThunk()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return dac_cast<PTR_UMEntryThunk>(GetDatum());
+}
+
+#ifdef DACCESS_COMPILE
+void UMThkCallFrame::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+ UnmanagedToManagedFrame::EnumMemoryRegions(flags);
+
+ // Pieces of the UMEntryThunk need to be saved.
+ UMEntryThunk *pThunk = GetUMEntryThunk();
+ DacEnumMemoryRegion(dac_cast<TADDR>(pThunk), sizeof(UMEntryThunk));
+
+ UMThunkMarshInfo *pMarshInfo = pThunk->GetUMThunkMarshInfo();
+ DacEnumMemoryRegion(dac_cast<TADDR>(pMarshInfo), sizeof(UMThunkMarshInfo));
+}
+#endif
+
+#endif // _TARGET_X86_
+
+#ifndef DACCESS_COMPILE
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
+#endif
+
+// Initialization of HelperMethodFrame.
+void HelperMethodFrame::Push()
+{
+ CONTRACTL {
+ if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ //
+ // Finish initialization
+ //
+
+ // Compiler would not inline GetGSCookiePtr() because of it is virtual method.
+ // Inline it manually and verify that it gives same result.
+ _ASSERTE(GetGSCookiePtr() == (((GSCookie *)(this)) - 1));
+ *(((GSCookie *)(this)) - 1) = GetProcessGSCookie();
+
+ _ASSERTE(!m_MachState.isValid());
+
+ Thread * pThread = ::GetThread();
+ m_pThread = pThread;
+
+ // Push the frame
+ Frame::Push(pThread);
+
+ if (!pThread->HasThreadStateOpportunistic((Thread::ThreadState)(Thread::TS_YieldRequested | Thread::TS_AbortRequested)))
+ return;
+
+ // Outline the slow path for better perf
+ PushSlowHelper();
+}
+
+void HelperMethodFrame::Pop()
+{
+ CONTRACTL {
+ if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ Thread * pThread = m_pThread;
+
+ if ((m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) || !pThread->HasThreadStateOpportunistic(Thread::TS_AbortInitiated))
+ {
+ Frame::Pop(pThread);
+ return;
+ }
+
+ // Outline the slow path for better perf
+ PopSlowHelper();
+}
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // Go back to command line default optimizations
+#endif
+
+NOINLINE void HelperMethodFrame::PushSlowHelper()
+{
+ CONTRACTL {
+ if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ if (!(m_Attribs & FRAME_ATTR_NO_THREAD_ABORT))
+ {
+ if (m_pThread->IsAbortRequested())
+ {
+ m_pThread->HandleThreadAbort();
+ }
+
+ }
+
+ if (m_pThread->IsYieldRequested())
+ {
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+}
+
+NOINLINE void HelperMethodFrame::PopSlowHelper()
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ m_pThread->HandleThreadAbort();
+ Frame::Pop(m_pThread);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+MethodDesc* HelperMethodFrame::GetFunction()
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifndef DACCESS_COMPILE
+ InsureInit(false, NULL);
+ return m_pMD;
+#else
+ if (m_MachState.isValid())
+ {
+ return m_pMD;
+ }
+ else
+ {
+ return ECall::MapTargetBackToMethod(m_FCallEntry);
+ }
+#endif
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Ensures the HelperMethodFrame gets initialized, if not already.
+//
+// Arguments:
+// * initialInit -
+// * true: ensure the simple, first stage of initialization has been completed.
+// This is used when the HelperMethodFrame is first created.
+// * false: complete any initialization that was left to do, if any.
+// * unwindState - [out] DAC builds use this to return the unwound machine state.
+// * hostCallPreference - (See code:HelperMethodFrame::HostCallPreference.)
+//
+// Return Value:
+// Normally, the function always returns TRUE meaning the initialization succeeded.
+//
+// However, if hostCallPreference is NoHostCalls, AND if a callee (like
+// LazyMachState::unwindLazyState) needed to acquire a JIT reader lock and was unable
+// to do so (lest it re-enter the host), then InsureInit will abort and return FALSE.
+// So any callers that specify hostCallPreference = NoHostCalls (which is not the
+// default), should check for FALSE return, and refuse to use the HMF in that case.
+// Currently only asynchronous calls made by profilers use that code path.
+//
+
+BOOL HelperMethodFrame::InsureInit(bool initialInit,
+ MachState * unwindState,
+ HostCallPreference hostCallPreference /* = AllowHostCalls */)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ if ((hostCallPreference == AllowHostCalls) && !m_MachState.isValid()) { HOST_CALLS; } else { HOST_NOCALLS; }
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if (m_MachState.isValid())
+ {
+ return TRUE;
+ }
+
+ _ASSERTE(m_Attribs != 0xCCCCCCCC);
+
+#ifndef DACCESS_COMPILE
+ if (!initialInit)
+ {
+ m_pMD = ECall::MapTargetBackToMethod(m_FCallEntry);
+
+ // if this is an FCall, we should find it
+ _ASSERTE(m_FCallEntry == 0 || m_pMD != 0);
+ }
+#endif
+
+ // Because TRUE FCalls can be called from via reflection, com-interop, etc.,
+ // we can't rely on the fact that we are called from jitted code to find the
+ // caller of the FCALL. Thus FCalls must erect the frame directly in the
+ // FCall. For JIT helpers, however, we can rely on this, and so they can
+ // be sneakier and defer the HelperMethodFrame setup to a called worker method.
+
+ // Work with a copy so that we only write the values once.
+ // this avoids race conditions.
+ LazyMachState* lazy = &m_MachState;
+ DWORD threadId = m_pThread->GetOSThreadId();
+ MachState unwound;
+
+ if (!initialInit &&
+ m_FCallEntry == 0 &&
+ !(m_Attribs & Frame::FRAME_ATTR_EXACT_DEPTH)) // Jit Helper
+ {
+ LazyMachState::unwindLazyState(
+ lazy,
+ &unwound,
+ threadId,
+ 0,
+ hostCallPreference);
+
+#if !defined(DACCESS_COMPILE)
+ if (!unwound.isValid())
+ {
+ // This only happens if LazyMachState::unwindLazyState had to abort as a
+ // result of failing to take a reader lock (because we told it not to yield,
+ // but the writer lock was already held). Since we've not yet updated
+ // m_MachState, this HelperMethodFrame will still be considered not fully
+ // initialized (so a future call into InsureInit() will attempt to complete
+ // initialization again).
+ //
+ // Note that, in DAC builds, the contract with LazyMachState::unwindLazyState
+ // is a bit different, and it's expected that LazyMachState::unwindLazyState
+ // will commonly return an unwound state with _pRetAddr==NULL (which counts
+ // as an "invalid" MachState). So have DAC builds deliberately fall through
+ // rather than aborting when unwound is invalid.
+ _ASSERTE(hostCallPreference == NoHostCalls);
+ return FALSE;
+ }
+#endif // !defined(DACCESS_COMPILE)
+ }
+ else if (!initialInit &&
+ (m_Attribs & Frame::FRAME_ATTR_CAPTURE_DEPTH_2) != 0)
+ {
+ // explictly told depth
+ LazyMachState::unwindLazyState(lazy, &unwound, threadId, 2);
+ }
+ else
+ {
+ // True FCall
+ LazyMachState::unwindLazyState(lazy, &unwound, threadId, 1);
+ }
+
+ _ASSERTE(unwound.isValid());
+
+#if !defined(DACCESS_COMPILE)
+ lazy->setLazyStateFromUnwind(&unwound);
+#else // DACCESS_COMPILE
+ if (unwindState)
+ {
+ *unwindState = unwound;
+ }
+#endif // DACCESS_COMPILE
+
+ return TRUE;
+}
+
+
+#include "comdelegate.h"
+
+Assembly* SecureDelegateFrame::GetAssembly()
+{
+ WRAPPER_NO_CONTRACT;
+
+#if !defined(DACCESS_COMPILE)
+ // obtain the frame off the delegate pointer
+ DELEGATEREF delegate = (DELEGATEREF) GetThis();
+ _ASSERTE(delegate);
+ if (!delegate->IsWrapperDelegate())
+ {
+ MethodDesc* pMethod = (MethodDesc*) delegate->GetMethodPtrAux();
+ Assembly* pAssembly = pMethod->GetAssembly();
+ _ASSERTE(pAssembly != NULL);
+ return pAssembly;
+ }
+ else
+ return NULL;
+#else
+ DacNotImpl();
+ return NULL;
+#endif
+}
+
+BOOL SecureDelegateFrame::TraceFrame(Thread *thread, BOOL fromPatch, TraceDestination *trace, REGDISPLAY *regs)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(!fromPatch);
+
+ // Unlike multicast delegates, secure delegates only call one method. So, we should just return false here
+ // and let the step out logic continue to the caller of the secure delegate stub.
+ LOG((LF_CORDB, LL_INFO1000, "SDF::TF: return FALSE\n"));
+
+ return FALSE;
+}
+
+BOOL MulticastFrame::TraceFrame(Thread *thread, BOOL fromPatch,
+ TraceDestination *trace, REGDISPLAY *regs)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(!fromPatch);
+
+#ifdef DACCESS_COMPILE
+ return FALSE;
+
+#else // !DACCESS_COMPILE
+ LOG((LF_CORDB,LL_INFO10000, "MulticastFrame::TF FromPatch:0x%x, at 0x%x\n", fromPatch, GetControlPC(regs)));
+
+ // At this point we have no way to recover the Stub object from the control pc. We can't use the MD stored
+ // in the MulticastFrame because it points to the dummy Invoke() method, not the method we want to call.
+
+ BYTE *pbDel = NULL;
+ int delegateCount = 0;
+
+#if defined(_TARGET_X86_)
+ // At this point the counter hasn't been incremented yet.
+ delegateCount = *regs->pEdi + 1;
+ pbDel = *(BYTE **)( (size_t)*(regs->pEsi) + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
+#elif defined(_TARGET_AMD64_)
+ // At this point the counter hasn't been incremented yet.
+ delegateCount = (int)regs->pCurrentContext->Rdi + 1;
+ pbDel = *(BYTE **)( (size_t)(regs->pCurrentContext->Rsi) + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
+#elif defined(_TARGET_ARM_)
+ // At this point the counter has not yet been incremented. Counter is in R7, frame pointer in R4.
+ delegateCount = regs->pCurrentContext->R7 + 1;
+ pbDel = *(BYTE **)( (size_t)(regs->pCurrentContext->R4) + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
+#else
+ delegateCount = 0;
+ PORTABILITY_ASSERT("MulticastFrame::TraceFrame (frames.cpp)");
+#endif
+
+ int totalDelegateCount = (int)*(size_t*)(pbDel + DelegateObject::GetOffsetOfInvocationCount());
+
+ _ASSERTE( COMDelegate::IsTrueMulticastDelegate( ObjectToOBJECTREF((Object*)pbDel) ) );
+
+ if (delegateCount == totalDelegateCount)
+ {
+ LOG((LF_CORDB, LL_INFO1000, "MF::TF: Executed all stubs, should return\n"));
+ // We've executed all the stubs, so we should return
+ return FALSE;
+ }
+ else
+ {
+ // We're going to execute stub delegateCount next, so go and grab it.
+ BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList());
+
+ pbDel = *(BYTE**)( ((ArrayBase *)pbDelInvocationList)->GetDataPtr() +
+ ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount);
+
+ _ASSERTE(pbDel);
+ return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace);
+ }
+#endif // !DACCESS_COMPILE
+}
+
+#ifndef DACCESS_COMPILE
+
+VOID InlinedCallFrame::Init()
+{
+ WRAPPER_NO_CONTRACT;
+
+ *((TADDR *)this) = GetMethodFrameVPtr();
+
+ // GetGSCookiePtr contains a virtual call and this is a perf critical method so we don't want to call it in ret builds
+ GSCookie *ptrGS = (GSCookie *)((BYTE *)this - sizeof(GSCookie));
+ _ASSERTE(ptrGS == GetGSCookiePtr());
+
+ *ptrGS = GetProcessGSCookie();
+
+ m_Datum = NULL;
+ m_pCallSiteSP = NULL;
+ m_pCallerReturnAddress = NULL;
+}
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+#if defined(_WIN64) && !defined(FEATURE_PAL)
+
+EXTERN_C void PInvokeStubForHostInner(DWORD dwStackSize, LPVOID pStackFrame, LPVOID pTarget);
+
+// C++ piece of one static stub for host on 64-bit. This is called by PInvokeStubForHost (PInvokeStubs.asm).
+void __stdcall PInvokeStubForHostWorker(DWORD dwStackSize, LPVOID pStackFrame, LPVOID pThis)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ ENTRY_POINT;
+ }
+ CONTRACTL_END;
+
+ InlinedCallFrame *pFrame = (InlinedCallFrame *)GetThread()->GetFrame();
+ _ASSERTE(InlinedCallFrame::FrameHasActiveCall(pFrame));
+
+ LPVOID pTarget = NULL;
+ MethodDesc *pMD = pFrame->GetFunction();
+
+ if (pMD == NULL)
+ {
+ // This is a CALLI and m_Datum is a mangled target
+ pTarget = (LPVOID)((UINT_PTR)pFrame->m_Datum >> 1);
+ }
+ else if (pMD->IsNDirect())
+ {
+ pTarget = ((NDirectMethodDesc *)pMD)->ndirect.m_pNativeNDirectTarget;
+ if (pMD->IsQCall())
+ {
+#ifdef FEATURE_STACK_PROBE
+ // We need just the stack probe for QCalls
+ RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT));
+#endif
+ PInvokeStubForHostInner(dwStackSize, pStackFrame, pTarget);
+ return;
+ }
+ }
+#ifdef FEATURE_COMINTEROP
+ else if (pMD->IsComPlusCall() || pMD->IsEEImpl())
+ {
+ LPVOID *lpVtbl = *(LPVOID **)pThis;
+ ComPlusCallInfo *pComPlusCallInfo = ComPlusCallInfo::FromMethodDesc(pMD);
+ pTarget = lpVtbl[pComPlusCallInfo->m_cachedComSlot];
+ }
+#endif // FEATURE_COMINTEROP
+ else
+ {
+ UNREACHABLE_MSG("Unexpected MethodDesc kind encountered in PInvokeStubForHostWorker");
+ }
+
+ // We ask the host on every call. This is different from x86 but we keep this
+ // behavior for maximum compatibility with previous releases.
+ if (CallNeedsHostHook((size_t)pTarget))
+ {
+ // call LeaveRuntime
+ class LeaveRuntimeHolderThrowComplus
+ {
+ public:
+ LeaveRuntimeHolderThrowComplus(size_t target)
+ {
+ Thread::LeaveRuntimeThrowComplus(target);
+ }
+ ~LeaveRuntimeHolderThrowComplus()
+ {
+ Thread::EnterRuntime();
+ }
+ } holder((size_t)pTarget);
+
+ PInvokeStubForHostInner(dwStackSize, pStackFrame, pTarget);
+ // ~LeaveRuntimeHolderThrowComplus calls EnterRuntime here
+ }
+ else
+ {
+ // The host doesn't want to be notified - just call the target
+ PInvokeStubForHostInner(dwStackSize, pStackFrame, pTarget);
+ }
+}
+#endif // _WIN64 && !FEATURE_PAL
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+
+
+void UnmanagedToManagedFrame::ExceptionUnwind()
+{
+ WRAPPER_NO_CONTRACT;
+
+ AppDomain::ExceptionUnwind(this);
+}
+
+#endif // !DACCESS_COMPILE
+
+void ContextTransitionFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Don't check app domains here - m_ReturnExecutionContext is in the parent frame's app domain
+ (*fn)(dac_cast<PTR_PTR_Object>(PTR_HOST_MEMBER_TADDR(ContextTransitionFrame, this, m_ReturnExecutionContext)), sc, 0);
+ LOG((LF_GC, INFO3, " " FMT_ADDR "\n", DBG_ADDR(m_ReturnExecutionContext) ));
+
+ // Don't check app domains here - m_LastThrownObjectInParentContext is in the parent frame's app domain
+ (*fn)(dac_cast<PTR_PTR_Object>(PTR_HOST_MEMBER_TADDR(ContextTransitionFrame, this, m_LastThrownObjectInParentContext)), sc, 0);
+ LOG((LF_GC, INFO3, " " FMT_ADDR "\n", DBG_ADDR(m_LastThrownObjectInParentContext) ));
+
+ // don't need to worry about the object moving as it is stored in a weak handle
+ // but do need to report it so it doesn't get collected if the only reference to
+ // it is in this frame. So only do something if are in promotion phase. And if are
+ // in reloc phase this could cause invalid refs as the object may have been moved.
+ if (! sc->promotion)
+ return;
+
+ // The dac only cares about strong references at the moment. Since this is always
+ // in a weak ref, we don't report it here.
+#if defined(FEATURE_REMOTING) && !defined(DACCESS_COMPILE)
+ Context *returnContext = GetReturnContext();
+ PREFIX_ASSUME(returnContext != NULL);
+ _ASSERTE(returnContext);
+ _ASSERTE(returnContext->GetDomain()); // this will make sure is a valid pointer
+
+ // In the VM we operate on a local to avoid double relocation. In the dac we actually want
+ // to know the real location.
+#ifdef DACCESS_COMPILE
+ PTR_PTR_Object ppRef = returnContext->GetExposedObjectRawUncheckedPtr();
+
+ if (*ppRef == NULL)
+ return;
+
+ (*fn)(ppRef, sc, 0);
+
+#else // DACCESS_COMPILE
+ // We are in the middle of the GC. OBJECTREFs can't be used here since their built-in validation
+ // chokes on objects that have been relocated already
+ Object *pRef = returnContext->GetExposedObjectRawUnchecked();
+
+ if (pRef == NULL)
+ return;
+
+ LOG((LF_GC, INFO3, "ContextTransitionFrame Protection Frame Promoting" FMT_ADDR "to ", DBG_ADDR(pRef) ));
+ // Don't check app domains here - the objects are in the parent frame's app domain
+
+ (*fn)(&pRef, sc, 0);
+ LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(pRef)));
+#endif // !DACCESS_COMPILE
+
+#endif
+}
+
+
+PCODE UnmanagedToManagedFrame::GetReturnAddress()
+{
+ WRAPPER_NO_CONTRACT;
+
+ PCODE pRetAddr = Frame::GetReturnAddress();
+
+ if (InlinedCallFrame::FrameHasActiveCall(m_Next) &&
+ pRetAddr == m_Next->GetReturnAddress())
+ {
+ // there's actually no unmanaged code involved - we were called directly
+ // from managed code using an InlinedCallFrame
+ return NULL;
+ }
+ else
+ {
+ return pRetAddr;
+ }
+}