diff options
Diffstat (limited to 'src/debug/daccess/nidump.cpp')
-rw-r--r-- | src/debug/daccess/nidump.cpp | 9579 |
1 files changed, 9579 insertions, 0 deletions
diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp new file mode 100644 index 0000000000..32eab498d3 --- /dev/null +++ b/src/debug/daccess/nidump.cpp @@ -0,0 +1,9579 @@ +// 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. + + +// + /*vim: set foldmethod=marker: */ +#include <stdafx.h> + +#if defined(FEATURE_PREJIT) +#include "nidump.h" + +#include <metadataexports.h> + +#include <comcallablewrapper.h> +#include <gcdump.h> + +#if !defined(FEATURE_CORESYSTEM) +#include <algorithm> +#endif + +#include <constrainedexecutionregion.h> + +#include <formattype.h> + +#include <pedecoder.h> + +#ifdef FEATURE_REMOTING +#include <crossdomaincalls.h> +#endif + +#include <mdfileformat.h> + +#if !defined(FEATURE_CORESYSTEM) +#include <cassert> +#undef _ASSERTE +#define _ASSERTE(x) assert(x) +#endif + +#include <compile.h> + +#ifdef USE_GC_INFO_DECODER +#include <gcinfodecoder.h> +#endif + +#include <ngenhash.inl> + +#define FEATURE_MSDIS + +//---------------------------------------------------------------------------- +// +// ClrDump functionality +// +//---------------------------------------------------------------------------- + +///////////////////////////////////////////////////////////////////////////////////////////// +// +// Given a compressed integer(*pData), expand the compressed int to *pDataOut. +// Return value is the number of bytes that the integer occupies in the compressed format +// It is caller's responsibility to ensure pDataOut has at least 4 bytes to be written to. +// +// This function returns -1 if pass in with an incorrectly compressed data, such as +// (*pBytes & 0xE0) == 0XE0. +///////////////////////////////////////////////////////////////////////////////////////////// +/* XXX Wed 09/14/2005 + * copied from cor.h. Modified to operate on PTR_PCCOR_SIGNATUREs + */ +inline ULONG DacSigUncompressBigData( + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + ULONG res; + + // 1 byte data is handled in DacSigUncompressData + // _ASSERTE(*pData & 0x80); + + // Medium. + if ((*pData & 0xC0) == 0x80) // 10?? ???? + { + res = (ULONG)((*pData++ & 0x3f) << 8); + res |= *pData++; + } + else // 110? ???? + { + res = (*pData++ & 0x1f) << 24; + res |= *pData++ << 16; + res |= *pData++ << 8; + res |= *pData++; + } + return res; +} +FORCEINLINE ULONG DacSigUncompressData( + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + // Handle smallest data inline. + if ((*pData & 0x80) == 0x00) // 0??? ???? + return *pData++; + return DacSigUncompressBigData(pData); +} +//const static mdToken g_tkCorEncodeToken[4] ={mdtTypeDef, mdtTypeRef, mdtTypeSpec, mdtBaseType}; + +// uncompress a token +inline mdToken DacSigUncompressToken( // return the token. + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + mdToken tk; + mdToken tkType; + + tk = DacSigUncompressData(pData); + tkType = g_tkCorEncodeToken[tk & 0x3]; + tk = TokenFromRid(tk >> 2, tkType); + return tk; +} +// uncompress encoded element type +FORCEINLINE CorElementType DacSigUncompressElementType(//Element type + PTR_CCOR_SIGNATURE &pData) // [IN,OUT] compressed data +{ + return (CorElementType)*pData++; +} + + + +const char * g_helperNames[] = +{ +#define JITHELPER(val, fn, sig) # val, +#include <jithelpers.h> +#undef JITHELPER +}; + + + +#define dim(x) (sizeof(x)/sizeof((x)[0])) + +void EnumFlagsToString( DWORD value, + const NativeImageDumper::EnumMnemonics * table, + int count, const WCHAR * sep, SString& output ) +{ + bool firstValue = true; + for( int i = 0; i < count; ++i ) + { + bool match = false; + const NativeImageDumper::EnumMnemonics& entry = table[i]; + if( entry.mask != 0 ) + match = ((entry.mask & value) == entry.value); + else + match = (entry.value == value); + + if( match ) + { + if( !firstValue ) + output.Append(sep); + firstValue = false; + + output.Append( table[i].mnemonic ); + + value &= ~entry.value; + } + } +} + +const NativeImageDumper::EnumMnemonics s_ImageSections[] = +{ +#define IS_ENTRY(v, s) NativeImageDumper::EnumMnemonics(v, s) + IS_ENTRY(IMAGE_SCN_MEM_READ, W("read")), + IS_ENTRY(IMAGE_SCN_MEM_WRITE, W("write")), + IS_ENTRY(IMAGE_SCN_MEM_EXECUTE, W("execute")), + IS_ENTRY(IMAGE_SCN_CNT_CODE, W("code")), + IS_ENTRY(IMAGE_SCN_CNT_INITIALIZED_DATA, W("init data")), + IS_ENTRY(IMAGE_SCN_CNT_UNINITIALIZED_DATA, W("uninit data")), +#undef IS_ENTRY +}; +inline int CheckFlags( DWORD source, DWORD flags ) +{ + return (source & flags) == flags; +} + +HRESULT ClrDataAccess::DumpNativeImage(CLRDATA_ADDRESS loadedBase, + LPCWSTR name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport *dis) +{ + DAC_ENTER(); + /* REVISIT_TODO Fri 09/09/2005 + * catch exceptions + */ + NativeImageDumper dump(dac_cast<PTR_VOID>(CLRDATA_ADDRESS_TO_TADDR(loadedBase)), name, display, + support, dis); + dump.DumpNativeImage(); + DAC_LEAVE(); + return S_OK; +} + + + +static ULONG bigBufferSize = 8192; +static WCHAR bigBuffer[8192]; +static BYTE bigByteBuffer[1024]; + +//---------------------------------------------------------------------------- +// +// NativeImageDumper +// +//---------------------------------------------------------------------------- +template<typename T> +inline T combine(T a, T b) +{ + return (T)(((DWORD)a) | ((DWORD)b)); +} +template<typename T> +inline T combine(T a, T b, T c) +{ + return (T)(((DWORD)a) | ((DWORD)b) | ((DWORD)c)); +} +#define CLRNATIVEIMAGE_ALWAYS ((CLRNativeImageDumpOptions)~0) +#define CHECK_OPT(opt) CheckOptions(CLRNATIVEIMAGE_ ## opt) +#define IF_OPT(opt) if( CHECK_OPT(opt) ) +#define IF_OPT_AND(opt1, opt2) if( CHECK_OPT(opt1) && CHECK_OPT(opt2) ) +#define IF_OPT_OR(opt1, opt2) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) ) +#define IF_OPT_OR3(opt1, opt2, opt3) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) ) +#define IF_OPT_OR4(opt1, opt2, opt3, opt4) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) || CHECK_OPT(opt4) ) +#define IF_OPT_OR5(opt1, opt2, opt3, opt4, opt5) if( CHECK_OPT(opt1) || CHECK_OPT(opt2) || CHECK_OPT(opt3) || CHECK_OPT(opt4) || CHECK_OPT(opt5) ) + +#define fieldsize(type, field) (sizeof(((type*)NULL)->field)) + + +/*{{{Display helpers*/ +#define DisplayStartCategory(name, filter)\ + do { IF_OPT(filter) m_display->StartCategory(name); } while(0) +#define DisplayEndCategory(filter)\ + do { IF_OPT(filter) m_display->EndCategory(); } while(0) +#define DisplayStartArray(name, fmt, filter) \ + do { IF_OPT(filter) m_display->StartArray(name, fmt); } while(0) +#define DisplayStartArrayWithOffset(field, fmt, type, filter) \ + do { IF_OPT(filter) m_display->StartArrayWithOffset( # field, offsetof(type, field), fieldsize(type, field), fmt); } while(0) + +#define DisplayStartElement( name, filter ) \ + do { IF_OPT(filter) m_display->StartElement( name ); } while(0) +#define DisplayStartStructure( name, ptr, size, filter ) \ + do { IF_OPT(filter) m_display->StartStructure( name, ptr, size ); } while(0) +#define DisplayStartList(fmt, filter) \ + do { IF_OPT(filter) m_display->StartList(fmt); } while(0) + +#define DisplayStartStructureWithOffset( field, ptr, size, type, filter ) \ + do { IF_OPT(filter) m_display->StartStructureWithOffset( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayStartVStructure( name, filter ) \ + do { IF_OPT(filter) m_display->StartVStructure( name ); } while(0) +#define DisplayEndVStructure( filter ) \ + do { IF_OPT(filter) m_display->EndVStructure(); } while(0) + +#define DisplayEndList(filter) \ + do { IF_OPT(filter) m_display->EndList(); } while(0) +#define DisplayEndArray(footer, filter) \ + do { IF_OPT(filter) m_display->EndArray(footer); } while(0) +#define DisplayEndStructure(filter) \ + do { IF_OPT(filter) m_display->EndStructure(); } while(0) + +#define DisplayEndElement(filter) \ + do { IF_OPT(filter) m_display->EndElement(); } while(0) + +#define DisplayWriteElementString(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementString(name, value); } while(0) +#define DisplayWriteElementStringW(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementStringW(name, value); } while(0) + +#define DisplayWriteElementStringW(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementStringW(name, value); } while(0) + +#define DisplayWriteElementInt(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementInt(name, value); } while(0) +#define DisplayWriteElementIntWithSuppress(name, value, defVal, filter) \ + do { IF_OPT(filter) m_display->WriteElementIntWithSuppress(name, value, defVal); } while(0) +#define DisplayWriteElementUInt(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementUInt(name, value); } while(0) +#define DisplayWriteElementFlag(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementFlag(name, value); } while(0) +#define DisplayWriteElementPointer(name, value, filter) \ + do { IF_OPT(filter) m_display->WriteElementPointer(name, value); } while(0) +#define DisplayWriteElementPointerAnnotated(name, value, annotation, filter) \ + do { IF_OPT(filter) m_display->WriteElementPointerAnnotated(name, value, annotation ); } while(0) +#define DisplayWriteEmptyElement(name, filter) \ + do { IF_OPT(filter) m_display->WriteEmptyElement(name); } while(0) + +#define DisplayWriteElementEnumerated(name, value, mnemonics, sep, filter) \ + do { \ + IF_OPT(filter) { \ + TempBuffer buf; \ + EnumFlagsToString(value, mnemonics, _countof(mnemonics), sep, buf);\ + m_display->WriteElementEnumerated( name, value, (const WCHAR*)buf ); \ + }\ + }while(0) +#define DisplayWriteFieldEnumerated(field, value, type, mnemonics, sep, filter)\ + do { \ + IF_OPT(filter) { \ + TempBuffer buf; \ + EnumFlagsToString(value, mnemonics, _countof(mnemonics), sep, buf);\ + m_display->WriteFieldEnumerated( # field, offsetof(type, field), \ + fieldsize(type, field), value,\ + (const WCHAR*)buf ); \ + }\ + }while(0) +#define DisplayWriteElementAddress(name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddress( name, ptr, size ); } while(0) +#define DisplayWriteElementAddressNamed(eltName, name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddressNamed( eltName, name, ptr, size ); } while(0) +#define DisplayWriteElementAddressNamedW(eltName, name, ptr, size, filter) \ + do { IF_OPT(filter) m_display->WriteElementAddressNamedW( eltName, name, ptr, size ); } while(0) + +#define DisplayWriteFieldString(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldString( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldStringW(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldStringW( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldInt(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldInt( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldUInt(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldUInt( # field, offsetof(type, field), fieldsize(type, field), value ); } while(0) +#define DisplayWriteFieldPointer(field, ptr, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointer( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define DisplayWriteFieldPointerWithSize(field, ptr, size, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointerWithSize( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayWriteFieldEmpty(field, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldEmpty( # field, offsetof(type, field), fieldsize(type, field) ); } while(0) +#define DisplayWriteFieldFlag(field, value, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldFlag(# field, offsetof(type, field), fieldsize(type, field), value); } while(0) +#define WriteFieldFieldDesc(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldFieldDesc( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldMethodDesc(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMethodDesc( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldStr(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldStr( ptr, # field, offsetof(type, field), fieldsize(type, field) ); } while(0) +#define WriteFieldMethodTable(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMethodTable( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldMDToken(field, token, type, filter) \ + do { IF_OPT(filter) DoWriteFieldMDToken( # field, offsetof(type, field), fieldsize(type, field), token ); } while(0) +#define WriteFieldAsHex(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldAsHex( # field, offsetof(type, field), fieldsize(type, field), ptr, fieldsize(type, field)); } while(0) +#define WriteFieldMDTokenImport(field, token, type, filter, import) \ + do { IF_OPT(filter) DoWriteFieldMDToken( # field, offsetof(type, field), fieldsize(type, field), token, import); } while(0) +#define WriteFieldTypeHandle(field, ptr, type, filter) \ + do { IF_OPT(filter) DoWriteFieldTypeHandle( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define WriteFieldCorElementType(field, et, type, filter) \ + do { IF_OPT(filter) DoWriteFieldCorElementType( # field, offsetof(type, field), fieldsize(type, field), et ); } while(0) +#define DumpFieldStub(field, ptr, type, filter) \ + do { IF_OPT(filter) DoDumpFieldStub( ptr, offsetof(type, field), fieldsize(type, field), # field ); } while(0) +#define DumpComPlusCallInfo(compluscall, filter) \ + do { IF_OPT(filter) DoDumpComPlusCallInfo( compluscall ); } while(0) +#define DisplayWriteFieldPointerAnnotated(field, ptr, annotation, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldPointer( # field, offsetof(type, field), fieldsize(type, field), ptr ); } while(0) +#define DisplayWriteFieldAddress(field, ptr, size, type, filter) \ + do { IF_OPT(filter) m_display->WriteFieldAddress( # field, offsetof(type, field), fieldsize(type, field), ptr, size ); } while(0) +#define DisplayStartTextElement(name, filter) \ + do { IF_OPT(filter) m_display->StartTextElement( name ); } while(0) +#define DisplayEndTextElement(filter) \ + do { IF_OPT(filter) m_display->EndTextElement(); } while(0) +#define DisplayWriteXmlText( args, filter ) \ + do { IF_OPT(filter) m_display->WriteXmlText args; } while(0) +#define DisplayWriteXmlTextBlock( args, filter ) \ + do { IF_OPT(filter) m_display->WriteXmlTextBlock args; } while(0) + +#define CoverageRead( ptr, size ) \ + do { IF_OPT(DEBUG_COVERAGE) PTR_READ(TO_TADDR(ptr), size); } while(0) +#define CoverageReadString( taddr ) \ + do { PTR_BYTE ptr(TO_TADDR(taddr)); while( *ptr++ ); }while(0) + +void AppendNilToken( mdToken token, SString& buf ) +{ + _ASSERTE(RidFromToken(token) == mdTokenNil); + + const WCHAR * id = NULL; + switch(token) + { +#define mdNilEnt(x) case x: \ + id = W(#x); \ + break + mdNilEnt(mdModuleNil); + mdNilEnt(mdTypeRefNil); + mdNilEnt(mdTypeDefNil); + mdNilEnt(mdFieldDefNil); + mdNilEnt(mdMethodDefNil); + mdNilEnt(mdParamDefNil); + mdNilEnt(mdInterfaceImplNil); + mdNilEnt(mdMemberRefNil); + mdNilEnt(mdCustomAttributeNil); + mdNilEnt(mdPermissionNil); + mdNilEnt(mdSignatureNil); + mdNilEnt(mdEventNil); + mdNilEnt(mdPropertyNil); + mdNilEnt(mdModuleRefNil); + mdNilEnt(mdTypeSpecNil); + mdNilEnt(mdAssemblyNil); + mdNilEnt(mdAssemblyRefNil); + mdNilEnt(mdFileNil); + mdNilEnt(mdExportedTypeNil); + mdNilEnt(mdManifestResourceNil); + + mdNilEnt(mdGenericParamNil); + mdNilEnt(mdGenericParamConstraintNil); + mdNilEnt(mdMethodSpecNil); + + mdNilEnt(mdStringNil); +#undef mdNilEnt + } + buf.Append( id ); +} +void appendByteArray(SString& buf, const BYTE * bytes, ULONG cbBytes) +{ + for( COUNT_T i = 0; i < cbBytes; ++i ) + { + buf.AppendPrintf(W("%02x"), bytes[i]); + } +} +/*}}}*/ + + + + +struct OptionDependencies +{ + OptionDependencies(CLRNativeImageDumpOptions value, + CLRNativeImageDumpOptions dep) : m_value(value), + m_dep(dep) + { + + } + CLRNativeImageDumpOptions m_value; + CLRNativeImageDumpOptions m_dep; +}; + +static OptionDependencies g_dependencies[] = +{ +#define OPT_DEP(value, dep) OptionDependencies(CLRNATIVEIMAGE_ ## value,\ + CLRNATIVEIMAGE_ ## dep) + OPT_DEP(RESOURCES, COR_INFO), + OPT_DEP(METADATA, COR_INFO), + OPT_DEP(PRECODES, MODULE), + //Does methoddescs require ModuleTables? + OPT_DEP(VERBOSE_TYPES, METHODDESCS), + OPT_DEP(GC_INFO, METHODS), + OPT_DEP(FROZEN_SEGMENT, MODULE), + OPT_DEP(SLIM_MODULE_TBLS, MODULE), + OPT_DEP(MODULE_TABLES, SLIM_MODULE_TBLS), + OPT_DEP(DISASSEMBLE_CODE, METHODS), + + OPT_DEP(FIXUP_HISTOGRAM, FIXUP_TABLES), + OPT_DEP(FIXUP_THUNKS, FIXUP_TABLES), + +#undef OPT_DEP +}; + +// Metadata helpers for DAC +// This is mostly copied from mscoree.cpp which isn't available in mscordacwks.dll. +// + +// This function gets the Dispenser interface given the CLSID and REFIID. +STDAPI MetaDataGetDispenser( + REFCLSID rclsid, // The class to desired. + REFIID riid, // Interface wanted on class factory. + LPVOID FAR * ppv) // Return interface pointer here. +{ + _ASSERTE(rclsid == CLSID_CorMetaDataDispenser); + + return InternalCreateMetaDataDispenser(riid, ppv); +} + + +NativeImageDumper::NativeImageDumper(PTR_VOID loadedBase, + const WCHAR * const name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport *dis) + : + m_decoder(loadedBase), + m_name(name), + m_baseAddress(loadedBase), + m_display(display), + m_librarySupport(support), + m_import(NULL), + m_assemblyImport(NULL), + m_manifestAssemblyImport(NULL), + m_dependencies(NULL), + m_imports(NULL), + m_dis(dis), + m_MetadataSize(0), + m_ILHostCopy(NULL), + m_isMscorlibHardBound(false), + m_sectionAlignment(0) +{ + IfFailThrow(m_display->GetDumpOptions(&m_dumpOptions)); + + //set up mscorwks stuff. + m_mscorwksBase = DacGlobalBase(); + _ASSERTE(m_mscorwksBase); + PEDecoder mscorwksDecoder(dac_cast<PTR_VOID>(m_mscorwksBase)); + m_mscorwksSize = mscorwksDecoder.GetSize(); + m_mscorwksPreferred = TO_TADDR(mscorwksDecoder.GetPreferredBase()); + //add implied options (i.e. if you want to dump the module, you also have + //to dump the native info. + CLRNativeImageDumpOptions current; + do + { + current = m_dumpOptions; + for( unsigned i = 0; i < _countof(g_dependencies); ++i ) + { + if( m_dumpOptions & g_dependencies[i].m_value ) + m_dumpOptions |= g_dependencies[i].m_dep; + } + }while( current != m_dumpOptions ); + IF_OPT(DISASSEMBLE_CODE) + { + //configure the disassembler + m_dis->SetTranslateAddrCallback(TranslateAddressCallback); + m_dis->SetTranslateFixupCallback(TranslateFixupCallback); + m_dis->PvClientSet(this); + } +} + +void GuidToString( GUID& guid, SString& s ) +{ + WCHAR guidString[64]; + GuidToLPWSTR(guid, guidString, sizeof(guidString) / sizeof(WCHAR)); + //prune the { and } + _ASSERTE(guidString[0] == W('{') + && guidString[wcslen(guidString) - 1] == W('}')); + guidString[wcslen(guidString) - 1] = W('\0'); + s.Append( guidString + 1 ); +} + +NativeImageDumper::~NativeImageDumper() +{ +} + +inline const void * ptr_add(const void * ptr, COUNT_T size) +{ + return reinterpret_cast<const BYTE *>(ptr) + size; +} + +//This does pointer arithmetic on a DPtr. +template<typename T> +inline const DPTR(T) dptr_add(T* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) + (offset * sizeof(T))); +} + +template<typename T> +inline const DPTR(T) dptr_sub(T* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) - (offset * sizeof(T))); +} +template<typename T> +inline const DPTR(T) dptr_sub(DPTR(T)* ptr, COUNT_T offset) +{ + return DPTR(T)(PTR_HOST_TO_TADDR(ptr) - (offset * sizeof(T))); +} + +struct MDTableType +{ + MDTableType(unsigned t, const char * n) : m_token(t), m_name(n) { } + unsigned m_token; + const char * m_name; +}; + +static unsigned s_tableTypes[] = +{ + /* +#ifdef MiniMdTable +#undef MiniMdTable +#endif +#define MiniMdTable(x) TBL_##x << 24, + MiniMdTables() +#undef MiniMdTable + mdtName + */ + mdtModule, + mdtTypeRef, + mdtTypeDef, + mdtFieldDef, + mdtMethodDef, + mdtParamDef, + mdtInterfaceImpl, + mdtMemberRef, + mdtCustomAttribute, + mdtPermission, + mdtSignature, + mdtEvent, + mdtProperty, + mdtModuleRef, + mdtTypeSpec, + mdtAssembly, + mdtAssemblyRef, + mdtFile, + mdtExportedType, + mdtManifestResource, + mdtGenericParam, + mdtMethodSpec, + mdtGenericParamConstraint, +}; + +const NativeImageDumper::EnumMnemonics s_CorHdrFlags[] = +{ +#define CHF_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, v) + CHF_ENTRY(COMIMAGE_FLAGS_ILONLY, W("IL Only")), + CHF_ENTRY(COMIMAGE_FLAGS_32BITREQUIRED, W("32-bit Required")), + CHF_ENTRY(COMIMAGE_FLAGS_IL_LIBRARY, W("IL Library")), + CHF_ENTRY(COMIMAGE_FLAGS_STRONGNAMESIGNED, W("Strong Name Signed")), + CHF_ENTRY(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT, W("Has Native Entrypoint")), + CHF_ENTRY(COMIMAGE_FLAGS_TRACKDEBUGDATA, W("Track Debug Data")), + CHF_ENTRY(COMIMAGE_FLAGS_32BITPREFERRED, W("32-bit Preferred")) +#undef CHF_ENTRY +}; + +void NativeImageDumper::DumpAssemblySignature(CORCOMPILE_ASSEMBLY_SIGNATURE & assemblySignature) +{ + { + TempBuffer buf; + GuidToString(assemblySignature.mvid, buf); + DisplayWriteFieldStringW( mvid, (const WCHAR*)buf, + CORCOMPILE_ASSEMBLY_SIGNATURE, + COR_INFO ); + } + DisplayWriteFieldInt( timeStamp, assemblySignature.timeStamp, + CORCOMPILE_ASSEMBLY_SIGNATURE, COR_INFO ); + DisplayWriteFieldInt( ilImageSize, + assemblySignature.ilImageSize, + CORCOMPILE_ASSEMBLY_SIGNATURE, COR_INFO ); +} + +#ifndef FEATURE_CORECLR + +const NativeImageDumper::EnumMnemonics s_CorCompileDependencyInfoFlags[] = +{ +#define CMDI_ENTRY(f) NativeImageDumper::EnumMnemonics(CORCOMPILE_DEPENDENCY_ ## f, W(#f)) + +#ifdef FEATURE_APTCA + CMDI_ENTRY(IS_APTCA), + CMDI_ENTRY(IS_CAPTCA), +#endif //FEATURE_APTCA +#undef CMDI_ENTRY +}; + +#endif //!FEATURE_CORECLR + +//error code return? +void +NativeImageDumper::DumpNativeImage() +{ + COUNT_T size; + const void *data; + + m_display->StartDocument(); + + DisplayStartCategory( "File", PE_INFO ); + DisplayWriteElementStringW( "path", m_name, PE_INFO ); + + DisplayWriteElementInt( "diskSize", m_decoder.GetSize(), PE_INFO ); + _ASSERTE(sizeof(IMAGE_DOS_HEADER) < m_decoder.GetSize()); + + PTR_IMAGE_DOS_HEADER dosHeader = + PTR_IMAGE_DOS_HEADER(dac_cast<TADDR>(m_baseAddress)); + DisplayWriteElementAddress( "IMAGE_DOS_HEADER", + DPtrToPreferredAddr(dosHeader), + sizeof(*dosHeader), PE_INFO ); + + // NT headers + + if (!m_decoder.HasNTHeaders()) + { + IF_OPT(PE_INFO) + { + DisplayWriteElementString("isPEFile", "false", PE_INFO); + DisplayEndCategory(PE_INFO); + } + else + m_display->ErrorPrintF("Non-PE file"); + + m_display->EndDocument(); + return; + } + + CONSISTENCY_CHECK(m_decoder.CheckNTHeaders()); + if (!m_decoder.CheckNTHeaders()) + { + m_display->ErrorPrintF("*** NT headers are not valid ***"); + return; + } + + DisplayWriteElementString("imageType", m_decoder.Has32BitNTHeaders() + ? "32 bit image" : "64 bit image", PE_INFO); + DisplayWriteElementAddress("address", (SIZE_T)m_decoder.GetNativePreferredBase(), + m_decoder.GetVirtualSize(), PE_INFO); + DisplayWriteElementInt( "TimeDateStamp", m_decoder.GetTimeDateStamp(), + PE_INFO ); + + if( m_decoder.Has32BitNTHeaders() ) + { + PTR_IMAGE_NT_HEADERS32 ntHeaders(m_decoder.GetNTHeaders32()); + //base, size, sectionAlign + _ASSERTE(ntHeaders->OptionalHeader.SectionAlignment >= + ntHeaders->OptionalHeader.FileAlignment); + m_imageSize = ntHeaders->OptionalHeader.SizeOfImage; + m_display->NativeImageDimensions(PTR_TO_TADDR(m_decoder.GetBase()), + ntHeaders->OptionalHeader.SizeOfImage, + ntHeaders->OptionalHeader.SectionAlignment); + /* REVISIT_TODO Mon 11/21/2005 + * I don't understand this. Sections start on a two page boundary, but + * data ends on a one page boundary. What's up with that? + */ + m_sectionAlignment = PAGE_SIZE; //ntHeaders->OptionalHeader.SectionAlignment; + unsigned ntHeaderSize = sizeof(*ntHeaders) + - sizeof(ntHeaders->OptionalHeader) + + ntHeaders->FileHeader.SizeOfOptionalHeader; + DisplayWriteElementAddress( "IMAGE_NT_HEADERS32", + DPtrToPreferredAddr(ntHeaders), + ntHeaderSize, PE_INFO ); + + } + else + { + PTR_IMAGE_NT_HEADERS64 ntHeaders(m_decoder.GetNTHeaders64()); + //base, size, sectionAlign + _ASSERTE(ntHeaders->OptionalHeader.SectionAlignment >= + ntHeaders->OptionalHeader.FileAlignment); + m_imageSize = ntHeaders->OptionalHeader.SizeOfImage; + m_display->NativeImageDimensions((SIZE_T)ntHeaders->OptionalHeader.ImageBase, + ntHeaders->OptionalHeader.SizeOfImage, + ntHeaders->OptionalHeader.SectionAlignment); + m_sectionAlignment = ntHeaders->OptionalHeader.SectionAlignment; + unsigned ntHeaderSize = sizeof(*ntHeaders) + - sizeof(ntHeaders->OptionalHeader) + + ntHeaders->FileHeader.SizeOfOptionalHeader; + DisplayWriteElementAddress( "IMAGE_NT_HEADERS64", + DPtrToPreferredAddr(ntHeaders), + ntHeaderSize, PE_INFO ); + } + DisplayEndCategory(PE_INFO); + + // PE Section info + + DisplayStartArray("Sections", W("%-8s%s\t(disk %s) %s"), PE_INFO); + + for (COUNT_T i = 0; i < m_decoder.GetNumberOfSections(); i++) + { + PTR_IMAGE_SECTION_HEADER section = dptr_add(m_decoder.FindFirstSection(), i); + m_display->Section(reinterpret_cast<char *>(section->Name), + section->VirtualAddress, + section->SizeOfRawData); + DisplayStartStructure( "Section", DPtrToPreferredAddr(section), + sizeof(*section), PE_INFO ); + DisplayWriteElementString("name", (const char *)section->Name, PE_INFO); + DisplayWriteElementAddress( "address", RvaToDisplay(section->VirtualAddress), + section->Misc.VirtualSize, PE_INFO ); + DisplayWriteElementAddress( "disk", section->PointerToRawData, + section->SizeOfRawData, PE_INFO ); + + DisplayWriteElementEnumerated( "access", section->Characteristics, + s_ImageSections, W(", "), PE_INFO ); + DisplayEndStructure( PE_INFO ); //Section + } + DisplayEndArray("Total Sections", PE_INFO); + + // Image directory info + + DisplayStartArray( "Directories", W("%-40s%s"), PE_INFO ); + + for ( COUNT_T i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) + { + static const char *directoryNames[] = + { + /* 0*/"IMAGE_DIRECTORY_ENTRY_EXPORT", + /* 1*/"IMAGE_DIRECTORY_ENTRY_IMPORT", + /* 2*/"IMAGE_DIRECTORY_ENTRY_RESOURCE", + /* 3*/"IMAGE_DIRECTORY_ENTRY_EXCEPTION", + /* 4*/"IMAGE_DIRECTORY_ENTRY_SECURITY", + /* 5*/"IMAGE_DIRECTORY_ENTRY_BASERELOC", + /* 6*/"IMAGE_DIRECTORY_ENTRY_DEBUG", + /* 7*/"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE", + /* 8*/"IMAGE_DIRECTORY_ENTRY_GLOBALPTR", + /* 9*/"IMAGE_DIRECTORY_ENTRY_TLS", + /*10*/"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", + /*11*/"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", + /*12*/"IMAGE_DIRECTORY_ENTRY_IAT", + /*13*/"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", + /*14*/"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", + /* 2*/"", + }; + + IMAGE_DATA_DIRECTORY *entry = m_decoder.GetDirectoryEntry(i); + + if (entry->VirtualAddress != 0) + { + DisplayStartElement("Directory", PE_INFO); + DisplayWriteElementString("name", directoryNames[i], PE_INFO); + DisplayWriteElementAddress("address", + RvaToDisplay(entry->VirtualAddress), + entry->Size, PE_INFO); + + DisplayEndElement( PE_INFO ); //Directory + } + } + DisplayEndArray("Total Directories", PE_INFO); //Directories + + // COM+ info + + if (!m_decoder.HasCorHeader()) + { + IF_OPT(COR_INFO) + DisplayWriteElementString("CLRInfo", "<none>", COR_INFO); + else + m_display->ErrorPrintF("Non-CLR image\n"); + + m_display->EndDocument(); + return; + } + + CONSISTENCY_CHECK(m_decoder.CheckCorHeader()); + if (!m_decoder.CheckCorHeader()) + { + m_display->ErrorPrintF("*** INVALID CLR Header ***"); + m_display->EndDocument(); + return; + } + + DisplayStartCategory("CLRInfo", COR_INFO); + PTR_IMAGE_COR20_HEADER pCor(m_decoder.GetCorHeader()); + { +#define WRITE_COR20_FIELD( name ) m_display->WriteFieldAddress( \ + # name, offsetof(IMAGE_COR20_HEADER, name), \ + fieldsize(IMAGE_COR20_HEADER, name), \ + RvaToDisplay( pCor-> name . VirtualAddress ), \ + pCor-> name . Size ) + + m_display->StartStructure( "IMAGE_COR20_HEADER", + DPtrToPreferredAddr(pCor), + sizeof(*pCor) ); + + DisplayWriteFieldUInt( MajorRuntimeVersion, pCor->MajorRuntimeVersion, IMAGE_COR20_HEADER, COR_INFO ); + DisplayWriteFieldUInt( MinorRuntimeVersion, pCor->MinorRuntimeVersion, IMAGE_COR20_HEADER, COR_INFO ); + + // Symbol table and startup information + WRITE_COR20_FIELD(MetaData); + DisplayWriteFieldEnumerated( Flags, pCor->Flags, IMAGE_COR20_HEADER, s_CorHdrFlags, W(", "), COR_INFO ); + DisplayWriteFieldUInt( EntryPointToken, pCor->EntryPointToken, IMAGE_COR20_HEADER, COR_INFO ); + + // Binding information + WRITE_COR20_FIELD(Resources); + WRITE_COR20_FIELD(StrongNameSignature); + + // Regular fixup and binding information + WRITE_COR20_FIELD(CodeManagerTable); + WRITE_COR20_FIELD(VTableFixups); + WRITE_COR20_FIELD(ExportAddressTableJumps); + + // Precompiled image info + WRITE_COR20_FIELD(ManagedNativeHeader); + + m_display->EndStructure(); //IMAGE_COR20_HEADER +#undef WRITE_COR20_FIELD + } + + //make sure to touch the strong name signature even if we won't print it. + if (m_decoder.HasStrongNameSignature()) + { + if (m_decoder.IsStrongNameSigned()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetStrongNameSignature(&size))); + + IF_OPT(COR_INFO) + { + TempBuffer sig; + + appendByteArray(sig, (BYTE*)data, size); + + DisplayWriteElementStringW( "StrongName", (const WCHAR *)sig, + COR_INFO ); + } + } + else + { + DisplayWriteEmptyElement("DelaySigned", COR_INFO); + } + } + +#ifdef FEATURE_READYTORUN + if (m_decoder.HasReadyToRunHeader()) + DisplayWriteElementString( "imageType", "ReadyToRun image", COR_INFO); + else +#endif + if (m_decoder.IsILOnly()) + DisplayWriteElementString( "imageType", "IL only image", COR_INFO); + else + if (m_decoder.HasNativeHeader()) + DisplayWriteElementString( "imageType", "Native image", COR_INFO); + else + DisplayWriteElementString( "imageType", "Mixed image", COR_INFO); + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetMetadata(&size))); + OpenMetadata(); + IF_OPT(METADATA) + { + DWORD dwAssemblyFlags = 0; + IfFailThrow(m_manifestAssemblyImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, &dwAssemblyFlags)); + if ((afContentType_WindowsRuntime & dwAssemblyFlags) == afContentType_WindowsRuntime) + { + // The WinMD adapter doesn't implement the IID_IMetaDataTables interface so we can't dump + // the raw metadata. + DisplayWriteElementString ("Metadata", "Not supported by WinRT", COR_INFO); + } + else + { + WriteElementsMetadata( "Metadata", TO_TADDR(data), size ); + } + } + + CoverageRead(TO_TADDR(data), size); + + if (m_decoder.HasNativeHeader()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetNativeManifestMetadata(&size))); + + IF_OPT(METADATA) + { + WriteElementsMetadata( "NativeManifestMetadata", TO_TADDR(data), size ); + } + else + { + DisplayWriteElementAddress( "NativeManifestMetadata", + DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + } + + + /* REVISIT_TODO Tue 09/20/2005 + * Anything to display in the native metadata? + */ + CoverageRead(TO_TADDR(data), size); + + /* REVISIT_TODO Tue 09/20/2005 + * Dump the debug map? Indexed by method RID... probably a good idea + */ + data = reinterpret_cast<void*>(m_decoder.GetNativeDebugMap(&size)); + + DisplayWriteElementAddress( "debugMap", DataPtrToDisplay((TADDR)data), size, + COR_INFO); + CoverageRead(TO_TADDR(data), size); + + //also read the entire debug map section + IMAGE_SECTION_HEADER * dbgmap = FindSection( ".dbgmap" ); + if (dbgmap != NULL) + { + CoverageRead(TO_TADDR(dbgmap->VirtualAddress) + + PTR_TO_TADDR(m_decoder.GetBase()), + (ULONG32)ALIGN_UP(dbgmap->Misc.VirtualSize, GetSectionAlignment())); + } + + //read the .il and .rsrc sections in their entirety + IF_OPT(DEBUG_COVERAGE) + { + IMAGE_SECTION_HEADER *hdr; + hdr = FindSection( ".rsrc" ); + if( hdr != NULL ) + { + CoverageRead( m_decoder.GetRvaData(hdr->VirtualAddress), + (ULONG32)hdr->Misc.VirtualSize ); + } + } + IF_OPT_OR(DEBUG_COVERAGE, IL) + { + IMAGE_SECTION_HEADER *hdr = FindSection( ".text" ); + + if( hdr != NULL ) + { + m_ILSectionStart = hdr->VirtualAddress; + m_ILHostCopy = (BYTE*)PTR_READ(m_decoder.GetRvaData(hdr->VirtualAddress), hdr->Misc.VirtualSize); +#ifdef _DEBUG + m_ILSectionSize = hdr->Misc.VirtualSize; +#endif + } + else + { + m_ILSectionStart = 0; + m_ILHostCopy = NULL; +#ifdef _DEBUG + m_ILSectionSize = 0; +#endif + } + _ASSERTE( (((TADDR)m_ILHostCopy) & 3) == 0 ); + _ASSERTE((m_ILSectionStart & 3) == 0); + + } + } + + data = m_decoder.GetResources(&size); + IF_OPT(RESOURCES) + { + DisplayStartStructure( "resource", DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + DisplayStartArray( "Resources", NULL, COR_INFO ); + HCORENUM hEnum = NULL; + for(;;) + { + mdManifestResource resTokens[1]; + ULONG numTokens = 0; + IfFailThrow(m_assemblyImport->EnumManifestResources(&hEnum, + resTokens, + 1, + &numTokens)); + if( numTokens == 0 ) + break; + + WCHAR resourceName[256]; + ULONG nameLen; + mdToken impl; + DWORD offset, flags; + IfFailThrow(m_assemblyImport->GetManifestResourceProps(resTokens[0], + resourceName, + _countof(resourceName), + &nameLen, + &impl, + &offset, + &flags)); + if( RidFromToken(impl) != 0 ) + continue; //skip all non-zero providers + resourceName[nameLen] = W('\0'); + DPTR(DWORD UNALIGNED) res(TO_TADDR(data) + offset); + DWORD resSize = *res; + DisplayWriteElementAddressNamedW( "Resource", resourceName, + DPtrToPreferredAddr(res), + resSize + sizeof(DWORD), + RESOURCES ); + } + DisplayEndArray( "Total Resources", COR_INFO ); //Resources + DisplayEndStructure( COR_INFO ); //resource + } + else + { + DisplayWriteElementAddress( "resource", DataPtrToDisplay((TADDR)data), size, + COR_INFO ); + } + + ULONG resultSize; + GUID mvid; + m_manifestImport->GetScopeProps(bigBuffer, bigBufferSize, &resultSize, &mvid); + /* REVISIT_TODO Wed 09/07/2005 + * The name is the .module entry. Why isn't it present in the ngen image? + */ + TempBuffer guidString; + GuidToString( mvid, guidString ); + if( wcslen(bigBuffer) ) + DisplayWriteElementStringW( "scopeName", bigBuffer, COR_INFO ); + DisplayWriteElementStringW( "mvid", (const WCHAR *)guidString, COR_INFO ); + + if (m_decoder.HasManagedEntryPoint()) + { + DisplayStartVStructure( "ManagedEntryPoint", COR_INFO ); + unsigned token = m_decoder.GetEntryPointToken(); + DisplayWriteElementUInt( "Token", token, COR_INFO ); + TempBuffer buf; + AppendTokenName( token, buf ); + DisplayWriteElementStringW( "TokenName", (const WCHAR *)buf, COR_INFO ); + DisplayEndVStructure( COR_INFO ); + } + else if (m_decoder.HasNativeEntryPoint()) + { + DisplayWriteElementPointer( "NativeEntryPoint", (SIZE_T)m_decoder.GetNativeEntryPoint(), + COR_INFO ); + } + + /* REVISIT_TODO Mon 11/21/2005 + * Dump the version info completely + */ + if( m_decoder.HasNativeHeader() ) + { + PTR_CORCOMPILE_VERSION_INFO versionInfo( m_decoder.GetNativeVersionInfo() ); + + DisplayStartStructure("CORCOMPILE_VERSION_INFO", + DPtrToPreferredAddr(versionInfo), + sizeof(*versionInfo), COR_INFO); + + DisplayStartStructureWithOffset( sourceAssembly, + DPtrToPreferredAddr(versionInfo) + offsetof(CORCOMPILE_VERSION_INFO, sourceAssembly), + sizeof(versionInfo->sourceAssembly), + CORCOMPILE_VERSION_INFO, COR_INFO ); + DumpAssemblySignature(versionInfo->sourceAssembly); + DisplayEndStructure(COR_INFO); //sourceAssembly + + COUNT_T numDeps; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&numDeps))); + + DisplayStartArray( "Dependencies", NULL, COR_INFO ); + + for( COUNT_T i = 0; i < numDeps; ++i ) + { + DisplayStartStructure("CORCOMPILE_DEPENDENCY", DPtrToPreferredAddr(deps + i), + sizeof(deps[i]), COR_INFO ); + WriteFieldMDTokenImport( dwAssemblyRef, deps[i].dwAssemblyRef, + CORCOMPILE_DEPENDENCY, COR_INFO, + m_manifestImport ); + WriteFieldMDTokenImport( dwAssemblyDef, deps[i].dwAssemblyDef, + CORCOMPILE_DEPENDENCY, COR_INFO, + m_manifestImport ); +#ifndef FEATURE_CORECLR + DisplayWriteFieldEnumerated( dependencyInfo, deps[i].dependencyInfo, + CORCOMPILE_DEPENDENCY, + s_CorCompileDependencyInfoFlags, W(", "), + COR_INFO ); +#endif // !FEATURE_CORECLR + DisplayStartStructureWithOffset( signAssemblyDef, + DPtrToPreferredAddr(deps + i) + offsetof(CORCOMPILE_DEPENDENCY, signAssemblyDef), + sizeof(deps[i]).signAssemblyDef, + CORCOMPILE_DEPENDENCY, COR_INFO ); + DumpAssemblySignature(deps[i].signAssemblyDef); + DisplayEndStructure(COR_INFO); //signAssemblyDef + + { + TempBuffer buf; + if( deps[i].signNativeImage == INVALID_NGEN_SIGNATURE ) + { + buf.Append( W("INVALID_NGEN_SIGNATURE") ); + } + else + { + GuidToString(deps[i].signNativeImage, buf); + } + DisplayWriteFieldStringW( signNativeImage, (const WCHAR*)buf, + CORCOMPILE_DEPENDENCY, COR_INFO ); +#if 0 + if( m_librarySupport + && deps[i].signNativeImage != INVALID_NGEN_SIGNATURE ) + { + buf.Clear(); + AppendTokenName(deps[i].dwAssemblyRef, buf, m_import ); + IfFailThrow(m_librarySupport->LoadDependency( (const WCHAR*)buf, + deps[i].signNativeImage )); + } +#endif + + } + + + DisplayEndStructure(COR_INFO); //CORCOMPILE_DEPENDENCY + } + DisplayEndArray( "Total Dependencies", COR_INFO ); + DisplayEndStructure(COR_INFO); //CORCOMPILE_VERSION_INFO + + //Now load all dependencies and imports. There may be more + //dependencies than imports, so make sure to get them all. + for( COUNT_T i = 0; i < m_decoder.GetNativeImportTableCount(); ++i ) + { + NativeImageDumper::Import * import = OpenImport(i); + TraceDumpImport( i, import ); + } + NativeImageDumper::Dependency * traceDependency = OpenDependency(0); + TraceDumpDependency( 0, traceDependency ); + + for( COUNT_T i = 0; i < numDeps; ++i ) + { + traceDependency = OpenDependency( i + 1 ); + TraceDumpDependency( i + 1, traceDependency ); + } + _ASSERTE(m_dependencies[0].pModule != NULL); + + /* XXX Wed 12/14/2005 + * Now for the real insanity. I need to initialize static classes in + * the DAC. First I need to find mscorlib's dependency entry. Search + * through all of the dependencies to find the one marked as + * fIsMscorlib. If I don't find anything marked that way, then "self" + * is mscorlib. + */ + Dependency * mscorlib = NULL; + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].fIsMscorlib ) + { + mscorlib = &m_dependencies[i]; + break; + } + } + + //If we're actually dumping mscorlib, remap the mscorlib dependency to our own native image. + if( (mscorlib == NULL) || !wcscmp(m_name, CoreLibName_W)) + { + mscorlib = GetDependency(0); + mscorlib->fIsMscorlib = TRUE; + _ASSERTE(mscorlib->fIsHardbound); + } + if( mscorlib->fIsHardbound ) + { + m_isMscorlibHardBound = true; + } + + _ASSERTE(mscorlib != NULL); + if( m_isMscorlibHardBound ) + { + //go through the module to the binder. + PTR_Module mscorlibModule = mscorlib->pModule; + + PTR_MscorlibBinder binder = mscorlibModule->m_pBinder; + g_Mscorlib = *binder; + + PTR_MethodTable mt = MscorlibBinder::GetExistingClass(CLASS__OBJECT); + g_pObjectClass = mt; + } + + + if (g_pObjectClass == NULL) + { + //if mscorlib is not hard bound, then warn the user (many features of nidump are shut off) + m_display->ErrorPrintF( "Assembly %S is soft bound to mscorlib. nidump cannot dump MethodTables completely.\n", m_name ); + // TritonTODO: reason? + // reset "hard bound state" + m_isMscorlibHardBound = false; + + } + } + + + // @todo: VTable Fixups + + // @todo: EAT Jumps + + DisplayEndCategory(COR_INFO); //CLRInfo + +#ifdef FEATURE_READYTORUN + if (m_decoder.HasReadyToRunHeader()) + { + DumpReadyToRun(); + } + else +#endif + if (m_decoder.HasNativeHeader()) + { + DumpNative(); + } + + m_display->EndDocument(); +} + +void NativeImageDumper::DumpNative() +{ + DisplayStartCategory("NativeInfo", NATIVE_INFO); + + CONSISTENCY_CHECK(m_decoder.CheckNativeHeader()); + if (!m_decoder.CheckNativeHeader()) + { + m_display->ErrorPrintF("*** INVALID NATIVE HEADER ***\n"); + return; + } + + IF_OPT(NATIVE_INFO) + DumpNativeHeader(); + + //host pointer + CORCOMPILE_EE_INFO_TABLE * infoTable = m_decoder.GetNativeEEInfoTable(); + + DisplayStartStructure( "CORCOMPILE_EE_INFO_TABLE", + DataPtrToDisplay(PTR_HOST_TO_TADDR(infoTable)), + sizeof(*infoTable), NATIVE_INFO ); + + /* REVISIT_TODO Mon 09/26/2005 + * Move this further down to include the dumping of the module, and + * other things. + */ + DisplayEndStructure(NATIVE_INFO); //NativeInfoTable + DisplayEndCategory(NATIVE_INFO); //NativeInfo +#if LATER + //come back here and dump all the fields of the CORCOMPILE_EE_INFO_TABLE +#endif + + IF_OPT(RELOCATIONS) + DumpBaseRelocs(); + + IF_OPT(NATIVE_TABLES) + DumpHelperTable(); + + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + + //this needs to run for precodes to load the tables that identify precode ranges + IF_OPT_OR5(MODULE, METHODTABLES, EECLASSES, TYPEDESCS, PRECODES) + DumpModule(module); + + IF_OPT_OR3(FIXUP_TABLES, FIXUP_HISTOGRAM, FIXUP_THUNKS) + DumpFixupTables( module ); + IF_OPT_OR3(METHODS, GC_INFO, DISASSEMBLE_CODE ) + DumpMethods( module ); + IF_OPT_OR3(METHODTABLES, EECLASSES, TYPEDESCS) + DumpTypes( module ); +} + +void NativeImageDumper::TraceDumpImport(int idx, NativeImageDumper::Import * import) +{ + IF_OPT(DEBUG_TRACE) + { + m_display->ErrorPrintF("Import: %d\n", idx); + m_display->ErrorPrintF("\tDependency: %p\n", import->dependency); + m_display->ErrorPrintF("\twAssemblyRid: %d\n", import->entry->wAssemblyRid); + m_display->ErrorPrintF("\twModuleRid %d\n", import->entry->wModuleRid); + } +} +void NativeImageDumper::TraceDumpDependency(int idx, NativeImageDumper::Dependency * dependency) +{ + IF_OPT(DEBUG_TRACE) + { + m_display->ErrorPrintF("Dependency: %d (%p)\n", idx, dependency); + m_display->ErrorPrintF("\tPreferred: %p\n", dependency->pPreferredBase); + m_display->ErrorPrintF("\tLoaded: %p\n", dependency->pLoadedAddress); + m_display->ErrorPrintF("\tSize: %x (%d)\n", dependency->size, dependency->size); + m_display->ErrorPrintF("\tModule: P=%p, L=%p\n", DataPtrToDisplay(dac_cast<TADDR>(dependency->pModule)), + PTR_TO_TADDR(dependency->pModule)); + m_display->ErrorPrintF("Mscorlib=%s, Hardbound=%s\n", + (dependency->fIsMscorlib ? "true" : "false"), + (dependency->fIsHardbound ? "true" : "false")); + m_display->ErrorPrintF("Name: %S\n", dependency->name); + } +} + +void NativeImageDumper::WriteElementsMetadata( const char * elementName, + TADDR data, SIZE_T size ) +{ + DisplayStartStructure( elementName, + DataPtrToDisplay(data), size, ALWAYS ); + + /* XXX Mon 03/13/2006 + * Create new metatadata dispenser. When I define the Emit for defining + * assemblyrefs for dependencies, I copy the memory and totally hork any + * mapping back to base addresses. + */ + ReleaseHolder<IMetaDataTables> tables; + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, (void **) &pDispenser)); + + VARIANT opt; + + TADDR hostCopyStart = TO_TADDR(PTR_READ(data, (ULONG32)size)); + TADDR rebasedPointer; + + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &opt)); + + IfFailThrow(pDispenser->OpenScopeOnMemory((const void *)hostCopyStart, (DWORD)size, + ofRead, IID_IMetaDataTables, + (IUnknown **) &tables)); + DisplayStartArray( "Tables", W("%s"), ALWAYS ); + + for( unsigned i = 0; i < _countof(s_tableTypes); ++i ) + { + HRESULT hr = S_OK; + ULONG idx = 0; + hr = tables->GetTableIndex(s_tableTypes[i], &idx); + _ASSERTE(SUCCEEDED(hr)); + ULONG cbRow = 0, cRows = 0, cCols = 0, iKey = 0; + const char * name = NULL; + BYTE * ptr = NULL; + hr = tables->GetTableInfo(idx, &cbRow, &cRows, &cCols, + &iKey, &name); + _ASSERTE(SUCCEEDED(hr) || hr == E_INVALIDARG); + if( hr == E_INVALIDARG || cRows == 0 ) + { + continue; //no such table. + } + + hr = tables->GetRow(idx, 1, (void**)&ptr); + IfFailThrow(hr); + _ASSERTE(SUCCEEDED(hr)); + //compute address + rebasedPointer = data + (TO_TADDR(ptr) - hostCopyStart); + _ASSERTE( rebasedPointer >= data && rebasedPointer < (data + size) ); + DisplayWriteElementAddressNamed( "table", name, + DataPtrToDisplay(rebasedPointer), + cbRow * cRows , ALWAYS ); +#if 0 + DisplayStartElement( "table", ALWAYS ); + DisplayWriteElementString( "name", name, ALWAYS ); + //compute address + rebasedPointer = data + (TO_TADDR(ptr) - hostCopyStart); + _ASSERTE( rebasedPointer >= data && rebasedPointer < (data + size) ); + DisplayWriteElementAddress( "address", DataPtrToDisplay(rebasedPointer), + cbRow * cRows, ALWAYS ); + DisplayEndElement( ALWAYS ); //Table +#endif + } + DisplayEndArray( "Total Tables", ALWAYS ); + + PTR_STORAGESIGNATURE root(data); + _ASSERTE(root->lSignature == STORAGE_MAGIC_SIG); + //the root is followed by the version string who's length is + //root->iVersionString. After that is a storage header that counts the + //number of streams. + PTR_STORAGEHEADER sHdr(data + sizeof(*root) + root->iVersionString); + DisplayStartArray( "Pools", NULL, ALWAYS ); + + //now check the pools + + //start of stream headers + PTR_STORAGESTREAM streamHeader( PTR_TO_TADDR(sHdr) + sizeof(*sHdr) ); + for( unsigned i = 0; i < sHdr->iStreams; ++i ) + { + if( streamHeader->iSize > 0 ) + { + DisplayWriteElementAddressNamed( "heap", streamHeader->rcName, + DataPtrToDisplay( data + streamHeader->iOffset ), + streamHeader->iSize, ALWAYS ); + } + //Stream headers aren't fixed size. the size is aligned up based on a + //variable length string at the end. + streamHeader = PTR_STORAGESTREAM(PTR_TO_TADDR(streamHeader) + + ALIGN_UP(offsetof(STORAGESTREAM, rcName) + strlen(streamHeader->rcName) + 1, 4)); + } + + DisplayEndArray( "Total Pools", ALWAYS ); //Pools + DisplayEndStructure( ALWAYS ); //nativeMetadata +} +void NativeImageDumper::OpenMetadata() +{ + COUNT_T size; + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + const void *data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetMetadata(&size))); + + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, (void **) &pDispenser)); + + VARIANT opt; + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &opt)); + + data = PTR_READ(TO_TADDR(data), size); + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, ofRead, + IID_IMetaDataImport2, (IUnknown **) &m_import)); + + IfFailThrow(m_import->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&m_assemblyImport)); + + m_MetadataStartTarget = TO_TADDR(data); + m_MetadataSize = size; + data = PTR_READ(TO_TADDR(data), size); + m_MetadataStartHost = TO_TADDR(data); + + if (m_decoder.HasNativeHeader()) + { + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(m_decoder.GetNativeManifestMetadata(&size))); + + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, ofRead, + IID_IMetaDataImport2, (IUnknown **) &m_manifestImport)); + + IfFailThrow(m_manifestImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&m_manifestAssemblyImport)); + } + else + { + m_manifestImport = m_import; + m_manifestImport->AddRef(); + + m_manifestAssemblyImport = m_assemblyImport; + m_manifestAssemblyImport->AddRef(); + } +} +void +NativeImageDumper::AppendTokenName(mdToken token, SString& buf) +{ + AppendTokenName(token, buf, NULL); +} +void +NativeImageDumper::AppendTokenName(mdToken token, SString& buf, + IMetaDataImport2 *pImport, + bool force) +{ + mdToken parent; + ULONG size; + DWORD attr; + PCCOR_SIGNATURE pSig; + PTR_CCOR_SIGNATURE dacSig; + ULONG cSig; + DWORD flags; + ULONG rva; + CQuickBytes bytes; + + if( CHECK_OPT(DISABLE_NAMES) && !force ) + { + buf.Append( W("Disabled") ); + return; + } + + if (pImport == NULL) + pImport = m_import; + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + switch (TypeFromToken(token)) + { + case mdtTypeDef: + IfFailThrow(pImport->GetTypeDefProps(token, bigBuffer, bigBufferSize, &size, &flags, &parent)); + buf.Append(bigBuffer); + break; + + case mdtTypeRef: + // TritonTODO: consolidate with desktop + // IfFailThrow(pImport->GetTypeRefProps(token, &parent, bigBuffer, bigBufferSize, &size)); + if (FAILED(pImport->GetTypeRefProps(token, &parent, bigBuffer, bigBufferSize, &size))) + buf.Append(W("ADDED TYPEREF (?)")); + else + buf.Append(bigBuffer); + break; + + case mdtTypeSpec: + IfFailThrow(pImport->GetTypeSpecFromToken(token, &pSig, &cSig)); + dacSig = metadataToHostDAC(pSig, pImport); + TypeToString(dacSig, buf, pImport); + break; + + case mdtFieldDef: + IfFailThrow(pImport->GetFieldProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &flags, NULL, NULL)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetFieldProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &flags, NULL, NULL)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtMethodDef: + IfFailThrow(pImport->GetMethodProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &rva, &flags)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetMethodProps(token, &parent, bigBuffer, bigBufferSize, &size, &attr, + &pSig, &cSig, &rva, &flags)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtMemberRef: + IfFailThrow(pImport->GetMemberRefProps(token, &parent, bigBuffer, bigBufferSize, &size, + &pSig, &cSig)); + AppendTokenName(parent, buf, pImport); + IfFailThrow(pImport->GetMemberRefProps(token, &parent, bigBuffer, bigBufferSize, &size, + &pSig, &cSig)); + buf.AppendPrintf( W("::%s"), bigBuffer ); + break; + + case mdtSignature: + IfFailThrow(pImport->GetSigFromToken(token, &pSig, &cSig)); +#if LATER + PrettyPrintSig(pSig, cSig, W(""), &bytes, pImport); + m_display->ErrorPrintF("%S", bytes.Ptr()); +#else + _ASSERTE(!"Unimplemented"); + m_display->ErrorPrintF( "unimplemented" ); +#endif + break; + + case mdtString: + IfFailThrow(pImport->GetUserString(token, bigBuffer, bigBufferSize, &size)); + bigBuffer[min(size, bigBufferSize-1)] = 0; + buf.Append( bigBuffer ); + break; + + case mdtAssembly: + case mdtAssemblyRef: + case mdtFile: + case mdtExportedType: + { + ReleaseHolder<IMetaDataAssemblyImport> pAssemblyImport; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + PrintManifestTokenName(token, buf, pAssemblyImport, force); + } + break; + + case mdtGenericParam: + { + ULONG nameLen; + IfFailThrow(pImport->GetGenericParamProps(token, NULL, NULL, NULL, NULL, bigBuffer, + _countof(bigBuffer), &nameLen)); + bigBuffer[min(nameLen, _countof(bigBuffer) - 1)] = 0; + buf.Append( bigBuffer ); + } + break; + + default: + _ASSERTE( !"Unknown token type in AppendToken" ); + buf.AppendPrintf( W("token 0x%x"), token ); + } + } +} +void NativeImageDumper::PrintManifestTokenName(mdToken token, SString& str) +{ + PrintManifestTokenName(token, str, NULL); +} +void +NativeImageDumper::PrintManifestTokenName(mdToken token, + SString& buf, + IMetaDataAssemblyImport *pAssemblyImport, + bool force) +{ + ULONG size; + const void *pSig; + ULONG cSig; + DWORD flags; + CQuickBytes bytes; + ULONG hash; + + if( CHECK_OPT(DISABLE_NAMES) && !force ) + { + buf.Append( W("Disabled") ); + return; + } + + if (pAssemblyImport == NULL) + pAssemblyImport = m_manifestAssemblyImport; + + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + switch (TypeFromToken(token)) + { + case mdtAssembly: + IfFailThrow(pAssemblyImport->GetAssemblyProps(token, &pSig, &cSig, + &hash, bigBuffer, + bigBufferSize, &size, + NULL, &flags)); + + buf.Append(bigBuffer); + break; + + case mdtAssemblyRef: + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, &pSig, + &cSig, bigBuffer, + bigBufferSize, + &size, NULL, NULL, + NULL, &flags)); + buf.Append(bigBuffer); + break; + + case mdtFile: + IfFailThrow(pAssemblyImport->GetFileProps(token, bigBuffer, + bigBufferSize, &size, + NULL, NULL, &flags)); + + buf.Append(bigBuffer); + break; + + case mdtExportedType: + IfFailThrow(pAssemblyImport->GetExportedTypeProps(token, bigBuffer, + bigBufferSize, &size, + NULL, NULL, &flags)); + + buf.Append(bigBuffer); + break; + + default: + buf.AppendPrintf(W("token %x"), token); + } + } +} + +BOOL NativeImageDumper::HandleFixupForHistogram(PTR_CORCOMPILE_IMPORT_SECTION pSection, + SIZE_T fixupIndex, + SIZE_T *fixupCell) +{ + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + COUNT_T tableSize; + TADDR tableBase = m_decoder.GetDirectoryData(&pSection->Section, &tableSize); + + COUNT_T table = (COUNT_T)(pSection - pImportSections); + _ASSERTE(table < nImportSections); + + SIZE_T offset = dac_cast<TADDR>(fixupCell) - tableBase; + _ASSERTE( offset < tableSize ); + + COUNT_T entry = (COUNT_T)(offset / sizeof(TADDR)); + m_fixupHistogram[table][entry]++; + + return TRUE; +} + +void NativeImageDumper::ComputeMethodFixupHistogram( PTR_Module module ) +{ + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + m_fixupHistogram = new COUNT_T * [nImportSections]; + + for (COUNT_T i=0; i < nImportSections; i++) + { + PTR_CORCOMPILE_IMPORT_SECTION pSection = m_decoder.GetNativeImportSectionFromIndex(i); + + COUNT_T count = pSection->Section.Size / sizeof(TADDR); + + m_fixupHistogram[i] = new COUNT_T [count]; + ZeroMemory(m_fixupHistogram[i], count * sizeof(COUNT_T)); + } + + ZeroMemory(&m_fixupCountHistogram, sizeof(m_fixupCountHistogram)); + // profiled hot code + + MethodIterator mi(module, &m_decoder, MethodIterator::Hot); + while (mi.Next()) + { + m_fixupCount = 0; + + TADDR pFixupList = mi.GetMethodDesc()->GetFixupList(); + + if (pFixupList != NULL) + { + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + module->FixupDelayListAux(pFixupList, this, + &NativeImageDumper::HandleFixupForHistogram, + pImportSections, nImportSections, + &m_decoder); + } + + if (m_fixupCount < COUNT_HISTOGRAM_SIZE) + m_fixupCountHistogram[m_fixupCount]++; + else + m_fixupCountHistogram[COUNT_HISTOGRAM_SIZE-1]++; + } + + // unprofiled code + MethodIterator miUnprofiled(module, &m_decoder, MethodIterator::Unprofiled); + + while(miUnprofiled.Next()) + { + m_fixupCount = 0; + + TADDR pFixupList = miUnprofiled.GetMethodDesc()->GetFixupList(); + + if (pFixupList != NULL) + { + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + module->FixupDelayListAux(pFixupList, this, + &NativeImageDumper::HandleFixupForHistogram, + pImportSections, nImportSections, + &m_decoder); + } + + if (m_fixupCount < COUNT_HISTOGRAM_SIZE) + m_fixupCountHistogram[m_fixupCount]++; + else + m_fixupCountHistogram[COUNT_HISTOGRAM_SIZE-1]++; + } +} + +void NativeImageDumper::DumpFixupTables( PTR_Module module ) +{ + IF_OPT(FIXUP_HISTOGRAM) + ComputeMethodFixupHistogram( module ); + + DisplayStartCategory( "Imports", FIXUP_TABLES ); + + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + for (COUNT_T iImportSections = 0; iImportSections < nImportSections; iImportSections++) + { + PTR_CORCOMPILE_IMPORT_SECTION pImportSection = pImportSections + iImportSections; + + COUNT_T size; + TADDR pTable(m_decoder.GetDirectoryData(&pImportSection->Section, &size)); + TADDR pTableEnd = pTable + size; + + TADDR pDataTable(NULL); + + if (pImportSection->Signatures != 0) + pDataTable = m_decoder.GetRvaData(pImportSection->Signatures); + + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + { + COUNT_T entrySize = pImportSection->EntrySize; + COUNT_T count = size / entrySize; + _ASSERTE(entrySize == sizeof(CORCOMPILE_VIRTUAL_IMPORT_THUNK)); + + for (TADDR pEntry = pTable; pEntry < pTableEnd; pEntry += entrySize) + { + PTR_CORCOMPILE_VIRTUAL_IMPORT_THUNK pThunk = pEntry; + + DisplayStartStructure("VirtualImportThunk", DPtrToPreferredAddr(pThunk), + entrySize, FIXUP_THUNKS ); + + DisplayWriteElementInt( "Slot", pThunk->slotNum, FIXUP_THUNKS); + + DisplayEndStructure( FIXUP_THUNKS ); + } + } + break; + + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + { + COUNT_T entrySize = pImportSection->EntrySize; + COUNT_T count = size / entrySize; + _ASSERTE(entrySize == sizeof(CORCOMPILE_EXTERNAL_METHOD_THUNK)); + + for (TADDR pEntry = pTable; pEntry < pTableEnd; pEntry += entrySize) + { + PTR_CORCOMPILE_EXTERNAL_METHOD_THUNK pThunk = pEntry; + + DisplayStartStructure("ExternalImportThunk", DPtrToPreferredAddr(pThunk), + entrySize, FIXUP_THUNKS ); + + TADDR pDataAddr = pDataTable + ((pEntry - pTable) / entrySize) * sizeof(DWORD); + PTR_DWORD pData = pDataAddr; + + DisplayWriteElementPointer( "DataAddress ", pDataAddr, FIXUP_THUNKS ); + + TADDR blobSigAddr = RvaToDisplay(*pData); + DisplayWriteElementPointer( "TargetSigAddress", blobSigAddr, FIXUP_THUNKS ); + TempBuffer buf; + FixupBlobToString(*pData, buf); + DisplayWriteElementStringW( "TargetName", (const WCHAR*)buf, FIXUP_THUNKS ); + + DisplayEndStructure( FIXUP_THUNKS ); + } + } + break; + + default: + { + COUNT_T count = size / sizeof(TADDR); + + for (COUNT_T j = 0; j < count; j++) + { + if (dac_cast<PTR_TADDR>(pTable)[j] == 0) + continue; + + SIZE_T nNextEntry = j + 1; + while (nNextEntry < count && dac_cast<PTR_TADDR>(pTable)[nNextEntry] == 0) + nNextEntry++; + + DisplayStartStructure("ImportEntry", DPtrToPreferredAddr(dac_cast<PTR_TADDR>(pTable) + j), + (nNextEntry - j) * sizeof(TADDR), FIXUP_TABLES ); + + if (pDataTable != NULL) + { + DWORD rva = dac_cast<PTR_DWORD>(pDataTable)[j]; + WriteElementsFixupTargetAndName(rva); + } + else + { + SIZE_T token = dac_cast<PTR_TADDR>(pTable)[j]; + DisplayWriteElementPointer( "TaggedValue", token, FIXUP_TABLES ); + WriteElementsFixupBlob(pImportSection, token); + } + + DisplayWriteElementInt( "index", j, FIXUP_HISTOGRAM); + DisplayWriteElementInt( "ReferenceCount", m_fixupHistogram[iImportSections][j], FIXUP_HISTOGRAM ); + + DisplayEndStructure( FIXUP_TABLES ); + } + } + } + } + DisplayEndCategory( FIXUP_TABLES ); +} + +void NativeImageDumper::FixupThunkToString(PTR_CORCOMPILE_IMPORT_SECTION pImportSection, TADDR addr, SString& buf) +{ + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + { + PTR_CORCOMPILE_VIRTUAL_IMPORT_THUNK pThunk = addr; + buf.AppendPrintf( W("slot %d"), pThunk->slotNum ); + } + break; + + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + case CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH: + { + TADDR pTable(m_decoder.GetDirectoryData(&pImportSection->Section)); + COUNT_T index = (COUNT_T)(addr - pTable) / pImportSection->EntrySize; + TADDR pDataTable(m_decoder.GetRvaData(pImportSection->Signatures)); + TADDR pDataAddr = pDataTable + (index * sizeof(DWORD)); + PTR_DWORD pData = pDataAddr; + FixupBlobToString(*pData, buf); + } + break; + + default: + _ASSERTE(!"Unknown import type"); + } +} + +void NativeImageDumper::WriteElementsFixupBlob(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixup) +{ + if (pSection != NULL && !CORCOMPILE_IS_FIXUP_TAGGED(fixup, pSection)) + { + TempBuffer buf; + if (pSection->Type == CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE) + { + TypeHandleToString(TypeHandle::FromTAddr((TADDR)fixup), buf); + } + else + if (pSection->Type == CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE) + { + MethodDescToString(PTR_MethodDesc((TADDR)fixup), buf); + } + else + { + _ASSERTE(!"Unknown Type"); + IfFailThrow(E_FAIL); + } + m_display->WriteElementStringW( "FixupTargetName", (const WCHAR*)buf ); + return; + } + + RVA rva = CORCOMPILE_UNTAG_TOKEN(fixup); + + WriteElementsFixupTargetAndName(rva); +} + +const NativeImageDumper::EnumMnemonics s_EncodeMethodSigFlags[] = +{ +#define EMS_ENTRY(f) NativeImageDumper::EnumMnemonics(ENCODE_METHOD_SIG_ ## f, W(#f)) + EMS_ENTRY(UnboxingStub), + EMS_ENTRY(InstantiatingStub), + EMS_ENTRY(MethodInstantiation), + EMS_ENTRY(SlotInsteadOfToken), + EMS_ENTRY(MemberRefToken), + EMS_ENTRY(Constrained), + EMS_ENTRY(OwnerType), +#undef EMS_ENTRY +}; + +void NativeImageDumper::FixupBlobToString(RVA rva, SString& buf) +{ + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + BYTE kind = *sig++; + + CorTokenType tkType = (CorTokenType)0; + + IMetaDataImport2 * pImport = m_import; + + if (kind & ENCODE_MODULE_OVERRIDE) + { + Import *import = OpenImport(DacSigUncompressData(sig)); + kind &= ~ENCODE_MODULE_OVERRIDE; + + Dependency *pDep = import->dependency; + if (pDep == NULL) + { + return; + } + + pImport = pDep->pImport; + + _ASSERTE(pImport != NULL); + + // print assembly/module info + + PTR_CORCOMPILE_IMPORT_TABLE_ENTRY entry = import->entry; + if (entry->wAssemblyRid != 0) + { + mdToken realRef = + MapAssemblyRefToManifest(TokenFromRid(entry->wAssemblyRid, + mdtAssemblyRef), + m_assemblyImport); + AppendToken(realRef, buf, m_manifestImport); + buf.Append( W(" ") ); + } + if (entry->wModuleRid != 0) + { + AppendToken(TokenFromRid(entry->wModuleRid, mdtFile), buf, pImport); + buf.Append( W(" ") ); + } + } + + // print further info + + mdToken token; + + switch (kind) + { + case ENCODE_MODULE_HANDLE: + // No further info + break; + + case ENCODE_TYPE_HANDLE: + EncodeType: + if (pImport != NULL) + TypeToString(sig, buf, pImport); + else + buf.Append( W("<unresolved type> ") ); + + break; + + case ENCODE_METHOD_HANDLE: + EncodeMethod: + { + //Flags are first + DWORD methodFlags = DacSigUncompressData(sig); + + // If the type portion for this generic method signature + // is from a different module then both the generic type and the + // generic method tokens are interpreted in the context of that module, + // and not the current import. This is returned by TypeToString. + // + IMetaDataImport2 * pMethodImport = pImport; + if (pImport != NULL) + { + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) + { + pMethodImport = TypeToString(sig, buf, pImport); + } + } + else + { + buf.Append( W("<unresolved method signature>") ); + break; + } + + //If we have SlotInsteadOfToken set then this is a slot number (i.e. for an array) + if( methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken ) + { + buf.AppendPrintf( W(" method slot %d"), DacSigUncompressData(sig) ); + } + else + { + // decode the methodToken (a rid is encoded) + RID rid = DacSigUncompressData(sig); + + mdMethodDef methodToken = ((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) ? mdtMemberRef : mdtMethodDef) | rid; + + buf.Append( W(" ") ); + + // Get the full signature of method from external module + // Need temporary buffer because method name will be inserted + // in between the signature + + TempBuffer tempName; + + AppendTokenName( methodToken, tempName, pMethodImport ); + + if( methodFlags & ENCODE_METHOD_SIG_MethodInstantiation ) + { + //for each generic arg, there is a type handle. + ULONG numParams = DacSigUncompressData(sig); + + tempName.Append( W("<") ); + for( unsigned i = 0;i < numParams; ++i ) + { + if( i != 0 ) + tempName.Append( W(", ") ); + + // switch back to using pImport to resolve tokens + TypeToString(sig, tempName, pImport); + } + tempName.Append( W(">") ); + } + + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + + if (methodFlags & ENCODE_METHOD_SIG_MemberRefToken) + { + IfFailThrow(pMethodImport->GetMemberRefProps(methodToken, + NULL, + NULL, + 0, + NULL, + &pvSigBlob, + &cbSigBlob)); + } + else + { + IfFailThrow(pMethodImport->GetMethodProps(methodToken, + NULL, + NULL, + 0, + NULL, + NULL, + &pvSigBlob, + &cbSigBlob, + NULL, + NULL)); + } + + CQuickBytes prettySig; + ReleaseHolder<IMDInternalImport> pInternal; + IfFailThrow(GetMDInternalInterfaceFromPublic(pMethodImport, IID_IMDInternalImport, + (void**)&pInternal)); + StackScratchBuffer buffer; + const ANSI * ansi = tempName.GetANSI(buffer); + ansi = PrettyPrintSig(pvSigBlob, cbSigBlob, ansi, &prettySig, pInternal, NULL); + tempName.SetANSI( ansi ); + buf.Append(tempName); + } + + buf.Append( W(" flags=(") ); + EnumFlagsToString( methodFlags, s_EncodeMethodSigFlags, _countof(s_EncodeMethodSigFlags), + W(", "), buf ); + buf.Append( W(")") ); + } + break; + + case ENCODE_FIELD_HANDLE: + EncodeField: + { + //Flags are first + DWORD fieldFlags = DacSigUncompressData(sig); + + IMetaDataImport2 * pFieldImport = pImport; + if (pImport != NULL) + { + if (fieldFlags & ENCODE_FIELD_SIG_OwnerType) + { + pFieldImport = TypeToString(sig, buf, pImport); + } + } + else + buf.Append( W("<unresolved type>") ); + + if (fieldFlags & ENCODE_FIELD_SIG_IndexInsteadOfToken) + { + buf.AppendPrintf( W(" field index %d"), DacSigUncompressData(sig) ); + } + else + { + // decode the methodToken (a rid is encoded) + RID rid = DacSigUncompressData(sig); + + mdMethodDef fieldToken = ((fieldFlags & ENCODE_FIELD_SIG_MemberRefToken) ? mdtMemberRef : mdtFieldDef) | rid; + + buf.Append( W(" ") ); + + AppendTokenName( fieldToken, buf, pFieldImport ); + } + } + break; + + case ENCODE_STRING_HANDLE: + token = TokenFromRid(DacSigUncompressData(sig), mdtString); + if (pImport != NULL) + AppendToken(token, buf, pImport); + else + buf.AppendPrintf( W("<unresolved token %d>"), token ); + break; + + case ENCODE_VARARGS_SIG: + tkType = mdtFieldDef; + goto DataToTokenCore; + case ENCODE_VARARGS_METHODREF: + tkType = mdtMemberRef; + goto DataToTokenCore; + case ENCODE_VARARGS_METHODDEF: + tkType = mdtMemberRef; + goto DataToTokenCore; +DataToTokenCore: + token = TokenFromRid(DacSigUncompressData(sig), tkType); + if (pImport != NULL) + AppendToken(token, buf, pImport); + else + buf.AppendPrintf( "<unresolved token %d>", token ); + break; + + case ENCODE_METHOD_ENTRY: + buf.Append( W("Entrypoint for ") ); + goto EncodeMethod; + + case ENCODE_METHOD_ENTRY_DEF_TOKEN: + { + buf.Append( W("Entrypoint for ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMethodDef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_METHOD_ENTRY_REF_TOKEN: + { + buf.Append( W("Entrypoint for ref ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMemberRef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY: + buf.Append( W("Entrypoint for ") ); + goto EncodeMethod; + + case ENCODE_VIRTUAL_ENTRY_DEF_TOKEN: + { + buf.Append( W("Virtual call for ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMethodDef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY_REF_TOKEN: + { + buf.Append( W("Virtual call for ref ") ); + token = TokenFromRid(DacSigUncompressData(sig), mdtMemberRef); + AppendTokenName(token, buf, pImport); + } + break; + + case ENCODE_VIRTUAL_ENTRY_SLOT: + { + buf.Append( W("Virtual call for ") ); + int slot = DacSigUncompressData(sig); + buf.AppendPrintf( W("slot %d "), slot ); + goto EncodeType; + } + + case ENCODE_MODULE_ID_FOR_STATICS: + buf.Append( W("Module For Statics") ); + // No further info + break; + + case ENCODE_MODULE_ID_FOR_GENERIC_STATICS: + buf.Append( W("Module For Statics for ") ); + goto EncodeType; + + case ENCODE_CLASS_ID_FOR_STATICS: + buf.Append( W("Statics ID for ") ); + goto EncodeType; + + case ENCODE_STATIC_FIELD_ADDRESS: + buf.Append( W("Static field address for ") ); + goto EncodeField; + + case ENCODE_SYNC_LOCK: + buf.Append( W("Synchronization handle for ") ); + break; + + case ENCODE_INDIRECT_PINVOKE_TARGET: + buf.Append( W("Indirect P/Invoke target for ") ); + break; + + case ENCODE_PROFILING_HANDLE: + buf.Append( W("Profiling handle for ") ); + goto EncodeMethod; + + case ENCODE_ACTIVE_DEPENDENCY: + { + buf.Append( W("Active dependency for ") ); + + int targetModuleIndex = DacSigUncompressData(sig); + Import *targetImport = OpenImport(targetModuleIndex); + + CORCOMPILE_IMPORT_TABLE_ENTRY *entry = targetImport->entry; + if (entry->wAssemblyRid != 0) + { + mdToken realRef = + MapAssemblyRefToManifest(TokenFromRid(entry->wAssemblyRid, + mdtAssemblyRef), + m_assemblyImport); + AppendToken(realRef, buf, m_manifestImport); + buf.Append( W(" ") ); + } + if (entry->wModuleRid != 0) + { + AppendToken(TokenFromRid(entry->wModuleRid, mdtFile), buf, + targetImport->dependency->pImport); + } + } + break; + + default: + buf.Append( W("Unknown fixup kind") ); + _ASSERTE(!"Unknown fixup kind"); + } +} + +void NativeImageDumper::WriteElementsFixupTargetAndName(RVA rva) +{ + if( rva == NULL ) + { + /* XXX Tue 04/11/2006 + * This should only happen for static fields. If the field is + * unaligned, we need an extra cell for an indirection. + */ + m_display->WriteElementPointer( "FixupTargetValue", NULL ); + m_display->WriteElementStringW( "FixupTargetName", W("NULL") ); + return; + } + + m_display->WriteElementPointer( "FixupTargetValue", RvaToDisplay(rva) ); + + TempBuffer buf; + FixupBlobToString(rva, buf); + + m_display->WriteElementStringW( "FixupTargetName", (const WCHAR*)buf ); +} + +NativeImageDumper::Dependency * NativeImageDumper::GetDependency(mdAssemblyRef token, IMetaDataAssemblyImport *pImport) +{ + if (RidFromToken(token) == 0) + return OpenDependency(0); + + if (pImport == NULL) + pImport = m_assemblyImport; + + // Need to map from IL token to manifest token + mdAssemblyRef manifestToken = MapAssemblyRefToManifest(token, pImport); + + if( manifestToken == mdAssemblyNil ) + { + //this is "self" + return OpenDependency(0); + } + + COUNT_T count; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&count))); + + for (COUNT_T i = 0; i < count; i++) + { + if (deps[i].dwAssemblyRef == manifestToken) + return OpenDependency(i+1); + } + + TempBuffer buf; + AppendTokenName(manifestToken, buf, m_manifestImport); + m_display->ErrorPrintF("Error: unlisted assembly dependency %S\n", (const WCHAR*)buf); + + return NULL; +} + +mdAssemblyRef NativeImageDumper::MapAssemblyRefToManifest(mdAssemblyRef token, IMetaDataAssemblyImport *pAssemblyImport) +{ + // Reference may be to self + if (TypeFromToken(token) == mdtAssembly) + return token; + + // Additional tokens not originally present overflow to manifest automatically during emit + /* REVISIT_TODO Tue 01/31/2006 + * Factor this code out so that it is shared with the module index code in the CLR that looks + * exactly thes same + */ + //count the assembly refs. + ULONG count = 0; + + HCORENUM iter = NULL; + for (;;) + { + ULONG tokens = 0; + mdAssemblyRef tmp; + IfFailThrow(pAssemblyImport->EnumAssemblyRefs(&iter, &tmp, 1, + &tokens)); + if (tokens == 0) + break; + count ++; + } + pAssemblyImport->CloseEnum(iter); + + if( RidFromToken(token) > count ) + { + //out of range import. This means that it has spilled over. Subtract + //off the max number of assembly refs and return it as a manifest + //token. + return token - (count + 1); + } + + ULONG cchName; + ASSEMBLYMETADATA metadata; + + ZeroMemory(&metadata, sizeof(metadata)); + + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, NULL, NULL, + NULL, 0, &cchName, + &metadata, NULL, NULL, + NULL)); + + LPWSTR szAssemblyName = NULL; + + if (cchName > 0) + szAssemblyName = (LPWSTR) _alloca(cchName * sizeof(WCHAR)); + + if (metadata.cbLocale > 0) + metadata.szLocale = (LPWSTR) _alloca(metadata.cbLocale * sizeof(WCHAR)); + if (metadata.ulProcessor > 0) + metadata.rProcessor = (DWORD*) _alloca(metadata.ulProcessor * sizeof(DWORD)); + if (metadata.ulOS > 0) + metadata.rOS = (OSINFO*) _alloca(metadata.ulOS * sizeof(OSINFO)); + + const void *pbPublicKey; + ULONG cbPublicKey; + DWORD flags; + const void *pbHashValue; + ULONG cbHashValue; + + + IfFailThrow(pAssemblyImport->GetAssemblyRefProps(token, &pbPublicKey, &cbPublicKey, + szAssemblyName, cchName, NULL, + &metadata, &pbHashValue, &cbHashValue, + &flags)); + + //Notice that we're searching for the provided metadata for the dependency info and then looking in the + //image we're dumping for the dependency. + // + //Also, sometimes we find "self" in these searches. If so, return mdAssemblyDefNil as a canary value. + + if( !wcscmp(szAssemblyName, m_name) ) + { + //we need "self". + return mdAssemblyNil; + } + + mdAssemblyRef ret = mdAssemblyRefNil; + /*HCORENUM*/ iter = NULL; + for(;;) + { + //Walk through all the assemblyRefs and search for a match. I would use DefineAssemblyRef here, but + //if I do it will create an assemblyRef is one is not found. Then I fail in a bad place. This + //way I can fail in a less bad place. + mdAssemblyRef currentRef; + //ULONG count; + IfFailThrow(m_manifestAssemblyImport->EnumAssemblyRefs(&iter, ¤tRef, 1, &count)); + if( 0 == count ) + break; + + //get the information about the assembly ref and compare. + const void * publicKeyToken; + ULONG pktSize = 0; + WCHAR name[128]; + /*ULONG*/ cchName = _countof(name); + ASSEMBLYMETADATA curMD = {0}; + + IfFailThrow(m_manifestAssemblyImport->GetAssemblyRefProps(currentRef, &publicKeyToken, &pktSize, name, + cchName, &cchName, &curMD, + NULL /*ppbHashValue*/, NULL/*pcbHashValue*/, + NULL/*pdwAssemblyRefFlags*/)); + if( !wcscmp(name, szAssemblyName) ) + { + if( cbPublicKey == pktSize && !memcmp(pbPublicKey, publicKeyToken, pktSize) + && curMD.usMajorVersion == metadata.usMajorVersion + && curMD.usMinorVersion == metadata.usMinorVersion) + { + ret = currentRef; + break; + } + else if (wcscmp(szAssemblyName, CoreLibName_W) == 0) + { + // Mscorlib is special - version number and public key token are ignored. + ret = currentRef; + break; + } + else if (metadata.usMajorVersion == 255 && + metadata.usMinorVersion == 255 && + metadata.usBuildNumber == 255 && + metadata.usRevisionNumber == 255) + { + // WinMDs encode all assemblyrefs with version 255.255.255.255 including CLR assembly dependencies (mscorlib, System). + ret = currentRef; + } + else + { + //there was an assembly with the correct name, but with the wrong version number. Let the + //user know. + m_display->ErrorPrintF("MapAssemblyRefToManifest: found %S with version %d.%d in manifest. Wanted version %d.%d.\n", szAssemblyName, curMD.usMajorVersion, curMD.usMinorVersion, metadata.usMajorVersion, metadata.usMinorVersion); + // TritonTODO: why? + ret = currentRef; + break; + } + + } + } + pAssemblyImport->CloseEnum(iter); + if( ret == mdAssemblyRefNil ) + { + TempBuffer pkt; + appendByteArray(pkt, (const BYTE*)pbPublicKey, cbPublicKey); + m_display->ErrorPrintF("MapAssemblyRefToManifest could not find token for %S, Version=%d.%d, PublicKeyToken=%S\n", szAssemblyName, metadata.usMajorVersion, metadata.usMinorVersion, (const WCHAR *)pkt); + _ASSERTE(!"MapAssemblyRefToManifest failed to find a match"); + } + + return ret; +} + +NativeImageDumper::Import * NativeImageDumper::OpenImport(int i) +{ + if (m_imports == NULL) + { + COUNT_T count = m_decoder.GetNativeImportTableCount(); + m_numImports = count; + m_imports = new Import [count]; + ZeroMemory(m_imports, count * sizeof(m_imports[0])); + } + + if (m_imports[i].entry == NULL) + { + //GetNativeImportFromIndex returns a host pointer. + CORCOMPILE_IMPORT_TABLE_ENTRY * entry = m_decoder.GetNativeImportFromIndex(i); + m_imports[i].entry = (PTR_CORCOMPILE_IMPORT_TABLE_ENTRY)(TADDR)entry; + + /* + mdToken tok = TokenFromRid(entry->wAssemblyRid, mdtAssemblyRef); + Dependency * dependency = GetDependency( MapAssemblyRefToManifest(tok, + */ + Dependency *dependency = GetDependency(TokenFromRid(entry->wAssemblyRid, mdtAssemblyRef)); + m_imports[i].dependency = dependency; + _ASSERTE(dependency); //Why can this be null? + + } + + return &m_imports[i]; +} + + +const NativeImageDumper::Dependency *NativeImageDumper::GetDependencyForFixup(RVA rva) +{ + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + if (*sig++ & ENCODE_MODULE_OVERRIDE) + { + unsigned idx = DacSigUncompressData(sig); + + _ASSERTE(idx >= 0 && idx < (int)m_decoder.GetNativeImportTableCount()); + return OpenImport(idx)->dependency; + } + + return &m_dependencies[0]; +} + + +void NativeImageDumper::AppendToken(mdToken token, SString& buf) +{ + return NativeImageDumper::AppendToken(token, buf, NULL); +} +void NativeImageDumper::AppendToken(mdToken token, SString& buf, + IMetaDataImport2 *pImport) +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + switch (TypeFromToken(token)) + { + case mdtTypeDef: + buf.Append( W("TypeDef ") ); + break; + + case mdtTypeRef: + buf.Append( W("TypeRef ") ); + break; + + case mdtTypeSpec: + buf.Append( W("TypeRef ") ); + break; + + case mdtFieldDef: + buf.Append( W("FieldDef ")); + break; + + case mdtMethodDef: + buf.Append( W("MethodDef ") ); + break; + + case mdtMemberRef: + buf.Append( W("MemberRef ") ); + break; + + case mdtAssemblyRef: + buf.Append( W("AssemblyRef ") ); + break; + + case mdtFile: + buf.Append( W("File ") ); + break; + + case mdtString: + buf.Append( W("String ") ); + break; + + case mdtSignature: + buf.Append( W("Signature ") ); + break; + + } + if( RidFromToken(token) == mdTokenNil ) + buf.Append( W("Nil") ); + else + AppendTokenName(token, buf, pImport); +} + +NativeImageDumper::Dependency *NativeImageDumper::OpenDependency(int index) +{ + CORCOMPILE_VERSION_INFO *info = m_decoder.GetNativeVersionInfo(); + + if (m_dependencies == NULL) + { + COUNT_T count; + m_decoder.GetNativeDependencies(&count); + + // Add one for self + count++; + + m_numDependencies = count; + m_dependencies = new Dependency [count]; + ZeroMemory(m_dependencies, count * sizeof (Dependency)); + } + + if (m_dependencies[index].entry == NULL) + { + CORCOMPILE_DEPENDENCY *entry; + + if (index == 0) + { + // Make dummy entry for self + entry = &m_self; + m_self.dwAssemblyRef = TokenFromRid(1, mdtAssembly); + m_self.dwAssemblyDef = TokenFromRid(1, mdtAssembly); + m_self.signAssemblyDef = info->sourceAssembly; + m_manifestImport->GetScopeProps(NULL, NULL, 0, &m_self.signNativeImage); + m_dependencies[index].pLoadedAddress = dac_cast<TADDR>(m_baseAddress); + m_dependencies[index].pPreferredBase = + TO_TADDR(m_decoder.GetNativePreferredBase()); + m_dependencies[index].size = m_imageSize; + m_dependencies[index].pImport = m_import; + m_dependencies[index].pMetadataStartTarget = + m_MetadataStartTarget; + m_dependencies[index].pMetadataStartHost = + m_MetadataStartHost; + m_dependencies[index].MetadataSize = m_MetadataSize; + m_dependencies[index].pModule = + (TADDR)m_decoder.GetPersistedModuleImage(); + m_dependencies[index].fIsHardbound = TRUE; + _ASSERTE( (m_dependencies[index].pModule + > m_dependencies[index].pLoadedAddress) + && (m_dependencies[index].pModule + < m_dependencies[index].pLoadedAddress + + m_dependencies[index].size) ); + // patch the Module vtable so that the DAC is able to instantiate it + TADDR vtbl = DacGetTargetVtForHostVt(Module::VPtrHostVTable(), true); + DacWriteAll( m_dependencies[index].pModule.GetAddr(), &vtbl, sizeof(vtbl), false ); + } + else + { + COUNT_T numDeps; + PTR_CORCOMPILE_DEPENDENCY deps(TO_TADDR(m_decoder.GetNativeDependencies(&numDeps))); + + entry = deps + (index-1); + + //load the dependency, get the pointer, and use the PEDecoder + //to open the metadata. + + TempBuffer buf; + TADDR loadedBase; + /* REVISIT_TODO Tue 11/22/2005 + * Is this the right name? + */ + Dependency& dependency = m_dependencies[index]; + AppendTokenName(entry->dwAssemblyRef, buf, m_manifestImport, true); + bool isHardBound = !!(entry->signNativeImage != INVALID_NGEN_SIGNATURE); + SString mscorlibStr(SString::Literal, CoreLibName_W); + bool isMscorlib = (0 == buf.Compare( mscorlibStr )); + dependency.fIsHardbound = isHardBound; + wcscpy_s(dependency.name, _countof(dependency.name), + (const WCHAR*)buf); + if( isHardBound ) + { + IfFailThrow(m_librarySupport->LoadHardboundDependency((const WCHAR*)buf, + entry->signNativeImage, &loadedBase)); + + dependency.pLoadedAddress = loadedBase; + } + else + { + ASSEMBLYMETADATA asmData = {0}; + const void * hashValue; + ULONG hashLength, size, flags; + IfFailThrow(m_manifestAssemblyImport->GetAssemblyRefProps(entry->dwAssemblyRef, &hashValue, &hashLength, bigBuffer, bigBufferSize, &size, &asmData, NULL, NULL, &flags)); + + + HRESULT hr = + m_librarySupport->LoadSoftboundDependency((const WCHAR*)buf, + (const BYTE*)&asmData, (const BYTE*)hashValue, hashLength, + &loadedBase); + if( FAILED(hr) ) + { + TempBuffer pkt; + if( hashLength > 0 ) + { + appendByteArray(pkt, (BYTE*)hashValue, hashLength); + } + else + { + pkt.Set( W("<No Hash>") ); + } + //try to continue without loading this softbound + //dependency. + m_display->ErrorPrintF( "WARNING Failed to load softbound dependency:\n\t%S,Version=%d.%d.0.0,PublicKeyToken=%S.\n\tAttempting to continue. May crash later in due to missing metadata\n", + (const WCHAR *)buf, asmData.usMajorVersion, + asmData.usMinorVersion, (const WCHAR *)pkt ); + m_dependencies[index].entry = entry; + return &m_dependencies[index]; + + } + //save this off to the side so OpenImport can find the metadata. + m_dependencies[index].pLoadedAddress = loadedBase; + } + /* REVISIT_TODO Wed 11/23/2005 + * Refactor this with OpenMetadata from above. + */ + //now load the metadata from the new image. + PEDecoder decoder(dac_cast<PTR_VOID>(loadedBase)); + if( isHardBound ) + { + dependency.pPreferredBase = + TO_TADDR(decoder.GetNativePreferredBase()); + dependency.size = decoder.Has32BitNTHeaders() ? + decoder.GetNTHeaders32()->OptionalHeader.SizeOfImage : + decoder.GetNTHeaders64()->OptionalHeader.SizeOfImage; + } + ReleaseHolder<IMetaDataDispenserEx> pDispenser; + IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser, + IID_IMetaDataDispenserEx, + (void **) &pDispenser)); + + VARIANT opt; + IfFailThrow(pDispenser->GetOption(MetaDataCheckDuplicatesFor, + &opt)); + V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile; + IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, + &opt)); + if( decoder.HasNativeHeader() ) + { + dependency.pModule = + TO_TADDR(decoder.GetPersistedModuleImage()); + _ASSERTE( (PTR_TO_TADDR(dependency.pModule) > loadedBase) + && (PTR_TO_TADDR(dependency.pModule) < loadedBase + + decoder.GetSize()) ); + // patch the Module vtable so that the DAC is able to instantiate it + TADDR vtbl = DacGetTargetVtForHostVt(Module::VPtrHostVTable(), true); + DacWriteAll( m_dependencies[index].pModule.GetAddr(), &vtbl, sizeof(vtbl), false ); + } + else + { + dependency.pModule = NULL; + } + + const void * data; + COUNT_T size; + + DACCOP_IGNORE(CastBetweenAddressSpaces,"nidump is in-proc and doesn't maintain a clean separation of address spaces (target and host are the same."); + data = reinterpret_cast<void*>(dac_cast<TADDR>(decoder.GetMetadata(&size))); + + dependency.pMetadataStartTarget = TO_TADDR(data); + dependency.MetadataSize = size; + data = PTR_READ(TO_TADDR(data), size); + dependency.pMetadataStartHost = TO_TADDR(data); + IfFailThrow(pDispenser->OpenScopeOnMemory(data, size, + ofRead, + IID_IMetaDataImport2, + (IUnknown **) &dependency.pImport)); + dependency.fIsMscorlib = isMscorlib; + } + + m_dependencies[index].entry = entry; + + } + + return &m_dependencies[index]; +} + +IMetaDataImport2* NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, SString& buf) +{ + return TypeToString(sig, buf, NULL); +} +#if 0 +void NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, + IMetaDataImport2 *pImport) +{ + CQuickBytes tmp; + + if (pImport == NULL) + pImport = m_import; + + LPCWSTR type = PrettyPrintSig( sig, INT_MAX, W(""), &tmp, pImport ); + _ASSERTE(type); + m_display->ErrorPrintF( "%S", type ); +} +#endif + +IMetaDataImport2 * NativeImageDumper::TypeToString(PTR_CCOR_SIGNATURE &sig, + SString& buf, + IMetaDataImport2 *pImport, + IMetaDataImport2 *pOrigImport /* =NULL */) + +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return pImport; + } + + if (pImport == NULL) + pImport = m_import; + if (pOrigImport == NULL) + pOrigImport = pImport; + + IMetaDataImport2 * pRet = pImport; +#define TYPEINFO(enumName, classSpace, className, size, gcType, isArray, isPrim, isFloat, isModifier, isGenVar) \ + className, + static const char *elementNames[] = { +#include "cortypeinfo.h" + }; +#undef TYPEINFO + + CorElementType type = DacSigUncompressElementType(sig); + + if (type == (CorElementType) ELEMENT_TYPE_MODULE_ZAPSIG) + { + unsigned idx = DacSigUncompressData(sig); + buf.AppendPrintf( W("module %d "), idx ); + //switch module + const Import * import = OpenImport(idx); + pImport = import->dependency->pImport; + + //if there was a module switch, return the import for the new module. + //This is useful for singatures, where the module index applies to + //subsequent tokens. + pRet = pImport; + + type = DacSigUncompressElementType(sig); + } + if (type >= 0 && (size_t)type < _countof(elementNames) + && elementNames[type] != NULL) + { + buf.AppendPrintf( "%s", elementNames[type] ); + } + else switch ((DWORD)type) + { + case ELEMENT_TYPE_CANON_ZAPSIG: + buf.Append( W("System.__Canon") ); + break; + + case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG: + case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG: + { + buf.Append( W("native ") ); + TypeToString(sig, buf, pImport); + } + break; + + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + if (type == ELEMENT_TYPE_VALUETYPE) + buf.Append( W("struct ") ); + + mdToken token = DacSigUncompressToken(sig); + AppendTokenName(token, buf, pImport); + } + break; + + case ELEMENT_TYPE_SZARRAY: + TypeToString(sig, buf, pImport); + buf.Append( W("[]") ); + break; + + case ELEMENT_TYPE_ARRAY: + { + TypeToString(sig, buf, pImport, pOrigImport); + unsigned rank = DacSigUncompressData(sig); + if (rank == 0) + buf.Append( W("[??]") ); + else + { + size_t cbLowerBounds; + if (!ClrSafeInt<size_t>::multiply(rank, 2*sizeof(int), cbLowerBounds/* passed by ref */)) + ThrowHR(COR_E_OVERFLOW); + int* lowerBounds = (int*) _alloca(cbLowerBounds); + int* sizes = &lowerBounds[rank]; + memset(lowerBounds, 0, sizeof(int)*2*rank); + + unsigned numSizes = DacSigUncompressData(sig); + _ASSERTE(numSizes <= rank); + unsigned int i; + for(i =0; i < numSizes; i++) + sizes[i] = DacSigUncompressData(sig); + + unsigned numLowBounds = DacSigUncompressData(sig); + _ASSERTE(numLowBounds <= rank); + for(i = 0; i < numLowBounds; i++) + lowerBounds[i] = DacSigUncompressData(sig); + + buf.Append(W("[")); + for(i = 0; i < rank; i++) + { + if (sizes[i] != 0 && lowerBounds[i] != 0) + { + if (lowerBounds[i] == 0) + buf.AppendPrintf( W("%s"), sizes[i] ); + else + { + buf.AppendPrintf( W("%d ..."), lowerBounds[i] ); + if (sizes[i] != 0) + buf.AppendPrintf( W("%d"), + lowerBounds[i] + sizes[i] + + 1 ); + } + } + if (i < rank-1) + buf.Append( W(",") ); + } + buf.Append( W("]") ); + } + } + break; + + case ELEMENT_TYPE_MVAR: + buf.Append( W("!") ); + // fall through + case ELEMENT_TYPE_VAR: + buf.AppendPrintf( W("!%d"), DacSigUncompressData(sig)); + break; + + case ELEMENT_TYPE_VAR_ZAPSIG: + { + buf.Append( W("var ") ); + + mdToken token = TokenFromRid(DacSigUncompressData(sig), mdtGenericParam); + AppendTokenName(token, buf, pImport); + } + break; + + case ELEMENT_TYPE_GENERICINST: + { + TypeToString(sig, buf, pImport, pOrigImport); + unsigned ntypars = DacSigUncompressData(sig); + buf.Append( W("<") ); + for (unsigned i = 0; i < ntypars; i++) + { + if (i > 0) + buf.Append( W(",") ); + // switch pImport back to our original Metadata importer + TypeToString(sig, buf, pOrigImport, pOrigImport); + } + buf.Append( W(">") ); + } + break; + + case ELEMENT_TYPE_FNPTR: + buf.Append( W("(fnptr)") ); + break; + + // Modifiers or depedant types + case ELEMENT_TYPE_PINNED: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W(" pinned") ); + break; + + case ELEMENT_TYPE_PTR: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W("*") ); + break; + + case ELEMENT_TYPE_BYREF: + TypeToString(sig, buf, pImport, pOrigImport); + buf.Append( W("&") ); + break; + + case ELEMENT_TYPE_SENTINEL: + case ELEMENT_TYPE_END: + default: + _ASSERTE(!"Unknown Type"); + IfFailThrow(E_FAIL); + break; + } + return pRet; +} + +void NativeImageDumper::DumpMethods(PTR_Module module) +{ + COUNT_T hotCodeSize; + PCODE hotCode = m_decoder.GetNativeHotCode(&hotCodeSize); + + + COUNT_T codeSize; + PCODE code = m_decoder.GetNativeCode(&codeSize); + + COUNT_T coldCodeSize; + PCODE coldCode = m_decoder.GetNativeColdCode(&coldCodeSize); + + DisplayStartCategory( "Code", METHODS ); + DisplayWriteElementAddress( "HotCode", DataPtrToDisplay(hotCode), + hotCodeSize, METHODS ); + + DisplayWriteElementAddress( "UnprofiledCode", + DataPtrToDisplay(code), + codeSize, METHODS ); + DisplayWriteElementAddress( "ColdCode", + DataPtrToDisplay(coldCode), + coldCodeSize, METHODS ); + + PTR_CORCOMPILE_CODE_MANAGER_ENTRY codeEntry(m_decoder.GetNativeCodeManagerTable()); + + DisplayWriteElementAddress( "ROData", + RvaToDisplay(codeEntry->ROData.VirtualAddress), + codeEntry->ROData.Size, METHODS ); + + DisplayWriteElementAddress( "HotCommonCode", + DataPtrToDisplay(hotCode), + codeEntry->HotIBCMethodOffset, METHODS ); + + DisplayWriteElementAddress( "HotIBCMethodCode", + DataPtrToDisplay(hotCode + + codeEntry->HotIBCMethodOffset), + codeEntry->HotGenericsMethodOffset + - codeEntry->HotIBCMethodOffset, + METHODS ); + + DisplayWriteElementAddress( "HotGenericsMethodCode", + DataPtrToDisplay(hotCode + + codeEntry->HotGenericsMethodOffset), + hotCodeSize - codeEntry->HotGenericsMethodOffset, + METHODS ); + + DisplayWriteElementAddress( "ColdIBCMethodCode", + DataPtrToDisplay(coldCode), + codeEntry->ColdUntrainedMethodOffset, + METHODS ); + + MethodIterator mi(module, &m_decoder); + + DisplayStartArray( "Methods", NULL, METHODS ); + + while( mi.Next() ) + { + DumpCompleteMethod( module, mi ); + } + + DisplayEndArray( "Total Methods", METHODS ); //Methods + + /* REVISIT_TODO Wed 12/14/2005 + * I have this coverage read in here because there is some other data between the + * methods in debug builds. For now just whack the whole text section. Go + * back later and check out that I really got everything. + */ + CoverageRead( hotCode, hotCodeSize ); + CoverageRead( coldCode, coldCodeSize ); +#ifdef USE_CORCOMPILE_HEADER + CoverageRead( hotCodeTable, hotCodeTableSize ); + CoverageRead( coldCodeTable, coldCodeTableSize ); +#endif + + DisplayEndCategory( METHODS ); //Code + + //m_display->StartCategory( "Methods" ); +} + +static SString g_holdStringOutData; + +static void stringOut( const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + g_holdStringOutData.AppendVPrintf(fmt, args); + va_end(args); +} + +static void nullStringOut( const char * fmt, ... ) { } + +const NativeImageDumper::EnumMnemonics s_CorExceptionFlags[] = +{ +#define CEF_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, v) + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_NONE, W("none")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FILTER, W("filter")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FINALLY, W("finally")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_FAULT, W("fault")), + CEF_ENTRY(COR_ILEXCEPTION_CLAUSE_DUPLICATED, W("duplicated")), +#undef CEF_ENTRY +}; + +void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi) +{ + PTR_MethodDesc md = mi.GetMethodDesc(); + +#ifdef WIN64EXCEPTIONS + PTR_RUNTIME_FUNCTION pRuntimeFunction = mi.GetRuntimeFunction(); +#endif + + //Read the GCInfo to get the total method size. + unsigned methodSize = 0; + unsigned gcInfoSize = UINT_MAX; + + //parse GCInfo for size information. + GCInfoToken gcInfoToken = mi.GetGCInfoToken(); + PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(gcInfoToken.Info); + + void (* stringOutFn)(const char *, ...); + IF_OPT(GC_INFO) + { + stringOutFn = stringOut; + } + else + { + stringOutFn = nullStringOut; + } + if (gcInfo != NULL) + { + PTR_CBYTE curGCInfoPtr = gcInfo; + g_holdStringOutData.Clear(); + GCDump gcDump(gcInfoToken.Version); + gcDump.gcPrintf = stringOutFn; +#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) + GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH); + methodSize = gcInfoDecoder.GetCodeLength(); +#endif + + //dump the data to a string first so we can get the gcinfo size. +#ifdef _TARGET_X86_ + InfoHdr hdr; + stringOutFn( "method info Block:\n" ); + curGCInfoPtr += gcDump.DumpInfoHdr(PTR_CBYTE(gcInfoToken.Info), &hdr, &methodSize, 0); + stringOutFn( "\n" ); +#endif + + IF_OPT(METHODS) + { +#ifdef _TARGET_X86_ + stringOutFn( "PointerTable:\n" ); + curGCInfoPtr += gcDump.DumpGCTable( curGCInfoPtr, + hdr, + methodSize, 0); + gcInfoSize = curGCInfoPtr - gcInfo; +#elif defined(USE_GC_INFO_DECODER) + stringOutFn( "PointerTable:\n" ); + curGCInfoPtr += gcDump.DumpGCTable( curGCInfoPtr, + methodSize, 0); + gcInfoSize = (unsigned)(curGCInfoPtr - gcInfo); +#endif + } + + //data is output below. + } + + TADDR hotCodePtr = mi.GetMethodStartAddress(); + TADDR coldCodePtr = mi.GetMethodColdStartAddress(); + + size_t hotCodeSize = methodSize; + size_t coldCodeSize = 0; + + if (coldCodePtr != NULL) + { + hotCodeSize = mi.GetHotCodeSize(); + coldCodeSize = methodSize - hotCodeSize; + } + + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))); + const Dependency* mdDep = GetDependencyFromMD(md); + TempBuffer buffer; + _ASSERTE(mdDep->pImport); + MethodDescToString(md, buffer); + + DisplayStartElement( "Method", METHODS ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buffer, METHODS ); + + /* REVISIT_TODO Mon 10/24/2005 + * Do I have to annotate this? + */ + DisplayWriteElementPointer("m_methodDesc", + DPtrToPreferredAddr(md), + METHODS); + + DisplayStartStructure( "m_gcInfo", + DPtrToPreferredAddr(gcInfo), + gcInfoSize, + METHODS ); + + DisplayStartTextElement( "Contents", GC_INFO ); + DisplayWriteXmlTextBlock( ("%S", (const WCHAR *)g_holdStringOutData), GC_INFO ); + DisplayEndTextElement( GC_INFO ); //Contents + + DisplayEndStructure( METHODS ); //GCInfo + + PTR_CORCOMPILE_EXCEPTION_LOOKUP_TABLE pExceptionInfoTable (PTR_TO_TADDR(module->GetNGenLayoutInfo()->m_ExceptionInfoLookupTable.StartAddress())); + if (pExceptionInfoTable) + { + COUNT_T numLookupEntries = (COUNT_T) (module->GetNGenLayoutInfo()->m_ExceptionInfoLookupTable.Size() / sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY)); + DWORD methodStartRVA = m_decoder.GetDataRva(TO_TADDR(hotCodePtr)); + + COUNT_T ehInfoSize = 0; + DWORD exceptionInfoRVA = NativeExceptionInfoLookupTable::LookupExceptionInfoRVAForMethod(pExceptionInfoTable, + numLookupEntries, + methodStartRVA, + &ehInfoSize); + + if( exceptionInfoRVA != 0 ) + { + PTR_CORCOMPILE_EXCEPTION_CLAUSE pExceptionInfoArray = dac_cast<PTR_CORCOMPILE_EXCEPTION_CLAUSE>(PTR_TO_TADDR(m_decoder.GetBase()) + exceptionInfoRVA); + COUNT_T ehCount = ehInfoSize / sizeof(CORCOMPILE_EXCEPTION_CLAUSE); + _ASSERTE(ehCount > 0); + DisplayStartArray("EHClauses", NULL, METHODS ); + for( unsigned i = 0; i < ehCount; ++i ) + { + PTR_CORCOMPILE_EXCEPTION_CLAUSE host = pExceptionInfoArray + i; + + DisplayStartStructure( "Clause", DPtrToPreferredAddr(host), sizeof(PTR_CORCOMPILE_EXCEPTION_CLAUSE), METHODS); + DisplayWriteFieldEnumerated( Flags, host->Flags, + EE_ILEXCEPTION_CLAUSE, + s_CorExceptionFlags, W(", "), + METHODS ); + DisplayWriteFieldUInt( TryStartPC, host->TryStartPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( TryEndPC, host->TryEndPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( HandlerStartPC, + host->HandlerStartPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + DisplayWriteFieldUInt( HandlerEndPC, + host->HandlerEndPC, + EE_ILEXCEPTION_CLAUSE, METHODS ); + if( host->Flags & COR_ILEXCEPTION_CLAUSE_FILTER ) + { + DisplayWriteFieldUInt( FilterOffset, host->FilterOffset, + EE_ILEXCEPTION_CLAUSE, METHODS ); + } + else if( !(host->Flags & (COR_ILEXCEPTION_CLAUSE_FAULT | COR_ILEXCEPTION_CLAUSE_FINALLY)) ) + { + WriteFieldMDTokenImport( ClassToken, host->ClassToken, + EE_ILEXCEPTION_CLAUSE, METHODS, + mdDep->pImport ); + } + DisplayEndStructure( METHODS ); //Clause + } + DisplayEndArray("Total EHClauses", METHODS ); // Clauses + } + } + + TADDR fixupList = md->GetFixupList(); + if (fixupList != NULL) + { + DisplayStartArray( "Fixups", NULL, METHODS ); + DumpMethodFixups(module, fixupList); + DisplayEndArray(NULL, METHODS); //Fixups + } + + DisplayStartStructure( "Code", DataPtrToDisplay(hotCodePtr), hotCodeSize, + METHODS ); + + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble hot code. Read the code into the host process. + /* REVISIT_TODO Mon 10/24/2005 + * Is this align up right? + */ + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(hotCodePtr, + (ULONG32)ALIGN_UP(hotCodeSize, + CODE_SIZE_ALIGN))); + DisassembleMethod( codeStartHost, hotCodeSize ); + } + else + { + CoverageRead(hotCodePtr, + (ULONG32)ALIGN_UP(hotCodeSize, CODE_SIZE_ALIGN)); + } + + DisplayEndStructure(METHODS); //HotCode + + if( coldCodePtr != NULL ) + { + DisplayStartStructure( "ColdCode", DataPtrToDisplay(coldCodePtr), + coldCodeSize, METHODS ); + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble cold code. Read the code into the host process. + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(coldCodePtr, + (ULONG32)ALIGN_UP(coldCodeSize, + CODE_SIZE_ALIGN))); + DisassembleMethod( codeStartHost, coldCodeSize ); + } + else + { + CoverageRead(coldCodePtr, + (ULONG32)ALIGN_UP(coldCodeSize, CODE_SIZE_ALIGN)); + + } + DisplayEndStructure( METHODS ); //ColdCode + } + DisplayEndElement( METHODS ); //Method +} +#undef IDC_SWITCH + + + +void NativeImageDumper::DisassembleMethod(BYTE *code, SIZE_T size) +{ + _ASSERTE(CHECK_OPT(DISASSEMBLE_CODE)); + + m_display->StartTextElement( "NativeCode" ); + +#ifdef FEATURE_MSDIS + + BYTE *codeStart = code; + + /* XXX Wed 8/22/2007 + * The way I compute code size includes the switch tables at the end of the hot and/or cold section. + * When the disassembler gets there, it has a tendency to crash as it runs off the end of mapped + * memory. In order to properly compute this I need to look at the UnwindData (which is a + * kernel32!RUNTIME_FUNCTION structure that gives the address range for the code. However, I also need + * to chase through the list of funclets to make sure I disassemble everything. Instead of doing that, + * I'll just trap the AV. + */ + EX_TRY + { + while (code < (codeStart + size)) + { + const size_t count = m_dis->CbDisassemble(0, code, size); + + if (count == 0) + { + m_display->WriteXmlText( "%04x\tUnknown instruction (%02x)\n", code-codeStart, *code); + code++; + continue; + } + + /* XXX Fri 09/16/2005 + * PTR_HOST_TO_TADDR doesn't work on interior pointers. + */ + m_currentAddress = m_decoder.GetDataRva(PTR_HOST_TO_TADDR(codeStart) + + (code - codeStart)) + + PTR_TO_TADDR(m_decoder.GetBase()); + + const size_t cinstr = m_dis->Cinstruction(); + size_t inum = 0; + while (true) + { + WCHAR szOpcode[4096]; + size_t len = m_dis->CchFormatInstr(szOpcode, _countof(szOpcode)); + _ASSERTE(szOpcode[len-1] == 0); + m_display->WriteXmlText( "%04x\t%S\n", (code-codeStart) + (inum * 4), szOpcode ); + +NEXT_INSTR: + if (++inum >= cinstr) + break; + + _ASSERTE((inum * 4) < count); // IA64 has 3 instructions per bundle commonly + // referenced as offset 0, 4, and 8 + if (!m_dis->FSelectInstruction(inum)) + { + m_display->WriteXmlText( "%04x\tUnknown instruction within bundle\n", (code-codeStart) + (inum * 4)); + goto NEXT_INSTR; + } + } + + code += count; + } + } + EX_CATCH + { + + } + EX_END_CATCH(SwallowAllExceptions); + +#else // FEATURE_MSDIS + + m_display->WriteXmlText( "Disassembly not supported\n" ); + +#endif // FEATURE_MSDIS + + m_display->EndTextElement(); //NativeCode +} + +SIZE_T NativeImageDumper::TranslateAddressCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + __out_ecount(nameSize) WCHAR *name, SIZE_T nameSize, + DWORDLONG *offset) +{ + NativeImageDumper *pThis = (NativeImageDumper *) dis->PvClient(); + + SIZE_T ret = pThis->TranslateSymbol(dis, + addr+(SIZE_T)pThis->m_currentAddress, + name, nameSize, offset); +#ifdef _DEBUG + if( ret == 0 ) + { + _snwprintf_s(name, nameSize, _TRUNCATE, W("@TRANSLATED ADDRESS@ %p"), + (TADDR)(addr + (SIZE_T)pThis->m_currentAddress) ); + ret = wcslen(name); + *offset = -1; + } +#endif + return ret; +} +SIZE_T NativeImageDumper::TranslateFixupCallback(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, + SIZE_T size, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, + DWORDLONG *offset) +{ + NativeImageDumper *pThis = (NativeImageDumper *) dis->PvClient(); + if( !dis->TargetIsAddress() ) + return 0; + + TADDR taddr = TO_TADDR(pThis->m_currentAddress) + (TADDR)addr; + SSIZE_T targetOffset; + switch (size) + { + case sizeof(void*): + targetOffset = *PTR_SIZE_T(taddr); + break; +#ifdef _WIN64 + case sizeof(INT32): + targetOffset = *PTR_INT32(taddr); + break; +#endif + case sizeof(short): + targetOffset = *(short*)(WORD*)PTR_WORD(taddr); + break; + case sizeof(signed char): + targetOffset = *PTR_SBYTE(taddr); + break; + default: + return 0; + } + + CLRDATA_ADDRESS address = targetOffset + TO_TADDR(pThis->m_currentAddress) + addr + size; + + SIZE_T ret = pThis->TranslateSymbol(dis, address, name, nameSize, offset); + if( ret == 0 ) + { + _snwprintf_s(name, nameSize, _TRUNCATE, W("@TRANSLATED FIXUP@ %p"), (TADDR)address); + ret = wcslen(name); + *offset = -1; + } + return ret; +} + +size_t NativeImageDumper::TranslateSymbol(IXCLRDisassemblySupport *dis, + CLRDATA_ADDRESS addr, __out_ecount(nameSize) WCHAR *name, + SIZE_T nameSize, DWORDLONG *offset) +{ +#ifdef FEATURE_READYTORUN + if (m_pReadyToRunHeader != NULL) + return 0; +#endif + + if (isInRange((TADDR)addr)) + { + COUNT_T rva = (COUNT_T)(addr - PTR_TO_TADDR(m_decoder.GetBase())); + + COUNT_T helperTableSize; + void *helperTable = m_decoder.GetNativeHelperTable(&helperTableSize); + + if (rva >= m_decoder.GetDataRva(TO_TADDR(helperTable)) + && rva < (m_decoder.GetDataRva(TO_TADDR(helperTable)) + +helperTableSize)) + { + int helperIndex = (USHORT)*PTR_DWORD(TO_TADDR(addr)); +// _ASSERTE(helperIndex < CORINFO_HELP_COUNT); + // because of literal blocks we might have bogus values + if (helperIndex < CORINFO_HELP_COUNT) + _snwprintf_s(name, nameSize, _TRUNCATE, W("<%S>"), g_helperNames[helperIndex]); + else + _snwprintf_s(name, nameSize, _TRUNCATE, W("Illegal HelperIndex<%04X>"), helperIndex); + *offset = 0; + return wcslen(name); + } + + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + PTR_NGenLayoutInfo pNgenLayout = module->GetNGenLayoutInfo(); + + for (int iRange = 0; iRange < 2; iRange++) + { + if (pNgenLayout->m_CodeSections[iRange].IsInRange((TADDR)addr)) + { + int MethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[iRange], 0, pNgenLayout->m_nRuntimeFunctions[iRange] - 1); + if (MethodIndex >= 0) + { +#ifdef WIN64EXCEPTIONS + while (pNgenLayout->m_MethodDescs[iRange][MethodIndex] == 0) + MethodIndex--; +#endif + + PTR_RUNTIME_FUNCTION pRuntimeFunction = pNgenLayout->m_pRuntimeFunctions[iRange] + MethodIndex; + + PTR_MethodDesc pMD = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + TempBuffer buf; + MethodDescToString( pMD, buf ); + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s "), (const WCHAR *)buf ); + *offset = rva - RUNTIME_FUNCTION__BeginAddress(pRuntimeFunction); + return wcslen(name); + } + } + } + + if (pNgenLayout->m_CodeSections[2].IsInRange((TADDR)addr)) + { + int ColdMethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[2], 0, pNgenLayout->m_nRuntimeFunctions[2] - 1); + if (ColdMethodIndex >= 0) + { + PTR_RUNTIME_FUNCTION pRuntimeFunction; + + PTR_CORCOMPILE_COLD_METHOD_ENTRY pColdCodeMap = pNgenLayout->m_ColdCodeMap; + +#ifdef WIN64EXCEPTIONS + while (pColdCodeMap[ColdMethodIndex].mainFunctionEntryRVA == 0) + ColdMethodIndex--; + + pRuntimeFunction = dac_cast<PTR_RUNTIME_FUNCTION>(PTR_TO_TADDR(m_decoder.GetBase()) + pColdCodeMap[ColdMethodIndex].mainFunctionEntryRVA); +#else + DWORD ColdUnwindData = pNgenLayout->m_pRuntimeFunctions[2][ColdMethodIndex].UnwindData; + _ASSERTE((ColdUnwindData & RUNTIME_FUNCTION_INDIRECT) != 0); + pRuntimeFunction = dac_cast<PTR_RUNTIME_FUNCTION>(PTR_TO_TADDR(m_decoder.GetBase()) + (ColdUnwindData & ~RUNTIME_FUNCTION_INDIRECT)); +#endif + + PTR_MethodDesc pMD = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + TempBuffer buf; + MethodDescToString( pMD, buf ); + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s (cold region)"), (const WCHAR *)buf ); + *offset = rva - RUNTIME_FUNCTION__BeginAddress(&pNgenLayout->m_pRuntimeFunctions[2][ColdMethodIndex]); + return wcslen(name); + } + } + + //Dumping precodes by name requires some information from the module (the precode ranges). + IF_OPT_OR(PRECODES, MODULE) + { + TempBuffer precodeBuf; + //maybe it is a precode + PTR_Precode maybePrecode((TADDR)addr); + const char * precodeName = NULL; + if (isPrecode((TADDR)addr)) + { + switch(maybePrecode->GetType()) + { + case PRECODE_INVALID: + precodeName = "InvalidPrecode"; break; + case PRECODE_STUB: + precodeName = "StubPrecode"; break; +#ifdef HAS_NDIRECT_IMPORT_PRECODE + case PRECODE_NDIRECT_IMPORT: + precodeName = "NDirectImportPrecode"; break; +#endif // HAS_NDIRECT_IMPORT_PRECODE +#ifdef HAS_REMOTING_PRECODE + case PRECODE_REMOTING: + precodeName = "RemotingPrecode"; break; +#endif // HAS_REMOTING_PRECODE +#ifdef HAS_FIXUP_PRECODE + case PRECODE_FIXUP: + precodeName = "FixupPrecode"; break; +#endif // HAS_FIXUP_PRECODE +#ifdef HAS_THISPTR_RETBUF_PRECODE + case PRECODE_THISPTR_RETBUF: + precodeName = "ThisPtrRetBufPrecode"; break; +#endif // HAS_THISPTR_RETBUF_PRECODE + } + + if( precodeName ) + { + //hot or cold? + precodeBuf.AppendPrintf( W("%S (0x%p)"), precodeName, addr ); + } + //get MethodDesc from precode and dump the target + PTR_MethodDesc precodeMD(maybePrecode->GetMethodDesc()); + precodeBuf.Append( W(" for ") ); + MethodDescToString(precodeMD, precodeBuf); + + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s"), (const WCHAR *)precodeBuf); + + *offset = 0; + return wcslen(name); + } + } + + PTR_CORCOMPILE_IMPORT_SECTION pImportSection = m_decoder.GetNativeImportSectionForRVA(rva); + if (pImportSection != NULL) + { + const char * wbRangeName = NULL; + switch (pImportSection->Type) + { + case CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD: + wbRangeName = "ExternalMethod"; + break; + +#if 0 + case CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD: + wbRangeName = "VirtualMethod"; + break; + + case CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH: + wbRangeName = "StubDispatch"; + break; +#endif + + // This method is only ever called for targets of direct calls right now and so the only + // import that can meaninfully show up here is external method thunk. + default: + return 0; + } + + TempBuffer fixupThunkBuf; + fixupThunkBuf.AppendPrintf( W("%S (0x%p) for "), wbRangeName, addr ); + FixupThunkToString(pImportSection, (TADDR)addr, fixupThunkBuf); + + _snwprintf_s(name, nameSize, _TRUNCATE, W("%s"), (const WCHAR *)fixupThunkBuf); + + *offset = 0; + return wcslen(name); + } + } + else if( g_dacImpl->GetJitHelperFunctionName(addr, + _countof(bigByteBuffer), + (char*)bigByteBuffer, + NULL ) == S_OK ) + { + *offset = 0; + _snwprintf_s( name, nameSize, _TRUNCATE, W("%S"), bigByteBuffer ); + return wcslen(name); + } + else + { + //check mscorwks + if( m_mscorwksBase <= addr && + addr < (m_mscorwksBase + m_mscorwksSize) ) + { + *offset = addr - m_mscorwksBase; + _snwprintf_s( name, nameSize, _TRUNCATE, W("clr") ); + return wcslen(name); + } + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + const Dependency& dep = m_dependencies[i]; + if( dep.pLoadedAddress <= addr && + addr < (dep.pLoadedAddress + dep.size) ) + { + *offset = addr - dep.pLoadedAddress; + _snwprintf_s( name, nameSize, _TRUNCATE, W("%s.ni"), dep.name ); + return wcslen(name); + } + } + } + + return 0; +} + +BOOL NativeImageDumper::HandleFixupForMethodDump(PTR_CORCOMPILE_IMPORT_SECTION pSection, SIZE_T fixupIndex, SIZE_T *fixupCell) +{ + PTR_SIZE_T fixupPtr(TO_TADDR(fixupCell)); + m_display->StartElement( "Fixup" ); + m_display->WriteElementPointer( "Address", + DataPtrToDisplay( TO_TADDR(fixupCell) ) ); + m_display->WriteElementUInt( "TaggedValue", (DWORD)*fixupPtr ); + WriteElementsFixupBlob(pSection, *fixupPtr); + m_display->EndElement(); + + return TRUE; +} + +void NativeImageDumper::DumpMethodFixups(PTR_Module module, + TADDR fixupList) +{ + _ASSERTE( CHECK_OPT(METHODS) ); + + COUNT_T nImportSections; + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_decoder.GetNativeImportSections(&nImportSections); + + //create the first element outside of the callback. The callback creates + //subsequent elements. + module->FixupDelayListAux( fixupList, this, + &NativeImageDumper::HandleFixupForMethodDump, + pImportSections, nImportSections, + &m_decoder ); +} + +IMAGE_SECTION_HEADER * NativeImageDumper::FindSection( char const * name ) +{ + COUNT_T numberOfSections = m_decoder.GetNumberOfSections(); + PTR_IMAGE_SECTION_HEADER curSection( m_decoder.FindFirstSection() ); + + for ( ; numberOfSections > 0; --numberOfSections, ++curSection ) + { + if ( ! strncmp( reinterpret_cast< char * >( curSection->Name ), name, 8 ) ) + break; + } + + if ( ! numberOfSections ) + return NULL; + + return curSection; +} + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_ModulePersistedFlags[] = +{ +#define MPF_ENTRY(f) NativeImageDumper::EnumMnemonics(Module::f, W(#f)) + MPF_ENTRY(COMPUTED_GLOBAL_CLASS), + + MPF_ENTRY(COMPUTED_STRING_INTERNING), + MPF_ENTRY(NO_STRING_INTERNING), + + MPF_ENTRY(COMPUTED_WRAP_EXCEPTIONS), + MPF_ENTRY(WRAP_EXCEPTIONS), + + MPF_ENTRY(COMPUTED_RELIABILITY_CONTRACT), + + MPF_ENTRY(COLLECTIBLE_MODULE), + MPF_ENTRY(COMPUTED_IS_PRE_V4_ASSEMBLY), + MPF_ENTRY(IS_PRE_V4_ASSEMBLY), + MPF_ENTRY(DEFAULT_DLL_IMPORT_SEARCH_PATHS_IS_CACHED), + MPF_ENTRY(DEFAULT_DLL_IMPORT_SEARCH_PATHS_STATUS), + + MPF_ENTRY(NEUTRAL_RESOURCES_LANGUAGE_IS_CACHED), + MPF_ENTRY(COMPUTED_METHODDEF_TO_PROPERTYINFO_MAP), + MPF_ENTRY(LOW_LEVEL_SYSTEM_ASSEMBLY_BY_NAME), +#undef MPF_ENTRY +}; + +//VirtualSectionTypes. +#define TEXTIFY(x) W(#x) +static const NativeImageDumper::EnumMnemonics s_virtualSectionFlags [] = +{ + +#define CORCOMPILE_SECTION_IBCTYPE(ibcType, _value) NativeImageDumper::EnumMnemonics(_value, TEXTIFY(ibcType)), + CORCOMPILE_SECTION_IBCTYPES() +#undef CORCOMPILE_SECTION_IBCTYPE + +#define CORCOMPILE_SECTION_RANGE_TYPE(rangeType, _value) NativeImageDumper::EnumMnemonics(_value, TEXTIFY(rangeType) W("Range")), + CORCOMPILE_SECTION_RANGE_TYPES() +#undef CORCOMPILE_SECTION_RANGE_TYPE +}; +const WCHAR * g_sectionNames[] = +{ + W("SECTION_DUMMY"), // the first section start at 0x1. Make the array 1 based. +#define CORCOMPILE_SECTION_TYPE(section) W("SECTION_") TEXTIFY(section), + CORCOMPILE_SECTION_TYPES() +#undef CORCOMPILE_SECTION + +}; +#undef TEXTIFY + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif + +const NativeImageDumper::EnumMnemonics s_MSDFlags[] = +{ +#define MSD_ENTRY(f) NativeImageDumper::EnumMnemonics(ModuleSecurityDescriptorFlags_ ## f, W(#f)) + MSD_ENTRY(IsComputed), +#ifdef FEATURE_APTCA + MSD_ENTRY(IsAPTCA), +#endif // FEATURE_APTCA + MSD_ENTRY(IsAllCritical), + MSD_ENTRY(IsAllTransparent), + MSD_ENTRY(IsTreatAsSafe), + MSD_ENTRY(IsOpportunisticallyCritical), + MSD_ENTRY(SkipFullTrustVerification) +#undef MSD_ENTRY +}; + +void NativeImageDumper::DumpModule( PTR_Module module ) +{ + + //the module is the fisrt thing in the .data section. We use this fact for + //the sectionBases down below. +// _ASSERTE(m_decoder.GetDataRva(PTR_TO_TADDR(module)) +// == FindSection(".data")->VirtualAddress ); + + DisplayStartStructure( "module", DPtrToPreferredAddr(module), + sizeof(*module), MODULE ); + PTR_PEFile file = module->m_file; + _ASSERTE(file == NULL); + DisplayWriteFieldPointer( m_file, DPtrToPreferredAddr(file), Module, + MODULE ); + + PTR_MethodDesc dllMain( TO_TADDR(module->m_pDllMain) ); + WriteFieldMethodDesc( m_pDllMain, dllMain, Module, + MODULE ); + + _ASSERTE(module->m_dwTransientFlags == 0U); + DisplayWriteFieldUInt(m_dwTransientFlags, module->m_dwTransientFlags, + Module, MODULE ); + + + + DisplayWriteFieldEnumerated( m_dwPersistedFlags, module->m_dwPersistedFlags, + Module, s_ModulePersistedFlags, W("|"), MODULE ); + + DisplayWriteFieldPointer( m_pAssembly, + DPtrToPreferredAddr(module->m_pAssembly), + Module, MODULE ); + _ASSERTE(module->m_pAssembly == NULL); //never appears in the image + + DisplayWriteFieldUInt( m_moduleRef, module->m_moduleRef, Module, MODULE ); + DisplayWriteFieldInt( m_dwDebuggerJMCProbeCount, + module->m_dwDebuggerJMCProbeCount, Module, MODULE ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump the binder + */ + PTR_MscorlibBinder binder = module->m_pBinder; + if( NULL != binder ) + { + DisplayStartStructureWithOffset( m_pBinder, DPtrToPreferredAddr(binder), + sizeof(*binder), Module, + MODULE ); + + //these four fields don't have anything useful in ngen images. + DisplayWriteFieldPointer( m_classDescriptions, + DPtrToPreferredAddr(binder->m_classDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_methodDescriptions, + DPtrToPreferredAddr(binder->m_methodDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_fieldDescriptions, + DPtrToPreferredAddr(binder->m_fieldDescriptions), + MscorlibBinder, MODULE ); + DisplayWriteFieldPointer( m_pModule, + DPtrToPreferredAddr(binder->m_pModule), + MscorlibBinder, MODULE ); + + DisplayWriteFieldInt( m_cClasses, binder->m_cClasses, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pClasses, + DPtrToPreferredAddr(binder->m_pClasses), + sizeof(*binder->m_pClasses) + * binder->m_cClasses, + MscorlibBinder, MODULE ); + DisplayWriteFieldInt( m_cFields, binder->m_cFields, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pFields, + DPtrToPreferredAddr(binder->m_pFields), + sizeof(*binder->m_pFields) + * binder->m_cFields, + MscorlibBinder, MODULE ); + DisplayWriteFieldInt( m_cMethods, binder->m_cMethods, MscorlibBinder, + MODULE ); + DisplayWriteFieldAddress( m_pMethods, + DPtrToPreferredAddr(binder->m_pMethods), + sizeof(*binder->m_pMethods) + * binder->m_cMethods, + MscorlibBinder, MODULE ); + + DisplayEndStructure( MODULE ); //m_pBinder + } + else + { + DisplayWriteFieldPointer( m_pBinder, NULL, Module, MODULE ); + } + _ASSERTE(module->m_activeDependencies.GetCount() == 0); + + + /* REVISIT_TODO Tue 10/25/2005 + * unconditional dependencies, activations, class dependencies, thunktable + */ + + + //round trip the LookupMap back through the DAC so that we don't have an + //interior host pointer. + PTR_LookupMapBase lookupMap( PTR_TO_TADDR(module) + + offsetof(Module, m_TypeDefToMethodTableMap) ); + TraverseMap( lookupMap, "m_TypeDefToMethodTableMap", + offsetof(Module, m_TypeDefToMethodTableMap), + fieldsize(Module, m_TypeDefToMethodTableMap), + &NativeImageDumper::IterateTypeDefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_TypeRefToMethodTableMap) ); + + TraverseMap( lookupMap, "m_TypeRefToMethodTableMap", + offsetof(Module, m_TypeRefToMethodTableMap), + fieldsize(Module, m_TypeRefToMethodTableMap), + &NativeImageDumper::IterateTypeRefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_MethodDefToDescMap) ); + TraverseMap( lookupMap, "m_MethodDefToDescMap", + offsetof(Module, m_MethodDefToDescMap), + fieldsize(Module, m_MethodDefToDescMap), + &NativeImageDumper::IterateMethodDefToMDCallback); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_FieldDefToDescMap) ); + TraverseMap( lookupMap, "m_FieldDefToDescMap", + offsetof(Module, m_FieldDefToDescMap), + fieldsize(Module, m_FieldDefToDescMap), + &NativeImageDumper::IterateFieldDefToFDCallback); + + TraverseMemberRefToDescHash(module->m_pMemberRefToDescHashTable, "m_pMemberRefToDescHashTable", + offsetof(Module, m_pMemberRefToDescHashTable), + fieldsize(Module, m_pMemberRefToDescHashTable), + FALSE); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_GenericParamToDescMap) ); + + TraverseMap( lookupMap, "m_GenericParamToDescMap", + offsetof(Module, m_GenericParamToDescMap), + fieldsize(Module, m_GenericParamToDescMap), + &NativeImageDumper::IterateGenericParamToDescCallback); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_GenericTypeDefToCanonMethodTableMap) ); + + TraverseMap( lookupMap, "m_GenericTypeDefToCanonMethodTableMap", + offsetof(Module, m_GenericTypeDefToCanonMethodTableMap), + fieldsize(Module, m_GenericTypeDefToCanonMethodTableMap), + &NativeImageDumper::IterateTypeDefToMTCallback ); + + lookupMap = PTR_LookupMapBase( PTR_TO_TADDR(module) + + offsetof(Module, m_FileReferencesMap) ); + TraverseMap( lookupMap, "m_FileReferencesMap", + offsetof(Module, m_FileReferencesMap), + fieldsize(Module, m_FileReferencesMap), + &NativeImageDumper::IterateMemberRefToDescCallback); + + lookupMap = PTR_LookupMapBase(PTR_TO_TADDR(module) + + offsetof(Module,m_ManifestModuleReferencesMap)); + + TraverseMap( lookupMap, "m_ManifestModuleReferencesMap", + offsetof(Module, m_ManifestModuleReferencesMap), + fieldsize(Module, m_ManifestModuleReferencesMap), + &NativeImageDumper::IterateManifestModules); + + TraverseClassHash( module->m_pAvailableClasses, "m_pAvailableClasses", + offsetof(Module, m_pAvailableClasses), + fieldsize(Module, m_pAvailableClasses), true ); + + TraverseTypeHash( module->m_pAvailableParamTypes, "m_pAvailableParamTypes", + offsetof(Module, m_pAvailableParamTypes), + fieldsize(Module, m_pAvailableParamTypes) ); + TraverseInstMethodHash( module->m_pInstMethodHashTable, + "m_pInstMethodHashTable", + offsetof(Module, m_pInstMethodHashTable), + fieldsize(Module, m_pInstMethodHashTable), + module ); + TraverseStubMethodHash( module->m_pStubMethodHashTable, + "m_pStubMethodHashTable", + offsetof(Module, m_pStubMethodHashTable), + fieldsize(Module, m_pStubMethodHashTable), + module ); + + IF_OPT(MODULE) + { + TraverseClassHash( module->m_pAvailableClassesCaseIns, + "m_pAvailableClassesCaseIns", + offsetof(Module, m_pAvailableClassesCaseIns), + fieldsize(Module, m_pAvailableClassesCaseIns), + false ); + } + +#ifdef FEATURE_COMINTEROP + TraverseGuidToMethodTableHash( module->m_pGuidToTypeHash, + "m_pGuidToTypeHash", + offsetof(Module, m_pGuidToTypeHash), + fieldsize(Module, m_pGuidToTypeHash), + true); + +#endif // FEATURE_COMINTEROP + + _ASSERTE(module->m_pProfilingBlobTable == NULL); + + DisplayWriteFieldFlag( m_nativeImageProfiling, + module->m_nativeImageProfiling, Module, MODULE ); + + DisplayWriteFieldPointer( m_methodProfileList, + DataPtrToDisplay((TADDR)module->m_methodProfileList), + Module, MODULE ); + _ASSERTE(module->m_methodProfileList == NULL); + + /* REVISIT_TODO Tue 10/04/2005 + * Dump module->m_moduleCtorInfo + */ + PTR_ModuleCtorInfo ctorInfo( PTR_HOST_MEMBER_TADDR(Module, module, + m_ModuleCtorInfo) ); + + DisplayStartStructureWithOffset( m_ModuleCtorInfo, + DPtrToPreferredAddr(ctorInfo), + sizeof(*ctorInfo), + Module, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numElements, ctorInfo->numElements, ModuleCtorInfo, + SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numLastAllocated, ctorInfo->numLastAllocated, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numElementsHot, ctorInfo->numElementsHot, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( ppMT, DPtrToPreferredAddr(ctorInfo->ppMT), + ctorInfo->numElements * sizeof(MethodTable*), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + /* REVISIT_TODO Tue 03/21/2006 + * is cctorInfoHot and cctorInfoCold actually have anything interesting + * inside of them? + */ + DisplayWriteFieldAddress( cctorInfoHot, + DPtrToPreferredAddr(ctorInfo->cctorInfoHot), + sizeof(*ctorInfo->cctorInfoHot) + * ctorInfo->numElementsHot, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( cctorInfoCold, + DPtrToPreferredAddr(ctorInfo->cctorInfoCold), + sizeof(*ctorInfo->cctorInfoCold) + * (ctorInfo->numElements + - ctorInfo->numElementsHot), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + /* XXX Thu 03/23/2006 + * See ModuleCtorInfo::Save for why these are +1. + */ + DisplayWriteFieldAddress( hotHashOffsets, + DPtrToPreferredAddr(ctorInfo->hotHashOffsets), + (ctorInfo->numHotHashes + 1) + * sizeof(*ctorInfo->hotHashOffsets), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( coldHashOffsets, + DPtrToPreferredAddr(ctorInfo->coldHashOffsets), + (ctorInfo->numColdHashes + 1) + * sizeof(*ctorInfo->coldHashOffsets), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayWriteFieldInt( numHotHashes, ctorInfo->numHotHashes, ModuleCtorInfo, + SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numColdHashes, ctorInfo->numColdHashes, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayWriteFieldAddress( ppHotGCStaticsMTs, + DPtrToPreferredAddr(ctorInfo->ppHotGCStaticsMTs), + ctorInfo->numHotGCStaticsMTs + * sizeof(*ctorInfo->ppHotGCStaticsMTs), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldAddress( ppColdGCStaticsMTs, + DPtrToPreferredAddr(ctorInfo->ppColdGCStaticsMTs), + ctorInfo->numColdGCStaticsMTs + * sizeof(*ctorInfo->ppColdGCStaticsMTs), + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numHotGCStaticsMTs, ctorInfo->numHotGCStaticsMTs, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + DisplayWriteFieldInt( numColdGCStaticsMTs, ctorInfo->numColdGCStaticsMTs, + ModuleCtorInfo, SLIM_MODULE_TBLS ); + + DisplayEndStructure( SLIM_MODULE_TBLS ); //m_ModuleCtorInfo + + _ASSERTE(module->m_pNgenStats == NULL); + + DisplayWriteFieldPointer( m_pNgenStats, + DataPtrToDisplay((TADDR)module->m_pNgenStats), + Module, MODULE ); +#if defined(FEATURE_MIXEDMODE) + DisplayWriteFieldPointer( m_pThunkHeap, + DataPtrToDisplay(dac_cast<TADDR>(module->m_pThunkHeap)), + Module, MODULE ); + _ASSERTE(module->m_pThunkHeap == NULL); +#endif + + DisplayWriteFieldAddress(m_propertyNameSet, + DPtrToPreferredAddr(module->m_propertyNameSet), + sizeof(module->m_propertyNameSet[0]) * + module->m_nPropertyNameSet, + Module, MODULE); + + DisplayWriteFieldPointer( m_ModuleID, + DataPtrToDisplay(dac_cast<TADDR>(module->m_ModuleID)), + Module, MODULE ); + _ASSERTE(module->m_ModuleID == NULL); + + /* XXX Tue 04/11/2006 + * Value is either -1 or 0, so no need to rebase. + */ + DisplayWriteFieldPointer( m_pRegularStaticOffsets, + PTR_TO_TADDR(module->m_pRegularStaticOffsets), + Module, MODULE ); + _ASSERTE(module->m_pRegularStaticOffsets == (void*)-1 + || module->m_pRegularStaticOffsets == 0 ); + + DisplayWriteFieldInt( m_dwMaxGCRegularStaticHandles, + module->m_dwMaxGCRegularStaticHandles, Module, MODULE ); + DisplayWriteFieldInt( m_dwRegularStaticsBlockSize, module->m_dwRegularStaticsBlockSize, + Module, MODULE ); + DisplayWriteFieldAddress( m_pDynamicStaticsInfo, + DataPtrToDisplay((TADDR)module->m_pDynamicStaticsInfo), + module->m_maxDynamicEntries + * sizeof(*(module->m_pDynamicStaticsInfo)), + Module, MODULE ); + + DisplayWriteFieldInt( m_cDynamicEntries, + (int)module->m_cDynamicEntries, Module, MODULE ); + + CoverageRead(TO_TADDR(module->m_pDynamicStaticsInfo), + (int)(module->m_maxDynamicEntries + * sizeof(*(module->m_pDynamicStaticsInfo)))); + + DisplayWriteFieldInt( m_dwReliabilityContract, + module->m_dwReliabilityContract, Module, MODULE ); + + DisplayWriteFieldPointer( m_pCerPrepInfo, + DataPtrToDisplay((TADDR)module->m_pCerPrepInfo), + Module, MODULE ); + DisplayWriteFieldPointer( m_pCerCrst, DataPtrToDisplay((TADDR)module->m_pCerCrst), + Module, MODULE ); + _ASSERTE(module->m_pCerPrepInfo == NULL && module->m_pCerCrst == NULL); + + IF_OPT_OR(MODULE, SLIM_MODULE_TBLS) + { + PTR_CerNgenRootTable table( TO_TADDR(module->m_pCerNgenRootTable) ); + DumpNgenRootTable( table, "m_pCerNgenRootTable", + offsetof(Module, m_pCerNgenRootTable), + fieldsize(Module, m_pCerNgenRootTable) ); + } + + + _ASSERTE(module->m_debuggerSpecificData.m_pDynamicILCrst == NULL); + DisplayWriteFieldPointer( m_debuggerSpecificData.m_pDynamicILCrst, + DataPtrToDisplay(dac_cast<TADDR>(module->m_debuggerSpecificData.m_pDynamicILCrst)), + Module, MODULE ); + + + _ASSERTE(module->m_pModuleSecurityDescriptor); + PTR_ModuleSecurityDescriptor msd(TO_TADDR(module->m_pModuleSecurityDescriptor)); + DisplayStartStructureWithOffset( m_pModuleSecurityDescriptor, + DPtrToPreferredAddr(msd), sizeof(*msd), + Module, MODULE ); + DisplayWriteElementEnumerated("Flags", msd->GetRawFlags(), s_MSDFlags, W(", "), MODULE ); + + _ASSERTE(msd->GetModule() == module); + DisplayEndStructure(MODULE); //ModuleSecurityDescriptor + + /* REVISIT_TODO Wed 09/21/2005 + * Get me in the debugger and look at the activations and module/class + * dependencies. + * As well as the thunks. + */ + + /* REVISIT_TODO Wed 09/21/2005 + * Dump the following + */ + //file + //assembly + + DisplayWriteFieldInt( m_DefaultDllImportSearchPathsAttributeValue, + module->m_DefaultDllImportSearchPathsAttributeValue, Module, MODULE ); + + + DisplayEndStructure(MODULE); //Module +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +bool NativeImageDumper::isPrecode(TADDR maybePrecode) +{ + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + + return !!module->IsZappedPrecode(maybePrecode); +} + +void NativeImageDumper::DumpNgenRootTable( PTR_CerNgenRootTable table, + const char * name, unsigned offset, + unsigned fieldSize ) +{ + if( table == NULL ) + { + IF_OPT(MODULE) + { + m_display->WriteFieldPointer( name, (unsigned)DPtrToPreferredAddr(table), + offset, fieldSize ); + } + return; + } + IF_OPT(MODULE) + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DPtrToPreferredAddr(table), + sizeof(*table) ); + } + + DisplayWriteFieldPointer( m_cRoots, table->GetRootCount(), + CerNgenRootTable, MODULE ); + DisplayWriteFieldAddress( m_pRestoreBitmap, + DataPtrToDisplay((TADDR)table->GetRestoreBitmap()), + table->SizeOfRestoreBitmap(), + CerNgenRootTable, MODULE ); + DisplayWriteFieldInt( m_cSlots, table->m_cSlots, CerNgenRootTable, + MODULE ); + DisplayStartArray( "Roots", NULL, SLIM_MODULE_TBLS ); + + PTR_CerRoot roots(TO_TADDR(table->GetRoots())); + for( unsigned i = 0; i < table->GetRootCount(); ++i ) + { + PTR_CerRoot root = roots + i; + DisplayStartStructure( "CerRoot", DPtrToPreferredAddr(root), + sizeof(*root), SLIM_MODULE_TBLS ); + WriteFieldMethodDesc( m_pRootMD, + PTR_MethodDesc(TO_TADDR(root->m_pRootMD)), + CerRoot, SLIM_MODULE_TBLS ); + + DisplayStartArray( "MethodContexts", NULL, SLIM_MODULE_TBLS ); + + PTR_MethodContextElement ctx(TO_TADDR(root->m_pList)); + bool dumpedSentinel = false; + while( !dumpedSentinel ) + { + DisplayStartStructure( "MethodContext", + DPtrToPreferredAddr(ctx), + sizeof(*ctx), SLIM_MODULE_TBLS ); + if( ctx->m_pMethodDesc.IsNull() ) + dumpedSentinel = true; + WriteFieldMethodDesc( m_pMethodDesc, + ctx->m_pMethodDesc.GetValue(), + MethodContextElement, SLIM_MODULE_TBLS ); + + if (!ctx->m_pExactMT.IsNull()) + { + WriteFieldMethodTable( m_pExactMT, + ctx->m_pExactMT.GetValue(), + MethodContextElement, SLIM_MODULE_TBLS ); + } + + DisplayEndStructure( SLIM_MODULE_TBLS ); //MethodContext + ++ctx; + } + + DisplayEndArray( "Total Contexts", SLIM_MODULE_TBLS); //MethodContexts + DisplayEndStructure(SLIM_MODULE_TBLS); //CerRoot + } + + DisplayEndArray( "Total Roots", SLIM_MODULE_TBLS ); //Roots + + /* REVISIT_TODO Wed 10/05/2005 + * m_cSlots seems to be set to something, but the number seems + * completely useless. What is up with that? + */ + + DisplayEndStructure( MODULE ); //CERNgenRootTable +} +void NativeImageDumper::IterateTypeDefToMTCallback( TADDR mtTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_MethodTable mt(mtTarget); + + DisplayWriteElementUInt( "Token", rid | mdtTypeDef, MODULE_TABLES ); + /* REVISIT_TODO Fri 10/21/2005 + * Can I use WriteElementMethodTable here? + */ + DisplayWriteElementPointer( "MethodTable", DPtrToPreferredAddr(mt), + MODULE_TABLES ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + /* REVISIT_TODO Fri 09/30/2005 + * This handles the extra entries in the type table that shouldn't be there. + */ + if( rid == 0 || ((rid != 1) && (mtTarget == NULL)) ) + { + DisplayWriteElementString( "Name", "mdTypeDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + + if( isInRange(PTR_TO_TADDR(mt)) ) + { + m_discoveredMTs.AppendEx(mt); + PTR_EEClass clazz = GetClassFromMT(mt); + if( isInRange(PTR_TO_TADDR(clazz)) ) + m_discoveredClasses.AppendEx(mt); + } +} + +void NativeImageDumper::IterateTypeRefToMTCallback( TADDR mtTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + mtTarget = ((FixupPointer<TADDR>&)mtTarget).GetValue(); + + PTR_MethodTable mt(mtTarget); + +#if 0 + RecordTypeRef(rid | mdtTypeRef, mt); +#endif + + DisplayWriteElementUInt( "Token", rid | mdtTypeRef, MODULE_TABLES ); + + DisplayWriteElementPointer( "MethodTable", DPtrToPreferredAddr(mt), + MODULE_TABLES ); + + if( rid == 0 ) + { + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementString( "Name", "mdtTypeRefNil", MODULE_TABLES ); + } + else if( mt == NULL ) + { + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + IF_OPT(MODULE_TABLES) + WriteElementMDToken( "Name", mdtTypeRef | rid ); + } + else if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + // + // This guy writes two things FixupTargetValue and FixupTargetName + // + WriteElementsFixupBlob( NULL,PTR_TO_TADDR(mt)); + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + if( isInRange(mtTarget) ) + { + m_discoveredMTs.AppendEx(mt); + PTR_EEClass clazz = GetClassFromMT(mt); + if( isInRange(PTR_TO_TADDR(clazz)) ) + m_discoveredClasses.AppendEx(mt); + } +} + +void NativeImageDumper::IterateMethodDefToMDCallback( TADDR mdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_MethodDesc md(mdTarget); + + DisplayWriteElementUInt( "Token", rid | mdtMethodDef, MODULE_TABLES ); + + DisplayWriteElementPointer( "MethodDesc", DPtrToPreferredAddr(md), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 ) + { + DisplayWriteElementString( "Name", "mdtMethodDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredMDs.AppendEx(md); +} + +void NativeImageDumper::IterateFieldDefToFDCallback( TADDR fdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + PTR_FieldDesc fd(fdTarget); + DisplayStartElement( "Entry", MODULE_TABLES ); + + + DisplayWriteElementUInt( "Token", rid | mdtFieldDef, MODULE_TABLES ); + + DisplayWriteElementPointer( "FieldDef", DPtrToPreferredAddr(fd), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 ) + { + DisplayWriteElementString( "Name", "mdtFieldDefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + FieldDescToString( fd, mdtFieldDef | rid, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + /* XXX Mon 10/17/2005 + * All FieldDescs are reachable from the EEClasses + */ + //m_discoveredFDs.AppendEx(PTR_FieldDesc(fdTarget)); +} + +void NativeImageDumper::IterateMemberRefToDescCallback( TADDR mrTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + + bool isFieldRef = (flags & IS_FIELD_MEMBER_REF) != 0; + mdToken targetToken = mdtMemberRef | rid; + mrTarget = ((FixupPointer<TADDR>&)mrTarget).GetValue(); + DisplayWriteElementUInt( "Token", targetToken, MODULE_TABLES ); + DisplayWriteElementPointer( isFieldRef ? "FieldDesc" : "MethodDesc", + DataPtrToDisplay(mrTarget), MODULE_TABLES ); + + TempBuffer buf; + if( rid == 0 ) + { + buf.Append( W("mdtMemberDefNil") ); + } + else if( CORCOMPILE_IS_POINTER_TAGGED(mrTarget) ) + { + WriteElementsFixupBlob( NULL, mrTarget ); + } + else if( isFieldRef ) + { + FieldDescToString( PTR_FieldDesc(mrTarget), buf ); + } + else + { + MethodDescToString( PTR_MethodDesc(mrTarget), buf ); + } + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement(MODULE_TABLES); + //m_discoveredMTs.AppendEx(mt); +} + +void NativeImageDumper::IterateGenericParamToDescCallback( TADDR tdTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + PTR_TypeDesc td(tdTarget); + DisplayStartElement( "Entry", MODULE_TABLES ); + + + DisplayWriteElementUInt( "Token", rid | mdtGenericParam, MODULE_TABLES ); + + DisplayWriteElementPointer( "GenericParam", DPtrToPreferredAddr(td), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || td == NULL ) + { + DisplayWriteElementString( "Name", "mdtGenericParamNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + TypeDescToString( td, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); +} + +#if 0 +void NativeImageDumper::IterateFileReferencesCallback(TADDR moduleTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + PTR_Module module(moduleTarget); + + DisplayWriteElementUInt( "Token", rid | mdtFile, MODULE_TABLES ); + + DisplayWriteElementPointer( "Module", DPtrToPreferredAddr(module), + MODULE_TABLES ); + + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || (module == NULL) ) + { + DisplayWriteElementString( "Name", "mdtFileNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + AppendTokenName(mdtFile | rid, buf); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredFDs.AppendEx(mt); +} +#endif + +void NativeImageDumper::IterateManifestModules( TADDR moduleTarget, + TADDR flags, + PTR_LookupMapBase map, + DWORD rid ) +{ + DisplayStartElement( "Entry", MODULE_TABLES ); + + moduleTarget = ((FixupPointer<TADDR>&)moduleTarget).GetValue(); + + PTR_Module module(moduleTarget); + + DisplayWriteElementUInt( "Token", rid | mdtAssemblyRef, MODULE_TABLES ); + + DisplayWriteElementPointer( "Module", DPtrToPreferredAddr(module), + MODULE_TABLES ); + DisplayWriteElementFlag( "fake", false, MODULE_TABLES ); + if( rid == 0 || (module == NULL) ) + { + DisplayWriteElementString( "Name", "mdtAssemblyRefNil", MODULE_TABLES ); + } + else + { + TempBuffer buf; + AppendTokenName(mdtAssemblyRef | rid, buf, m_import); + DisplayWriteElementStringW( "Name", (const WCHAR*)buf, MODULE_TABLES ); + } + DisplayWriteElementFlag( "hot", !!map->FindHotItemValuePtr(rid), + MODULE_TABLES ); + DisplayEndElement( MODULE_TABLES ); + //m_discoveredFDs.AppendEx(mt); +} + +void NativeImageDumper::TraverseMap(PTR_LookupMapBase map, const char * name, + unsigned offset, unsigned fieldSize, + void(NativeImageDumper::*cb)(TADDR, + TADDR, + PTR_LookupMapBase, + DWORD)) +{ + if( map == NULL ) + { + IF_OPT(MODULE) + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + return; + } + DisplayStartVStructure(name, MODULE); + + DisplayStartArray( "Tables", W("%s"), MODULE ); + PTR_LookupMapBase current = map; + do + { + DWORD cbTable = map->MapIsCompressed() ? map->cbTable : map->dwCount * sizeof(*map->pTable); + + IF_OPT(MODULE) + { + DisplayWriteElementAddress( "Table", + DPtrToPreferredAddr(map->pTable), + cbTable, + MODULE); + } + + CoverageRead( PTR_TO_TADDR(map->pTable), cbTable ); + _ASSERTE(current == map || current->hotItemList == NULL); + current = current->pNext; + }while( current != NULL ); + + DisplayEndArray( "Total Tables", MODULE ); //Tables + + DisplayWriteFieldAddress( hotItemList, + DPtrToPreferredAddr(map->hotItemList), + map->dwNumHotItems * sizeof(*map->hotItemList), + LookupMapBase, MODULE ); + + DisplayStartArray( "Map", W("[%s]: %s %s%s %s %s %s"), MODULE_TABLES ); + + IF_OPT_OR3(MODULE_TABLES, EECLASSES, METHODTABLES) + { + LookupMap<TADDR>::Iterator iter(dac_cast<DPTR(LookupMap<TADDR>)>(map)); + DWORD rid = 0; + while(iter.Next()) + { + TADDR flags = 0; + TADDR element = iter.GetElementAndFlags(&flags); + (this->*cb)( element, flags, map, rid ); + rid++; + } + + } + CoverageRead( PTR_TO_TADDR(map->hotItemList), + map->dwNumHotItems * sizeof(*map->hotItemList) ); + DisplayEndArray( "Total" , MODULE_TABLES );//Map + + DisplayEndVStructure(MODULE); //name +} + +// Templated method containing the core code necessary to traverse hash tables based on NgenHash (see +// vm\NgenHash.h). +template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> +void NativeImageDumper::TraverseNgenHash(DPTR(HASH_CLASS) pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext) +{ + if (pTable == NULL) + { + IF_OPT(MODULE) + m_display->WriteFieldPointer(name, offset, fieldSize, NULL); + return; + } + IF_OPT(MODULE) + { + m_display->StartStructureWithOffset(name, offset, fieldSize, + DPtrToPreferredAddr(pTable), + sizeof(HASH_CLASS)); + } + + DisplayWriteFieldPointer(m_pModule, + DPtrToPreferredAddr(pTable->m_pModule), + HASH_CLASS, MODULE); + + // Dump warm (volatile) entries. + DisplayWriteFieldUInt(m_cWarmEntries, pTable->m_cWarmEntries, HASH_CLASS, MODULE); + DisplayWriteFieldUInt(m_cWarmBuckets, pTable->m_cWarmBuckets, HASH_CLASS, MODULE); + DisplayWriteFieldAddress(m_pWarmBuckets, + DPtrToPreferredAddr(pTable->m_pWarmBuckets), + sizeof(HASH_ENTRY_CLASS*) * pTable->m_cWarmBuckets, + HASH_CLASS, MODULE); + + // Dump hot (persisted) entries. + DPTR(typename HASH_CLASS::PersistedEntries) pHotEntries(PTR_HOST_MEMBER_TADDR(HASH_CLASS, pTable, m_sHotEntries)); + DisplayStartStructureWithOffset(m_sHotEntries, DPtrToPreferredAddr(pHotEntries), + sizeof(typename HASH_CLASS::PersistedEntries), + HASH_CLASS, MODULE); + TraverseNgenPersistedEntries<HASH_CLASS, HASH_ENTRY_CLASS>(pTable, pHotEntries, saveClasses, DisplayEntryFunction, pContext); + DisplayEndStructure(MODULE); // Hot entries + + // Dump cold (persisted) entries. + DPTR(typename HASH_CLASS::PersistedEntries) pColdEntries(PTR_HOST_MEMBER_TADDR(HASH_CLASS, pTable, m_sColdEntries)); + DisplayStartStructureWithOffset(m_sColdEntries, DPtrToPreferredAddr(pColdEntries), + sizeof(typename HASH_CLASS::PersistedEntries), + HASH_CLASS, MODULE); + TraverseNgenPersistedEntries<HASH_CLASS, HASH_ENTRY_CLASS>(pTable, pColdEntries, saveClasses, DisplayEntryFunction, pContext); + DisplayEndStructure(MODULE); // Cold entries + + DisplayEndStructure(MODULE); // pTable +} + +// Helper used by TraverseNgenHash above to traverse an ngen persisted section of a table (separated out here +// because NgenHash-based tables can have two such sections, one for hot and one for cold entries). +template<typename HASH_CLASS, typename HASH_ENTRY_CLASS> +void NativeImageDumper::TraverseNgenPersistedEntries(DPTR(HASH_CLASS) pTable, + DPTR(typename HASH_CLASS::PersistedEntries) pEntries, + bool saveClasses, + void (NativeImageDumper::*DisplayEntryFunction)(void *, DPTR(HASH_ENTRY_CLASS), bool), + void *pContext) +{ + // Display top-level fields. + DisplayWriteFieldUInt(m_cEntries, pEntries->m_cEntries, typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldUInt(m_cBuckets, pEntries->m_cBuckets, typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldAddress(m_pBuckets, + DPtrToPreferredAddr(pEntries->m_pBuckets), + pEntries->m_cBuckets ? pEntries->m_pBuckets->GetSize(pEntries->m_cBuckets) : 0, + typename HASH_CLASS::PersistedEntries, MODULE); + DisplayWriteFieldAddress(m_pEntries, + DPtrToPreferredAddr(pEntries->m_pEntries), + sizeof(typename HASH_CLASS::PersistedEntry) * pEntries->m_cEntries, + typename HASH_CLASS::PersistedEntries, MODULE); + + // Display entries (or maybe just the classes referenced by those entries). + DisplayStartArray("Entries", NULL, SLIM_MODULE_TBLS); + + // Enumerate bucket list. + for (DWORD i = 0; i < pEntries->m_cBuckets; ++i) + { + // Get index of the first entry and the count of entries in the bucket. + DWORD dwEntryId, cEntries; + pEntries->m_pBuckets->GetBucket(i, &dwEntryId, &cEntries); + + // Loop over entries. + while (cEntries && (CHECK_OPT(SLIM_MODULE_TBLS) + || CHECK_OPT(EECLASSES) + || CHECK_OPT(METHODTABLES))) + { + // Lookup entry in the array via the index we have. + typename HASH_CLASS::PTR_PersistedEntry pEntry(PTR_TO_TADDR(pEntries->m_pEntries) + + (dwEntryId * sizeof(typename HASH_CLASS::PersistedEntry))); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("PersistedEntry", + DPtrToPreferredAddr(pEntry), + sizeof(typename HASH_CLASS::PersistedEntry), SLIM_MODULE_TBLS); + } + + // Display entry via a member function specific to the type of hash table we're traversing. Each + // sub-class of NgenHash hash its own entry structure that is embedded NgenHash's entry. The + // helper function expects a pointer to this inner entry. + DPTR(HASH_ENTRY_CLASS) pInnerEntry(PTR_TO_MEMBER_TADDR(typename HASH_CLASS::PersistedEntry, pEntry, m_sValue)); + (this->*DisplayEntryFunction)(pContext, pInnerEntry, saveClasses); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayWriteFieldUInt(m_iHashValue, pEntry->m_iHashValue, + typename HASH_CLASS::PersistedEntry, SLIM_MODULE_TBLS); + + DisplayEndStructure(SLIM_MODULE_TBLS); // Entry + } + + dwEntryId++; + cEntries--; + } + } + + DisplayEndArray("Total Entries", SLIM_MODULE_TBLS); // Entry array +} + +void NativeImageDumper::TraverseClassHashEntry(void *pContext, PTR_EEClassHashEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("EEClassHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(EEClassHashEntry), SLIM_MODULE_TBLS); + } + + size_t datum = size_t(PTR_TO_TADDR(pEntry->GetData())); + + if (datum & EECLASSHASH_TYPEHANDLE_DISCR) + { + IF_OPT(SLIM_MODULE_TBLS) + { + /* REVISIT_TODO Tue 10/25/2005 + * Raw data with annotation? + */ + mdTypeDef tk; + tk = EEClassHashTable::UncompressModuleAndClassDef(pEntry->GetData()); + DoWriteFieldMDToken("Token", + offsetof(EEClassHashEntry, m_Data), + fieldsize(EEClassHashEntry, m_Data), + tk); + } + } + else + { + PTR_MethodTable pMT(TO_TADDR(datum)); + IF_OPT(SLIM_MODULE_TBLS) + { + DoWriteFieldMethodTable("MethodTable", + offsetof(EEClassHashEntry, m_Data), + fieldsize(EEClassHashEntry, m_Data), + pMT); + } + + if (saveClasses) + { + // These are MethodTables. Get back to the EEClass from there. + if (isInRange(PTR_TO_TADDR(pMT))) + m_discoveredMTs.AppendEx(pMT); + if (pMT != NULL) + { + PTR_EEClass pClass = GetClassFromMT(pMT); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(pMT); + } + } + } + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayWriteFieldPointer(m_pEncloser, + DPtrToPreferredAddr(pEntry->GetEncloser()), + EEClassHashEntry, SLIM_MODULE_TBLS); + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseClassHash(PTR_EEClassHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<EEClassHashTable, EEClassHashEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseClassHashEntry, + NULL); +} + +#ifdef FEATURE_COMINTEROP + +void NativeImageDumper::TraverseGuidToMethodTableEntry(void *pContext, PTR_GuidToMethodTableEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("GuidToMethodTableEntry", + DPtrToPreferredAddr(pEntry), + sizeof(GuidToMethodTableEntry), SLIM_MODULE_TBLS); + } + + WriteFieldMethodTable(m_pMT, pEntry->m_pMT, GuidToMethodTableEntry, ALWAYS); + + TempBuffer buf; + GuidToString( *(pEntry->m_Guid), buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidToMethodTableEntry, ALWAYS ); + + DisplayEndStructure( SLIM_MODULE_TBLS ); +} + +void NativeImageDumper::TraverseGuidToMethodTableHash(PTR_GuidToMethodTableHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<GuidToMethodTableHashTable, GuidToMethodTableEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseGuidToMethodTableEntry, + NULL); +} + +#endif // FEATURE_COMINTEROP + +void NativeImageDumper::TraverseMemberRefToDescHashEntry(void *pContext, PTR_MemberRefToDescHashEntry pEntry, bool saveClasses) +{ + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("MemberRefToDescHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(MemberRefToDescHashEntry), SLIM_MODULE_TBLS); + } + + if(pEntry->m_value & IS_FIELD_MEMBER_REF) + WriteFieldFieldDesc(m_value, dac_cast<PTR_FieldDesc>(pEntry->m_value & (~MEMBER_REF_MAP_ALL_FLAGS)), MemberRefToDescHashEntry, MODULE_TABLES); + else + WriteFieldMethodDesc(m_value, dac_cast<PTR_MethodDesc>(pEntry->m_value), MemberRefToDescHashEntry, MODULE_TABLES); + + DisplayEndStructure( SLIM_MODULE_TBLS ); +} + +void NativeImageDumper::TraverseMemberRefToDescHash(PTR_MemberRefToDescHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize, + bool saveClasses) +{ + TraverseNgenHash<MemberRefToDescHashTable, MemberRefToDescHashEntry>(pTable, + name, + offset, + fieldSize, + saveClasses, + &NativeImageDumper::TraverseMemberRefToDescHashEntry, + NULL); +} + + +void NativeImageDumper::TraverseTypeHashEntry(void *pContext, PTR_EETypeHashEntry pEntry, bool saveClasses) +{ + TypeHandle th = pEntry->GetTypeHandle(); + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("EETypeHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(EETypeHashEntry), SLIM_MODULE_TBLS); + + DoWriteFieldTypeHandle("TypeHandle", + offsetof(EETypeHashEntry, m_data), + fieldsize(EETypeHashEntry, m_data), + th); + } + + if (!CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) && th.IsTypeDesc()) + { + PTR_TypeDesc td(th.AsTypeDesc()); + if (isInRange(PTR_TO_TADDR(td))) + m_discoveredTypeDescs.AppendEx(td); + if (td->HasTypeParam()) + { + PTR_ParamTypeDesc ptd(td); + + /* REVISIT_TODO Thu 12/15/2005 + * Check OwnsTemplateMethodTable. However, this asserts in + * this special completely unrestored and messed up state + * (also, it chases through MT->GetClass()). There isn't + * all that much harm here (bloats m_discoveredMTs though, + * but not by a huge amount. + */ + PTR_MethodTable mt(ptd->m_TemplateMT.GetValue()); + if (isInRange(PTR_TO_TADDR(mt))) + { + m_discoveredMTs.AppendEx(mt); + if (mt->IsClassPointerValid()) + { + PTR_EEClass pClass = mt->GetClass(); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(mt); + } + } + } + } + else + { + PTR_MethodTable mt(th.AsTAddr()); + + if (isInRange( PTR_TO_TADDR(mt))) + m_discoveredMTs.AppendEx(mt); + //don't use GetClassFromMT here. mt->m_pEEClass might be a + //fixup. In that case, just skip it. + if (mt->IsClassPointerValid()) + { + PTR_EEClass pClass = mt->GetClass(); + if (isInRange(PTR_TO_TADDR(pClass))) + m_discoveredClasses.AppendEx(mt); + } + } + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseTypeHash(PTR_EETypeHashTable pTable, + const char * name, + unsigned offset, + unsigned fieldSize) +{ + TraverseNgenHash<EETypeHashTable, EETypeHashEntry>(pTable, + name, + offset, + fieldSize, + true, + &NativeImageDumper::TraverseTypeHashEntry, + NULL); +} + +void NativeImageDumper::TraverseInstMethodHashEntry(void *pContext, PTR_InstMethodHashEntry pEntry, bool saveClasses) +{ + PTR_Module pModule((TADDR)pContext); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("InstMethodHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(InstMethodHashEntry), SLIM_MODULE_TBLS); + } + + IF_OPT_OR(SLIM_MODULE_TBLS, METHODDESCS) + { + IF_OPT(METHODDESCS) + { + PTR_MethodDesc md = pEntry->GetMethod(); + _ASSERTE(md != NULL); + + //if we want methoddescs, write the data field as a + //structure with the whole contents of the method desc. + m_display->StartVStructureWithOffset("data", offsetof(InstMethodHashEntry, data), + sizeof(pEntry->data)); + DumpMethodDesc(md, pModule); + DisplayEndVStructure(ALWAYS); //data + } + else + { + PTR_MethodDesc md = pEntry->GetMethod(); + WriteFieldMethodDesc(data, md, + InstMethodHashEntry, ALWAYS); + } + } + else + CoverageRead(PTR_TO_TADDR(pEntry), sizeof(*pEntry)); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseStubMethodHashEntry(void *pContext, PTR_StubMethodHashEntry pEntry, bool saveClasses) +{ + PTR_Module pModule((TADDR)pContext); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayStartStructure("StubMethodHashEntry", + DPtrToPreferredAddr(pEntry), + sizeof(StubMethodHashEntry), SLIM_MODULE_TBLS); + } + + IF_OPT_OR(SLIM_MODULE_TBLS, METHODDESCS) + { + PTR_MethodDesc md = pEntry->GetMethod(); + _ASSERTE(md != NULL); + + PTR_MethodDesc stub = pEntry->GetStubMethod(); + _ASSERTE(stub != NULL); + + IF_OPT(METHODDESCS) + { + //if we want methoddescs, write the data fields as a + //structure with the whole contents of the method desc. + m_display->StartVStructureWithOffset("pMD", offsetof(StubMethodHashEntry, pMD), + sizeof(pEntry->pMD)); + DumpMethodDesc(md, pModule); + DisplayEndVStructure(ALWAYS); //pMD + + m_display->StartVStructureWithOffset("pStubMD", offsetof(StubMethodHashEntry, pStubMD), + sizeof(pEntry->pStubMD)); + DumpMethodDesc(stub, pModule); + DisplayEndVStructure(ALWAYS); //pStubMD + } + else + { + WriteFieldMethodDesc(pMD, md, + StubMethodHashEntry, ALWAYS); + WriteFieldMethodDesc(pStubMD, stub, + StubMethodHashEntry, ALWAYS); + } + } + else + CoverageRead(PTR_TO_TADDR(pEntry), sizeof(*pEntry)); + + IF_OPT(SLIM_MODULE_TBLS) + { + DisplayEndStructure(SLIM_MODULE_TBLS); + } +} + +void NativeImageDumper::TraverseInstMethodHash(PTR_InstMethodHashTable pTable, + const char * name, + unsigned fieldOffset, + unsigned fieldSize, + PTR_Module module) +{ + TraverseNgenHash<InstMethodHashTable, InstMethodHashEntry>(pTable, + name, + fieldOffset, + fieldSize, + true, + &NativeImageDumper::TraverseInstMethodHashEntry, + (void*)dac_cast<TADDR>(module)); +} + +void NativeImageDumper::TraverseStubMethodHash(PTR_StubMethodHashTable pTable, + const char * name, + unsigned fieldOffset, + unsigned fieldSize, + PTR_Module module) +{ + TraverseNgenHash<StubMethodHashTable, StubMethodHashEntry>(pTable, + name, + fieldOffset, + fieldSize, + true, + &NativeImageDumper::TraverseStubMethodHashEntry, + (void*)dac_cast<TADDR>(module)); +} + +const NativeImageDumper::Dependency * +NativeImageDumper::GetDependencyForModule( PTR_Module module ) +{ + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + if( m_dependencies[i].pModule == module ) + return &m_dependencies[i]; + } + return NULL; +} + +#if 0 +const NativeImageDumper::Import * +NativeImageDumper::GetImportForPointer( TADDR ptr ) +{ + for( int i = 0; i < m_numImports; ++i ) + { + const Import * import = &m_imports[i]; + if( import->dependency->pPreferredBase == NULL ) + continue; + if( import->dependency->pPreferredBase <= ptr + && ((import->dependency->pPreferredBase + + import->dependency->size) > ptr) ) + { + //found the right target + return import; + } + } + return NULL; +} +#endif +const NativeImageDumper::Dependency * +NativeImageDumper::GetDependencyForPointer( TADDR ptr ) +{ + for( COUNT_T i = 0; i < m_numDependencies; ++i ) + { + const Dependency * dependency = &m_dependencies[i]; + if( dependency->pLoadedAddress == NULL ) + continue; + if( dependency->pLoadedAddress <= ptr + && ((dependency->pLoadedAddress + dependency->size) > ptr) ) + { + //found the right target + return dependency; + } + } + return NULL; +} + +void NativeImageDumper::DictionaryToArgString( PTR_Dictionary dictionary, unsigned numArgs, SString& buf ) +{ + //this can be called with numArgs == 0 for value type instantiations. + buf.Append( W("<") ); + + for( unsigned i = 0; i < numArgs; ++i ) + { + if( i > 0 ) + buf.Append( W(",") ); + + TypeHandle th = dictionary->GetInstantiation()[i].GetValue(); + if( CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(dictionary)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(th.AsTAddr()); + FixupBlobToString(rva, buf); + } + } + else + { + TypeHandleToString( th, buf ); + } + } + buf.Append( W(">") ); +} + +void NativeImageDumper::MethodTableToString( PTR_MethodTable mt, SString& buf ) +{ + bool hasCompleteExtents = true; + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + mdToken token = mdTokenNil; + if( mt == NULL ) + buf.Append( W("mdTypeDefNil") ); + else + { + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt))); + const Dependency * dependency; + if( !mt->IsClassPointerValid() ) + { + if( isSelf(GetDependencyForPointer(PTR_TO_TADDR(mt))) ) + { + + hasCompleteExtents = false; + RVA rva = CORCOMPILE_UNTAG_TOKEN(mt->GetCanonicalMethodTableFixup()); + PTR_CCOR_SIGNATURE sig = (TADDR) m_decoder.GetRvaData(rva); + + BYTE kind = *sig++; + + if (kind & ENCODE_MODULE_OVERRIDE) + { + /* int moduleIndex = */ DacSigUncompressData(sig); + kind &= ~ENCODE_MODULE_OVERRIDE; + } + + _ASSERTE(kind == ENCODE_TYPE_HANDLE); + CorElementType et = DacSigUncompressElementType(sig); + if( et == ELEMENT_TYPE_GENERICINST ) + { + //generic instances have another element type + et = DacSigUncompressElementType(sig); + } + if (et == ELEMENT_TYPE_VALUETYPE || et == ELEMENT_TYPE_CLASS) + { + token = DacSigUncompressToken(sig); + } + else + { + // Arrays, etc. + token = mdtTypeDef; + } + dependency = GetDependencyForFixup(rva); + } + else + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + return; + } + } + else + { + token = mt->GetCl(); + dependency = GetDependencyFromMT(mt); + } + + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, buf, + m_manifestImport ); + buf.Append(W("!")); + } + + _ASSERTE(dependency->pImport); + if( token == mdtTypeDef ) + buf.Append( W("No Token") ); + else + AppendTokenName( token, buf, dependency->pImport ); + + if( mt->HasPerInstInfo() ) + { + unsigned numDicts; + if( hasCompleteExtents ) + { + numDicts = mt->GetNumDicts(); + _ASSERTE(numDicts == CountDictionariesInClass(token, dependency->pImport)); + } + else + { + numDicts = (DWORD)CountDictionariesInClass(token, dependency->pImport); + } + PTR_Dictionary dictionary( mt->GetPerInstInfo()[numDicts-1] ); + unsigned numArgs = mt->GetNumGenericArgs(); + + DictionaryToArgString( dictionary, numArgs, buf ); + } + } +} + +mdToken NativeImageDumper::ConvertToTypeDef( mdToken typeToken, IMetaDataImport2* (&pImport) ) +{ + _ASSERTE( (TypeFromToken(typeToken) == mdtTypeDef) || (TypeFromToken(typeToken) == mdtTypeRef) + || (TypeFromToken(typeToken) == mdtTypeSpec) ); + if( mdtTypeDef == TypeFromToken(typeToken) ) + return typeToken; + if( mdtTypeRef == TypeFromToken(typeToken) ) + { + //convert the ref to a def. + mdToken scope; + WCHAR trName[MAX_CLASS_NAME]; + ULONG trNameLen; + IfFailThrow(pImport->GetTypeRefProps(typeToken, &scope, trName, _countof(trName), &trNameLen)); + _ASSERTE(trName[trNameLen-1] == 0); + + //scope is now a moduleRef or assemblyRef. Find the IMetaData import for that Ref + /* REVISIT_TODO Fri 10/6/2006 + * How do I handle moduleRefs? + */ + _ASSERTE(TypeFromToken(scope) == mdtAssemblyRef); + ReleaseHolder<IMetaDataAssemblyImport> pAssemblyImport; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + NativeImageDumper::Dependency * dep = GetDependency(scope, pAssemblyImport); + + pImport = dep->pImport; + + /* REVISIT_TODO Fri 10/6/2006 + * Does this work for inner types? + */ + //now I have the correct MetaData. Find the typeDef + HRESULT hr = pImport->FindTypeDefByName(trName, mdTypeDefNil, &typeToken); + while (hr == CLDB_E_RECORD_NOTFOUND) + { + // No matching TypeDef, try ExportedType + pAssemblyImport = NULL; + IfFailThrow(pImport->QueryInterface(IID_IMetaDataAssemblyImport, + (void **)&pAssemblyImport)); + mdExportedType tkExportedType = mdExportedTypeNil; + IfFailThrow(pAssemblyImport->FindExportedTypeByName(trName, mdExportedTypeNil, &tkExportedType)); + mdToken tkImplementation; + IfFailThrow(pAssemblyImport->GetExportedTypeProps(tkExportedType, NULL, 0, NULL, &tkImplementation, NULL, NULL)); + dep = GetDependency(tkImplementation, pAssemblyImport); + + pImport = dep->pImport; + hr = pImport->FindTypeDefByName(trName, mdTypeDefNil, &typeToken); + } + IfFailThrow(hr); + } + else + { + PCCOR_SIGNATURE pSig; + ULONG cbSig; + IfFailThrow(pImport->GetTypeSpecFromToken(typeToken, &pSig, &cbSig)); + //GENERICINST (CLASS|VALUETYPE) typeDefOrRef + CorElementType et = CorSigUncompressElementType(pSig); + _ASSERTE(et == ELEMENT_TYPE_GENERICINST); + et = CorSigUncompressElementType(pSig); + _ASSERTE((et == ELEMENT_TYPE_CLASS) || (et == ELEMENT_TYPE_VALUETYPE)); + typeToken = CorSigUncompressToken(pSig); + } + + //we just removed one level of indirection. We still might have a ref or spec. + typeToken = ConvertToTypeDef(typeToken, pImport); + _ASSERTE(TypeFromToken(typeToken) == mdtTypeDef); + return typeToken; +} + +SIZE_T NativeImageDumper::CountDictionariesInClass( mdToken typeToken, IMetaDataImport2 * pImport ) +{ + SIZE_T myDicts; //either 0 or 1 + + _ASSERTE((TypeFromToken(typeToken) == mdtTypeDef) || (TypeFromToken(typeToken) == mdtTypeRef) + || (TypeFromToken(typeToken) == mdtTypeSpec)); + + + //for refs and specs, convert to a def. This is a nop for defs. + typeToken = ConvertToTypeDef(typeToken, pImport); + + _ASSERTE(TypeFromToken(typeToken) == mdtTypeDef); + + + //count the number of generic arguments. If there are any, then we have a dictionary. + HCORENUM hEnum = NULL; + mdGenericParam params[2]; + ULONG numParams = 0; + IfFailThrow(pImport->EnumGenericParams(&hEnum, typeToken, params, _countof(params), &numParams)); + myDicts = (numParams > 0) ? 1 : 0; + + pImport->CloseEnum(hEnum); + + //get my parent for the recursive call. + mdToken parent; + IfFailThrow(pImport->GetTypeDefProps(typeToken, NULL, 0, NULL, NULL, &parent)); + return myDicts + (IsNilToken(parent) ? 0 : CountDictionariesInClass(parent, pImport)); +} + +const NativeImageDumper::EnumMnemonics s_Subsystems[] = +{ +#define S_ENTRY(f,v) NativeImageDumper::EnumMnemonics(f, 0, v) + S_ENTRY(IMAGE_SUBSYSTEM_UNKNOWN, W("Unknown")), + S_ENTRY(IMAGE_SUBSYSTEM_NATIVE, W("Native")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CUI, W("Windows CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_GUI, W("Windows GUI")), + S_ENTRY(IMAGE_SUBSYSTEM_OS2_CUI, W("OS/2 CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_POSIX_CUI, W("POSIX CUI")), + S_ENTRY(IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, W("WinCE GUI")), + S_ENTRY(IMAGE_SUBSYSTEM_XBOX, W("XBox")) +#undef S_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_CorCompileHdrFlags[] = +{ +#define CCHF_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + CCHF_ENTRY(CORCOMPILE_HEADER_HAS_SECURITY_DIRECTORY), + CCHF_ENTRY(CORCOMPILE_HEADER_IS_IBC_OPTIMIZED), + CCHF_ENTRY(CORCOMPILE_HEADER_IS_READY_TO_RUN), +#undef CCHF_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_CorPEKind[] = +{ +#define CPEK_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + CPEK_ENTRY(peNot), + CPEK_ENTRY(peILonly), + CPEK_ENTRY(pe32BitRequired), + CPEK_ENTRY(pe32Plus), + CPEK_ENTRY(pe32Unmanaged), + CPEK_ENTRY(pe32BitPreferred) +#undef CPEK_ENTRY +}; +const NativeImageDumper::EnumMnemonics s_IFH_Machine[] = +{ +#define IFH_ENTRY(f) NativeImageDumper::EnumMnemonics(f, 0, W(#f)) + IFH_ENTRY(IMAGE_FILE_MACHINE_UNKNOWN), + IFH_ENTRY(IMAGE_FILE_MACHINE_I386), + IFH_ENTRY(IMAGE_FILE_MACHINE_AMD64), + IFH_ENTRY(IMAGE_FILE_MACHINE_ARMNT), +#undef IFH_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_IFH_Characteristics[] = +{ +#define IFH_ENTRY(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + IFH_ENTRY(IMAGE_FILE_RELOCS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_EXECUTABLE_IMAGE), + IFH_ENTRY(IMAGE_FILE_LINE_NUMS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_LOCAL_SYMS_STRIPPED), + IFH_ENTRY(IMAGE_FILE_AGGRESIVE_WS_TRIM), + IFH_ENTRY(IMAGE_FILE_LARGE_ADDRESS_AWARE), + IFH_ENTRY(IMAGE_FILE_BYTES_REVERSED_LO), + IFH_ENTRY(IMAGE_FILE_32BIT_MACHINE), + IFH_ENTRY(IMAGE_FILE_DEBUG_STRIPPED), + IFH_ENTRY(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP), + IFH_ENTRY(IMAGE_FILE_NET_RUN_FROM_SWAP), + IFH_ENTRY(IMAGE_FILE_SYSTEM), + IFH_ENTRY(IMAGE_FILE_DLL), + IFH_ENTRY(IMAGE_FILE_UP_SYSTEM_ONLY), + IFH_ENTRY(IMAGE_FILE_BYTES_REVERSED_HI), +#undef IFH_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_ImportSectionType[] = +{ +#define IST_ENTRY(f) NativeImageDumper::EnumMnemonics(f, 0, W(#f)) + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_UNKNOWN), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_STRING_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE), + IST_ENTRY(CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD), +#undef IST_ENTRY +}; + +const NativeImageDumper::EnumMnemonics s_ImportSectionFlags[] = +{ +#define IST_FLAGS(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_EAGER), + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_CODE), + IST_FLAGS(CORCOMPILE_IMPORT_FLAGS_PCODE), +#undef IST_FLAGS +}; + +void NativeImageDumper::DumpNativeHeader() +{ + PTR_CORCOMPILE_HEADER nativeHeader(m_decoder.GetNativeHeader()); + + IF_OPT(NATIVE_INFO) + { + +#define WRITE_NATIVE_FIELD( name ) m_display->WriteFieldAddress(\ + # name, offsetof(CORCOMPILE_HEADER, name), \ + fieldsize(CORCOMPILE_HEADER, name), \ + RvaToDisplay( nativeHeader-> name . VirtualAddress ), \ + nativeHeader-> name . Size ) + + m_display->StartStructure( "CORCOMPILE_HEADER", + DPtrToPreferredAddr(nativeHeader), + sizeof(*nativeHeader) ); + + DisplayWriteFieldUInt( Signature, nativeHeader->Signature, CORCOMPILE_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MajorVersion, nativeHeader->MajorVersion, CORCOMPILE_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MinorVersion, nativeHeader->MinorVersion, CORCOMPILE_HEADER, ALWAYS ); + + WRITE_NATIVE_FIELD(HelperTable); + + WRITE_NATIVE_FIELD(ImportSections); + PTR_CORCOMPILE_IMPORT_SECTION pImportSections = + nativeHeader->ImportSections.VirtualAddress + + PTR_TO_TADDR(m_decoder.GetBase()); + DisplayStartArray( "ImportSections", NULL, ALWAYS ); + for( COUNT_T i = 0; i < nativeHeader->ImportSections.Size + / sizeof(*pImportSections); ++i ) + { + DisplayStartStructure( "CORCOMPILE_IMPORT_SECTION", + DPtrToPreferredAddr(pImportSections + i), + sizeof(pImportSections[i]), ALWAYS ); + DisplayWriteElementAddress( "Section", + RvaToDisplay(pImportSections[i].Section.VirtualAddress), + pImportSections[i].Section.Size, ALWAYS ); + + DisplayWriteFieldEnumerated( Flags, pImportSections[i].Flags, + CORCOMPILE_IMPORT_SECTION, s_ImportSectionFlags, W(", "), ALWAYS ); + DisplayWriteFieldEnumerated( Type, pImportSections[i].Type, + CORCOMPILE_IMPORT_SECTION, s_ImportSectionType, W(""), ALWAYS ); + + DisplayWriteFieldUInt( EntrySize, pImportSections[i].EntrySize, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayWriteFieldUInt( Signatures, pImportSections[i].Signatures, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayWriteFieldUInt( AuxiliaryData, pImportSections[i].AuxiliaryData, + CORCOMPILE_IMPORT_SECTION, ALWAYS ); + DisplayEndStructure( ALWAYS ); //PTR_CORCOMPILE_IMPORT_SECTION + + } + DisplayEndArray( NULL, ALWAYS ); //delayLoads + + WRITE_NATIVE_FIELD(ImportTable); + DisplayStartArray( "imports", NULL, ALWAYS ); + PTR_CORCOMPILE_IMPORT_TABLE_ENTRY ent( nativeHeader->ImportTable.VirtualAddress + PTR_TO_TADDR(m_decoder.GetBase()) ); + for( COUNT_T i = 0; i < nativeHeader->ImportTable.Size / sizeof(*ent); ++i ) + { + DisplayStartStructure( "CORCOMPILE_IMPORT_TABLE_ENTRY", + DPtrToPreferredAddr(ent + i), + sizeof(ent[i]), ALWAYS ); + DisplayWriteFieldUInt( wAssemblyRid, ent[i].wAssemblyRid, + CORCOMPILE_IMPORT_TABLE_ENTRY, ALWAYS ); + DisplayWriteFieldUInt( wModuleRid, ent[i].wModuleRid, + CORCOMPILE_IMPORT_TABLE_ENTRY, ALWAYS ); + DisplayEndStructure( ALWAYS ); //CORCOMPILE_IMPORT_TABLE_ENTRY + } + DisplayEndArray( NULL, ALWAYS ); //imports + + WRITE_NATIVE_FIELD(VersionInfo); + WRITE_NATIVE_FIELD(DebugMap); + WRITE_NATIVE_FIELD(ModuleImage); + WRITE_NATIVE_FIELD(CodeManagerTable); + WRITE_NATIVE_FIELD(ProfileDataList); + WRITE_NATIVE_FIELD(ManifestMetaData); + + WRITE_NATIVE_FIELD(VirtualSectionsTable); + DisplayStartArray( "VirtualSections", W("%-48s%s"), SLIM_MODULE_TBLS ); + PTR_CORCOMPILE_VIRTUAL_SECTION_INFO sects( nativeHeader->VirtualSectionsTable.VirtualAddress + PTR_TO_TADDR(m_decoder.GetBase()) ); + COUNT_T numVirtualSections = nativeHeader->VirtualSectionsTable.Size / sizeof (CORCOMPILE_VIRTUAL_SECTION_INFO); + + for( COUNT_T i = 0; i < numVirtualSections; ++i ) + { + TempBuffer sectionNameBuf; + TempBuffer sectionFlags; + StackScratchBuffer scratch; + + sectionNameBuf.Append(g_sectionNames[VirtualSectionData::VirtualSectionType(sects[i].SectionType)]); + + EnumFlagsToString( sects[i].SectionType, s_virtualSectionFlags, dim(s_virtualSectionFlags), + W(" | "), sectionFlags); + + sectionNameBuf.Append(W(" [")); + sectionNameBuf.Append(sectionFlags); + sectionNameBuf.Append(W("]")); + + DisplayStartElement( "Section", SLIM_MODULE_TBLS ); + DisplayWriteElementString("Name", sectionNameBuf.GetANSI(scratch), SLIM_MODULE_TBLS); + + DisplayWriteElementAddress( "Address", + RvaToDisplay(sects[i].VirtualAddress), + sects[i].Size, + SLIM_MODULE_TBLS ); + DisplayEndElement( SLIM_MODULE_TBLS ); //Section + } + DisplayEndArray( "Total VirtualSections", SLIM_MODULE_TBLS ); + + WRITE_NATIVE_FIELD(EEInfoTable); + +#undef WRITE_NATIVE_FIELD + DisplayWriteFieldEnumerated( Flags, nativeHeader->Flags, + CORCOMPILE_HEADER, s_CorCompileHdrFlags, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( PEKind, nativeHeader->PEKind, + CORCOMPILE_HEADER, s_CorPEKind, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( COR20Flags, nativeHeader->COR20Flags, + CORCOMPILE_HEADER, s_CorHdrFlags, W(", "), + NATIVE_INFO ); + + DisplayWriteFieldEnumerated( Machine, nativeHeader->Machine, + CORCOMPILE_HEADER, s_IFH_Machine, + W(""), NATIVE_INFO ); + DisplayWriteFieldEnumerated( Characteristics, + nativeHeader->Characteristics, + CORCOMPILE_HEADER, s_IFH_Characteristics, + W(", "), NATIVE_INFO ); + + m_display->EndStructure(); //CORCOMPILE_HEADER + } +} + +const NativeImageDumper::EnumMnemonics s_RelocType[] = +{ +#define REL_ENTRY(x) NativeImageDumper::EnumMnemonics( x, 0, W(#x)) + REL_ENTRY(IMAGE_REL_BASED_ABSOLUTE), + REL_ENTRY(IMAGE_REL_BASED_HIGHLOW), + REL_ENTRY(IMAGE_REL_BASED_DIR64), + REL_ENTRY(IMAGE_REL_BASED_THUMB_MOV32), +#undef REL_ENTRY +}; + +void NativeImageDumper::DumpBaseRelocs() +{ + COUNT_T size; + TADDR data; + + data = m_decoder.GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_BASERELOC, &size); + + if (size != 0) + { + DisplayStartStructure( "Relocations", DataPtrToDisplay(data), size, + ALWAYS ); + + while (size != 0) + { + IMAGE_BASE_RELOCATION * pBaseRelocation = dac_cast<DPTR(IMAGE_BASE_RELOCATION)>(data); + _ASSERTE(size >= pBaseRelocation->SizeOfBlock); + + SIZE_T rel = sizeof(IMAGE_BASE_RELOCATION); + while (rel < pBaseRelocation->SizeOfBlock) + { + USHORT typeOffset = *PTR_USHORT(data + rel); + + DisplayStartElement( "Entry", ALWAYS ); + + DisplayWriteElementPointer( "Address", RvaToDisplay(pBaseRelocation->VirtualAddress + (typeOffset & 0xFFF)), ALWAYS ); + + DisplayWriteElementEnumerated( "Type", (typeOffset >> 12), + s_RelocType, W(", "), ALWAYS ); + + DisplayEndElement( ALWAYS ); //Entry + + rel += sizeof(USHORT); + } + + data += pBaseRelocation->SizeOfBlock; + size -= pBaseRelocation->SizeOfBlock; + } + + DisplayEndStructure( ALWAYS ); //Relocations + } +} + +void NativeImageDumper::DumpHelperTable() +{ + COUNT_T size; + TADDR data; + + data = TO_TADDR(m_decoder.GetNativeHelperTable(&size)); + if( size != 0 ) + { + DisplayStartStructure( "HelperTable", DataPtrToDisplay(data), size, + ALWAYS ); + + TADDR curEntry = data; + TADDR tableEnd = data + size; + + while (curEntry < tableEnd) + { + DWORD dwHelper = *PTR_DWORD(curEntry); + + int iHelper = (USHORT)dwHelper; + _ASSERTE(iHelper < CORINFO_HELP_COUNT); + + DisplayStartStructure( "Helper", + DataPtrToDisplay(curEntry), (dwHelper & CORCOMPILE_HELPER_PTR) ? sizeof(TADDR) : HELPER_TABLE_ENTRY_LEN, + ALWAYS ); + + DisplayWriteElementUInt( "dwHelper", dwHelper, ALWAYS ); + DisplayWriteElementString( "Name", g_helperNames[iHelper], ALWAYS ); + + DisplayEndStructure( ALWAYS ); //Helper + + curEntry += (dwHelper & CORCOMPILE_HELPER_PTR) ? sizeof(TADDR) : HELPER_TABLE_ENTRY_LEN; + } + + DisplayEndStructure( ALWAYS ); //HelperTable + } +} + +// TODO: fix these to work with the updated flags in MethodTable, AND to understand +// the new overloading of component size... + +NativeImageDumper::EnumMnemonics s_MTFlagsLow[] = +{ +#define MTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + + MTFLAG_ENTRY(UNUSED_ComponentSize_1), + MTFLAG_ENTRY(StaticsMask), + MTFLAG_ENTRY(StaticsMask_NonDynamic), + MTFLAG_ENTRY(StaticsMask_Dynamic), + MTFLAG_ENTRY(StaticsMask_Generics), + MTFLAG_ENTRY(StaticsMask_CrossModuleGenerics), + MTFLAG_ENTRY(NotInPZM), + MTFLAG_ENTRY(GenericsMask), + MTFLAG_ENTRY(GenericsMask_NonGeneric), + MTFLAG_ENTRY(GenericsMask_GenericInst), + MTFLAG_ENTRY(GenericsMask_SharedInst), + MTFLAG_ENTRY(GenericsMask_TypicalInst), +#if defined(FEATURE_REMOTING) + MTFLAG_ENTRY(ContextStatic), +#endif + MTFLAG_ENTRY(HasRemotingVtsInfo), + MTFLAG_ENTRY(HasVariance), + MTFLAG_ENTRY(HasDefaultCtor), + MTFLAG_ENTRY(HasPreciseInitCctors), +#if defined(FEATURE_HFA) + MTFLAG_ENTRY(IsHFA), +#endif // FEATURE_HFA +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING_ITF) + MTFLAG_ENTRY(IsRegStructPassed), +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING_ITF + MTFLAG_ENTRY(IsByRefLike), + MTFLAG_ENTRY(UNUSED_ComponentSize_5), + MTFLAG_ENTRY(UNUSED_ComponentSize_6), + MTFLAG_ENTRY(UNUSED_ComponentSize_7), +#undef MTFLAG_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_MTFlagsHigh[] = +{ +#define MTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + +#define MTFLAG_CATEGORY_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_Category_ ## x, MethodTable::enum_flag_Category_Mask, W("Category_") W(#x)) + +#define MTFLAG_CATEGORY_ENTRY_WITH_MASK(x, m) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_Category_ ## x, MethodTable::enum_flag_Category_ ## m, W("Category_") W(#x)) + + MTFLAG_CATEGORY_ENTRY(Class), + MTFLAG_CATEGORY_ENTRY(Unused_1), + MTFLAG_CATEGORY_ENTRY(MarshalByRef), + MTFLAG_CATEGORY_ENTRY(Contextful), + MTFLAG_CATEGORY_ENTRY(ValueType), + MTFLAG_CATEGORY_ENTRY(Nullable), + MTFLAG_CATEGORY_ENTRY(PrimitiveValueType), + MTFLAG_CATEGORY_ENTRY(TruePrimitive), + + MTFLAG_CATEGORY_ENTRY(Interface), + MTFLAG_CATEGORY_ENTRY(Unused_2), + MTFLAG_CATEGORY_ENTRY(TransparentProxy), + MTFLAG_CATEGORY_ENTRY(AsyncPin), + + MTFLAG_CATEGORY_ENTRY_WITH_MASK(Array, Array_Mask), + MTFLAG_CATEGORY_ENTRY_WITH_MASK(IfArrayThenSzArray, IfArrayThenSzArray), + +#undef MTFLAG_CATEGORY_ENTRY_WITH_MASK +#undef MTFLAG_CATEGORY_ENTRY + + MTFLAG_ENTRY(HasFinalizer), + MTFLAG_ENTRY(IfNotInterfaceThenMarshalable), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(IfInterfaceThenHasGuidInfo), +#endif +#if defined(FEATURE_ICASTABLE) + MTFLAG_ENTRY(ICastable), +#endif + MTFLAG_ENTRY(HasIndirectParent), + MTFLAG_ENTRY(ContainsPointers), + MTFLAG_ENTRY(HasTypeEquivalence), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(HasRCWPerTypeData), +#endif + MTFLAG_ENTRY(HasCriticalFinalizer), + MTFLAG_ENTRY(Collectible), + MTFLAG_ENTRY(ContainsGenericVariables), +#if defined(FEATURE_COMINTEROP) + MTFLAG_ENTRY(ComObject), +#endif + MTFLAG_ENTRY(HasComponentSize), +#undef MTFLAG_ENTRY +}; + + +NativeImageDumper::EnumMnemonics s_MTFlags2[] = +{ +#define MTFLAG2_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTable::enum_flag_ ## x, W(#x)) + MTFLAG2_ENTRY(HasPerInstInfo), + MTFLAG2_ENTRY(HasInterfaceMap), + MTFLAG2_ENTRY(HasDispatchMapSlot), + MTFLAG2_ENTRY(HasNonVirtualSlots), + MTFLAG2_ENTRY(HasModuleOverride), + MTFLAG2_ENTRY(IsZapped), + MTFLAG2_ENTRY(IsPreRestored), + MTFLAG2_ENTRY(HasModuleDependencies), + MTFLAG2_ENTRY(NoSecurityProperties), + MTFLAG2_ENTRY(RequiresDispatchTokenFat), + MTFLAG2_ENTRY(HasCctor), + MTFLAG2_ENTRY(HasCCWTemplate), +#ifdef FEATURE_64BIT_ALIGNMENT + MTFLAG2_ENTRY(RequiresAlign8), +#endif + MTFLAG2_ENTRY(HasBoxedRegularStatics), + MTFLAG2_ENTRY(HasSingleNonVirtualSlot), + MTFLAG2_ENTRY(DependsOnEquivalentOrForwardedStructs), +#undef MTFLAG2_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_WriteableMTFlags[] = +{ +#define WMTFLAG_ENTRY(x) \ + NativeImageDumper::EnumMnemonics(MethodTableWriteableData::enum_flag_ ## x,\ + W(#x)) + + WMTFLAG_ENTRY(RemotingConfigChecked), + WMTFLAG_ENTRY(RequiresManagedActivation), + WMTFLAG_ENTRY(Unrestored), + WMTFLAG_ENTRY(CriticalTypePrepared), + WMTFLAG_ENTRY(HasApproxParent), + WMTFLAG_ENTRY(UnrestoredTypeKey), + WMTFLAG_ENTRY(IsNotFullyLoaded), + WMTFLAG_ENTRY(DependenciesLoaded), + +#ifdef _DEBUG + WMTFLAG_ENTRY(ParentMethodTablePointerValid), +#endif + + WMTFLAG_ENTRY(NGEN_IsFixedUp), + WMTFLAG_ENTRY(NGEN_IsNeedsRestoreCached), + WMTFLAG_ENTRY(NGEN_CachedNeedsRestore), +#undef WMTFLAG_ENTRY +}; + +static NativeImageDumper::EnumMnemonics s_CorElementType[] = +{ +#define CET_ENTRY(x) NativeImageDumper::EnumMnemonics(ELEMENT_TYPE_ ## x, 0, W("ELEMENT_TYPE_") W(#x)) + CET_ENTRY(END), + CET_ENTRY(VOID), + CET_ENTRY(BOOLEAN), + CET_ENTRY(CHAR), + CET_ENTRY(I1), + CET_ENTRY(U1), + CET_ENTRY(I2), + CET_ENTRY(U2), + CET_ENTRY(I4), + CET_ENTRY(U4), + CET_ENTRY(I8), + CET_ENTRY(U8), + CET_ENTRY(R4), + CET_ENTRY(R8), + CET_ENTRY(STRING), + CET_ENTRY(PTR), + CET_ENTRY(BYREF), + CET_ENTRY(VALUETYPE), + CET_ENTRY(CLASS), + CET_ENTRY(VAR), + CET_ENTRY(ARRAY), + CET_ENTRY(GENERICINST), + CET_ENTRY(TYPEDBYREF), + CET_ENTRY(VALUEARRAY_UNSUPPORTED), + CET_ENTRY(I), + CET_ENTRY(U), + CET_ENTRY(R_UNSUPPORTED), + CET_ENTRY(FNPTR), + CET_ENTRY(OBJECT), + CET_ENTRY(SZARRAY), + CET_ENTRY(MVAR), + CET_ENTRY(CMOD_REQD), + CET_ENTRY(CMOD_OPT), + CET_ENTRY(INTERNAL), + + CET_ENTRY(SENTINEL), + CET_ENTRY(PINNED), +#undef CET_ENTRY +}; + +void NativeImageDumper::DoWriteFieldCorElementType( const char * name, + unsigned offset, + unsigned fieldSize, + CorElementType type ) +{ + TempBuffer buf; + EnumFlagsToString( (int)type, s_CorElementType, dim(s_CorElementType), + W(""), buf ); + m_display->WriteFieldEnumerated( name, offset, fieldSize, (unsigned)type, + (const WCHAR *) buf ); + +} + +static NativeImageDumper::EnumMnemonics s_CorTypeAttr[] = +{ +#define CTA_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CTA_VISIBILITY_ENTRY(x) NativeImageDumper::EnumMnemonics( x, tdVisibilityMask, W(#x) ) + CTA_VISIBILITY_ENTRY(tdNotPublic), + CTA_VISIBILITY_ENTRY(tdPublic), + CTA_VISIBILITY_ENTRY(tdNestedPublic), + CTA_VISIBILITY_ENTRY(tdNestedPrivate), + CTA_VISIBILITY_ENTRY(tdNestedFamily), + CTA_VISIBILITY_ENTRY(tdNestedAssembly), + CTA_VISIBILITY_ENTRY(tdNestedFamANDAssem), + CTA_VISIBILITY_ENTRY(tdNestedFamORAssem), +#undef CTA_VISIBILITY_ENTRY + + CTA_ENTRY(tdSequentialLayout), + CTA_ENTRY(tdExplicitLayout), + + CTA_ENTRY(tdInterface), + + CTA_ENTRY(tdAbstract), + CTA_ENTRY(tdSealed), + CTA_ENTRY(tdSpecialName), + + CTA_ENTRY(tdImport), + CTA_ENTRY(tdSerializable), + + CTA_ENTRY(tdUnicodeClass), + CTA_ENTRY(tdAutoClass), + CTA_ENTRY(tdCustomFormatClass), + CTA_ENTRY(tdCustomFormatMask), + + CTA_ENTRY(tdBeforeFieldInit), + CTA_ENTRY(tdForwarder), + + CTA_ENTRY(tdRTSpecialName), + CTA_ENTRY(tdHasSecurity) +#undef CTA_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_VMFlags[] = +{ +#define VMF_ENTRY_TRANSPARENCY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, EEClass::VMFLAG_TRANSPARENCY_MASK, W(#x) ) + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_UNKNOWN), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TRANSPARENT), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALL_TRANSPARENT), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL_TAS), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL_TAS), + VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TAS_NOTCRITICAL), +#undef VMF_ENTRY_TRANSPARENCY + +#define VMF_ENTRY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, W(#x) ) + +#ifdef FEATURE_READYTORUN + VMF_ENTRY(LAYOUT_DEPENDS_ON_OTHER_MODULES), +#endif + VMF_ENTRY(DELEGATE), + VMF_ENTRY(FIXED_ADDRESS_VT_STATICS), + VMF_ENTRY(HASLAYOUT), + VMF_ENTRY(ISNESTED), +#ifdef FEATURE_REMOTING + VMF_ENTRY(CANNOT_BE_BLITTED_BY_OBJECT_CLONER), +#endif + VMF_ENTRY(IS_EQUIVALENT_TYPE), + + VMF_ENTRY(HASOVERLAYEDFIELDS), + VMF_ENTRY(HAS_FIELDS_WHICH_MUST_BE_INITED), + VMF_ENTRY(UNSAFEVALUETYPE), + + VMF_ENTRY(BESTFITMAPPING_INITED), + VMF_ENTRY(BESTFITMAPPING), + VMF_ENTRY(THROWONUNMAPPABLECHAR), + + VMF_ENTRY(NOSUPPRESSUNMGDCODEACCESS), + VMF_ENTRY(NO_GUID), + VMF_ENTRY(HASNONPUBLICFIELDS), + VMF_ENTRY(REMOTING_PROXY_ATTRIBUTE), + VMF_ENTRY(PREFER_ALIGN8), + VMF_ENTRY(METHODS_REQUIRE_INHERITANCE_CHECKS), + +#ifdef FEATURE_COMINTEROP + VMF_ENTRY(SPARSE_FOR_COMINTEROP), + VMF_ENTRY(HASCOCLASSATTRIB), + VMF_ENTRY(COMEVENTITFMASK), + VMF_ENTRY(PROJECTED_FROM_WINRT), + VMF_ENTRY(EXPORTED_TO_WINRT), +#endif // FEATURE_COMINTEROP + + VMF_ENTRY(NOT_TIGHTLY_PACKED), + VMF_ENTRY(CONTAINS_METHODIMPLS), +#ifdef FEATURE_COMINTEROP + VMF_ENTRY(MARSHALINGTYPE_MASK), + VMF_ENTRY(MARSHALINGTYPE_INHIBIT), + VMF_ENTRY(MARSHALINGTYPE_FREETHREADED), + VMF_ENTRY(MARSHALINGTYPE_STANDARD), +#endif +#undef VMF_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_SecurityProperties[] = +{ +#define SP_ENTRY(x) NativeImageDumper::EnumMnemonics(DECLSEC_ ## x, W(#x)) + SP_ENTRY(DEMANDS), + SP_ENTRY(ASSERTIONS), + SP_ENTRY(DENIALS), + SP_ENTRY(INHERIT_CHECKS), + SP_ENTRY(LINK_CHECKS), + SP_ENTRY(PERMITONLY), + SP_ENTRY(REQUESTS), + SP_ENTRY(UNMNGD_ACCESS_DEMAND), + SP_ENTRY(NONCAS_DEMANDS), + SP_ENTRY(NONCAS_LINK_DEMANDS), + SP_ENTRY(NONCAS_INHERITANCE), + + SP_ENTRY(NULL_INHERIT_CHECKS), + SP_ENTRY(NULL_LINK_CHECKS), +#undef SP_ENTRY +}; +static NativeImageDumper::EnumMnemonics s_CorFieldAttr[] = +{ +#define CFA_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CFA_ACCESS_ENTRY(x) NativeImageDumper::EnumMnemonics( x, fdFieldAccessMask, W(#x) ) + CFA_ENTRY(fdPrivateScope), + CFA_ENTRY(fdPrivate), + CFA_ENTRY(fdFamANDAssem), + CFA_ENTRY(fdAssembly), + CFA_ENTRY(fdFamily), + CFA_ENTRY(fdFamORAssem), + CFA_ENTRY(fdPublic), +#undef CFA_ACCESS_ENTRY + + CFA_ENTRY(fdStatic), + CFA_ENTRY(fdInitOnly), + CFA_ENTRY(fdLiteral), + CFA_ENTRY(fdNotSerialized), + + CFA_ENTRY(fdSpecialName), + + CFA_ENTRY(fdPinvokeImpl), + + CFA_ENTRY(fdRTSpecialName), + CFA_ENTRY(fdHasFieldMarshal), + CFA_ENTRY(fdHasDefault), + CFA_ENTRY(fdHasFieldRVA), +#undef CFA_ENTRY +}; + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDFlag2[] = +{ +#define MDF2_ENTRY(x) NativeImageDumper::EnumMnemonics( MethodDesc::enum_flag2_ ## x, W("enum_flag2_") W(#x) ) + MDF2_ENTRY(HasStableEntryPoint), + MDF2_ENTRY(HasPrecode), + MDF2_ENTRY(IsUnboxingStub), + MDF2_ENTRY(HasNativeCodeSlot), + MDF2_ENTRY(Transparency_TreatAsSafe), + MDF2_ENTRY(Transparency_Transparent), + MDF2_ENTRY(Transparency_Critical), + MDF2_ENTRY(HostProtectionLinkCheckOnly), + MDF2_ENTRY(CASDemandsOnly), +#undef MDF2_ENTRY +}; + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDC[] = +{ +#define MDC_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define MDC_ENTRY_CLASSIFICATION(x) NativeImageDumper::EnumMnemonics( x, mdcClassification, W(#x) ) + MDC_ENTRY_CLASSIFICATION(mcIL), + MDC_ENTRY_CLASSIFICATION(mcFCall), + MDC_ENTRY_CLASSIFICATION(mcNDirect), + MDC_ENTRY_CLASSIFICATION(mcEEImpl), + MDC_ENTRY_CLASSIFICATION(mcArray), + MDC_ENTRY_CLASSIFICATION(mcInstantiated), +#ifdef FEATURE_COMINTEROP + MDC_ENTRY_CLASSIFICATION(mcComInterop), +#endif // FEATURE_COMINTEROP + MDC_ENTRY_CLASSIFICATION(mcDynamic), +#undef MDC_ENTRY_CLASSIFICATION + + MDC_ENTRY(mdcHasNonVtableSlot), + MDC_ENTRY(mdcMethodImpl), + + // Method is static + MDC_ENTRY(mdcStatic), + MDC_ENTRY(mdcIntercepted), + + MDC_ENTRY(mdcRequiresLinktimeCheck), + + MDC_ENTRY(mdcRequiresInheritanceCheck), + + MDC_ENTRY(mdcParentRequiresInheritanceCheck), + + MDC_ENTRY(mdcDuplicate), + MDC_ENTRY(mdcVerifiedState), + MDC_ENTRY(mdcVerifiable), + MDC_ENTRY(mdcNotInline), + MDC_ENTRY(mdcSynchronized), + MDC_ENTRY(mdcRequiresFullSlotNumber), +#undef MDC_ENTRY +}; + + + +void NativeImageDumper::DumpTypes(PTR_Module module) +{ + _ASSERTE(CHECK_OPT(EECLASSES) || CHECK_OPT(METHODTABLES) + || CHECK_OPT(TYPEDESCS)); + + IF_OPT_OR3(METHODTABLES, EECLASSES, TYPEDESCS) + m_display->StartCategory( "Types" ); + IF_OPT(METHODTABLES) + { + //there may be duplicates in the list. Remove them before moving on. + COUNT_T mtCount = m_discoveredMTs.GetCount(); + +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredMTs.Begin(), + (&*m_discoveredMTs.Begin()) + + (m_discoveredMTs.End() - m_discoveredMTs.Begin())); + PTR_MethodTable* newEnd = std::unique(&*m_discoveredMTs.Begin(), + (&*m_discoveredMTs.Begin()) + + (m_discoveredMTs.End() + - m_discoveredMTs.Begin())); + mtCount = (COUNT_T)(newEnd - &*m_discoveredMTs.Begin()); +#endif + + DisplayStartArray( "MethodTables", NULL, METHODTABLES ); + for(COUNT_T i = 0; i < mtCount; ++i ) + { + PTR_MethodTable mt = m_discoveredMTs[i]; + if( mt == NULL ) + continue; + DumpMethodTable( mt, "MethodTable", module ); + } + + DisplayEndArray( "Total MethodTables", METHODTABLES ); + + DisplayStartArray( "MethodTableSlotChunks", NULL, METHODTABLES ); + { + COUNT_T slotChunkCount = m_discoveredSlotChunks.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredSlotChunks.Begin(), + (&*m_discoveredSlotChunks.Begin()) + + (m_discoveredSlotChunks.End() - m_discoveredSlotChunks.Begin())); + SlotChunk *newEndChunks = std::unique(&*m_discoveredSlotChunks.Begin(), + (&*m_discoveredSlotChunks.Begin()) + + (m_discoveredSlotChunks.End() - m_discoveredSlotChunks.Begin())); + slotChunkCount = (COUNT_T)(newEndChunks - &*m_discoveredSlotChunks.Begin()); +#endif + + for (COUNT_T i = 0; i < slotChunkCount; ++i) + { + DumpMethodTableSlotChunk(m_discoveredSlotChunks[i].addr, m_discoveredSlotChunks[i].nSlots); + } + } + DisplayEndArray( "Total MethodTableSlotChunks", METHODTABLES ); + } + IF_OPT(EECLASSES) + { + DisplayStartArray( "EEClasses", NULL, EECLASSES ); + + //there may be duplicates in the list. Remove them before moving on. + COUNT_T clazzCount = m_discoveredClasses.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredClasses.Begin(), + (&*m_discoveredClasses.Begin()) + + (m_discoveredClasses.End() - m_discoveredClasses.Begin())); + PTR_MethodTable * newEndClazz = std::unique(&*m_discoveredClasses.Begin(), + (&*m_discoveredClasses.Begin()) + +(m_discoveredClasses.End() + -m_discoveredClasses.Begin())); + clazzCount = (COUNT_T)(newEndClazz - &*m_discoveredClasses.Begin()); +#endif + + for(COUNT_T i = 0; i < clazzCount; ++i ) + { + PTR_MethodTable mt = m_discoveredClasses[i]; + if( mt == NULL ) + continue; + DumpEEClassForMethodTable( mt ); + } + + DisplayEndArray( "Total EEClasses", EECLASSES ); //EEClasses + + } + IF_OPT(TYPEDESCS) + { + DisplayStartArray( "TypeDescs", NULL, TYPEDESCS ); + + //there may be duplicates in the list. Remove them before moving on. + COUNT_T tdCount = m_discoveredTypeDescs.GetCount(); +#if !defined(FEATURE_CORESYSTEM) // no STL right now + std::sort(&*m_discoveredTypeDescs.Begin(), + (&*m_discoveredTypeDescs.Begin()) + + (m_discoveredTypeDescs.End() + - m_discoveredTypeDescs.Begin())); + PTR_TypeDesc* newEndTD = std::unique(&*m_discoveredTypeDescs.Begin(), + (&*m_discoveredTypeDescs.Begin()) + +(m_discoveredTypeDescs.End() + -m_discoveredTypeDescs.Begin())); + tdCount = (COUNT_T)(newEndTD - &*m_discoveredTypeDescs.Begin()); +#endif + + for(COUNT_T i = 0; i < tdCount; ++i ) + { + PTR_TypeDesc td = m_discoveredTypeDescs[i]; + if( td == NULL ) + continue; + DumpTypeDesc( td ); + } + + DisplayEndArray( "Total TypeDescs", TYPEDESCS ); //EEClasses + + } + IF_OPT_OR3(EECLASSES, METHODTABLES, TYPEDESCS) + m_display->EndCategory(); //Types +} + +PTR_EEClass NativeImageDumper::GetClassFromMT( PTR_MethodTable mt ) +{ + /* REVISIT_TODO Tue 10/11/2005 + * Handle fixups + */ + _ASSERTE( mt->IsClassPointerValid() ); + PTR_EEClass clazz( mt->GetClass() ); + return clazz; +} +PTR_MethodTable NativeImageDumper::GetParent( PTR_MethodTable mt ) +{ + /* REVISIT_TODO Thu 12/01/2005 + * Handle fixups + */ + PTR_MethodTable parent( mt->m_pParentMethodTable ); + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(parent))); + return parent; +} + +//Counts the FieldDescs in a class. This is all of the non-static and static +//non-literal fields. +SIZE_T NativeImageDumper::CountFields( PTR_MethodTable mt ) +{ + SIZE_T fieldCount = 0; + + HCORENUM hEnum = NULL; + + const Dependency * dep = GetDependencyFromMT(mt); + mdToken classToken = mt->GetCl(); + + _ASSERTE(dep); + _ASSERTE(dep->pImport); + + //Arrays have no token. + if( RidFromToken(classToken) == 0 ) + return 0; + + for (;;) + { + mdToken fields[1]; + ULONG numFields; + + IfFailThrow(dep->pImport->EnumFields( &hEnum, classToken, fields, + 1, &numFields)); + + if (numFields == 0) + break; + + DWORD dwAttr; + IfFailThrow(dep->pImport->GetFieldProps( fields[0], NULL, NULL, 0, + NULL, & dwAttr, NULL, NULL, + NULL, NULL, NULL ) ); + if( !IsFdStatic(dwAttr) || !IsFdLiteral(dwAttr) ) + ++fieldCount; + } + dep->pImport->CloseEnum(hEnum); + return fieldCount; +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromMT( PTR_MethodTable mt ) +{ + if( !mt->IsClassPointerValid() ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(mt)))); + + //the EEClass is a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(mt->GetCanonicalMethodTableFixup()); + return GetDependencyForFixup(rva); + } + PTR_Module module = mt->GetModule(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module)) ) + { + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(module)); + return GetDependencyForFixup(rva); + } + return GetDependencyForModule(module); +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromFD( PTR_FieldDesc fd ) +{ + PTR_MethodTable mt = fd->GetApproxEnclosingMethodTable(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(fd)))); + + //the MethodTable has a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + return GetDependencyForFixup(rva); + } + return GetDependencyFromMT(mt); +} +const NativeImageDumper::Dependency* +NativeImageDumper::GetDependencyFromMD( PTR_MethodDesc md ) +{ + PTR_MethodDescChunk chunk( md->GetMethodDescChunk() ); + PTR_MethodTable mt = chunk->GetMethodTable(); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(md)))); + + //the MethodTable has a fixup. The home for that fixup tells us the + //home for the metadata. + unsigned rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + return GetDependencyForFixup(rva); + } + return GetDependencyFromMT(mt); +} + +BOOL NativeImageDumper::DoWriteFieldAsFixup( const char * name, + unsigned offset, + unsigned fieldSize, TADDR fixup) +{ + if( !CORCOMPILE_IS_POINTER_TAGGED(fixup) ) + return FALSE; + if( UINT_MAX == offset ) + m_display->StartVStructure( name ); + else + m_display->StartVStructureWithOffset( name, offset, fieldSize ); + + WriteElementsFixupBlob( NULL, fixup ); + m_display->EndVStructure(); //name + return TRUE; +} + +void AppendTypeQualifier( CorElementType kind, DWORD rank, SString& buf ) +{ + switch( kind ) + { + case ELEMENT_TYPE_BYREF : + buf.Append( W("&") ); + break; + case ELEMENT_TYPE_PTR : + buf.Append( W("*") ); + break; + case ELEMENT_TYPE_SZARRAY : + buf.Append( W("[]") ); + break; + case ELEMENT_TYPE_ARRAY : + if( rank == 1 ) + { + buf.Append( W("[*]") ); + } + else + { + buf.Append( W("[") ); + for( COUNT_T i = 0; i < rank; ++i ) + buf.Append( W(",")); + buf.Append( W("]") ); + } + break; + default : + break; + } +} +void NativeImageDumper::TypeDescToString( PTR_TypeDesc td, SString& buf ) +{ + _ASSERTE(!(PTR_TO_TADDR(td) & 0x2)); + if( td->IsGenericVariable() ) + { + PTR_TypeVarTypeDesc tvtd( PTR_TO_TADDR(td) ); + //From code:TypeString::AppendType + mdGenericParam token = tvtd->GetToken(); + PTR_Module module(tvtd->GetModule()); + IMetaDataImport2 * pImport; + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module)) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(td)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + return; + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(module)); + pImport = GetDependencyForFixup(rva)->pImport; + } + } + else + { + pImport = GetDependencyForModule(module)->pImport; + } + AppendTokenName(token, buf, pImport); + } + else if( ELEMENT_TYPE_FNPTR == td->GetInternalCorElementType() ) + { + PTR_FnPtrTypeDesc fptd( PTR_TO_TADDR(td) ); + buf.Append( W("(fnptr)") ); + } + else if( td->HasTypeParam() || td->IsArray() ) + { + //either a Parameter or an Array. + PTR_ParamTypeDesc ptd(PTR_TO_TADDR(td)); + TypeHandle elemType; + /* REVISIT_TODO Thu 10/5/2006 + * Do I need to find a rank somewhere in the TypeDesc? + */ + unsigned rank; + if( td->IsArray() ) + { + //td->HasTypeParam() may also be true. + PTR_MethodTable mt = ptd->m_TemplateMT.GetValue(); + _ASSERTE( PTR_TO_TADDR(mt) ); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + if (!isSelf(GetDependencyForPointer(PTR_TO_TADDR(ptd)))) + { + //this is an RVA from another hardbound dependency. We cannot decode it + buf.Append(W("OUT_OF_MODULE_FIXUP")); + } + else + { + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + FixupBlobToString(rva, buf); + } + return; + } + else + { + _ASSERTE( !CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ); + MethodTableToString( mt, buf ); + rank = PTR_ArrayTypeDesc(PTR_TO_TADDR(ptd))->GetRank(); + } + } + else + { + _ASSERTE(td->HasTypeParam()); + TypeHandle th(ptd->GetTypeParam()); + _ASSERTE( !CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) ); + _ASSERTE( th.AsTAddr() ); + TypeHandleToString(th, buf); + rank = 0; + } + AppendTypeQualifier( td->GetInternalCorElementType(), rank, buf ); + } + else + { + //generic typedesc? + EnumFlagsToString( (int)td->GetInternalCorElementType(), s_CorElementType, dim(s_CorElementType), + W(""), buf ); + } +} +void NativeImageDumper::TypeHandleToString( TypeHandle th, SString& buf ) +{ + TADDR arg = th.AsTAddr(); + /* REVISIT_TODO Thu 10/5/2006 + * Is this constant somewhere? + */ + //0x2 is the subtle hint that this is a typedesc. code:TypeHandle::AsTypeDesc + if( arg & 0x2 ) + { + PTR_TypeDesc argTD( arg & ~0x2 ); + TypeDescToString( argTD, buf ); + } + else + { + PTR_MethodTable argMT( th.AsTAddr() ); + MethodTableToString( argMT, buf ); + } +} + +void NativeImageDumper::DoWriteFieldTypeHandle( const char * name, + unsigned offset, + unsigned fieldSize, + TypeHandle th ) +{ + TempBuffer buf; + TADDR ptr = th.AsTAddr(); + if( DoWriteFieldAsFixup(name, offset, fieldSize, th.AsTAddr() ) ) + return; + else + { + TypeHandleToString(th, buf); + + buf.Append( W(" (from TypeHandle)") ); + /* REVISIT_TODO Fri 10/14/2005 + * Do a better job of this + */ + if( offset == UINT_MAX ) + { + m_display->WriteElementPointerAnnotated( name, + DataPtrToDisplay(ptr), + (const WCHAR*) buf ); + } + else + { + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DataPtrToDisplay(ptr), + (const WCHAR*) buf ); + } + } +} +void NativeImageDumper::WriteElementTypeHandle( const char * name, + TypeHandle th ) +{ + DoWriteFieldTypeHandle( name, UINT_MAX, UINT_MAX, th ); +} + +void NativeImageDumper::DoDumpFieldStub( PTR_Stub stub, unsigned offset, + unsigned fieldSize, const char * name ) +{ + _ASSERTE(CHECK_OPT(EECLASSES)); + if( stub == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DPtrToPreferredAddr(stub), + sizeof(*stub) ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump stub + */ + m_display->EndStructure(); + } +} + +#ifdef FEATURE_COMINTEROP +void NativeImageDumper::DoDumpComPlusCallInfo( PTR_ComPlusCallInfo compluscall ) +{ + m_display->StartStructure( "ComPlusCallInfo", + DPtrToPreferredAddr(compluscall), + sizeof(*compluscall) ); + + DisplayWriteFieldPointer( m_pILStub, compluscall->m_pILStub, + ComPlusCallInfo, ALWAYS); + /* REVISIT_TODO Fri 12/16/2005 + * Coverage read stub? + */ + WriteFieldMethodTable(m_pInterfaceMT, + compluscall->m_pInterfaceMT, + ComPlusCallInfo, ALWAYS); + + PTR_MethodDesc pEventProviderMD = PTR_MethodDesc((TADDR)compluscall->m_pEventProviderMD); + WriteFieldMethodDesc(m_pEventProviderMD, + pEventProviderMD, + ComPlusCallInfo, ALWAYS); + DisplayWriteFieldInt( m_cachedComSlot, compluscall->m_cachedComSlot, + ComPlusCallInfo, ALWAYS ); + + /* REVISIT_TODO Fri 12/16/2005 + * Dump these as mnemonics + */ + DisplayWriteFieldInt( m_flags, compluscall->m_flags, + ComPlusCallInfo, ALWAYS ); + WriteFieldMethodDesc( m_pStubMD, + compluscall->m_pStubMD.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(ComPlusCallInfo, compluscall, m_pStubMD)), + ComPlusCallInfo, ALWAYS ); + +#ifdef _TARGET_X86_ + DisplayWriteFieldInt( m_cbStackArgumentSize, compluscall->m_cbStackArgumentSize, + ComPlusCallInfo, ALWAYS ); + + DisplayWriteFieldPointer( m_pRetThunk, + DataPtrToDisplay((TADDR)compluscall->m_pRetThunk), + ComPlusCallInfo, ALWAYS ); +#endif + m_display->EndStructure(); //ComPlusCallInfo +} +#endif // FEATURE_COMINTEROP + +void NativeImageDumper::DoWriteFieldStr( PTR_BYTE ptr, const char * name, + unsigned offset, unsigned fieldSize ) +{ + if( ptr == NULL ) + { + if( UINT_MAX == offset ) + m_display->WriteElementPointer( name, NULL ); + else + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + /* REVISIT_TODO Wed 03/22/2006 + * Obviously this does the wrong thing for UTF-8. + */ + TempBuffer buf; + BYTE b; + TADDR taddr = DPtrToPreferredAddr(ptr); + PTR_BYTE current = ptr; + /* REVISIT_TODO Mon 03/27/2006 + * Actually handle UTF-8 properly + */ + while( (b = *current++) != 0 ) + buf.Append( (WCHAR)b ); + /* REVISIT_TODO Wed 03/22/2006 + * This seems way way way more verbose than it needs to be. + */ + if( UINT_MAX == offset ) + { + m_display->StartStructure( name, DataPtrToDisplay(taddr), + current - ptr ); + } + else + { + m_display->StartStructureWithOffset( name, offset, fieldSize, + DataPtrToDisplay(taddr), + current - ptr ); + } + DisplayWriteElementStringW( "Value", (const WCHAR *)buf, ALWAYS ); + m_display->EndStructure(); + /* + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + taddr, (const WCHAR *)buf ); + */ + } +} +void NativeImageDumper::WriteFieldDictionaryLayout(const char * name, + unsigned offset, + unsigned fieldSize, + PTR_DictionaryLayout layout, + IMetaDataImport2 * import) +{ + if( layout == NULL ) + { + m_display->WriteFieldPointer(name, NULL, offset, fieldSize); + return; + } + m_display->StartVStructureWithOffset( name, offset, fieldSize ); + DisplayStartArray( "DictionaryLayouts", NULL, ALWAYS ); + do + { + DisplayStartStructure( "DictionaryLayout", DPtrToPreferredAddr(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1), ALWAYS ); + + + DisplayWriteFieldPointer( m_pNext, DataPtrToDisplay((TADDR)layout->m_pNext), + DictionaryLayout, ALWAYS ); + DisplayWriteFieldInt( m_numSlots, layout->m_numSlots, + DictionaryLayout, ALWAYS ); + DisplayStartArrayWithOffset( m_slots, NULL, DictionaryLayout, ALWAYS ); + for( unsigned i = 0; i < layout->m_numSlots; ++i ) + { + PTR_DictionaryEntryLayout entry( PTR_HOST_MEMBER_TADDR(DictionaryLayout, layout, m_slots) + (i * sizeof(DictionaryEntryLayout)) ); + DisplayStartStructure( "DictionaryEntryLayout", + DPtrToPreferredAddr(entry), sizeof(*entry), + ALWAYS ); + const char * kind = NULL; + switch( entry->GetKind() ) + { +#define KIND_ENTRY(x) case x : kind = # x ; break + KIND_ENTRY(EmptySlot); + KIND_ENTRY(TypeHandleSlot); + KIND_ENTRY(MethodDescSlot); + KIND_ENTRY(MethodEntrySlot); + KIND_ENTRY(ConstrainedMethodEntrySlot); + KIND_ENTRY(DispatchStubAddrSlot); + KIND_ENTRY(FieldDescSlot); +#undef KIND_ENTRY + default: + _ASSERTE( !"unreachable" ); + } + DisplayWriteElementString( "Kind", kind, ALWAYS ); + DisplayWriteElementPointer( "Signature", DPtrToPreferredAddr(entry->m_signature), ALWAYS ); + DisplayEndStructure( ALWAYS ); //DictionaryEntryLayout + } + DisplayEndArray( "Total Dictionary Entries", ALWAYS ); //m_slots + DisplayEndStructure( ALWAYS ); //Layout + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + }while( layout != NULL ); + DisplayEndArray( "Total Dictionary Layouts", ALWAYS ); //DictionaryLayouts + + + DisplayEndVStructure( ALWAYS ); // name +} +void NativeImageDumper::DoWriteFieldFieldDesc( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_FieldDesc fd ) +{ + if( fd == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else + { + TempBuffer buf; + FieldDescToString( fd, buf ); + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(fd), + (const WCHAR*) buf ); + } + +} +void NativeImageDumper::DoWriteFieldMethodDesc( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_MethodDesc md ) +{ + if( md == NULL ) + { + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else if( DoWriteFieldAsFixup(name, offset, fieldSize, PTR_TO_TADDR(md)) ) + { + return; + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(md), + (const WCHAR*) buf ); + } +} + +void NativeImageDumper::EntryPointToString( TADDR pEntryPoint, + SString& buf ) +{ + const Dependency * dependency = GetDependencyForPointer(pEntryPoint); + + PTR_MethodDesc md; + if (dependency->pModule->IsZappedPrecode(pEntryPoint)) + { + md = dac_cast<PTR_MethodDesc>(Precode::GetPrecodeFromEntryPoint(pEntryPoint)->GetMethodDesc()); + } + else + { + PTR_Module module = (TADDR)m_decoder.GetPersistedModuleImage(); + PTR_NGenLayoutInfo pNgenLayout = module->GetNGenLayoutInfo(); + DWORD rva = (DWORD)(pEntryPoint - PTR_TO_TADDR(m_decoder.GetBase())); + + for (int iRange = 0; iRange < 2; iRange++) + { + if (pNgenLayout->m_CodeSections[iRange].IsInRange(pEntryPoint)) + { + int MethodIndex = NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod(rva, pNgenLayout->m_pRuntimeFunctions[iRange], 0, pNgenLayout->m_nRuntimeFunctions[iRange] - 1); + if (MethodIndex >= 0) + { +#ifdef WIN64EXCEPTIONS + while (pNgenLayout->m_MethodDescs[iRange][MethodIndex] == 0) + MethodIndex--; +#endif + + PTR_RUNTIME_FUNCTION pRuntimeFunction = pNgenLayout->m_pRuntimeFunctions[iRange] + MethodIndex; + + md = NativeUnwindInfoLookupTable::GetMethodDesc(pNgenLayout, pRuntimeFunction, PTR_TO_TADDR(m_decoder.GetBase())); + break; + } + } + } + } + + MethodDescToString(md, buf); +} + +void NativeImageDumper::MethodDescToString( PTR_MethodDesc md, + SString& buf ) +{ + if( md == NULL ) + buf.Append( W("mdMethodDefNil") ); + else if( md->IsILStub() ) + buf.AppendUTF8(md->AsDynamicMethodDesc()->GetName()); + else + { + //write the name to a temporary location, since I'm going to insert it + //into the middle of a signature. + TempBuffer tempName; + + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))); + //work back to the EEClass. That gives us the context for the token. + PTR_MethodDescChunk chunk(md->GetMethodDescChunk()); + //chunk is implicitly remapped because it's calculated from the pointer + //to MD. + PTR_MethodTable mt = chunk->GetMethodTable(); + const Dependency * dependency; + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + //This code will not work for out of module dependencies. + _ASSERTE(isSelf(GetDependencyForPointer(PTR_TO_TADDR(md)))); + + RVA rva = CORCOMPILE_UNTAG_TOKEN(PTR_TO_TADDR(mt)); + dependency = GetDependencyForFixup(rva); + mt = NULL; //make sure we don't use this for anything. + } + else + dependency = GetDependencyFromMT(mt); + + _ASSERTE(dependency); + + + /* REVISIT_TODO Fri 10/13/2006 + * Don't I need the array type name here? + */ + _ASSERTE(dependency->pImport); + if( md->GetClassification() == mcArray ) + { + + //We don't need to append the dependency all the time. + //MethodTableToString() already appends it to the MethodTable. + //Only do it in cases where we don't call MethodTableToString. + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, tempName, + m_manifestImport ); + tempName.Append(W("!")); + } + + _ASSERTE(PTR_TO_TADDR(mt)); + MethodTableToString( mt, tempName ); + tempName.Append( W("::") ); + + //there are four hard coded names for array method descs, use these + //instead of the token. + PTR_ArrayMethodDesc amd(PTR_TO_TADDR(md)); + tempName.AppendUTF8( amd->GetMethodName() ); + } + else + { + //if we have a MethodTable, use that and compose the name + //ourselves. That way we can get generic arguments. + if( mt ) + { + ULONG size; + MethodTableToString( mt, tempName ); + tempName.Append( W("::") ); + IfFailThrow(dependency->pImport->GetMethodProps(md->GetMemberDef(), NULL, bigBuffer, + bigBufferSize, &size, NULL, NULL, NULL, NULL, + NULL)); + tempName.Append(bigBuffer); + } + else + { + //We don't need to append the dependency all the time. + //MethodTableToString() already appends it to the MethodTable. + //Only do it in cases where we don't call MethodTableToString. + if( !isSelf(dependency) ) + { + AppendTokenName( dependency->entry->dwAssemblyRef, tempName, + m_manifestImport ); + tempName.Append(W("!")); + } + AppendTokenName( md->GetMemberDef(), tempName, dependency->pImport ); + } + + if( mcInstantiated == md->GetClassification() ) + { + PTR_InstantiatedMethodDesc imd(PTR_TO_TADDR(md)); + unsigned numArgs = imd->m_wNumGenericArgs; + PTR_Dictionary dict(imd->IMD_GetMethodDictionary()); + if( dict != NULL ) + { + DictionaryToArgString( dict, numArgs, tempName ); + } + } + + PCCOR_SIGNATURE pvSigBlob; + ULONG cbSigBlob; + IfFailThrow(dependency->pImport->GetMethodProps(md->GetMemberDef(), + NULL, + NULL, + 0, + NULL, + NULL, + &pvSigBlob, + &cbSigBlob, + NULL, + NULL)); + + + CQuickBytes prettySig; + ReleaseHolder<IMDInternalImport> pInternal; + IfFailThrow(GetMDInternalInterfaceFromPublic(dependency->pImport, IID_IMDInternalImport, + (void**)&pInternal)); + StackScratchBuffer buffer; + const ANSI * ansi = tempName.GetANSI(buffer); + ansi = PrettyPrintSig(pvSigBlob, cbSigBlob, ansi, &prettySig, pInternal, NULL); + tempName.SetANSI( ansi ); + } + buf.Append(tempName); + } +} +void NativeImageDumper::WriteElementMethodDesc( const char * name, + PTR_MethodDesc md ) +{ + if( md == NULL ) + { + m_display->WriteElementPointer( name, NULL ); + } + else + { + TempBuffer buf; + MethodDescToString( md, buf ); + m_display->WriteElementPointerAnnotated( name, DPtrToPreferredAddr(md), + (const WCHAR*) buf ); + } +} +void NativeImageDumper::FieldDescToString( PTR_FieldDesc fd, SString& buf ) +{ + FieldDescToString( fd, mdFieldDefNil, buf ); +} +void NativeImageDumper::FieldDescToString( PTR_FieldDesc fd, mdFieldDef tok, + SString& buf ) +{ + IF_OPT(DISABLE_NAMES) + { + buf.Append( W("Disabled") ); + return; + } + if( fd == NULL ) + { + if( tok == mdFieldDefNil ) + buf.Append( W("mdFieldDefNil") ); + else + AppendTokenName( tok, buf ); + } + else + { + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(fd))); + IMetaDataImport2 * importMD = NULL; + if( !isInRange(PTR_TO_TADDR(fd)) ) + { + const Dependency * dependency = GetDependencyFromFD(fd); + _ASSERTE(dependency); + AppendTokenName( dependency->entry->dwAssemblyRef, buf, + m_manifestImport ); + buf.Append(W("!")); + importMD = dependency->pImport; + _ASSERTE(importMD); + + } + else + { + importMD = m_import; + } + AppendTokenName( fd->GetMemberDef(), buf, importMD ); + } +} + +void NativeImageDumper::DoWriteFieldAsHex( const char * name, unsigned offset, + unsigned fieldSize, PTR_BYTE ptr, + unsigned dataLen ) +{ + TempBuffer buffer; + for( unsigned i = 0; i < dataLen; ++i ) + { + unsigned char b = ptr[i]; + buffer.AppendPrintf( W("%02x%02x"), (b & 0xf0) >> 4, b & 0xf ); + } + if( offset == UINT_MAX ) + { + m_display->WriteElementStringW( name, (const WCHAR *)buffer ); + } + else + { + m_display->WriteFieldStringW( name, offset, fieldSize, + (const WCHAR *)buffer ); + } +} +void NativeImageDumper::WriteElementMDToken( const char * name, mdToken token ) +{ + DoWriteFieldMDToken( name, UINT_MAX, UINT_MAX, token ); +} +void NativeImageDumper::DoWriteFieldMDToken( const char * name, unsigned offset, + unsigned fieldSize, mdToken token, + IMetaDataImport2 * import ) +{ + TempBuffer buf; + if( RidFromToken(token) == mdTokenNil ) + { + AppendNilToken( token, buf ); + } + else + { + AppendToken( token, buf, import ); + } + if( UINT_MAX == offset ) + m_display->WriteElementEnumerated( name, token, (const WCHAR *)buf ); + else + { + m_display->WriteFieldEnumerated(name, offset, fieldSize, token, + (const WCHAR*)buf); + } +} + +void NativeImageDumper::WriteElementMethodTable( const char * name, + PTR_MethodTable mt ) +{ + DoWriteFieldMethodTable( name, UINT_MAX, UINT_MAX, mt ); +} +void NativeImageDumper::DoWriteFieldMethodTable( const char * name, + unsigned offset, + unsigned fieldSize, + PTR_MethodTable mt ) +{ + if( mt == NULL ) + { + if( UINT_MAX == offset ) + m_display->WriteElementPointer( name, NULL ); + else + m_display->WriteFieldPointer( name, offset, fieldSize, NULL ); + } + else if( DoWriteFieldAsFixup( name, offset, fieldSize, PTR_TO_TADDR(mt) ) ) + { + return; + } + else + { + TempBuffer buf; + MethodTableToString( mt, buf ); + if( UINT_MAX == offset ) + { + + m_display->WriteElementPointerAnnotated( name, + DPtrToPreferredAddr(mt), + (const WCHAR*) buf ); + } + else + { + m_display->WriteFieldPointerAnnotated( name, offset, fieldSize, + DPtrToPreferredAddr(mt), + (const WCHAR*) buf ); + } + } +} + +const char * s_VTSCallbackNames[] = +{ +#define VTSCB_ENTRY(x) # x + VTSCB_ENTRY(VTS_CALLBACK_ON_SERIALIZING), + VTSCB_ENTRY(VTS_CALLBACK_ON_SERIALIZED), + VTSCB_ENTRY(VTS_CALLBACK_ON_DESERIALIZING), + VTSCB_ENTRY(VTS_CALLBACK_ON_DESERIALIZED), +#undef VTSCB_ENTRY +}; +void NativeImageDumper::DumpFieldDesc( PTR_FieldDesc fd, const char * name ) +{ + DisplayStartStructure( name, DPtrToPreferredAddr(fd), sizeof(*fd), + ALWAYS ); + WriteFieldMethodTable( m_pMTOfEnclosingClass, + fd->GetApproxEnclosingMethodTable(), FieldDesc, ALWAYS ); + m_display->WriteFieldUInt( "m_mb", offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->GetMemberDef() ); + m_display->WriteFieldFlag( "m_isStatic", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isStatic ); + m_display->WriteFieldFlag( "m_isThreadLocal", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isThreadLocal ); +#if defined(FEATURE_REMOTING) + m_display->WriteFieldFlag( "m_isContextLocal", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isContextLocal ); +#endif + m_display->WriteFieldFlag( "m_isRVA", offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_isRVA ); + + { + TempBuffer buf; + EnumFlagsToString( fd->m_prot, s_CorFieldAttr, + _countof(s_CorFieldAttr), W(" "), buf ); + m_display->WriteFieldEnumerated( "m_prot", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_prot, (const WCHAR *)buf ); + } + m_display->WriteFieldFlag( "m_requiresFullMbValue", + offsetof(FieldDesc, m_dword1), + fieldsize(FieldDesc, m_dword1), + fd->m_requiresFullMbValue ); + m_display->WriteFieldInt( "m_dwOffset", + offsetof(FieldDesc, m_dword2), + fieldsize(FieldDesc, m_dword2), + fd->m_dwOffset ); + DoWriteFieldCorElementType( "m_type", + offsetof(FieldDesc, m_dword2), + fieldsize(FieldDesc, m_dword2), + (CorElementType)fd->m_type ); +#ifdef _DEBUG + WriteFieldStr( m_debugName, PTR_BYTE(TO_TADDR(fd->m_debugName)), + FieldDesc, ALWAYS ); +#endif + DisplayEndStructure( ALWAYS ); //name +} +#if defined(FEATURE_REMOTING) +NativeImageDumper::EnumMnemonics s_XADOptFlags[] = +{ +#define XAD_ENTRY(x) NativeImageDumper::EnumMnemonics( RemotableMethodInfo:: x, W(#x) ) + XAD_ENTRY(XAD_FLAGS_INITIALIZED), + XAD_ENTRY(XAD_NOT_OPTIMIZABLE), + XAD_ENTRY(XAD_BLITTABLE_ARGS), + XAD_ENTRY(XAD_BLITTABLE_RET), + + //this is actually the OR of the three below this. It is a "flag" so + //it gets cleared out. + XAD_ENTRY(XAD_RET_GC_REF), + XAD_ENTRY(XAD_RET_FLOAT), + XAD_ENTRY(XAD_RET_DOUBLE), +#ifdef FEATURE_HFA + XAD_ENTRY(XAD_RET_HFA_TYPE), +#endif + + XAD_ENTRY(XAD_METHOD_IS_VIRTUAL), + XAD_ENTRY(XAD_ARGS_HAVE_A_FLOAT), + +#undef XAD_ENTRY +}; +#endif + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void +NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name, + PTR_Module module ) +{ + _ASSERTE(NULL != mt); + TADDR start, end; + bool haveCompleteExtents = true; + PTR_EEClass clazz = NULL; + if( !mt->IsCanonicalMethodTable() && CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt->GetCanonicalMethodTable())) ) + { + /* REVISIT_TODO Wed 02/01/2006 + * GetExtent requires the class in order to compute GetInstAndDictSize. + * If the EEClass isn't present, I cannot compute the size. If we are + * in this case, skip all of the generic dictionaries. + */ + haveCompleteExtents = false; + TempBuffer buf; + MethodTableToString( mt, buf ); + m_display->ErrorPrintF( "WARNING! MethodTable %S is generic but is not hard bound to its EEClass. Cannot compute generic dictionary sizes.\n", (const WCHAR *)buf ); + } + else if( !m_isMscorlibHardBound ) + { + /* REVISIT_TODO Mon 8/20/2007 + * If we're not hard bound to mscorlib, most things don't work. They depend on knowing what + * g_pObjectClass is. Without the hard binding to mscorlib, I can't figure that out. + */ + haveCompleteExtents = false; + } + if( haveCompleteExtents ) + { + mt->GetSavedExtent(&start, &end); + clazz = mt->GetClass(); + } + else + { + start = PTR_TO_TADDR(mt); + end = start + sizeof(*mt); + } + IF_OPT(METHODTABLES) + { + m_display->StartStructureWithNegSpace( name, DPtrToPreferredAddr(mt), + DataPtrToDisplay(start), end - start ); + } + + IF_OPT(METHODTABLES) + { + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, ALWAYS ); + } + if( mt->ContainsPointers() ) + { + PTR_CGCDesc cgc = CGCDesc::GetCGCDescFromMT(mt); + unsigned size = (unsigned)cgc->GetSize(); + /* REVISIT_TODO Tue 12/13/2005 + * Does anyone actually care about what's inside here? + */ + m_display->WriteFieldEmpty( "CGCDesc", ~size + 1, size ); + } + } + + /* XXX Mon 10/24/2005 + * The MT might have a component size as the low WORD of the m_dwFlags + * field, if it doesn't then that field instead represents a number of + * flags, which we know as the "low flags" + */ + if (mt->HasComponentSize()) + { + DisplayWriteElementInt( "ComponentSize", mt->RawGetComponentSize(), + METHODTABLES ); + } + else + { + DisplayWriteFieldEnumerated( m_dwFlags, mt->m_dwFlags & 0xFFFF, MethodTable, + s_MTFlagsLow, W(", "), METHODTABLES ); + } + + /* XXX Fri 10/07/2005 + * The low WORD of the flags is used for either a component size or flags + * (see above), the high WORD is always flags. If this changes then this + * might be busted. + */ + DisplayWriteFieldEnumerated( m_dwFlags, mt->m_dwFlags & ~0xFFFF, MethodTable, + s_MTFlagsHigh, W(", "), METHODTABLES ); + + DisplayWriteFieldInt( m_BaseSize, mt->m_BaseSize, MethodTable, + METHODTABLES ); + + DisplayWriteFieldEnumerated( m_wFlags2, mt->m_wFlags2, MethodTable, + s_MTFlags2, W(", "), METHODTABLES ); + + DisplayWriteFieldInt( m_wToken, mt->m_wToken, MethodTable, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumVirtuals, mt->m_wNumVirtuals, MethodTable, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumInterfaces, mt->m_wNumInterfaces, MethodTable, + METHODTABLES ); + + + + PTR_MethodTable parent = mt->m_pParentMethodTable; + if( parent == NULL ) + { + DisplayWriteFieldPointer( m_pParentMethodTable, NULL, MethodTable, + METHODTABLES ); + } + else + { + IF_OPT(METHODTABLES) + { + DoWriteFieldMethodTable( "m_pParentMethodTable", + offsetof(MethodTable, m_pParentMethodTable), + fieldsize(MethodTable, m_pParentMethodTable), + mt->GetParentMethodTable() ); + } + } + DisplayWriteFieldPointer( m_pLoaderModule, + DPtrToPreferredAddr(mt->GetLoaderModule()), + MethodTable, METHODTABLES ); + + PTR_MethodTableWriteableData wd = mt->m_pWriteableData; + _ASSERTE(wd != NULL); + DisplayStartStructureWithOffset( m_pWriteableData, DPtrToPreferredAddr(wd), + sizeof(*wd), MethodTable, METHODTABLES ); + DisplayWriteFieldEnumerated( m_dwFlags, wd->m_dwFlags, + MethodTableWriteableData, s_WriteableMTFlags, + W(", "), METHODTABLES ); + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(wd->m_hExposedClassObject), + MethodTableWriteableData, METHODTABLES ); + _ASSERTE(wd->m_hExposedClassObject == 0); + DisplayEndStructure( METHODTABLES ); //m_pWriteableData + + if( !mt->IsCanonicalMethodTable() ) + { + WriteFieldMethodTable( m_pCanonMT, mt->GetCanonicalMethodTable(), + MethodTable, METHODTABLES ); + } + else + { + DisplayWriteFieldPointer( m_pEEClass, DPtrToPreferredAddr(mt->GetClass()), + MethodTable, METHODTABLES ); + } + + if( mt->IsArray() ) + { + WriteFieldTypeHandle( m_ElementTypeHnd, + mt->GetApproxArrayElementTypeHandle(), + MethodTable, METHODTABLES ); + } + + if( mt->HasPerInstInfo() && haveCompleteExtents ) + { + //print out the generics dictionary info, and then print out + //the contents of those dictionaries. + PTR_GenericsDictInfo di = mt->GetGenericsDictInfo(); + _ASSERTE(NULL != di); + + DisplayStartStructure("GenericsDictInfo", DPtrToPreferredAddr(di), sizeof(*di), METHODTABLES); + + DisplayWriteFieldInt( m_wNumDicts, di->m_wNumDicts, GenericsDictInfo, + METHODTABLES ); + DisplayWriteFieldInt( m_wNumTyPars, di->m_wNumTyPars, + GenericsDictInfo, METHODTABLES); + DisplayEndStructure( METHODTABLES ); //GenericsDictInfo + + + DPTR(PTR_Dictionary) perInstInfo = mt->GetPerInstInfo(); + + DisplayStartStructure( "PerInstInfo", + DPtrToPreferredAddr(perInstInfo), + mt->GetPerInstInfoSize(), + METHODTABLES ); + /* XXX Tue 10/11/2005 + * Only dump this type's dictionary, rather than the inherited + * dictionaries. (there are multiple entries in m_pPerInstInfo, but + * only print the last one, which is the one for this class). + * cloned from Genericdict.cpp + */ + PTR_Dictionary currentDictionary(mt->GetDictionary()); + if( currentDictionary != NULL ) + { + PTR_DictionaryEntry entry(currentDictionary->EntryAddr(0)); + + PTR_DictionaryLayout layout( clazz->GetDictionaryLayout() ); + + DisplayStartStructure( "Dictionary", + DPtrToPreferredAddr(currentDictionary), + //if there is a layout, use it to compute + //the size, otherwise there is just the one + //entry. + DictionaryLayout::GetFirstDictionaryBucketSize(mt->GetNumGenericArgs(), layout), + METHODTABLES ); + + DisplayStartArrayWithOffset( m_pEntries, NULL, Dictionary, + METHODTABLES ); + + /* REVISIT_TODO Thu 12/15/2005 + * use VERBOSE_TYPES here. + */ + _ASSERTE(CHECK_OPT(METHODTABLES)); + + //for each generic arg, there is a type handle slot + for( unsigned i = 0; i < mt->GetNumGenericArgs(); ++i ) + DumpDictionaryEntry("Entry", TypeHandleSlot, entry + i); + + //now check for a layout. If it is present, then there are more + //entries. + if( layout != NULL && (layout->GetNumUsedSlots() > 0) ) + { + unsigned numUsedSlots = layout->GetNumUsedSlots(); + for( unsigned i = 0; i < numUsedSlots; ++i ) + { + //DictionaryLayout::GetEntryLayout + PTR_DictionaryEntryLayout entryLayout(layout->GetEntryLayout(i)); + + //Dictionary::GetSlotAddr + PTR_DictionaryEntry ent(currentDictionary->EntryAddr(mt->GetNumGenericArgs() + i)); + + DumpDictionaryEntry( "Entry", entryLayout->GetKind(), ent ); + } + } + if( layout != NULL ) + { + /* REVISIT_TODO Thu 12/15/2005 + * Where is this data? + */ + } + DisplayEndArray( "Total Per instance Info", + METHODTABLES ); //m_pEntries + DisplayEndStructure( METHODTABLES ); //Dictionary + } + DisplayEndStructure( METHODTABLES ); //m_pPerInstInfo + } + +#ifdef _DEBUG + WriteFieldStr( debug_m_szClassName, + PTR_BYTE(TO_TADDR(mt->debug_m_szClassName)), MethodTable, + METHODTABLES ); +#if 0 //already dumping the optional member + PTR_InterfaceInfo imap( TO_TADDR(mt->m_pIMapDEBUG) ); + /* REVISIT_TODO Mon 10/24/2005 + * Dump interface map + */ + DisplayStartArrayWithOffset( m_pIMapDEBUG, NULL, MethodTable, + METHODTABLES ); + DisplayEndArray( "Total Interfaces", METHODTABLES ); +#endif +#endif + + if( mt->HasDispatchMapSlot() ) + { + PTR_DispatchMap dispatchMap(mt->GetDispatchMap()); + + DisplayStartStructure( "DispatchMap", + DPtrToPreferredAddr(dispatchMap), + DispatchMap::GetObjectSize(dispatchMap->GetMapSize()), + METHODTABLES ); + + IF_OPT(VERBOSE_TYPES ) + { + DispatchMap::Iterator iter(mt); + DisplayStartArray( "DispatchMap", NULL, VERBOSE_TYPES ); + while( iter.Next() ) + { + DispatchMapEntry * ent = iter.Entry(); + + DisplayStartElement( "Entry", METHODTABLES ); + DisplayStartVStructure( "TypeID", METHODTABLES ); + DispatchMapTypeID typeID = ent->GetTypeID(); + if( typeID.IsThisClass() ) + DisplayWriteElementFlag("IsThisClass", true, METHODTABLES ); + else if( typeID.IsImplementedInterface() ) + { + DisplayWriteElementFlag( "IsImplementedInterface", + true, METHODTABLES ); + DisplayWriteElementInt( "GetInterfaceNum", + typeID.GetInterfaceNum(), METHODTABLES ); + } + DisplayEndStructure( METHODTABLES ); //TypeID + m_display->WriteElementInt( "SlotNumber", + ent->GetSlotNumber() ); + DisplayWriteElementInt( "TargetSlotNumber", + ent->GetSlotNumber(), METHODTABLES ); + + m_display->EndElement(); //Entry + } + //DispatchMap + DisplayEndArray("Total Dispatch Map Entries", METHODTABLES ); + } + else + { + CoverageRead(PTR_TO_TADDR(dispatchMap), + DispatchMap::GetObjectSize(dispatchMap->GetMapSize())); + } + + DisplayEndStructure( METHODTABLES ); //DispatchMap + } + + IF_OPT( METHODTABLES ) + { + m_display->StartStructureWithOffset("Vtable", + mt->GetVtableOffset(), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE), + DataPtrToDisplay(PTR_TO_TADDR(mt) + mt->GetVtableOffset()), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE)); + + + MethodTable::VtableIndirectionSlotIterator itIndirect = mt->IterateVtableIndirectionSlots(); + while (itIndirect.Next()) + { + SlotChunk sc; + sc.addr = itIndirect.GetIndirectionSlot(); + sc.nSlots = (WORD)itIndirect.GetNumSlots(); + m_discoveredSlotChunks.AppendEx(sc); + } + + IF_OPT(VERBOSE_TYPES) + { + DisplayStartList( W("[%-4s]: %s (%s)"), ALWAYS ); + for( unsigned i = 0; i < mt->GetNumVtableIndirections(); ++i ) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", i, ALWAYS ); + PTR_PCODE tgt = mt->GetVtableIndirections()[i]; + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(dac_cast<TADDR>(tgt)), + ALWAYS ); + DisplayWriteElementString( "Type", "chunk indirection", + ALWAYS ); + DisplayEndElement( ALWAYS ); //Slot + } + + if (mt->HasNonVirtualSlotsArray()) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", -1, ALWAYS ); + PTR_PCODE tgt = mt->GetNonVirtualSlotsArray(); + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(dac_cast<TADDR>(tgt)), + ALWAYS ); + DisplayWriteElementString( "Type", "non-virtual chunk indirection", + ALWAYS ); + DisplayEndElement( ALWAYS ); //Slot + + SlotChunk sc; + sc.addr = tgt; + sc.nSlots = (mt->GetNumVtableSlots() - mt->GetNumVirtuals()); + m_discoveredSlotChunks.AppendEx(sc); + } + else if (mt->HasSingleNonVirtualSlot()) + { + DumpSlot((unsigned)-1, mt->GetSlot(mt->GetNumVirtuals())); + } + + DisplayEndList( ALWAYS ); //vtable + } + else + { + CoverageRead( PTR_TO_TADDR(mt) + mt->GetVtableOffset(), + mt->GetNumVtableIndirections() * sizeof(PTR_PCODE) ); + + if (mt->HasNonVirtualSlotsArray()) + { + CoverageRead( PTR_TO_TADDR(mt->GetNonVirtualSlotsArray()), + mt->GetNonVirtualSlotsArraySize() ); + } + + } + DisplayEndStructure(ALWAYS); //Vtable + } + + if( mt->HasInterfaceMap() && CHECK_OPT(METHODTABLES) ) + { + PTR_InterfaceInfo ifMap = mt->GetInterfaceMap(); + m_display->StartArrayWithOffset( "InterfaceMap", + offsetof(MethodTable, m_pMultipurposeSlot2), + sizeof(void*), + NULL ); + for( unsigned i = 0; i < mt->GetNumInterfaces(); ++i ) + { + PTR_InterfaceInfo info = ifMap + i; + DisplayStartStructure( "InterfaceInfo_t", DPtrToPreferredAddr(info), + sizeof(*info), METHODTABLES ); + WriteFieldMethodTable( m_pMethodTable, + info->GetMethodTable(), + InterfaceInfo_t, METHODTABLES ); + DisplayEndStructure( METHODTABLES ); //InterfaceInfo_t + } + DisplayEndArray( "Total InterfaceInfos", + METHODTABLES ); //InterfaceMap + } + + //rest of the optional members + + //GenericStatics comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if( haveCompleteExtents && + mt->HasGenericsStaticsInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_GenericsStaticsInfo genStatics = mt->GetGenericsStaticsInfo(); + m_display->StartStructureWithOffset( "OptionalMember_" + "GenericsStaticsInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_GenericsStaticsInfo), + sizeof(*genStatics), + DPtrToPreferredAddr(genStatics), + sizeof(*genStatics) ); + + PTR_FieldDesc fieldDescs = genStatics->m_pFieldDescs; + if( fieldDescs == NULL ) + { + DisplayWriteFieldPointer( m_pFieldDescs, NULL, GenericsStaticsInfo, + ALWAYS ); + } + else + { + DisplayStartArrayWithOffset( m_pFieldDescs, NULL, + GenericsStaticsInfo, ALWAYS ); + _ASSERTE(clazz == GetClassFromMT(mt)); + for( int i = 0; i < clazz->GetNumStaticFields(); ++i ) + { + PTR_FieldDesc fd = fieldDescs + i; + DumpFieldDesc( fd, "FieldDesc" ); + } + DisplayEndArray( "Total Static Fields", ALWAYS ); // m_pFieldDescs + } + DisplayWriteFieldUInt( m_DynamicTypeID, (DWORD)genStatics->m_DynamicTypeID, + GenericsStaticsInfo, METHODTABLES ); + + DisplayEndStructure( METHODTABLES );//OptionalMember_GenericsStaticsInfo + + } + +#ifdef FEATURE_COMINTEROP + if (haveCompleteExtents && + mt->HasGuidInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_GuidInfo guidInfo(*mt->GetGuidInfoPtr()); + + if (guidInfo != NULL) + { + m_display->StartStructureWithOffset( "OptionalMember_GuidInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_GuidInfo), + sizeof(void*), + DPtrToPreferredAddr(guidInfo), + sizeof(GuidInfo) + ); + TempBuffer buf; + GuidToString( guidInfo->m_Guid, buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidInfo, + ALWAYS ); + DisplayWriteFieldFlag( m_bGeneratedFromName, + guidInfo->m_bGeneratedFromName, + GuidInfo, ALWAYS ); + DisplayEndStructure( ALWAYS ); // OptionalMember_GuidInfo + } + } + + if (haveCompleteExtents && + mt->HasCCWTemplate() + && CHECK_OPT(METHODTABLES) + ) + { + PTR_ComCallWrapperTemplate ccwTemplate(TO_TADDR(*mt->GetCCWTemplatePtr())); + m_display->WriteFieldPointer( "OptionalMember_CCWTemplate", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_CCWTemplate), + sizeof(void *), + DPtrToPreferredAddr(ccwTemplate) + ); + } +#endif // FEATURE_COMINTEROP + +#if defined(FEATURE_REMOTING) + //RemotingVtsInfo comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasRemotingVtsInfo() + && CHECK_OPT(METHODTABLES) + ) + { + _ASSERTE(clazz == GetClassFromMT(mt)); + /* REVISIT_TODO Tue 12/13/2005 + * Is this right? is m_wNumStaticFields actually the number of static + * fields? + */ + unsigned numFields = (unsigned)(CountFields(mt) + - clazz->GetNumStaticFields()); + PTR_RemotingVtsInfo vts = *mt->GetRemotingVtsInfoPtr(); + m_display->StartStructureWithOffset( "OptionalMember_RemotingVtsInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_RemotingVtsInfo), + sizeof(void*), + DPtrToPreferredAddr(vts), + RemotingVtsInfo::GetSize(numFields) + ); + + DisplayStartArrayWithOffset( m_pCallbacks, W("%-30s: %s"), + RemotingVtsInfo, ALWAYS ); + for( int i = 0; i < _countof(vts->m_pCallbacks); ++i ) + { + PTR_MethodDesc md( vts->m_pCallbacks[i].GetValue() ); + DisplayStartElement( "Callback", ALWAYS ); + DisplayWriteElementString( "CallbackType", s_VTSCallbackNames[i], + ALWAYS ); + if (CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(md))) + { + DoWriteFieldMethodDesc( "MethodDesc", UINT_MAX, UINT_MAX, md ); + } + else + { + WriteElementMethodDesc( "MethodDesc", md ); + } + DisplayEndElement( ALWAYS ); //Callback + } + DisplayEndArray( NULL, ALWAYS ); //m_pCallbacks +#ifdef _DEBUG + DisplayWriteFieldInt( m_dwNumFields, vts->m_dwNumFields, + RemotingVtsInfo, ALWAYS ); +#endif + /* REVISIT_TODO Tue 12/13/2005 + * Is there any reason to dump this map? + */ + m_display->WriteFieldEmpty("m_rFieldTypes", + offsetof(RemotingVtsInfo, m_rFieldTypes), + RemotingVtsInfo::GetSize(numFields) + - offsetof(RemotingVtsInfo, m_rFieldTypes) ); + DisplayEndStructure( ALWAYS ); //OptionalMember_RemotingVtsInfo + } + + //RemotableMethodInfo comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasRemotableMethodInfo() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_CrossDomainOptimizationInfo cdOpt = *mt->GetRemotableMethodInfoPtr(); + if( cdOpt == NULL ) + { + _ASSERTE(mt->GetNumVtableSlots() == 0 ); + m_display->WriteElementPointer("OptionalMember_RemotableMethodInfo", + NULL); + } + else + { + _ASSERTE(mt->GetNumVtableSlots() > 0 ); + /* REVISIT_TODO Tue 12/13/2005 + * One liner copied from CrossDomainCalls.cpp + */ + unsigned size = sizeof(RemotableMethodInfo) * mt->GetNumVtableSlots(); + //one remotable method info for each method. + m_display->StartStructureWithOffset( "OptionalMember_RemotableMethodInfo", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_RemotableMethodInfo), + sizeof(void*), + DPtrToPreferredAddr(cdOpt), + size ); + m_display->StartArrayWithOffset( "m_rmi", + offsetof(CrossDomainOptimizationInfo, + m_rmi), + mt->GetNumVtableSlots() + * sizeof(RemotableMethodInfo), NULL ); + PTR_RemotableMethodInfo rmi( PTR_HOST_MEMBER_TADDR(CrossDomainOptimizationInfo, cdOpt, m_rmi) ); + for( unsigned i = 0; i < mt->GetNumVtableSlots(); ++i ) + { + PTR_RemotableMethodInfo current = rmi + i; + DisplayStartStructure( "RemotableMethodInfo", + DPtrToPreferredAddr(current), + sizeof(*current), ALWAYS ); + DisplayWriteFieldEnumerated( m_OptFlags, current->m_OptFlags, + RemotableMethodInfo, s_XADOptFlags, + W(", "), ALWAYS ); + DisplayEndStructure( ALWAYS ); //RemotableMethodInfo + } + DisplayEndArray( "Total RemotableMethodInfos", ALWAYS ); //m_rmi + DisplayEndStructure( ALWAYS ); // OptionalMember_RemotableMethodInfo + } + } + + //ContextStatics comes after the generic dictionaries. So if I + //don't have extents, I can't print them. + if(haveCompleteExtents && + mt->HasContextStatics() && + CHECK_OPT(METHODTABLES) + ) + { + PTR_ContextStaticsBucket statics = + *(mt->GetContextStaticsBucketPtr()); + m_display->StartStructureWithOffset( "OptionalMember_ContextStatics", + mt->GetOffsetOfOptionalMember(MethodTable::OptionalMember_ContextStatics), + sizeof(void*), + DPtrToPreferredAddr(statics), + sizeof(*statics) ); + +#define HANDLE_FIELD(field) \ + DisplayWriteFieldInt( field, statics->field, \ + ContextStaticsBucket, ALWAYS ) + HANDLE_FIELD(m_dwContextStaticsOffset); + HANDLE_FIELD(m_wContextStaticsSize); +#undef HANDLE_FIELD + DisplayEndStructure( ALWAYS ); //OptionalMember_ContextStatics + } +#endif + DisplayEndStructure( METHODTABLES ); //MethodTable +} // NativeImageDumper::DumpMethodTable +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +void +NativeImageDumper::DumpMethodTableSlotChunk( PTR_PCODE slotChunk, COUNT_T numSlots ) +{ + IF_OPT( METHODTABLES ) + { + DisplayStartStructure( "MethodTableSlotChunk", DPtrToPreferredAddr(slotChunk), numSlots * sizeof(PCODE), + METHODTABLES ); + + IF_OPT(VERBOSE_TYPES) + { + DisplayStartList( W("[%-4s]: %s (%s)"), ALWAYS ); + for( unsigned i = 0; i < numSlots; ++i ) + { + DumpSlot(i, slotChunk[i]); + } + DisplayEndList( ALWAYS ); //Slot list + } + else + CoverageRead( PTR_TO_TADDR(slotChunk), + numSlots * sizeof(PCODE) ); + DisplayEndStructure(ALWAYS); //Slot chunk + } +} + + +void +NativeImageDumper::DumpSlot( unsigned index, PCODE tgt ) +{ + IF_OPT(VERBOSE_TYPES) + { + DisplayStartElement( "Slot", ALWAYS ); + DisplayWriteElementInt( "Index", index, ALWAYS ); + DisplayWriteElementPointer( "Pointer", + DataPtrToDisplay(tgt), + ALWAYS ); + if( !isInRange(TO_TADDR(tgt)) ) + { + DisplayWriteElementString( "Type", "external", + ALWAYS ); + } + else if( isPrecode(TO_TADDR(tgt)) + && Precode::IsValidType(PTR_Precode(TO_TADDR(tgt))->GetType()) ) + { + PTR_Precode precode(TO_TADDR(tgt)); + DisplayWriteElementString( "Type", "precode", + ALWAYS ); + //DumpPrecode( precode, module ); + } + else + { + DisplayWriteElementString( "Type", "code pointer", + ALWAYS ); + } + DisplayEndElement( ALWAYS ); //Slot + } +} + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_SSMDExtendedFlags[] = +{ +#define SSMD_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define SSMD_ACCESS_ENTRY(x) NativeImageDumper::EnumMnemonics( x, mdMemberAccessMask, W(#x) ) + SSMD_ACCESS_ENTRY(mdPrivateScope), + SSMD_ACCESS_ENTRY(mdPrivate), + SSMD_ACCESS_ENTRY(mdFamANDAssem), + SSMD_ACCESS_ENTRY(mdAssem), + SSMD_ACCESS_ENTRY(mdFamily), + SSMD_ACCESS_ENTRY(mdFamORAssem), + SSMD_ACCESS_ENTRY(mdPublic), +#undef SSMD_ACCESS_ENTRY + + SSMD_ENTRY(mdStatic), + SSMD_ENTRY(mdFinal), + SSMD_ENTRY(mdVirtual), + SSMD_ENTRY(mdHideBySig), + + SSMD_ENTRY(mdVtableLayoutMask), + SSMD_ENTRY(mdNewSlot), + + SSMD_ENTRY(mdCheckAccessOnOverride), + SSMD_ENTRY(mdAbstract), + SSMD_ENTRY(mdSpecialName), + + SSMD_ENTRY(mdPinvokeImpl), + SSMD_ENTRY(mdUnmanagedExport), + + SSMD_ENTRY(mdRTSpecialName), + SSMD_ENTRY(mdHasSecurity), + SSMD_ENTRY(mdRequireSecObject), + + NativeImageDumper::EnumMnemonics( DynamicMethodDesc::nomdILStub, + W("nomdILStub") ), + NativeImageDumper::EnumMnemonics( DynamicMethodDesc::nomdLCGMethod, + W("nomdLCGMethod") ), +#undef SSMD_ENTRY +}; + +//maps MethodClassification to a name for a MethodDesc +const char * const s_MDTypeName[] = +{ + "MethodDesc", //mcIL + "FCallMethodDesc", //mcFCall + "NDirectMethodDesc", //mcNDirect + "EEImplMethodDesc", //mcEEImpl - //public StoredSigMethodDesc + "ArrayMethodDesc", //mcArray - //public StoredSigMethodDesc + "InstantiatedMethodDesc", //mcInstantiated +#if defined(FEATURE_COMINTEROP) + "ComPlusCallMethodDesc", //mcComInterop +#else + "", +#endif + "DynamicMethodDesc", //mcDynamic -- //public StoredSigMethodDesc +}; + +unsigned s_MDSizes[] = +{ + sizeof(MethodDesc), //mcIL + sizeof(FCallMethodDesc), //mcFCall + sizeof(NDirectMethodDesc), //mcNDirect + sizeof(EEImplMethodDesc), //mcEEImpl + sizeof(ArrayMethodDesc), //mcArray + sizeof(InstantiatedMethodDesc), //mcInstantiated +#if defined(FEATURE_COMINTEROP) + sizeof(ComPlusCallMethodDesc), //mcComInterop +#else + 0, +#endif + sizeof(DynamicMethodDesc), //mcDynamic +}; + +static NativeImageDumper::EnumMnemonics g_NDirectFlags[] = +{ +#define NDF_ENTRY(x) NativeImageDumper::EnumMnemonics( NDirectMethodDesc:: x, W(#x) ) + NDF_ENTRY(kEarlyBound), + NDF_ENTRY(kHasSuppressUnmanagedCodeAccess), + NDF_ENTRY(kIsMarshalingRequiredCached), + NDF_ENTRY(kCachedMarshalingRequired), + NDF_ENTRY(kNativeAnsi), + NDF_ENTRY(kLastError), + NDF_ENTRY(kNativeNoMangle), + NDF_ENTRY(kVarArgs), + NDF_ENTRY(kStdCall), + NDF_ENTRY(kThisCall), + NDF_ENTRY(kIsQCall), + NDF_ENTRY(kHasCopyCtorArgs), + NDF_ENTRY(kStdCallWithRetBuf), +#undef NDF_ENTRY +}; +NativeImageDumper::EnumMnemonics NativeImageDumper::s_IMDFlags[] = +{ +#define IMD_ENTRY(x) NativeImageDumper::EnumMnemonics( InstantiatedMethodDesc:: x, W(#x) ) + +#define IMD_KIND_ENTRY(x) NativeImageDumper::EnumMnemonics( InstantiatedMethodDesc:: x, InstantiatedMethodDesc::KindMask, W(#x) ) + IMD_KIND_ENTRY(GenericMethodDefinition), + IMD_KIND_ENTRY(UnsharedMethodInstantiation), + IMD_KIND_ENTRY(SharedMethodInstantiation), + IMD_KIND_ENTRY(WrapperStubWithInstantiations), +#undef IMD_KIND_ENTRY + +#ifdef EnC_SUPPORTED + // Method is a new virtual function added through EditAndContinue. + IMD_ENTRY(EnCAddedMethod), +#endif // EnC_SUPPORTED + + IMD_ENTRY(Unrestored), + +#ifdef FEATURE_COMINTEROP + IMD_ENTRY(HasComPlusCallInfo), +#endif // FEATURE_COMINTEROP + +#undef IMD_ENTRY +}; + +void NativeImageDumper::DumpPrecode( PTR_Precode precode, PTR_Module module ) +{ + _ASSERTE(isPrecode(PTR_TO_TADDR(precode))); + + PrecodeType pType = precode->GetType(); + switch(pType) + { +#define DISPLAY_PRECODE(type) \ + IF_OPT_AND(PRECODES, METHODDESCS) \ + { \ + PTR_ ## type p( precode->As ## type () ); \ + DisplayStartStructure( # type, \ + DPtrToPreferredAddr(p), \ + sizeof(*p), ALWAYS ); \ + WriteFieldMethodDesc( m_pMethodDesc, \ + p->m_pMethodDesc, \ + type, ALWAYS ); \ + TADDR target = p->GetTarget(); \ + DisplayWriteElementPointer("Target",\ + DataPtrToDisplay(target),\ + ALWAYS );\ + DisplayEndStructure( ALWAYS ); \ + } + + case PRECODE_STUB: + DISPLAY_PRECODE(StubPrecode); break; +#ifdef HAS_NDIRECT_IMPORT_PRECODE + case PRECODE_NDIRECT_IMPORT: + DISPLAY_PRECODE(NDirectImportPrecode); break; +#endif +#ifdef HAS_REMOTING_PRECODE + case PRECODE_REMOTING: + DISPLAY_PRECODE(RemotingPrecode); break; +#endif +#ifdef HAS_FIXUP_PRECODE + case PRECODE_FIXUP: + IF_OPT_AND(PRECODES, METHODDESCS) + { + PTR_FixupPrecode p( precode->AsFixupPrecode() ); + DisplayStartStructure( "FixupPrecode", + DPtrToPreferredAddr(p), + sizeof(*p), + ALWAYS ); + PTR_MethodDesc precodeMD(p->GetMethodDesc()); +#ifdef HAS_FIXUP_PRECODE_CHUNKS + { + DisplayWriteFieldInt( m_MethodDescChunkIndex, + p->m_MethodDescChunkIndex, FixupPrecode, + ALWAYS ); + DisplayWriteFieldInt( m_PrecodeChunkIndex, + p->m_PrecodeChunkIndex, FixupPrecode, + ALWAYS ); + if( p->m_PrecodeChunkIndex == 0 ) + { + //dump the location of the Base + DisplayWriteElementAddress( "PrecodeChunkBase", + DataPtrToDisplay(p->GetBase()), + sizeof(void*), ALWAYS ); + } + //Make sure I align up if there is no code slot to make + //sure that I get the padding + TADDR mdPtrStart = p->GetBase() + + (p->m_MethodDescChunkIndex * MethodDesc::ALIGNMENT); + TADDR mdPtrEnd = ALIGN_UP( mdPtrStart + sizeof(MethodDesc*), + 8 ); + CoverageRead( mdPtrStart, (ULONG32)(mdPtrEnd - mdPtrStart) ); + TADDR precodeMDSlot = p->GetBase() + + p->m_MethodDescChunkIndex * MethodDesc::ALIGNMENT; + DoWriteFieldMethodDesc( "MethodDesc", + (DWORD)(precodeMDSlot - PTR_TO_TADDR(p)), + sizeof(TADDR), precodeMD ); + } +#else //HAS_FIXUP_PRECODE_CHUNKS + WriteFieldMethodDesc( m_pMethodDesc, + p->m_pMethodDesc, + FixupPrecode, ALWAYS ); +#endif //HAS_FIXUP_PRECODE_CHUNKS + TADDR target = p->GetTarget(); + DisplayWriteElementPointer("Target", + DataPtrToDisplay(target), + ALWAYS ); + /* REVISIT_TODO Thu 01/05/2006 + * dump slot with offset if it is here + */ + DisplayEndStructure( ALWAYS ); //FixupPrecode + } + break; +#endif +#ifdef HAS_THISPTR_RETBUF_PRECODE + case PRECODE_THISPTR_RETBUF: + DISPLAY_PRECODE(ThisPtrRetBufPrecode); break; +#endif + default: + _ASSERTE( !"Unsupported precode type" ); +#undef DISPLAY_PRECODE +#undef PrecodeMDWrite + } +} + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module ) +{ + //StoredSigMethodDesc + + MethodClassification mc = + (MethodClassification)md->GetClassification(); + _ASSERTE(mc >= 0 && mc < mcCount); + const char * mdTypeName = s_MDTypeName[mc]; + unsigned mdSize = (unsigned)md->SizeOf(); + + DisplayStartStructure( mdTypeName, DPtrToPreferredAddr(md), + mdSize, METHODDESCS ); + IF_OPT(METHODDESCS) + { + TempBuffer buf; + MethodDescToString( md, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, METHODDESCS ); + } +#ifdef _DEBUG + IF_OPT(METHODDESCS) + { + WriteFieldStr(m_pszDebugMethodName, + PTR_BYTE(TO_TADDR(md->m_pszDebugMethodName)), + MethodDesc, METHODDESCS); + WriteFieldStr(m_pszDebugClassName, + PTR_BYTE(TO_TADDR(md->m_pszDebugClassName)), + MethodDesc, METHODDESCS); + WriteFieldStr(m_pszDebugMethodSignature, + PTR_BYTE(TO_TADDR(md->m_pszDebugMethodSignature)), + MethodDesc, METHODDESCS); + } + else + { + CoverageReadString(TO_TADDR(md->m_pszDebugMethodName)); + CoverageReadString(TO_TADDR(md->m_pszDebugClassName)); + CoverageReadString(TO_TADDR(md->m_pszDebugMethodSignature)); + } +#endif + + DisplayWriteFieldInt( m_wFlags3AndTokenRemainder, md->m_wFlags3AndTokenRemainder, + MethodDesc, METHODDESCS ); + + DisplayWriteFieldInt( m_chunkIndex, md->m_chunkIndex, + MethodDesc, METHODDESCS ); + + /* XXX Fri 03/24/2006 + * This is a workaround. The InstantiatedMethodDescs are in chunks, but there's + * no obvious place to display the chunk, so display the bounds here. + */ + if( mc == mcInstantiated && md->m_chunkIndex == 0 ) + { + PTR_MethodDescChunk chunk( md->GetMethodDescChunk() ); + DisplayWriteElementAddress( "MethodDescChunk", DPtrToPreferredAddr(chunk), + chunk->SizeOf(), METHODDESCS ); + } + + DisplayWriteFieldEnumerated( m_bFlags2, md->m_bFlags2, MethodDesc, + s_MDFlag2, W(", "), METHODDESCS ); + + DisplayWriteFieldInt( m_wSlotNumber, md->GetSlot(), MethodDesc, + METHODDESCS ); + DisplayWriteFieldEnumerated( m_wFlags, md->m_wFlags, MethodDesc, + s_MDC, W(", "), METHODDESCS ); + + IF_OPT(IL) + { + if( md->IsIL() ) + { + PTR_MethodDescChunk chunk(md->GetMethodDescChunk()); + //chunk is implicitly remapped because it's calculated from the pointer + //to MD. + PTR_MethodTable mt = chunk->GetMethodTable(); + if( !CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(mt)) ) + { + if ( md->IsTypicalMethodDefinition() ) + { + DWORD dwRVA = 0; + m_import->GetMethodProps(md->GetMemberDef(), NULL, NULL, NULL, 0, + NULL, NULL, NULL, &dwRVA, NULL); + + if (dwRVA != 0) + { + _ASSERTE(m_ILHostCopy); + _ASSERTE(m_ILSectionStart); + _ASSERTE(dwRVA >= m_ILSectionStart); + _ASSERTE(dwRVA < (m_ILSectionStart + m_ILSectionSize)); + //The RVA is from the start of the file, so convert it + //to an RVA to the start of the .text section. + TADDR pILTarget = (TADDR)m_decoder.GetRvaData(dwRVA); + COR_ILMETHOD * pILHeader = (COR_ILMETHOD*)(m_ILHostCopy + dwRVA - m_ILSectionStart); + + COR_ILMETHOD_DECODER decoder(pILHeader); + + DisplayStartStructure( "IL", + DataPtrToDisplay(pILTarget), + PEDecoder::ComputeILMethodSize(pILTarget), + ALWAYS ); + + DisplayWriteElementInt( "CodeSize", decoder.GetCodeSize(), ALWAYS ); + + // Dump the disassembled IL code? + + DisplayEndStructure( ALWAYS ); + } + } + } + } + } + if( md->HasPrecode() ) + { + PTR_Precode precode( md->GetPrecode() ); + + DumpPrecode( precode, module ); + } + if ( md->HasNonVtableSlot() ) + { + DisplayWriteElementInt( "Slot", (DWORD)(PTR_TO_TADDR(md->GetAddrOfSlot()) - PTR_TO_TADDR(md)), ALWAYS); + } + if (md->HasNativeCodeSlot()) + { + DisplayWriteElementInt( "NativeCode", DWORD(md->GetAddrOfNativeCodeSlot() - PTR_TO_TADDR(md)), ALWAYS); + //m_display->WriteFieldPointer( "NativeCode", + // DWORD(md->GetAddrOfNativeCodeSlot() - PTR_TO_TADDR(md)), + // sizeof(TADDR), + // md->GetNativeCode() ); + } + if (md->HasMethodImplSlot()) + { + DisplayStartVStructure( "MethodImpl", METHODDESCS ); + PTR_MethodImpl impl(md->GetMethodImpl()); + PTR_DWORD slots = impl->GetSlots() - 1; // GetSlots returns the address of the first real slot (past the size) + unsigned numSlots = impl->GetSize(); + _ASSERTE(!numSlots || numSlots == slots[0]); + _ASSERTE(slots == NULL || isInRange(PTR_TO_TADDR(slots))); + if ((slots != NULL) && isInRange(PTR_TO_TADDR(slots))) + { + DisplayWriteFieldAddress(pdwSlots, DataPtrToDisplay(dac_cast<TADDR>(slots)), + (numSlots + 1) * sizeof(*slots), + MethodImpl, METHODDESCS); + } + else + { + DisplayWriteFieldPointer(pdwSlots, DataPtrToDisplay(dac_cast<TADDR>(slots)), + MethodImpl, METHODDESCS); + + } + _ASSERTE(impl->pImplementedMD == NULL + || isInRange(PTR_TO_TADDR(impl->pImplementedMD))); + if ((impl->pImplementedMD != NULL) && + isInRange(PTR_TO_TADDR(impl->pImplementedMD))) + { + DisplayWriteFieldAddress( pImplementedMD, + DataPtrToDisplay(dac_cast<TADDR>(impl->pImplementedMD)), + numSlots * sizeof(MethodDesc*), + MethodImpl, METHODDESCS ); + } + else + { + DisplayWriteFieldPointer( pImplementedMD, + DataPtrToDisplay(dac_cast<TADDR>(impl->pImplementedMD)), + MethodImpl, METHODDESCS ); + } + DisplayEndVStructure( METHODDESCS ); + } + if (md->HasStoredSig()) + { + DisplayStartVStructure( "StoredSigMethodDesc", METHODDESCS ); + PTR_StoredSigMethodDesc ssmd(md); + //display signature information. + if( isInRange(ssmd->m_pSig) ) + { + DisplayWriteFieldAddress(m_pSig, DataPtrToDisplay(ssmd->m_pSig), + ssmd->m_cSig, StoredSigMethodDesc, + METHODDESCS); + } + else + { + DisplayWriteFieldPointer(m_pSig, DataPtrToDisplay(ssmd->m_pSig), + StoredSigMethodDesc, METHODDESCS); + + } + CoverageRead(TO_TADDR(ssmd->m_pSig), ssmd->m_cSig); + DisplayWriteFieldInt( m_cSig, ssmd->m_cSig, + StoredSigMethodDesc, METHODDESCS ); +#ifdef _WIN64 + DisplayWriteFieldEnumerated( m_dwExtendedFlags, + ssmd->m_dwExtendedFlags, + StoredSigMethodDesc, + s_SSMDExtendedFlags, W(", "), + METHODDESCS ); +#endif + DisplayEndVStructure( METHODDESCS ); //StoredSigMethodDesc + } + if( mc == mcDynamic ) + { + PTR_DynamicMethodDesc dmd(md); + DisplayStartVStructure( "DynamicMethodDesc", METHODDESCS ); + WriteFieldStr( m_pszMethodName, PTR_BYTE(dmd->m_pszMethodName), + DynamicMethodDesc, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString( PTR_TO_TADDR(dmd->m_pszMethodName) ); + DisplayWriteFieldPointer( m_pResolver, + DPtrToPreferredAddr(dmd->m_pResolver), + DynamicMethodDesc, METHODDESCS ); +#ifndef _WIN64 + DisplayWriteFieldEnumerated( m_dwExtendedFlags, + dmd->m_dwExtendedFlags, + DynamicMethodDesc, + s_SSMDExtendedFlags, W(", "), + METHODDESCS ); +#endif + DisplayEndVStructure( METHODDESCS ); + } + if (mc == mcFCall ) + { + PTR_FCallMethodDesc fcmd(md); + DisplayStartVStructure( "FCallMethodDesc", METHODDESCS ); + + DisplayWriteFieldInt( m_dwECallID, + fcmd->m_dwECallID, + FCallMethodDesc, + METHODDESCS ); + + DisplayEndVStructure( METHODDESCS ); //NDirectMethodDesc + } + if( mc == mcNDirect ) + { + PTR_NDirectMethodDesc ndmd(md); + DisplayStartVStructure( "NDirectMethodDesc", METHODDESCS ); + DPTR(NDirectMethodDesc::temp1) nd( PTR_HOST_MEMBER_TADDR(NDirectMethodDesc, ndmd, ndirect) ); + DisplayStartStructureWithOffset( ndirect, + DPtrToPreferredAddr(nd), + sizeof(*nd), NDirectMethodDesc, + METHODDESCS ); + DisplayWriteFieldPointer( m_pNativeNDirectTarget, + DataPtrToDisplay((TADDR)nd->m_pNativeNDirectTarget), + NDirectMethodDesc::temp1, + METHODDESCS ); + DisplayWriteFieldEnumerated( m_wFlags, nd->m_wFlags, + NDirectMethodDesc::temp1, + g_NDirectFlags, W(", "), + METHODDESCS ); + + WriteFieldStr( m_pszEntrypointName, + PTR_BYTE(TO_TADDR(nd->m_pszEntrypointName)), + NDirectMethodDesc::temp1, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString(TO_TADDR(nd->m_pszEntrypointName)); + if (md->IsQCall()) + { + DisplayWriteFieldInt( m_dwECallID, + nd->m_dwECallID, + NDirectMethodDesc::temp1, + METHODDESCS ); + } + else + { + WriteFieldStr( m_pszLibName, + PTR_BYTE(TO_TADDR(nd->m_pszLibName)), + NDirectMethodDesc::temp1, METHODDESCS ); + } + if( !CHECK_OPT(METHODDESCS) ) + CoverageReadString( TO_TADDR(nd->m_pszLibName) ); + + PTR_NDirectWriteableData wnd( nd->m_pWriteableData ); + DisplayStartStructureWithOffset( m_pWriteableData, + DPtrToPreferredAddr(wnd), + sizeof(*wnd), + NDirectMethodDesc::temp1, + METHODDESCS ); + DisplayWriteFieldPointer( m_pNDirectTarget, + DataPtrToDisplay((TADDR)wnd->m_pNDirectTarget), NDirectWriteableData, METHODDESCS ); + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead( PTR_TO_TADDR(wnd), sizeof(*wnd) ); + DisplayEndStructure( METHODDESCS ); //m_pWriteableData + + +#ifdef HAS_NDIRECT_IMPORT_PRECODE + PTR_NDirectImportThunkGlue glue(nd->m_pImportThunkGlue); +#else + PTR_NDirectImportThunkGlue glue(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc::temp1, nd, m_ImportThunkGlue)); +#endif + +#ifdef HAS_NDIRECT_IMPORT_PRECODE + if (glue == NULL) + { + // import thunk glue is not needed for P/Invoke that is not inlinable + DisplayWriteFieldPointer( m_pImportThunkGlue, + NULL, + NDirectMethodDesc::temp1, + METHODDESCS ); + } + else + { + DisplayStartStructureWithOffset( m_pImportThunkGlue, + DPtrToPreferredAddr(glue), + sizeof(*glue), + NDirectMethodDesc::temp1, + METHODDESCS); +#else + DisplayStartStructureWithOffset( m_ImportThunkGlue, + DPtrToPreferredAddr(glue), + sizeof(*glue), + NDirectMethodDesc::temp1, + METHODDESCS); +#endif +#ifdef HAS_NDIRECT_IMPORT_PRECODE + /* REVISIT_TODO Thu 01/05/2006 + * Dump this properly as a precode + */ + WriteFieldMethodDesc( m_pMethodDesc, glue->m_pMethodDesc, + NDirectImportThunkGlue, METHODDESCS ); + { + PTR_Precode p(glue); + DumpPrecode( p, module ); + } + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead(PTR_TO_TADDR(glue), sizeof(*glue)); + /* REVISIT_TODO Fri 12/16/2005 + * Factor out this code into some shared precode dumping code + */ +#else //!HAS_NDIRECT_IMPORT_PRECODE + /* REVISIT_TODO Fri 10/27/2006 + * For Whidbey AMD64 (!HAS_NDIRECT_IMPORT_PRECODE), I don't have this data structure in the output. + */ +#endif //HAS_NDIRECT_IMPORT_PRECODE + + DisplayEndStructure( METHODDESCS ); //m_pImportThunkGlue +#ifdef HAS_NDIRECT_IMPORT_PRECODE + } +#endif + +#ifdef _TARGET_X86_ + DisplayWriteFieldInt( m_cbStackArgumentSize, + nd->m_cbStackArgumentSize, + NDirectMethodDesc::temp1, METHODDESCS ); +#endif + + WriteFieldMethodDesc( m_pStubMD, + nd->m_pStubMD.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(NDirectMethodDesc::temp1, nd, m_pStubMD)), + NDirectMethodDesc::temp1, METHODDESCS ); + + DisplayEndStructure( METHODDESCS ); //ndirect + + + DisplayEndVStructure( METHODDESCS ); //NDirectMethodDesc + } + if( mc == mcEEImpl ) + { + DisplayStartVStructure( "EEImplMethodDesc", METHODDESCS ); + DisplayEndVStructure( METHODDESCS ); + } +#if defined(FEATURE_COMINTEROP) + if( mc == mcComInterop ) + { + PTR_ComPlusCallMethodDesc cpmd(md); + DisplayStartVStructure( "ComPlusCallMethodDesc", METHODDESCS ); + PTR_ComPlusCallInfo compluscall((TADDR)cpmd->m_pComPlusCallInfo); + + if (compluscall == NULL) + { + DisplayWriteFieldPointer( m_pComPlusCallInfo, + NULL, + ComPlusCallMethodDesc, + METHODDESCS ); + } + else + { + DumpComPlusCallInfo( compluscall, METHODDESCS ); + } + + DisplayEndVStructure( METHODDESCS ); //ComPlusCallMethodDesc + } +#endif + if( mc == mcInstantiated ) + { + PTR_InstantiatedMethodDesc imd(md); + DisplayStartVStructure( "InstantiatedMethodDesc", METHODDESCS ); + unsigned kind = imd->m_wFlags2 + & InstantiatedMethodDesc::KindMask; + if( kind == InstantiatedMethodDesc::SharedMethodInstantiation ) + { + PTR_DictionaryLayout layout(TO_TADDR(imd->m_pDictLayout)); + IF_OPT(METHODDESCS) + { + WriteFieldDictionaryLayout( "m_pDictLayout", + offsetof(InstantiatedMethodDesc, m_pDictLayout ), + fieldsize(InstantiatedMethodDesc, m_pDictLayout), + layout, + GetDependencyFromMD(md)->pImport ); + } + else + { + while( layout != NULL ) + { + CoverageRead( PTR_TO_TADDR(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1) ); + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + } + } + } + else if( kind == + InstantiatedMethodDesc::WrapperStubWithInstantiations ) + { + PTR_MethodDesc wimd(imd->m_pWrappedMethodDesc.GetValue()); + if( wimd == NULL || !DoWriteFieldAsFixup( "m_pWrappedMethodDesc", + offsetof(InstantiatedMethodDesc, m_pWrappedMethodDesc), + fieldsize(InstantiatedMethodDesc, m_pWrappedMethodDesc), + PTR_TO_TADDR(wimd) ) ) + { + WriteFieldMethodDesc( m_pWrappedMethodDesc, wimd, + InstantiatedMethodDesc, METHODDESCS ); + } + } + else + { + _ASSERTE(imd->m_pDictLayout == NULL); + DisplayWriteFieldPointer( m_pDictLayout, NULL, + InstantiatedMethodDesc, + METHODDESCS ); + } + //now handle the contents of the m_pMethInst/m_pPerInstInfo union. + unsigned numSlots = imd->m_wNumGenericArgs; + PTR_Dictionary inst(imd->m_pPerInstInfo); + unsigned dictSize; + if( kind == InstantiatedMethodDesc::SharedMethodInstantiation ) + { + dictSize = sizeof(TypeHandle); + } + else if( kind == InstantiatedMethodDesc::WrapperStubWithInstantiations ) + { + PTR_InstantiatedMethodDesc wrapped = + PTR_InstantiatedMethodDesc(imd->m_pWrappedMethodDesc.GetValue()); + if( CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(wrapped)) ) + { + /* XXX Mon 03/27/2006 + * Note that 4 is the correct answer for all IMDs at this time. + */ + TempBuffer buf; + MethodDescToString( md, buf ); + //m_display->ErrorPrintF( "WARNING! InstantiatedMethodDesc %S wraps a MethodDesc that is a fixup. I cannot accurately determine the size of the associated generic dictionary. Assuming 4.\n", (const WCHAR *)buf ); + dictSize = (imd->GetNumGenericMethodArgs() + 4) * sizeof(void*); + } + else + { + PTR_DictionaryLayout layout(wrapped->IsSharedByGenericMethodInstantiations() + ? TO_TADDR(wrapped->m_pDictLayout) : NULL ); + dictSize = DictionaryLayout::GetFirstDictionaryBucketSize(imd->GetNumGenericMethodArgs(), + layout); + } + } + else + { + dictSize = sizeof(TypeHandle); + } + //instantiations has the number of slots of + //GetNumGenericMethodArgs. + if( inst == NULL ) + { + m_display->WriteFieldPointer( "m_pPerInstInfo", + offsetof(InstantiatedMethodDesc, m_pPerInstInfo), + fieldsize(InstantiatedMethodDesc, m_pPerInstInfo), + NULL ); + } + else + { + IF_OPT(METHODDESCS) + { + + m_display->StartStructureWithOffset( "m_pPerInstInfo", + offsetof(InstantiatedMethodDesc, m_pPerInstInfo), + fieldsize(InstantiatedMethodDesc, m_pPerInstInfo), + DPtrToPreferredAddr(inst), + dictSize ); + } + DisplayStartArray( "InstantiationInfo", W("[%-2s]: %s"), + METHODDESCS ); + /* REVISIT_TODO Thu 03/23/2006 + * This doesn't dump the contents of the dictionary which are + * hanging around after the real slots. Get around to doing that. + */ + for( unsigned i = 0; i < numSlots + && CHECK_OPT(METHODDESCS); ++i ) + { + DisplayStartElement( "Handle", METHODDESCS ); + DisplayWriteElementInt( "Index", i, METHODDESCS ); + + TypeHandle thArg = inst->GetInstantiation()[i].GetValue(); + IF_OPT(METHODDESCS) + WriteElementTypeHandle( "TypeHandle", thArg); + + /* XXX Fri 03/24/2006 + * There is no really good home for TypeDescs, so I gotta check + * lots of places for them. + */ + if( !CORCOMPILE_IS_POINTER_TAGGED(thArg.AsTAddr()) && + thArg.IsTypeDesc() ) + { + PTR_TypeDesc td(thArg.AsTypeDesc()); + if( isInRange(PTR_TO_TADDR(td)) ) + { + m_discoveredTypeDescs.AppendEx(td); + } + } + DisplayEndElement( METHODDESCS ); //Handle + } + //Instantiation Info + DisplayEndArray( "Total TypeHandles", METHODDESCS ); + + DisplayEndVStructure(METHODDESCS); //m_pPerInstInfo; + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead(PTR_TO_TADDR(inst), numSlots * sizeof(*inst)); + } + + DisplayWriteFieldEnumerated( m_wFlags2, imd->m_wFlags2, + InstantiatedMethodDesc, s_IMDFlags, + W(", "), METHODDESCS ); + DisplayWriteFieldInt( m_wNumGenericArgs, imd->m_wNumGenericArgs, + InstantiatedMethodDesc, METHODDESCS ); + +#ifdef FEATURE_COMINTEROP + if (imd->IMD_HasComPlusCallInfo()) + { + PTR_ComPlusCallInfo compluscall = imd->IMD_GetComPlusCallInfo(); + DumpComPlusCallInfo( compluscall, METHODDESCS ); + } +#endif // FEATURE_COMINTEROP + + DisplayEndStructure( METHODDESCS ); + } + + DisplayEndStructure( METHODDESCS ); //MethodDesc (mdTypeName) + if( !CHECK_OPT(METHODDESCS) ) + CoverageRead( PTR_TO_TADDR(md), mdSize ); + +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +NativeImageDumper::EnumMnemonics NativeImageDumper::s_EECLIFlags[] = +{ +#define EECLI_FLAGS_ENTRY(x) NativeImageDumper::EnumMnemonics( EEClassLayoutInfo:: x, W(#x) ) + EECLI_FLAGS_ENTRY(e_BLITTABLE), + EECLI_FLAGS_ENTRY(e_MANAGED_SEQUENTIAL), + EECLI_FLAGS_ENTRY(e_ZERO_SIZED), + EECLI_FLAGS_ENTRY(e_HAS_EXPLICIT_SIZE), +#undef EECLI_FLAGS_ENTRY +}; + + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void +NativeImageDumper::DumpEEClassForMethodTable( PTR_MethodTable mt ) +{ + PTR_EEClass clazz = mt->GetClass(); + + _ASSERTE(CHECK_OPT(EECLASSES)); + _ASSERTE(clazz != NULL); + _ASSERTE(isInRange(PTR_TO_TADDR(clazz))); + + const char * eeClassType; + + if( clazz->HasLayout() ) + eeClassType = "LayoutEEClass"; + else if( mt->IsArray() ) + eeClassType = "ArrayClass"; + else if( clazz->IsDelegate() ) + eeClassType = "DelegateEEClass"; + else + eeClassType = "EEClass"; + + DisplayStartStructure( eeClassType, DPtrToPreferredAddr(clazz), clazz->GetSize(), + EECLASSES ); + { + TempBuffer buf; + MethodTableToString( mt, buf ); + DisplayWriteElementStringW( "Name", (const WCHAR *)buf, EECLASSES ); + } + + PTR_GuidInfo guidInfo = clazz->GetGuidInfo(); + if(guidInfo != NULL) + { + DisplayStartStructureWithOffset( m_pGuidInfo, + DPtrToPreferredAddr(guidInfo), + sizeof(*guidInfo), EEClass, + EECLASSES ); + TempBuffer buf; + GuidToString( guidInfo->m_Guid, buf ); + DisplayWriteFieldStringW( m_Guid, (const WCHAR *)buf, GuidInfo, + EECLASSES ); + DisplayWriteFieldFlag( m_bGeneratedFromName, + guidInfo->m_bGeneratedFromName, + GuidInfo, EECLASSES ); + DisplayEndStructure( EECLASSES ); //guidinfo + } + else + { + /* XXX Fri 10/14/2005 + * if Clazz isn't an interface, m_pGuidInfo is undefined. + */ + DisplayWriteFieldPointerAnnotated( m_pGuidInfo, PTR_TO_TADDR(guidInfo), + W("Invalid"), EEClass, EECLASSES ); + } + + +#ifdef _DEBUG + WriteFieldStr( m_szDebugClassName, + PTR_BYTE(TO_TADDR(clazz->m_szDebugClassName)), + EEClass, EECLASSES ); + DisplayWriteFieldFlag( m_fDebuggingClass, clazz->m_fDebuggingClass, + EEClass, EECLASSES ); +#endif + + WriteFieldMethodTable( m_pMethodTable, clazz->m_pMethodTable, EEClass, + EECLASSES ); + + WriteFieldCorElementType( m_NormType, (CorElementType)clazz->m_NormType, + EEClass, EECLASSES ); + + PTR_FieldDesc fdList = clazz->GetFieldDescList(); + + ULONG fieldCount = (ULONG)CountFields(mt); + _ASSERTE((fdList == NULL) == (fieldCount == 0)); + + IF_OPT(EECLASSES) + { + m_display->StartStructureWithOffset( "m_pFieldDescList", + offsetof(EEClass, m_pFieldDescList), + fieldsize(EEClass, m_pFieldDescList), + DPtrToPreferredAddr(fdList), + fdList != NULL ? + sizeof(*fdList) * fieldCount : + 0 ); + } + IF_OPT(VERBOSE_TYPES) + { + if( fdList != NULL ) + { + DisplayStartArray( "FieldDescs", NULL, EECLASSES ); + for( SIZE_T i = 0; i < fieldCount; ++i ) + { + PTR_FieldDesc fd = fdList + i; + IF_OPT(EECLASSES) + DumpFieldDesc( fd, "FieldDesc" ); + } + DisplayEndArray( "Total FieldDescs", EECLASSES ); //FieldDescs + } + } + else if( (fdList != NULL) && CHECK_OPT(DEBUG_COVERAGE) ) + { + for( SIZE_T i = 0; i < fieldCount; ++i ) + { + PTR_FieldDesc fd = fdList + i; +#ifdef _DEBUG + if( fd != NULL && fd->m_debugName != NULL ) + CoverageReadString( fd->m_debugName ); +#endif + } + CoverageRead( PTR_TO_TADDR(fdList), sizeof(*fdList) * fieldCount ); + } + + DisplayEndStructure( EECLASSES ); //FieldDescList + + DisplayWriteFieldEnumerated( m_dwAttrClass, clazz->GetAttrClass(), + EEClass, s_CorTypeAttr, W(" "), EECLASSES ); + DisplayWriteFieldEnumerated( m_VMFlags, clazz->m_VMFlags, EEClass, + s_VMFlags, W(", "), EECLASSES ); + + PTR_MethodDescChunk chunk = clazz->GetChunks(); + + DisplayStartArrayWithOffset( m_pChunks, NULL, EEClass, EECLASSES ); + while( chunk != NULL ) + { + DisplayStartStructure( "MethodDescChunk", + DPtrToPreferredAddr(chunk), + chunk->SizeOf(), EECLASSES ); + _ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(chunk->GetMethodTable()))); + PTR_MethodTable chunkMT = chunk->GetMethodTable(); + DisplayWriteFieldPointer( m_methodTable, + DPtrToPreferredAddr(chunkMT), + MethodDescChunk, EECLASSES ); + PTR_MethodDescChunk chunkNext = chunk->GetNextChunk(); + DisplayWriteFieldPointer( m_next, + DPtrToPreferredAddr(chunkNext), + MethodDescChunk, EECLASSES ); + DisplayWriteFieldInt( m_size, chunk->m_size, MethodDescChunk, + EECLASSES ); + DisplayWriteFieldInt( m_count, chunk->m_count, MethodDescChunk, + EECLASSES ); + DisplayWriteFieldInt( m_flagsAndTokenRange, chunk->m_flagsAndTokenRange, MethodDescChunk, + EECLASSES ); + /* XXX Wed 12/14/2005 + * Don't skip walking this array. I need to make sure I touch the + * precodes. + */ + DisplayStartArray( "MethodDescs", NULL, METHODDESCS ); + PTR_MethodDesc md(chunk->GetFirstMethodDesc()); + while (md != NULL) + { + IF_OPT_OR(METHODDESCS, DEBUG_COVERAGE) + { + PTR_Module module = mt->GetModule(); + if(CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(module) )) + DumpMethodDesc( md, PTR_Module((TADDR)0) ); + else + DumpMethodDesc( md, module ); + } + + // Check whether the next MethodDesc is within the bounds of the current chunks + TADDR pNext = PTR_HOST_TO_TADDR(md) + md->SizeOf(); + TADDR pEnd = PTR_HOST_TO_TADDR(chunk) + chunk->SizeOf(); + + md = (pNext < pEnd) ? PTR_MethodDesc(pNext) : NULL; + } + + DisplayEndArray( "Total MethodDescs", METHODDESCS); //MethodDescs + + chunk = chunk->GetNextChunk(); + + DisplayEndStructure( EECLASSES ); //MethodDescChunk + } + + DisplayEndArray( "Total MethodDescChunks", EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump the class dependencies + */ + //_ASSERTE(!clazz->m_classDependencies.TestAnyBit()); + + /* REVISIT_TODO Mon 10/24/2005 + * Create vstructure for union? + */ + //decode union here +#ifdef FEATURE_COMINTEROP + if( clazz->IsBlittable() || clazz->HasLayout() ) + { + DisplayWriteFieldInt(m_cbNativeSize, clazz->m_cbNativeSize, EEClass, + EECLASSES ); + } + else if( clazz->IsInterface() ) + { + DisplayWriteFieldPointer( m_ohDelegate, + DataPtrToDisplay(clazz->m_ohDelegate), + EEClass, EECLASSES ); + } + else + { + static const WCHAR * ifnames[] ={W("Dual"),W("Vtable"),W("Dispatch")}; + m_display->WriteFieldEnumerated( "ComInterfaceType", + offsetof(EEClass, + m_ComInterfaceType), + fieldsize(EEClass, + m_ComInterfaceType), + (int)clazz->m_ComInterfaceType, + ifnames[(int)clazz->m_ComInterfaceType] ); + } +#else + DisplayWriteFieldInt( m_cbNativeSize, clazz->m_cbNativeSize, + EEClass, EECLASSES ); +#endif + +#if defined(FEATURE_COMINTEROP) + PTR_ComCallWrapperTemplate ccwTemplate(TO_TADDR(clazz->m_pccwTemplate)); + if( ccwTemplate != NULL ) + { + DisplayWriteFieldPointer( m_pccwTemplate, NULL, EEClass, + EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump CcwTemplate + */ + DisplayWriteFieldPointer( m_pccwTemplate, + DPtrToPreferredAddr(ccwTemplate), EEClass, + EECLASSES ); + } +#endif // defined(FEATURE_COMINTEROP) + + //fields for classes that aren't just EEClasses. + if( clazz->HasLayout() ) + { + PTR_LayoutEEClass layoutClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure("LayoutEEClass", EECLASSES ); + + PTR_EEClassLayoutInfo eecli( PTR_HOST_MEMBER_TADDR( LayoutEEClass, + layoutClass, + m_LayoutInfo ) ); + DisplayStartStructureWithOffset( m_LayoutInfo, + DPtrToPreferredAddr(eecli), + sizeof(EEClassLayoutInfo), + LayoutEEClass, EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * Dump EEClassLayoutInfo + */ + DisplayWriteFieldInt( m_cbNativeSize, eecli->m_cbNativeSize, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_cbManagedSize, eecli->m_cbManagedSize, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_LargestAlignmentRequirementOfAllMembers, + eecli->m_LargestAlignmentRequirementOfAllMembers, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_ManagedLargestAlignmentRequirementOfAllMembers, + eecli->m_ManagedLargestAlignmentRequirementOfAllMembers, + EEClassLayoutInfo, VERBOSE_TYPES ); + DisplayWriteFieldEnumerated( m_bFlags, eecli->m_bFlags, + EEClassLayoutInfo, s_EECLIFlags, W(", "), + VERBOSE_TYPES ); + DisplayWriteFieldInt( m_numCTMFields, eecli->m_numCTMFields, + EEClassLayoutInfo, VERBOSE_TYPES ); + PTR_FieldMarshaler fmArray( TO_TADDR(eecli->m_pFieldMarshalers) ); + DisplayWriteFieldAddress( m_pFieldMarshalers, + DPtrToPreferredAddr(fmArray), + eecli->m_numCTMFields + * MAXFIELDMARSHALERSIZE, + EEClassLayoutInfo, VERBOSE_TYPES ); + /* REVISIT_TODO Wed 03/22/2006 + * Dump the various types of FieldMarshalers. + */ +#if 0 + DisplayStartArrayWithOffset( m_pFieldMarshalers, NULL, + EEClassLayoutInfo, VERBOSE_TYPES ); + for( unsigned i = 0; i < eecli->m_numCTMFields; ++i ) + { + /* REVISIT_TODO Wed 03/22/2006 + * Try to display the type of the field marshaler in the future. + */ + PTR_FieldMarshaler current = fmArray + i; + DisplayStartStructure( "FieldMarshaler", + DPtrToPreferredAddr(current), + sizeof(*current), VERBOSE_TYPES ); + WriteFieldFieldDesc( m_pFD, PTR_FieldDesc(TO_TADDR(current->m_pFD)), + FieldMarshaler, VERBOSE_TYPES ); + DisplayWriteFieldInt( m_dwExternalOffset, + current->m_dwExternalOffset, FieldMarshaler, + VERBOSE_TYPES ); + DisplayEndStructure( VERBOSE_TYPES ); //FieldMarshaler + } + + DisplayEndArray( "Number of FieldMarshalers", VERBOSE_TYPES ); //m_pFieldMarshalers +#endif + + DisplayEndStructure( EECLASSES ); //LayoutInfo + + DisplayEndVStructure( EECLASSES ); //LayoutEEClass + } + else if( mt->IsArray() ) + { + PTR_ArrayClass arrayClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure( "ArrayClass", EECLASSES); + IF_OPT(EECLASSES) + { + m_display->WriteFieldInt( "m_rank", offsetof(ArrayClass, m_rank), + fieldsize(ArrayClass, m_rank), + arrayClass->GetRank() ); + } + DoWriteFieldCorElementType( "m_ElementType", + offsetof(ArrayClass, m_ElementType), + fieldsize(ArrayClass, m_ElementType), + arrayClass->GetArrayElementType() ); + + DisplayEndVStructure( EECLASSES ); //ArrayClass + } + else if( clazz->IsDelegate() ) + { + PTR_DelegateEEClass delegateClass(PTR_TO_TADDR(clazz)); + DisplayStartVStructure( "DelegateEEClass", EECLASSES ); + + DumpFieldStub( m_pStaticCallStub, delegateClass->m_pStaticCallStub, + DelegateEEClass, EECLASSES ); + DumpFieldStub( m_pInstRetBuffCallStub, + delegateClass->m_pInstRetBuffCallStub, + DelegateEEClass, EECLASSES ); + + WriteFieldMethodDesc( m_pInvokeMethod, + delegateClass->m_pInvokeMethod, + DelegateEEClass, EECLASSES ); + DumpFieldStub( m_pMultiCastInvokeStub, + delegateClass->m_pMultiCastInvokeStub, + DelegateEEClass, EECLASSES ); + + DPTR(UMThunkMarshInfo) + umInfo(TO_TADDR(delegateClass->m_pUMThunkMarshInfo)); + + if( umInfo == NULL ) + { + DisplayWriteFieldPointer( m_pUMThunkMarshInfo, NULL, + DelegateEEClass, EECLASSES ); + } + else + { + DisplayStartStructureWithOffset( m_pUMThunkMarshInfo, + DPtrToPreferredAddr(umInfo), + sizeof(*umInfo), + DelegateEEClass, EECLASSES ); + /* REVISIT_TODO Fri 10/14/2005 + * DumpUMThunkMarshInfo + */ + DisplayEndStructure( EECLASSES ); //UMThunkMarshInfo + } + + WriteFieldMethodDesc( m_pBeginInvokeMethod, + delegateClass->m_pBeginInvokeMethod, + DelegateEEClass, EECLASSES ); + WriteFieldMethodDesc( m_pEndInvokeMethod, + delegateClass->m_pEndInvokeMethod, + DelegateEEClass, EECLASSES ); + DisplayWriteFieldPointer( m_pMarshalStub, delegateClass->m_pMarshalStub, + DelegateEEClass, EECLASSES ); + + WriteFieldMethodDesc( m_pForwardStubMD, + PTR_MethodDesc(TO_TADDR(delegateClass->m_pForwardStubMD)), + DelegateEEClass, EECLASSES ); + WriteFieldMethodDesc( m_pReverseStubMD, + PTR_MethodDesc(TO_TADDR(delegateClass->m_pReverseStubMD)), + DelegateEEClass, EECLASSES ); + +#ifdef FEATURE_COMINTEROP + DPTR(ComPlusCallInfo) compluscall((TADDR)delegateClass->m_pComPlusCallInfo); + if (compluscall == NULL) + { + DisplayWriteFieldPointer( m_pComPlusCallInfo, + NULL, + DelegateEEClass, + EECLASSES ); + } + else + { + DumpComPlusCallInfo( compluscall, EECLASSES ); + } +#endif // FEATURE_COMINTEROP + + DisplayEndVStructure( EECLASSES ); //DelegateEEClass + } + + DisplayEndStructure( EECLASSES ); //eeClassType + + PTR_EEClassOptionalFields pClassOptional = clazz->GetOptionalFields(); + if (pClassOptional) + { + DisplayStartStructure( "EEClassOptionalFields", DPtrToPreferredAddr(pClassOptional), sizeof(EEClassOptionalFields), + EECLASSES ); + +#ifdef FEATURE_COMINTEROP + PTR_SparseVTableMap sparseVTMap(TO_TADDR(pClassOptional->m_pSparseVTableMap)); + if( sparseVTMap == NULL ) + { + DisplayWriteFieldPointer( m_pSparseVTableMap, NULL, EEClassOptionalFields, + EECLASSES ); + } + else + { + _ASSERTE( !"Untested code" ); + IF_OPT(EECLASSES) + { + m_display->StartStructure( "m_SparseVTableMap", + DPtrToPreferredAddr(sparseVTMap), + sizeof(*sparseVTMap) ); + } + _ASSERTE(sparseVTMap->m_MapList != NULL); + PTR_SparseVTableMap_Entry mapList(TO_TADDR(sparseVTMap->m_MapList)); + DisplayStartArray( "m_MapList", NULL, EECLASSES ); + for( WORD i = 0; i < sparseVTMap->m_MapEntries; ++i ) + { + DisplayWriteFieldInt( m_Start, mapList[i].m_Start, + SparseVTableMap::Entry, EECLASSES ); + DisplayWriteFieldInt( m_Span, mapList[i].m_Span, + SparseVTableMap::Entry, EECLASSES ); + DisplayWriteFieldInt( m_Span, mapList[i].m_MapTo, + SparseVTableMap::Entry, EECLASSES ); + } + + DisplayEndArray( "Total Entries", EECLASSES ); //m_MapList + + DisplayWriteFieldInt( m_MapEntries, sparseVTMap->m_MapEntries, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_Allocated, sparseVTMap->m_Allocated, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_LastUsed, sparseVTMap->m_LastUsed, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_VTSlot, sparseVTMap->m_VTSlot, + SparseVTableMap, EECLASSES ); + DisplayWriteFieldInt( m_MTSlot, sparseVTMap->m_MTSlot, + SparseVTableMap, EECLASSES ); + + DisplayEndStructure( EECLASSES ); //SparseVTableMap + } + + WriteFieldTypeHandle( m_pCoClassForIntf, pClassOptional->m_pCoClassForIntf, + EEClassOptionalFields, EECLASSES ); + + PTR_ClassFactoryBase classFactory(TO_TADDR(pClassOptional->m_pClassFactory)); + if( classFactory != NULL ) + { + DisplayWriteFieldPointer( m_pClassFactory, NULL, EEClassOptionalFields, + EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump ComClassFactory + */ + DisplayWriteFieldPointer( m_pClassFactory, + DPtrToPreferredAddr(classFactory), + EEClassOptionalFields, EECLASSES ); + } +#endif // FEATURE_COMINTEROP + + PTR_DictionaryLayout layout = pClassOptional->m_pDictLayout; + if( layout == NULL ) + { + DisplayWriteFieldPointer( m_pDictLayout, NULL, EEClassOptionalFields, EECLASSES ); + } + else + { + IF_OPT(VERBOSE_TYPES) + { + WriteFieldDictionaryLayout( "m_pDictLayout", + offsetof(EEClassOptionalFields, m_pDictLayout), + fieldsize(EEClassOptionalFields, m_pDictLayout), + layout, GetDependencyFromMT(mt)->pImport ); + } + else + { + while( layout != NULL ) + { + CoverageRead( PTR_TO_TADDR(layout), + sizeof(DictionaryLayout) + + sizeof(DictionaryEntryLayout) + * (layout->m_numSlots - 1) ); + layout = PTR_DictionaryLayout(TO_TADDR(layout->m_pNext)); + } + } + } + PTR_BYTE varianceInfo = TO_TADDR(pClassOptional->m_pVarianceInfo); + if( varianceInfo == NULL ) + { + DisplayWriteFieldPointer( m_pVarianceInfo, NULL, + EEClassOptionalFields, EECLASSES ); + } + else + { + /* REVISIT_TODO Fri 10/14/2005 + * Dump variance info + */ + DisplayWriteFieldPointer( m_pVarianceInfo, + DPtrToPreferredAddr(varianceInfo), EEClassOptionalFields, + EECLASSES ); + } + + DisplayWriteFieldInt( m_cbModuleDynamicID, pClassOptional->m_cbModuleDynamicID, + EEClassOptionalFields, EECLASSES ); + + /* REVISIT_TODO Fri 10/14/2005 + * Use the macros from ConstrainedExecutionRegion.cpp on this? + */ + DisplayWriteFieldUInt( m_dwReliabilityContract, + clazz->GetReliabilityContract(), + EEClassOptionalFields, EECLASSES ); + + DisplayWriteFieldEnumerated( m_SecProps, clazz->GetSecurityProperties()->dwFlags, + EEClassOptionalFields, s_SecurityProperties, W("|"), + EECLASSES ); + + DisplayEndStructure( EECLASSES ); // EEClassOptionalFields + } +} // NativeImageDumper::DumpEEClassForMethodTable +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +enum TypeDescType +{ + TDT_IsTypeDesc, + TDT_IsParamTypeDesc, + TDT_IsArrayTypeDesc, + TDT_IsTypeVarTypeDesc, + TDT_IsFnPtrTypeDesc +}; +const char * const g_typeDescTypeNames[] = +{ + "TypeDesc", + "ParamTypeDesc", + "ArrayTypeDesc", + "TypeVarTypeDesc", + "FnPtrTypeDesc" +}; +int g_typeDescSizes[] = +{ + sizeof(TypeDesc), + sizeof(ParamTypeDesc), + sizeof(ArrayTypeDesc), + sizeof(TypeVarTypeDesc), + -1//sizeof(FnPtrTypeDesc) -- variable size +}; +TypeDescType getTypeDescType( PTR_TypeDesc td ) +{ + _ASSERTE(td != NULL); + if( td->IsArray() ) + return TDT_IsArrayTypeDesc; + if( td->HasTypeParam() ) + return TDT_IsParamTypeDesc; + if( td->IsGenericVariable() ) + return TDT_IsTypeVarTypeDesc; + if( td->GetInternalCorElementType() == ELEMENT_TYPE_FNPTR ) + return TDT_IsFnPtrTypeDesc; + return TDT_IsTypeDesc; +} +NativeImageDumper::EnumMnemonics NativeImageDumper::s_TDFlags[] = +{ + +#define TDF_ENTRY(x) NativeImageDumper::EnumMnemonics(TypeDesc:: x, W(#x) ) + TDF_ENTRY(enum_flag_NeedsRestore), + TDF_ENTRY(enum_flag_PreRestored), + TDF_ENTRY(enum_flag_Unrestored), + TDF_ENTRY(enum_flag_UnrestoredTypeKey), + TDF_ENTRY(enum_flag_IsNotFullyLoaded), + TDF_ENTRY(enum_flag_DependenciesLoaded), +#undef TDF_ENTRY +}; + +NativeImageDumper::EnumMnemonics s_CConv[] = +{ +#define CC_ENTRY(x) NativeImageDumper::EnumMnemonics( x, W(#x) ) + +#define CC_CALLCONV_ENTRY(x) NativeImageDumper::EnumMnemonics( x, IMAGE_CEE_CS_CALLCONV_MASK, W(#x) ) + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_VARARG), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_FIELD), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_LOCAL_SIG), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_PROPERTY), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_UNMGD), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_GENERICINST), + CC_CALLCONV_ENTRY(IMAGE_CEE_CS_CALLCONV_NATIVEVARARG), +#undef CC_CALLCONV_ENTRY + + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_HASTHIS), + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS), + CC_ENTRY(IMAGE_CEE_CS_CALLCONV_GENERIC) +}; + + +void NativeImageDumper::DumpTypeDesc( PTR_TypeDesc td ) +{ + _ASSERTE(CHECK_OPT(TYPEDESCS)); + TypeDescType tdt = getTypeDescType(td); + int size = g_typeDescSizes[(int)tdt]; + if( size == -1 ) + { + _ASSERTE(tdt == TDT_IsFnPtrTypeDesc); + size = FnPtrTypeDesc::DacSize(PTR_TO_TADDR(td)); + } + DisplayStartStructure( g_typeDescTypeNames[(int)tdt], + DPtrToPreferredAddr(td), size, TYPEDESCS ); + + //first handle the fields of typedesc + WriteFieldCorElementType( m_typeAndFlags, td->GetInternalCorElementType(), + TypeDesc, TYPEDESCS ); + DisplayWriteFieldEnumerated( m_typeAndFlags, td->m_typeAndFlags, TypeDesc, + s_TDFlags, W(", "), TYPEDESCS ); + if( tdt == TDT_IsParamTypeDesc || tdt == TDT_IsArrayTypeDesc ) + { + PTR_ParamTypeDesc ptd(td); + DisplayStartVStructure( "ParamTypeDesc", TYPEDESCS ); + WriteFieldMethodTable( m_TemplateMT, ptd->m_TemplateMT.GetValue(), + ParamTypeDesc, TYPEDESCS ); + WriteFieldTypeHandle( m_Arg, ptd->m_Arg, + ParamTypeDesc, TYPEDESCS ); + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(ptd->m_hExposedClassObject), + ParamTypeDesc, TYPEDESCS ); + + DisplayEndVStructure( TYPEDESCS ); //ParamTypeDesc + } + else if( tdt == TDT_IsFnPtrTypeDesc ) + { + PTR_FnPtrTypeDesc ftd(td); + DisplayStartVStructure( "FnPtrTypeDesc", TYPEDESCS ); + DisplayWriteFieldInt( m_NumArgs, ftd->m_NumArgs, FnPtrTypeDesc, + TYPEDESCS ); + DisplayWriteFieldEnumerated( m_CallConv, ftd->m_CallConv, + FnPtrTypeDesc, s_CConv, W(", "), + TYPEDESCS ); + DisplayStartArrayWithOffset( m_RetAndArgTypes, W("[%-4s]: %s"), + FnPtrTypeDesc, TYPEDESCS ); + PTR_TypeHandle args( PTR_HOST_MEMBER_TADDR(FnPtrTypeDesc, ftd, + m_RetAndArgTypes) ); + for( unsigned i = 0; i < ftd->m_NumArgs; ++i ) + { + DisplayStartElement( "Argument", TYPEDESCS ); + DisplayWriteElementInt( "Index", i, TYPEDESCS ); + IF_OPT( TYPEDESCS ) + WriteElementTypeHandle( "TypeHandle", args[i] ); + DisplayEndElement( TYPEDESCS ); + } + DisplayEndArray( "Total Arguments", TYPEDESCS ); + DisplayEndVStructure( TYPEDESCS ); + } + else if( tdt == TDT_IsTypeVarTypeDesc ) + { + PTR_TypeVarTypeDesc tvtd(td); + DisplayStartVStructure( "TypeVarTypeDesc", TYPEDESCS ); + DisplayWriteFieldPointer( m_pModule, + DPtrToPreferredAddr(tvtd->m_pModule), + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldUInt( m_typeOrMethodDef, + tvtd->m_typeOrMethodDef, + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldInt( m_numConstraints, tvtd->m_numConstraints, + TypeVarTypeDesc, TYPEDESCS ); + if( tvtd->m_constraints == NULL ) + { + DisplayWriteFieldPointer( m_constraints, NULL, TypeVarTypeDesc, + TYPEDESCS ); + } + else + { + DisplayStartStructureWithOffset( m_constraints, + DPtrToPreferredAddr(tvtd->m_constraints), + sizeof(*tvtd->m_constraints) * + tvtd->m_numConstraints, + TypeVarTypeDesc, TYPEDESCS ); + DisplayStartArray( "Constraints", NULL, TYPEDESCS ); + for( unsigned i = 0; i < tvtd->m_numConstraints; ++i ) + { + WriteElementTypeHandle( "TypeHandle", tvtd->m_constraints[i] ); + } + DisplayEndArray( "Total Constraints", TYPEDESCS ); //Constraints + DisplayEndStructure( TYPEDESCS ); //m_constraints + } + DisplayWriteFieldPointer( m_hExposedClassObject, + DataPtrToDisplay(tvtd->m_hExposedClassObject), + TypeVarTypeDesc, TYPEDESCS ); + DisplayWriteFieldUInt( m_token, tvtd->m_token, TypeVarTypeDesc, + TYPEDESCS ); + DisplayWriteFieldInt( m_index, tvtd->m_index, TypeVarTypeDesc, + TYPEDESCS ); + + DisplayEndVStructure( TYPEDESCS ); //TypeVarTypeDesc + } + + + DisplayEndStructure( TYPEDESCS ); // g_typeDescTypeNames + +} + +void NativeImageDumper::DumpDictionaryEntry( const char * elementName, + DictionaryEntryKind kind, + PTR_DictionaryEntry entry ) +{ + m_display->StartElement( elementName ); + const char * name = NULL; + switch(kind) + { + case EmptySlot: + m_display->WriteEmptyElement("EmptySlot"); + break; + case TypeHandleSlot: + { + TypeHandle th = dac_cast<DPTR(FixupPointer<TypeHandle>)>(entry)->GetValue(); + WriteElementTypeHandle( "TypeHandle", th ); + /* XXX Fri 03/24/2006 + * There is no straightforward home for these, so make sure to + * record them + */ + if( !CORCOMPILE_IS_POINTER_TAGGED(th.AsTAddr()) && th.IsTypeDesc() ) + { + PTR_TypeDesc td(th.AsTypeDesc()); + if( isInRange(PTR_TO_TADDR(td)) ) + { + m_discoveredTypeDescs.AppendEx(td); + } + } + } + break; + case MethodDescSlot: + { + TempBuffer buf; + PTR_MethodDesc md(TO_TADDR(*entry)); + WriteElementMethodDesc( "MethodDesc", md ); + } + break; + case MethodEntrySlot: + name = "MethodEntry"; + goto StandardEntryDisplay; + case ConstrainedMethodEntrySlot: + name = "ConstrainedMethodEntry"; + goto StandardEntryDisplay; + case DispatchStubAddrSlot: + name = "DispatchStubAddr"; + goto StandardEntryDisplay; + /* REVISIT_TODO Tue 10/11/2005 + * Print out name information here + */ + case FieldDescSlot: + name = "FieldDescSlot"; +StandardEntryDisplay: + m_display->WriteElementPointer(name, DataPtrToDisplay((TADDR)*entry)); + break; + default: + _ASSERTE( !"unreachable" ); + } + m_display->EndElement(); //elementName +} + +#ifdef FEATURE_READYTORUN +IMAGE_DATA_DIRECTORY * NativeImageDumper::FindReadyToRunSection(DWORD type) +{ + PTR_READYTORUN_SECTION pSections = dac_cast<PTR_READYTORUN_SECTION>(dac_cast<TADDR>(m_pReadyToRunHeader) + sizeof(READYTORUN_HEADER)); + for (DWORD i = 0; i < m_pReadyToRunHeader->NumberOfSections; i++) + { + // Verify that section types are sorted + _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type)); + + READYTORUN_SECTION * pSection = pSections + i; + if (pSection->Type == type) + return &pSection->Section; + } + return NULL; +} + +// +// Ready to Run specific dumping methods +// +void NativeImageDumper::DumpReadyToRun() +{ + m_pReadyToRunHeader = m_decoder.GetReadyToRunHeader(); + + m_nativeReader = NativeFormat::NativeReader(dac_cast<PTR_BYTE>(m_decoder.GetBase()), m_decoder.GetVirtualSize()); + + IMAGE_DATA_DIRECTORY * pRuntimeFunctionsDir = FindReadyToRunSection(READYTORUN_SECTION_RUNTIME_FUNCTIONS); + if (pRuntimeFunctionsDir != NULL) + { + m_pRuntimeFunctions = dac_cast<PTR_RUNTIME_FUNCTION>(m_decoder.GetDirectoryData(pRuntimeFunctionsDir)); + m_nRuntimeFunctions = pRuntimeFunctionsDir->Size / sizeof(T_RUNTIME_FUNCTION); + } + else + { + m_nRuntimeFunctions = 0; + } + + IMAGE_DATA_DIRECTORY * pEntryPointsDir = FindReadyToRunSection(READYTORUN_SECTION_METHODDEF_ENTRYPOINTS); + if (pEntryPointsDir != NULL) + m_methodDefEntryPoints = NativeFormat::NativeArray((TADDR)&m_nativeReader, pEntryPointsDir->VirtualAddress); + + DisplayStartCategory("NativeInfo", NATIVE_INFO); + + IF_OPT(NATIVE_INFO) + DumpReadyToRunHeader(); + + DisplayEndCategory(NATIVE_INFO); //NativeInfo + + IF_OPT_OR3(METHODS, GC_INFO, DISASSEMBLE_CODE) + DumpReadyToRunMethods(); + + IF_OPT(RELOCATIONS) + DumpBaseRelocs(); +} + +const NativeImageDumper::EnumMnemonics s_ReadyToRunFlags[] = +{ +#define RTR_FLAGS(f) NativeImageDumper::EnumMnemonics(f, W(#f)) + RTR_FLAGS(READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE), +#undef RTR_FLAGS +}; + +void NativeImageDumper::DumpReadyToRunHeader() +{ + IF_OPT(NATIVE_INFO) + { + m_display->StartStructure( "READYTORUN_HEADER", + DPtrToPreferredAddr(dac_cast<PTR_READYTORUN_HEADER>(m_pReadyToRunHeader)), + sizeof(*m_pReadyToRunHeader) ); + + DisplayWriteFieldUInt( Signature, m_pReadyToRunHeader->Signature, READYTORUN_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MajorVersion, m_pReadyToRunHeader->MajorVersion, READYTORUN_HEADER, ALWAYS ); + DisplayWriteFieldUInt( MinorVersion, m_pReadyToRunHeader->MinorVersion, READYTORUN_HEADER, ALWAYS ); + + DisplayWriteFieldEnumerated( Flags, m_pReadyToRunHeader->Flags, + READYTORUN_HEADER, s_ReadyToRunFlags, W(", "), + NATIVE_INFO ); + + m_display->EndStructure(); //READYTORUN_HEADER + } +} + +void NativeImageDumper::DumpReadyToRunMethods() +{ + DisplayStartArray("Methods", NULL, METHODS); + + for (uint rid = 1; rid <= m_methodDefEntryPoints.GetCount(); rid++) + { + uint offset; + if (!m_methodDefEntryPoints.TryGetAt(rid - 1, &offset)) + continue; + + uint id; + offset = m_nativeReader.DecodeUnsigned(offset, &id); + + if (id & 1) + { + if (id & 2) + { + uint val; + m_nativeReader.DecodeUnsigned(offset, &val); + offset -= val; + } + + // TODO: Dump fixups from dac_cast<TADDR>(m_pLayout->GetBase()) + offset + + id >>= 2; + } + else + { + id >>= 1; + } + + _ASSERTE(id < m_nRuntimeFunctions); + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctions + id; + PCODE pEntryPoint = dac_cast<TADDR>(m_decoder.GetBase()) + pRuntimeFunction->BeginAddress; + + SString buf; + AppendTokenName(TokenFromRid(rid, mdtMethodDef), buf, m_import); + + DumpReadyToRunMethod(pEntryPoint, pRuntimeFunction, buf); + } + + DisplayEndArray("Total Methods", METHODS); //Methods +} + +extern PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ SIZE_T * pSize); + +void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNCTION pRuntimeFunction, SString& name) +{ + //Read the GCInfo to get the total method size. + unsigned methodSize = 0; + unsigned gcInfoSize = UINT_MAX; + + SIZE_T nUnwindDataSize; + PTR_VOID pUnwindData = GetUnwindDataBlob(dac_cast<TADDR>(m_decoder.GetBase()), pRuntimeFunction, &nUnwindDataSize); + + // GCInfo immediatelly follows unwind data + PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(pUnwindData) + nUnwindDataSize; + + void(*stringOutFn)(const char *, ...); + IF_OPT(GC_INFO) + { + stringOutFn = stringOut; + } + else + { + stringOutFn = nullStringOut; + } + if (gcInfo != NULL) + { + PTR_CBYTE curGCInfoPtr = gcInfo; + g_holdStringOutData.Clear(); + GCDump gcDump(GCINFO_VERSION); + gcDump.gcPrintf = stringOutFn; +#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) + UINT32 r2rversion = m_pReadyToRunHeader->MajorVersion; + UINT32 gcInfoVersion = GCInfoToken::ReadyToRunVersionToGcInfoVersion(r2rversion); + GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, gcInfoVersion }, DECODE_CODE_LENGTH); + methodSize = gcInfoDecoder.GetCodeLength(); +#endif + + //dump the data to a string first so we can get the gcinfo size. +#ifdef _TARGET_X86_ + InfoHdr hdr; + stringOutFn("method info Block:\n"); + curGCInfoPtr += gcDump.DumpInfoHdr(curGCInfoPtr, &hdr, &methodSize, 0); + stringOutFn("\n"); +#endif + + IF_OPT(METHODS) + { +#ifdef _TARGET_X86_ + stringOutFn("PointerTable:\n"); + curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr, + hdr, + methodSize, 0); + gcInfoSize = curGCInfoPtr - gcInfo; +#elif defined(USE_GC_INFO_DECODER) + stringOutFn("PointerTable:\n"); + curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr, + methodSize, 0); + gcInfoSize = (unsigned)(curGCInfoPtr - gcInfo); +#endif + } + + //data is output below. + } + + DisplayStartElement("Method", METHODS); + DisplayWriteElementStringW("Name", (const WCHAR *)name, METHODS); + + DisplayStartStructure("GCInfo", + DPtrToPreferredAddr(gcInfo), + gcInfoSize, + METHODS); + + DisplayStartTextElement("Contents", GC_INFO); + DisplayWriteXmlTextBlock(("%S", (const WCHAR *)g_holdStringOutData), GC_INFO); + DisplayEndTextElement(GC_INFO); //Contents + + DisplayEndStructure(METHODS); //GCInfo + + DisplayStartStructure("Code", DataPtrToDisplay(pEntryPoint), methodSize, + METHODS); + + IF_OPT(DISASSEMBLE_CODE) + { + // Disassemble hot code. Read the code into the host process. + /* REVISIT_TODO Mon 10/24/2005 + * Is this align up right? + */ + BYTE * codeStartHost = + reinterpret_cast<BYTE*>(PTR_READ(pEntryPoint, + (ULONG32)ALIGN_UP(methodSize, + CODE_SIZE_ALIGN))); + DisassembleMethod(codeStartHost, methodSize); + } + + DisplayEndStructure(METHODS); //Code + + DisplayEndElement(METHODS); //Method +} +#endif // FEATURE_READYTORUN + +#if 0 +void NativeImageDumper::RecordTypeRef( mdTypeRef token, PTR_MethodTable mt ) +{ + if( mt != NULL ) + m_mtToTypeRefMap.Add( mt, token ); +} +mdTypeRef NativeImageDumper::FindTypeRefForMT( PTR_MethodTable mt ) +{ + return m_mtToTypeRefMap.Find(mt); +} +#endif + + +/* REVISIT_TODO Mon 10/10/2005 + * Here is where it gets bad. There is no DAC build of gcdump, so instead + * build it directly into the the dac. That's what all these ugly defines + * are all about. + */ +#ifdef __MSC_VER +#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data +#pragma warning(disable:4189) // local variable is initialized but not referenced +#endif // __MSC_VER + +#undef assert +#define assert(a) +#define NOTHROW +#define GC_NOTRIGGER +#include <gcdecoder.cpp> +#undef NOTHROW +#undef GC_NOTRIGGER + +#if defined _DEBUG && defined _TARGET_X86_ +// disable FPO for checked build +#pragma optimize("y", off) +#endif + +#undef _ASSERTE +#define _ASSERTE(a) do {} while (0) +#ifdef _TARGET_X86_ +#include <gcdump.cpp> +#endif + +#undef LIMITED_METHOD_CONTRACT +#undef WRAPPER_NO_CONTRACT +#ifdef _TARGET_X86_ +#include <i386/gcdumpx86.cpp> +#else // !_TARGET_X86_ +#undef PREGDISPLAY +#include <gcdumpnonx86.cpp> +#endif // !_TARGET_X86_ + +#ifdef __MSC_VER +#pragma warning(default:4244) +#pragma warning(default:4189) +#endif // __MSC_VER + + +#else //!FEATURE_PREJIT +//dummy implementation for dac +HRESULT ClrDataAccess::DumpNativeImage(CLRDATA_ADDRESS loadedBase, + LPCWSTR name, + IXCLRDataDisplay * display, + IXCLRLibrarySupport * support, + IXCLRDisassemblySupport * dis) +{ + return E_FAIL; +} +#endif //FEATURE_PREJIT |