summaryrefslogtreecommitdiff
path: root/src/gcdump
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/gcdump
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/gcdump')
-rw-r--r--src/gcdump/.gitmirror1
-rw-r--r--src/gcdump/gcdump.cpp80
-rw-r--r--src/gcdump/gcdump.settings.targets24
-rw-r--r--src/gcdump/gcdumpnonx86.cpp528
-rw-r--r--src/gcdump/i386/.gitmirror1
-rw-r--r--src/gcdump/i386/gcdumpx86.cpp1068
6 files changed, 1702 insertions, 0 deletions
diff --git a/src/gcdump/.gitmirror b/src/gcdump/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/gcdump/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/gcdump/gcdump.cpp b/src/gcdump/gcdump.cpp
new file mode 100644
index 0000000000..1c512c88e0
--- /dev/null
+++ b/src/gcdump/gcdump.cpp
@@ -0,0 +1,80 @@
+// 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.
+/*****************************************************************************
+ * GCDump.cpp
+ *
+ * Defines functions to display the GCInfo as defined by the GC-encoding
+ * spec. The GC information may be either dynamically created by a
+ * Just-In-Time compiler conforming to the standard code-manager spec,
+ * or may be persisted by a managed native code compiler conforming
+ * to the standard code-manager spec.
+ */
+
+#include "utilcode.h" // For _ASSERTE()
+#include "gcdump.h"
+
+/*****************************************************************************/
+
+
+
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion (gcInfoVer),
+ fDumpEncBytes (encBytes ),
+ cMaxEncBytes (maxEncBytes ),
+ fDumpCodeOffsets(dumpCodeOffs)
+{
+ // By default, use the standard printf function to dump
+ GCDump::gcPrintf = (printfFtn) ::printf;
+}
+
+/*****************************************************************************
+ *
+ * Display the byte encodings for the given range of the GC tables.
+ */
+
+PTR_CBYTE GCDump::DumpEncoding(PTR_CBYTE gcInfoBlock, int cDumpBytes)
+{
+ _ASSERTE((cDumpBytes >= 0) && (cMaxEncBytes < 256));
+
+ if (fDumpEncBytes)
+ {
+ PTR_CBYTE pCurPos;
+ unsigned count;
+ int cBytesLeft;
+
+ for (count = cMaxEncBytes, cBytesLeft = cDumpBytes, pCurPos = gcInfoBlock;
+ count > 0;
+ count--, pCurPos++, cBytesLeft--)
+ {
+ if (cBytesLeft > 0)
+ {
+ if (cBytesLeft > 1 && count == 1)
+ gcPrintf("...");
+ else
+ gcPrintf("%02X ", *pCurPos);
+ }
+ else
+ gcPrintf(" ");
+ }
+
+ gcPrintf("| ");
+ }
+
+ return gcInfoBlock + cDumpBytes;
+}
+
+/*****************************************************************************/
+
+void GCDump::DumpOffset(unsigned o)
+{
+ gcPrintf("%04X", o);
+}
+
+void GCDump::DumpOffsetEx(unsigned o)
+{
+ if (fDumpCodeOffsets)
+ DumpOffset(o);
+}
+
+/*****************************************************************************/
diff --git a/src/gcdump/gcdump.settings.targets b/src/gcdump/gcdump.settings.targets
new file mode 100644
index 0000000000..9a44330a0d
--- /dev/null
+++ b/src/gcdump/gcdump.settings.targets
@@ -0,0 +1,24 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <UserIncludes>$(UserIncludes);
+ .</UserIncludes>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <ClAdditionalOptions Condition="'$(USE_ICECAP)' != ''">$(ClAdditionalOptions) -D_ICECAP</ClAdditionalOptions>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <ItemGroup>
+ <CppCompile Include="..\GCDump.cpp" />
+ <CppCompile Include="..\i386\GCDumpX86.cpp" Condition="'$(RealBuildArchitecture)' == 'x86'" />
+ <CppCompile Include="..\rotor_x86\GCDumpX86.cpp" Condition="'$(_BuildArch)' == 'rotor_x86'" />
+ </ItemGroup>
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/gcdump/gcdumpnonx86.cpp b/src/gcdump/gcdumpnonx86.cpp
new file mode 100644
index 0000000000..7343ac9771
--- /dev/null
+++ b/src/gcdump/gcdumpnonx86.cpp
@@ -0,0 +1,528 @@
+// 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.
+/*****************************************************************************
+ * GCDumpNonX86.cpp
+ */
+
+#include "gcdump.h"
+
+#define LIMITED_METHOD_CONTRACT ((void)0)
+#define WRAPPER_NO_CONTRACT ((void)0)
+
+#define GCINFODECODER_NO_EE
+#include "gcinfodecoder.h"
+#include "gcinfodumper.h"
+
+
+PCSTR GetRegName (UINT32 regnum)
+{
+#ifdef _TARGET_AMD64_
+
+ switch (regnum)
+ {
+ case 0: return "rax";
+ case 1: return "rcx";
+ case 2: return "rdx";
+ case 3: return "rbx";
+ case 4: return "rsp";
+ case 5: return "rbp";
+ case 6: return "rsi";
+ case 7: return "rdi";
+ case 8: return "r8";
+ case 9: return "r9";
+ case 10: return "r10";
+ case 11: return "r11";
+ case 12: return "r12";
+ case 13: return "r13";
+ case 14: return "r14";
+ case 15: return "r15";
+ }
+
+
+ return "???";
+#elif defined(_TARGET_ARM64_)
+
+ static CHAR szRegName[16];
+ if (regnum < 29)
+ {
+ _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "X%u", regnum);
+ return szRegName;
+ }
+ else if(regnum == 29)
+ {
+ return "Fp";
+ }
+ else if(regnum == 30)
+ {
+ return "Lr";
+ }
+ else if(regnum == 31)
+ {
+ return "Sp";
+ }
+
+ return "???";
+#elif defined(_TARGET_ARM_)
+ if (regnum > 128)
+ return "???";
+
+ static CHAR szRegName[16];
+ _snprintf_s(szRegName, _countof(szRegName), sizeof(szRegName), "r%u", regnum);
+ return szRegName;
+
+#endif
+}
+
+
+/*****************************************************************************/
+
+
+GCDump::GCDump(UINT32 gcInfoVer, bool encBytes, unsigned maxEncBytes, bool dumpCodeOffs)
+ : gcInfoVersion(gcInfoVer),
+ fDumpEncBytes (encBytes ),
+ cMaxEncBytes (maxEncBytes ),
+ fDumpCodeOffsets(dumpCodeOffs)
+{
+}
+
+
+/*****************************************************************************/
+
+
+struct GcInfoDumpState
+{
+ UINT32 LastCodeOffset;
+ BOOL fAnythingPrinted;
+ BOOL fSafePoint;
+ UINT32 FrameRegister;
+ GCDump::printfFtn pfnPrintf;
+};
+
+
+BOOL InterruptibleStateChangeCallback (
+ UINT32 CodeOffset,
+ BOOL fInterruptible,
+ PVOID pvData)
+{
+ GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
+
+ if (pState->fAnythingPrinted)
+ {
+ pState->pfnPrintf("\n");
+ pState->fAnythingPrinted = FALSE;
+ pState->fSafePoint = FALSE;
+ }
+
+ pState->pfnPrintf("%08x%s interruptible\n", CodeOffset, fInterruptible ? "" : " not");
+
+ pState->LastCodeOffset = -1;
+
+ return FALSE;
+}
+
+BOOL SafePointCallback (
+ UINT32 CodeOffset,
+ PVOID pvData)
+{
+ GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
+
+ if (pState->fAnythingPrinted)
+ {
+ pState->pfnPrintf("\n");
+ }
+
+ pState->pfnPrintf("%08x is a safepoint: ", CodeOffset);
+
+ pState->LastCodeOffset = CodeOffset;
+ pState->fAnythingPrinted = TRUE;
+ pState->fSafePoint = TRUE;
+
+ return FALSE;
+}
+
+
+VOID PrintFlags (GCDump::printfFtn pfnPrintf, GcSlotFlags Flags)
+{
+ if (Flags & GC_SLOT_PINNED)
+ pfnPrintf("(pinned)");
+
+ if (Flags & GC_SLOT_INTERIOR)
+ pfnPrintf("(interior)");
+
+ if (Flags & GC_SLOT_UNTRACKED)
+ pfnPrintf("(untracked)");
+}
+
+
+BOOL RegisterStateChangeCallback (
+ UINT32 CodeOffset,
+ UINT32 RegisterNumber,
+ GcSlotFlags Flags,
+ GcSlotState NewState,
+ PVOID pvData)
+{
+ GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
+
+ if (pState->fSafePoint && (GC_SLOT_LIVE != NewState))
+ {
+ // Don't print deaths for safepoints
+ return FALSE;
+ }
+
+ if (pState->LastCodeOffset != CodeOffset)
+ {
+ if (pState->fAnythingPrinted)
+ pState->pfnPrintf("\n");
+
+ pState->pfnPrintf("%08x", CodeOffset);
+
+ pState->LastCodeOffset = CodeOffset;
+ }
+
+ char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-');
+
+ pState->pfnPrintf(" %c%s", delta, GetRegName(RegisterNumber));
+
+ PrintFlags(pState->pfnPrintf, Flags);
+
+ pState->fAnythingPrinted = TRUE;
+
+ return FALSE;
+}
+
+
+BOOL StackSlotStateChangeCallback (
+ UINT32 CodeOffset,
+ GcSlotFlags flags,
+ GcStackSlotBase BaseRegister,
+ SSIZE_T StackOffset,
+ GcSlotState NewState,
+ PVOID pvData)
+{
+ GcInfoDumpState *pState = (GcInfoDumpState*)pvData;
+
+ if (pState->fSafePoint && (GC_SLOT_LIVE != NewState))
+ {
+ // Don't print deaths for safepoints
+ return FALSE;
+ }
+
+ if (pState->LastCodeOffset != CodeOffset)
+ {
+ if (pState->fAnythingPrinted)
+ pState->pfnPrintf("\n");
+
+ if ((CodeOffset == -2) && !pState->fAnythingPrinted)
+ pState->pfnPrintf("Untracked:");
+ else
+ pState->pfnPrintf("%08x", CodeOffset);
+
+ pState->LastCodeOffset = CodeOffset;
+ }
+
+ char delta = ((GC_SLOT_LIVE == NewState) ? '+' : '-');
+
+ CHAR sign = '+';
+
+ // the dumper's call back (in GcInfoDumper.cpp) has to "guess" the base register
+ // for stack slots it usually guesses it wrong ......
+ // We try to filter out at least the non-sensical combinations
+ // - negative offset relative to SP
+ // - positive offset relative to CALLER_SP
+
+ if (StackOffset < 0)
+ {
+ StackOffset = -StackOffset;
+ sign = '-';
+#ifndef GCINFODUMPER_IS_FIXED
+ if (BaseRegister == GC_SP_REL)
+ { // negative offset to SP????
+ BaseRegister = GC_CALLER_SP_REL;
+ }
+#endif // !GCINFODUMPER_IS_FIXED
+ }
+#ifndef GCINFODUMPER_IS_FIXED
+ else if (BaseRegister == GC_CALLER_SP_REL)
+ { // positive offset to Caller_SP????
+ BaseRegister = GC_SP_REL;
+ }
+#endif // !GCINFODUMPER_IS_FIXED
+
+
+
+ PCSTR pszBaseReg;
+
+ switch (BaseRegister)
+ {
+ case GC_CALLER_SP_REL: pszBaseReg = "caller.sp"; break;
+ case GC_SP_REL: pszBaseReg = "sp"; break;
+ case GC_FRAMEREG_REL: pszBaseReg = GetRegName(pState->FrameRegister); break;
+ default: pszBaseReg = "???"; break;
+ }
+
+ pState->pfnPrintf(" %c%s%c%x", delta, pszBaseReg, sign, StackOffset);
+
+ PrintFlags(pState->pfnPrintf, flags);
+
+ pState->fAnythingPrinted = TRUE;
+
+ return FALSE;
+}
+
+
+size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
+ unsigned methodSize,
+ bool verifyGCTables)
+{
+ GCInfoToken gcInfoToken = { dac_cast<PTR_VOID>(gcInfoBlock), gcInfoVersion };
+ GcInfoDecoder hdrdecoder(gcInfoToken,
+ (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT
+ | DECODE_GS_COOKIE
+ | DECODE_CODE_LENGTH
+ | DECODE_PSP_SYM
+ | DECODE_VARARG
+ | DECODE_GENERICS_INST_CONTEXT
+ | DECODE_GC_LIFETIMES
+ | DECODE_PROLOG_LENGTH
+ | DECODE_RETURN_KIND),
+ 0);
+
+ if (NO_SECURITY_OBJECT != hdrdecoder.GetSecurityObjectStackSlot() ||
+ NO_GENERICS_INST_CONTEXT != hdrdecoder.GetGenericsInstContextStackSlot() ||
+ NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot())
+ {
+ gcPrintf("Prolog size: ");
+ UINT32 prologSize = hdrdecoder.GetPrologSize();
+ gcPrintf("%d\n", prologSize);
+ }
+
+ gcPrintf("Security object: ");
+ if (NO_SECURITY_OBJECT == hdrdecoder.GetSecurityObjectStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetSecurityObjectStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+ gcPrintf("caller.sp%c%x\n", sign, ofs);
+ }
+
+ gcPrintf("GS cookie: ");
+ if (NO_GS_COOKIE == hdrdecoder.GetGSCookieStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetGSCookieStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+ gcPrintf("caller.sp%c%x\n", sign, ofs);
+
+ UINT32 validRangeStart = hdrdecoder.GetGSCookieValidRangeStart();
+ UINT32 validRangeEnd = hdrdecoder.GetGSCookieValidRangeEnd();
+ gcPrintf("GS cookie valid range: [%x;%x)\n", validRangeStart, validRangeEnd);
+ }
+
+ gcPrintf("PSPSym: ");
+ if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetPSPSymStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+#ifdef _TARGET_AMD64_
+ // The PSPSym is relative to InitialSP on X64 and CallerSP on other platforms.
+ gcPrintf("initial.sp%c%x\n", sign, ofs);
+#else
+ gcPrintf("caller.sp%c%x\n", sign, ofs);
+#endif
+ }
+
+ gcPrintf("Generics inst context: ");
+ if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+ gcPrintf("caller.sp%c%x\n", sign, ofs);
+ }
+
+ gcPrintf("PSP slot: ");
+ if (NO_PSP_SYM == hdrdecoder.GetPSPSymStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetPSPSymStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+ gcPrintf("caller.sp%c%x\n", sign, ofs);
+
+ }
+
+ gcPrintf("GenericInst slot: ");
+ if (NO_GENERICS_INST_CONTEXT == hdrdecoder.GetGenericsInstContextStackSlot())
+ {
+ gcPrintf("<none>\n");
+ }
+ else
+ {
+ INT32 ofs = hdrdecoder.GetGenericsInstContextStackSlot();
+ char sign = '+';
+
+ if (ofs < 0)
+ {
+ sign = '-';
+ ofs = -ofs;
+ }
+
+ gcPrintf("caller.sp%c%x ", sign, ofs);
+
+ if (hdrdecoder.HasMethodDescGenericsInstContext())
+ gcPrintf("(GENERIC_PARAM_CONTEXT_METHODDESC)\n");
+ else if (hdrdecoder.HasMethodTableGenericsInstContext())
+ gcPrintf("(GENERIC_PARAM_CONTEXT_METHODHANDLE)\n");
+ else
+ gcPrintf("(GENERIC_PARAM_CONTEXT_THIS)\n");
+ }
+
+ gcPrintf("Varargs: %u\n", hdrdecoder.GetIsVarArg());
+ gcPrintf("Frame pointer: %s\n", NO_STACK_BASE_REGISTER == hdrdecoder.GetStackBaseRegister()
+ ? "<none>"
+ : GetRegName(hdrdecoder.GetStackBaseRegister()));
+
+ gcPrintf("Wants Report Only Leaf: %u\n", hdrdecoder.WantsReportOnlyLeaf());
+
+#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
+ gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea());
+#endif
+
+ ReturnKind returnKind = hdrdecoder.GetReturnKind();
+ gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind));
+
+ UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
+ gcPrintf("Code size: %x\n", cbEncodedMethodSize);
+
+ GcInfoDumper dumper(gcInfoToken);
+
+ GcInfoDumpState state;
+ state.LastCodeOffset = -1;
+ state.fAnythingPrinted = FALSE;
+ state.fSafePoint = FALSE;
+ state.FrameRegister = hdrdecoder.GetStackBaseRegister();
+ state.pfnPrintf = gcPrintf;
+
+ GcInfoDumper::EnumerateStateChangesResults result = dumper.EnumerateStateChanges(
+ &InterruptibleStateChangeCallback,
+ &RegisterStateChangeCallback,
+ &StackSlotStateChangeCallback,
+ &SafePointCallback,
+ &state);
+
+ if (state.fAnythingPrinted)
+ gcPrintf("\n");
+
+ switch (result)
+ {
+ case GcInfoDumper::SUCCESS:
+ // do nothing
+ break;
+
+ case GcInfoDumper::OUT_OF_MEMORY:
+ gcPrintf("out of memory\n");
+ break;
+
+ case GcInfoDumper::REPORTED_REGISTER_IN_CALLERS_FRAME:
+ gcPrintf("reported register in caller's frame\n");
+ break;
+
+ case GcInfoDumper::REPORTED_FRAME_POINTER:
+ gcPrintf("reported frame register\n");
+ break;
+
+ case GcInfoDumper::REPORTED_INVALID_BASE_REGISTER:
+ gcPrintf("reported pointer relative to wrong base register\n");
+ break;
+
+ case GcInfoDumper::REPORTED_INVALID_POINTER:
+ gcPrintf("reported invalid pointer\n");
+ break;
+
+ case GcInfoDumper::DECODER_FAILED:
+ gcPrintf("decoder failed\n");
+ break;
+
+ default:
+ gcPrintf("invalid GC info\n");
+ break;
+ }
+
+ return (result == GcInfoDumper::SUCCESS) ? dumper.GetGCInfoSize() : 0;
+}
+
+
+/*****************************************************************************/
+
+void GCDump::DumpPtrsInFrame(PTR_CBYTE infoBlock,
+ PTR_CBYTE codeBlock,
+ unsigned offs,
+ bool verifyGCTables)
+{
+ _ASSERTE(!"NYI");
+}
+
+
+#define _common_h_
+
+#ifndef LOG
+#define LOG(x) ((void)0)
+#endif
+
+#define GCINFODECODER_CONTRACT(contract)
+#define GET_CALLER_SP(pREGDISPLAY) ((size_t)GetSP(pREGDISPLAY->pCallerContext))
+#define VALIDATE_OBJECTREF(objref, fDeep) ((void)0)
+#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef) ((void)0)
+#include "../vm/gcinfodecoder.cpp"
+#include "../gcinfo/gcinfodumper.cpp"
diff --git a/src/gcdump/i386/.gitmirror b/src/gcdump/i386/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/gcdump/i386/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/gcdump/i386/gcdumpx86.cpp b/src/gcdump/i386/gcdumpx86.cpp
new file mode 100644
index 0000000000..70334dee65
--- /dev/null
+++ b/src/gcdump/i386/gcdumpx86.cpp
@@ -0,0 +1,1068 @@
+// 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.
+/*****************************************************************************
+ * GCDumpX86.cpp
+ */
+
+/*****************************************************************************/
+#ifdef _TARGET_X86_
+/*****************************************************************************/
+
+#include "utilcode.h" // For _ASSERTE()
+#include "gcdump.h"
+
+
+/*****************************************************************************/
+
+#define castto(var,typ) (*(typ *)&var)
+
+#define sizeto(typ,mem) (offsetof(typ, mem) + sizeof(((typ*)0)->mem))
+
+#define CALLEE_SAVED_REG_MAXSZ (4*sizeof(int)) // EBX,ESI,EDI,EBP
+
+/*****************************************************************************/
+
+const char * RegName(unsigned reg)
+{
+ static const char * const regNames[] =
+ {
+ "EAX",
+ "ECX",
+ "EDX",
+ "EBX",
+
+ "ESP",
+ "EBP",
+ "ESI",
+ "EDI"
+ };
+
+ _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
+
+ return regNames[reg];
+}
+
+const char * CalleeSavedRegName(unsigned reg)
+{
+ static const char * const regNames[] =
+ {
+ "EDI",
+ "ESI",
+ "EBX",
+ "EBP"
+ };
+
+ _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
+
+ return regNames[reg];
+}
+
+/*****************************************************************************/
+
+unsigned GCDump::DumpInfoHdr (PTR_CBYTE table,
+ InfoHdr* header,
+ unsigned * methodSize,
+ bool verifyGCTables)
+{
+ unsigned count;
+ PTR_CBYTE tableStart = table;
+ PTR_CBYTE bp = table;
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
+
+ /* Get the method size */
+
+ table += decodeUnsigned(table, methodSize);
+
+ table = decodeHeader(table, header);
+
+ BOOL hasArgTabOffset = FALSE;
+ if (header->untrackedCnt == HAS_UNTRACKED)
+ {
+ hasArgTabOffset = TRUE;
+ table += decodeUnsigned(table, &count);
+ header->untrackedCnt = count;
+ }
+
+ if (header->varPtrTableSize == HAS_VARPTR)
+ {
+ hasArgTabOffset = TRUE;
+ table += decodeUnsigned(table, &count);
+ header->varPtrTableSize = count;
+ }
+
+ if (header->gsCookieOffset == HAS_GS_COOKIE_OFFSET)
+ {
+ table += decodeUnsigned(table, &count);
+ header->gsCookieOffset = count;
+ }
+
+ if (header->syncStartOffset == HAS_SYNC_OFFSET)
+ {
+ table += decodeUnsigned(table, &count);
+ header->syncStartOffset = count;
+ table += decodeUnsigned(table, &count);
+ header->syncEndOffset = count;
+ }
+
+ //
+ // First print out all the basic information
+ //
+
+ gcPrintf(" method size = %04X\n", *methodSize);
+ gcPrintf(" prolog size = %2u \n", header->prologSize);
+ gcPrintf(" epilog size = %2u \n", header->epilogSize);
+ gcPrintf(" epilog count = %2u \n", header->epilogCount);
+ gcPrintf(" epilog end = %s \n", header->epilogAtEnd ? "yes" : "no");
+
+ gcPrintf(" callee-saved regs = ");
+ if (header->ediSaved) gcPrintf("EDI ");
+ if (header->esiSaved) gcPrintf("ESI ");
+ if (header->ebxSaved) gcPrintf("EBX ");
+ if (header->ebpSaved) gcPrintf("EBP ");
+ gcPrintf("\n");
+
+ gcPrintf(" ebp frame = %s \n", header->ebpFrame ? "yes" : "no");
+ gcPrintf(" fully interruptible= %s \n", header->interruptible ? "yes" : "no");
+ gcPrintf(" double align = %s \n", header->doubleAlign ? "yes" : "no");
+ gcPrintf(" arguments size = %2u DWORDs\n", header->argCount);
+ gcPrintf(" stack frame size = %2u DWORDs\n", header->frameSize);
+ gcPrintf(" untracked count = %2u \n", header->untrackedCnt);
+ gcPrintf(" var ptr tab count = %2u \n", header->varPtrTableSize);
+
+ //
+ // Now display optional information
+ //
+
+ if (header->security) gcPrintf(" security check obj = yes\n");
+ if (header->handlers) gcPrintf(" exception handlers = yes\n");
+ if (header->localloc) gcPrintf(" localloc = yes\n");
+ if (header->editNcontinue) gcPrintf(" edit & continue = yes\n");
+ if (header->profCallbacks) gcPrintf(" profiler callbacks = yes\n");
+ if (header->varargs) gcPrintf(" varargs = yes\n");
+ if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
+ gcPrintf(" GuardStack cookie = [%s%u]\n",
+ header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset);
+ if (header->syncStartOffset != INVALID_SYNC_OFFSET)
+ gcPrintf(" Sync region = [%u,%u]\n",
+ header->syncStartOffset, header->syncEndOffset);
+
+ if (header->epilogCount > 1 || (header->epilogCount != 0 &&
+ header->epilogAtEnd == 0))
+ {
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
+
+ unsigned offs = 0;
+
+ for (unsigned i = 0; i < header->epilogCount; i++)
+ {
+ table += decodeUDelta(table, &offs, offs);
+ gcPrintf(" epilog #%2u at %04X\n", i, offs);
+ }
+ }
+ else
+ {
+ if (header->epilogCount)
+ gcPrintf(" epilog at %04X\n", (*methodSize - header->epilogSize));
+ }
+
+ if (hasArgTabOffset)
+ {
+ unsigned argTabOffset;
+ table += decodeUnsigned(table, &argTabOffset);
+ gcPrintf(" argTabOffset = %x \n", argTabOffset);
+ }
+
+ {
+ unsigned cur = 0;
+ unsigned last = table-bp;
+ while (cur < last)
+ {
+ unsigned amount = last - cur;
+ if (amount>5)
+ amount = 5;
+
+ DumpEncoding(bp+cur, amount);
+ gcPrintf("\n");
+
+ cur += amount;
+ }
+ }
+
+ return (table - tableStart);
+}
+
+/*****************************************************************************/
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+size_t GCDump::DumpGCTable(PTR_CBYTE table,
+ const InfoHdr& header,
+ unsigned methodSize,
+ bool verifyGCTables)
+{
+ int sz;
+ PTR_CBYTE tableStart = table;
+ PTR_CBYTE bp;
+
+ unsigned count;
+ unsigned curOffs;
+
+//#if!TGT_x86
+// _ASSERTE(!"NYI");
+//#endif
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF);
+
+ unsigned calleeSavedRegs = 0;
+ if (header.doubleAlign)
+ {
+ calleeSavedRegs = 0;
+ if (header.ediSaved) calleeSavedRegs++;
+ if (header.esiSaved) calleeSavedRegs++;
+ if (header.ebxSaved) calleeSavedRegs++;
+ }
+
+ /* Dump the untracked frame variable table */
+
+ count = header.untrackedCnt;
+
+ int lastStkOffs = 0;
+ while (count-- > 0)
+ {
+ int stkOffsDelta;
+ unsigned lowBits;
+
+ char reg = header.ebpFrame ? 'B' : 'S';
+
+ sz = (unsigned int)decodeSigned(table, &stkOffsDelta);
+ int stkOffs = lastStkOffs - stkOffsDelta;
+ lastStkOffs = stkOffs;
+
+ table = DumpEncoding(table, sz);
+
+ _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
+
+ lowBits = OFFSET_MASK & stkOffs;
+ stkOffs &= ~OFFSET_MASK;
+
+ assert(!header.doubleAlign || stkOffs >= 0);
+
+ if (header.doubleAlign &&
+ unsigned(stkOffs) >= sizeof(int)*(header.frameSize+calleeSavedRegs))
+ {
+ reg = 'B';
+ stkOffs -= sizeof(int)*(header.frameSize+calleeSavedRegs);
+ _ASSERTE(stkOffs >= (int) (2*sizeof(int)));
+ }
+
+ if (stkOffs < 0)
+ gcPrintf(" [E%cP-%02XH] ", reg, -stkOffs);
+ else
+ gcPrintf(" [E%cP+%02XH] ", reg, +stkOffs);
+
+ gcPrintf("an untracked %s%s local\n",
+ (lowBits & pinned_OFFSET_FLAG) ? "pinned " : "",
+ (lowBits & byref_OFFSET_FLAG) ? "byref" : ""
+ );
+ }
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xCAFE);
+
+ /* Dump the frame variable lifetime table */
+
+ count = header.varPtrTableSize;
+ curOffs = 0;
+
+ while (count-- > 0)
+ {
+ unsigned varOffs;
+ unsigned begOffs;
+ unsigned endOffs;
+ unsigned lowBits;
+
+ bp = table;
+
+ table += decodeUnsigned(table, &varOffs);
+ table += decodeUDelta (table, &begOffs, curOffs);
+ table += decodeUDelta (table, &endOffs, begOffs);
+
+ DumpEncoding(bp, table-bp);
+
+ _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
+
+ lowBits = varOffs & 0x3;
+ varOffs &= ~OFFSET_MASK;
+
+ // [EBP+0] is the return address - cant be a var
+ _ASSERTE(!header.ebpFrame || varOffs);
+
+ curOffs = begOffs;
+
+ DumpOffset(begOffs);
+ gcPrintf("..");
+ DumpOffset(endOffs);
+ gcPrintf(" [E%s%02XH] a ", header.ebpFrame ? "BP-" : "SP+",
+ varOffs);
+
+ gcPrintf("%s%s pointer\n",
+ (lowBits & byref_OFFSET_FLAG) ? "byref " : "",
+ (lowBits & this_OFFSET_FLAG) ? "this" : ""
+ );
+
+ _ASSERTE(endOffs <= methodSize);
+ }
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xBABE);
+
+ /* Dump the pointer table */
+
+ curOffs = 0;
+ bp = table;
+
+ if (header.interruptible)
+ {
+ //
+ // Dump the Fully Interruptible pointer table
+ //
+ unsigned argCnt = 0;
+ bool isThis = false;
+ bool iptr = false;
+
+ for (;;)
+ {
+ unsigned isPop;
+ unsigned argOffs;
+ unsigned val = *table++;
+
+ _ASSERTE(curOffs <= methodSize);
+
+ if (!(val & 0x80))
+ {
+ /* A small 'regPtr' encoding */
+
+ curOffs += val & 0x7;
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+ gcPrintf(" reg %s becoming %s", RegName(((val >> 3) & 7)),
+ (val & 0x40) ? "live"
+ : "dead");
+ if (isThis)
+ gcPrintf(" 'this'");
+ if (iptr)
+ gcPrintf(" (iptr)");
+ gcPrintf("\n");
+
+ isThis = false;
+ iptr = false;
+ continue;
+ }
+
+ /* This is probably an argument push/pop */
+
+ argOffs = (val & 0x38) >> 3;
+
+ /* 6 [110] and 7 [111] are reserved for other encodings */
+
+ if (argOffs < 6)
+ {
+ /* A small argument encoding */
+
+ curOffs += (val & 0x07);
+ isPop = (val & 0x40);
+
+ ARG:
+
+ if (isPop)
+ {
+ // A Pop of 0, means little-delta
+
+ if (argOffs != 0)
+ {
+ _ASSERTE(header.ebpFrame || argOffs <= argCnt);
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" pop %2d ", argOffs);
+ if (!header.ebpFrame)
+ {
+ argCnt -= argOffs;
+ gcPrintf("args (%d)", argCnt);
+ }
+ else
+ gcPrintf("ptrs");
+
+ gcPrintf("\n");
+ }
+ }
+ else
+ {
+ _ASSERTE(header.ebpFrame || argOffs >= argCnt);
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" push ptr %2d", argOffs);
+ if (!header.ebpFrame)
+ {
+ argCnt = argOffs+1;
+ gcPrintf(" (%d)", argCnt);
+ }
+ if (isThis)
+ gcPrintf(" 'this'");
+ if (iptr)
+ gcPrintf(" (iptr)");
+ gcPrintf("\n");
+
+ isThis = false;
+ iptr = false;
+ }
+
+ continue;
+ }
+ else if (argOffs == 6)
+ {
+ if (val & 0x40)
+ {
+ /* Bigger delta 000=8,001=16,010=24,...,111=64 */
+
+ curOffs += (((val & 0x07) + 1) << 3);
+ }
+ else
+ {
+ /* non-ptr arg push */
+
+ curOffs += (val & 0x07);
+ _ASSERTE(!header.ebpFrame);
+ argCnt++;
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" push non-ptr (%d)\n", argCnt);
+ }
+
+ continue;
+ }
+
+ /* argOffs was 7 [111] which is reserved for the larger encodings */
+
+ _ASSERTE(argOffs==7);
+
+ switch (val)
+ {
+ case 0xFF:
+ goto DONE_REGTAB;
+
+ case 0xBC:
+ isThis = true;
+ break;
+
+ case 0xBF:
+ iptr = true;
+ break;
+
+ case 0xB8:
+ table += decodeUnsigned(table, &val);
+ curOffs += val;
+ break;
+
+ case 0xF8:
+ case 0xFC:
+ isPop = val & 0x04;
+ table += decodeUnsigned(table, &argOffs);
+ goto ARG;
+
+ case 0xFD:
+ table += decodeUnsigned(table, &argOffs);
+ assert(argOffs);
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" kill args %2d\n", argOffs);
+ break;
+
+ case 0xF9:
+ table += decodeUnsigned(table, &argOffs);
+ argCnt += argOffs;
+ break;
+
+ default:
+ gcPrintf("Unexpected special code %04X\n", val);
+ _ASSERTE(!"");
+ }
+ }
+ }
+ else if (header.ebpFrame) // interruptible is false
+ {
+ //
+ // Dump the Partially Interruptible, EBP-frame method, pointer table
+ //
+
+ for (;;)
+ {
+ unsigned argMask = 0, byrefArgMask = 0;
+ unsigned regMask, byrefRegMask = 0;
+
+ unsigned argCnt = 0;
+ PTR_CBYTE argTab = NULL;
+ unsigned argTabSize;
+
+ unsigned val, nxt;
+
+ /* Get the next byte and check for a 'special' entry */
+
+ unsigned encType = *table++;
+
+ _ASSERTE(curOffs <= methodSize);
+
+ switch (encType)
+ {
+
+ default:
+
+ /* A tiny or small call entry */
+
+ val = encType;
+
+ if ((val & 0x80) == 0x00)
+ {
+ if (val & 0x0F)
+ {
+ /* A tiny call entry */
+
+ curOffs += (val & 0x0F);
+ regMask = (val & 0x70) >> 4;
+ argMask = 0;
+ }
+ else
+ {
+ DumpEncoding(bp, table-bp); bp = table;
+
+ gcPrintf(" thisptr in ");
+ if (val & 0x10)
+ gcPrintf("EDI\n");
+ else if (val & 0x20)
+ gcPrintf("ESI\n");
+ else if (val & 0x40)
+ gcPrintf("EBX\n");
+ else
+ _ASSERTE(!"Reserved GC encoding");
+
+ continue;
+ }
+ }
+ else
+ {
+ /* A small call entry */
+
+ curOffs += (val & 0x7F);
+ val = *table++;
+ regMask = val >> 5;
+ argMask = val & 0x1F;
+ }
+ break;
+
+ case 0xFD: // medium encoding
+
+ argMask = *table++;
+ val = *table++;
+ argMask |= (val & 0xF0) << 4;
+ nxt = *table++;
+ curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4);
+ regMask = nxt >> 5; // EBX,ESI,EDI
+
+ break;
+
+ case 0xF9: // medium encoding with byrefs
+
+ curOffs += *table++;
+ val = *table++;
+ argMask = val & 0x1F;
+ regMask = val >> 5;
+ val = *table++;
+ byrefArgMask = val & 0x1F;
+ byrefRegMask = val >> 5;
+
+ break;
+
+ case 0xFE: // large encoding
+ case 0xFA: // large encoding with byrefs
+
+ val = *table++;
+ regMask = val & 0x7;
+ byrefRegMask= val >> 4;
+ curOffs += *PTR_DWORD(table); table += sizeof(DWORD);
+ argMask = *PTR_DWORD(table); table += sizeof(DWORD);
+ if (encType == 0xFA) // read byrefArgMask
+ {byrefArgMask = *PTR_DWORD(table); table += sizeof(DWORD);}
+
+ break;
+
+ case 0xFB: // huge encoding
+
+ val = *table++;
+ regMask = val & 0x7;
+ byrefRegMask= val >> 4;
+ curOffs = *PTR_DWORD(table); table += sizeof(DWORD);
+ argCnt = *PTR_DWORD(table); table += sizeof(DWORD);
+ argTabSize = *PTR_DWORD(table); table += sizeof(DWORD);
+ argTab = table; table += argTabSize;
+
+ break;
+
+ case 0xFF:
+ goto DONE_REGTAB;
+ }
+
+ /*
+ Here we have the following values:
+
+ curOffs ... the code offset of the call
+ regMask ... mask of live pointer register variables
+ argMask ... bitmask of pushed pointer arguments
+ byrefRegMask ... byref qualifier for regMask
+ byrefArgMask ... byrer qualifier for argMask
+ */
+
+ _ASSERTE((byrefArgMask & argMask) == byrefArgMask);
+ _ASSERTE((byrefRegMask & regMask) == byrefRegMask);
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" call [ ");
+
+ if (regMask & 1)
+ gcPrintf("EDI%c", (byrefRegMask & 1) ? '\'' : ' ');
+ if (regMask & 2)
+ gcPrintf("ESI%c", (byrefRegMask & 2) ? '\'' : ' ');
+ if (regMask & 4)
+ gcPrintf("EBX%c", (byrefRegMask & 4) ? '\'' : ' ');
+
+ if (!header.ebpFrame)
+ {
+ if (regMask & 8)
+ gcPrintf("EBP ");
+ }
+
+ if (argCnt)
+ {
+ gcPrintf("] ptrArgs=[");
+
+ do
+ {
+ argTab += decodeUnsigned(argTab, &val);
+
+ assert((val & this_OFFSET_FLAG) == 0);
+ unsigned stkOffs = val & ~byref_OFFSET_FLAG;
+ unsigned lowBit = val & byref_OFFSET_FLAG;
+
+ gcPrintf("%u%s", stkOffs, lowBit ? "i" : "");
+ if (argCnt > 1)
+ gcPrintf(" ");
+ }
+ while (--argCnt);
+ assert(argTab == table);
+
+ gcPrintf("]");
+ }
+ else
+ {
+ gcPrintf("] argMask=%02X", argMask);
+
+ if (byrefArgMask) gcPrintf(" (iargs=%02X)", byrefArgMask);
+ }
+
+ gcPrintf("\n");
+ }
+ }
+ else // interruptible is false, ebpFrame is false
+ {
+ //
+ // Dump the Partially Interruptible, EBP-less method, pointer table
+ //
+ unsigned lastSkip = 0;
+ unsigned imask = 0;
+
+ for (;;)
+ {
+ unsigned val = *table++;
+
+ _ASSERTE(curOffs <= methodSize);
+
+ if (!(val & 0x80))
+ {
+ if (!(val & 0x40))
+ {
+ if (!(val & 0x20))
+ {
+ //
+ // push 000DDDDD push one item, 5-bit delta
+ //
+
+ curOffs += val & 0x1F;
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" push\n");
+ }
+ else
+ {
+ //
+ // push 00100000 [pushCount] ESP push multiple items
+ //
+
+ unsigned pushCount;
+
+ assert(val == 0x20);
+ table += decodeUnsigned(table, &pushCount);
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" push %d\n", pushCount);
+ }
+ }
+ else
+ {
+ unsigned popSize;
+ unsigned skip;
+
+ if ((val & 0x3f) == 0)
+ {
+ //
+ // skip 01000000 [Delta] Skip arbitrary sized delta
+ //
+
+ table += decodeUnsigned(table, &skip);
+ curOffs += skip;
+ lastSkip = skip;
+ }
+ else
+ {
+ //
+ // pop 01CCDDDD pop CC items, 4-bit delta
+ //
+
+ popSize = (val & 0x30) >> 4;
+ skip = val & 0x0f;
+ curOffs += skip;
+
+ if (popSize > 0)
+ {
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" pop %d\n", popSize);
+ }
+ else
+ lastSkip = skip;
+ }
+ }
+ }
+ else
+ {
+ unsigned callArgCnt;
+ unsigned callRegMask;
+ bool callPndTab = false;
+ unsigned callPndMask = 0;
+ unsigned callPndTabCnt = 0, callPndTabSize = 0;
+
+ switch ((val & 0x70) >> 4)
+ {
+ default:
+ //
+ // call 1PPPPPPP Call Pattern, P=[0..79]
+ //
+ decodeCallPattern((val & 0x7f), &callArgCnt, &callRegMask,
+ &callPndMask, &lastSkip);
+ curOffs += lastSkip;
+
+ PRINT_CALL:
+
+ DumpEncoding(bp, table-bp); bp = table;
+ DumpOffsetEx(curOffs);
+
+ gcPrintf(" call %d [ ", callArgCnt);
+
+ unsigned iregMask, iargMask;
+
+ iregMask = imask & 0xF;
+ iargMask = imask >> 4;
+
+ assert((callRegMask & 0x0F) == callRegMask);
+ if (callRegMask & 1)
+ gcPrintf("EDI%c", (iregMask & 1) ? '\'' : ' ');
+ if (callRegMask & 2)
+ gcPrintf("ESI%c", (iregMask & 2) ? '\'' : ' ');
+ if (callRegMask & 4)
+ gcPrintf("EBX%c", (iregMask & 4) ? '\'' : ' ');
+ if (callRegMask & 8)
+ gcPrintf("EBP%c", (iregMask & 8) ? '\'' : ' ');
+ gcPrintf("]");
+
+ if (callPndTab)
+ {
+#if defined(_DEBUG) && !defined(STRIKE)
+ // note: _ASSERTE is a no-op for strike
+ PTR_CBYTE offsStart = table;
+#endif
+ gcPrintf(" argOffs(%d) =", callPndTabCnt);
+ for (unsigned i=0; i < callPndTabCnt; i++)
+ {
+ unsigned pndOffs;
+ table += decodeUnsigned(table, &pndOffs);
+ gcPrintf(" %4X", pndOffs);
+ }
+ _ASSERTE(offsStart + callPndTabSize == table);
+ bp = table;
+ }
+ else
+ {
+ if (callPndMask)
+ gcPrintf(" argMask=%02X", callPndMask);
+ if (iargMask)
+ gcPrintf(" (iargs=%02X)", iargMask);
+ }
+ gcPrintf("\n");
+
+ imask = lastSkip = 0;
+ break;
+
+ case 5:
+ //
+ // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC,
+ // ArgMask=MMM Delta=commonDelta[DD]
+ //
+ callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
+ val = *table++;
+ callPndMask = (val & 0x7);
+ callArgCnt = (val >> 3) & 0x7;
+ lastSkip = callCommonDelta[val>>6];
+ curOffs += lastSkip;
+
+ goto PRINT_CALL;
+
+ case 6:
+ //
+ // call 1110RRRR [ArgCnt] [ArgMask]
+ // Call ArgCnt,RegMask=RRR,ArgMask
+ //
+ callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
+ table += decodeUnsigned(table, &callArgCnt);
+ table += decodeUnsigned(table, &callPndMask);
+ goto PRINT_CALL;
+
+ case 7:
+ switch (val & 0x0C)
+ {
+ case 0x00:
+ assert(val == 0xF0);
+ /* iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask */
+ table += decodeUnsigned(table, &imask);
+ DumpEncoding(bp, table-bp); bp = table;
+ gcPrintf(" iptrMask = %02X\n", imask);
+ break;
+
+ case 0x04:
+ DumpEncoding(bp, table-bp); bp = table;
+ gcPrintf(" thisptr in %s\n", CalleeSavedRegName(val&0x3));
+ break;
+
+ case 0x08:
+ val = *table++;
+ callRegMask = val & 0xF;
+ imask = val >> 4;
+ lastSkip = *PTR_DWORD(table); table += sizeof(DWORD);
+ curOffs += lastSkip;
+ callArgCnt = *PTR_DWORD(table); table += sizeof(DWORD);
+ callPndTabCnt = *PTR_DWORD(table); table += sizeof(DWORD);
+ callPndTabSize = *PTR_DWORD(table); table += sizeof(DWORD);
+ callPndTab = true;
+ goto PRINT_CALL;
+
+ case 0x0C:
+ assert(val==0xff);
+ goto DONE_REGTAB;
+ break;
+
+ default:
+ _ASSERTE(!"reserved GC encoding");
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+DONE_REGTAB:
+
+ _ASSERTE(curOffs <= methodSize);
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEB);
+
+ _ASSERTE(table > bp);
+
+ DumpEncoding(bp, table-bp);
+// gcPrintf(" ");
+ gcPrintf("\n");
+
+ return (table - tableStart);
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
+/*****************************************************************************/
+
+void GCDump::DumpPtrsInFrame(PTR_CBYTE infoBlock,
+ PTR_CBYTE codeBlock,
+ unsigned offs,
+ bool verifyGCTables)
+{
+ PTR_CBYTE table = infoBlock;
+
+ size_t methodSize;
+ size_t stackSize;
+ size_t prologSize;
+ size_t epilogSize;
+ unsigned epilogCnt;
+ BOOL epilogEnd;
+ size_t argSize;
+ BOOL secCheck;
+ BOOL dblAlign;
+
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
+
+ /* Get hold of the method size */
+
+ unsigned int methodSizeTemp;
+ table += decodeUnsigned(table, &methodSizeTemp);
+ methodSize = methodSizeTemp;
+
+ //
+ // New style InfoBlk Header
+ //
+ // Typically only uses one-byte to store everything.
+ //
+ InfoHdr header;
+ table = decodeHeader(table, &header);
+
+ if (header.untrackedCnt == HAS_UNTRACKED)
+ {
+ unsigned count;
+ table += decodeUnsigned(table, &count);
+ header.untrackedCnt = count;
+ }
+ if (header.varPtrTableSize == HAS_VARPTR)
+ {
+ unsigned count;
+ table += decodeUnsigned(table, &count);
+ header.varPtrTableSize = count;
+ }
+ if (header.gsCookieOffset == HAS_GS_COOKIE_OFFSET)
+ {
+ unsigned offset;
+ table += decodeUnsigned(table, &offset);
+ header.gsCookieOffset = offset;
+ _ASSERTE(offset != INVALID_GS_COOKIE_OFFSET);
+ }
+ if (header.syncStartOffset == HAS_SYNC_OFFSET)
+ {
+ unsigned offset;
+ table += decodeUnsigned(table, &offset);
+ header.syncStartOffset = offset;
+ _ASSERTE(offset != INVALID_SYNC_OFFSET);
+ table += decodeUnsigned(table, &offset);
+ header.syncEndOffset = offset;
+ _ASSERTE(offset != INVALID_SYNC_OFFSET);
+ }
+
+ prologSize = header.prologSize;
+ epilogSize = header.epilogSize;
+ epilogCnt = header.epilogCount;
+ epilogEnd = header.epilogAtEnd;
+ secCheck = header.security;
+ dblAlign = header.doubleAlign;
+ argSize = header.argCount * 4;
+ stackSize = header.frameSize;
+
+#ifdef DEBUG
+ if (offs == 0)
+ {
+ gcPrintf(" method size = %04X\n", methodSize);
+ gcPrintf(" stack frame size = %3u \n", stackSize);
+ gcPrintf(" prolog size = %3u \n", prologSize);
+ gcPrintf(" epilog size = %3u \n", epilogSize);
+ gcPrintf(" epilog end = %s \n", epilogEnd ? "yes" : "no");
+ gcPrintf(" epilog count = %3u \n", epilogCnt );
+ gcPrintf(" security = %s \n", secCheck ? "yes" : "no");
+ gcPrintf(" dblAlign = %s \n", dblAlign ? "yes" : "no");
+ gcPrintf(" untracked count = %3u \n", header.untrackedCnt);
+ gcPrintf(" var ptr tab count= %3u \n", header.varPtrTableSize);
+ gcPrintf("\n");
+ }
+#endif
+
+ /* Are we within the prolog of the method? */
+
+ if (offs < prologSize)
+ {
+ gcPrintf(" Offset %04X is within the method's prolog\n", offs);
+ return;
+ }
+
+ /* Are we within an epilog of the method? */
+
+ if (epilogCnt)
+ {
+ unsigned eps;
+
+ if (epilogCnt > 1 || !epilogEnd)
+ {
+ if (verifyGCTables)
+ _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
+
+ unsigned prevEps = 0;
+ for (unsigned i = 0; i < epilogCnt; i++)
+ {
+ table += decodeUDelta(table, &eps, prevEps);
+
+ if ((offs >= eps) && (offs < eps + epilogSize))
+ goto EPILOG_MSG;
+ }
+ }
+ else
+ {
+ eps = (int)(methodSize - epilogSize);
+ if ((offs >= eps) && (offs < eps + epilogSize))
+ {
+EPILOG_MSG: gcPrintf(" Offset %04X is within the method's epilog"
+ " (%02X bytes into it)\n", offs, offs - eps);
+ return;
+ }
+ }
+ }
+ gcPrintf(" Offset %04X is within the method's body\n", offs);
+}
+
+/*****************************************************************************/
+#endif // _TARGET_X86_
+/*****************************************************************************/