R2RDump fixes for dumping method instance entrypoint table (#20243)
authorTomáš Rylek <trylek@microsoft.com>
Thu, 4 Oct 2018 19:29:24 +0000 (21:29 +0200)
committerGitHub <noreply@github.com>
Thu, 4 Oct 2018 19:29:24 +0000 (21:29 +0200)
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
src/tools/r2rdump/R2RMethod.cs
src/tools/r2rdump/R2RReader.cs
src/tools/r2rdump/R2RSignature.cs
src/tools/r2rdump/TextDumper.cs
src/tools/r2rdump/XmlDumper.cs

index 34b86b7..0901ee6 100644 (file)
@@ -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<R2RSection> NormalizedSections()
+        {
+            IEnumerable<R2RSection> sections = _r2r.R2RHeader.Sections.Values;
+            if (_options.Normalize)
+            {
+                sections = sections.OrderBy((s) => s.Type);
+            }
+            return sections;
+        }
+
+        public IEnumerable<R2RMethod> NormalizedMethods()
+        {
+            IEnumerable<R2RMethod> methods = _r2r.R2RMethods;
+            if (_options.Normalize)
+            {
+                methods = methods.OrderBy((m) => m.SignatureString);
+            }
+            return methods;
+        }
 
         /// <summary>
         /// Run right before printing output
@@ -54,18 +90,13 @@ namespace R2RDump
         private bool _help;
         private IReadOnlyList<string> _inputFilenames = Array.Empty<string>();
         private string _outputFilename = null;
-        private bool _xml;
-        private bool _raw;
-        private bool _header;
-        private bool _disasm;
+        private DumpOptions _options = new DumpOptions();
         private IReadOnlyList<string> _queries = Array.Empty<string>();
         private IReadOnlyList<string> _keywords = Array.Empty<string>();
         private IReadOnlyList<int> _runtimeFunctions = Array.Empty<int>();
         private IReadOnlyList<string> _sections = Array.Empty<string>();
         private bool _diff;
-        private bool _unwind;
-        private bool _gc;
-        private bool _sectionContents;
+        private bool _xml;
         private TextWriter _writer;
         private Dictionary<R2RSection.SectionType, bool> _selectedSections = new Dictionary<R2RSection.SectionType, bool>();
         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)
index 1ea77ea..89cfd51 100644 (file)
@@ -211,8 +211,10 @@ namespace R2RDump
     {
         private const int _mdtMethodDef = 0x06000000;
 
-        MetadataReader _mdReader;
-        MethodDefinition _methodDef;
+        /// <summary>
+        /// ECMA metadata reader for the method module.
+        /// </summary>
+        public MetadataReader MetadataReader { get; }
 
         /// <summary>
         /// An unique index for the method
@@ -230,8 +232,6 @@ namespace R2RDump
         /// </summary>
         public string SignatureString { get; set; }
 
-        public bool IsGeneric { get; set; }
-
         public MethodSignature<string> Signature { get; }
 
         /// <summary>
@@ -240,9 +240,9 @@ namespace R2RDump
         public string DeclaringType { get; set; }
 
         /// <summary>
-        /// The token of the method consisting of the table code (0x06) and row id
+        /// The method metadata handle
         /// </summary>
-        public uint Token { get; set; }
+        public EntityHandle MethodHandle { get; set; }
 
         /// <summary>
         /// The row id of the method
@@ -264,105 +264,96 @@ namespace R2RDump
 
         public FixupCell[] Fixups { get; set; }
 
-        /// <summary>
-        /// Maps all the generic parameters to the type in the instance
-        /// </summary>
-        private Dictionary<string, string> _genericParamInstanceMap;
-
         public R2RMethod() { }
 
         /// <summary>
         /// Extracts the method signature from the metadata by rid
         /// </summary>
-        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<RuntimeFunction>();
 
-            // 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<string, string>();
-            
-            int argCount = signatureReader.ReadCompressedInteger();
-            if (IsGeneric)
-            {
-                argCount = signatureReader.ReadCompressedInteger();
-            }
+            EntityHandle owningTypeHandle;
+            GenericParameterHandleCollection genericParams = default(GenericParameterHandleCollection);
 
-            Fixups = fixups;
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(typeParameters: Array.Empty<string>(), 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<string, DisassemblingGenericContext>(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<string, DisassemblingGenericContext>(typeProvider, genericContext);
+                        owningTypeHandle = memberRef.Parent;
+                    }
+                    break;
 
-            SignatureString = GetSignature();
-        }
+                default:
+                    throw new NotImplementedException();
+            }
 
