From: Tomáš Rylek Date: Tue, 30 Apr 2019 21:16:46 +0000 (+0200) Subject: Support for larger version bubbles in R2RDump (dotnet/coreclr#24277) X-Git-Tag: submit/tizen/20210909.063632~11030^2~1673 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f2744c685b3be4f29c39c4660a988a30c9350fd3;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Support for larger version bubbles in R2RDump (dotnet/coreclr#24277) After JanV fixed basically all test failures related to larger version bubbles originally implemented by Andon, it's high time to fix R2RDump for its support as a prerequisite to enabling larger version bubbles in CPAOT. This change implements this support via the following particular changes: 1) In DumpOptions, I added a list of explicit assembly references, another list of reference paths that are used for resolving assembly references, a cache for repeated assembly lookups and a new method FindAssembly for file resolution of a given simple assembly name. 2) I have moved ECMA metadata-related logic from R2RReader to its new base class EcmaMetadataReader. This class can represent both R2R and MSIL binaries and adds the logic for reference assembly lookup. 3) Large version bubble R2R PE exe's contain a special R2R header table READYTORUN_SECTION_MANIFEST_METADATA that contains ECMA metadata with a list of extra AssemblyRef's on top of those stored in the original MSIL. I have added support for parsing this table to the R2RReader constructor. 4) I have modified R2RSignature to be based off the new EcmaMetadataReader instead of R2RReader which cannot represent arbitrary reference assemblies. I have patched the two places dealing with module overrides to use the new logic to temporarily switch to a "remote module" SignatureDecoder for the purpose of parsing an external entity. 5) I have removed temporary hacks Andon put in place to keep R2RDump alive in some cases with larger version bubbles without proper support for parsing module overrides. 6) I have added logic for dumping the contents of the manifest metadata along with the standard AssemblyRef table to the header dump as I fought with recalculation of the indices when debugging larger version bubble code for quite some time. 7) As an extra half-forgotten bit from the past I added dumping of unboxing and instantiation stubs. I have noticed that the MSIL AssemblyRef count was off by one. I have also found out that XML output has bitrotten over time. This additional delta fixes both issues. [That forced me to add a bunch of empty ctors even though in practice we don't actually serialize anything into the dumper, we only use the XML serializer to emit the XML output file. Thanks Tomas Commit migrated from https://github.com/dotnet/coreclr/commit/fc05a4297c903c1456762d89913d79e1780b55b1 --- diff --git a/src/coreclr/src/tools/r2rdump/GCRefMap.cs b/src/coreclr/src/tools/r2rdump/GCRefMap.cs index 55c06ad..ba3dc6e 100644 --- a/src/coreclr/src/tools/r2rdump/GCRefMap.cs +++ b/src/coreclr/src/tools/r2rdump/GCRefMap.cs @@ -41,6 +41,10 @@ namespace R2RDump public readonly uint StackPop; public readonly GCRefMapEntry[] Entries; + public GCRefMap() + { + } + public GCRefMap(uint stackPop, GCRefMapEntry[] entries) { StackPop = stackPop; diff --git a/src/coreclr/src/tools/r2rdump/R2RDump.cs b/src/coreclr/src/tools/r2rdump/R2RDump.cs index 07e3298..cb5310b 100644 --- a/src/coreclr/src/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/src/tools/r2rdump/R2RDump.cs @@ -26,6 +26,49 @@ namespace R2RDump public bool GC; public bool SectionContents; public bool EntryPoints; + + public IReadOnlyList ReferenceAssemblies = Array.Empty(); + public IReadOnlyList ReferencePaths = Array.Empty(); + public Dictionary AssemblyCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Probing extensions to use when looking up assemblies under reference paths. + /// + private readonly static string[] ProbeExtensions = new string[] { ".ni.exe", ".ni.dll", ".exe", ".dll" }; + + /// + /// Try to locate a (reference) assembly using the list of explicit reference assemblies + /// and the list of reference paths passed to R2RDump. + /// + /// Simple name of the assembly to look up + /// Name of assembly from which we're performing the lookup + /// + public string FindAssembly(string simpleName, string parentFile) + { + foreach (string refAsm in ReferenceAssemblies) + { + if (Path.GetFileNameWithoutExtension(refAsm).Equals(simpleName, StringComparison.OrdinalIgnoreCase)) + { + return refAsm; + } + } + + IEnumerable allRefPaths = new string[] { Path.GetDirectoryName(parentFile) }.Concat(ReferencePaths); + + foreach (string refPath in allRefPaths) + { + foreach (string extension in ProbeExtensions) + { + string probeFile = Path.Combine(refPath, simpleName + extension); + if (File.Exists(probeFile)) + { + return probeFile; + } + } + } + + return null; + } } public abstract class Dumper @@ -131,7 +174,7 @@ namespace R2RDump syntax.DefineOption("naked", ref _options.Naked, "Naked dump suppresses most compilation details like placement addresses"); syntax.DefineOptionList("q|query", ref _queries, "Query method by exact name, signature, row id or token"); syntax.DefineOptionList("k|keyword", ref _keywords, "Search method by keyword"); - syntax.DefineOptionList("r|runtimefunction", ref _runtimeFunctions, ArgStringToInt, "Get one runtime function by id or relative virtual address"); + syntax.DefineOptionList("f|runtimefunction", ref _runtimeFunctions, ArgStringToInt, "Get one runtime function by id or relative virtual address"); syntax.DefineOptionList("s|section", ref _sections, "Get section by keyword"); syntax.DefineOption("unwind", ref _options.Unwind, "Dump unwindInfo"); syntax.DefineOption("gc", ref _options.GC, "Dump gcInfo and slot table"); @@ -141,6 +184,8 @@ namespace R2RDump syntax.DefineOption("v|verbose", ref verbose, "Dump disassembly, unwindInfo, gcInfo and section contents"); syntax.DefineOption("diff", ref _diff, "Compare two R2R images"); syntax.DefineOption("ignoreSensitive", ref _ignoreSensitive, "Ignores sensitive properties in xml dump to avoid failing tests"); + syntax.DefineOptionList("r|reference", ref _options.ReferenceAssemblies, "Explicit reference assembly files"); + syntax.DefineOptionList("rp|referencepath", ref _options.ReferencePaths, "Search paths for reference assemblies"); }); if (verbose) diff --git a/src/coreclr/src/tools/r2rdump/R2RImportSection.cs b/src/coreclr/src/tools/r2rdump/R2RImportSection.cs index 8e5e32e..b0ebcf0 100644 --- a/src/coreclr/src/tools/r2rdump/R2RImportSection.cs +++ b/src/coreclr/src/tools/r2rdump/R2RImportSection.cs @@ -27,6 +27,10 @@ namespace R2RDump public string Signature { get; set; } public GCRefMap GCRefMap { get; set; } + public ImportSectionEntry() + { + } + public ImportSectionEntry(int index, int startOffset, int startRVA, long section, uint signatureRVA, string signature) { Index = index; diff --git a/src/coreclr/src/tools/r2rdump/R2RReader.cs b/src/coreclr/src/tools/r2rdump/R2RReader.cs index 62bf8a1..2574f4d 100644 --- a/src/coreclr/src/tools/r2rdump/R2RReader.cs +++ b/src/coreclr/src/tools/r2rdump/R2RReader.cs @@ -75,7 +75,7 @@ namespace R2RDump } } - public class R2RReader + public class EcmaMetadataReader { /// /// Option are used to specify details of signature formatting. @@ -94,6 +94,18 @@ namespace R2RDump public readonly MetadataReader MetadataReader; /// + /// Extra reference assemblies parsed from the manifest metadata. + /// Only used by R2R assemblies with larger version bubble. + /// The manifest contains extra assembly references created by resolved + /// inlines and facades (non-existent in the source MSIL). + /// In module overrides, these assembly references are represented + /// by indices larger than the number of AssemblyRef rows in MetadataReader. + /// The list originates in the top-level R2R image and is copied + /// to all reference assemblies for the sake of simplicity. + /// + public readonly List ManifestReferenceAssemblies; + + /// /// Byte array containing the ReadyToRun image /// public byte[] Image { get; } @@ -104,6 +116,82 @@ namespace R2RDump public string Filename { get; set; } /// + /// The default constructor initializes an empty metadata reader. + /// + public EcmaMetadataReader() + { + } + + /// + /// Open an MSIL binary and locate the metadata blob. + /// + /// Ambient options to use + /// PE image + /// List of reference assemblies from the R2R metadata manifest + /// The Cor header flag must be ILLibrary + public unsafe EcmaMetadataReader(DumpOptions options, string filename, List manifestReferenceAssemblies) + { + Options = options; + Filename = filename; + ManifestReferenceAssemblies = manifestReferenceAssemblies; + Image = File.ReadAllBytes(filename); + + fixed (byte* p = Image) + { + IntPtr ptr = (IntPtr)p; + PEReader = new PEReader(p, Image.Length); + + if (!PEReader.HasMetadata) + { + throw new Exception($"ECMA metadata not found in file '{filename}'"); + } + + MetadataReader = PEReader.GetMetadataReader(); + } + } + + /// + /// Open a given reference assembly (relative to this ECMA metadata file). + /// + /// Reference assembly index + /// EcmaMetadataReader instance representing the reference assembly + public EcmaMetadataReader OpenReferenceAssembly(int refAsmIndex) + { + if (refAsmIndex == 0) + { + return this; + } + + int assemblyRefCount = MetadataReader.GetTableRowCount(TableIndex.AssemblyRef); + string name; + if (refAsmIndex <= assemblyRefCount) + { + AssemblyReference asmRef = MetadataReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(refAsmIndex)); + name = MetadataReader.GetString(asmRef.Name); + } + else + { + name = ManifestReferenceAssemblies[refAsmIndex - assemblyRefCount - 2]; + } + + EcmaMetadataReader ecmaReader; + if (!Options.AssemblyCache.TryGetValue(name, out ecmaReader)) + { + string assemblyPath = Options.FindAssembly(name, Filename); + if (assemblyPath == null) + { + throw new Exception($"Missing reference assembly: {name}"); + } + ecmaReader = new EcmaMetadataReader(Options, assemblyPath, ManifestReferenceAssemblies); + Options.AssemblyCache.Add(name, ecmaReader); + } + return ecmaReader; + } + } + + public class R2RReader : EcmaMetadataReader + { + /// /// True if the image is ReadyToRun /// public bool IsR2R { get; set; } @@ -176,7 +264,7 @@ namespace R2RDump private Dictionary _runtimeFunctionToDebugInfo = new Dictionary(); - public unsafe R2RReader() { } + public R2RReader() { } /// /// Initializes the fields of the R2RHeader and R2RMethods @@ -184,118 +272,121 @@ namespace R2RDump /// PE image /// The Cor header flag must be ILLibrary public unsafe R2RReader(DumpOptions options, string filename) + : base(options, filename, new List()) { - Options = options; - Filename = filename; - Image = File.ReadAllBytes(filename); - - fixed (byte* p = Image) + IsR2R = ((PEReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) != 0); + if (!IsR2R) { - IntPtr ptr = (IntPtr)p; - PEReader = new PEReader(p, Image.Length); + throw new BadImageFormatException("The file is not a ReadyToRun image"); + } - IsR2R = ((PEReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) != 0); - if (!IsR2R) + uint machine = (uint)PEReader.PEHeaders.CoffHeader.Machine; + OS = OperatingSystem.Unknown; + foreach (OperatingSystem os in Enum.GetValues(typeof(OperatingSystem))) + { + Machine = (Machine)(machine ^ (uint)os); + if (Enum.IsDefined(typeof(Machine), Machine)) { - throw new BadImageFormatException("The file is not a ReadyToRun image"); + OS = os; + break; } + } + if (OS == OperatingSystem.Unknown) + { + throw new BadImageFormatException($"Invalid Machine: {machine}"); + } - uint machine = (uint)PEReader.PEHeaders.CoffHeader.Machine; - OS = OperatingSystem.Unknown; - foreach(OperatingSystem os in Enum.GetValues(typeof(OperatingSystem))) - { - Machine = (Machine)(machine ^ (uint)os); - if (Enum.IsDefined(typeof(Machine), Machine)) - { - OS = os; - break; - } - } - if (OS == OperatingSystem.Unknown) - { - throw new BadImageFormatException($"Invalid Machine: {machine}"); - } + switch (Machine) + { + case Machine.I386: + Architecture = Architecture.X86; + PointerSize = 4; + break; - switch (Machine) - { - case Machine.I386: - Architecture = Architecture.X86; - PointerSize = 4; - break; + case Machine.Amd64: + Architecture = Architecture.X64; + PointerSize = 8; + break; - case Machine.Amd64: - Architecture = Architecture.X64; - PointerSize = 8; - break; + case Machine.Arm: + case Machine.Thumb: + case Machine.ArmThumb2: + Architecture = Architecture.Arm; + PointerSize = 4; + break; - case Machine.Arm: - case Machine.Thumb: - case Machine.ArmThumb2: - Architecture = Architecture.Arm; - PointerSize = 4; - break; + case Machine.Arm64: + Architecture = Architecture.Arm64; + PointerSize = 8; + break; - case Machine.Arm64: - Architecture = Architecture.Arm64; - PointerSize = 8; - break; + default: + throw new NotImplementedException(Machine.ToString()); + } - default: - throw new NotImplementedException(Machine.ToString()); - } + ImageBase = PEReader.PEHeaders.PEHeader.ImageBase; - ImageBase = PEReader.PEHeaders.PEHeader.ImageBase; + // initialize R2RHeader + DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; + int r2rHeaderOffset = GetOffset(r2rHeaderDirectory.RelativeVirtualAddress); + R2RHeader = new R2RHeader(Image, r2rHeaderDirectory.RelativeVirtualAddress, r2rHeaderOffset); + if (r2rHeaderDirectory.Size != R2RHeader.Size) + { + throw new BadImageFormatException("The calculated size of the R2RHeader doesn't match the size saved in the ManagedNativeHeaderDirectory"); + } - // initialize R2RHeader - DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; - int r2rHeaderOffset = GetOffset(r2rHeaderDirectory.RelativeVirtualAddress); - R2RHeader = new R2RHeader(Image, r2rHeaderDirectory.RelativeVirtualAddress, r2rHeaderOffset); - if (r2rHeaderDirectory.Size != R2RHeader.Size) - { - throw new BadImageFormatException("The calculated size of the R2RHeader doesn't match the size saved in the ManagedNativeHeaderDirectory"); - } + ParseDebugInfo(); - if (PEReader.HasMetadata) + if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA)) + { + R2RSection manifestMetadata = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA]; + fixed (byte* image = Image) { - MetadataReader = PEReader.GetMetadataReader(); - - ParseDebugInfo(); - - if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO)) + MetadataReader manifestReader = new MetadataReader(image + GetOffset(manifestMetadata.RelativeVirtualAddress), manifestMetadata.Size); + int assemblyRefCount = manifestReader.GetTableRowCount(TableIndex.AssemblyRef); + for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) { - R2RSection exceptionInfoSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO]; - EHLookupTable = new EHLookupTable(Image, GetOffset(exceptionInfoSection.RelativeVirtualAddress), exceptionInfoSection.Size); + AssemblyReferenceHandle asmRefHandle = MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex); + AssemblyReference asmRef = manifestReader.GetAssemblyReference(asmRefHandle); + string asmRefName = manifestReader.GetString(asmRef.Name); + ManifestReferenceAssemblies.Add(asmRefName); } + } + } - ImportSections = new List(); - ImportCellNames = new Dictionary(); - ParseImportSections(); - - R2RMethods = new List(); - InstanceMethods = new List(); + if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO)) + { + R2RSection exceptionInfoSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_EXCEPTION_INFO]; + EHLookupTable = new EHLookupTable(Image, GetOffset(exceptionInfoSection.RelativeVirtualAddress), exceptionInfoSection.Size); + } - if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS)) - { - int runtimeFunctionSize = CalculateRuntimeFunctionSize(); - R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS]; + ImportSections = new List(); + ImportCellNames = new Dictionary(); + ParseImportSections(); - uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize); - int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress); - bool[] isEntryPoint = new bool[nRuntimeFunctions]; + R2RMethods = new List(); + InstanceMethods = new List(); - // initialize R2RMethods - ParseMethodDefEntrypoints(isEntryPoint); - ParseInstanceMethodEntrypoints(isEntryPoint); - ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize); - } + if (R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS)) + { + int runtimeFunctionSize = CalculateRuntimeFunctionSize(); + R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS]; - AvailableTypes = new List(); - ParseAvailableTypes(); + uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize); + int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress); + bool[] isEntryPoint = new bool[nRuntimeFunctions]; - CompilerIdentifier = ParseCompilerIdentifier(); - } + // initialize R2RMethods + ParseMethodDefEntrypoints(isEntryPoint); + ParseInstanceMethodEntrypoints(isEntryPoint); + ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize); } + + AvailableTypes = new List(); + ParseAvailableTypes(); + + CompilerIdentifier = ParseCompilerIdentifier(); } public bool InputArchitectureSupported() @@ -470,7 +561,7 @@ namespace R2RDump { Console.WriteLine($"Warning: Could not parse GC Info for method: {method.SignatureString}"); } - + } } else if (Machine == Machine.I386) diff --git a/src/coreclr/src/tools/r2rdump/R2RSection.cs b/src/coreclr/src/tools/r2rdump/R2RSection.cs index 0c715cc..3ba922b 100644 --- a/src/coreclr/src/tools/r2rdump/R2RSection.cs +++ b/src/coreclr/src/tools/r2rdump/R2RSection.cs @@ -27,7 +27,8 @@ namespace R2RDump READYTORUN_SECTION_AVAILABLE_TYPES = 108, READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS = 109, READYTORUN_SECTION_INLINING_INFO = 110, - READYTORUN_SECTION_PROFILEDATA_INFO = 111 + READYTORUN_SECTION_PROFILEDATA_INFO = 111, + READYTORUN_SECTION_MANIFEST_METADATA = 112, // Added in v2.3 } /// diff --git a/src/coreclr/src/tools/r2rdump/R2RSignature.cs b/src/coreclr/src/tools/r2rdump/R2RSignature.cs index 9bf4412..a316953 100644 --- a/src/coreclr/src/tools/r2rdump/R2RSignature.cs +++ b/src/coreclr/src/tools/r2rdump/R2RSignature.cs @@ -41,9 +41,9 @@ namespace R2RDump return formatter.EmitHandleName(handle, namespaceQualified, owningTypeOverride); } - public static string FormatSignature(DumpOptions options, R2RReader r2rReader, int imageOffset) + public static string FormatSignature(DumpOptions options, EcmaMetadataReader ecmaReader, int imageOffset) { - SignatureDecoder decoder = new SignatureDecoder(options, r2rReader, imageOffset); + SignatureDecoder decoder = new SignatureDecoder(options, ecmaReader, imageOffset); string result = decoder.ReadR2RSignature(); return result; } @@ -74,6 +74,9 @@ namespace R2RDump case HandleKind.TypeDefinition: return EmitTypeDefinitionName((TypeDefinitionHandle)handle, namespaceQualified); + case HandleKind.FieldDefinition: + return EmitFieldDefinitionName((FieldDefinitionHandle)handle, namespaceQualified, owningTypeOverride); + default: throw new NotImplementedException(); } @@ -279,6 +282,26 @@ namespace R2RDump return typeSpec.DecodeSignature(this, genericContext); } + /// + /// Emit the textual representation of a FieldDef metadata record. + /// + /// Field definition handle to format + /// True = display namespace information for the owning type + /// Owning type override when non-null + /// Textual representation of the field declaration + private string EmitFieldDefinitionName(FieldDefinitionHandle fieldDefHandle, bool namespaceQualified, string owningTypeOverride) + { + FieldDefinition fieldDef = _metadataReader.GetFieldDefinition(fieldDefHandle); + DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty(), Array.Empty()); + StringBuilder output = new StringBuilder(); + output.Append(fieldDef.DecodeSignature(this, genericContext)); + output.Append(' '); + output.Append(EmitHandleName(fieldDef.GetDeclaringType(), namespaceQualified, owningTypeOverride)); + output.Append('.'); + output.Append(_metadataReader.GetString(fieldDef.Name)); + return output.ToString(); + } + private string EmitString(StringHandle handle) { return _metadataReader.GetString(handle); @@ -291,9 +314,9 @@ namespace R2RDump public class SignatureDecoder { /// - /// Metadata reader is used to access the embedded MSIL metadata blob in the R2R file. + /// ECMA reader is used to access the embedded MSIL metadata blob in the R2R file. /// - private readonly MetadataReader _metadataReader; + private readonly EcmaMetadataReader _ecmaReader; /// /// Dump options are used to specify details of signature formatting. @@ -318,26 +341,27 @@ namespace R2RDump /// /// Construct the signature decoder by storing the image byte array and offset within the array. /// - /// R2RReader object representing the R2R PE file - /// Signature offset within the array - /// Formatting options - public SignatureDecoder(DumpOptions options, R2RReader reader, int offset) + /// Dump options and paths + /// EcmaMetadataReader object representing the PE file containing the ECMA metadata + /// Signature offset within the PE file byte array + public SignatureDecoder(DumpOptions options, EcmaMetadataReader ecmaReader, int offset) { - _metadataReader = reader.MetadataReader; + _ecmaReader = ecmaReader; _options = options; - _image = reader.Image; + _image = ecmaReader.Image; _offset = offset; } /// /// Construct the signature decoder by storing the image byte array and offset within the array. /// - /// Metadata reader for the R2R image + /// Dump options and paths + /// Metadata reader for the R2R image /// Signature to parse - /// Optional signature offset within the signature byte array, 0 by default - public SignatureDecoder(DumpOptions options, MetadataReader metadataReader, byte[] signature, int offset = 0) + /// Signature offset within the signature byte array + public SignatureDecoder(DumpOptions options, EcmaMetadataReader ecmaReader, byte[] signature, int offset) { - _metadataReader = metadataReader; + _ecmaReader = ecmaReader; _options = options; _image = signature; _offset = offset; @@ -465,21 +489,34 @@ namespace R2RDump /// /// Parse the signature into a given output string builder. /// - /// + /// Output signature builder private void ParseSignature(StringBuilder builder) { uint fixupType = ReadByte(); bool moduleOverride = (fixupType & (byte)CORCOMPILE_FIXUP_BLOB_KIND.ENCODE_MODULE_OVERRIDE) != 0; + SignatureDecoder moduleDecoder = this; + // Check first byte for a module override being encoded if (moduleOverride) { - builder.Append("ENCODE_MODULE_OVERRIDE @ "); fixupType &= ~(uint)CORCOMPILE_FIXUP_BLOB_KIND.ENCODE_MODULE_OVERRIDE; - uint moduleIndex = ReadUInt(); - builder.Append(string.Format(" Index: {0:X2}", moduleIndex)); + int moduleIndex = (int)ReadUInt(); + EcmaMetadataReader refAsmEcmaReader = _ecmaReader.OpenReferenceAssembly(moduleIndex); + moduleDecoder = new SignatureDecoder(_options, refAsmEcmaReader, _image, _offset); } - switch ((ReadyToRunFixupKind)fixupType) + moduleDecoder.ParseSignature((ReadyToRunFixupKind)fixupType, builder); + _offset = moduleDecoder.Offset; + } + + /// + /// Parse the signature with a given fixup type after module overrides have been resolved. + /// + /// Fixup type to parse + /// Output signature builder + private void ParseSignature(ReadyToRunFixupKind fixupType, StringBuilder builder) + { + switch (fixupType) { case ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup: builder.Append("THISOBJ_DICTIONARY_LOOKUP @ "); @@ -509,8 +546,8 @@ namespace R2RDump break; case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldHandle: - builder.Append("FIELD_HANDLE"); - // TODO + ParseField(builder); + builder.Append(" (FIELD_HANDLE)"); break; @@ -520,10 +557,7 @@ namespace R2RDump break; case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_DefToken: - if (!moduleOverride) - { - ParseMethodDefToken(builder, owningTypeOverride: null); - } + ParseMethodDefToken(builder, owningTypeOverride: null); builder.Append(" (METHOD_ENTRY"); builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)"); break; @@ -536,10 +570,7 @@ namespace R2RDump case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry: - if(!moduleOverride) - { - ParseMethod(builder); - } + ParseMethod(builder); builder.Append(" (VIRTUAL_ENTRY)"); break; @@ -558,10 +589,7 @@ namespace R2RDump case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_Slot: { uint slot = ReadUInt(); - if (!moduleOverride) - { - ParseType(builder); - } + ParseType(builder); builder.Append($@" #{slot} (VIRTUAL_ENTRY_SLOT)"); } @@ -893,7 +921,13 @@ namespace R2RDump break; case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG: - builder.Append("module_zapsig"); + { + int moduleIndex = (int)ReadUInt(); + EcmaMetadataReader refAsmReader = _ecmaReader.OpenReferenceAssembly(moduleIndex); + SignatureDecoder refAsmDecoder = new SignatureDecoder(_options, refAsmReader, _image, _offset); + refAsmDecoder.ParseType(builder); + _offset = refAsmDecoder.Offset; + } break; default: @@ -919,7 +953,7 @@ namespace R2RDump private void ParseTypeToken(StringBuilder builder) { uint token = ReadToken(); - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)token))); + builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)token))); } /// @@ -929,10 +963,20 @@ namespace R2RDump private void ParseMethod(StringBuilder builder) { uint methodFlags = ReadUInt(); + + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0) + { + builder.Append("[UNBOX] "); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0) + { + builder.Append("[INST] "); + } + string owningTypeOverride = null; if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0) { - SignatureDecoder owningTypeDecoder = new SignatureDecoder(_options, _metadataReader, _image, _offset); + SignatureDecoder owningTypeDecoder = new SignatureDecoder(_options, _ecmaReader, _image, _offset); owningTypeOverride = owningTypeDecoder.ReadTypeSignature(); _offset = owningTypeDecoder._offset; } @@ -978,7 +1022,7 @@ namespace R2RDump private void ParseMethodDefToken(StringBuilder builder, string owningTypeOverride) { uint methodDefToken = ReadUInt() | (uint)CorTokenType.mdtMethodDef; - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodDefToken), namespaceQualified: true, owningTypeOverride: owningTypeOverride)); + builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)methodDefToken), namespaceQualified: true, owningTypeOverride: owningTypeOverride)); } /// @@ -989,7 +1033,7 @@ namespace R2RDump private void ParseMethodRefToken(StringBuilder builder, string owningTypeOverride) { uint methodRefToken = ReadUInt() | (uint)CorTokenType.mdtMemberRef; - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodRefToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride)); + builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)methodRefToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride)); } /// @@ -1015,7 +1059,7 @@ namespace R2RDump { fieldToken = ReadUInt() | (uint)CorTokenType.mdtFieldDef; } - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)fieldToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride)); + builder.Append(MetadataNameFormatter.FormatHandle(_ecmaReader.MetadataReader, MetadataTokens.Handle((int)fieldToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride)); } /// @@ -1405,7 +1449,7 @@ namespace R2RDump { uint rid = ReadUInt(); UserStringHandle stringHandle = MetadataTokens.UserStringHandle((int)rid); - builder.Append(_metadataReader.GetUserString(stringHandle)); + builder.Append(_ecmaReader.MetadataReader.GetUserString(stringHandle)); } } } diff --git a/src/coreclr/src/tools/r2rdump/TextDumper.cs b/src/coreclr/src/tools/r2rdump/TextDumper.cs index f31c903..db4c5f3 100644 --- a/src/coreclr/src/tools/r2rdump/TextDumper.cs +++ b/src/coreclr/src/tools/r2rdump/TextDumper.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Text; using System.Xml; @@ -356,6 +358,22 @@ namespace R2RDump } } break; + case R2RSection.SectionType.READYTORUN_SECTION_MANIFEST_METADATA: + int assemblyRefCount = _r2r.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef); + _writer.WriteLine($"MSIL AssemblyRef's ({assemblyRefCount} entries):"); + for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) + { + AssemblyReference assemblyRef = _r2r.MetadataReader.GetAssemblyReference(MetadataTokens.AssemblyReferenceHandle(assemblyRefIndex)); + string assemblyRefName = _r2r.MetadataReader.GetString(assemblyRef.Name); + _writer.WriteLine($"[ID 0x{assemblyRefIndex:X2}]: {assemblyRefName}"); + } + + _writer.WriteLine($"Manifest metadata AssemblyRef's ({_r2r.ManifestReferenceAssemblies.Count} entries):"); + for (int manifestAsmIndex = 0; manifestAsmIndex < _r2r.ManifestReferenceAssemblies.Count; manifestAsmIndex++) + { + _writer.WriteLine($"[ID 0x{manifestAsmIndex + assemblyRefCount + 2:X2}]: {_r2r.ManifestReferenceAssemblies[manifestAsmIndex]}"); + } + break; } } diff --git a/src/coreclr/src/tools/r2rdump/XmlDumper.cs b/src/coreclr/src/tools/r2rdump/XmlDumper.cs index 5f67e27..25bbef5 100644 --- a/src/coreclr/src/tools/r2rdump/XmlDumper.cs +++ b/src/coreclr/src/tools/r2rdump/XmlDumper.cs @@ -38,6 +38,10 @@ namespace R2RDump _ignoredProperties.Add(typeof(RuntimeFunction), "UnwindRVA", attrs); _ignoredProperties.Add(typeof(R2RSection), "RelativeVirtualAddress", attrs); _ignoredProperties.Add(typeof(R2RSection), "Size", attrs); + + XmlAttributes ignoreAlways = new XmlAttributes(); + ignoreAlways.XmlIgnore = true; + _ignoredProperties.Add(typeof(R2RReader), "ImportCellNames", ignoreAlways); } internal override void Begin()