summaryrefslogtreecommitdiff
path: root/src/tools/r2rdump/R2RDump.cs
diff options
context:
space:
mode:
authorAmy <amycmyu@gmail.com>2018-06-01 08:05:44 -0700
committerZach Montoya <zamont@microsoft.com>2018-06-01 08:05:44 -0700
commit88c10681e2b9a8584f574df234ee2a2ff74a8ea3 (patch)
tree8a593110c50fcd9603e4e8b8aad8a887decbccf5 /src/tools/r2rdump/R2RDump.cs
parentc17ca743320b8c0c54ca1b8248b4e8cbc771c241 (diff)
downloadcoreclr-88c10681e2b9a8584f574df234ee2a2ff74a8ea3.tar.gz
coreclr-88c10681e2b9a8584f574df234ee2a2ff74a8ea3.tar.bz2
coreclr-88c10681e2b9a8584f574df234ee2a2ff74a8ea3.zip
R2RDump - Commandline interface (#18136)
* Use ReadCompressedData for NativeHashTable, fix ref signature types, save array dimension, use AppendLine to avoid line-ending problems * Include System.CommandLine, arg parsing, read/write file * Add commandline options to search sections/methods/runtimeFunctions, option to dump raw data * Added comments, save cli options in global variables, renamed some functions * Add DumpByte function to R2RSection and R2RHeader, indent raw bytes * Change some cli option names, use DumpRuntimeFunction, return list of query matches, changes to ArgStringToInt * Move DumpBytes to R2RDump, print method of runtime function * Use writer object instead of Console * Use TextWriter instead of own writer class * Handle jagged arrays and other cases using MethodDefinition.DecodeSignature with a slightly modified DisassemblingTypeProvider * Close the _writer in finally block
Diffstat (limited to 'src/tools/r2rdump/R2RDump.cs')
-rw-r--r--src/tools/r2rdump/R2RDump.cs453
1 files changed, 434 insertions, 19 deletions
diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs
index 7d0bd2fd19..9aa80395f5 100644
--- a/src/tools/r2rdump/R2RDump.cs
+++ b/src/tools/r2rdump/R2RDump.cs
@@ -4,55 +4,470 @@
using System;
using System.Collections.Generic;
+using System.CommandLine;
+using System.IO;
namespace R2RDump
{
class R2RDump
{
- public static void OutputWarning(string warning)
+ private bool _help = false;
+ private IReadOnlyList<string> _inputFilenames = Array.Empty<string>();
+ private string _outputFilename = null;
+ private bool _raw = false;
+ private bool _header = false;
+ private bool _disasm = false;
+ 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 = false;
+ private TextWriter _writer;
+
+ private R2RDump()
+ {
+ }
+
+ private ArgumentSyntax ParseCommandLine(string[] args)
+ {
+ ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax =>
+ {
+ syntax.ApplicationName = "R2RDump";
+ syntax.HandleHelp = false;
+ syntax.HandleErrors = true;
+
+ syntax.DefineOption("h|help", ref _help, "Help message for 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("v|verbose|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.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("diff", ref _diff, "Compare two R2R images"); // not yet implemented
+ });
+
+ return argSyntax;
+ }
+
+ private int ArgStringToInt(string arg)
+ {
+ int n;
+ if (!ArgStringToInt(arg, out n))
+ {
+ throw new ArgumentException("Can't parse argument to int");
+ }
+ return n;
+ }
+
+ /// <summary>
+ /// Converts string passed as cmd line args into int, works for hexidecimal with 0x as prefix
+ /// </summary>
+ /// <param name="arg">The argument string to convert</param>
+ /// <param name="n">The integer representation</param>
+ private bool ArgStringToInt(string arg, out int n)
+ {
+ arg = arg.Trim();
+ if (arg.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ {
+ return int.TryParse(arg.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out n);
+ }
+ return int.TryParse(arg, out n);
+ }
+
+ public static void WriteWarning(string warning)
{
Console.WriteLine($"Warning: {warning}");
}
- public static int Main(string[] args)
+ private void WriteDivider(string title)
{
- try
+ _writer.WriteLine("============== " + title + " ==============");
+ _writer.WriteLine();
+ }
+
+ private void WriteSubDivider()
+ {
+ _writer.WriteLine("------------------");
+ _writer.WriteLine();
+ }
+
+ /// <summary>
+ /// Dumps the R2RHeader and all the sections in the header
+ /// </summary>
+ private void DumpHeader(R2RReader r2r, bool dumpSections)
+ {
+ _writer.WriteLine(r2r.R2RHeader.ToString());
+ if (_raw)
+ {
+ DumpBytes(r2r, r2r.R2RHeader.RelativeVirtualAddress, (uint)r2r.R2RHeader.Size);
+ }
+ if (dumpSections)
+ {
+ WriteDivider("R2R Sections");
+ foreach (R2RSection section in r2r.R2RHeader.Sections.Values)
+ {
+ DumpSection(r2r, section);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Dumps one R2RSection
+ /// </summary>
+ private void DumpSection(R2RReader r2r, R2RSection section)
+ {
+ WriteSubDivider();
+ _writer.WriteLine(section.ToString());
+ if (_raw)
+ {
+ DumpBytes(r2r, section.RelativeVirtualAddress, (uint)section.Size);
+ }
+ }
+
+ /// <summary>
+ /// Dumps one R2RMethod.
+ /// </summary>
+ private void DumpMethod(R2RReader r2r, R2RMethod method)
+ {
+ WriteSubDivider();
+ _writer.WriteLine(method.ToString());
+
+ foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions)
+ {
+ DumpRuntimeFunction(r2r, runtimeFunction);
+ }
+ }
+
+ /// <summary>
+ /// Dumps one runtime function.
+ /// </summary>
+ private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf)
+ {
+ _writer.WriteLine(rtf.ToString());
+ if (_raw)
+ {
+ DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size);
+ }
+ _writer.WriteLine();
+ }
+
+ /// <summary>
+ /// Prints a formatted string containing a block of bytes from the relative virtual address and size
+ /// </summary>
+ public void DumpBytes(R2RReader r2r, int rva, uint size)
+ {
+ uint start = (uint)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)
+ {
+ _writer.WriteLine();
+ _writer.Write(" ");
+ }
+ }
+ _writer.WriteLine();
+ }
+
+ // <summary>
+ /// For each query in the list of queries, search for all methods matching the query by name, signature or id
+ /// </summary>
+ /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
+ /// <param name="title">The title to print, "R2R Methods by Query" or "R2R Methods by Keyword"</param>
+ /// <param name="queries">The keywords/ids to search for</param>
+ /// <param name="exact">Specifies whether to look for methods with names/signatures/ids matching the method exactly or partially</param>
+ private void QueryMethod(R2RReader r2r, string title, IReadOnlyList<string> queries, bool exact)
+ {
+ if (queries.Count > 0)
{
- if (args.Length < 1)
+ WriteDivider(title);
+ }
+ foreach (string q in queries)
+ {
+ IList<R2RMethod> res = FindMethod(r2r, q, exact);
+
+ _writer.WriteLine(res.Count + " result(s) for \"" + q + "\"");
+ _writer.WriteLine();
+ foreach (R2RMethod method in res)
{
- throw new System.ArgumentException("File name must be passed as argument");
+ DumpMethod(r2r, method);
}
+ }
+ }
- R2RReader r2r = new R2RReader(args[0]);
+ // <summary>
+ /// For each query in the list of queries, search for all sections by the name or value of the ReadyToRunSectionType enum
+ /// </summary>
+ /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
+ /// <param name="queries">The names/values to search for</param>
+ private void QuerySection(R2RReader r2r, IReadOnlyList<string> queries)
+ {
+ if (queries.Count > 0)
+ {
+ WriteDivider("R2R Section");
+ }
+ foreach (string q in queries)
+ {
+ IList<R2RSection> res = FindSection(r2r, q);
- if (r2r.IsR2R)
+ _writer.WriteLine(res.Count + " result(s) for \"" + q + "\"");
+ _writer.WriteLine();
+ foreach (R2RSection section in res)
{
- Console.WriteLine($"Filename: {r2r.Filename}");
- Console.WriteLine($"Machine: {r2r.Machine}");
- Console.WriteLine($"ImageBase: 0x{r2r.ImageBase:X8}");
+ DumpSection(r2r, section);
+ }
+ }
+ }
+
+ // <summary>
+ /// For each query in the list of queries, search for a runtime function by id.
+ /// The method containing the runtime function gets outputted, along with the single runtime function that was searched
+ /// </summary>
+ /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
+ /// <param name="queries">The ids to search for</param>
+ private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList<int> queries)
+ {
+ if (queries.Count > 0)
+ {
+ WriteDivider("Runtime Functions");
+ }
+ foreach (int q in queries)
+ {
+ RuntimeFunction rtf = FindRuntimeFunction(r2r, q);
- Console.WriteLine("============== R2R Header ==============");
- Console.WriteLine(r2r.R2RHeader.ToString());
- foreach (KeyValuePair<R2RSection.SectionType, R2RSection> section in r2r.R2RHeader.Sections)
+ if (rtf == null)
+ {
+ WriteWarning("Unable to find by id " + q);
+ continue;
+ }
+ _writer.WriteLine(rtf.Method.SignatureString);
+ DumpRuntimeFunction(r2r, rtf);
+ }
+ }
+
+ /// <summary>
+ /// Outputs specified headers, sections, methods or runtime functions for one ReadyToRun image
+ /// </summary>
+ /// <param name="r2r">The structure containing the info of the ReadyToRun image</param>
+ public void Dump(R2RReader r2r)
+ {
+ _writer.WriteLine($"Filename: {r2r.Filename}");
+ _writer.WriteLine($"Machine: {r2r.Machine}");
+ _writer.WriteLine($"ImageBase: 0x{r2r.ImageBase:X8}");
+ _writer.WriteLine();
+
+ if (_queries.Count == 0 && _keywords.Count == 0 && _runtimeFunctions.Count == 0 && _sections.Count == 0) //dump all sections and methods
+ {
+ WriteDivider("R2R Header");
+ DumpHeader(r2r, true);
+
+ if (!_header)
+ {
+ WriteDivider("R2R Methods");
+ _writer.WriteLine();
+ foreach (R2RMethod method in r2r.R2RMethods)
{
- Console.WriteLine("------------------\n");
- Console.WriteLine(section.Value.ToString());
+ DumpMethod(r2r, method);
}
+ }
+ }
+ else //dump queried sections/methods/runtimeFunctions
+ {
+ if (_header)
+ {
+ DumpHeader(r2r, false);
+ }
- Console.WriteLine("============== Native Code ==============\n");
- foreach (R2RMethod method in r2r.R2RMethods)
+ QuerySection(r2r, _sections);
+ QueryRuntimeFunction(r2r, _runtimeFunctions);
+ QueryMethod(r2r, "R2R Methods by Query", _queries, true);
+ QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false);
+ }
+
+ _writer.WriteLine("========================================================");
+ _writer.WriteLine();
+ }
+
+ /// <summary>
+ /// Returns true if the name/signature/id of <param>method</param> matches <param>query</param>
+ /// </summary>
+ /// <param name="exact">Specifies exact or partial match</param>
+ /// <remarks>Case-insensitive and ignores whitespace</remarks>
+ private bool Match(R2RMethod method, string query, bool exact)
+ {
+ int id;
+ bool isNum = ArgStringToInt(query, out id);
+ bool idMatch = isNum && (method.Rid == id || method.Token == id);
+
+ bool sigMatch = false;
+ if (exact)
+ {
+ sigMatch = method.Name.Equals(query, StringComparison.OrdinalIgnoreCase);
+ if (!sigMatch)
+ {
+ string sig = method.SignatureString.Replace(" ", "");
+ string q = query.Replace(" ", "");
+ int iMatch = sig.IndexOf(q, StringComparison.OrdinalIgnoreCase);
+ sigMatch = (iMatch == 0 || (iMatch > 0 && iMatch == (sig.Length - q.Length) && sig[iMatch - 1] == '.'));
+ }
+ }
+ else
+ {
+ string sig = method.Signature.ReturnType + method.SignatureString.Replace(" ", "");
+ sigMatch = (sig.IndexOf(query.Replace(" ", ""), StringComparison.OrdinalIgnoreCase) >= 0);
+ }
+
+ return idMatch || sigMatch;
+ }
+
+ /// <summary>
+ /// Returns true if the name or value of the ReadyToRunSectionType of <param>section</param> matches <param>query</param>
+ /// </summary>
+ /// <remarks>Case-insensitive</remarks>
+ private bool Match(R2RSection section, string query)
+ {
+ int queryInt;
+ bool isNum = ArgStringToInt(query, out queryInt);
+ string typeName = Enum.GetName(typeof(R2RSection.SectionType), section.Type);
+
+ return (isNum && (int)section.Type == queryInt) || typeName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
+ }
+
+ /// <summary>
+ /// Finds all R2RMethods by name/signature/id matching <param>query</param>
+ /// </summary>
+ /// <param name="r2r">Contains all extracted info about the ReadyToRun image</param>
+ /// <param name="query">The name/signature/id to search for</param>
+ /// <param name="exact">Specifies exact or partial match</param>
+ /// <out name="res">List of all matching methods</out>
+ /// <remarks>Case-insensitive and ignores whitespace</remarks>
+ public IList<R2RMethod> FindMethod(R2RReader r2r, string query, bool exact)
+ {
+ List<R2RMethod> res = new List<R2RMethod>();
+ foreach (R2RMethod method in r2r.R2RMethods)
+ {
+ if (Match(method, query, exact))
+ {
+ res.Add(method);
+ }
+ }
+ return res;
+ }
+
+ /// <summary>
+ /// Finds all R2RSections by name or value of the ReadyToRunSectionType matching <param>query</param>
+ /// </summary>
+ /// <param name="r2r">Contains all extracted info about the ReadyToRun image</param>
+ /// <param name="query">The name or value to search for</param>
+ /// <out name="res">List of all matching sections</out>
+ /// <remarks>Case-insensitive</remarks>
+ public IList<R2RSection> FindSection(R2RReader r2r, string query)
+ {
+ List<R2RSection> res = new List<R2RSection>();
+ foreach (R2RSection section in r2r.R2RHeader.Sections.Values)
+ {
+ if (Match(section, query))
+ {
+ res.Add(section);
+ }
+ }
+ return res;
+ }
+
+ /// <summary>
+ /// Returns the runtime function with id matching <param>rtfQuery</param>
+ /// </summary>
+ /// <param name="r2r">Contains all extracted info about the ReadyToRun image</param>
+ /// <param name="rtfQuery">The name or value to search for</param>
+ public RuntimeFunction FindRuntimeFunction(R2RReader r2r, int rtfQuery)
+ {
+ foreach (R2RMethod m in r2r.R2RMethods)
+ {
+ foreach (RuntimeFunction rtf in m.RuntimeFunctions)
+ {
+ if (rtf.Id == rtfQuery || (rtf.StartAddress >= rtfQuery && rtf.StartAddress + rtf.Size < rtfQuery))
{
- Console.Write(method.ToString());
- Console.WriteLine("------------------\n");
+ return rtf;
}
}
}
+ return null;
+ }
+
+ private int Run(string[] args)
+ {
+ ArgumentSyntax syntax = ParseCommandLine(args);
+
+ if (_help)
+ {
+ _writer.WriteLine(syntax.GetHelpText());
+ return 0;
+ }
+
+ if (_inputFilenames.Count == 0)
+ throw new ArgumentException("Input filename must be specified (--in <file>)");
+
+ // open output stream
+ if (_outputFilename != null)
+ {
+ _writer = File.CreateText(_outputFilename);
+ }
+ else
+ {
+ _writer = Console.Out;
+ }
+
+ try
+ {
+ foreach (string filename in _inputFilenames)
+ {
+ R2RReader r2r = new R2RReader(filename);
+ Dump(r2r);
+ }
+ }
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
return 1;
}
+ finally
+ {
+ // close output stream
+ _writer.Close();
+ }
+
return 0;
}
+
+ public static int Main(string[] args)
+ {
+ try
+ {
+ return new R2RDump().Run(args);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Error: " + e.ToString());
+ return 1;
+ }
+ }
}
}