From f1e2b9d9ac06ae1d71aee7f1865aa26cacd077fd Mon Sep 17 00:00:00 2001 From: Amy Yu Date: Fri, 22 Jun 2018 16:17:24 -0700 Subject: [PATCH] Refactor into TextDumper and XmlDumper classes Commit migrated from https://github.com/dotnet/coreclr/commit/f377d7b4c3771cba99189b957349001878a23840 --- src/coreclr/src/tools/r2rdump/R2RDump.cs | 582 +++------------------------- src/coreclr/src/tools/r2rdump/R2RHeader.cs | 6 +- src/coreclr/src/tools/r2rdump/R2RReader.cs | 6 +- src/coreclr/src/tools/r2rdump/TextDumper.cs | 286 ++++++++++++++ src/coreclr/src/tools/r2rdump/XmlDumper.cs | 309 +++++++++++++++ 5 files changed, 661 insertions(+), 528 deletions(-) create mode 100644 src/coreclr/src/tools/r2rdump/TextDumper.cs create mode 100644 src/coreclr/src/tools/r2rdump/XmlDumper.cs diff --git a/src/coreclr/src/tools/r2rdump/R2RDump.cs b/src/coreclr/src/tools/r2rdump/R2RDump.cs index 6606b49..9a0062b 100644 --- a/src/coreclr/src/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/src/tools/r2rdump/R2RDump.cs @@ -12,13 +12,39 @@ using System.Xml.Serialization; namespace R2RDump { + abstract class Dumper + { + internal R2RReader _r2r; + internal bool _raw; + internal bool _header; + internal bool _disasm; + internal IntPtr _disassembler; + internal bool _unwind; + internal bool _gc; + internal bool _sectionContents; + internal TextWriter _writer; + + abstract internal void Begin(); + abstract internal void End(); + abstract internal void WriteDivider(string title); + abstract internal void WriteSubDivider(); + abstract internal void SkipLine(); + abstract internal void DumpHeader(bool dumpSections); + abstract internal void DumpSection(R2RSection section, XmlNode parentNode = null); + abstract internal void DumpAllMethods(); + abstract internal void DumpMethod(R2RMethod method, XmlNode parentNode = null); + abstract internal void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode = null); + abstract internal void DumpBytes(int rva, uint size, XmlNode parentNode = null, bool convertToOffset = true); + abstract internal void DumpSectionContents(R2RSection section, XmlNode parentNode = null); + abstract internal XmlNode DumpQueryCount(string q, string title, int count); + } + class R2RDump { private bool _help; private IReadOnlyList _inputFilenames = Array.Empty(); private string _outputFilename = null; private bool _xml; - private XmlDocument _xmlDocument; private bool _raw; private bool _header; private bool _disasm; @@ -33,6 +59,7 @@ namespace R2RDump private bool _sectionContents; private TextWriter _writer; private Dictionary _selectedSections = new Dictionary(); + private Dumper _dumper; private R2RDump() { @@ -107,411 +134,6 @@ namespace R2RDump Console.WriteLine($"Warning: {warning}"); } - private void WriteDivider(string title) - { - if (_xml) - return; - int len = 61 - title.Length - 2; - _writer.WriteLine(new String('=', len/2) + " " + title + " " + new String('=', (int)Math.Ceiling(len/2.0))); - SkipLine(); - } - - private void WriteSubDivider() - { - if(_xml) - return; - _writer.WriteLine("_______________________________________________"); - SkipLine(); - } - - private void SkipLine() - { - if (_xml) - return; - _writer.WriteLine(); - } - - /// - /// Dumps the R2RHeader and all the sections in the header - /// - private void DumpHeader(R2RReader r2r, bool dumpSections, XmlNode parentNode) - { - XmlNode headerNode = null; - if (_xml) - { - headerNode = _xmlDocument.CreateNode("element", "Header", ""); - parentNode.AppendChild(headerNode); - Serialize(r2r.R2RHeader, headerNode); - } - else - { - _writer.WriteLine(r2r.R2RHeader.ToString()); - } - if (_raw) - { - DumpBytes(r2r, r2r.R2RHeader.RelativeVirtualAddress, (uint)r2r.R2RHeader.Size, headerNode); - } - SkipLine(); - if (dumpSections) - { - XmlNode sectionsNode = null; - if (_xml) - { - sectionsNode = _xmlDocument.CreateNode("element", "Sections", ""); - parentNode.AppendChild(sectionsNode); - AddXMLNode("Count", r2r.R2RHeader.Sections.Count.ToString(), sectionsNode); - } - else - { - WriteDivider("R2R Sections"); - _writer.WriteLine($"{r2r.R2RHeader.Sections.Count} sections"); - SkipLine(); - } - foreach (R2RSection section in r2r.R2RHeader.Sections.Values) - { - DumpSection(r2r, section, sectionsNode); - } - } - SkipLine(); - } - - /// - /// Dumps one R2RSection - /// - private void DumpSection(R2RReader r2r, R2RSection section, XmlNode parentNode) - { - XmlNode sectionNode = null; - if (_xml) - { - sectionNode = _xmlDocument.CreateNode("element", "Section", ""); - parentNode.AppendChild(sectionNode); - Serialize(section, sectionNode); - } - else - { - WriteSubDivider(); - _writer.WriteLine(section.ToString()); - } - - if (_raw) - { - DumpBytes(r2r, section.RelativeVirtualAddress, (uint)section.Size, sectionNode); - SkipLine(); - } - if (_sectionContents) - { - DumpSectionContents(r2r, section, sectionNode); - SkipLine(); - } - } - - /// - /// Dumps one R2RMethod. - /// - private void DumpMethod(R2RReader r2r, R2RMethod method, XmlNode parentNode) - { - XmlNode methodNode = null; - if (_xml) - { - methodNode = _xmlDocument.CreateNode("element", "Method", ""); - parentNode.AppendChild(methodNode); - Serialize(method, methodNode); - } - else - { - WriteSubDivider(); - _writer.WriteLine(method.ToString()); - } - if (_gc) - { - if (_xml) - { - XmlNode gcNode = _xmlDocument.CreateNode("element", "GcInfo", ""); - methodNode.AppendChild(gcNode); - Serialize(method.GcInfo, gcNode); - - foreach (KeyValuePair transition in method.GcInfo.Transitions) - { - Serialize(transition, gcNode); - } - } - else - { - _writer.WriteLine("GcInfo:"); - _writer.Write(method.GcInfo); - } - - if (_raw) - { - DumpBytes(r2r, method.GcInfo.Offset, (uint)method.GcInfo.Size, methodNode, false); - } - } - SkipLine(); - - XmlNode rtfsNode = null; - if (_xml) - { - rtfsNode = _xmlDocument.CreateNode("element", "RuntimeFunctions", ""); - methodNode.AppendChild(rtfsNode); - } - foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions) - { - DumpRuntimeFunction(r2r, runtimeFunction, rtfsNode); - } - } - - /// - /// Dumps one runtime function. - /// - private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf, XmlNode parentNode) - { - XmlNode rtfNode = null; - if (_xml) - { - rtfNode = _xmlDocument.CreateNode("element", "RuntimeFunction", ""); - parentNode.AppendChild(rtfNode); - AddXMLNode("MethodRid", rtf.Method.Rid.ToString(), rtfNode); - Serialize(rtf, rtfNode); - } - - if (_disasm) - { - string disassembly = CoreDisTools.GetCodeBlock(_disassembler, rtf, r2r.GetOffset(rtf.StartAddress), r2r.Image); - if (_xml) - { - AddXMLNode("Disassembly", disassembly, rtfNode); - } - else - { - _writer.WriteLine($"Id: {rtf.Id}"); - _writer.Write(disassembly); - } - } - else if (!_xml) - { - _writer.Write($"{rtf}"); - } - - if (_raw) - { - if (!_xml) - _writer.WriteLine("Raw Bytes:"); - DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size, rtfNode); - } - if (_unwind) - { - XmlNode unwindNode = null; - if (_xml) - { - unwindNode = _xmlDocument.CreateNode("element", "UnwindInfo", ""); - rtfNode.AppendChild(unwindNode); - Serialize(rtf.UnwindInfo, unwindNode); - } - else - { - _writer.WriteLine("UnwindInfo:"); - _writer.Write(rtf.UnwindInfo); - } - if (_raw) - { - DumpBytes(r2r, rtf.UnwindRVA, (uint)((Amd64.UnwindInfo)rtf.UnwindInfo).Size, unwindNode); - } - } - SkipLine(); - } - - /// - /// Prints a formatted string containing a block of bytes from the relative virtual address and size - /// - public void DumpBytes(R2RReader r2r, int rva, uint size, XmlNode parentNode, bool convertToOffset = true) - { - int start = rva; - if (convertToOffset) - start = r2r.GetOffset(rva); - if (start > r2r.Image.Length || start + size > r2r.Image.Length) - { - throw new IndexOutOfRangeException(); - } - - if (_xml && parentNode != null) - { - StringBuilder sb = new StringBuilder(); - sb.Append($"{r2r.Image[start]:X2}"); - for (uint i = 1; i < size; i++) - { - sb.Append($" {r2r.Image[start + i]:X2}"); - } - AddXMLNode("Raw", sb.ToString(), parentNode); - return; - } - - _writer.Write(" "); - if (rva % 16 != 0) - { - int floor = rva / 16 * 16; - _writer.Write($"{floor:X8}:"); - _writer.Write(new String(' ', (rva - floor) * 3)); - } - for (uint i = 0; i < size; i++) - { - if ((rva + i) % 16 == 0) - { - _writer.Write($"{rva + i:X8}:"); - } - _writer.Write($" {r2r.Image[start + i]:X2}"); - if ((rva + i) % 16 == 15 && i != size - 1) - { - SkipLine(); - _writer.Write(" "); - } - } - SkipLine(); - } - - private void DumpSectionContents(R2RReader r2r, R2RSection section, XmlNode contentsNode) - { - switch (section.Type) - { - case R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES: - if(!_xml) - { - uint availableTypesSectionOffset = (uint)r2r.GetOffset(section.RelativeVirtualAddress); - NativeParser availableTypesParser = new NativeParser(r2r.Image, availableTypesSectionOffset); - NativeHashtable availableTypes = new NativeHashtable(r2r.Image, availableTypesParser, (uint)(availableTypesSectionOffset + section.Size)); - _writer.WriteLine(availableTypes.ToString()); - } - - if (_xml) - { - Serialize(r2r.AvailableTypes, contentsNode); - } - else - { - foreach (string name in r2r.AvailableTypes) - { - _writer.WriteLine(name); - } - } - break; - case R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS: - if (!_xml) - { - NativeArray methodEntryPoints = new NativeArray(r2r.Image, (uint)r2r.GetOffset(section.RelativeVirtualAddress)); - _writer.Write(methodEntryPoints.ToString()); - } - break; - case R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS: - if (!_xml) - { - uint instanceSectionOffset = (uint)r2r.GetOffset(section.RelativeVirtualAddress); - NativeParser instanceParser = new NativeParser(r2r.Image, instanceSectionOffset); - NativeHashtable instMethodEntryPoints = new NativeHashtable(r2r.Image, instanceParser, (uint)(instanceSectionOffset + section.Size)); - _writer.Write(instMethodEntryPoints.ToString()); - } - break; - case R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS: - int rtfOffset = r2r.GetOffset(section.RelativeVirtualAddress); - int rtfEndOffset = rtfOffset + section.Size; - int rtfIndex = 0; - while (rtfOffset < rtfEndOffset) - { - uint rva = NativeReader.ReadUInt32(r2r.Image, ref rtfOffset); - if (_xml) - { - AddXMLNode($"{rtfIndex}", $"0x{rva:X8}", contentsNode); - } - else - { - _writer.WriteLine($"{rtfIndex}: 0x{rva:X8}"); - } - rtfIndex++; - } - break; - case R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER: - if(_xml) - { - AddXMLNode("CompileIdentifier", r2r.CompileIdentifier, contentsNode); - } - else - { - _writer.WriteLine(r2r.CompileIdentifier); - } - break; - case R2RSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS: - foreach (R2RImportSection importSection in r2r.ImportSections) - { - if (_xml) - { - Serialize(importSection, contentsNode); - } - else - { - _writer.Write(importSection.ToString()); - } - if (_raw && importSection.Entries.Count != 0) - { - if (importSection.SectionRVA != 0) - { - XmlNode bytesNode = null; - if (_xml) - { - bytesNode = _xmlDocument.CreateNode("element", "SectionBytes", ""); - contentsNode.AppendChild(bytesNode); - } - else - { - _writer.WriteLine("Section Bytes:"); - } - DumpBytes(r2r, importSection.SectionRVA, (uint)importSection.SectionSize, bytesNode); - } - if (importSection.SignatureRVA != 0) - { - XmlNode bytesNode = null; - if (_xml) - { - bytesNode = _xmlDocument.CreateNode("element", "SignatureBytes", ""); - contentsNode.AppendChild(bytesNode); - } - else - { - _writer.WriteLine("Signature Bytes:"); - } - DumpBytes(r2r, importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int), bytesNode); - } - if (importSection.AuxiliaryDataRVA != 0) - { - XmlNode bytesNode = null; - if (_xml) - { - bytesNode = _xmlDocument.CreateNode("element", "AuxiliaryDataBytes", ""); - contentsNode.AppendChild(bytesNode); - } - else - { - _writer.WriteLine("AuxiliaryData Bytes:"); - } - DumpBytes(r2r, importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size, bytesNode); - } - } - foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries) - { - if (_xml) - { - Serialize(entry, contentsNode); - } - else - { - _writer.WriteLine(); - _writer.WriteLine(entry.ToString()); - } - } - if (!_xml) - _writer.WriteLine(); - } - break; - } - } - // /// For each query in the list of queries, search for all methods matching the query by name, signature or id /// @@ -519,31 +141,19 @@ namespace R2RDump /// The title to print, "R2R Methods by Query" or "R2R Methods by Keyword" /// The keywords/ids to search for /// Specifies whether to look for methods with names/signatures/ids matching the method exactly or partially - private void QueryMethod(R2RReader r2r, string title, IReadOnlyList queries, bool exact, XmlNode parentNode) + private void QueryMethod(R2RReader r2r, string title, IReadOnlyList queries, bool exact) { if (queries.Count > 0) { - WriteDivider(title); + _dumper.WriteDivider(title); } foreach (string q in queries) { IList res = FindMethod(r2r, q, exact); - XmlNode queryNode = null; - if (_xml) - { - queryNode = _xmlDocument.CreateNode("element", "Methods", ""); - parentNode.AppendChild(queryNode); - AddXMLNode("Query", q, queryNode); - AddXMLNode("Count", res.Count.ToString(), queryNode); - } - else - { - _writer.WriteLine(res.Count + " result(s) for \"" + q + "\""); - SkipLine(); - } + XmlNode queryNode = _dumper.DumpQueryCount(q, "Methods", res.Count); foreach (R2RMethod method in res) { - DumpMethod(r2r, method, queryNode); + _dumper.DumpMethod(method, queryNode); } } } @@ -553,31 +163,19 @@ namespace R2RDump /// /// Contains all the extracted info about the ReadyToRun image /// The names/values to search for - private void QuerySection(R2RReader r2r, IReadOnlyList queries, XmlNode parentNode) + private void QuerySection(R2RReader r2r, IReadOnlyList queries) { if (queries.Count > 0) { - WriteDivider("R2R Section"); + _dumper.WriteDivider("R2R Section"); } foreach (string q in queries) { IList res = FindSection(r2r, q); - XmlNode queryNode = null; - if (_xml) - { - queryNode = _xmlDocument.CreateNode("element", "Sections", ""); - parentNode.AppendChild(queryNode); - AddXMLNode("Query", q, queryNode); - AddXMLNode("Count", res.Count.ToString(), queryNode); - } - else - { - _writer.WriteLine(res.Count + " result(s) for \"" + q + "\""); - SkipLine(); - } + XmlNode queryNode = _dumper.DumpQueryCount(q, "Sections", res.Count); foreach (R2RSection section in res) { - DumpSection(r2r, section, queryNode); + _dumper.DumpSection(section, queryNode); } } } @@ -588,11 +186,11 @@ namespace R2RDump /// /// Contains all the extracted info about the ReadyToRun image /// The ids to search for - private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList queries, XmlNode parentNode) + private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList queries) { if (queries.Count > 0) { - WriteDivider("Runtime Functions"); + _dumper.WriteDivider("Runtime Functions"); } foreach (int q in queries) { @@ -603,19 +201,8 @@ namespace R2RDump WriteWarning("Unable to find by id " + q); continue; } - XmlNode queryNode = null; - if (_xml) - { - queryNode = _xmlDocument.CreateNode("element", "RuntimeFunctions", ""); - parentNode.AppendChild(queryNode); - AddXMLNode("Query", q.ToString(), queryNode); - AddXMLNode("Count", "1", queryNode); - } - else - { - _writer.WriteLine(rtf.Method.SignatureString); - } - DumpRuntimeFunction(r2r, rtf, queryNode); + XmlNode queryNode = _dumper.DumpQueryCount(q.ToString(), "Runtime Function", 1); + _dumper.DumpRuntimeFunction(rtf, queryNode); } } @@ -625,82 +212,33 @@ namespace R2RDump /// The structure containing the info of the ReadyToRun image public void Dump(R2RReader r2r) { - XmlNode rootNode = null; - if (_xml) - { - rootNode = _xmlDocument.CreateNode("element", "R2RDump", ""); - _xmlDocument.AppendChild(rootNode); - Serialize(r2r, rootNode); - } - else - { - _writer.WriteLine($"Filename: {r2r.Filename}"); - _writer.WriteLine($"Machine: {r2r.Machine}"); - _writer.WriteLine($"ImageBase: 0x{r2r.ImageBase:X8}"); - SkipLine(); - } + + _dumper.Begin(); if (_queries.Count == 0 && _keywords.Count == 0 && _runtimeFunctions.Count == 0 && _sections.Count == 0) //dump all sections and methods { - WriteDivider("R2R Header"); - DumpHeader(r2r, true, rootNode); + _dumper.WriteDivider("R2R Header"); + _dumper.DumpHeader(true); if (!_header) { - XmlNode methodsNode = null; - if (_xml) - { - methodsNode = _xmlDocument.CreateNode("element", "Methods", ""); - rootNode.AppendChild(methodsNode); - AddXMLNode("Count", r2r.R2RMethods.Count.ToString(), methodsNode); - } - else - { - WriteDivider("R2R Methods"); - _writer.WriteLine($"{r2r.R2RMethods.Count} methods"); - SkipLine(); - } - foreach (R2RMethod method in r2r.R2RMethods) - { - DumpMethod(r2r, method, methodsNode); - } + _dumper.DumpAllMethods(); } } else //dump queried sections/methods/runtimeFunctions { if (_header) { - DumpHeader(r2r, false, rootNode); + _dumper.DumpHeader(false); } - QuerySection(r2r, _sections, rootNode); - QueryRuntimeFunction(r2r, _runtimeFunctions, rootNode); - QueryMethod(r2r, "R2R Methods by Query", _queries, true, rootNode); - QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false, rootNode); - } - if (!_xml) - { - _writer.WriteLine("============================================================="); - SkipLine(); - } - } - - private void Serialize(object obj, XmlNode node) - { - using (XmlWriter xmlWriter = node.CreateNavigator().AppendChild()) - { - xmlWriter.WriteWhitespace(""); - XmlSerializer Serializer = new XmlSerializer(obj.GetType()); - Serializer.Serialize(xmlWriter, obj); + QuerySection(r2r, _sections); + QueryRuntimeFunction(r2r, _runtimeFunctions); + QueryMethod(r2r, "R2R Methods by Query", _queries, true); + QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false); } - } - private XmlNode AddXMLNode(String name, String contents, XmlNode parentNode) - { - XmlNode node = _xmlDocument.CreateNode("element", name, ""); - parentNode.AppendChild(node); - node.InnerText = contents; - return node; + _dumper.End(); } /// @@ -837,27 +375,27 @@ namespace R2RDump foreach (string filename in _inputFilenames) { R2RReader r2r = new R2RReader(filename); - if (_xml) - { - _xmlDocument = new XmlDocument(); - } if (_disasm) { _disassembler = CoreDisTools.GetDisasm(r2r.Machine); } + if (_xml) + { + _dumper = new XmlDumper(r2r, _writer, _raw, _header, _disasm, _disassembler, _unwind, _gc, _sectionContents); + } + else + { + _dumper = new TextDumper(r2r, _writer, _raw, _header, _disasm, _disassembler, _unwind, _gc, _sectionContents); + } + Dump(r2r); if (_disasm) { CoreDisTools.FinishDisasm(_disassembler); } - - if (_xml) - { - _xmlDocument.Save(_writer); - } } } catch (Exception e) diff --git a/src/coreclr/src/tools/r2rdump/R2RHeader.cs b/src/coreclr/src/tools/r2rdump/R2RHeader.cs index cf7e65a..7212d3e 100644 --- a/src/coreclr/src/tools/r2rdump/R2RHeader.cs +++ b/src/coreclr/src/tools/r2rdump/R2RHeader.cs @@ -70,9 +70,9 @@ namespace R2RDump RelativeVirtualAddress = rva; int startOffset = curOffset; - byte[] signature = new byte[sizeof(uint)]; - Array.Copy(image, curOffset, signature, 0, sizeof(uint)); - SignatureString = Encoding.UTF8.GetString(signature).Replace("\0", string.Empty); ; + byte[] signature = new byte[sizeof(uint) - 1]; // -1 removes the null character at the end of the cstring + Array.Copy(image, curOffset, signature, 0, sizeof(uint) - 1); + SignatureString = Encoding.UTF8.GetString(signature); Signature = NativeReader.ReadUInt32(image, ref curOffset); if (Signature != READYTORUN_SIGNATURE) { diff --git a/src/coreclr/src/tools/r2rdump/R2RReader.cs b/src/coreclr/src/tools/r2rdump/R2RReader.cs index 56539da..026d7cc 100644 --- a/src/coreclr/src/tools/r2rdump/R2RReader.cs +++ b/src/coreclr/src/tools/r2rdump/R2RReader.cs @@ -312,10 +312,10 @@ namespace R2RDump return ""; } R2RSection compilerIdentifierSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER]; - byte[] identifier = new byte[compilerIdentifierSection.Size]; + byte[] identifier = new byte[compilerIdentifierSection.Size - 1]; int identifierOffset = GetOffset(compilerIdentifierSection.RelativeVirtualAddress); - Array.Copy(Image, identifierOffset, identifier, 0, compilerIdentifierSection.Size); - return Encoding.UTF8.GetString(identifier).Replace("\0", string.Empty); + Array.Copy(Image, identifierOffset, identifier, 0, compilerIdentifierSection.Size - 1); + return Encoding.UTF8.GetString(identifier); } private void ParseImportSections() diff --git a/src/coreclr/src/tools/r2rdump/TextDumper.cs b/src/coreclr/src/tools/r2rdump/TextDumper.cs new file mode 100644 index 0000000..f444f0d --- /dev/null +++ b/src/coreclr/src/tools/r2rdump/TextDumper.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace R2RDump +{ + class TextDumper : Dumper + { + public TextDumper(R2RReader r2r, TextWriter writer, bool raw, bool header, bool disasm, IntPtr disassembler, bool unwind, bool gc, bool sectionContents) + { + _r2r = r2r; + _writer = writer; + + _raw = raw; + _header = header; + _disasm = disasm; + _disassembler = disassembler; + _unwind = unwind; + _gc = gc; + _sectionContents = sectionContents; + } + + internal override void Begin() + { + _writer.WriteLine($"Filename: {_r2r.Filename}"); + _writer.WriteLine($"Machine: {_r2r.Machine}"); + _writer.WriteLine($"ImageBase: 0x{_r2r.ImageBase:X8}"); + SkipLine(); + } + + internal override void End() + { + _writer.WriteLine("============================================================="); + SkipLine(); + } + + internal override void WriteDivider(string title) + { + int len = 61 - title.Length - 2; + _writer.WriteLine(new String('=', len / 2) + " " + title + " " + new String('=', (int)Math.Ceiling(len / 2.0))); + SkipLine(); + } + + internal override void WriteSubDivider() + { + _writer.WriteLine("_______________________________________________"); + SkipLine(); + } + + internal override void SkipLine() + { + _writer.WriteLine(); + } + + /// + /// Dumps the R2RHeader and all the sections in the header + /// + internal override void DumpHeader(bool dumpSections) + { + _writer.WriteLine(_r2r.R2RHeader.ToString()); + + if (_raw) + { + DumpBytes(_r2r.R2RHeader.RelativeVirtualAddress, (uint)_r2r.R2RHeader.Size); + } + SkipLine(); + if (dumpSections) + { + WriteDivider("R2R Sections"); + _writer.WriteLine($"{_r2r.R2RHeader.Sections.Count} sections"); + SkipLine(); + + foreach (R2RSection section in _r2r.R2RHeader.Sections.Values) + { + DumpSection(section); + } + } + SkipLine(); + } + + /// + /// Dumps one R2RSection + /// + internal override void DumpSection(R2RSection section, XmlNode parentNode = null) + { + WriteSubDivider(); + _writer.WriteLine(section.ToString()); + + if (_raw) + { + DumpBytes(section.RelativeVirtualAddress, (uint)section.Size); + SkipLine(); + } + if (_sectionContents) + { + DumpSectionContents(section); + SkipLine(); + } + } + + internal override void DumpAllMethods() + { + WriteDivider("R2R Methods"); + _writer.WriteLine($"{_r2r.R2RMethods.Count} methods"); + SkipLine(); + foreach (R2RMethod method in _r2r.R2RMethods) + { + DumpMethod(method); + } + } + + /// + /// Dumps one R2RMethod. + /// + internal override void DumpMethod(R2RMethod method, XmlNode parentNode = null) + { + WriteSubDivider(); + _writer.WriteLine(method.ToString()); + + if (_gc) + { + _writer.WriteLine("GcInfo:"); + _writer.Write(method.GcInfo); + + if (_raw) + { + DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, null, false); + } + } + SkipLine(); + + foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions) + { + DumpRuntimeFunction(runtimeFunction); + } + } + + /// + /// Dumps one runtime function. + /// + internal override void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode = null) + { + _writer.WriteLine(rtf.Method.SignatureString); + _writer.Write($"{rtf}"); + + if (_disasm) + { + string disassembly = CoreDisTools.GetCodeBlock(_disassembler, rtf, _r2r.GetOffset(rtf.StartAddress), _r2r.Image); + _writer.Write(disassembly); + } + + if (_raw) + { + _writer.WriteLine("Raw Bytes:"); + DumpBytes(rtf.StartAddress, (uint)rtf.Size); + } + if (_unwind) + { + _writer.WriteLine("UnwindInfo:"); + _writer.Write(rtf.UnwindInfo); + if (_raw) + { + DumpBytes(rtf.UnwindRVA, (uint)((UnwindInfo)rtf.UnwindInfo).Size); + } + } + SkipLine(); + } + + /// + /// Prints a formatted string containing a block of bytes from the relative virtual address and size + /// + internal override void DumpBytes(int rva, uint size, XmlNode parentNode = null, bool convertToOffset = true) + { + int start = rva; + if (convertToOffset) + start = _r2r.GetOffset(rva); + if (start > _r2r.Image.Length || start + size > _r2r.Image.Length) + { + throw new IndexOutOfRangeException(); + } + + _writer.Write(" "); + if (rva % 16 != 0) + { + int floor = rva / 16 * 16; + _writer.Write($"{floor:X8}:"); + _writer.Write(new String(' ', (rva - floor) * 3)); + } + for (uint i = 0; i < size; i++) + { + if ((rva + i) % 16 == 0) + { + _writer.Write($"{rva + i:X8}:"); + } + _writer.Write($" {_r2r.Image[start + i]:X2}"); + if ((rva + i) % 16 == 15 && i != size - 1) + { + SkipLine(); + _writer.Write(" "); + } + } + SkipLine(); + } + + internal override void DumpSectionContents(R2RSection section, XmlNode parentNode = null) + { + switch (section.Type) + { + case R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES: + uint availableTypesSectionOffset = (uint)_r2r.GetOffset(section.RelativeVirtualAddress); + NativeParser availableTypesParser = new NativeParser(_r2r.Image, availableTypesSectionOffset); + NativeHashtable availableTypes = new NativeHashtable(_r2r.Image, availableTypesParser, (uint)(availableTypesSectionOffset + section.Size)); + _writer.WriteLine(availableTypes.ToString()); + + foreach (string name in _r2r.AvailableTypes) + { + _writer.WriteLine(name); + } + break; + case R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS: + NativeArray methodEntryPoints = new NativeArray(_r2r.Image, (uint)_r2r.GetOffset(section.RelativeVirtualAddress)); + _writer.Write(methodEntryPoints.ToString()); + break; + case R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS: + uint instanceSectionOffset = (uint)_r2r.GetOffset(section.RelativeVirtualAddress); + NativeParser instanceParser = new NativeParser(_r2r.Image, instanceSectionOffset); + NativeHashtable instMethodEntryPoints = new NativeHashtable(_r2r.Image, instanceParser, (uint)(instanceSectionOffset + section.Size)); + _writer.Write(instMethodEntryPoints.ToString()); + break; + case R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS: + int rtfOffset = _r2r.GetOffset(section.RelativeVirtualAddress); + int rtfEndOffset = rtfOffset + section.Size; + int rtfIndex = 0; + while (rtfOffset < rtfEndOffset) + { + uint rva = NativeReader.ReadUInt32(_r2r.Image, ref rtfOffset); + _writer.WriteLine($"{rtfIndex}: 0x{rva:X8}"); + rtfIndex++; + } + break; + case R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER: + _writer.WriteLine(_r2r.CompileIdentifier); + break; + case R2RSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS: + foreach (R2RImportSection importSection in _r2r.ImportSections) + { + _writer.Write(importSection.ToString()); + if (_raw && importSection.Entries.Count != 0) + { + if (importSection.SectionRVA != 0) + { + _writer.WriteLine("Section Bytes:"); + DumpBytes(importSection.SectionRVA, (uint)importSection.SectionSize); + } + if (importSection.SignatureRVA != 0) + { + _writer.WriteLine("Signature Bytes:"); + DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int)); + } + if (importSection.AuxiliaryDataRVA != 0) + { + _writer.WriteLine("AuxiliaryData Bytes:"); + DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size); + } + } + foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries) + { + _writer.WriteLine(); + _writer.WriteLine(entry.ToString()); + } + _writer.WriteLine(); + } + break; + } + } + + internal override XmlNode DumpQueryCount(string q, string title, int count) + { + _writer.WriteLine(count + " result(s) for \"" + q + "\""); + SkipLine(); + return null; + } + } +} diff --git a/src/coreclr/src/tools/r2rdump/XmlDumper.cs b/src/coreclr/src/tools/r2rdump/XmlDumper.cs new file mode 100644 index 0000000..52c28f4 --- /dev/null +++ b/src/coreclr/src/tools/r2rdump/XmlDumper.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace R2RDump +{ + class XmlDumper : Dumper + { + private XmlDocument _xmlDocument; + private XmlNode _rootNode; + + public XmlDumper(R2RReader r2r, TextWriter writer, bool raw, bool header, bool disasm, IntPtr disassembler, bool unwind, bool gc, bool sectionContents) + { + _r2r = r2r; + _writer = writer; + _xmlDocument = new XmlDocument(); + + _raw = raw; + _header = header; + _disasm = disasm; + _disassembler = disassembler; + _unwind = unwind; + _gc = gc; + _sectionContents = sectionContents; + } + + internal override void Begin() + { + _rootNode = _xmlDocument.CreateNode("element", "R2RDump", ""); + _xmlDocument.AppendChild(_rootNode); + Serialize(_r2r, _rootNode); + } + + internal override void End() { + _xmlDocument.Save(_writer); + } + + internal override void WriteDivider(string title) + { + } + + internal override void WriteSubDivider() + { + } + + internal override void SkipLine() + { + } + + /// + /// Dumps the R2RHeader and all the sections in the header + /// + internal override void DumpHeader(bool dumpSections) + { + XmlNode headerNode = _xmlDocument.CreateNode("element", "Header", ""); + _rootNode.AppendChild(headerNode); + Serialize(_r2r.R2RHeader, headerNode); + + if (_raw) + { + DumpBytes(_r2r.R2RHeader.RelativeVirtualAddress, (uint)_r2r.R2RHeader.Size, headerNode); + } + + if (dumpSections) + { + XmlNode sectionsNode = _xmlDocument.CreateNode("element", "Sections", ""); + _rootNode.AppendChild(sectionsNode); + AddXMLNode("Count", _r2r.R2RHeader.Sections.Count.ToString(), sectionsNode); + + foreach (R2RSection section in _r2r.R2RHeader.Sections.Values) + { + DumpSection(section, sectionsNode); + } + } + } + + /// + /// Dumps one R2RSection + /// + internal override void DumpSection(R2RSection section, XmlNode parentNode) + { + XmlNode sectionNode = _xmlDocument.CreateNode("element", "Section", ""); + parentNode.AppendChild(sectionNode); + Serialize(section, sectionNode); + + if (_raw) + { + DumpBytes(section.RelativeVirtualAddress, (uint)section.Size, sectionNode); + } + if (_sectionContents) + { + DumpSectionContents(section, sectionNode); + } + } + + internal override void DumpAllMethods() + { + XmlNode methodsNode = _xmlDocument.CreateNode("element", "Methods", ""); + _rootNode.AppendChild(methodsNode); + AddXMLNode("Count", _r2r.R2RMethods.Count.ToString(), methodsNode); + foreach (R2RMethod method in _r2r.R2RMethods) + { + DumpMethod(method, methodsNode); + } + } + + /// + /// Dumps one R2RMethod. + /// + internal override void DumpMethod(R2RMethod method, XmlNode parentNode) + { + XmlNode methodNode = _xmlDocument.CreateNode("element", "Method", ""); + parentNode.AppendChild(methodNode); + Serialize(method, methodNode); + + if (_gc) + { + XmlNode gcNode = _xmlDocument.CreateNode("element", "GcInfo", ""); + methodNode.AppendChild(gcNode); + Serialize(method.GcInfo, gcNode); + + foreach (KeyValuePair transition in method.GcInfo.Transitions) + { + Serialize(transition, gcNode); + } + + if (_raw) + { + DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, methodNode, false); + } + } + + XmlNode rtfsNode = null; + rtfsNode = _xmlDocument.CreateNode("element", "RuntimeFunctions", ""); + methodNode.AppendChild(rtfsNode); + + foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions) + { + DumpRuntimeFunction(runtimeFunction, rtfsNode); + } + } + + /// + /// Dumps one runtime function. + /// + internal override void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode) + { + XmlNode rtfNode = _xmlDocument.CreateNode("element", "RuntimeFunction", ""); + parentNode.AppendChild(rtfNode); + AddXMLNode("MethodRid", rtf.Method.Rid.ToString(), rtfNode); + Serialize(rtf, rtfNode); + + if (_disasm) + { + string disassembly = CoreDisTools.GetCodeBlock(_disassembler, rtf, _r2r.GetOffset(rtf.StartAddress), _r2r.Image); + AddXMLNode("Disassembly", disassembly, rtfNode); + } + + if (_raw) + { + DumpBytes(rtf.StartAddress, (uint)rtf.Size, rtfNode); + } + if (_unwind) + { + XmlNode unwindNode = null; + unwindNode = _xmlDocument.CreateNode("element", "UnwindInfo", ""); + rtfNode.AppendChild(unwindNode); + Serialize(rtf.UnwindInfo, unwindNode); + + if (_raw) + { + DumpBytes(rtf.UnwindRVA, (uint)((UnwindInfo)rtf.UnwindInfo).Size, unwindNode); + } + } + } + + /// + /// Prints a formatted string containing a block of bytes from the relative virtual address and size + /// + internal override void DumpBytes(int rva, uint size, XmlNode parentNode, bool convertToOffset = true) + { + int start = rva; + if (convertToOffset) + start = _r2r.GetOffset(rva); + if (start > _r2r.Image.Length || start + size > _r2r.Image.Length) + { + throw new IndexOutOfRangeException(); + } + + if (parentNode != null) + { + StringBuilder sb = new StringBuilder(); + sb.Append($"{_r2r.Image[start]:X2}"); + for (uint i = 1; i < size; i++) + { + sb.Append($" {_r2r.Image[start + i]:X2}"); + } + AddXMLNode("Raw", sb.ToString(), parentNode); + return; + } + + _writer.Write(" "); + if (rva % 16 != 0) + { + int floor = rva / 16 * 16; + _writer.Write($"{floor:X8}:"); + _writer.Write(new String(' ', (rva - floor) * 3)); + } + for (uint i = 0; i < size; i++) + { + if ((rva + i) % 16 == 0) + { + _writer.Write($"{rva + i:X8}:"); + } + _writer.Write($" {_r2r.Image[start + i]:X2}"); + if ((rva + i) % 16 == 15 && i != size - 1) + { + _writer.Write(" "); + } + } + } + + internal override void DumpSectionContents(R2RSection section, XmlNode parentNode) + { + XmlNode contentsNode = _xmlDocument.CreateNode("element", "Contents", ""); + parentNode.AppendChild(contentsNode); + + switch (section.Type) + { + case R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES: + + foreach (string name in _r2r.AvailableTypes) + { + AddXMLNode("AvailableType", name, contentsNode); + } + break; + case R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS: + int rtfOffset = _r2r.GetOffset(section.RelativeVirtualAddress); + int rtfEndOffset = rtfOffset + section.Size; + int rtfIndex = 0; + while (rtfOffset < rtfEndOffset) + { + uint rva = NativeReader.ReadUInt32(_r2r.Image, ref rtfOffset); + AddXMLNode($"id{rtfIndex}", $"0x{rva:X8}", contentsNode); + rtfIndex++; + } + break; + case R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER: + AddXMLNode("CompileIdentifier", _r2r.CompileIdentifier, contentsNode); + break; + case R2RSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS: + foreach (R2RImportSection importSection in _r2r.ImportSections) + { + Serialize(importSection, contentsNode); + if (_raw && importSection.Entries.Count != 0) + { + if (importSection.SectionRVA != 0) + { + DumpBytes(importSection.SectionRVA, (uint)importSection.SectionSize, contentsNode); + } + if (importSection.SignatureRVA != 0) + { + DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int), contentsNode); + } + if (importSection.AuxiliaryDataRVA != 0) + { + DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size, contentsNode); + } + } + foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries) + { + Serialize(entry, contentsNode); + } + } + break; + } + } + + internal override XmlNode DumpQueryCount(string q, string title, int count) + { + XmlNode queryNode = _xmlDocument.CreateNode("element", title, ""); + _rootNode.AppendChild(queryNode); + AddXMLNode("Query", q, queryNode); + AddXMLNode("Count", count.ToString(), queryNode); + return queryNode; + } + + private void Serialize(object obj, XmlNode node) + { + using (XmlWriter xmlWriter = node.CreateNavigator().AppendChild()) + { + xmlWriter.WriteWhitespace(""); + XmlSerializer Serializer = new XmlSerializer(obj.GetType()); + Serializer.Serialize(xmlWriter, obj); + } + } + + private XmlNode AddXMLNode(String name, String contents, XmlNode parentNode) + { + XmlNode node = _xmlDocument.CreateNode("element", name, ""); + parentNode.AppendChild(node); + node.InnerText = contents; + return node; + } + } +} -- 2.7.4