R2RDump - Dump AvailableTypes section contents (#18227)
authorAmy <amycmyu@gmail.com>
Mon, 4 Jun 2018 18:40:58 +0000 (11:40 -0700)
committerZach Montoya <zamont@microsoft.com>
Mon, 4 Jun 2018 18:40:58 +0000 (11:40 -0700)
* Move R2RReader constructor to separate functions, parse READYTORUN_SECTION_AVAILABLE_TYPES

* Fix bug from merging, output formatting changes

* Rename availableTypes variables, save availableTypes as string instead of tuple

src/tools/r2rdump/R2RDump.cs
src/tools/r2rdump/R2RReader.cs

index f7fc729..ea078e0 100644 (file)
@@ -23,6 +23,7 @@ namespace R2RDump
         private IReadOnlyList<string> _sections = Array.Empty<string>();
         private bool _diff = false;
         private long _disassembler;
+        private bool _types = false;
         private TextWriter _writer;
 
         private R2RDump()
@@ -47,7 +48,8 @@ namespace R2RDump
                 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("diff", ref _diff, "Compare two R2R images"); // not yet implemented
+                syntax.DefineOption("types", ref _types, "Dump available types");
+                syntax.DefineOption("diff", ref _diff, "Compare two R2R images (not yet implemented)"); // not yet implemented
             });
 
             return argSyntax;
@@ -85,7 +87,8 @@ namespace R2RDump
 
         private void WriteDivider(string title)
         {
-            _writer.WriteLine("============== " + title + " ==============");
+            int len = 61 - title.Length - 2;
+            _writer.WriteLine(new String('=', len/2) + " " + title + " " + new String('=', (int)Math.Ceiling(len/2.0)));
             _writer.WriteLine();
         }
 
@@ -108,6 +111,8 @@ namespace R2RDump
             if (dumpSections)
             {
                 WriteDivider("R2R Sections");
+                _writer.WriteLine($"{r2r.R2RHeader.Sections.Count} sections");
+                _writer.WriteLine();
                 foreach (R2RSection section in r2r.R2RHeader.Sections.Values)
                 {
                     DumpSection(r2r, section);
@@ -196,6 +201,16 @@ namespace R2RDump
             _writer.WriteLine();
         }
 
+        private void DumpAvailableTypes(R2RReader r2r)
+        {
+            WriteDivider("Available Types");
+            foreach (string name in r2r.AvailableTypes)
+            {
+                _writer.WriteLine(name);
+            }
+            _writer.WriteLine();
+        }
+
         // <summary>
         /// For each query in the list of queries, search for all methods matching the query by name, signature or id
         /// </summary>
@@ -291,6 +306,7 @@ namespace R2RDump
                 if (!_header)
                 {
                     WriteDivider("R2R Methods");
+                    _writer.WriteLine($"{r2r.R2RMethods.Count} methods");
                     _writer.WriteLine();
                     foreach (R2RMethod method in r2r.R2RMethods)
                     {
@@ -311,6 +327,11 @@ namespace R2RDump
                 QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false);
             }
 
+            if (_types)
+            {
+                DumpAvailableTypes(r2r);
+            }
+
             _writer.WriteLine("=============================================================");
             _writer.WriteLine();
         }
index 18969d1..5bad29f 100644 (file)
@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
 using System.Reflection.PortableExecutable;
 using System.Text;
 
