From: Amy Date: Mon, 4 Jun 2018 18:40:58 +0000 (-0700) Subject: R2RDump - Dump AvailableTypes section contents (#18227) X-Git-Tag: accepted/tizen/unified/20190422.045933~1992 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=613358db8c36ec5d564f2772de2d083c0f701754;p=platform%2Fupstream%2Fcoreclr.git R2RDump - Dump AvailableTypes section contents (#18227) * 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 --- diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs index f7fc729..ea078e0 100644 --- a/src/tools/r2rdump/R2RDump.cs +++ b/src/tools/r2rdump/R2RDump.cs @@ -23,6 +23,7 @@ namespace R2RDump private IReadOnlyList _sections = Array.Empty(); 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(); + } + // /// For each query in the list of queries, search for all methods matching the query by name, signature or id /// @@ -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(); } diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs index 18969d1..5bad29f 100644 --- a/src/tools/r2rdump/R2RReader.cs +++ b/src/tools/r2rdump/R2RReader.cs @@ -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; /// /// Byte array containing the ReadyToRun image @@ -48,11 +50,15 @@ namespace R2RDump /// /// The runtime functions and method signatures of each method - /// TODO: generic methods /// public IList R2RMethods { get; } /// + /// The available types from READYTORUN_SECTION_AVAILABLE_TYPES + /// + public List AvailableTypes { get; } + + /// /// Initializes the fields of the R2RHeader and R2RMethods /// /// PE image @@ -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(); + ParseMethodDefEntrypoints(isEntryPoint); + ParseInstanceMethodEntrypoints(isEntryPoint); + ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize); + + AvailableTypes = new List(); + ParseAvailableTypes(); + } + } + } + + /// + /// Each runtime function entry has 3 fields for Amd64 machines (StartAddress, EndAddress, UnwindRVA), otherwise 2 fields (StartAddress, UnwindRVA) + /// + private int CalculateRuntimeFunctionSize() + { + if (Machine == Machine.Amd64) + { + return 3 * sizeof(int); + } + return 2 * sizeof(int); + } + + /// + /// Initialize non-generic R2RMethods with method signatures from MethodDefHandle, and runtime function indices from MethodDefEntryPoints + /// + 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(); - for (uint rid = 1; rid <= nMethodEntryPoints; rid++) + /// + /// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints + /// + 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) + /// + /// Get the RVAs of the runtime functions for each method + /// + 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 /// The relative virtual address 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; }