summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>2016-07-20 21:11:30 -0700
committerGitHub <noreply@github.com>2016-07-20 21:11:30 -0700
commit0d5eac078847baebb32653655373086d6729f923 (patch)
tree88141f3f44e841444bf6af772b29d28d0f94c6e2
parent32a020e47a0797bba4167e85efc38a34397f633e (diff)
parentf915aebaf5db0b829f062dc9940e23bb5c38d575 (diff)
downloadcoreclr-0d5eac078847baebb32653655373086d6729f923.tar.gz
coreclr-0d5eac078847baebb32653655373086d6729f923.tar.bz2
coreclr-0d5eac078847baebb32653655373086d6729f923.zip
Merge pull request #6325 from swaroop-sridhar/R2Rver
GCInfo: Support versioning.
-rw-r--r--src/ToolBox/SOS/Strike/disasm.cpp12
-rw-r--r--src/ToolBox/SOS/Strike/disasm.h8
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM.cpp6
-rw-r--r--src/ToolBox/SOS/Strike/disasmARM64.cpp6
-rw-r--r--src/ToolBox/SOS/Strike/exts.h5
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp8
-rw-r--r--src/debug/daccess/daccess.cpp2
-rw-r--r--src/debug/daccess/enummem.cpp5
-rw-r--r--src/debug/daccess/nidump.cpp13
-rw-r--r--src/debug/daccess/request.cpp2
-rw-r--r--src/gcdump/gcdump.cpp11
-rw-r--r--src/gcdump/gcdumpnonx86.cpp12
-rw-r--r--src/gcinfo/gcinfodumper.cpp14
-rw-r--r--src/inc/eetwain.h10
-rw-r--r--src/inc/gcdecoder.cpp2
-rw-r--r--src/inc/gcdump.h15
-rw-r--r--src/inc/gcinfo.h249
-rw-r--r--src/inc/gcinfodecoder.h6
-rw-r--r--src/inc/gcinfodumper.h4
-rw-r--r--src/inc/gcinfotypes.h270
-rw-r--r--src/jit/gcencode.cpp8
-rw-r--r--src/jit/jitgcinfo.h4
-rw-r--r--src/vm/codeman.cpp50
-rw-r--r--src/vm/codeman.h34
-rw-r--r--src/vm/debughelp.cpp10
-rw-r--r--src/vm/eedbginterfaceimpl.cpp6
-rw-r--r--src/vm/eetwain.cpp63
-rw-r--r--src/vm/gccover.cpp20
-rw-r--r--src/vm/gccover.h2
-rw-r--r--src/vm/gcenv.ee.cpp10
-rw-r--r--src/vm/gcinfodecoder.cpp16
-rw-r--r--src/vm/stackwalk.h7
32 files changed, 481 insertions, 409 deletions
diff --git a/src/ToolBox/SOS/Strike/disasm.cpp b/src/ToolBox/SOS/Strike/disasm.cpp
index 097f4cd14c..e141f8038f 100644
--- a/src/ToolBox/SOS/Strike/disasm.cpp
+++ b/src/ToolBox/SOS/Strike/disasm.cpp
@@ -9,6 +9,7 @@
// ==--==
#include "strike.h"
+#include "gcinfo.h"
#include "util.h"
#include <dbghelp.h>
#include <limits.h>
@@ -1058,10 +1059,11 @@ void PrintNothing (const char *fmt, ...)
///
/// Dump X86 GCInfo header and table
///
-void X86Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void X86Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
X86GCDump::InfoHdr header;
- X86GCDump::GCDump gcDump(encBytes, 5, true);
+ X86GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
+ BYTE* pTable = dac_cast<PTR_BYTE>(gcInfoToken.Info);
if (bPrintHeader)
{
gcDump.gcPrintf = gcPrintf;
@@ -1107,17 +1109,17 @@ LPCSTR AMD64Machine::s_SPName = "RSP";
///
/// Dump AMD64 GCInfo table
///
-void AMD64Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void AMD64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
if (bPrintHeader)
{
ExtOut("Pointer table:\n");
}
- GCDump gcDump(encBytes, 5, true);
+ GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
}
#endif // SOS_TARGET_AMD64
diff --git a/src/ToolBox/SOS/Strike/disasm.h b/src/ToolBox/SOS/Strike/disasm.h
index 6972c39ccb..59fc168a6e 100644
--- a/src/ToolBox/SOS/Strike/disasm.h
+++ b/src/ToolBox/SOS/Strike/disasm.h
@@ -159,7 +159,7 @@ public:
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
X86Machine() {}
@@ -225,7 +225,7 @@ public:
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
ARMMachine() {}
@@ -293,7 +293,7 @@ public:
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs); }
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
AMD64Machine() {}
@@ -357,7 +357,7 @@ public:
virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const
{ _ASSERTE(cntRegs != NULL); *regNames = s_GCRegs; *cntRegs = _countof(s_GCRegs);}
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const;
private:
ARM64Machine() {}
diff --git a/src/ToolBox/SOS/Strike/disasmARM.cpp b/src/ToolBox/SOS/Strike/disasmARM.cpp
index 80dce71890..a82d4b9b65 100644
--- a/src/ToolBox/SOS/Strike/disasmARM.cpp
+++ b/src/ToolBox/SOS/Strike/disasmARM.cpp
@@ -607,7 +607,7 @@ BOOL ARMMachine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CRO
///
/// Dump ARM GCInfo table
///
-void ARMMachine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
#ifndef FEATURE_PAL
if (bPrintHeader)
@@ -615,10 +615,10 @@ void ARMMachine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrint
ExtOut("Pointer table:\n");
}
- ARMGCDump::GCDump gcDump(encBytes, 5, true);
+ ARMGCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
#endif // !FEATURE_PAL
}
diff --git a/src/ToolBox/SOS/Strike/disasmARM64.cpp b/src/ToolBox/SOS/Strike/disasmARM64.cpp
index 2c581bc946..4ac8c59105 100644
--- a/src/ToolBox/SOS/Strike/disasmARM64.cpp
+++ b/src/ToolBox/SOS/Strike/disasmARM64.cpp
@@ -377,16 +377,16 @@ BOOL ARM64Machine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, C
///
/// Dump ARM GCInfo table
///
-void ARM64Machine::DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
+void ARM64Machine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
{
if (bPrintHeader)
{
ExtOut("Pointer table:\n");
}
- ARM64GCDump::GCDump gcDump(encBytes, 5, true);
+ ARM64GCDump::GCDump gcDump(gcInfoToken.Version, encBytes, 5, true);
gcDump.gcPrintf = gcPrintf;
- gcDump.DumpGCTable(pTable, methodSize, 0);
+ gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
}
diff --git a/src/ToolBox/SOS/Strike/exts.h b/src/ToolBox/SOS/Strike/exts.h
index baef6d7084..36b5230c37 100644
--- a/src/ToolBox/SOS/Strike/exts.h
+++ b/src/ToolBox/SOS/Strike/exts.h
@@ -23,7 +23,6 @@
#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
#endif
#include "strike.h"
-
#include <wdbgexts.h>
#include <dbgeng.h>
#include <stdio.h>
@@ -43,6 +42,8 @@
// the DAC to read the DAC-ized data structures.
#include "daccess.h"
+#include "gcinfo.h"
+
// Convert between CLRDATA_ADDRESS and TADDR.
#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
@@ -386,7 +387,7 @@ public:
typedef void (*printfFtn)(const char* fmt, ...);
// Dumps the GCInfo
- virtual void DumpGCInfo(BYTE* pTable, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
+ virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
protected:
IMachine() {}
diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp
index 3b0086fdb7..df4a18443f 100644
--- a/src/ToolBox/SOS/Strike/strike.cpp
+++ b/src/ToolBox/SOS/Strike/strike.cpp
@@ -8017,10 +8017,10 @@ DECLARE_API(GCInfo)
// Mutable table pointer since we need to pass the appropriate
// offset into the table to DumpGCTable.
- BYTE *pTable = table;
+ GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
- g_targetMachine->DumpGCInfo(pTable, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
+ g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
return Status;
}
@@ -8101,8 +8101,8 @@ void DecodeGCTableEntry (const char *fmt, ...)
VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
{
GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
-
- g_targetMachine->DumpGCInfo(pInfo->table, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
+ GCInfoToken gcInfoToken = { pInfo, GCINFO_VERSION };
+ g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
pInfo->fDoneDecoding = true;
SwitchToFiber(pInfo->pvMainFiber);
diff --git a/src/debug/daccess/daccess.cpp b/src/debug/daccess/daccess.cpp
index 20395f0018..ba3995b1f7 100644
--- a/src/debug/daccess/daccess.cpp
+++ b/src/debug/daccess/daccess.cpp
@@ -6012,7 +6012,7 @@ ClrDataAccess::GetMethodExtents(MethodDesc* methodDesc,
EECodeInfo codeInfo(methodStart);
_ASSERTE(codeInfo.IsValid());
- TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfo());
+ TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
*extents = new (nothrow) METH_EXTENTS;
if (!*extents)
diff --git a/src/debug/daccess/enummem.cpp b/src/debug/daccess/enummem.cpp
index f88fb628ba..068c2f2b13 100644
--- a/src/debug/daccess/enummem.cpp
+++ b/src/debug/daccess/enummem.cpp
@@ -979,10 +979,11 @@ HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags,
codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo);
// The stackwalker needs GC info to find the parent 'stack pointer' or PSP
- PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(codeInfo.GetGCInfo());
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info);
if (pGCInfo != NULL)
{
- GcInfoDecoder gcDecoder(pGCInfo, DECODE_PSP_SYM, 0);
+ GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0);
DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true);
}
}
diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
index 6de9ec0b94..d151a54212 100644
--- a/src/debug/daccess/nidump.cpp
+++ b/src/debug/daccess/nidump.cpp
@@ -3093,7 +3093,8 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
unsigned gcInfoSize = UINT_MAX;
//parse GCInfo for size information.
- PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(mi.GetGCInfo());
+ GCInfoToken gcInfoToken = mi.GetGCInfoToken();
+ PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(gcInfoToken.Info);
void (* stringOutFn)(const char *, ...);
IF_OPT(GC_INFO)
@@ -3108,10 +3109,10 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
{
PTR_CBYTE curGCInfoPtr = gcInfo;
g_holdStringOutData.Clear();
- GCDump gcDump;
+ GCDump gcDump(gcInfoToken.Version);
gcDump.gcPrintf = stringOutFn;
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
- GcInfoDecoder gcInfoDecoder(curGCInfoPtr, DECODE_CODE_LENGTH, 0);
+ GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH, 0);
methodSize = gcInfoDecoder.GetCodeLength();
#endif
@@ -3119,7 +3120,7 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
#ifdef _TARGET_X86_
InfoHdr hdr;
stringOutFn( "method info Block:\n" );
- curGCInfoPtr += gcDump.DumpInfoHdr(curGCInfoPtr, &hdr, &methodSize, 0);
+ curGCInfoPtr += gcDump.DumpInfoHdr(PTR_CBYTE(gcInfoToken.Info), &hdr, &methodSize, 0);
stringOutFn( "\n" );
#endif
@@ -9436,10 +9437,10 @@ void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNC
{
PTR_CBYTE curGCInfoPtr = gcInfo;
g_holdStringOutData.Clear();
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = stringOutFn;
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
- GcInfoDecoder gcInfoDecoder(curGCInfoPtr, DECODE_CODE_LENGTH, 0);
+ GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, GCINFO_VERSION }, DECODE_CODE_LENGTH, 0);
methodSize = gcInfoDecoder.GetCodeLength();
#endif
diff --git a/src/debug/daccess/request.cpp b/src/debug/daccess/request.cpp
index 9e864769c4..62dd5f51f9 100644
--- a/src/debug/daccess/request.cpp
+++ b/src/debug/daccess/request.cpp
@@ -1148,7 +1148,7 @@ ClrDataAccess::GetCodeHeaderData(CLRDATA_ADDRESS ip, struct DacpCodeHeaderData *
codeHeaderData->MethodStart =
(CLRDATA_ADDRESS) codeInfo.GetStartAddress();
- size_t methodSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfo());
+ size_t methodSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
_ASSERTE(FitsIn<DWORD>(methodSize));
codeHeaderData->MethodSize = static_cast<DWORD>(methodSize);
diff --git a/src/gcdump/gcdump.cpp b/src/gcdump/gcdump.cpp
index d2fda049fc..1c512c88e0 100644
--- a/src/gcdump/gcdump.cpp
+++ b/src/gcdump/gcdump.cpp
@@ -18,8 +18,9 @@
-GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
- : fDumpEncBytes (encBytes ),
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion (gcInfoVer),
+ fDumpEncBytes (encBytes ),
cMaxEncBytes (maxEncBytes ),
fDumpCodeOffsets(dumpCodeOffs)
{
@@ -32,7 +33,7 @@ GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
* Display the byte encodings for the given range of the GC tables.
*/
-PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
+PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE gcInfoBlock, int cDumpBytes)
{
_ASSERTE((cDumpBytes >= 0) && (cMaxEncBytes < 256));
@@ -42,7 +43,7 @@ PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
unsigned count;
int cBytesLeft;
- for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = table;
+ for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = gcInfoBlock;
count > 0;
count--, pCurPos++, cBytesLeft--)
{
@@ -60,7 +61,7 @@ PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE table, int cDumpBytes)
gcPrintf("| ");
}
- return table + cDumpBytes;
+ return gcInfoBlock + cDumpBytes;
}
/*****************************************************************************/
diff --git a/src/gcdump/gcdumpnonx86.cpp b/src/gcdump/gcdumpnonx86.cpp
index 8167d3abd8..b6562d9892 100644
--- a/src/gcdump/gcdumpnonx86.cpp
+++ b/src/gcdump/gcdumpnonx86.cpp
@@ -78,8 +78,9 @@ PCSTR GetRegName (UINT32 regnum)
/*****************************************************************************/
-GCDump::GCDump(bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
- : fDumpEncBytes (encBytes ),
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion(gcInfoVer),
+ fDumpEncBytes (encBytes ),
cMaxEncBytes (maxEncBytes ),
fDumpCodeOffsets(dumpCodeOffs)
{
@@ -270,11 +271,12 @@ BOOL StackSlotStateChangeCallback (
}
-size_t GCDump::DumpGCTable(PTR_CBYTE table,
+size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
unsigned methodSize,
bool verifyGCTables)
{
- GcInfoDecoder hdrdecoder(table,
+ GCInfoToken gcInfoToken = { dac_cast<PTR_VOID>(gcInfoBlock), gcInfoVersion };
+ GcInfoDecoder hdrdecoder(gcInfoToken,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_GS_COOKIE
| DECODE_CODE_LENGTH
@@ -439,7 +441,7 @@ size_t GCDump::DumpGCTable(PTR_CBYTE table,
UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
gcPrintf("Code size: %x\n", cbEncodedMethodSize);
- GcInfoDumper dumper(table);
+ GcInfoDumper dumper(gcInfoToken);
GcInfoDumpState state;
state.LastCodeOffset = -1;
diff --git a/src/gcinfo/gcinfodumper.cpp b/src/gcinfo/gcinfodumper.cpp
index 432e7066ce..4e31871f67 100644
--- a/src/gcinfo/gcinfodumper.cpp
+++ b/src/gcinfo/gcinfodumper.cpp
@@ -21,9 +21,9 @@
#error pick suitable ADDRESS_SPACING for platform
#endif
-GcInfoDumper::GcInfoDumper (PTR_CBYTE pbGCInfo)
+GcInfoDumper::GcInfoDumper (GCInfoToken gcInfoToken)
{
- m_pbGCInfo = pbGCInfo;
+ m_gcTable = gcInfoToken;
m_pRecords = NULL;
m_gcInfoSize = 0;
}
@@ -492,7 +492,7 @@ GcInfoDumper::EnumerateStateChangesResults GcInfoDumper::EnumerateStateChanges (
//
// Decode header information
//
- GcInfoDecoder hdrdecoder(m_pbGCInfo,
+ GcInfoDecoder hdrdecoder(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_GC_LIFETIMES
@@ -617,11 +617,11 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
//
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
- GcInfoDecoder safePointDecoder(m_pbGCInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(m_gcTable, (GcInfoDecoderFlags)0, 0);
#endif
{
- GcInfoDecoder untrackedDecoder(m_pbGCInfo, DECODE_GC_LIFETIMES, 0);
+ GcInfoDecoder untrackedDecoder(m_gcTable, DECODE_GC_LIFETIMES, 0);
untrackedDecoder.EnumerateUntrackedSlots(&regdisp,
0,
&LivePointerCallback,
@@ -646,7 +646,7 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
{
BOOL fNewInterruptible = FALSE;
- GcInfoDecoder decoder1(m_pbGCInfo,
+ GcInfoDecoder decoder1(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_VARARG
@@ -680,7 +680,7 @@ PORTABILITY_ASSERT("GcInfoDumper::EnumerateStateChanges is not implemented on th
}
#endif
- GcInfoDecoder decoder2(m_pbGCInfo,
+ GcInfoDecoder decoder2(m_gcTable,
(GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
| DECODE_CODE_LENGTH
| DECODE_VARARG
diff --git a/src/inc/eetwain.h b/src/inc/eetwain.h
index a7bab8701e..6e183c5546 100644
--- a/src/inc/eetwain.h
+++ b/src/inc/eetwain.h
@@ -30,6 +30,7 @@
#include "corjit.h" // For NativeVarInfo
#include "stackwalktypes.h"
#include "bitvector.h"
+#include "gcinfotypes.h"
#if !defined(_TARGET_X86_)
#define USE_GC_INFO_DECODER
@@ -218,7 +219,7 @@ virtual bool IsGcSafe(EECodeInfo *pCodeInfo,
*/
virtual unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr) = 0;
+ GCInfoToken gcInfoToken) = 0;
#endif // _TARGET_AMD64_ && _DEBUG
/*
@@ -293,7 +294,7 @@ virtual bool IsInSynchronizedRegion(
not take procedure splitting into account). For the actual size of
the hot region call IJitManager::JitTokenToMethodHotSize.
*/
-virtual size_t GetFunctionSize(PTR_VOID methodInfoPtr) = 0;
+virtual size_t GetFunctionSize(GCInfoToken gcInfoToken) = 0;
/*
Returns the size of the frame (barring localloc)
@@ -447,7 +448,7 @@ bool IsGcSafe( EECodeInfo *pCodeInfo,
virtual
unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr);
+ GCInfoToken gcInfoToken);
#endif // _TARGET_AMD64_ && _DEBUG
/*
@@ -551,8 +552,7 @@ bool IsInSynchronizedRegion(
Returns the size of a given function.
*/
virtual
-size_t GetFunctionSize(
- PTR_VOID methodInfoPtr);
+size_t GetFunctionSize(GCInfoToken gcInfoToken);
/*
Returns the size of the frame (barring localloc)
diff --git a/src/inc/gcdecoder.cpp b/src/inc/gcdecoder.cpp
index 7472c9aa62..d337faeebc 100644
--- a/src/inc/gcdecoder.cpp
+++ b/src/inc/gcdecoder.cpp
@@ -18,7 +18,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
/* This file is shared between the VM and JIT/IL and SOS/Strike directories */
-#include "gcinfo.h"
+#include "gcinfotypes.h"
/*****************************************************************************/
/*
diff --git a/src/inc/gcdump.h b/src/inc/gcdump.h
index aded6bb102..cd73940ded 100644
--- a/src/inc/gcdump.h
+++ b/src/inc/gcdump.h
@@ -17,7 +17,7 @@
#define __GCDUMP_H__
/*****************************************************************************/
-#include "gcinfo.h" // For InfoHdr
+#include "gcinfotypes.h" // For InfoHdr
#ifndef FASTCALL
#ifndef FEATURE_PAL
@@ -32,7 +32,8 @@ class GCDump
{
public:
- GCDump (bool encBytes = true,
+ GCDump (UINT32 gcInfoVersion,
+ bool encBytes = true,
unsigned maxEncBytes = 5,
bool dumpCodeOffs = true);
@@ -44,7 +45,7 @@ public:
* Return value : Size in bytes of the header encoding
*/
- unsigned FASTCALL DumpInfoHdr (PTR_CBYTE table,
+ unsigned FASTCALL DumpInfoHdr (PTR_CBYTE gcInfoBlock,
InfoHdr * header, /* OUT */
unsigned * methodSize, /* OUT */
bool verifyGCTables = false);
@@ -52,13 +53,12 @@ public:
/*-------------------------------------------------------------------------
* Dumps the GC tables to 'stdout'
- * table : Ptr to the start of the table part of the GC info.
- * This immediately follows the GCinfo header
+ * table : The GCInfoToken
* verifyGCTables : If the JIT has been compiled with VERIFY_GC_TABLES
* Return value : Size in bytes of the GC table encodings
*/
- size_t FASTCALL DumpGCTable (PTR_CBYTE table,
+ size_t FASTCALL DumpGCTable (PTR_CBYTE gcInfoBlock,
#ifdef _TARGET_X86_
const InfoHdr& header,
#endif
@@ -79,6 +79,7 @@ public:
public:
typedef void (*printfFtn)(const char* fmt, ...);
printfFtn gcPrintf;
+ UINT32 gcInfoVersion;
//-------------------------------------------------------------------------
protected:
@@ -89,7 +90,7 @@ protected:
/* Helper methods */
- PTR_CBYTE DumpEncoding(PTR_CBYTE table,
+ PTR_CBYTE DumpEncoding(PTR_CBYTE gcInfoBlock,
int cDumpBytes);
void DumpOffset (unsigned o);
void DumpOffsetEx(unsigned o);
diff --git a/src/inc/gcinfo.h b/src/inc/gcinfo.h
index bb80620f31..500e1b7a02 100644
--- a/src/inc/gcinfo.h
+++ b/src/inc/gcinfo.h
@@ -8,11 +8,8 @@
#define _GCINFO_H_
/*****************************************************************************/
-#include <stdlib.h> // For memcmp()
-#include "windef.h" // For BYTE
#include "daccess.h"
-
-#include "bitvector.h" // for ptrArgTP
+#include "windef.h" // For BYTE
// Some declarations in this file are used on non-x86 platforms, but most are x86-specific.
@@ -31,234 +28,32 @@ const unsigned byref_OFFSET_FLAG = 0x1; // the offset is an interior ptr
const unsigned pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr
const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this"
-#ifdef _TARGET_X86_
-
-#ifndef FASTCALL
-#define FASTCALL __fastcall
-#endif
+//-----------------------------------------------------------------------------
+// The current GCInfo Version
+//-----------------------------------------------------------------------------
-// we use offsetof to get the offset of a field
-#include <stddef.h> // offsetof
-#ifndef offsetof
-#define offsetof(s,m) ((size_t)&(((s *)0)->m))
-#endif
-
-enum infoHdrAdjustConstants {
- // Constants
- SET_FRAMESIZE_MAX = 7,
- SET_ARGCOUNT_MAX = 8, // Change to 6
- SET_PROLOGSIZE_MAX = 16,
- SET_EPILOGSIZE_MAX = 10, // Change to 6
- SET_EPILOGCNT_MAX = 4,
- SET_UNTRACKED_MAX = 3
-};
+#define GCINFO_VERSION 1
+//-----------------------------------------------------------------------------
+// GCInfoToken: A wrapper that contains the GcInfo data and version number.
//
-// Enum to define the 128 codes that are used to incrementally adjust the InfoHdr structure
+// The version# is not stored in the GcInfo structure -- because it is
+// wasteful to store the version once for every method.
+// Instead, the version# istracked per range-section of generated/loaded methods.
//
-enum infoHdrAdjust {
-
- SET_FRAMESIZE = 0, // 0x00
- SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08
- SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11
- SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22
- SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d
- SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37
-
- FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1,
-
- FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b
- FLIP_ESI_SAVED, // 0x3c
- FLIP_EBX_SAVED, // 0x3d
- FLIP_EBP_SAVED, // 0x3e
- FLIP_EBP_FRAME, // 0x3f
- FLIP_INTERRUPTIBLE, // 0x40
- FLIP_DOUBLE_ALIGN, // 0x41
- FLIP_SECURITY, // 0x42
- FLIP_HANDLERS, // 0x43
- FLIP_LOCALLOC, // 0x44
- FLIP_EDITnCONTINUE, // 0x45
- FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding
- FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding
- FLIP_VARARGS, // 0x48
- FLIP_PROF_CALLBACKS, // 0x49
- FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding
- FLIP_SYNC, // 0x4B
- FLIP_HAS_GENERICS_CONTEXT,// 0x4C
- FLIP_GENERICS_CONTEXT_IS_METHODDESC,// 0x4D
-
- // 0x4E .. 0x4f unused
-
- NEXT_FOUR_START = 0x50,
- NEXT_FOUR_FRAMESIZE = 0x50,
- NEXT_FOUR_ARGCOUNT = 0x60,
- NEXT_THREE_PROLOGSIZE = 0x70,
- NEXT_THREE_EPILOGSIZE = 0x78
-};
-
-#define HAS_UNTRACKED ((unsigned int) -1)
-#define HAS_VARPTR ((unsigned int) -1)
-// 0 is not a valid offset for EBP-frames as all locals are at a negative offset
-// For ESP frames, the cookie is above (at a higher address than) the buffers,
-// and so cannot be at offset 0.
-#define INVALID_GS_COOKIE_OFFSET 0
-// Temporary value to indicate that the offset needs to be read after the header
-#define HAS_GS_COOKIE_OFFSET ((unsigned int) -1)
-
-// 0 is not a valid sync offset
-#define INVALID_SYNC_OFFSET 0
-// Temporary value to indicate that the offset needs to be read after the header
-#define HAS_SYNC_OFFSET ((unsigned int) -1)
-
-#define INVALID_ARGTAB_OFFSET 0
-
-#include <pshpack1.h>
-
-// Working set optimization: saving 12 * 128 = 1536 bytes in infoHdrShortcut
-struct InfoHdr;
-
-struct InfoHdrSmall {
- unsigned char prologSize; // 0
- unsigned char epilogSize; // 1
- unsigned char epilogCount : 3; // 2 [0:2]
- unsigned char epilogAtEnd : 1; // 2 [3]
- unsigned char ediSaved : 1; // 2 [4] which callee-saved regs are pushed onto stack
- unsigned char esiSaved : 1; // 2 [5]
- unsigned char ebxSaved : 1; // 2 [6]
- unsigned char ebpSaved : 1; // 2 [7]
- unsigned char ebpFrame : 1; // 3 [0] locals accessed relative to ebp
- unsigned char interruptible : 1; // 3 [1] is intr. at all points (except prolog/epilog), not just call-sites
- unsigned char doubleAlign : 1; // 3 [2] uses double-aligned stack (ebpFrame will be false)
- unsigned char security : 1; // 3 [3] has slot for security object
- unsigned char handlers : 1; // 3 [4] has callable handlers
- unsigned char localloc : 1; // 3 [5] uses localloc
- unsigned char editNcontinue : 1; // 3 [6] was JITed in EnC mode
- unsigned char varargs : 1; // 3 [7] function uses varargs calling convention
- unsigned char profCallbacks : 1; // 4 [0]
- unsigned char genericsContext : 1;//4 [1] function reports a generics context parameter is present
- unsigned char genericsContextIsMethodDesc : 1;//4[2]
- unsigned short argCount; // 5,6 in bytes
- unsigned int frameSize; // 7,8,9,10 in bytes
- unsigned int untrackedCnt; // 11,12,13,14
- unsigned int varPtrTableSize; // 15.16,17,18
-
- // Checks whether "this" is compatible with "target".
- // It is not an exact bit match as "this" could have some
- // marker/place-holder values, which will have to be written out
- // after the header.
-
- bool isHeaderMatch(const InfoHdr& target) const;
-};
-
-
-struct InfoHdr : public InfoHdrSmall {
- // 0 (zero) means that there is no GuardStack cookie
- // The cookie is either at ESP+gsCookieOffset or EBP-gsCookieOffset
- unsigned int gsCookieOffset; // 19,20,21,22
- unsigned int syncStartOffset; // 23,24,25,26
- unsigned int syncEndOffset; // 27,28,29,30
-
- // 31 bytes total
-
- // Checks whether "this" is compatible with "target".
- // It is not an exact bit match as "this" could have some
- // marker/place-holder values, which will have to be written out
- // after the header.
-
- bool isHeaderMatch(const InfoHdr& target) const
- {
-#ifdef _ASSERTE
- // target cannot have place-holder values.
- _ASSERTE(target.untrackedCnt != HAS_UNTRACKED &&
- target.varPtrTableSize != HAS_VARPTR &&
- target.gsCookieOffset != HAS_GS_COOKIE_OFFSET &&
- target.syncStartOffset != HAS_SYNC_OFFSET);
-#endif
-
- // compare two InfoHdr's up to but not including the untrackCnt field
- if (memcmp(this, &target, offsetof(InfoHdr, untrackedCnt)) != 0)
- return false;
-
- if (untrackedCnt != target.untrackedCnt) {
- if (target.untrackedCnt <= SET_UNTRACKED_MAX)
- return false;
- else if (untrackedCnt != HAS_UNTRACKED)
- return false;
- }
-
- if (varPtrTableSize != target.varPtrTableSize) {
- if ((varPtrTableSize != 0) != (target.varPtrTableSize != 0))
- return false;
- }
-
- if ((gsCookieOffset == INVALID_GS_COOKIE_OFFSET) !=
- (target.gsCookieOffset == INVALID_GS_COOKIE_OFFSET))
- return false;
-
- if ((syncStartOffset == INVALID_SYNC_OFFSET) !=
- (target.syncStartOffset == INVALID_SYNC_OFFSET))
- return false;
-
- return true;
- }
-};
-
-
-union CallPattern {
- struct {
- unsigned char argCnt;
- unsigned char regMask; // EBP=0x8, EBX=0x4, ESI=0x2, EDI=0x1
- unsigned char argMask;
- unsigned char codeDelta;
- } fld;
- unsigned val;
-};
-
-#include <poppack.h>
-
-#define IH_MAX_PROLOG_SIZE (51)
-
-extern const InfoHdrSmall infoHdrShortcut[];
-extern int infoHdrLookup[];
-
-inline void GetInfoHdr(int index, InfoHdr * header)
+// The GCInfo version is computed as :
+// 1) The current GCINFO_VERSION for JITted and Ngened images
+// 2) A function of the Ready - to - run major version stored in READYTORUN_HEADER
+// for ready - to - run images.ReadyToRunJitManager::JitTokenToGCInfoVersion()
+// provides the GcInfo version for any Method.Currently, there's only one
+// version of GCInfo.
+//-----------------------------------------------------------------------------
+
+struct GCInfoToken
{
- * ((InfoHdrSmall *) header) = infoHdrShortcut[index];
-
- header->gsCookieOffset = 0;
- header->syncStartOffset = 0;
- header->syncEndOffset = 0;
-}
-
-PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, InfoHdr* header);
-
-BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached);
-BYTE FASTCALL encodeHeaderNext (const InfoHdr& header, InfoHdr* state);
-
-size_t FASTCALL decodeUnsigned (PTR_CBYTE src, unsigned* value);
-size_t FASTCALL decodeUDelta (PTR_CBYTE src, unsigned* value, unsigned lastValue);
-size_t FASTCALL decodeSigned (PTR_CBYTE src, int * value);
-
-#define CP_MAX_CODE_DELTA (0x23)
-#define CP_MAX_ARG_CNT (0x02)
-#define CP_MAX_ARG_MASK (0x00)
-
-extern const unsigned callPatternTable[];
-extern const unsigned callCommonDelta[];
-
-
-int FASTCALL lookupCallPattern(unsigned argCnt,
- unsigned regMask,
- unsigned argMask,
- unsigned codeDelta);
-
-void FASTCALL decodeCallPattern(int pattern,
- unsigned * argCnt,
- unsigned * regMask,
- unsigned * argMask,
- unsigned * codeDelta);
-
-#endif // _TARGET_86_ || _TARGET_ARM_
+ PTR_VOID Info;
+ UINT32 Version;
+};
/*****************************************************************************/
#endif //_GCINFO_H_
diff --git a/src/inc/gcinfodecoder.h b/src/inc/gcinfodecoder.h
index 52e8ed8b62..466ac15b68 100644
--- a/src/inc/gcinfodecoder.h
+++ b/src/inc/gcinfodecoder.h
@@ -11,7 +11,7 @@
#ifndef _GC_INFO_DECODER_
#define _GC_INFO_DECODER_
-#include "daccess.h"
+#include "gcinfotypes.h"
#define _max(a, b) (((a) > (b)) ? (a) : (b))
#define _min(a, b) (((a) < (b)) ? (a) : (b))
@@ -433,12 +433,11 @@ public:
// If you are not insterested in interruptibility or gc lifetime information, pass 0 as instructionOffset
GcInfoDecoder(
- PTR_CBYTE gcInfoAddr,
+ GCInfoToken gcInfoToken,
GcInfoDecoderFlags flags,
UINT32 instructionOffset = 0
);
-
//------------------------------------------------------------------------
// Interruptibility
//------------------------------------------------------------------------
@@ -538,6 +537,7 @@ private:
#ifdef _DEBUG
GcInfoDecoderFlags m_Flags;
PTR_CBYTE m_GcInfoAddress;
+ UINT32 m_Version;
#endif
#ifdef VERIFY_GCINFO
diff --git a/src/inc/gcinfodumper.h b/src/inc/gcinfodumper.h
index 64801b06c2..296dd29543 100644
--- a/src/inc/gcinfodumper.h
+++ b/src/inc/gcinfodumper.h
@@ -18,7 +18,7 @@ class GcInfoDumper
{
public:
- GcInfoDumper (PTR_CBYTE pbGCInfo);
+ GcInfoDumper (GCInfoToken gcInfoToken);
~GcInfoDumper ();
// Returns TRUE to stop decoding.
@@ -80,7 +80,7 @@ private:
UINT marked;
};
- PTR_CBYTE m_pbGCInfo;
+ GCInfoToken m_gcTable;
UINT32 m_StackBaseRegister;
UINT32 m_SizeOfEditAndContinuePreservedArea;
LivePointerRecord *m_pRecords;
diff --git a/src/inc/gcinfotypes.h b/src/inc/gcinfotypes.h
index a54cec30e5..fc624b2c0a 100644
--- a/src/inc/gcinfotypes.h
+++ b/src/inc/gcinfotypes.h
@@ -6,6 +6,8 @@
#ifndef __GCINFOTYPES_H__
#define __GCINFOTYPES_H__
+#include "gcinfo.h"
+
// This file is included when building an "alt jit". In that case, we are doing a cross-compile:
// we may be building the ARM jit on x86, for example. We generally make that work by conditionalizing on
// a _TARGET_XXX_ variable that we explicitly set in the build, rather than the _XXX_ variable implicitly
@@ -62,19 +64,19 @@
__forceinline size_t SAFE_SHIFT_LEFT(size_t x, size_t count)
{
_ASSERTE(count <= BITS_PER_SIZE_T);
- return (x << 1) << (count-1);
+ return (x << 1) << (count - 1);
}
__forceinline size_t SAFE_SHIFT_RIGHT(size_t x, size_t count)
{
_ASSERTE(count <= BITS_PER_SIZE_T);
- return (x >> 1) >> (count-1);
+ return (x >> 1) >> (count - 1);
}
inline UINT32 CeilOfLog2(size_t x)
{
_ASSERTE(x > 0);
- UINT32 result = (x & (x-1)) ? 1 : 0;
- while(x != 1)
+ UINT32 result = (x & (x - 1)) ? 1 : 0;
+ while (x != 1)
{
result++;
x >>= 1;
@@ -84,24 +86,24 @@ inline UINT32 CeilOfLog2(size_t x)
enum GcSlotFlags
{
- GC_SLOT_BASE = 0x0,
- GC_SLOT_INTERIOR = 0x1,
- GC_SLOT_PINNED = 0x2,
- GC_SLOT_UNTRACKED = 0x4,
+ GC_SLOT_BASE = 0x0,
+ GC_SLOT_INTERIOR = 0x1,
+ GC_SLOT_PINNED = 0x2,
+ GC_SLOT_UNTRACKED = 0x4,
// For internal use by the encoder/decoder
- GC_SLOT_IS_REGISTER = 0x8,
- GC_SLOT_IS_DELETED = 0x10,
+ GC_SLOT_IS_REGISTER = 0x8,
+ GC_SLOT_IS_DELETED = 0x10,
};
enum GcStackSlotBase
{
- GC_CALLER_SP_REL = 0x0,
- GC_SP_REL = 0x1,
- GC_FRAMEREG_REL = 0x2,
+ GC_CALLER_SP_REL = 0x0,
+ GC_SP_REL = 0x1,
+ GC_FRAMEREG_REL = 0x2,
- GC_SPBASE_FIRST = GC_CALLER_SP_REL,
- GC_SPBASE_LAST = GC_FRAMEREG_REL,
+ GC_SPBASE_FIRST = GC_CALLER_SP_REL,
+ GC_SPBASE_LAST = GC_FRAMEREG_REL,
};
#ifdef _DEBUG
@@ -113,11 +115,10 @@ const char* const GcStackSlotBaseNames[] =
};
#endif
-
enum GcSlotState
{
- GC_SLOT_DEAD = 0x0,
- GC_SLOT_LIVE = 0x1,
+ GC_SLOT_DEAD = 0x0,
+ GC_SLOT_LIVE = 0x1,
};
struct GcStackSlot
@@ -135,6 +136,238 @@ struct GcStackSlot
}
};
+#ifdef _TARGET_X86_
+
+#include <stdlib.h> // For memcmp()
+#include "bitvector.h" // for ptrArgTP
+
+#ifndef FASTCALL
+#define FASTCALL __fastcall
+#endif
+
+// we use offsetof to get the offset of a field
+#include <stddef.h> // offsetof
+#ifndef offsetof
+#define offsetof(s,m) ((size_t)&(((s *)0)->m))
+#endif
+
+enum infoHdrAdjustConstants {
+ // Constants
+ SET_FRAMESIZE_MAX = 7,
+ SET_ARGCOUNT_MAX = 8, // Change to 6
+ SET_PROLOGSIZE_MAX = 16,
+ SET_EPILOGSIZE_MAX = 10, // Change to 6
+ SET_EPILOGCNT_MAX = 4,
+ SET_UNTRACKED_MAX = 3
+};
+
+//
+// Enum to define the 128 codes that are used to incrementally adjust the InfoHdr structure
+//
+enum infoHdrAdjust {
+
+ SET_FRAMESIZE = 0, // 0x00
+ SET_ARGCOUNT = SET_FRAMESIZE + SET_FRAMESIZE_MAX + 1, // 0x08
+ SET_PROLOGSIZE = SET_ARGCOUNT + SET_ARGCOUNT_MAX + 1, // 0x11
+ SET_EPILOGSIZE = SET_PROLOGSIZE + SET_PROLOGSIZE_MAX + 1, // 0x22
+ SET_EPILOGCNT = SET_EPILOGSIZE + SET_EPILOGSIZE_MAX + 1, // 0x2d
+ SET_UNTRACKED = SET_EPILOGCNT + (SET_EPILOGCNT_MAX + 1) * 2, // 0x37
+
+ FIRST_FLIP = SET_UNTRACKED + SET_UNTRACKED_MAX + 1,
+
+ FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b
+ FLIP_ESI_SAVED, // 0x3c
+ FLIP_EBX_SAVED, // 0x3d
+ FLIP_EBP_SAVED, // 0x3e
+ FLIP_EBP_FRAME, // 0x3f
+ FLIP_INTERRUPTIBLE, // 0x40
+ FLIP_DOUBLE_ALIGN, // 0x41
+ FLIP_SECURITY, // 0x42
+ FLIP_HANDLERS, // 0x43
+ FLIP_LOCALLOC, // 0x44
+ FLIP_EDITnCONTINUE, // 0x45
+ FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding
+ FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding
+ FLIP_VARARGS, // 0x48
+ FLIP_PROF_CALLBACKS, // 0x49
+ FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding
+ FLIP_SYNC, // 0x4B
+ FLIP_HAS_GENERICS_CONTEXT,// 0x4C
+ FLIP_GENERICS_CONTEXT_IS_METHODDESC,// 0x4D
+
+ // 0x4E .. 0x4f unused
+
+ NEXT_FOUR_START = 0x50,
+ NEXT_FOUR_FRAMESIZE = 0x50,
+ NEXT_FOUR_ARGCOUNT = 0x60,
+ NEXT_THREE_PROLOGSIZE = 0x70,
+ NEXT_THREE_EPILOGSIZE = 0x78
+};
+
+#define HAS_UNTRACKED ((unsigned int) -1)
+#define HAS_VARPTR ((unsigned int) -1)
+// 0 is not a valid offset for EBP-frames as all locals are at a negative offset
+// For ESP frames, the cookie is above (at a higher address than) the buffers,
+// and so cannot be at offset 0.
+#define INVALID_GS_COOKIE_OFFSET 0
+// Temporary value to indicate that the offset needs to be read after the header
+#define HAS_GS_COOKIE_OFFSET ((unsigned int) -1)
+
+// 0 is not a valid sync offset
+#define INVALID_SYNC_OFFSET 0
+// Temporary value to indicate that the offset needs to be read after the header
+#define HAS_SYNC_OFFSET ((unsigned int) -1)
+
+#define INVALID_ARGTAB_OFFSET 0
+
+#include <pshpack1.h>
+
+// Working set optimization: saving 12 * 128 = 1536 bytes in infoHdrShortcut
+struct InfoHdr;
+
+struct InfoHdrSmall {
+ unsigned char prologSize; // 0
+ unsigned char epilogSize; // 1
+ unsigned char epilogCount : 3; // 2 [0:2]
+ unsigned char epilogAtEnd : 1; // 2 [3]
+ unsigned char ediSaved : 1; // 2 [4] which callee-saved regs are pushed onto stack
+ unsigned char esiSaved : 1; // 2 [5]
+ unsigned char ebxSaved : 1; // 2 [6]
+ unsigned char ebpSaved : 1; // 2 [7]
+ unsigned char ebpFrame : 1; // 3 [0] locals accessed relative to ebp
+ unsigned char interruptible : 1; // 3 [1] is intr. at all points (except prolog/epilog), not just call-sites
+ unsigned char doubleAlign : 1; // 3 [2] uses double-aligned stack (ebpFrame will be false)
+ unsigned char security : 1; // 3 [3] has slot for security object
+ unsigned char handlers : 1; // 3 [4] has callable handlers
+ unsigned char localloc : 1; // 3 [5] uses localloc
+ unsigned char editNcontinue : 1; // 3 [6] was JITed in EnC mode
+ unsigned char varargs : 1; // 3 [7] function uses varargs calling convention
+ unsigned char profCallbacks : 1; // 4 [0]
+ unsigned char genericsContext : 1;//4 [1] function reports a generics context parameter is present
+ unsigned char genericsContextIsMethodDesc : 1;//4[2]
+ unsigned short argCount; // 5,6 in bytes
+ unsigned int frameSize; // 7,8,9,10 in bytes
+ unsigned int untrackedCnt; // 11,12,13,14
+ unsigned int varPtrTableSize; // 15.16,17,18
+
+ // Checks whether "this" is compatible with "target".
+ // It is not an exact bit match as "this" could have some
+ // marker/place-holder values, which will have to be written out
+ // after the header.
+
+ bool isHeaderMatch(const InfoHdr& target) const;
+};
+
+
+struct InfoHdr : public InfoHdrSmall {
+ // 0 (zero) means that there is no GuardStack cookie
+ // The cookie is either at ESP+gsCookieOffset or EBP-gsCookieOffset
+ unsigned int gsCookieOffset; // 19,20,21,22
+ unsigned int syncStartOffset; // 23,24,25,26
+ unsigned int syncEndOffset; // 27,28,29,30
+
+ // 31 bytes total
+
+ // Checks whether "this" is compatible with "target".
+ // It is not an exact bit match as "this" could have some
+ // marker/place-holder values, which will have to be written out
+ // after the header.
+
+ bool isHeaderMatch(const InfoHdr& target) const
+ {
+#ifdef _ASSERTE
+ // target cannot have place-holder values.
+ _ASSERTE(target.untrackedCnt != HAS_UNTRACKED &&
+ target.varPtrTableSize != HAS_VARPTR &&
+ target.gsCookieOffset != HAS_GS_COOKIE_OFFSET &&
+ target.syncStartOffset != HAS_SYNC_OFFSET);
+#endif
+
+ // compare two InfoHdr's up to but not including the untrackCnt field
+ if (memcmp(this, &target, offsetof(InfoHdr, untrackedCnt)) != 0)
+ return false;
+
+ if (untrackedCnt != target.untrackedCnt) {
+ if (target.untrackedCnt <= SET_UNTRACKED_MAX)
+ return false;
+ else if (untrackedCnt != HAS_UNTRACKED)
+ return false;
+ }
+
+ if (varPtrTableSize != target.varPtrTableSize) {
+ if ((varPtrTableSize != 0) != (target.varPtrTableSize != 0))
+ return false;
+ }
+
+ if ((gsCookieOffset == INVALID_GS_COOKIE_OFFSET) !=
+ (target.gsCookieOffset == INVALID_GS_COOKIE_OFFSET))
+ return false;
+
+ if ((syncStartOffset == INVALID_SYNC_OFFSET) !=
+ (target.syncStartOffset == INVALID_SYNC_OFFSET))
+ return false;
+
+ return true;
+ }
+};
+
+
+union CallPattern {
+ struct {
+ unsigned char argCnt;
+ unsigned char regMask; // EBP=0x8, EBX=0x4, ESI=0x2, EDI=0x1
+ unsigned char argMask;
+ unsigned char codeDelta;
+ } fld;
+ unsigned val;
+};
+
+#include <poppack.h>
+
+#define IH_MAX_PROLOG_SIZE (51)
+
+extern const InfoHdrSmall infoHdrShortcut[];
+extern int infoHdrLookup[];
+
+inline void GetInfoHdr(int index, InfoHdr * header)
+{
+ *((InfoHdrSmall *)header) = infoHdrShortcut[index];
+
+ header->gsCookieOffset = 0;
+ header->syncStartOffset = 0;
+ header->syncEndOffset = 0;
+}
+
+PTR_CBYTE FASTCALL decodeHeader(PTR_CBYTE table, InfoHdr* header);
+
+BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int *pCached);
+BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state);
+
+size_t FASTCALL decodeUnsigned(PTR_CBYTE src, unsigned* value);
+size_t FASTCALL decodeUDelta(PTR_CBYTE src, unsigned* value, unsigned lastValue);
+size_t FASTCALL decodeSigned(PTR_CBYTE src, int * value);
+
+#define CP_MAX_CODE_DELTA (0x23)
+#define CP_MAX_ARG_CNT (0x02)
+#define CP_MAX_ARG_MASK (0x00)
+
+extern const unsigned callPatternTable[];
+extern const unsigned callCommonDelta[];
+
+
+int FASTCALL lookupCallPattern(unsigned argCnt,
+ unsigned regMask,
+ unsigned argMask,
+ unsigned codeDelta);
+
+void FASTCALL decodeCallPattern(int pattern,
+ unsigned * argCnt,
+ unsigned * regMask,
+ unsigned * argMask,
+ unsigned * codeDelta);
+
+#endif // _TARGET_86_
+
// Stack offsets must be 8-byte aligned, so we use this unaligned
// offset to represent that the method doesn't have a security object
#define NO_SECURITY_OBJECT (-1)
@@ -144,7 +377,6 @@ struct GcStackSlot
#define NO_GENERICS_INST_CONTEXT (-1)
#define NO_PSP_SYM (-1)
-
#if defined(_TARGET_AMD64_)
#ifndef TARGET_POINTER_SIZE
diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp
index c3ae12bcf4..fb033ddfae 100644
--- a/src/jit/gcencode.cpp
+++ b/src/jit/gcencode.cpp
@@ -21,7 +21,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif
-#include "gcinfo.h"
+#include "gcinfotypes.h"
#ifdef JIT32_GCENCODER
@@ -3236,7 +3236,7 @@ unsigned GCInfo::gcInfoBlockHdrDump(const BYTE* table,
InfoHdr* header,
unsigned* methodSize)
{
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
printf("Method info block:\n");
@@ -3252,7 +3252,7 @@ unsigned GCInfo::gcDumpPtrTable(const BYTE* table,
{
printf("Pointer table:\n");
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
return gcDump.DumpGCTable(table, header, methodSize, verifyGCTables);
@@ -3268,7 +3268,7 @@ void GCInfo::gcFindPtrsInFrame(const void* infoBlock,
const void* codeBlock,
unsigned offs)
{
- GCDump gcDump;
+ GCDump gcDump(GCINFO_VERSION);
gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
gcDump.DumpPtrsInFrame((const BYTE*)infoBlock, (const BYTE*)codeBlock, offs, verifyGCTables);
diff --git a/src/jit/jitgcinfo.h b/src/jit/jitgcinfo.h
index 480f88491a..e5092cfaa1 100644
--- a/src/jit/jitgcinfo.h
+++ b/src/jit/jitgcinfo.h
@@ -8,11 +8,11 @@
#ifndef _JITGCINFO_H_
#define _JITGCINFO_H_
-#include "gcinfo.h"
+
+#include "gcinfotypes.h"
#ifndef JIT32_GCENCODER
#include "gcinfoencoder.h"
-#include "gcinfotypes.h"
#endif
/*****************************************************************************/
diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp
index 7eea254646..b66215f5f2 100644
--- a/src/vm/codeman.cpp
+++ b/src/vm/codeman.cpp
@@ -3015,7 +3015,7 @@ void * EEJitManager::allocCodeFragmentBlock(size_t blockSize, unsigned alignment
#endif // !DACCESS_COMPILE
-PTR_VOID EEJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken EEJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
@@ -3024,7 +3024,8 @@ PTR_VOID EEJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
SUPPORTS_DAC;
} CONTRACTL_END;
- return GetCodeHeader(MethodToken)->GetGCInfo();
+ // The JIT-ed code always has the current version of GCInfo
+ return{ GetCodeHeader(MethodToken)->GetGCInfo(), GCINFO_VERSION };
}
// creates an enumeration and returns the number of EH clauses
@@ -5035,7 +5036,7 @@ NativeImageJitManager::NativeImageJitManager()
#endif // #ifndef DACCESS_COMPILE
-PTR_VOID NativeImageJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken NativeImageJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
@@ -5060,7 +5061,8 @@ PTR_VOID NativeImageJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
PTR_VOID pUnwindData = GetUnwindDataBlob(baseAddress, pRuntimeFunction, &nUnwindDataSize);
// GCInfo immediatelly follows unwind data
- return dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ // GCInfo from an NGEN-ed image is always the current version
+ return{ dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize, GCINFO_VERSION };
}
unsigned NativeImageJitManager::InitializeEHEnumeration(const METHODTOKEN& MethodToken, EH_CLAUSE_ENUMERATOR* pEnumState)
@@ -5681,7 +5683,7 @@ void NativeImageJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& Method
//
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
@@ -6274,14 +6276,17 @@ PTR_MethodDesc MethodIterator::GetMethodDesc()
return NativeUnwindInfoLookupTable::GetMethodDesc(m_pNgenLayout, GetRuntimeFunction(), m_ModuleBase);
}
-PTR_VOID MethodIterator::GetGCInfo()
+GCInfoToken MethodIterator::GetGCInfoToken()
{
LIMITED_METHOD_CONTRACT;
// get the gc info from the RT function
SIZE_T size;
PTR_VOID pUnwindData = GetUnwindDataBlob(m_ModuleBase, GetRuntimeFunction(), &size);
- return (PTR_VOID)((PTR_BYTE)pUnwindData + size);
+ PTR_VOID gcInfo = (PTR_VOID)((PTR_BYTE)pUnwindData + size);
+ // MethodIterator is used to iterate over methods of an NgenImage.
+ // So, GcInfo version is always current
+ return{ gcInfo, GCINFO_VERSION };
}
TADDR MethodIterator::GetMethodStartAddress()
@@ -6359,8 +6364,8 @@ void MethodIterator::GetMethodRegionInfo(IJitManager::MethodRegionInfo *methodRe
methodRegionInfo->hotStartAddress = GetMethodStartAddress();
methodRegionInfo->coldStartAddress = GetMethodColdStartAddress();
-
- methodRegionInfo->hotSize = ExecutionManager::GetNativeImageJitManager()->GetCodeManager()->GetFunctionSize(GetGCInfo());
+ GCInfoToken gcInfoToken = GetGCInfoToken();
+ methodRegionInfo->hotSize = ExecutionManager::GetNativeImageJitManager()->GetCodeManager()->GetFunctionSize(gcInfoToken);
methodRegionInfo->coldSize = 0;
if (methodRegionInfo->coldStartAddress != NULL)
@@ -6408,6 +6413,24 @@ ReadyToRunInfo * ReadyToRunJitManager::JitTokenToReadyToRunInfo(const METHODTOKE
return dac_cast<PTR_Module>(MethodToken.m_pRangeSection->pHeapListOrZapModule)->GetReadyToRunInfo();
}
+UINT32 ReadyToRunJitManager::JitTokenToGCInfoVersion(const METHODTOKEN& MethodToken)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ HOST_NOCALLS;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ READYTORUN_HEADER * header = JitTokenToReadyToRunInfo(MethodToken)->GetImage()->GetReadyToRunHeader();
+ UINT32 gcInfoVersion = header->MajorVersion;
+
+ // Currently there's only one version of GCInfo.
+ _ASSERTE(gcInfoVersion == GCINFO_VERSION);
+
+ return gcInfoVersion;
+}
+
PTR_RUNTIME_FUNCTION ReadyToRunJitManager::JitTokenToRuntimeFunction(const METHODTOKEN& MethodToken)
{
CONTRACTL {
@@ -6433,7 +6456,7 @@ TADDR ReadyToRunJitManager::JitTokenToStartAddress(const METHODTOKEN& MethodToke
RUNTIME_FUNCTION__BeginAddress(dac_cast<PTR_RUNTIME_FUNCTION>(MethodToken.m_pCodeHeader));
}
-PTR_VOID ReadyToRunJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
+GCInfoToken ReadyToRunJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken)
{
CONTRACTL {
NOTHROW;
@@ -6458,7 +6481,10 @@ PTR_VOID ReadyToRunJitManager::GetGCInfo(const METHODTOKEN& MethodToken)
PTR_VOID pUnwindData = GetUnwindDataBlob(baseAddress, pRuntimeFunction, &nUnwindDataSize);
// GCInfo immediatelly follows unwind data
- return dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ PTR_BYTE gcInfo = dac_cast<PTR_BYTE>(pUnwindData) + nUnwindDataSize;
+ UINT32 gcInfoVersion = JitTokenToGCInfoVersion(MethodToken);
+
+ return{ gcInfo, gcInfoVersion };
}
unsigned ReadyToRunJitManager::InitializeEHEnumeration(const METHODTOKEN& MethodToken, EH_CLAUSE_ENUMERATOR* pEnumState)
@@ -6863,7 +6889,7 @@ void ReadyToRunJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& MethodT
// READYTORUN: FUTURE: Hot-cold spliting
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
}
diff --git a/src/vm/codeman.h b/src/vm/codeman.h
index 855c15125a..ae86a25e93 100644
--- a/src/vm/codeman.h
+++ b/src/vm/codeman.h
@@ -24,9 +24,10 @@ Abstract:
An IJitManager knows about which method bodies live in each RangeSection.
It can handle methods of one given CodeType. It can map a method body to
a MethodDesc. It knows where the GCInfo about the method lives.
- Today, we have 2 IJitManagers viz.
+ Today, we have three IJitManagers viz.
1. EEJitManager for JITcompiled code generated by clrjit.dll
2. NativeImageJitManager for ngenned code.
+ 3. ReadyToRunJitManager for version resiliant ReadyToRun code
An ICodeManager knows how to crack a specific format of GCInfo. There is
a default format (handled by ExecutionManager::GetDefaultCodeManager())
@@ -66,6 +67,7 @@ Abstract:
#include "debuginfostore.h"
#include "shash.h"
#include "pedecoder.h"
+#include "gcinfo.h"
class MethodDesc;
class ICorJitCompiler;
@@ -113,6 +115,7 @@ enum StubCodeBlockKind : int
// Method header which exists just before the code.
// Every IJitManager could have its own format for the header.
// Today CodeHeader is used by the EEJitManager.
+// The GCInfo version is always current GCINFO_VERSION in this header.
#ifdef USE_INDIRECT_CODEHEADER
typedef DPTR(struct _hpRealCodeHdr) PTR_RealCodeHeader;
@@ -735,7 +738,11 @@ public:
CrawlFrame *pCf)=0;
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken)=0;
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken)=0;
+ PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken)
+ {
+ return GetGCInfoToken(MethodToken).Info;
+ }
TADDR JitTokenToModuleBase(const METHODTOKEN& MethodToken);
@@ -965,7 +972,7 @@ public:
virtual TypeHandle ResolveEHClause(EE_ILEXCEPTION_CLAUSE* pEHClause,
CrawlFrame *pCf);
#endif // !DACCESS_COMPILE
- PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#endif // !CROSSGEN_COMPILE
#if !defined DACCESS_COMPILE && !defined CROSSGEN_COMPILE
void RemoveJitData(CodeHeader * pCHdr, size_t GCinfo_len, size_t EHinfo_len);
@@ -1486,7 +1493,7 @@ inline void EEJitManager::JitTokenToMethodRegionInfo(const METHODTOKEN& MethodTo
} CONTRACTL_END;
methodRegionInfo->hotStartAddress = JitTokenToStartAddress(MethodToken);
- methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfo(MethodToken));
+ methodRegionInfo->hotSize = GetCodeManager()->GetFunctionSize(GetGCInfoToken(MethodToken));
methodRegionInfo->coldStartAddress = 0;
methodRegionInfo->coldSize = 0;
}
@@ -1543,7 +1550,7 @@ public:
CrawlFrame *pCf);
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#if defined(WIN64EXCEPTIONS)
virtual PTR_RUNTIME_FUNCTION LazyGetFunctionEntry(EECodeInfo * pCodeInfo);
@@ -1638,6 +1645,8 @@ public:
virtual PCODE GetCodeAddressForRelOffset(const METHODTOKEN& MethodToken, DWORD relOffset);
static ReadyToRunInfo * JitTokenToReadyToRunInfo(const METHODTOKEN& MethodToken);
+ static UINT32 JitTokenToGCInfoVersion(const METHODTOKEN& MethodToken);
+
static PTR_RUNTIME_FUNCTION JitTokenToRuntimeFunction(const METHODTOKEN& MethodToken);
virtual TADDR JitTokenToStartAddress(const METHODTOKEN& MethodToken);
@@ -1653,7 +1662,7 @@ public:
CrawlFrame *pCf);
#endif // #ifndef DACCESS_COMPILE
- virtual PTR_VOID GetGCInfo(const METHODTOKEN& MethodToken);
+ virtual GCInfoToken GetGCInfoToken(const METHODTOKEN& MethodToken);
#if defined(WIN64EXCEPTIONS)
virtual PTR_RUNTIME_FUNCTION LazyGetFunctionEntry(EECodeInfo * pCodeInfo);
@@ -1754,10 +1763,15 @@ public:
return m_relOffset;
}
- PTR_VOID GetGCInfo()
+ GCInfoToken GetGCInfoToken()
{
WRAPPER_NO_CONTRACT;
- return GetJitManager()->GetGCInfo(GetMethodToken());
+ return GetJitManager()->GetGCInfoToken(GetMethodToken());
+ }
+
+ PTR_VOID GetGCInfo()
+ {
+ return GetGCInfoToken().Info;
}
void GetMethodRegionInfo(IJitManager::MethodRegionInfo *methodRegionInfo)
@@ -1824,7 +1838,7 @@ class MethodSectionIterator;
//
// MethodIterator class is used to iterate all the methods in an ngen image.
// It will match and report hot (and cold, if any) sections of a method at the same time.
-//
+// GcInfo version is always current
class MethodIterator
{
public:
@@ -1852,7 +1866,7 @@ private:
BOOL Next();
PTR_MethodDesc GetMethodDesc();
- PTR_VOID GetGCInfo();
+ GCInfoToken GetGCInfoToken();
TADDR GetMethodStartAddress();
TADDR GetMethodColdStartAddress();
ULONG GetHotCodeSize();
diff --git a/src/vm/debughelp.cpp b/src/vm/debughelp.cpp
index 7e4455a7ff..df769455aa 100644
--- a/src/vm/debughelp.cpp
+++ b/src/vm/debughelp.cpp
@@ -1198,24 +1198,24 @@ void DumpGCInfo(MethodDesc* method)
_ASSERTE(codeInfo.GetRelOffset() == 0);
ICodeManager* codeMan = codeInfo.GetCodeManager();
- BYTE* table = (BYTE*) codeInfo.GetGCInfo();
+ GCInfoToken table = codeInfo.GetGCInfoToken();
unsigned methodSize = (unsigned)codeMan->GetFunctionSize(table);
- GCDump gcDump;
+ GCDump gcDump(table.Version);
+ PTR_CBYTE gcInfo = PTR_CBYTE(table.Info);
gcDump.gcPrintf = printfToDbgOut;
InfoHdr header;
printfToDbgOut ("Method info block:\n");
-
- table += gcDump.DumpInfoHdr(table, &header, &methodSize, 0);
+ gcInfo += gcDump.DumpInfoHdr(gcInfo, &header, &methodSize, 0);
printfToDbgOut ("\n");
printfToDbgOut ("Pointer table:\n");
- table += gcDump.DumpGCTable(table, header, methodSize, 0);
+ gcInfo += gcDump.DumpGCTable(gcInfo, header, methodSize, 0);
}
void DumpGCInfoMD(size_t method)
diff --git a/src/vm/eedbginterfaceimpl.cpp b/src/vm/eedbginterfaceimpl.cpp
index 53cb288319..93decc9b0d 100644
--- a/src/vm/eedbginterfaceimpl.cpp
+++ b/src/vm/eedbginterfaceimpl.cpp
@@ -665,10 +665,8 @@ size_t EEDbgInterfaceImpl::GetFunctionSize(MethodDesc *pFD)
return 0;
EECodeInfo codeInfo(methodStart);
-
- PTR_VOID methodInfo = codeInfo.GetGCInfo();
-
- return codeInfo.GetCodeManager()->GetFunctionSize(methodInfo);
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ return codeInfo.GetCodeManager()->GetFunctionSize(gcInfoToken);
}
#endif //!DACCESS_COMPILE
diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp
index 69eb177542..82b76f69e0 100644
--- a/src/vm/eetwain.cpp
+++ b/src/vm/eetwain.cpp
@@ -11,8 +11,6 @@
#define RETURN_ADDR_OFFS 1 // in DWORDS
-#include "gcinfo.h"
-
#ifdef USE_GC_INFO_DECODER
#include "gcinfodecoder.h"
#endif
@@ -942,14 +940,14 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx,
// GCInfo for old method
GcInfoDecoder oldGcDecoder(
- dac_cast<PTR_CBYTE>(pOldCodeInfo->GetGCInfo()),
+ pOldCodeInfo->GetGCInfoToken(),
GcInfoDecoderFlags(DECODE_SECURITY_OBJECT | DECODE_PSP_SYM | DECODE_EDIT_AND_CONTINUE),
0 // Instruction offset (not needed)
);
// GCInfo for new method
GcInfoDecoder newGcDecoder(
- dac_cast<PTR_CBYTE>(pNewCodeInfo->GetGCInfo()),
+ pNewCodeInfo->GetGCInfoToken(),
GcInfoDecoderFlags(DECODE_SECURITY_OBJECT | DECODE_PSP_SYM | DECODE_EDIT_AND_CONTINUE),
0 // Instruction offset (not needed)
);
@@ -1437,8 +1435,10 @@ bool EECodeManager::IsGcSafe( EECodeInfo *pCodeInfo,
GC_NOTRIGGER;
} CONTRACTL_END;
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
+
GcInfoDecoder gcInfoDecoder(
- dac_cast<PTR_CBYTE>(pCodeInfo->GetGCInfo()),
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
dwRelOffset
);
@@ -1502,13 +1502,11 @@ bool FindEndOfLastInterruptibleRegionCB (
*/
unsigned EECodeManager::FindEndOfLastInterruptibleRegion(unsigned curOffset,
unsigned endOffset,
- PTR_VOID methodInfoPtr)
+ GCInfoToken gcInfoToken)
{
#ifndef DACCESS_COMPILE
- BYTE* gcInfoAddr = (BYTE*) methodInfoPtr;
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_FOR_RANGES_CALLBACK,
0);
@@ -4758,7 +4756,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
methodName, curOffs));
#endif
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCodeInfo->GetGCInfo());
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
#if defined(STRESS_HEAP) && defined(PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
#ifdef USE_GC_INFO_DECODER
@@ -4770,7 +4768,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
if (flags & ActiveStackFrame)
{
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
curOffs
);
@@ -4778,7 +4776,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
{
// This must be the offset after a call
#ifdef _DEBUG
- GcInfoDecoder _safePointDecoder(gcInfoAddr, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder _safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
_ASSERTE(_safePointDecoder.IsSafePoint(curOffs));
#endif
flags &= ~((unsigned)ActiveStackFrame);
@@ -4791,7 +4789,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
if (flags & ActiveStackFrame)
{
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_INTERRUPTIBILITY,
curOffs
);
@@ -4839,7 +4837,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
// We've been given an override offset for GC Info
#ifdef _DEBUG
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
@@ -4884,7 +4882,7 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
curOffs
);
@@ -5027,7 +5025,7 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
unsigned relOffset = pCF->GetRelOffset();
CodeManState* pState = pCF->GetCodeManState();
- PTR_VOID methodInfoPtr = pJitMan->GetGCInfo(methodToken);
+ GCInfoToken gcInfoToken = pJitMan->GetGCInfoToken(methodToken);
_ASSERTE(sizeof(CodeManStateBuf) <= sizeof(pState->stateBuf));
@@ -5035,7 +5033,7 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
CodeManStateBuf * stateBuf = (CodeManStateBuf*)pState->stateBuf;
/* Extract the necessary information from the info block header */
- stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(methodInfoPtr, // <TODO>truncation</TODO>
+ stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(gcInfoToken.Info, // <TODO>truncation</TODO>
relOffset,
&stateBuf->hdrInfoBody);
@@ -5051,10 +5049,8 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF)
}
#elif defined(USE_GC_INFO_DECODER) && !defined(CROSSGEN_COMPILE)
- BYTE* gcInfoAddr = (BYTE*) methodInfoPtr;
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_SECURITY_OBJECT,
0
);
@@ -5270,11 +5266,10 @@ GenericParamContextType EECodeManager::GetParamContextType(PREGDISPLAY pCont
}
// On x86 the generic param context parameter is never this.
#elif defined(USE_GC_INFO_DECODER)
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_GENERICS_INST_CONTEXT),
0
);
@@ -5363,11 +5358,10 @@ PTR_VOID EECodeManager::GetExactGenericsToken(SIZE_T baseStackSlot,
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
GcInfoDecoderFlags (DECODE_PSP_SYM | DECODE_GENERICS_INST_CONTEXT),
0
);
@@ -5432,7 +5426,7 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY pContext,
_ASSERTE(sizeof(CodeManStateBuf) <= sizeof(pState->stateBuf));
- PTR_VOID methodInfoPtr = pCodeInfo->GetGCInfo();
+ GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
unsigned relOffset = pCodeInfo->GetRelOffset();
#if defined(_TARGET_X86_)
@@ -5440,7 +5434,7 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY pContext,
/* Extract the necessary information from the info block header */
hdrInfo * info = &stateBuf->hdrInfoBody;
- stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(methodInfoPtr, // <TODO>truncation</TODO>
+ stateBuf->hdrInfoSize = (DWORD)crackMethodInfoHdr(gcInfoToken.Info, // <TODO>truncation</TODO>
relOffset,
info);
@@ -5459,22 +5453,20 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY pContext,
}
else
{
- PTR_CBYTE table = PTR_CBYTE(methodInfoPtr) + stateBuf->hdrInfoSize;
+ PTR_CBYTE table = PTR_CBYTE(gcInfoToken.Info) + stateBuf->hdrInfoSize;
unsigned argSize = GetPushedArgSize(info, table, relOffset);
return PVOID(SIZE_T(pContext->Esp + argSize + info->gsCookieOffset));
}
#elif defined(USE_GC_INFO_DECODER) && !defined(CROSSGEN_COMPILE)
- PTR_CBYTE gcInfoAddr = PTR_CBYTE(methodInfoPtr);
-
if (pCodeInfo->IsFunclet())
{
return NULL;
}
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_GS_COOKIE,
0
);
@@ -5567,7 +5559,7 @@ bool EECodeManager::IsInSynchronizedRegion(
*
* Returns the size of a given function.
*/
-size_t EECodeManager::GetFunctionSize(PTR_VOID methodInfoPtr)
+size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken)
{
CONTRACTL {
NOTHROW;
@@ -5577,16 +5569,15 @@ size_t EECodeManager::GetFunctionSize(PTR_VOID methodInfoPtr)
#if defined(_TARGET_X86_)
hdrInfo info;
+ PTR_VOID methodInfoPtr = gcInfoToken.Info;
crackMethodInfoHdr(methodInfoPtr, 0, &info);
return info.methodSize;
#elif defined(USE_GC_INFO_DECODER)
- PTR_BYTE gcInfoAddr = PTR_BYTE(methodInfoPtr);
-
GcInfoDecoder gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
diff --git a/src/vm/gccover.cpp b/src/vm/gccover.cpp
index 2dd7c9e2a5..3220cddd8e 100644
--- a/src/vm/gccover.cpp
+++ b/src/vm/gccover.cpp
@@ -79,7 +79,7 @@ void SetupAndSprinkleBreakpoints(
gcCover->methodRegion = methodRegionInfo;
gcCover->codeMan = pCodeInfo->GetCodeManager();
- gcCover->gcInfo = pCodeInfo->GetGCInfo();
+ gcCover->gcInfoToken = pCodeInfo->GetGCInfoToken();
gcCover->callerThread = 0;
gcCover->doingEpilogChecks = true;
@@ -286,7 +286,7 @@ class GCCoverageRangeEnumerator
private:
ICodeManager *m_pCodeManager;
- LPVOID m_pvGCInfo;
+ GCInfoToken m_pvGCTable;
BYTE *m_codeStart;
BYTE *m_codeEnd;
BYTE *m_curFuncletEnd;
@@ -318,7 +318,7 @@ private:
unsigned ofsLastInterruptible = m_pCodeManager->FindEndOfLastInterruptibleRegion(
static_cast<unsigned int>(pCurFunclet - m_codeStart),
static_cast<unsigned int>(m_curFuncletEnd - m_codeStart),
- m_pvGCInfo);
+ m_pvGCTable);
if (ofsLastInterruptible)
{
@@ -332,10 +332,10 @@ private:
public:
- GCCoverageRangeEnumerator (ICodeManager *pCodeManager, LPVOID pvGCInfo, BYTE *codeStart, SIZE_T codeSize)
+ GCCoverageRangeEnumerator (ICodeManager *pCodeManager, GCInfoToken pvGCTable, BYTE *codeStart, SIZE_T codeSize)
{
m_pCodeManager = pCodeManager;
- m_pvGCInfo = pvGCInfo;
+ m_pvGCTable = pvGCTable;
m_codeStart = codeStart;
m_codeEnd = codeStart + codeSize;
m_nextFunclet = codeStart;
@@ -458,9 +458,9 @@ void GCCoverageInfo::SprinkleBreakpoints(
#ifdef _TARGET_AMD64_
- GCCoverageRangeEnumerator rangeEnum(codeMan, gcInfo, codeStart, codeSize);
+ GCCoverageRangeEnumerator rangeEnum(codeMan, gcInfoToken, codeStart, codeSize);
- GcInfoDecoder safePointDecoder((const BYTE*)gcInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
bool fSawPossibleSwitch = false;
#endif
@@ -582,7 +582,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
#ifdef _TARGET_X86_
// we will whack every instruction in the prolog and epilog to make certain
// our unwinding logic works there.
- if (codeMan->IsInPrologOrEpilog((cur - codeStart) + (DWORD)regionOffsetAdj, gcInfo, NULL)) {
+ if (codeMan->IsInPrologOrEpilog((cur - codeStart) + (DWORD)regionOffsetAdj, gcInfoToken.Info, NULL)) {
*cur = INTERRUPT_INSTR;
}
#endif
@@ -632,7 +632,7 @@ void GCCoverageInfo::SprinkleBreakpoints(
}
}
- GcInfoDecoder safePointDecoder((const BYTE*)gcInfo, (GcInfoDecoderFlags)0, 0);
+ GcInfoDecoder safePointDecoder(gcInfoToken, (GcInfoDecoderFlags)0, 0);
assert(methodRegion.hotSize > 0);
@@ -1469,7 +1469,7 @@ void DoGcStress (PCONTEXT regs, MethodDesc *pMD)
/* are we in a prolog or epilog? If so just test the unwind logic
but don't actually do a GC since the prolog and epilog are not
GC safe points */
- if (gcCover->codeMan->IsInPrologOrEpilog(offset, gcCover->gcInfo, NULL))
+ if (gcCover->codeMan->IsInPrologOrEpilog(offset, gcCover->gcInfoToken.Info, NULL))
{
// We are not at a GC safe point so we can't Suspend EE (Suspend EE will yield to GC).
// But we still have to update the GC Stress instruction. We do it directly without suspending
diff --git a/src/vm/gccover.h b/src/vm/gccover.h
index 0308f473f2..b2dedefa31 100644
--- a/src/vm/gccover.h
+++ b/src/vm/gccover.h
@@ -26,7 +26,7 @@ public:
// Following 6 variables are for prolog / epilog walking coverage
ICodeManager* codeMan; // CodeMan for this method
- void* gcInfo; // gcInfo for this method
+ GCInfoToken gcInfoToken; // gcInfo for this method
Thread* callerThread; // Thread associated with context callerRegs
T_CONTEXT callerRegs; // register state when method was entered
diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp
index aa1edbb555..5ecae4f8fc 100644
--- a/src/vm/gcenv.ee.cpp
+++ b/src/vm/gcenv.ee.cpp
@@ -132,7 +132,7 @@ inline bool SafeToReportGenericParamContext(CrawlFrame* pCF)
#else // USE_GC_INFO_DECODER
- GcInfoDecoder gcInfoDecoder((PTR_CBYTE)pCF->GetGCInfo(),
+ GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(),
DECODE_PROLOG_LENGTH,
0);
UINT32 prologLength = gcInfoDecoder.GetPrologSize();
@@ -199,8 +199,8 @@ bool FindFirstInterruptiblePointStateCB(
// the end is exclusive). Return -1 if no such point exists.
unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs)
{
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo());
- GcInfoDecoder gcInfoDecoder(gcInfoAddr, DECODE_FOR_RANGES_CALLBACK, 0);
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
+ GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK, 0);
FindFirstInterruptiblePointState state;
state.offs = offs;
@@ -281,9 +281,9 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
#if defined(WIN64EXCEPTIONS)
if (pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting())
{
- PTR_BYTE gcInfoAddr = dac_cast<PTR_BYTE>(pCF->GetCodeInfo()->GetGCInfo());
+ GCInfoToken gcInfoToken = pCF->GetGCInfoToken();
GcInfoDecoder _gcInfoDecoder(
- gcInfoAddr,
+ gcInfoToken,
DECODE_CODE_LENGTH,
0
);
diff --git a/src/vm/gcinfodecoder.cpp b/src/vm/gcinfodecoder.cpp
index 5a3bbd94eb..b2f5640331 100644
--- a/src/vm/gcinfodecoder.cpp
+++ b/src/vm/gcinfodecoder.cpp
@@ -6,7 +6,6 @@
#include "common.h"
#include "gcinfodecoder.h"
-
#ifdef USE_GC_INFO_DECODER
#ifndef CHECK_APP_DOMAIN
@@ -84,11 +83,11 @@ bool GcInfoDecoder::SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset,
GcInfoDecoder::GcInfoDecoder(
- PTR_CBYTE gcInfoAddr,
+ GCInfoToken gcInfoToken,
GcInfoDecoderFlags flags,
UINT32 breakOffset
)
- : m_Reader( gcInfoAddr
+ : m_Reader(dac_cast<PTR_CBYTE>(gcInfoToken.Info)
#ifdef VERIFY_GCINFO
+ sizeof(size_t)
#endif
@@ -97,13 +96,14 @@ GcInfoDecoder::GcInfoDecoder(
, m_IsInterruptible(false)
#ifdef _DEBUG
, m_Flags( flags )
- , m_GcInfoAddress(gcInfoAddr)
+ , m_GcInfoAddress(dac_cast<PTR_CBYTE>(gcInfoToken.Info))
+ , m_Version(gcInfoToken.Version)
#endif
#ifdef VERIFY_GCINFO
- , m_DbgDecoder(gcInfoAddr+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[3])<<24)+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[2])<<16)+
- (((UINT32)((PTR_BYTE)(TADDR)gcInfoAddr)[1])<<8)+
+ , m_DbgDecoder(dac_cast<PTR_CBYTE>(gcInfoToken.Info) +
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[3])<<24)+
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[2])<<16)+
+ (((UINT32)((PTR_BYTE)(TADDR)gcInfoToken.Info)[1])<<8)+
((PTR_BYTE)(TADDR)gcInfoAddr)[0],
flags, breakOffset)
#endif
diff --git a/src/vm/stackwalk.h b/src/vm/stackwalk.h
index 3d6dbdcb14..004d673a2a 100644
--- a/src/vm/stackwalk.h
+++ b/src/vm/stackwalk.h
@@ -324,6 +324,13 @@ public:
return &codeInfo;
}
+ GCInfoToken GetGCInfoToken()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(isFrameless);
+ return codeInfo.GetGCInfoToken();
+ }
+
PTR_VOID GetGCInfo()
{
LIMITED_METHOD_DAC_CONTRACT;