From 030e0af89bb897554acef575075c69aaf5176268 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Thu, 4 Oct 2018 21:29:24 +0200 Subject: [PATCH] R2RDump fixes for dumping method instance entrypoint table (#20243) R2RDump fixes for dumping method instance entrypoint table While investigating Michal's unit test demonstrating a bug in the CPAOT compiler I found out that R2RDump is broken in multiple aspects w.r.t. instantiated methods. Parsing of the method signatures in the instance entrypoint table was just broken and the R2RMethod was duplicating parts of the signature decoding and name formatting process. I created a new flag "normalize" that requests normalization of various R2R tables in the dump aimed at improving diff quality. I have also noticed and fixed somewhat weird formatting of method signatures. As a slight cleanup I have lumped the various dump flags into a helper class DumpOptions. Last but not least I have renamed "Canon" to "__Canon". Thanks Tomas --- src/tools/r2rdump/R2RDump.cs | 96 ++++++++++++++++--------- src/tools/r2rdump/R2RMethod.cs | 147 ++++++++++++++++++-------------------- src/tools/r2rdump/R2RReader.cs | 69 ++++++++++++------ src/tools/r2rdump/R2RSignature.cs | 96 +++++++++++++++++-------- src/tools/r2rdump/TextDumper.cs | 40 +++++------ src/tools/r2rdump/XmlDumper.cs | 46 ++++-------- 6 files changed, 278 insertions(+), 216 deletions(-) diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs index 34b86b7..0901ee6 100644 --- a/src/tools/r2rdump/R2RDump.cs +++ b/src/tools/r2rdump/R2RDump.cs @@ -6,24 +6,60 @@ using System; using System.Collections.Generic; using System.CommandLine; using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Text; using System.Xml; using System.Xml.Serialization; namespace R2RDump { + public class DumpOptions + { + public bool Raw; + public bool Normalize; + public bool Header; + public bool Disasm; + public bool Unwind; + public bool GC; + public bool SectionContents; + } + public abstract class Dumper { - internal R2RReader _r2r; - internal TextWriter _writer; + protected readonly R2RReader _r2r; + protected readonly TextWriter _writer; + protected readonly Disassembler _disassembler; + protected readonly DumpOptions _options; - internal bool _raw; - internal bool _header; - internal bool _disasm; - internal Disassembler _disassembler; - internal bool _unwind; - internal bool _gc; - internal bool _sectionContents; + public Dumper(R2RReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) + { + _r2r = r2r; + _writer = writer; + _disassembler = disassembler; + _options = options; + } + + public IEnumerable NormalizedSections() + { + IEnumerable sections = _r2r.R2RHeader.Sections.Values; + if (_options.Normalize) + { + sections = sections.OrderBy((s) => s.Type); + } + return sections; + } + + public IEnumerable NormalizedMethods() + { + IEnumerable methods = _r2r.R2RMethods; + if (_options.Normalize) + { + methods = methods.OrderBy((m) => m.SignatureString); + } + return methods; + } /// /// Run right before printing output @@ -54,18 +90,13 @@ namespace R2RDump private bool _help; private IReadOnlyList _inputFilenames = Array.Empty(); private string _outputFilename = null; - private bool _xml; - private bool _raw; - private bool _header; - private bool _disasm; + private DumpOptions _options = new DumpOptions(); private IReadOnlyList _queries = Array.Empty(); private IReadOnlyList _keywords = Array.Empty(); private IReadOnlyList _runtimeFunctions = Array.Empty(); private IReadOnlyList _sections = Array.Empty(); private bool _diff; - private bool _unwind; - private bool _gc; - private bool _sectionContents; + private bool _xml; private TextWriter _writer; private Dictionary _selectedSections = new Dictionary(); private Dumper _dumper; @@ -91,16 +122,17 @@ namespace R2RDump syntax.DefineOptionList("i|in", ref _inputFilenames, "Input file(s) to dump. Expects them to by ReadyToRun images"); syntax.DefineOption("o|out", ref _outputFilename, "Output file path. Dumps everything to the specified file except help message and exception messages"); syntax.DefineOption("x|xml", ref _xml, "Output in XML format"); - syntax.DefineOption("raw", ref _raw, "Dump the raw bytes of each section or runtime function"); - syntax.DefineOption("header", ref _header, "Dump R2R header"); - syntax.DefineOption("d|disasm", ref _disasm, "Show disassembly of methods or runtime functions"); + syntax.DefineOption("raw", ref _options.Raw, "Dump the raw bytes of each section or runtime function"); + syntax.DefineOption("header", ref _options.Header, "Dump R2R header"); + syntax.DefineOption("d|disasm", ref _options.Disasm, "Show disassembly of methods or runtime functions"); 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("s|section", ref _sections, "Get section by keyword"); - syntax.DefineOption("unwind", ref _unwind, "Dump unwindInfo"); - syntax.DefineOption("gc", ref _gc, "Dump gcInfo and slot table"); - syntax.DefineOption("sc", ref _sectionContents, "Dump section contents"); + syntax.DefineOption("unwind", ref _options.Unwind, "Dump unwindInfo"); + syntax.DefineOption("gc", ref _options.GC, "Dump gcInfo and slot table"); + syntax.DefineOption("sc", ref _options.SectionContents, "Dump section contents"); + syntax.DefineOption("n|normalize", ref _options.Normalize, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)"); 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"); @@ -108,10 +140,10 @@ namespace R2RDump if (verbose) { - _disasm = true; - _unwind = true; - _gc = true; - _sectionContents = true; + _options.Disasm = true; + _options.Unwind = true; + _options.GC = true; + _options.SectionContents = true; } return argSyntax; @@ -237,14 +269,14 @@ namespace R2RDump _dumper.WriteDivider("R2R Header"); _dumper.DumpHeader(true); - if (!_header) + if (!_options.Header) { _dumper.DumpAllMethods(); } } else //dump queried sections, methods and runtimeFunctions { - if (_header) + if (_options.Header) { _dumper.DumpHeader(false); } @@ -267,7 +299,7 @@ namespace R2RDump { int id; bool isNum = ArgStringToInt(query, out id); - bool idMatch = isNum && (method.Rid == id || method.Token == id); + bool idMatch = isNum && (method.Rid == id || MetadataTokens.GetRowNumber(method.MetadataReader, method.MethodHandle) == id); bool sigMatch = false; if (exact) @@ -401,7 +433,7 @@ namespace R2RDump // parse the ReadyToRun image R2RReader r2r = new R2RReader(filename); - if (_disasm) + if (_options.Disasm) { if (r2r.InputArchitectureSupported() && r2r.DisassemblerArchitectureSupported()) { @@ -415,11 +447,11 @@ namespace R2RDump if (_xml) { - _dumper = new XmlDumper(_ignoreSensitive, r2r, _writer, _raw, _header, _disasm, disassembler, _unwind, _gc, _sectionContents); + _dumper = new XmlDumper(_ignoreSensitive, r2r, _writer, disassembler, _options); } else { - _dumper = new TextDumper(r2r, _writer, _raw, _header, _disasm, disassembler, _unwind, _gc, _sectionContents); + _dumper = new TextDumper(r2r, _writer, disassembler, _options); } if (!_diff) diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs index 1ea77ea..89cfd51 100644 --- a/src/tools/r2rdump/R2RMethod.cs +++ b/src/tools/r2rdump/R2RMethod.cs @@ -211,8 +211,10 @@ namespace R2RDump { private const int _mdtMethodDef = 0x06000000; - MetadataReader _mdReader; - MethodDefinition _methodDef; + /// + /// ECMA metadata reader for the method module. + /// + public MetadataReader MetadataReader { get; } /// /// An unique index for the method @@ -230,8 +232,6 @@ namespace R2RDump /// public string SignatureString { get; set; } - public bool IsGeneric { get; set; } - public MethodSignature Signature { get; } /// @@ -240,9 +240,9 @@ namespace R2RDump public string DeclaringType { get; set; } /// - /// The token of the method consisting of the table code (0x06) and row id + /// The method metadata handle /// - public uint Token { get; set; } + public EntityHandle MethodHandle { get; set; } /// /// The row id of the method @@ -264,105 +264,96 @@ namespace R2RDump public FixupCell[] Fixups { get; set; } - /// - /// Maps all the generic parameters to the type in the instance - /// - private Dictionary _genericParamInstanceMap; - public R2RMethod() { } /// /// Extracts the method signature from the metadata by rid /// - public R2RMethod(int index, MetadataReader mdReader, uint rid, int entryPointId, CorElementType[] instanceArgs, uint[] tok, FixupCell[] fixups) + public R2RMethod( + int index, + MetadataReader mdReader, + EntityHandle methodHandle, + int entryPointId, + string owningType, + string constrainedType, + string[] instanceArgs, + FixupCell[] fixups) { Index = index; - Token = _mdtMethodDef | rid; - Rid = rid; + MethodHandle = methodHandle; EntryPointRuntimeFunctionId = entryPointId; - _mdReader = mdReader; + MetadataReader = mdReader; RuntimeFunctions = new List(); - // get the method signature from the MethodDefhandle - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)rid); - _methodDef = mdReader.GetMethodDefinition(methodDefHandle); - Name = mdReader.GetString(_methodDef.Name); - BlobReader signatureReader = mdReader.GetBlobReader(_methodDef.Signature); - - TypeDefinitionHandle declaringTypeHandle = _methodDef.GetDeclaringType(); - DeclaringType = MetadataNameFormatter.FormatHandle(mdReader, declaringTypeHandle); - - SignatureHeader signatureHeader = signatureReader.ReadSignatureHeader(); - IsGeneric = signatureHeader.IsGeneric; - GenericParameterHandleCollection genericParams = _methodDef.GetGenericParameters(); - _genericParamInstanceMap = new Dictionary(); - - int argCount = signatureReader.ReadCompressedInteger(); - if (IsGeneric) - { - argCount = signatureReader.ReadCompressedInteger(); - } + EntityHandle owningTypeHandle; + GenericParameterHandleCollection genericParams = default(GenericParameterHandleCollection); - Fixups = fixups; + DisassemblingGenericContext genericContext = new DisassemblingGenericContext(typeParameters: Array.Empty(), methodParameters: instanceArgs); + DisassemblingTypeProvider typeProvider = new DisassemblingTypeProvider(); - DisassemblingTypeProvider provider = new DisassemblingTypeProvider(); - if (IsGeneric && instanceArgs != null && tok != null) + // get the method signature from the method handle + switch (MethodHandle.Kind) { - InitGenericInstances(genericParams, instanceArgs, tok); - } + case HandleKind.MethodDefinition: + { + MethodDefinition methodDef = MetadataReader.GetMethodDefinition((MethodDefinitionHandle)MethodHandle); + Name = MetadataReader.GetString(methodDef.Name); + Signature = methodDef.DecodeSignature(typeProvider, genericContext); + owningTypeHandle = methodDef.GetDeclaringType(); + genericParams = methodDef.GetGenericParameters(); + } + break; - DisassemblingGenericContext genericContext = new DisassemblingGenericContext(new string[0], _genericParamInstanceMap.Values.ToArray()); - Signature = _methodDef.DecodeSignature(provider, genericContext); + case HandleKind.MemberReference: + { + MemberReference memberRef = MetadataReader.GetMemberReference((MemberReferenceHandle)MethodHandle); + Name = MetadataReader.GetString(memberRef.Name); + Signature = memberRef.DecodeMethodSignature(typeProvider, genericContext); + owningTypeHandle = memberRef.Parent; + } + break; - SignatureString = GetSignature(); - } + default: + throw new NotImplementedException(); + } - /// - /// Initialize map of generic parameters names to the type in the instance - /// - private void InitGenericInstances(GenericParameterHandleCollection genericParams, CorElementType[] instanceArgs, uint[] tok) - { - if (instanceArgs.Length != genericParams.Count || tok.Length != genericParams.Count) + if (owningType != null) { - throw new BadImageFormatException("Generic param indices out of bounds"); + DeclaringType = owningType; } - - for (int i = 0; i < instanceArgs.Length; i++) + else { - string key = _mdReader.GetString(_mdReader.GetGenericParameter(genericParams.ElementAt(i)).Name); // name of the generic param, eg. "T" - string type = instanceArgs[i].ToString(); // type of the generic param instance - if (instanceArgs[i] == CorElementType.ELEMENT_TYPE_VALUETYPE) - { - var t = _mdReader.GetTypeDefinition(MetadataTokens.TypeDefinitionHandle((int)tok[i])); - type = _mdReader.GetString(t.Name); // name of the struct - - } - _genericParamInstanceMap[key] = type; + DeclaringType = MetadataNameFormatter.FormatHandle(MetadataReader, owningTypeHandle); } - } - /// - /// Returns a string with format DeclaringType.Name(ArgTypes,...) - /// - private string GetSignature() - { - StringBuilder sb = new StringBuilder(); + Fixups = fixups; - sb.AppendFormat($"{DeclaringType}.{Name}"); + StringBuilder sb = new StringBuilder(); + sb.Append(Signature.ReturnType); + sb.Append(" "); + sb.Append(DeclaringType); + sb.Append("."); + sb.Append(Name); - if (IsGeneric) + if (Signature.GenericParameterCount != 0) { sb.Append("<"); - int i = 0; - foreach (var instance in _genericParamInstanceMap.Values) + for (int i = 0; i < Signature.GenericParameterCount; i++) { if (i > 0) { sb.Append(", "); } - sb.AppendFormat($"{instance}"); - i++; + if (instanceArgs != null && instanceArgs.Length > i) + { + sb.Append(instanceArgs[i]); + } + else + { + sb.Append("!"); + sb.Append(i); + } } sb.Append(">"); } @@ -378,17 +369,17 @@ namespace R2RDump } sb.Append(")"); - return sb.ToString(); + SignatureString = sb.ToString(); } public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.AppendLine($"{Signature.ReturnType} {SignatureString}"); + sb.AppendLine(SignatureString); - sb.AppendLine($"Token: 0x{Token:X8}"); - sb.AppendLine($"Rid: {Rid}"); + sb.AppendLine($"Handle: 0x{MetadataTokens.GetToken(MetadataReader, MethodHandle):X8}"); + sb.AppendLine($"Rid: {MetadataTokens.GetRowNumber(MetadataReader, MethodHandle)}"); sb.AppendLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}"); sb.AppendLine($"Number of RuntimeFunctions: {RuntimeFunctions.Count}"); if (Fixups != null) diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs index d67f967..265000a 100644 --- a/src/tools/r2rdump/R2RReader.cs +++ b/src/tools/r2rdump/R2RReader.cs @@ -268,10 +268,11 @@ namespace R2RDump int offset = 0; if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset)) { + EntityHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)rid); int runtimeFunctionId; FixupCell[] fixups; GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixups); - R2RMethod method = new R2RMethod(R2RMethods.Count, MetadataReader, rid, runtimeFunctionId, null, null, fixups); + R2RMethod method = new R2RMethod(R2RMethods.Count, MetadataReader, methodHandle, runtimeFunctionId, owningType: null, constrainedType: null, instanceArgs: null, fixups: fixups); if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length) { @@ -300,33 +301,55 @@ namespace R2RDump NativeParser curParser = allEntriesEnum.GetNext(); while (!curParser.IsNull()) { - uint methodFlags = curParser.GetCompressedData(); - uint rid = curParser.GetCompressedData(); - if ((methodFlags & (byte)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0) + SignatureDecoder decoder = new SignatureDecoder(this, (int)curParser.Offset); + + string owningType = null; + + uint methodFlags = decoder.ReadUInt(); + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0) + { + owningType = decoder.ReadTypeSignature(); + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0) + { + throw new NotImplementedException(); + } + EntityHandle methodHandle; + int rid = (int)decoder.ReadUInt(); + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0) { - uint nArgs = curParser.GetCompressedData(); - CorElementType[] args = new CorElementType[nArgs]; - uint[] tokens = new uint[nArgs]; - for (int i = 0; i < nArgs; i++) + methodHandle = MetadataTokens.MemberReferenceHandle(rid); + } + else + { + methodHandle = MetadataTokens.MethodDefinitionHandle(rid); + } + string[] methodTypeArgs = null; + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0) + { + uint typeArgCount = decoder.ReadUInt(); + methodTypeArgs = new string[typeArgCount]; + for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++) { - args[i] = (CorElementType)curParser.GetByte(); - if (args[i] == CorElementType.ELEMENT_TYPE_VALUETYPE) - { - tokens[i] = curParser.GetCompressedData(); - tokens[i] = (tokens[i] >> 2); - } + methodTypeArgs[typeArgIndex] = decoder.ReadTypeSignature(); } + } - int runtimeFunctionId; - FixupCell[] fixups; - GetRuntimeFunctionIndexFromOffset((int)curParser.Offset, out runtimeFunctionId, out fixups); - R2RMethod method = new R2RMethod(R2RMethods.Count, MetadataReader, rid, runtimeFunctionId, args, tokens, fixups); - if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length) - { - isEntryPoint[method.EntryPointRuntimeFunctionId] = true; - } - R2RMethods.Add(method); + string constrainedType = null; + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0) + { + constrainedType = decoder.ReadTypeSignature(); + } + + int runtimeFunctionId; + FixupCell[] fixups; + GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixups); + R2RMethod method = new R2RMethod(R2RMethods.Count, MetadataReader, methodHandle, runtimeFunctionId, owningType, constrainedType, methodTypeArgs, fixups); + if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length) + { + isEntryPoint[method.EntryPointRuntimeFunctionId] = true; } + R2RMethods.Add(method); curParser = allEntriesEnum.GetNext(); } } diff --git a/src/tools/r2rdump/R2RSignature.cs b/src/tools/r2rdump/R2RSignature.cs index 2522200..7cf81bd 100644 --- a/src/tools/r2rdump/R2RSignature.cs +++ b/src/tools/r2rdump/R2RSignature.cs @@ -35,10 +35,10 @@ namespace R2RDump /// Metadata reader corresponding to the handle /// Metadata handle to parse /// Include namespace in type names - public static string FormatHandle(MetadataReader metadataReader, Handle handle, bool namespaceQualified = true) + public static string FormatHandle(MetadataReader metadataReader, Handle handle, bool namespaceQualified = true, string owningTypeOverride = null) { MetadataNameFormatter formatter = new MetadataNameFormatter(metadataReader); - return formatter.EmitHandleName(handle, namespaceQualified); + return formatter.EmitHandleName(handle, namespaceQualified, owningTypeOverride); } public static string FormatSignature(R2RReader r2rReader, int imageOffset) @@ -52,18 +52,18 @@ namespace R2RDump /// Emit a given token to a specified string builder. /// /// ECMA token to provide string representation for - private string EmitHandleName(Handle handle, bool namespaceQualified) + private string EmitHandleName(Handle handle, bool namespaceQualified, string owningTypeOverride) { switch (handle.Kind) { case HandleKind.MemberReference: - return EmitMemberReferenceName((MemberReferenceHandle)handle); + return EmitMemberReferenceName((MemberReferenceHandle)handle, owningTypeOverride); case HandleKind.MethodSpecification: - return EmitMethodSpecificationName((MethodSpecificationHandle)handle); + return EmitMethodSpecificationName((MethodSpecificationHandle)handle, owningTypeOverride); case HandleKind.MethodDefinition: - return EmitMethodDefinitionName((MethodDefinitionHandle)handle); + return EmitMethodDefinitionName((MethodDefinitionHandle)handle, owningTypeOverride); case HandleKind.TypeReference: return EmitTypeReferenceName((TypeReferenceHandle)handle, namespaceQualified); @@ -83,18 +83,19 @@ namespace R2RDump /// Emit a method specification. /// /// Method specification handle - private string EmitMethodSpecificationName(MethodSpecificationHandle methodSpecHandle) + private string EmitMethodSpecificationName(MethodSpecificationHandle methodSpecHandle, string owningTypeOverride) { MethodSpecification methodSpec = _metadataReader.GetMethodSpecification(methodSpecHandle); DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty(), Array.Empty()); - return EmitHandleName(methodSpec.Method, namespaceQualified: true) + methodSpec.DecodeSignature(this, genericContext); + return EmitHandleName(methodSpec.Method, namespaceQualified: true, owningTypeOverride: owningTypeOverride) + + methodSpec.DecodeSignature(this, genericContext); } /// /// Emit a method reference. /// /// Member reference handle - private string EmitMemberReferenceName(MemberReferenceHandle memberRefHandle) + private string EmitMemberReferenceName(MemberReferenceHandle memberRefHandle, string owningTypeOverride) { MemberReference methodRef = _metadataReader.GetMemberReference(memberRefHandle); StringBuilder builder = new StringBuilder(); @@ -102,7 +103,7 @@ namespace R2RDump MethodSignature methodSig = methodRef.DecodeMethodSignature(this, genericContext); builder.Append(methodSig.ReturnType); builder.Append(" "); - builder.Append(EmitContainingTypeAndMethodName(methodRef)); + builder.Append(EmitContainingTypeAndMethodName(methodRef, owningTypeOverride)); builder.Append(EmitMethodSignature(methodSig)); return builder.ToString(); } @@ -111,7 +112,7 @@ namespace R2RDump /// Emit a method definition. /// /// Method definition handle - private string EmitMethodDefinitionName(MethodDefinitionHandle methodDefinitionHandle) + private string EmitMethodDefinitionName(MethodDefinitionHandle methodDefinitionHandle, string owningTypeOverride) { MethodDefinition methodDef = _metadataReader.GetMethodDefinition(methodDefinitionHandle); DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty(), Array.Empty()); @@ -119,7 +120,11 @@ namespace R2RDump StringBuilder builder = new StringBuilder(); builder.Append(methodSig.ReturnType); builder.Append(" "); - builder.Append(EmitHandleName(methodDef.GetDeclaringType(), namespaceQualified: true)); + if (owningTypeOverride == null) + { + owningTypeOverride = EmitHandleName(methodDef.GetDeclaringType(), namespaceQualified: false, owningTypeOverride: null); + } + builder.Append(owningTypeOverride); builder.Append("."); builder.Append(EmitString(methodDef.Name)); builder.Append(EmitMethodSignature(methodSig)); @@ -175,9 +180,13 @@ namespace R2RDump /// /// Method reference to format /// Output method signature - private string EmitContainingTypeAndMethodName(MemberReference methodRef) + private string EmitContainingTypeAndMethodName(MemberReference methodRef, string owningTypeOverride) { - return EmitHandleName(methodRef.Parent, namespaceQualified: true) + "." + EmitString(methodRef.Name); + if (owningTypeOverride == null) + { + owningTypeOverride = EmitHandleName(methodRef.Parent, namespaceQualified: true, owningTypeOverride: null); + } + return owningTypeOverride + "." + EmitString(methodRef.Name); } /// @@ -193,7 +202,7 @@ namespace R2RDump if (typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference) { // Nested type - format enclosing type followed by the nested type - return EmitHandleName(typeRef.ResolutionScope, namespaceQualified) + "+" + typeName; + return EmitHandleName(typeRef.ResolutionScope, namespaceQualified, owningTypeOverride: null) + "+" + typeName; } if (namespaceQualified) { @@ -219,7 +228,7 @@ namespace R2RDump if (typeDef.IsNested) { // Nested type - return EmitHandleName(typeDef.GetDeclaringType(), namespaceQualified) + "+" + typeName; + return EmitHandleName(typeDef.GetDeclaringType(), namespaceQualified, owningTypeOverride: null) + "+" + typeName; } string output; @@ -294,6 +303,19 @@ namespace R2RDump } /// + /// Construct the signature decoder by storing the image byte array and offset within the array. + /// + /// Metadata reader for the R2R image + /// Signature to parse + /// Optional signature offset within the signature byte array, 0 by default + public SignatureDecoder(MetadataReader metadataReader, byte[] signature, int offset = 0) + { + _image = signature; + _metadataReader = metadataReader; + _offset = offset; + } + + /// /// Read a single byte from the signature stream and advances the current offset. /// public byte ReadByte() @@ -398,6 +420,20 @@ namespace R2RDump return builder.ToString(); } + public string ReadMethodSignature() + { + StringBuilder builder = new StringBuilder(); + ParseMethod(builder); + return builder.ToString(); + } + + public string ReadTypeSignature() + { + StringBuilder builder = new StringBuilder(); + ParseType(builder); + return builder.ToString(); + } + /// /// Parse the signature into a given output string builder. /// @@ -445,12 +481,12 @@ namespace R2RDump break; case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_DefToken: - ParseMethodDefToken(builder); + ParseMethodDefToken(builder, owningTypeOverride: null); builder.Append(" (METHOD_ENTRY_DEF_TOKEN)"); break; case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_RefToken: - ParseMethodRefToken(builder); + ParseMethodRefToken(builder, owningTypeOverride: null); builder.Append(" (METHOD_ENTRY_REF_TOKEN)"); break; @@ -461,12 +497,12 @@ namespace R2RDump break; case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_DefToken: - ParseMethodDefToken(builder); + ParseMethodDefToken(builder, owningTypeOverride: null); builder.Append(" (VIRTUAL_ENTRY_DEF_TOKEN)"); break; case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_RefToken: - ParseMethodRefToken(builder); + ParseMethodRefToken(builder, owningTypeOverride: null); builder.Append(" (VIRTUAL_ENTRY_REF_TOKEN)"); break; @@ -744,7 +780,7 @@ namespace R2RDump break; case CorElementType.ELEMENT_TYPE_CANON_ZAPSIG: - builder.Append("canon_zapsig"); + builder.Append("__Canon"); break; case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG: @@ -784,9 +820,12 @@ namespace R2RDump private void ParseMethod(StringBuilder builder) { uint methodFlags = ReadUInt(); + string owningTypeOverride = null; if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0) { - ParseType(builder); + SignatureDecoder owningTypeDecoder = new SignatureDecoder(_metadataReader, _image, _offset); + owningTypeOverride = owningTypeDecoder.ReadTypeSignature(); + _offset = owningTypeDecoder._offset; } if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0) { @@ -794,11 +833,11 @@ namespace R2RDump } if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0) { - ParseMethodRefToken(builder); + ParseMethodRefToken(builder, owningTypeOverride: owningTypeOverride); } else { - ParseMethodDefToken(builder); + ParseMethodDefToken(builder, owningTypeOverride: owningTypeOverride); } if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0) @@ -827,20 +866,21 @@ namespace R2RDump /// Read a methodDef token from the signature and output the corresponding object to the builder. /// /// Output string builder - private void ParseMethodDefToken(StringBuilder builder) + private void ParseMethodDefToken(StringBuilder builder, string owningTypeOverride) { uint methodDefToken = ReadUInt() | (uint)CorTokenType.mdtMethodDef; - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodDefToken))); + builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodDefToken), namespaceQualified: true, owningTypeOverride: owningTypeOverride)); } /// /// Read a memberRef token from the signature and output the corresponding object to the builder. /// /// Output string builder - private void ParseMethodRefToken(StringBuilder builder) + /// Explicit owning type override + private void ParseMethodRefToken(StringBuilder builder, string owningTypeOverride) { uint methodRefToken = ReadUInt() | (uint)CorTokenType.mdtMemberRef; - builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodRefToken))); + builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodRefToken), namespaceQualified: false, owningTypeOverride: owningTypeOverride)); } /// diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs index 2b95b21..3fa444c 100644 --- a/src/tools/r2rdump/TextDumper.cs +++ b/src/tools/r2rdump/TextDumper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection.PortableExecutable; using System.Text; using System.Xml; @@ -9,18 +10,9 @@ namespace R2RDump { class TextDumper : Dumper { - public TextDumper(R2RReader r2r, TextWriter writer, bool raw, bool header, bool disasm, Disassembler disassembler, bool unwind, bool gc, bool sectionContents) + public TextDumper(R2RReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) + : base(r2r, writer, disassembler, options) { - _r2r = r2r; - _writer = writer; - - _raw = raw; - _header = header; - _disasm = disasm; - _disassembler = disassembler; - _unwind = unwind; - _gc = gc; - _sectionContents = sectionContents; } internal override void Begin() @@ -63,7 +55,7 @@ namespace R2RDump { _writer.WriteLine(_r2r.R2RHeader.ToString()); - if (_raw) + if (_options.Raw) { DumpBytes(_r2r.R2RHeader.RelativeVirtualAddress, (uint)_r2r.R2RHeader.Size); } @@ -73,8 +65,8 @@ namespace R2RDump WriteDivider("R2R Sections"); _writer.WriteLine($"{_r2r.R2RHeader.Sections.Count} sections"); SkipLine(); - - foreach (R2RSection section in _r2r.R2RHeader.Sections.Values) + + foreach (R2RSection section in NormalizedSections()) { DumpSection(section); } @@ -90,12 +82,12 @@ namespace R2RDump WriteSubDivider(); _writer.WriteLine(section.ToString()); - if (_raw) + if (_options.Raw) { DumpBytes(section.RelativeVirtualAddress, (uint)section.Size); SkipLine(); } - if (_sectionContents) + if (_options.SectionContents) { DumpSectionContents(section); SkipLine(); @@ -107,7 +99,7 @@ namespace R2RDump WriteDivider("R2R Methods"); _writer.WriteLine($"{_r2r.R2RMethods.Count} methods"); SkipLine(); - foreach (R2RMethod method in _r2r.R2RMethods) + foreach (R2RMethod method in NormalizedMethods()) { DumpMethod(method); } @@ -121,12 +113,12 @@ namespace R2RDump WriteSubDivider(); _writer.WriteLine(method.ToString()); - if (_gc && method.GcInfo != null) + if (_options.GC && method.GcInfo != null) { _writer.WriteLine("GcInfo:"); _writer.Write(method.GcInfo); - if (_raw) + if (_options.Raw) { DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, null, "", false); } @@ -147,21 +139,21 @@ namespace R2RDump _writer.WriteLine(rtf.Method.SignatureString); _writer.Write($"{rtf}"); - if (_disasm) + if (_options.Disasm) { DumpDisasm(rtf, _r2r.GetOffset(rtf.StartAddress)); } - if (_raw) + if (_options.Raw) { _writer.WriteLine("Raw Bytes:"); DumpBytes(rtf.StartAddress, (uint)rtf.Size); } - if (_unwind) + if (_options.Unwind) { _writer.WriteLine("UnwindInfo:"); _writer.Write(rtf.UnwindInfo); - if (_raw) + if (_options.Raw) { DumpBytes(rtf.UnwindRVA, (uint)rtf.UnwindInfo.Size); } @@ -303,7 +295,7 @@ namespace R2RDump foreach (R2RImportSection importSection in _r2r.ImportSections) { _writer.Write(importSection.ToString()); - if (_raw && importSection.Entries.Count != 0) + if (_options.Raw && importSection.Entries.Count != 0) { if (importSection.SectionRVA != 0) { diff --git a/src/tools/r2rdump/XmlDumper.cs b/src/tools/r2rdump/XmlDumper.cs index f260000..97a90e7 100644 --- a/src/tools/r2rdump/XmlDumper.cs +++ b/src/tools/r2rdump/XmlDumper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection.PortableExecutable; using System.Text; using System.Xml; @@ -15,21 +16,12 @@ namespace R2RDump private bool _ignoreSensitive; private XmlAttributeOverrides _ignoredProperties; - public XmlDumper(bool ignoreSensitive, R2RReader r2r, TextWriter writer, bool raw, bool header, bool disasm, Disassembler disassembler, bool unwind, bool gc, bool sectionContents) + public XmlDumper(bool ignoreSensitive, R2RReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) + : base(r2r, writer, disassembler, options) { _ignoreSensitive = ignoreSensitive; - _r2r = r2r; - _writer = writer; XmlDocument = new XmlDocument(); - _raw = raw; - _header = header; - _disasm = disasm; - _disassembler = disassembler; - _unwind = unwind; - _gc = gc; - _sectionContents = sectionContents; - _ignoredProperties = new XmlAttributeOverrides(); XmlAttributes attrs = new XmlAttributes(); attrs.XmlIgnore = _ignoreSensitive; @@ -48,14 +40,6 @@ namespace R2RDump _ignoredProperties.Add(typeof(R2RSection), "Size", attrs); } - public XmlDocument GetXmlDocument() - { - Begin(); - DumpHeader(true); - DumpAllMethods(); - return XmlDocument; - } - internal override void Begin() { _rootNode = XmlDocument.CreateNode("element", "R2RDump", ""); @@ -91,7 +75,7 @@ namespace R2RDump _rootNode.AppendChild(headerNode); Serialize(_r2r.R2RHeader, headerNode); - if (_raw) + if (_options.Raw) { DumpBytes(_r2r.R2RHeader.RelativeVirtualAddress, (uint)_r2r.R2RHeader.Size, headerNode); } @@ -102,7 +86,7 @@ namespace R2RDump _rootNode.AppendChild(sectionsNode); AddXMLNode("Count", _r2r.R2RHeader.Sections.Count.ToString(), sectionsNode); - foreach (R2RSection section in _r2r.R2RHeader.Sections.Values) + foreach (R2RSection section in NormalizedSections()) { DumpSection(section, sectionsNode); } @@ -120,11 +104,11 @@ namespace R2RDump parentNode.AppendChild(sectionNode); Serialize(section, sectionNode); - if (_raw) + if (_options.Raw) { DumpBytes(section.RelativeVirtualAddress, (uint)section.Size, sectionNode); } - if (_sectionContents) + if (_options.SectionContents) { DumpSectionContents(section, sectionNode); } @@ -135,7 +119,7 @@ namespace R2RDump XmlNode methodsNode = XmlDocument.CreateNode("element", "Methods", ""); _rootNode.AppendChild(methodsNode); AddXMLAttribute(methodsNode, "Count", _r2r.R2RMethods.Count.ToString()); - foreach (R2RMethod method in _r2r.R2RMethods) + foreach (R2RMethod method in NormalizedMethods()) { DumpMethod(method, methodsNode); } @@ -151,7 +135,7 @@ namespace R2RDump parentNode.AppendChild(methodNode); Serialize(method, methodNode); - if (_gc && method.GcInfo != null) + if (_options.GC && method.GcInfo != null) { XmlNode gcNode = XmlDocument.CreateNode("element", "GcInfo", ""); methodNode.AppendChild(gcNode); @@ -165,7 +149,7 @@ namespace R2RDump } } - if (_raw) + if (_options.Raw) { DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, gcNode, "Raw", false); } @@ -192,23 +176,23 @@ namespace R2RDump AddXMLNode("MethodRid", rtf.Method.Rid.ToString(), rtfNode); Serialize(rtf, rtfNode); - if (_disasm) + if (_options.Disasm) { DumpDisasm(rtf, _r2r.GetOffset(rtf.StartAddress), rtfNode); } - if (_raw) + if (_options.Raw) { DumpBytes(rtf.StartAddress, (uint)rtf.Size, rtfNode); } - if (_unwind && rtf.UnwindInfo != null) + if (_options.Unwind && rtf.UnwindInfo != null) { XmlNode unwindNode = null; unwindNode = XmlDocument.CreateNode("element", "UnwindInfo", ""); rtfNode.AppendChild(unwindNode); Serialize(rtf.UnwindInfo, unwindNode); - if (_raw) + if (_options.Raw) { DumpBytes(rtf.UnwindRVA, (uint)((Amd64.UnwindInfo)rtf.UnwindInfo).Size, unwindNode); } @@ -308,7 +292,7 @@ namespace R2RDump contentsNode.AppendChild(importSectionsNode); Serialize(importSection, importSectionsNode); - if (_raw && importSection.Entries.Count != 0) + if (_options.Raw && importSection.Entries.Count != 0) { if (importSection.SectionRVA != 0) { -- 2.7.4