@@ -13,7 +14,8 @@ namespace R2RDump
 {
     class R2RReader
     {
-        private readonly PEReader peReader;
+        private readonly PEReader _peReader;
+        private readonly MetadataReader _mdReader;
 
         /// <summary>
         /// Byte array containing the ReadyToRun image
@@ -48,11 +50,15 @@ namespace R2RDump
 
         /// <summary>
         /// The runtime functions and method signatures of each method
-        /// TODO: generic methods
         /// </summary>
         public IList<R2RMethod> R2RMethods { get; }
 
         /// <summary>
+        /// The available types from READYTORUN_SECTION_AVAILABLE_TYPES
+        /// </summary>
+        public List<string> AvailableTypes { get; }
+
+        /// <summary>
         /// Initializes the fields of the R2RHeader and R2RMethods
         /// </summary>
         /// <param name="filename">PE image</param>
@@ -65,19 +71,19 @@ namespace R2RDump
             fixed (byte* p = Image)
             {
                 IntPtr ptr = (IntPtr)p;
-                peReader = new PEReader(p, Image.Length);
+                _peReader = new PEReader(p, Image.Length);
 
-                IsR2R = (peReader.PEHeaders.CorHeader.Flags == CorFlags.ILLibrary);
+                IsR2R = (_peReader.PEHeaders.CorHeader.Flags == CorFlags.ILLibrary);
                 if (!IsR2R)
                 {
                     throw new BadImageFormatException("The file is not a ReadyToRun image");
                 }
 
-                Machine = peReader.PEHeaders.CoffHeader.Machine;
-                ImageBase = peReader.PEHeaders.PEHeader.ImageBase;
+                Machine = _peReader.PEHeaders.CoffHeader.Machine;
+                ImageBase = _peReader.PEHeaders.PEHeader.ImageBase;
 
                 // initialize R2RHeader
-                DirectoryEntry r2rHeaderDirectory = peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
+                DirectoryEntry r2rHeaderDirectory = _peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
                 int r2rHeaderOffset = GetOffset(r2rHeaderDirectory.RelativeVirtualAddress);
                 R2RHeader = new R2RHeader(Image, r2rHeaderDirectory.RelativeVirtualAddress, r2rHeaderOffset);
                 if (r2rHeaderDirectory.Size != R2RHeader.Size)
@@ -85,110 +91,161 @@ namespace R2RDump
                     throw new BadImageFormatException("The calculated size of the R2RHeader doesn't match the size saved in the ManagedNativeHeaderDirectory");
                 }
 
-                // initialize R2RMethods
-                if (peReader.HasMetadata)
+                if (_peReader.HasMetadata)
                 {
-                    MetadataReader mdReader = peReader.GetMetadataReader();
+                    _mdReader = _peReader.GetMetadataReader();
 
-                    int runtimeFunctionSize = 2;
-                    if (Machine == Machine.Amd64)
-                    {
-                        runtimeFunctionSize = 3;
-                    }
-                    runtimeFunctionSize *= sizeof(int);
+                    int runtimeFunctionSize = CalculateRuntimeFunctionSize();
                     R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS];
                     uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize);
                     int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress);
                     bool[] isEntryPoint = new bool[nRuntimeFunctions];
-                    for (int i = 0; i < nRuntimeFunctions; i++)
+
+                    // initialize R2RMethods
+                    R2RMethods = new List<R2RMethod>();
+                    ParseMethodDefEntrypoints(isEntryPoint);
+                    ParseInstanceMethodEntrypoints(isEntryPoint);
+                    ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize);
+
+                    AvailableTypes = new List<string>();
+                    ParseAvailableTypes();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Each runtime function entry has 3 fields for Amd64 machines (StartAddress, EndAddress, UnwindRVA), otherwise 2 fields (StartAddress, UnwindRVA)
+        /// </summary>
+        private int CalculateRuntimeFunctionSize()
+        {
+            if (Machine == Machine.Amd64)
+            {
+                return 3 * sizeof(int);
+            }
+            return 2 * sizeof(int);
+        }
+
+        /// <summary>
+        /// Initialize non-generic R2RMethods with method signatures from MethodDefHandle, and runtime function indices from MethodDefEntryPoints
+        /// </summary>
+        private void ParseMethodDefEntrypoints(bool[] isEntryPoint)
+        {
+            int methodDefEntryPointsRVA = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS].RelativeVirtualAddress;
+            int methodDefEntryPointsOffset = GetOffset(methodDefEntryPointsRVA);
+            NativeArray methodEntryPoints = new NativeArray(Image, (uint)methodDefEntryPointsOffset);
+            uint nMethodEntryPoints = methodEntryPoints.GetCount();
+
+            for (uint rid = 1; rid <= nMethodEntryPoints; rid++)
+            {
+                int offset = 0;
+                if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
+                {
+                    R2RMethod method = new R2RMethod(Image, _mdReader, rid, GetEntryPointIdFromOffset(offset), null, null);
+
+                    if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length)
                     {
-                        isEntryPoint[i] = false;
+                        throw new BadImageFormatException("EntryPointRuntimeFunctionId out of bounds");
                     }
+                    isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
+                    R2RMethods.Add(method);
+                }
+            }
+        }
 
