diff options
-rw-r--r-- | src/tools/r2rdump/R2RDump.cs | 96 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RMethod.cs | 147 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RReader.cs | 69 | ||||
-rw-r--r-- | src/tools/r2rdump/R2RSignature.cs | 96 | ||||
-rw-r--r-- | src/tools/r2rdump/TextDumper.cs | 40 | ||||
-rw-r--r-- | 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 34b86b75ef..0901ee672e 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<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) diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs index 1ea77ea04b..89cfd51b17 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; + /// <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) diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs index d67f9679d3..265000aa1b 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 2522200fd8..7cf81bdf3a 100644 --- a/src/tools/r2rdump/R2RSignature.cs +++ b/src/tools/r2rdump/R2RSignature.cs @@ -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> diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs index 2b95b21e67..3fa444c763 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 f260000e1f..97a90e78aa 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) { |