summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorTomáš Rylek <trylek@microsoft.com>2018-12-15 01:40:33 +0100
committerGitHub <noreply@github.com>2018-12-15 01:40:33 +0100
commit1df63bdbce55f2feb60238c244c6723dd9530e82 (patch)
tree1ea21d2f4a6b919632caa6e13c557c224d544dd6 /src/tools
parentca65764c029f2dac6f4a187694f4232eec9b1115 (diff)
downloadcoreclr-1df63bdbce55f2feb60238c244c6723dd9530e82.tar.gz
coreclr-1df63bdbce55f2feb60238c244c6723dd9530e82.tar.bz2
coreclr-1df63bdbce55f2feb60238c244c6723dd9530e82.zip
Implement GC ref map parsing and display in R2RDump (#21509)
As part of my work on CPAOT implementation of GC ref map info I have implemented decoder of the info so that it can be displayed next to the import cell signatures. This also uncovered one possible cause of R2RDump GcInfo-related crashes that were observed by Andon and myself. It looks like Amy in her initial implementation confused the various GC encodings and used GC info to parse the import section auxiliary data which actually contains the GC ref map info. Thanks Tomas
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/r2rdump/CoreDisTools.cs26
-rw-r--r--src/tools/r2rdump/GCRefMap.cs229
-rw-r--r--src/tools/r2rdump/R2RImportSection.cs89
-rw-r--r--src/tools/r2rdump/R2RMethod.cs29
-rw-r--r--src/tools/r2rdump/R2RReader.cs47
-rw-r--r--src/tools/r2rdump/R2RSection.cs17
-rw-r--r--src/tools/r2rdump/TextDumper.cs37
-rw-r--r--src/tools/r2rdump/TransitionBlock.cs151
-rw-r--r--src/tools/r2rdump/XmlDumper.cs2
9 files changed, 553 insertions, 74 deletions
diff --git a/src/tools/r2rdump/CoreDisTools.cs b/src/tools/r2rdump/CoreDisTools.cs
index d6a82f1b63..925b88b08e 100644
--- a/src/tools/r2rdump/CoreDisTools.cs
+++ b/src/tools/r2rdump/CoreDisTools.cs
@@ -156,13 +156,17 @@ namespace R2RDump
line[colon + 2] == ' ')
{
colon += 3;
- }
- nakedInstruction.Append(new string(' ', 32) + line.Substring(colon).TrimStart());
+ }
+
+ nakedInstruction.Append($"{(rtfOffset + rtf.CodeOffset),8:x4}:");
+ nakedInstruction.Append(" ");
+ nakedInstruction.Append(line.Substring(colon).TrimStart());
nakedInstruction.Append('\n');
}
else
{
- nakedInstruction.Append(line);
+ nakedInstruction.Append(' ', 7);
+ nakedInstruction.Append(line.TrimStart());
nakedInstruction.Append('\n');
}
}
@@ -172,7 +176,6 @@ namespace R2RDump
switch (_reader.Machine)
{
case Machine.Amd64:
- case Machine.IA64:
ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
break;
@@ -312,19 +315,19 @@ namespace R2RDump
{
sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
int target = rtf.StartAddress + rtfOffset + instrSize + offset;
- ReplaceRelativeOffset(ref instruction, target);
+ ReplaceRelativeOffset(ref instruction, target, rtf);
}
else if (instrSize == 5 && IsIntel1ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
{
int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 1);
int target = rtf.StartAddress + rtfOffset + instrSize + offset;
- ReplaceRelativeOffset(ref instruction, target);
+ ReplaceRelativeOffset(ref instruction, target, rtf);
}
else if (instrSize == 6 && IsIntel2ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
{
int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 2);
int target = rtf.StartAddress + rtfOffset + instrSize + offset;
- ReplaceRelativeOffset(ref instruction, target);
+ ReplaceRelativeOffset(ref instruction, target, rtf);
}
}
@@ -401,7 +404,7 @@ namespace R2RDump
/// </summary>
/// <param name="instruction"></param>
/// <param name="target"></param>
- private void ReplaceRelativeOffset(ref string instruction, int target)
+ private void ReplaceRelativeOffset(ref string instruction, int target, RuntimeFunction rtf)
{
int numberEnd = instruction.IndexOf('\n');
int number = numberEnd;
@@ -417,7 +420,12 @@ namespace R2RDump
StringBuilder translated = new StringBuilder();
translated.Append(instruction, 0, number);
- translated.AppendFormat("0x{0:x4}", target);
+ int outputOffset = target;
+ if (_options.Naked)
+ {
+ outputOffset -= rtf.StartAddress;
+ }
+ translated.AppendFormat("0x{0:x4}", outputOffset);
translated.Append(instruction, numberEnd, instruction.Length - numberEnd);
instruction = translated.ToString();
}
diff --git a/src/tools/r2rdump/GCRefMap.cs b/src/tools/r2rdump/GCRefMap.cs
new file mode 100644
index 0000000000..55c06add61
--- /dev/null
+++ b/src/tools/r2rdump/GCRefMap.cs
@@ -0,0 +1,229 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace R2RDump
+{
+ public enum CORCOMPILE_GCREFMAP_TOKENS
+ {
+ GCREFMAP_SKIP = 0,
+ GCREFMAP_REF = 1,
+ GCREFMAP_INTERIOR = 2,
+ GCREFMAP_METHOD_PARAM = 3,
+ GCREFMAP_TYPE_PARAM = 4,
+ GCREFMAP_VASIG_COOKIE = 5,
+ }
+
+ public struct GCRefMapEntry
+ {
+ public readonly int Offset;
+ public readonly CORCOMPILE_GCREFMAP_TOKENS Token;
+
+ public GCRefMapEntry(int offset, CORCOMPILE_GCREFMAP_TOKENS token)
+ {
+ Offset = offset;
+ Token = token;
+ }
+ }
+
+ public class GCRefMap
+ {
+ public const int GCREFMAP_LOOKUP_STRIDE = 1024;
+
+ public const uint InvalidStackPop = ~0u;
+
+ public readonly uint StackPop;
+ public readonly GCRefMapEntry[] Entries;
+
+ public GCRefMap(uint stackPop, GCRefMapEntry[] entries)
+ {
+ StackPop = stackPop;
+ Entries = entries;
+ }
+
+ public void WriteTo(TextWriter writer)
+ {
+ if (StackPop != InvalidStackPop)
+ {
+ writer.Write(@"POP(0x{StackPop:X}) ");
+ }
+ for (int entryIndex = 0; entryIndex < Entries.Length; entryIndex++)
+ {
+ GCRefMapEntry entry = Entries[entryIndex];
+ if (entryIndex == 0 || entry.Token != Entries[entryIndex - 1].Token)
+ {
+ if (entryIndex != 0)
+ {
+ writer.Write(") ");
+ }
+ switch (entry.Token)
+ {
+ case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF:
+ writer.Write("R");
+ break;
+ case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR:
+ writer.Write("I");
+ break;
+ case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_METHOD_PARAM:
+ writer.Write("M");
+ break;
+ case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_TYPE_PARAM:
+ writer.Write("T");
+ break;
+ case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_VASIG_COOKIE:
+ writer.Write("V");
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+ writer.Write("(");
+ }
+ else
+ {
+ writer.Write(" ");
+ }
+ writer.Write($"{entry.Offset:X2}");
+ }
+ writer.Write(")");
+ }
+ }
+
+ /// <summary>
+ /// Helper class for decoding the bit-oriented GC ref map format.
+ /// </summary>
+ public class GCRefMapDecoder
+ {
+ private readonly R2RReader _reader;
+ private int _offset;
+ private int _pendingByte;
+ private int _pos;
+
+ public GCRefMapDecoder(R2RReader reader, int offset)
+ {
+ _reader = reader;
+ _offset = offset;
+ _pendingByte = 0x80;
+ _pos = 0;
+ }
+
+ public int GetBit()
+ {
+ int x = _pendingByte;
+ if ((x & 0x80) != 0)
+ {
+ x = _reader.Image[_offset++];
+ x |= ((x & 0x80) << 7);
+ }
+ _pendingByte = x >> 1;
+ return x & 1;
+ }
+
+ public int GetTwoBit()
+ {
+ int result = GetBit();
+ result |= GetBit() << 1;
+ return result;
+ }
+
+ public int GetInt()
+ {
+ int result = 0;
+
+ int bit = 0;
+ do
+ {
+ result |= GetBit() << (bit++);
+ result |= GetBit() << (bit++);
+ result |= GetBit() << (bit++);
+ }
+ while (GetBit() != 0);
+
+ return result;
+ }
+
+ public bool AtEnd()
+ {
+ return _pendingByte == 0;
+ }
+
+ public int GetOffset()
+ {
+ return _offset;
+ }
+
+ public uint ReadStackPop()
+ {
+ Debug.Assert(_reader.Architecture == Architecture.X86);
+
+ int x = GetTwoBit();
+
+ if (x == 3)
+ x = GetInt() + 3;
+
+ return (uint)x;
+ }
+
+ public int CurrentPos()
+ {
+ return _pos;
+ }
+
+ public CORCOMPILE_GCREFMAP_TOKENS ReadToken()
+ {
+ int val = GetTwoBit();
+ if (val == 3)
+ {
+ int ext = GetInt();
+ if ((ext & 1) == 0)
+ {
+ _pos += (ext >> 1) + 4;
+ return CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_SKIP;
+ }
+ else
+ {
+ _pos++;
+ return (CORCOMPILE_GCREFMAP_TOKENS)((ext >> 1) + 3);
+ }
+ }
+ _pos++;
+ return (CORCOMPILE_GCREFMAP_TOKENS)val;
+ }
+
+ public GCRefMap ReadMap()
+ {
+ TransitionBlock transitionBlock = TransitionBlock.FromReader(_reader);
+
+ List<GCRefMapEntry> entries = new List<GCRefMapEntry>();
+ uint stackPop = GCRefMap.InvalidStackPop;
+
+ if (_reader.Architecture == Architecture.X86)
+ {
+ stackPop = ReadStackPop();
+ }
+
+ while (!AtEnd())
+ {
+ int pos = CurrentPos();
+ CORCOMPILE_GCREFMAP_TOKENS token = ReadToken();
+ if (token != CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_SKIP)
+ {
+ int offset = transitionBlock.OffsetFromGCRefMapPos(pos);
+ entries.Add(new GCRefMapEntry(offset, token));
+ }
+ }
+
+ if (stackPop != GCRefMap.InvalidStackPop || entries.Count > 0)
+ {
+ return new GCRefMap(stackPop, entries.ToArray());
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/tools/r2rdump/R2RImportSection.cs b/src/tools/r2rdump/R2RImportSection.cs
index 4216f231f5..8e5e32e322 100644
--- a/src/tools/r2rdump/R2RImportSection.cs
+++ b/src/tools/r2rdump/R2RImportSection.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml.Serialization;
@@ -15,7 +16,7 @@ namespace R2RDump
/// </summary>
public struct R2RImportSection
{
- public struct ImportSectionEntry
+ public class ImportSectionEntry
{
[XmlAttribute("Index")]
public int Index { get; set; }
@@ -24,6 +25,8 @@ namespace R2RDump
public long Section { get; set; }
public uint SignatureRVA { get; set; }
public string Signature { get; set; }
+ public GCRefMap GCRefMap { get; set; }
+
public ImportSectionEntry(int index, int startOffset, int startRVA, long section, uint signatureRVA, string signature)
{
Index = index;
@@ -34,15 +37,22 @@ namespace R2RDump
Signature = signature;
}
- public override string ToString()
+ public void WriteTo(TextWriter writer, DumpOptions options)
{
- StringBuilder builder = new StringBuilder();
- builder.AppendFormat("+{0:X4}", StartOffset);
- builder.AppendFormat(" ({0:X4})", StartRVA);
- builder.AppendFormat(" Section: 0x{0:X8}", Section);
- builder.AppendFormat(" SignatureRVA: 0x{0:X8}", SignatureRVA);
- builder.AppendFormat(" {0}", Signature);
- return builder.ToString();
+ if (!options.Naked)
+ {
+ writer.Write($"+{StartOffset:X4}");
+ writer.Write($" ({StartRVA:X4})");
+ writer.Write($" Section: 0x{Section:X8}");
+ writer.Write($" SignatureRVA: 0x{SignatureRVA:X8}");
+ writer.Write(" ");
+ }
+ writer.Write(Signature);
+ if (GCRefMap != null)
+ {
+ writer.Write(" -- ");
+ GCRefMap.WriteTo(writer);
+ }
}
}
@@ -80,10 +90,23 @@ namespace R2RDump
/// RVA of optional auxiliary data (typically GC info)
/// </summary>
public int AuxiliaryDataRVA { get; set; }
- [XmlIgnore]
- public BaseGcInfo AuxiliaryData { get; set; }
- public R2RImportSection(int index, byte[] image, int rva, int size, CorCompileImportFlags flags, byte type, byte entrySize, int signatureRVA, List<ImportSectionEntry> entries, int auxDataRVA, int auxDataOffset, Machine machine, ushort majorVersion)
+ public int AuxiliaryDataSize { get; set; }
+
+ public R2RImportSection(
+ int index,
+ R2RReader reader,
+ int rva,
+ int size,
+ CorCompileImportFlags flags,
+ byte type,
+ byte entrySize,
+ int signatureRVA,
+ List<ImportSectionEntry> entries,
+ int auxDataRVA,
+ int auxDataOffset,
+ Machine machine,
+ ushort majorVersion)
{
Index = index;
SectionRVA = rva;
@@ -96,36 +119,36 @@ namespace R2RDump
Entries = entries;
AuxiliaryDataRVA = auxDataRVA;
- AuxiliaryData = null;
+ AuxiliaryDataSize = 0;
if (AuxiliaryDataRVA != 0)
{
- if (machine == Machine.Amd64)
- {
- AuxiliaryData = new Amd64.GcInfo(image, auxDataOffset, machine, majorVersion);
- }
- else if (machine == Machine.I386)
+ int startOffset = auxDataOffset + BitConverter.ToInt32(reader.Image, auxDataOffset);
+
+ for (int i = 0; i < Entries.Count; i++)
{
- AuxiliaryData = new x86.GcInfo(image, auxDataOffset, machine, majorVersion);
+ GCRefMapDecoder decoder = new GCRefMapDecoder(reader, startOffset);
+ Entries[i].GCRefMap = decoder.ReadMap();
+ startOffset = decoder.GetOffset();
}
+
+ AuxiliaryDataSize = startOffset - auxDataOffset;
}
}
+ public void WriteTo(TextWriter writer)
+ {
+ writer.WriteLine($"SectionRVA: 0x{SectionRVA:X8} ({SectionRVA})");
+ writer.WriteLine($"SectionSize: {SectionSize} bytes");
+ writer.WriteLine($"Flags: {Flags}");
+ writer.WriteLine($"Type: {Type}");
+ writer.WriteLine($"EntrySize: {EntrySize}");
+ writer.WriteLine($"SignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
+ writer.WriteLine($"AuxiliaryDataRVA: 0x{AuxiliaryDataRVA:X8} ({AuxiliaryDataRVA})");
+ }
+
public override string ToString()
{
- StringBuilder sb = new StringBuilder();
- sb.AppendLine($"SectionRVA: 0x{SectionRVA:X8} ({SectionRVA})");
- sb.AppendLine($"SectionSize: {SectionSize} bytes");
- sb.AppendLine($"Flags: {Flags}");
- sb.AppendLine($"Type: {Type}");
- sb.AppendLine($"EntrySize: {EntrySize}");
- sb.AppendLine($"SignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
- sb.AppendLine($"AuxiliaryDataRVA: 0x{AuxiliaryDataRVA:X8} ({AuxiliaryDataRVA})");
- if (AuxiliaryDataRVA != 0 && AuxiliaryData != null)
- {
- sb.AppendLine("AuxiliaryData:");
- sb.AppendLine(AuxiliaryData.ToString());
- }
- return sb.ToString();
+ throw new NotImplementedException();
}
}
}
diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs
index 6078e6f6bc..27f218536f 100644
--- a/src/tools/r2rdump/R2RMethod.cs
+++ b/src/tools/r2rdump/R2RMethod.cs
@@ -139,8 +139,11 @@ namespace R2RDump
public void WriteTo(TextWriter writer, DumpOptions options)
{
- writer.WriteLine($"Id: {Id}");
- writer.WriteLine($"StartAddress: 0x{StartAddress:X8}");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"Id: {Id}");
+ writer.WriteLine($"StartAddress: 0x{StartAddress:X8}");
+ }
if (Size == -1)
{
writer.WriteLine("Size: Unavailable");
@@ -149,7 +152,10 @@ namespace R2RDump
{
writer.WriteLine($"Size: {Size} bytes");
}
- writer.WriteLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+ }
if (UnwindInfo is Amd64.UnwindInfo amd64UnwindInfo)
{
string parsedFlags = "";
@@ -175,7 +181,10 @@ namespace R2RDump
writer.WriteLine($"CountOfUnwindCodes: {amd64UnwindInfo.CountOfUnwindCodes}");
writer.WriteLine($"FrameRegister: {amd64UnwindInfo.FrameRegister}");
writer.WriteLine($"FrameOffset: 0x{amd64UnwindInfo.FrameOffset}");
- writer.WriteLine($"PersonalityRVA: 0x{amd64UnwindInfo.PersonalityRoutineRVA:X4}");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"PersonalityRVA: 0x{amd64UnwindInfo.PersonalityRoutineRVA:X4}");
+ }
for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++)
{
@@ -375,7 +384,10 @@ namespace R2RDump
writer.WriteLine($"Handle: 0x{MetadataTokens.GetToken(R2RReader.MetadataReader, MethodHandle):X8}");
writer.WriteLine($"Rid: {MetadataTokens.GetRowNumber(R2RReader.MetadataReader, MethodHandle)}");
- writer.WriteLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}");
+ }
writer.WriteLine($"Number of RuntimeFunctions: {RuntimeFunctions.Count}");
if (Fixups != null)
{
@@ -388,7 +400,12 @@ namespace R2RDump
foreach (FixupCell cell in fixups)
{
- writer.WriteLine($" TableIndex {cell.TableIndex}, Offset {cell.CellOffset:X4}: {cell.Signature}");
+ writer.Write(" ");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"TableIndex {cell.TableIndex}, Offset {cell.CellOffset:X4}: ");
+ }
+ writer.WriteLine(cell.Signature);
}
}
}
diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs
index 9d20d61e86..b74e9b2f36 100644
--- a/src/tools/r2rdump/R2RReader.cs
+++ b/src/tools/r2rdump/R2RReader.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Serialization;
@@ -107,9 +108,22 @@ namespace R2RDump
/// </summary>
public Machine Machine { get; set; }
+ /// <summary>
+ /// Targeting operating system for the R2R executable
+ /// </summary>
public OperatingSystem OS { get; set; }
/// <summary>
+ /// Targeting processor architecture of the R2R executable
+ /// </summary>
+ public Architecture Architecture { get; set; }
+
+ /// <summary>
+ /// Pointer size in bytes for the target architecture
+ /// </summary>
+ public int PointerSize { get; set; }
+
+ /// <summary>
/// The preferred address of the first byte of image when loaded into memory;
/// must be a multiple of 64K.
/// </summary>
@@ -195,6 +209,36 @@ namespace R2RDump
{
throw new BadImageFormatException($"Invalid Machine: {machine}");
}
+
+ switch (Machine)
+ {
+ case Machine.I386:
+ Architecture = Architecture.X86;
+ PointerSize = 4;
+ break;
+
+ case Machine.Amd64:
+ Architecture = Architecture.X64;
+ PointerSize = 8;
+ break;
+
+ case Machine.Arm:
+ case Machine.Thumb:
+ case Machine.ArmThumb2:
+ Architecture = Architecture.Arm;
+ PointerSize = 4;
+ break;
+
+ case Machine.Arm64:
+ Architecture = Architecture.Arm64;
+ PointerSize = 8;
+ break;
+
+ default:
+ throw new NotImplementedException(Machine.ToString());
+ }
+
+
ImageBase = PEReader.PEHeaders.PEHeader.ImageBase;
// initialize R2RHeader
@@ -555,7 +599,6 @@ namespace R2RDump
break;
case Machine.Amd64:
- case Machine.IA64:
case Machine.Arm64:
entrySize = 8;
break;
@@ -594,7 +637,7 @@ namespace R2RDump
{
auxDataOffset = GetOffset(auxDataRVA);
}
- ImportSections.Add(new R2RImportSection(ImportSections.Count, Image, rva, size, flags, type, entrySize, signatureRVA, entries, auxDataRVA, auxDataOffset, Machine, R2RHeader.MajorVersion));
+ ImportSections.Add(new R2RImportSection(ImportSections.Count, this, rva, size, flags, type, entrySize, signatureRVA, entries, auxDataRVA, auxDataOffset, Machine, R2RHeader.MajorVersion));
}
}
diff --git a/src/tools/r2rdump/R2RSection.cs b/src/tools/r2rdump/R2RSection.cs
index 125d040657..0c715cc652 100644
--- a/src/tools/r2rdump/R2RSection.cs
+++ b/src/tools/r2rdump/R2RSection.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Text;
using System.Xml.Serialization;
@@ -52,13 +53,19 @@ namespace R2RDump
Size = size;
}
+ public void WriteTo(TextWriter writer, DumpOptions options)
+ {
+ writer.WriteLine($"Type: {Enum.GetName(typeof(SectionType), Type)} ({Type:D})");
+ if (!options.Naked)
+ {
+ writer.WriteLine($"RelativeVirtualAddress: 0x{RelativeVirtualAddress:X8}");
+ }
+ writer.WriteLine($"Size: {Size} bytes");
+ }
+
public override string ToString()
{
- StringBuilder sb = new StringBuilder();
- sb.AppendLine($"Type: {Enum.GetName(typeof(SectionType), Type)} ({Type:D})");
- sb.AppendLine($"RelativeVirtualAddress: 0x{RelativeVirtualAddress:X8}");
- sb.AppendLine($"Size: {Size} bytes");
- return sb.ToString();
+ throw new NotImplementedException();
}
}
}
diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs
index da45a71757..f31c903ec1 100644
--- a/src/tools/r2rdump/TextDumper.cs
+++ b/src/tools/r2rdump/TextDumper.cs
@@ -71,7 +71,7 @@ namespace R2RDump
foreach (R2RSection section in NormalizedSections())
{
- DumpSection(section);
+ DumpSection(section, parentNode: null);
}
}
SkipLine();
@@ -83,7 +83,7 @@ namespace R2RDump
internal override void DumpSection(R2RSection section, XmlNode parentNode = null)
{
WriteSubDivider();
- _writer.WriteLine(section.ToString());
+ section.WriteTo(_writer, _options);
if (_options.Raw)
{
@@ -92,7 +92,7 @@ namespace R2RDump
}
if (_options.SectionContents)
{
- DumpSectionContents(section);
+ DumpSectionContents(section, parentNode);
SkipLine();
}
}
@@ -178,6 +178,8 @@ namespace R2RDump
/// </summary>
internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
{
+ int indent = (_options.Naked ? 11 : 32);
+ string indentString = new string(' ', indent);
int rtfOffset = 0;
int codeOffset = rtf.CodeOffset;
while (rtfOffset < rtf.Size)
@@ -190,10 +192,10 @@ namespace R2RDump
List<Amd64.UnwindCode> codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
foreach (Amd64.UnwindCode code in codes)
{
- _writer.Write($" {code.UnwindOp} {code.OpInfoStr}");
+ _writer.Write($"{indentString}{code.UnwindOp} {code.OpInfoStr}");
if (code.NextFrameOffset != -1)
{
- _writer.WriteLine($" {code.NextFrameOffset}");
+ _writer.WriteLine($"{indentString}{code.NextFrameOffset}");
}
_writer.WriteLine();
}
@@ -203,7 +205,7 @@ namespace R2RDump
{
foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
{
- _writer.WriteLine($" {transition.ToString()}");
+ _writer.WriteLine($"{indentString}{transition}");
}
}
@@ -326,7 +328,7 @@ namespace R2RDump
{
foreach (R2RImportSection importSection in _r2r.ImportSections)
{
- _writer.Write(importSection.ToString());
+ importSection.WriteTo(_writer);
if (_options.Raw && importSection.Entries.Count != 0)
{
if (importSection.SectionRVA != 0)
@@ -339,15 +341,16 @@ namespace R2RDump
_writer.WriteLine("Signature Bytes:");
DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int));
}
- if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryData != null)
+ if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryDataSize != 0)
{
_writer.WriteLine("AuxiliaryData Bytes:");
- DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size);
+ DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryDataSize);
}
}
foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)
{
- _writer.WriteLine(entry.ToString());
+ entry.WriteTo(_writer, _options);
+ _writer.WriteLine();
}
_writer.WriteLine();
}
@@ -358,18 +361,16 @@ namespace R2RDump
private void DumpNakedImportSections()
{
- List<string> importSignatures = new List<string>();
+ List<R2RImportSection.ImportSectionEntry> entries = new List<R2RImportSection.ImportSectionEntry>();
foreach (R2RImportSection importSection in _r2r.ImportSections)
{
- foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)
- {
- importSignatures.Add(entry.Signature);
- }
+ entries.AddRange(importSection.Entries);
}
- importSignatures.Sort();
- foreach (string sig in importSignatures)
+ entries.Sort((e1, e2) => e1.Signature.CompareTo(e2.Signature));
+ foreach (R2RImportSection.ImportSectionEntry entry in entries)
{
- _writer.WriteLine(sig);
+ entry.WriteTo(_writer, _options);
+ _writer.WriteLine();
}
}
diff --git a/src/tools/r2rdump/TransitionBlock.cs b/src/tools/r2rdump/TransitionBlock.cs
new file mode 100644
index 0000000000..46a17248d7
--- /dev/null
+++ b/src/tools/r2rdump/TransitionBlock.cs
@@ -0,0 +1,151 @@
+// 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.Collections.Generic;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace R2RDump
+{
+ public abstract class TransitionBlock
+ {
+ public R2RReader _reader;
+
+ public static TransitionBlock FromReader(R2RReader reader)
+ {
+ switch (reader.Architecture)
+ {
+ case Architecture.X86:
+ return X86TransitionBlock.Instance;
+
+ case Architecture.X64:
+ return reader.OS == OperatingSystem.Windows ? X64WindowsTransitionBlock.Instance : X64UnixTransitionBlock.Instance;
+
+ case Architecture.Arm:
+ return ArmTransitionBlock.Instance;
+
+ case Architecture.Arm64:
+ return Arm64TransitionBlock.Instance;
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ public abstract int PointerSize { get; }
+
+ public abstract int NumArgumentRegisters { get; }
+
+ public int SizeOfArgumentRegisters => NumArgumentRegisters * PointerSize;
+
+ public abstract int NumCalleeSavedRegisters { get; }
+
+ public int SizeOfCalleeSavedRegisters => NumCalleeSavedRegisters * PointerSize;
+
+ public abstract int SizeOfTransitionBlock { get; }
+
+ public abstract int OffsetOfArgumentRegisters { get; }
+
+ /// <summary>
+ /// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures
+ /// except for X86 where it's overridden to supply a more complex algorithm.
+ /// </summary>
+ /// <param name="pos"></param>
+ /// <returns></returns>
+ public virtual int OffsetFromGCRefMapPos(int pos)
+ {
+ return OffsetOfArgumentRegisters + pos * PointerSize;
+ }
+
+ /// <summary>
+ /// The transition block should define everything pushed by callee. The code assumes in number of places that
+ /// end of the transition block is caller's stack pointer.
+ /// </summary>
+ public int OffsetOfArgs => SizeOfTransitionBlock;
+
+ private sealed class X86TransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new X86TransitionBlock();
+
+ public override int PointerSize => 4;
+ public override int NumArgumentRegisters => 2;
+ public override int NumCalleeSavedRegisters => 4;
+ // Argument registers, callee-save registers, return address
+ public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
+ public override int OffsetOfArgumentRegisters => 0;
+
+ public override int OffsetFromGCRefMapPos(int pos)
+ {
+ if (pos < NumArgumentRegisters)
+ {
+ return OffsetOfArgumentRegisters + SizeOfArgumentRegisters - (pos + 1) * PointerSize;
+ }
+ else
+ {
+ return OffsetOfArgs + (pos - NumArgumentRegisters) * PointerSize;
+ }
+ }
+ }
+
+ private sealed class X64WindowsTransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new X64WindowsTransitionBlock();
+
+ public override int PointerSize => 8;
+ // RCX, RDX, R8, R9
+ public override int NumArgumentRegisters => 4;
+ // RDI, RSI, RBX, RBP, R12, R13, R14, R15
+ public override int NumCalleeSavedRegisters => 8;
+ // Callee-saved registers, return address
+ public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + PointerSize;
+ public override int OffsetOfArgumentRegisters => SizeOfTransitionBlock;
+ }
+
+ private sealed class X64UnixTransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new X64UnixTransitionBlock();
+
+ public override int PointerSize => 8;
+ // RDI, RSI, RDX, RCX, R8, R9
+ public override int NumArgumentRegisters => 6;
+ // R12, R13, R14, R15, RBX, RBP
+ public override int NumCalleeSavedRegisters => 6;
+ // Argument registers, callee-saved registers, return address
+ public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
+ public override int OffsetOfArgumentRegisters => 0;
+ }
+
+ private sealed class ArmTransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new ArmTransitionBlock();
+
+ public override int PointerSize => 4;
+ // R0, R1, R2, R3
+ public override int NumArgumentRegisters => 4;
+ // R4, R5, R6, R7, R8, R9, R10, R11, R14
+ public override int NumCalleeSavedRegisters => 9;
+ // Callee-saves, argument registers
+ public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
+ public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters;
+ }
+
+ private sealed class Arm64TransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new Arm64TransitionBlock();
+
+ public override int PointerSize => 8;
+ // X0 .. X7
+ public override int NumArgumentRegisters => 8;
+ // X29, X30, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28
+ public override int NumCalleeSavedRegisters => 12;
+ // Callee-saves, padding, m_x8RetBuffReg, argument registers
+ public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + 2 * PointerSize + SizeOfArgumentRegisters;
+ public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters + 2 * PointerSize;
+ }
+ }
+
+}
+
diff --git a/src/tools/r2rdump/XmlDumper.cs b/src/tools/r2rdump/XmlDumper.cs
index 3712b893c4..5f67e27fe2 100644
--- a/src/tools/r2rdump/XmlDumper.cs
+++ b/src/tools/r2rdump/XmlDumper.cs
@@ -315,7 +315,7 @@ namespace R2RDump
}
if (importSection.AuxiliaryDataRVA != 0)
{
- DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size, importSectionsNode, "AuxiliaryDataBytes");
+ DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryDataSize, importSectionsNode, "AuxiliaryDataBytes");
}
}
foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)