-        /// <summary>
-        /// Initialize map of generic parameters names to the type in the instance
-        /// </summary>
-        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);
             }
-        }
 
-        /// <summary>
-        /// Returns a string with format DeclaringType.Name<GenericTypes,...>(ArgTypes,...)
-        /// </summary>
-        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)
index d67f967..265000a 100644 (file)
@@ -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();
             }
         }
index 2522200..7cf81bd 100644 (file)
@@ -35,10 +35,10 @@ namespace R2RDump
         /// <param name="metadataReader">Metadata reader corresponding to the handle</param>
         /// <param name="handle">Metadata handle to parse</param>
         /// <param name="namespaceQualified">Include namespace in type names</param>
-        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.
         /// </summary>
         /// <param name="methodToken">ECMA token to provide string representation for</param>
-        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.
         /// </summary>
         /// <param name="methodSpecHandle">Method specification handle</param>
-        private string EmitMethodSpecificationName(MethodSpecificationHandle methodSpecHandle)
+        private string EmitMethodSpecificationName(MethodSpecificationHandle methodSpecHandle, string owningTypeOverride)
         {
             MethodSpecification methodSpec = _metadataReader.GetMethodSpecification(methodSpecHandle);
             DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
-            return EmitHandleName(methodSpec.Method, namespaceQualified: true) + methodSpec.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
+            return EmitHandleName(methodSpec.Method, namespaceQualified: true, owningTypeOverride: owningTypeOverride)
+                + methodSpec.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
         }
 
         /// <summary>
         /// Emit a method reference.
         /// </summary>
         /// <param name="memberRefHandle">Member reference handle</param>
-        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<String> methodSig = methodRef.DecodeMethodSignature<string, DisassemblingGenericContext>(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.
         /// </summary>
         /// <param name="methodSpecHandle">Method definition handle</param>
-        private string EmitMethodDefinitionName(MethodDefinitionHandle methodDefinitionHandle)
+        private string EmitMethodDefinitionName(MethodDefinitionHandle methodDefinitionHandle, string owningTypeOverride)
         {
             MethodDefinition methodDef = _metadataReader.GetMethodDefinition(methodDefinitionHandle);
             DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
@@ -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
         /// </summary>
         /// <param name="methodRef">Method reference to format</param>
         /// <param name="methodSignature">Output method signature</param>
-        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);
         }
 
         /// <summary>
@@ -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
         }
 
         /// <summary>
+        /// Construct the signature decoder by storing the image byte array and offset within the array. 
+        /// </summary>
+        /// <param name="metadataReader">Metadata reader for the R2R image</param>
+        /// <param name="signature">Signature to parse</param>
+        /// <param name="offset">Optional signature offset within the signature byte array, 0 by default</param>
+        public SignatureDecoder(MetadataReader metadataReader, byte[] signature, int offset = 0)
+        {
+            _image = signature;
+            _metadataReader = metadataReader;
+            _offset = offset;
+        }
+
+        /// <summary>
         /// Read a single byte from the signature stream and advances the current offset.
         /// </summary>
         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();
+        }
+
         /// <summary>
         /// Parse the signature into a given output string builder.
         /// </summary>
@@ -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.
         /// </summary>
         /// <param name="builder">Output string builder</param>
-        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));
         }
 
         /// <summary>
         /// Read a memberRef token from the signature and output the corresponding object to the builder.
         /// </summary>
         /// <param name="builder">Output string builder</param>
-        private void ParseMethodRefToken(StringBuilder builder)
+        /// <param name="owningTypeOverride">Explicit owning type override</param>
+        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));
         }
 
         /// <summary>
index 2b95b21..3fa444c 100644 (file)
@@ -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)
                             {
index f260000..97a90e7 100644 (file)
@@ -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)
                             {