-                    // initialize R2RMethods with method signatures from MethodDefHandle, and runtime function indices from MethodDefEntryPoints
-                    int methodDefEntryPointsRVA = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS].RelativeVirtualAddress;
-                    int methodDefEntryPointsOffset = GetOffset(methodDefEntryPointsRVA);
-                    NativeArray methodEntryPoints = new NativeArray(Image, (uint)methodDefEntryPointsOffset);
-                    uint nMethodEntryPoints = methodEntryPoints.GetCount();
-                    R2RMethods = new List<R2RMethod>();
-                    for (uint rid = 1; rid <= nMethodEntryPoints; rid++)
+        /// <summary>
+        /// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
+        /// </summary>
+        private void ParseInstanceMethodEntrypoints(bool[] isEntryPoint)
+        {
+            R2RSection instMethodEntryPointSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS];
+            int instMethodEntryPointsOffset = GetOffset(instMethodEntryPointSection.RelativeVirtualAddress);
+            NativeParser parser = new NativeParser(Image, (uint)instMethodEntryPointsOffset);
+            NativeHashtable instMethodEntryPoints = new NativeHashtable(Image, parser);
+            NativeHashtable.AllEntriesEnumerator allEntriesEnum = instMethodEntryPoints.EnumerateAllEntries();
+            NativeParser curParser = allEntriesEnum.GetNext();
+            while (!curParser.IsNull())
+            {
+                uint methodFlags = curParser.GetCompressedData();
+                uint rid = curParser.GetCompressedData();
+                if ((methodFlags & (byte)R2RMethod.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation) != 0)
+                {
+                    uint nArgs = curParser.GetCompressedData();
+                    R2RMethod.GenericElementTypes[] args = new R2RMethod.GenericElementTypes[nArgs];
+                    uint[] tokens = new uint[nArgs];
+                    for (int i = 0; i < nArgs; i++)
                     {
-                        int offset = 0;
-                        if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
+                        args[i] = (R2RMethod.GenericElementTypes)curParser.GetByte();
+                        if (args[i] == R2RMethod.GenericElementTypes.ValueType)
                         {
-                            R2RMethod method = new R2RMethod(Image, mdReader, rid, GetEntryPointIdFromOffset(offset), null, null);
-
-                            if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= nRuntimeFunctions)
-                            {
-                                throw new BadImageFormatException("EntryPointRuntimeFunctionId out of bounds");
-                            }
-                            isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
-                            R2RMethods.Add(method);
+                            tokens[i] = curParser.GetCompressedData();
+                            tokens[i] = (tokens[i] >> 2);
                         }
                     }
 
-                    // instance method table
-                    R2RSection instMethodEntryPointSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS];
-                    int instMethodEntryPointsOffset = GetOffset(instMethodEntryPointSection.RelativeVirtualAddress);
-                    NativeParser parser = new NativeParser(Image, (uint)instMethodEntryPointsOffset);
-                    NativeHashtable instMethodEntryPoints = new NativeHashtable(Image, parser);
-                    NativeHashtable.AllEntriesEnumerator allEntriesEnum = instMethodEntryPoints.EnumerateAllEntries();
-                    NativeParser curParser = allEntriesEnum.GetNext();
-                    while (!curParser.IsNull())
+                    uint id = curParser.GetUnsigned();
+                    id = id >> 1;
+                    R2RMethod method = new R2RMethod(Image, _mdReader, rid, (int)id, args, tokens);
+                    if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length)
                     {
-                        uint methodFlags = curParser.GetCompressedData();
-                        uint rid = curParser.GetCompressedData();
-                        if ((methodFlags & (byte)R2RMethod.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation) != 0)
-                        {
-                            uint nArgs = curParser.GetCompressedData();
-                            R2RMethod.GenericElementTypes[] args = new R2RMethod.GenericElementTypes[nArgs];
-                            uint[] tokens = new uint[nArgs];
-                            for (int i = 0; i < nArgs; i++)
-                            {
-                                args[i] = (R2RMethod.GenericElementTypes)curParser.GetByte();
-                                if (args[i] == R2RMethod.GenericElementTypes.ValueType)
-                                {
-                                    tokens[i] = curParser.GetCompressedData();
-                                    tokens[i] = (tokens[i] >> 2);
-                                }
-                            }
-
-                            uint id = curParser.GetUnsigned();
-                            id = id >> 1;
-                            R2RMethod method = new R2RMethod(Image, mdReader, rid, (int)id, args, tokens);
-                            if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < nRuntimeFunctions)
-                            {
-                                isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
-                            }
-                            R2RMethods.Add(method);
-                        }
-                        curParser = allEntriesEnum.GetNext();
+                        isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
                     }
