--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.CommandLine;
+using System.CommandLine.Builder;
+using System.CommandLine.Invocation;
+using System.IO;
+
+namespace R2RDump
+{
+ internal static class CommandLineOptions
+ {
+ public static RootCommand RootCommand()
+ {
+ RootCommand command = new RootCommand();
+ command.AddOption(new Option(new[] { "--in", "-i" }, "Input file(s) to dump. Expects them to by ReadyToRun images", new Argument<FileInfo[]>()));
+ command.AddOption(new Option(new[] { "--out", "-o" }, "Output file path. Dumps everything to the specified file except for help message and exception messages", new Argument<FileInfo>()));
+ command.AddOption(new Option(new[] { "--xml", "-x" }, "Output in XML format", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--raw" }, "Dump the raw bytes of each section or runtime function", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--header" }, "Dump R2R header", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--disasm", "-d" }, "Show disassembly of methods or runtime functions", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--naked" }, "Naked dump suppresses most compilation details like placement addresses", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--query", "-q" }, "Query method by exact name, signature, row ID or token", new Argument<string[]>()));
+ command.AddOption(new Option(new[] { "--keyword", "-k" }, "Search method by keyword", new Argument<string[]>()));
+ command.AddOption(new Option(new[] { "--runtimefunction", "-f" }, "Get one runtime function by id or relative virtual address", new Argument<string[]>()));
+ command.AddOption(new Option(new[] { "--section", "-s" }, "Get section by keyword", new Argument<string[]>()));
+ command.AddOption(new Option(new[] { "--unwind" }, "Dump unwindInfo", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--gc" }, "Dump gcInfo and slot table", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--sectionContents", "--sc" }, "Dump section contents", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--verbose", "-v" }, "Dump disassembly, unwindInfo, gcInfo and sectionContents", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--diff" }, "Compare two R2R images", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--ignoreSensitive" }, "Ignore sensitive properties in xml dump to avoid failing tests", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--reference", "-r" }, "Explicit reference assembly files", new Argument<FileInfo[]>()));
+ command.AddOption(new Option(new[] { "--referencePath", "--rp" }, "Search paths for reference assemblies", new Argument<DirectoryInfo[]>()));
+ command.AddOption(new Option(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation", new Argument<bool>()));
+ command.AddOption(new Option(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation", new Argument<bool>()));
+ return command;
+ }
+ }
+}
using System;
using System.Collections.Generic;
using System.CommandLine;
+using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
-using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
+using System.Threading.Tasks;
using System.Xml;
-using System.Xml.Serialization;
namespace R2RDump
{
public class DumpOptions
{
- public bool Raw;
- public bool Normalize;
- public bool Naked;
- public bool Header;
- public bool Disasm;
- public bool Unwind;
- public bool GC;
- public bool SectionContents;
- public bool EntryPoints;
- public bool SignatureBinary;
- public bool InlineSignatureBinary;
-
- public IReadOnlyList<string> ReferenceAssemblies = Array.Empty<string>();
- public IReadOnlyList<string> ReferencePaths = Array.Empty<string>();
+ public FileInfo[] In { get; set; }
+ public FileInfo Out { get; set; }
+
+ public bool Xml { get; set; }
+ public bool Raw { get; set; }
+ public bool Header { get; set; }
+ public bool Disasm { get; set; }
+ public bool Naked { get; set; }
+
+ public string[] Query { get; set; }
+ public string[] Keyword { get; set; }
+ public string[] RuntimeFunction { get; set; }
+ public string[] Section { get; set; }
+
+ public bool Unwind { get; set; }
+ public bool GC { get; set; }
+ public bool SectionContents { get; set; }
+ public bool EntryPoints { get; set; }
+ public bool Normalize { get; set; }
+ public bool Verbose { get; set; }
+ public bool Diff { get; set; }
+ public bool IgnoreSensitive { get; set; }
+
+ public FileInfo[] Reference { get; set; }
+ public DirectoryInfo[] ReferencePath { get; set; }
+
+ public bool SignatureBinary { get; set; }
+ public bool InlineSignatureBinary { get; set; }
+
public Dictionary<string, EcmaMetadataReader> AssemblyCache = new Dictionary<string, EcmaMetadataReader>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// <returns></returns>
public string FindAssembly(string simpleName, string parentFile)
{
- foreach (string refAsm in ReferenceAssemblies)
+ foreach (FileInfo refAsm in Reference ?? Enumerable.Empty<FileInfo>())
{
- if (Path.GetFileNameWithoutExtension(refAsm).Equals(simpleName, StringComparison.OrdinalIgnoreCase))
+ if (Path.GetFileNameWithoutExtension(refAsm.FullName).Equals(simpleName, StringComparison.OrdinalIgnoreCase))
{
- return refAsm;
+ return refAsm.FullName;
}
}
- IEnumerable<string> allRefPaths = new string[] { Path.GetDirectoryName(parentFile) }.Concat(ReferencePaths);
+ IEnumerable<string> allRefPaths = new string[] { Path.GetDirectoryName(parentFile) }
+ .Concat((ReferencePath ?? Enumerable.Empty<DirectoryInfo>()).Select(path => path.FullName));
foreach (string refPath in allRefPaths)
{
class R2RDump
{
- // Options set by user specifying what to dump
- private bool _help;
- private IReadOnlyList<string> _inputFilenames = Array.Empty<string>();
- private string _outputFilename = null;
- 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 _xml;
- private TextWriter _writer;
- private Dictionary<R2RSection.SectionType, bool> _selectedSections = new Dictionary<R2RSection.SectionType, bool>();
+ private readonly DumpOptions _options;
+ private readonly TextWriter _writer;
+ private readonly Dictionary<R2RSection.SectionType, bool> _selectedSections = new Dictionary<R2RSection.SectionType, bool>();
private Dumper _dumper;
- private bool _ignoreSensitive;
- private R2RDump()
+ private R2RDump(DumpOptions options)
{
- }
+ _options = options;
- /// <summary>
- /// Parse commandline options
- /// </summary>
- private ArgumentSyntax ParseCommandLine(string[] args)
- {
- bool verbose = false;
- 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("x|xml", ref _xml, "Output in XML format");
- 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.DefineOption("naked", ref _options.Naked, "Naked dump suppresses most compilation details like placement addresses");
- 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("f|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 _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("e|entrypoints", ref _options.EntryPoints, "Dump list of method / instance entrypoints in the R2R file");
- 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");
- syntax.DefineOptionList("r|reference", ref _options.ReferenceAssemblies, "Explicit reference assembly files");
- syntax.DefineOptionList("rp|referencepath", ref _options.ReferencePaths, "Search paths for reference assemblies");
- syntax.DefineOption("isb|inlineSignatureBinary", ref _options.InlineSignatureBinary, "Embed binary signature into its textual description");
- syntax.DefineOption("sb|signatureBinary", ref _options.SignatureBinary, "Append signature binary to its textual description");
- });
-
- if (verbose)
+ if (_options.Verbose)
{
_options.Disasm = true;
_options.Unwind = true;
_options.GC = true;
_options.SectionContents = true;
- _options.EntryPoints = true;
}
- return argSyntax;
+ // open output stream
+ if (_options.Out != null)
+ {
+ _writer = new StreamWriter(_options.Out.FullName, append: false, encoding: Encoding.ASCII);
+ }
+ else
+ {
+ _writer = Console.Out;
+ }
}
- private int ArgStringToInt(string arg)
+ private static int ArgStringToInt(string arg)
{
int n;
if (!ArgStringToInt(arg, out n))
}
/// <summary>
- /// Converts string passed as cmd line args into int, works for hexidecimal with 0x as prefix
+ /// Converts string passed as cmd line args into int, works for hexadecimal 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)
+ private static bool ArgStringToInt(string arg, out int n)
{
- arg = arg.Trim();
+ arg = arg.Trim();
if (arg.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
return int.TryParse(arg.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out n);
/// </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)
+ private void QueryRuntimeFunction(R2RReader r2r, IEnumerable<string> queries)
{
- if (queries.Count > 0)
+ if (queries.Any())
{
_dumper.WriteDivider("Runtime Functions");
}
- foreach (int q in queries)
+ foreach (string q in queries)
{
- RuntimeFunction rtf = FindRuntimeFunction(r2r, q);
+ RuntimeFunction rtf = FindRuntimeFunction(r2r, ArgStringToInt(q));
if (rtf == null)
{
{
_dumper.Begin();
- if (_queries.Count == 0 && _keywords.Count == 0 && _runtimeFunctions.Count == 0 && _sections.Count == 0) //dump all sections and methods if no queries specified
+ if (_options.Header || !_options.EntryPoints)
{
- if (_options.Header || !_options.EntryPoints)
- {
- _dumper.WriteDivider("R2R Header");
- _dumper.DumpHeader(true);
- }
-
+ _dumper.WriteDivider("R2R Header");
+ _dumper.DumpHeader(true);
+ }
+
+ bool haveQuery = false;
+ if (_options.Section != null)
+ {
+ haveQuery = true;
+ QuerySection(r2r, _options.Section);
+ }
+
+ if (_options.RuntimeFunction != null)
+ {
+ haveQuery = true;
+ QueryRuntimeFunction(r2r, _options.RuntimeFunction);
+ }
+
+ if (_options.Query != null)
+ {
+ haveQuery = true;
+ QueryMethod(r2r, "R2R Methods by Query", _options.Query, true);
+ }
+
+ if (_options.Keyword != null)
+ {
+ haveQuery = true;
+ QueryMethod(r2r, "R2R Methods by Keyword", _options.Keyword, false);
+ }
+
+ if (!haveQuery)
+ {
+ // Dump all sections and methods if no queries specified
if (_options.EntryPoints)
{
_dumper.DumpEntryPoints();
_dumper.DumpAllMethods();
}
}
- else //dump queried sections, methods and runtimeFunctions
- {
- if (_options.Header)
- {
- _dumper.DumpHeader(false);
- }
-
- QuerySection(r2r, _sections);
- QueryRuntimeFunction(r2r, _runtimeFunctions);
- QueryMethod(r2r, "R2R Methods by Query", _queries, true);
- QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false);
- }
_dumper.End();
}
return null;
}
- private int Run(string[] args)
+ private int Run()
{
- ArgumentSyntax syntax = ParseCommandLine(args);
-
- // open output stream
- if (_outputFilename != null)
- {
- _writer = new StreamWriter(_outputFilename, append: false, encoding: Encoding.ASCII);
- }
- else
- {
- _writer = Console.Out;
- }
-
- if (_help)
- {
- _writer.WriteLine(syntax.GetHelpText());
- return 0;
- }
-
Disassembler disassembler = null;
try
{
- if (_inputFilenames.Count == 0)
+ if (_options.In == null || _options.In.Length == 0)
throw new ArgumentException("Input filename must be specified (--in <file>)");
- if (_diff && _inputFilenames.Count < 2)
+ if (_options.Diff && _options.In.Length < 2)
throw new ArgumentException("Need at least 2 input files in diff mode");
if (_options.Naked && _options.Raw)
R2RReader previousReader = null;
- foreach (string filename in _inputFilenames)
+ foreach (FileInfo filename in _options.In)
{
// parse the ReadyToRun image
- R2RReader r2r = new R2RReader(_options, filename);
+ R2RReader r2r = new R2RReader(_options, filename.FullName);
if (_options.Disasm)
{
}
}
- if (_xml)
+ if (_options.Xml)
{
- _dumper = new XmlDumper(_ignoreSensitive, r2r, _writer, disassembler, _options);
+ _dumper = new XmlDumper(_options.IgnoreSensitive, r2r, _writer, disassembler, _options);
}
else
{
_dumper = new TextDumper(r2r, _writer, disassembler, _options);
}
- if (!_diff)
+ if (!_options.Diff)
{
// output the ReadyToRun info
Dump(r2r);
if (e is ArgumentException)
{
Console.WriteLine();
- Console.WriteLine(syntax.GetHelpText());
}
- if (_xml)
+ if (_options.Xml)
{
XmlDocument document = new XmlDocument();
XmlNode node = document.CreateNode("element", "Error", "");
return 0;
}
- public static int Main(string[] args)
+ public static async Task<int> Main(string[] args)
{
- try
- {
- return new R2RDump().Run(args);
- }
- catch (Exception e)
- {
- Console.WriteLine("Error: " + e.ToString());
- return 1;
- }
+ var command = CommandLineOptions.RootCommand();
+ command.Handler = CommandHandler.Create<DumpOptions>((DumpOptions options) => new R2RDump(options).Run());
+ return await command.InvokeAsync(args);
}
}
}