diff --git a/src/tools/r2rdump/Amd64/UnwindInfo.cs b/src/tools/r2rdump/Amd64/UnwindInfo.cs index 1adca9a8c6ca..00c55bb79496 100644 --- a/src/tools/r2rdump/Amd64/UnwindInfo.cs +++ b/src/tools/r2rdump/Amd64/UnwindInfo.cs @@ -22,16 +22,24 @@ public enum UnwindOpCodes UWOP_SET_FPREG_LARGE, } - struct UnwindCode + public enum UnwindFlags { - public byte CodeOffset { get; } - public UnwindOpCodes UnwindOp { get; } //4 bits - public byte OpInfo { get; } //4 bits + UNW_FLAG_NHANDLER = 0x0, + UNW_FLAG_EHANDLER = 0x1, + UNW_FLAG_UHANDLER = 0x2, + UNW_FLAG_CHAININFO = 0x4, + } + + public struct UnwindCode + { + public byte CodeOffset { get; set; } + public UnwindOpCodes UnwindOp { get; set; } //4 bits + public byte OpInfo { get; set; } //4 bits - public byte OffsetLow { get; } - public byte OffsetHigh { get; } //4 bits + public byte OffsetLow { get; set; } + public byte OffsetHigh { get; set; } //4 bits - public uint FrameOffset { get; } + public uint FrameOffset { get; set; } public UnwindCode(byte[] image, ref int offset) { @@ -48,20 +56,20 @@ public UnwindCode(byte[] image, ref int offset) } } - struct UnwindInfo : BaseUnwindInfo + public struct UnwindInfo : BaseUnwindInfo { private const int _sizeofUnwindCode = 2; private const int _offsetofUnwindCode = 4; - public byte Version { get; } //3 bits - public byte Flags { get; } //5 bits - public byte SizeOfProlog { get; } - public byte CountOfUnwindCodes { get; } - public Amd64Registers FrameRegister { get; } //4 bits - public byte FrameOffset { get; } //4 bits - public UnwindCode[] UnwindCode { get; } - public uint PersonalityRoutineRVA { get; } - public int Size { get; } + public byte Version { get; set; } //3 bits + public byte Flags { get; set; } //5 bits + public byte SizeOfProlog { get; set; } + public byte CountOfUnwindCodes { get; set; } + public Amd64Registers FrameRegister { get; set; } //4 bits + public byte FrameOffset { get; set; } //4 bits + public UnwindCode[] UnwindCode { get; set; } + public uint PersonalityRoutineRVA { get; set; } + public int Size { get; set; } public UnwindInfo(byte[] image, int offset) { @@ -92,7 +100,22 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.AppendLine($"\tVersion: {Version}"); - sb.AppendLine($"\tFlags: 0x{Flags:X8}"); + sb.Append($"\tFlags: 0x{Flags:X8} ("); + if (Flags == (byte)UnwindFlags.UNW_FLAG_NHANDLER) + { + sb.Append(" UNW_FLAG_NHANDLER"); + } + else + { + if ((Flags & (byte)UnwindFlags.UNW_FLAG_EHANDLER) != 0) + sb.Append(" UNW_FLAG_EHANDLER"); + if ((Flags & (byte)UnwindFlags.UNW_FLAG_UHANDLER) != 0) + sb.Append(" UNW_FLAG_UHANDLER"); + if ((Flags & (byte)UnwindFlags.UNW_FLAG_CHAININFO) != 0) + sb.Append(" UNW_FLAG_CHAININFO"); + } + sb.AppendLine(" )"); + sb.AppendLine($"\tSizeOfProlog: {SizeOfProlog}"); sb.AppendLine($"\tCountOfUnwindCodes: {CountOfUnwindCodes}"); sb.AppendLine($"\tFrameRegister: {FrameRegister}"); @@ -113,24 +136,23 @@ public override string ToString() private string GetUnwindCode(ref int i) { StringBuilder sb = new StringBuilder(); - string tab2 = new string(' ', 8); - sb.AppendLine($"{tab2}CodeOffset: 0x{UnwindCode[i].CodeOffset:X2}"); - sb.AppendLine($"{tab2}UnwindOp: {UnwindCode[i].UnwindOp}({(byte)UnwindCode[i].UnwindOp})"); + sb.AppendLine($"\t\tCodeOffset: 0x{UnwindCode[i].CodeOffset:X2}"); + sb.AppendLine($"\t\tUnwindOp: {UnwindCode[i].UnwindOp}({(byte)UnwindCode[i].UnwindOp})"); switch (UnwindCode[i].UnwindOp) { case UnwindOpCodes.UWOP_PUSH_NONVOL: - sb.AppendLine($"{tab2}OpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); break; case UnwindOpCodes.UWOP_ALLOC_LARGE: - sb.Append($"{tab2}OpInfo: {UnwindCode[i].OpInfo} - "); + sb.Append($"\t\tOpInfo: {UnwindCode[i].OpInfo} - "); if (UnwindCode[i].OpInfo == 0) { i++; sb.AppendLine("Scaled small"); uint frameOffset = UnwindCode[i].FrameOffset * 8; - sb.AppendLine($"{tab2}FrameOffset: {UnwindCode[i].FrameOffset} * 8 = {frameOffset} = 0x{frameOffset:X5})"); + sb.AppendLine($"\t\tFrameOffset: {UnwindCode[i].FrameOffset} * 8 = {frameOffset} = 0x{frameOffset:X5})"); } else if (UnwindCode[i].OpInfo == 1) { @@ -139,7 +161,7 @@ private string GetUnwindCode(ref int i) uint offset = UnwindCode[i].FrameOffset; i++; offset = ((UnwindCode[i].FrameOffset << 16) | offset); - sb.AppendLine($"{tab2}FrameOffset: 0x{offset:X8})"); + sb.AppendLine($"\t\tFrameOffset: 0x{offset:X8})"); } else { @@ -148,19 +170,19 @@ private string GetUnwindCode(ref int i) break; case UnwindOpCodes.UWOP_ALLOC_SMALL: int opInfo = UnwindCode[i].OpInfo * 8 + 8; - sb.AppendLine($"{tab2}OpInfo: {UnwindCode[i].OpInfo} * 8 + 8 = {opInfo} = 0x{opInfo:X2}"); + sb.AppendLine($"\t\tOpInfo: {UnwindCode[i].OpInfo} * 8 + 8 = {opInfo} = 0x{opInfo:X2}"); break; case UnwindOpCodes.UWOP_SET_FPREG: - sb.AppendLine($"{tab2}OpInfo: Unused({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: Unused({UnwindCode[i].OpInfo})"); break; case UnwindOpCodes.UWOP_SET_FPREG_LARGE: { - sb.AppendLine($"{tab2}OpInfo: Unused({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: Unused({UnwindCode[i].OpInfo})"); i++; uint offset = UnwindCode[i].FrameOffset; i++; offset = ((UnwindCode[i].FrameOffset << 16) | offset); - sb.AppendLine($"{tab2}Scaled Offset: {offset} * 16 = {offset * 16} = 0x{(offset * 16):X8}"); + sb.AppendLine($"\t\tScaled Offset: {offset} * 16 = {offset * 16} = 0x{(offset * 16):X8}"); if ((UnwindCode[i].FrameOffset & 0xF0000000) != 0) { R2RDump.WriteWarning("Illegal unwindInfo unscaled offset: too large"); @@ -169,51 +191,51 @@ private string GetUnwindCode(ref int i) break; case UnwindOpCodes.UWOP_SAVE_NONVOL: { - sb.AppendLine($"{tab2}OpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); i++; uint offset = UnwindCode[i].FrameOffset * 8; - sb.AppendLine($"{tab2}Scaled Offset: {UnwindCode[i].FrameOffset} * 8 = {offset} = 0x{offset:X5}"); + sb.AppendLine($"\t\tScaled Offset: {UnwindCode[i].FrameOffset} * 8 = {offset} = 0x{offset:X5}"); } break; case UnwindOpCodes.UWOP_SAVE_NONVOL_FAR: { - sb.AppendLine($"{tab2}OpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); i++; uint offset = UnwindCode[i].FrameOffset; i++; offset = ((UnwindCode[i].FrameOffset << 16) | offset); - sb.AppendLine($"{tab2}Unscaled Large Offset: 0x{offset:X8}"); + sb.AppendLine($"\t\tUnscaled Large Offset: 0x{offset:X8}"); } break; case UnwindOpCodes.UWOP_SAVE_XMM128: { - sb.AppendLine($"{tab2}OpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); i++; uint offset = UnwindCode[i].FrameOffset * 16; - sb.AppendLine($"{tab2}Scaled Offset: {UnwindCode[i].FrameOffset} * 16 = {offset} = 0x{offset:X5}"); + sb.AppendLine($"\t\tScaled Offset: {UnwindCode[i].FrameOffset} * 16 = {offset} = 0x{offset:X5}"); } break; case UnwindOpCodes.UWOP_SAVE_XMM128_FAR: { - sb.AppendLine($"{tab2}OpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); + sb.AppendLine($"\t\tOpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})"); i++; uint offset = UnwindCode[i].FrameOffset; i++; offset = ((UnwindCode[i].FrameOffset << 16) | offset); - sb.AppendLine($"{tab2}Unscaled Large Offset: 0x{offset:X8}"); + sb.AppendLine($"\t\tUnscaled Large Offset: 0x{offset:X8}"); } break; case UnwindOpCodes.UWOP_EPILOG: case UnwindOpCodes.UWOP_SPARE_CODE: case UnwindOpCodes.UWOP_PUSH_MACHFRAME: default: - sb.AppendLine($"{tab2}OpInfo: {UnwindCode[i].OpInfo}"); + sb.AppendLine($"\t\tOpInfo: {UnwindCode[i].OpInfo}"); sb.AppendLine(); - sb.AppendLine($"{tab2}OffsetLow: {UnwindCode[i].OffsetLow}"); - sb.AppendLine($"{tab2}OffsetHigh: {UnwindCode[i].OffsetHigh}"); + sb.AppendLine($"\t\tOffsetLow: {UnwindCode[i].OffsetLow}"); + sb.AppendLine($"\t\tOffsetHigh: {UnwindCode[i].OffsetHigh}"); sb.AppendLine(); - sb.AppendLine($"{tab2}FrameOffset: {FrameOffset}"); + sb.AppendLine($"\t\tFrameOffset: {FrameOffset}"); break; } return sb.ToString(); diff --git a/src/tools/r2rdump/CoreDisTools.cs b/src/tools/r2rdump/CoreDisTools.cs index 6070e43fd390..a84046d359de 100644 --- a/src/tools/r2rdump/CoreDisTools.cs +++ b/src/tools/r2rdump/CoreDisTools.cs @@ -42,36 +42,17 @@ public enum TargetArch [DllImport(_dll)] public static extern void FinishDisasm(IntPtr Disasm); - public unsafe static string GetCodeBlock(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image) + public unsafe static int GetInstruction(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, int rtfOffset, byte[] image, out string instr) { - StringBuilder sb = new StringBuilder(); - - int rtfOffset = 0; - int codeOffset = rtf.CodeOffset; - Dictionary transitions = rtf.Method.GcInfo.Transitions; - GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable; - while (rtfOffset < rtf.Size) + int instrSize = 1; + fixed (byte* p = image) { - int instrSize = 1; - fixed (byte* p = image) - { - IntPtr ptr = (IntPtr)(p + imageOffset + rtfOffset); - instrSize = DumpInstruction(Disasm, (ulong)(rtf.StartAddress + rtfOffset), ptr, rtf.Size); - } - IntPtr pBuffer = GetOutputBuffer(); - string instr = Marshal.PtrToStringAnsi(pBuffer); - - sb.Append(instr); - if (transitions.ContainsKey(codeOffset)) - { - sb.AppendLine($"\t\t\t\t{transitions[codeOffset].GetSlotState(slotTable)}"); - } - - ClearOutputBuffer(); - rtfOffset += instrSize; - codeOffset += instrSize; + IntPtr ptr = (IntPtr)(p + imageOffset + rtfOffset); + instrSize = DumpInstruction(Disasm, (ulong)(rtf.StartAddress + rtfOffset), ptr, rtf.Size); } - return sb.ToString(); + IntPtr pBuffer = GetOutputBuffer(); + instr = Marshal.PtrToStringAnsi(pBuffer); + return instrSize; } public static IntPtr GetDisasm(Machine machine) diff --git a/src/tools/r2rdump/GCInfo.cs b/src/tools/r2rdump/GCInfo.cs index cf57db62ac97..c423eabf2af2 100644 --- a/src/tools/r2rdump/GCInfo.cs +++ b/src/tools/r2rdump/GCInfo.cs @@ -6,10 +6,11 @@ using System.Collections.Generic; using System.Reflection.PortableExecutable; using System.Text; +using System.Xml.Serialization; namespace R2RDump { - class GcInfo + public class GcInfo { private enum GcInfoHeaderFlags { @@ -33,8 +34,8 @@ private enum GcInfoHeaderFlags public struct InterruptibleRange { - public uint StartOffset { get; } - public uint StopOffset { get; } + public uint StartOffset { get; set; } + public uint StopOffset { get; set; } public InterruptibleRange(uint start, uint stop) { StartOffset = start; @@ -45,9 +46,11 @@ public InterruptibleRange(uint start, uint stop) public class GcTransition { public int CodeOffset { get; set; } - public int SlotId { get; } - public bool IsLive { get; } - public int ChunkId { get; } + public int SlotId { get; set; } + public bool IsLive { get; set; } + public int ChunkId { get; set; } + + public GcTransition() { } public GcTransition(int codeOffset, int slotId, bool isLive, int chunkId) { @@ -104,27 +107,31 @@ public string GetSlotState(GcSlotTable slotTable) private Machine _machine; private GcInfoTypes _gcInfoTypes; - public int Version { get; } - public int CodeLength { get; } - public ReturnKinds ReturnKind { get; } - public uint ValidRangeStart { get; } - public uint ValidRangeEnd { get; } - public int SecurityObjectStackSlot { get; } - public int GSCookieStackSlot { get; } - public int PSPSymStackSlot { get; } - public int GenericsInstContextStackSlot { get; } - public uint StackBaseRegister { get; } - public uint SizeOfEditAndContinuePreservedArea { get; } - public int ReversePInvokeFrameStackSlot { get; } - public uint SizeOfStackOutgoingAndScratchArea { get; } - public uint NumSafePoints { get; } - public uint NumInterruptibleRanges { get; } - public IEnumerable SafePointOffsets { get; } - public IEnumerable InterruptibleRanges { get; } - public GcSlotTable SlotTable { get; } - public int Size { get; } - public int Offset { get; } - public Dictionary Transitions { get; } + public int Version { get; set; } + public int CodeLength { get; set; } + public ReturnKinds ReturnKind { get; set; } + public uint ValidRangeStart { get; set; } + public uint ValidRangeEnd { get; set; } + public int SecurityObjectStackSlot { get; set; } + public int GSCookieStackSlot { get; set; } + public int PSPSymStackSlot { get; set; } + public int GenericsInstContextStackSlot { get; set; } + public uint StackBaseRegister { get; set; } + public uint SizeOfEditAndContinuePreservedArea { get; set; } + public int ReversePInvokeFrameStackSlot { get; set; } + public uint SizeOfStackOutgoingAndScratchArea { get; set; } + public uint NumSafePoints { get; set; } + public uint NumInterruptibleRanges { get; set; } + public List SafePointOffsets { get; set; } + public List InterruptibleRanges { get; set; } + public GcSlotTable SlotTable { get; set; } + public int Size { get; set; } + public int Offset { get; set; } + + [XmlIgnore] + public Dictionary Transitions { get; set; } + + public GcInfo() { } public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion) { @@ -335,7 +342,7 @@ private void ParseHeaderFlags(byte[] image, ref int bitOffset) _wantsReportOnlyLeaf = ((headerFlags & GcInfoHeaderFlags.GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0); } - private IEnumerable EnumerateSafePoints(byte[] image, ref int bitOffset) + private List EnumerateSafePoints(byte[] image, ref int bitOffset) { List safePoints = new List(); uint numBitsPerOffset = GcInfoTypes.CeilOfLog2(CodeLength); @@ -347,7 +354,7 @@ private IEnumerable EnumerateSafePoints(byte[] image, ref int bitOffset) return safePoints; } - private IEnumerable EnumerateInterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset) + private List EnumerateInterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset) { List ranges = new List(); uint lastinterruptibleRangeStopOffset = 0; diff --git a/src/tools/r2rdump/GCInfoTypes.cs b/src/tools/r2rdump/GCInfoTypes.cs index 9b44a595a8ee..a285ebba5a19 100644 --- a/src/tools/r2rdump/GCInfoTypes.cs +++ b/src/tools/r2rdump/GCInfoTypes.cs @@ -8,7 +8,7 @@ namespace R2RDump { - class GcInfoTypes + public class GcInfoTypes { private Machine _target; @@ -196,8 +196,11 @@ public enum GcStackSlotBase public class GcStackSlot { - public int SpOffset { get; } - public GcStackSlotBase Base { get; } + public int SpOffset { get; set; } + public GcStackSlotBase Base { get; set; } + + public GcStackSlot() { } + public GcStackSlot(int spOffset, GcStackSlotBase stackSlotBase) { SpOffset = spOffset; diff --git a/src/tools/r2rdump/GCSlotTable.cs b/src/tools/r2rdump/GCSlotTable.cs index f2ebf414c688..d788f4dce288 100644 --- a/src/tools/r2rdump/GCSlotTable.cs +++ b/src/tools/r2rdump/GCSlotTable.cs @@ -9,13 +9,13 @@ namespace R2RDump { - class GcSlotTable + public class GcSlotTable { public struct GcSlot { - public int RegisterNumber { get; } - public GcStackSlot StackSlot { get; } - public GcSlotFlags Flags { get; } + public int RegisterNumber { get; set; } + public GcStackSlot StackSlot { get; set; } + public GcSlotFlags Flags { get; set; } public GcSlot(int registerNumber, GcStackSlot stack, GcSlotFlags flags, bool isUntracked = false) { @@ -50,11 +50,13 @@ public override string ToString() } } - public uint NumRegisters { get; } - public uint NumStackSlots { get; } - public uint NumUntracked { get; } - public uint NumSlots { get; } - public List GcSlots { get; } + public uint NumRegisters { get; set; } + public uint NumStackSlots { get; set; } + public uint NumUntracked { get; set; } + public uint NumSlots { get; set; } + public List GcSlots { get; set; } + + public GcSlotTable() { } public GcSlotTable(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, ref int bitOffset) { diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs index b909c1389a89..659bc31511c3 100644 --- a/src/tools/r2rdump/R2RDump.cs +++ b/src/tools/r2rdump/R2RDump.cs @@ -6,14 +6,46 @@ using System.Collections.Generic; using System.CommandLine; using System.IO; +using System.Text; +using System.Xml; +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 unsafe void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode = null); + abstract internal void DumpBytes(int rva, uint size, XmlNode parentNode = null, string name = "Raw", 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 bool _raw; private bool _header; private bool _disasm; @@ -28,6 +60,7 @@ class R2RDump private bool _sectionContents; private TextWriter _writer; private Dictionary _selectedSections = new Dictionary(); + private Dumper _dumper; private R2RDump() { @@ -45,6 +78,7 @@ private ArgumentSyntax ParseCommandLine(string[] args) 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 _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"); @@ -101,221 +135,6 @@ public static void WriteWarning(string warning) Console.WriteLine($"Warning: {warning}"); } - private 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))); - _writer.WriteLine(); - } - - private void WriteSubDivider() - { - _writer.WriteLine("_______________________________________________"); - _writer.WriteLine(); - } - - /// - /// Dumps the R2RHeader and all the sections in the header - /// - private void DumpHeader(R2RReader r2r, bool dumpSections) - { - _writer.WriteLine(r2r.R2RHeader.ToString()); - if (_raw) - { - DumpBytes(r2r, r2r.R2RHeader.RelativeVirtualAddress, (uint)r2r.R2RHeader.Size); - } - _writer.WriteLine(); - 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); - } - } - _writer.WriteLine(); - } - - /// - /// Dumps one R2RSection - /// - private void DumpSection(R2RReader r2r, R2RSection section) - { - WriteSubDivider(); - _writer.WriteLine(section.ToString()); - if (_raw) - { - DumpBytes(r2r, section.RelativeVirtualAddress, (uint)section.Size); - _writer.WriteLine(); - } - if (_sectionContents) - { - DumpSectionContents(r2r, section); - _writer.WriteLine(); - } - } - - /// - /// Dumps one R2RMethod. - /// - private void DumpMethod(R2RReader r2r, R2RMethod method) - { - WriteSubDivider(); - _writer.WriteLine(method.ToString()); - if (_gc) - { - _writer.WriteLine("GcInfo:"); - _writer.Write(method.GcInfo); - if (_raw) - { - DumpBytes(r2r, method.GcInfo.Offset, (uint)method.GcInfo.Size, false); - } - } - _writer.WriteLine(); - - foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions) - { - DumpRuntimeFunction(r2r, runtimeFunction); - } - } - - /// - /// Dumps one runtime function. - /// - private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf) - { - _writer.Write($"{rtf}"); - if (_disasm) - { - _writer.Write(CoreDisTools.GetCodeBlock(_disassembler, rtf, r2r.GetOffset(rtf.StartAddress), r2r.Image)); - } - - if (_raw) - { - _writer.WriteLine("Raw Bytes:"); - DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size); - } - if (_unwind) - { - _writer.WriteLine("UnwindInfo:"); - _writer.Write(rtf.UnwindInfo); - if (_raw) - { - DumpBytes(r2r, rtf.UnwindRVA, (uint)((Amd64.UnwindInfo)rtf.UnwindInfo).Size); - } - } - _writer.WriteLine(); - } - - /// - /// 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, 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) - { - _writer.WriteLine(); - _writer.Write(" "); - } - } - _writer.WriteLine(); - } - - private void DumpSectionContents(R2RReader r2r, R2RSection section) - { - 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(r2r, importSection.SectionRVA, (uint)importSection.SectionSize); - } - if (importSection.SignatureRVA != 0) - { - _writer.WriteLine("Signature Bytes:"); - DumpBytes(r2r, importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int)); - } - if (importSection.AuxiliaryDataRVA != 0) - { - _writer.WriteLine("AuxiliaryData Bytes:"); - DumpBytes(r2r, importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size); - } - } - foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries) - { - _writer.WriteLine(); - _writer.WriteLine(entry.ToString()); - } - _writer.WriteLine(); - } - break; - } - } - // /// For each query in the list of queries, search for all methods matching the query by name, signature or id /// @@ -327,17 +146,15 @@ private void QueryMethod(R2RReader r2r, string title, IReadOnlyList quer { if (queries.Count > 0) { - WriteDivider(title); + _dumper.WriteDivider(title); } foreach (string q in queries) { IList res = FindMethod(r2r, q, exact); - - _writer.WriteLine(res.Count + " result(s) for \"" + q + "\""); - _writer.WriteLine(); + XmlNode queryNode = _dumper.DumpQueryCount(q, "Methods", res.Count); foreach (R2RMethod method in res) { - DumpMethod(r2r, method); + _dumper.DumpMethod(method, queryNode); } } } @@ -351,17 +168,15 @@ 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); - - _writer.WriteLine(res.Count + " result(s) for \"" + q + "\""); - _writer.WriteLine(); + XmlNode queryNode = _dumper.DumpQueryCount(q, "Sections", res.Count); foreach (R2RSection section in res) { - DumpSection(r2r, section); + _dumper.DumpSection(section, queryNode); } } } @@ -376,7 +191,7 @@ private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList queries) { if (queries.Count > 0) { - WriteDivider("Runtime Functions"); + _dumper.WriteDivider("Runtime Functions"); } foreach (int q in queries) { @@ -387,8 +202,8 @@ private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList queries) WriteWarning("Unable to find by id " + q); continue; } - _writer.WriteLine(rtf.Method.SignatureString); - DumpRuntimeFunction(r2r, rtf); + XmlNode queryNode = _dumper.DumpQueryCount(q.ToString(), "Runtime Function", 1); + _dumper.DumpRuntimeFunction(rtf, queryNode); } } @@ -398,32 +213,24 @@ private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList queries) /// The structure containing the info of the ReadyToRun image public void Dump(R2RReader r2r) { - _writer.WriteLine($"Filename: {r2r.Filename}"); - _writer.WriteLine($"Machine: {r2r.Machine}"); - _writer.WriteLine($"ImageBase: 0x{r2r.ImageBase:X8}"); - _writer.WriteLine(); + + _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); + _dumper.WriteDivider("R2R Header"); + _dumper.DumpHeader(true); if (!_header) { - WriteDivider("R2R Methods"); - _writer.WriteLine($"{r2r.R2RMethods.Count} methods"); - _writer.WriteLine(); - foreach (R2RMethod method in r2r.R2RMethods) - { - DumpMethod(r2r, method); - } + _dumper.DumpAllMethods(); } } else //dump queried sections/methods/runtimeFunctions { if (_header) { - DumpHeader(r2r, false); + _dumper.DumpHeader(false); } QuerySection(r2r, _sections); @@ -432,8 +239,7 @@ public void Dump(R2RReader r2r) QueryMethod(r2r, "R2R Methods by Keyword", _keywords, false); } - _writer.WriteLine("============================================================="); - _writer.WriteLine(); + _dumper.End(); } /// @@ -545,15 +351,6 @@ public RuntimeFunction FindRuntimeFunction(R2RReader r2r, int rtfQuery) 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 )"); // open output stream if (_outputFilename != null) @@ -565,16 +362,35 @@ private int Run(string[] args) _writer = Console.Out; } + if (_help) + { + _writer.WriteLine(syntax.GetHelpText()); + return 0; + } + + if (_inputFilenames.Count == 0) + throw new ArgumentException("Input filename must be specified (--in )"); + try { foreach (string filename in _inputFilenames) { R2RReader r2r = new R2RReader(filename); + 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) diff --git a/src/tools/r2rdump/R2RHeader.cs b/src/tools/r2rdump/R2RHeader.cs index 96eacd583a92..7212d3eff70b 100644 --- a/src/tools/r2rdump/R2RHeader.cs +++ b/src/tools/r2rdump/R2RHeader.cs @@ -8,7 +8,7 @@ namespace R2RDump { - class R2RHeader + public class R2RHeader { [Flags] public enum ReadyToRunFlag @@ -26,35 +26,37 @@ public enum ReadyToRunFlag /// /// RVA to the begining of the ReadyToRun header /// - public int RelativeVirtualAddress { get; } + public int RelativeVirtualAddress { get; set; } /// /// Size of the ReadyToRun header /// - public int Size { get; } + public int Size { get; set; } /// /// Signature of the header in string and hex formats /// - public string SignatureString { get; } - public uint Signature { get; } + public string SignatureString { get; set; } + public uint Signature { get; set; } /// /// The ReadyToRun version /// - public ushort MajorVersion { get; } - public ushort MinorVersion { get; } + public ushort MajorVersion { get; set; } + public ushort MinorVersion { get; set; } /// /// Flags in the header /// eg. PLATFORM_NEUTRAL_SOURCE, SKIP_TYPE_VALIDATION /// - public uint Flags { get; } + public uint Flags { get; set; } /// /// The ReadyToRun section RVAs and sizes /// - public Dictionary Sections { get; } + public IDictionary Sections { get; } + + public R2RHeader() { } /// /// Initializes the fields of the R2RHeader @@ -68,9 +70,9 @@ public R2RHeader(byte[] image, int rva, int curOffset) RelativeVirtualAddress = rva; int startOffset = curOffset; - byte[] signature = new byte[sizeof(uint)]; - Array.Copy(image, curOffset, signature, 0, sizeof(uint)); - SignatureString = System.Text.Encoding.UTF8.GetString(signature); + 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/tools/r2rdump/R2RImportSection.cs b/src/tools/r2rdump/R2RImportSection.cs index aa567d51e82c..4f591dff4d5e 100644 --- a/src/tools/r2rdump/R2RImportSection.cs +++ b/src/tools/r2rdump/R2RImportSection.cs @@ -9,7 +9,7 @@ namespace R2RDump { - struct R2RImportSection + public struct R2RImportSection { public enum CorCompileImportType { @@ -24,6 +24,7 @@ public enum CorCompileImportType public enum CorCompileImportFlags { + CORCOMPILE_IMPORT_FLAGS_UNKNOWN = 0x0000, CORCOMPILE_IMPORT_FLAGS_EAGER = 0x0001, // Section at module load time. CORCOMPILE_IMPORT_FLAGS_CODE = 0x0002, // Section contains code. CORCOMPILE_IMPORT_FLAGS_PCODE = 0x0004, // Section contains pointers to code. @@ -31,9 +32,9 @@ public enum CorCompileImportFlags public struct ImportSectionEntry { - public long Section { get; } - public uint SignatureRVA { get; } - public uint Signature { get; } + public long Section { get; set; } + public uint SignatureRVA { get; set; } + public uint Signature { get; set; } public ImportSectionEntry(long section, uint signatureRVA, uint signature) { Section = section; @@ -54,35 +55,35 @@ public override string ToString() /// /// Section containing values to be fixed up /// - public int SectionRVA { get; } - public int SectionSize { get; } + public int SectionRVA { get; set; } + public int SectionSize { get; set; } /// /// One or more of ImportSectionFlags /// - public CorCompileImportFlags Flags { get; } + public CorCompileImportFlags Flags { get; set; } /// /// One of ImportSectionType /// - public CorCompileImportType Type { get; } + public CorCompileImportType Type { get; set; } /// /// /// - public byte EntrySize { get; } + public byte EntrySize { get; set; } /// /// RVA of optional signature descriptors /// - public int SignatureRVA { get; } - public List Entries { get; } + public int SignatureRVA { get; set; } + public List Entries { get; set; } /// /// RVA of optional auxiliary data (typically GC info) /// - public int AuxiliaryDataRVA { get; } - public GcInfo AuxiliaryData { get; } + public int AuxiliaryDataRVA { get; set; } + public GcInfo AuxiliaryData { get; set; } public R2RImportSection(byte[] image, int rva, int size, CorCompileImportFlags flags, byte type, byte entrySize, int signatureRVA, List entries, int auxDataRVA, int auxDataOffset, Machine machine, ushort majorVersion) { diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs index 448b5b73e8f0..20d48701915a 100644 --- a/src/tools/r2rdump/R2RMethod.cs +++ b/src/tools/r2rdump/R2RMethod.cs @@ -9,25 +9,26 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; +using System.Xml.Serialization; namespace R2RDump { - interface BaseUnwindInfo + public interface BaseUnwindInfo { } - class RuntimeFunction + public class RuntimeFunction { /// /// The index of the runtime function /// - public int Id { get; } + public int Id { get; set; } /// /// The relative virtual address to the start of the code block /// - public int StartAddress { get; } + public int StartAddress { get; set; } /// /// The size of the code block in bytes @@ -36,12 +37,12 @@ class RuntimeFunction /// The EndAddress field in the runtime functions section is conditional on machine type /// Size is -1 for images without the EndAddress field /// - public int Size { get; } + public int Size { get; set; } /// /// The relative virtual address to the unwind info /// - public int UnwindRVA { get; } + public int UnwindRVA { get; set; } public int CodeOffset { get; set; } @@ -52,6 +53,8 @@ class RuntimeFunction public BaseUnwindInfo UnwindInfo { get; } + public RuntimeFunction() { } + public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, int codeOffset, R2RMethod method, BaseUnwindInfo unwindInfo, GcInfo gcInfo) { Id = id; @@ -91,7 +94,7 @@ public override string ToString() } } - class R2RMethod + public class R2RMethod { private const int _mdtMethodDef = 0x06000000; @@ -101,31 +104,31 @@ class R2RMethod /// /// The name of the method /// - public string Name { get; } + public string Name { get; set; } /// /// The signature with format: namespace.class.methodName(S, T, ...) /// - public string SignatureString { get; } + public string SignatureString { get; set; } - public bool IsGeneric { get; } + public bool IsGeneric { get; set; } public MethodSignature Signature { get; } /// /// The type that the method belongs to /// - public string DeclaringType { get; } + public string DeclaringType { get; set; } /// /// The token of the method consisting of the table code (0x06) and row id /// - public uint Token { get; } + public uint Token { get; set; } /// /// The row id of the method /// - public uint Rid { get; } + public uint Rid { get; set; } /// /// All the runtime functions of this method @@ -135,8 +138,9 @@ class R2RMethod /// /// The id of the entrypoint runtime function /// - public int EntryPointRuntimeFunctionId { get; } + public int EntryPointRuntimeFunctionId { get; set; } + [XmlIgnore] public GcInfo GcInfo { get; set; } /// @@ -179,6 +183,8 @@ public enum GenericElementTypes Array = 0x1d, }; + public R2RMethod() { } + /// /// Extracts the method signature from the metadata by rid /// diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs index b5d150b59750..d365f87f3a80 100644 --- a/src/tools/r2rdump/R2RReader.cs +++ b/src/tools/r2rdump/R2RReader.cs @@ -32,7 +32,7 @@ public enum Amd64Registers E15 = 15, } - class R2RReader + public class R2RReader { private readonly PEReader _peReader; private readonly MetadataReader _mdReader; @@ -45,23 +45,23 @@ class R2RReader /// /// Name of the image file /// - public string Filename { get; } + public string Filename { get; set; } /// /// True if the image is ReadyToRun /// - public bool IsR2R { get; } + public bool IsR2R { get; set; } /// /// The type of target machine /// - public Machine Machine { get; } + public Machine Machine { get; set; } /// /// The preferred address of the first byte of image when loaded into memory; /// must be a multiple of 64K. /// - public ulong ImageBase { get; } + public ulong ImageBase { get; set; } /// /// The ReadyToRun header @@ -79,12 +79,14 @@ class R2RReader public IList AvailableTypes { get; } /// - /// The compile identifier string from READYTORUN_SECTION_COMPILER_IDENTIFIER + /// The compiler identifier string from READYTORUN_SECTION_COMPILER_IDENTIFIER /// - public string CompileIdentifier { get; } + public string CompilerIdentifier { get; } public IList ImportSections { get; } + public unsafe R2RReader() { } + /// /// Initializes the fields of the R2RHeader and R2RMethods /// @@ -140,7 +142,7 @@ public unsafe R2RReader(string filename) AvailableTypes = new List(); ParseAvailableTypes(); - CompileIdentifier = ParseCompilerIdentifier(); + CompilerIdentifier = ParseCompilerIdentifier(); ImportSections = new List(); ParseImportSections(); @@ -310,9 +312,9 @@ private string ParseCompilerIdentifier() 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); + Array.Copy(Image, identifierOffset, identifier, 0, compilerIdentifierSection.Size - 1); return Encoding.UTF8.GetString(identifier); } diff --git a/src/tools/r2rdump/R2RSection.cs b/src/tools/r2rdump/R2RSection.cs index 8d6d7cf99c57..77cf24d98f11 100644 --- a/src/tools/r2rdump/R2RSection.cs +++ b/src/tools/r2rdump/R2RSection.cs @@ -8,7 +8,7 @@ namespace R2RDump { - struct R2RSection + public struct R2RSection { public enum SectionType { @@ -28,17 +28,17 @@ public enum SectionType /// /// The ReadyToRun section type /// - public SectionType Type { get; } + public SectionType Type { get; set; } /// /// The RVA to the section /// - public int RelativeVirtualAddress { get; } + public int RelativeVirtualAddress { get; set; } /// /// The size of the section /// - public int Size { get; } + public int Size { get; set; } public R2RSection(SectionType type, int rva, int size) { diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs new file mode 100644 index 000000000000..303cb904d891 --- /dev/null +++ b/src/tools/r2rdump/TextDumper.cs @@ -0,0 +1,308 @@ +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) + { + DumpDisasm(_disassembler, rtf, _r2r.GetOffset(rtf.StartAddress), _r2r.Image); + } + + 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)((Amd64.UnwindInfo)rtf.UnwindInfo).Size); + } + } + SkipLine(); + } + + internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode = null) + { + int rtfOffset = 0; + int codeOffset = rtf.CodeOffset; + Dictionary transitions = rtf.Method.GcInfo.Transitions; + GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable; + while (rtfOffset < rtf.Size) + { + string instr; + int instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr); + + _writer.Write(instr); + if (transitions.ContainsKey(codeOffset)) + { + _writer.WriteLine($"\t\t\t\t{transitions[codeOffset].GetSlotState(slotTable)}"); + } + + CoreDisTools.ClearOutputBuffer(); + rtfOffset += instrSize; + codeOffset += instrSize; + } + } + + /// + /// 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, string name = "Raw", 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.CompilerIdentifier); + 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/tools/r2rdump/XmlDumper.cs b/src/tools/r2rdump/XmlDumper.cs new file mode 100644 index 000000000000..c10ae7c72b1d --- /dev/null +++ b/src/tools/r2rdump/XmlDumper.cs @@ -0,0 +1,311 @@ +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 (GcInfo.GcTransition transition in method.GcInfo.Transitions.Values) + { + Serialize(transition, gcNode); + } + + if (_raw) + { + DumpBytes(method.GcInfo.Offset, (uint)method.GcInfo.Size, gcNode, "Raw", 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) + { + DumpDisasm(_disassembler, rtf, _r2r.GetOffset(rtf.StartAddress), _r2r.Image, 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)((Amd64.UnwindInfo)rtf.UnwindInfo).Size, unwindNode); + } + } + } + + internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode) + { + int rtfOffset = 0; + int codeOffset = rtf.CodeOffset; + Dictionary transitions = rtf.Method.GcInfo.Transitions; + GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable; + while (rtfOffset < rtf.Size) + { + string instr; + int instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr); + + AddXMLNode("offset"+codeOffset, instr, parentNode); + if (transitions.ContainsKey(codeOffset)) + { + AddXMLNode("Transition", transitions[codeOffset].GetSlotState(slotTable), parentNode); + } + + CoreDisTools.ClearOutputBuffer(); + rtfOffset += instrSize; + codeOffset += instrSize; + } + } + + /// + /// 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, string name = "Raw", 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(name, sb.ToString(), parentNode); + return; + } + } + + 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("CompilerIdentifier", _r2r.CompilerIdentifier, 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, "SectionBytes"); + } + if (importSection.SignatureRVA != 0) + { + DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int), contentsNode, "SignatureBytes"); + } + if (importSection.AuxiliaryDataRVA != 0) + { + DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size, contentsNode, "AuxiliaryDataBytes"); + } + } + 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; + } + } +}