+                    R2RMethods.Add(method);
+                }
+                curParser = allEntriesEnum.GetNext();
+            }
+        }
 
-                    // get the RVAs of the runtime functions for each method
-                    int curOffset = 0;
-                    foreach (R2RMethod method in R2RMethods)
+        /// <summary>
+        /// Get the RVAs of the runtime functions for each method
+        /// </summary>
+        private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset, int runtimeFunctionSize)
+        {
+            int curOffset = 0;
+            foreach (R2RMethod method in R2RMethods)
+            {
+                int runtimeFunctionId = method.EntryPointRuntimeFunctionId;
+                if (runtimeFunctionId == -1)
+                    continue;
+                curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize;
+                do
+                {
+                    int startRva = NativeReader.ReadInt32(Image, ref curOffset);
+                    int endRva = -1;
+                    if (Machine == Machine.Amd64)
                     {
-                        int runtimeFunctionId = method.EntryPointRuntimeFunctionId;
-                        if (runtimeFunctionId == -1)
-                            continue;
-                        curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize;
-                        do
-                        {
-                            int startRva = NativeReader.ReadInt32(Image, ref curOffset);
-                            int endRva = -1;
-                            if (Machine == Machine.Amd64)
-                            {
-                                endRva = NativeReader.ReadInt32(Image, ref curOffset);
-                            }
-                            int unwindRva = NativeReader.ReadInt32(Image, ref curOffset);
-
-                            method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method));
-                            runtimeFunctionId++;
-                        }
-                        while (runtimeFunctionId < nRuntimeFunctions && !isEntryPoint[runtimeFunctionId]);
+                        endRva = NativeReader.ReadInt32(Image, ref curOffset);
                     }
+                    int unwindRva = NativeReader.ReadInt32(Image, ref curOffset);
+
+                    method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method));
+                    runtimeFunctionId++;
+                }
+                while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]);
+            }
+        }
+
+        private void ParseAvailableTypes()
+        {
+            R2RSection availableTypesSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES];
+            int availableTypesOffset = GetOffset(availableTypesSection.RelativeVirtualAddress);
+            NativeParser parser = new NativeParser(Image, (uint)availableTypesOffset);
+            NativeHashtable availableTypes = new NativeHashtable(Image, parser);
+            NativeHashtable.AllEntriesEnumerator allEntriesEnum = availableTypes.EnumerateAllEntries();
+            NativeParser curParser = allEntriesEnum.GetNext();
+            while (!curParser.IsNull())
+            {
+                uint rid = curParser.GetUnsigned();
+                rid = rid >> 1;
+                TypeDefinitionHandle typeDefHandle = MetadataTokens.TypeDefinitionHandle((int)rid);
+
+                TypeDefinition typeDef = _mdReader.GetTypeDefinition(typeDefHandle);
+                string name = _mdReader.GetString(typeDef.Name);
+                if (!typeDef.Namespace.IsNil)
+                {
+                    name = _mdReader.GetString(typeDef.Namespace) + "." + name;
                 }
+                AvailableTypes.Add(name);
+                curParser = allEntriesEnum.GetNext();
             }
         }
 
@@ -198,8 +255,8 @@ namespace R2RDump
         /// <param name="rva">The relative virtual address</param>
         public int GetOffset(int rva)
         {
-            int index = peReader.PEHeaders.GetContainingSectionIndex(rva);
-            SectionHeader containingSection = peReader.PEHeaders.SectionHeaders[index];
+            int index = _peReader.PEHeaders.GetContainingSectionIndex(rva);
+            SectionHeader containingSection = _peReader.PEHeaders.SectionHeaders[index];
             return rva - containingSection.VirtualAddress + containingSection.PointerToRawData;
         }