summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorAmy Yu <amycmyu@gmail.com>2018-08-08 16:52:53 -0700
committerAmy <amycmyu@gmail.com>2018-08-15 15:24:43 -0700
commit26286b460e53d03ec8b74b85f09c0754bb10605f (patch)
tree95aa16a249dbfe6834f6bac0245f182ab8aa9c1d /src/tools
parent024f809869d156f73ea6d7527d9664ce90b5505e (diff)
downloadcoreclr-26286b460e53d03ec8b74b85f09c0754bb10605f.tar.gz
coreclr-26286b460e53d03ec8b74b85f09c0754bb10605f.tar.bz2
coreclr-26286b460e53d03ec8b74b85f09c0754bb10605f.zip
R2RDump - GcInfo for x86
x86 GcInfo headers x86 GcSlotTable x86 GcTransitions Update expected xml for R2RDumpTests Add license headers Allow multiple GcTransitions with same codeOffset Add index property in GcSlot, make GcSlot a class instead of struct, add missing spaces, update tests Remove placeholder functions for parsing partially interruptible pointer tables Implement partially interruptible GcInfo Example GcInfo output: CodeLength: 22 bytes InfoHdr: PrologSize: 7 EpilogSize: 4 EpilogCount: 1 EpilogAtEnd: yes Callee-saved regs = EBP EbpFrame: yes Fully Interruptible: yes DoubleAlign: no Arguments Size: 0 DWORDs Stack Frame Size: 1 DWORDs UntrackedCnt: 1 VarPtrTableSize: 0 GenericsContext: 0 GenericsContextIsMethodDesc: 0 ReturnKind: RT_Scalar RevPInvokeOffset: 0 Epilogs: 18 GcSlots: ------------------------- [EBP-4] Flags: GC_SLOT_UNTRACKED LowBits: ------------------------- 28fc: 55 push ebp 28fd: 8b ec mov ebp, esp 28ff: 50 push eax 2900: 89 4d fc mov dword ptr [ebp - 4], ecx 2903: 8b 4d fc mov ecx, dword ptr [ebp - 4] 2906: ff 15 10 10 01 10 call dword ptr [268505104] reg ECX becoming live 290c: 90 nop reg ECX becoming dead 290d: 90 nop 290e: 8b e5 mov esp, ebp 2910: 5d pop ebp 2911: c3 ret
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/r2rdump/Amd64/GcInfo.cs (renamed from src/tools/r2rdump/GCInfo.cs)97
-rw-r--r--src/tools/r2rdump/Amd64/GcSlotTable.cs (renamed from src/tools/r2rdump/GCSlotTable.cs)6
-rw-r--r--src/tools/r2rdump/Amd64/GcTransition.cs67
-rw-r--r--src/tools/r2rdump/Amd64/Registers.cs30
-rw-r--r--src/tools/r2rdump/Amd64/UnwindInfo.cs10
-rw-r--r--src/tools/r2rdump/GCInfoTypes.cs56
-rw-r--r--src/tools/r2rdump/NativeReader.cs25
-rw-r--r--src/tools/r2rdump/R2RDump.cs30
-rw-r--r--src/tools/r2rdump/R2RImportSection.cs12
-rw-r--r--src/tools/r2rdump/R2RMethod.cs26
-rw-r--r--src/tools/r2rdump/R2RReader.cs26
-rw-r--r--src/tools/r2rdump/TextDumper.cs8
-rw-r--r--src/tools/r2rdump/XmlDumper.cs21
-rw-r--r--src/tools/r2rdump/x86/CallPattern.cs105
-rw-r--r--src/tools/r2rdump/x86/GcInfo.cs496
-rw-r--r--src/tools/r2rdump/x86/GcSlotTable.cs186
-rw-r--r--src/tools/r2rdump/x86/GcTransition.cs283
-rw-r--r--src/tools/r2rdump/x86/InfoHdr.cs503
-rw-r--r--src/tools/r2rdump/x86/Registers.cs30
19 files changed, 1888 insertions, 129 deletions
diff --git a/src/tools/r2rdump/GCInfo.cs b/src/tools/r2rdump/Amd64/GcInfo.cs
index a940634436..df4b48f9a9 100644
--- a/src/tools/r2rdump/GCInfo.cs
+++ b/src/tools/r2rdump/Amd64/GcInfo.cs
@@ -8,9 +8,9 @@ using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml.Serialization;
-namespace R2RDump
+namespace R2RDump.Amd64
{
- public class GcInfo
+ public class GcInfo : BaseGcInfo
{
private enum GcInfoHeaderFlags
{
@@ -32,68 +32,6 @@ namespace R2RDump
GC_INFO_FLAGS_BIT_SIZE = 10,
};
- public struct InterruptibleRange
- {
- [XmlAttribute("Index")]
- public uint Index { get; set; }
- public uint StartOffset { get; set; }
- public uint StopOffset { get; set; }
- public InterruptibleRange(uint index, uint start, uint stop)
- {
- Index = index;
- StartOffset = start;
- StopOffset = stop;
- }
- }
-
- public class GcTransition
- {
- [XmlAttribute("Index")]
- public int CodeOffset { get; set; }
- 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)
- {
- CodeOffset = codeOffset;
- SlotId = slotId;
- IsLive = isLive;
- ChunkId = chunkId;
- }
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.AppendLine($"\t\tCodeOffset: {CodeOffset}");
- sb.AppendLine($"\t\tSlotId: {SlotId}");
- sb.AppendLine($"\t\tIsLive: {IsLive}");
- sb.AppendLine($"\t\tChunkId: {ChunkId}");
- sb.Append($"\t\t--------------------");
-
- return sb.ToString();
- }
- public string GetSlotState(GcSlotTable slotTable)
- {
- GcSlotTable.GcSlot slot = slotTable.GcSlots[SlotId];
- string slotStr = "";
- if (slot.StackSlot == null)
- {
- slotStr = Enum.GetName(typeof(Amd64Registers), slot.RegisterNumber);
- }
- else
- {
- slotStr = $"sp{slot.StackSlot.SpOffset:+#;-#;+0}";
- }
- string isLiveStr = "live";
- if (!IsLive)
- isLiveStr = "dead";
- return $"{slotStr} is {isLiveStr}";
- }
- }
-
public struct SafePointOffset
{
[XmlAttribute("Index")]
@@ -124,7 +62,6 @@ namespace R2RDump
private GcInfoTypes _gcInfoTypes;
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; }
@@ -141,11 +78,6 @@ namespace R2RDump
public List<SafePointOffset> SafePointOffsets { get; set; }
public List<InterruptibleRange> InterruptibleRanges { get; set; }
public GcSlotTable SlotTable { get; set; }
- public int Size { get; set; }
- public int Offset { get; set; }
-
- [XmlIgnore]
- public Dictionary<int, GcTransition> Transitions { get; set; }
public GcInfo() { }
@@ -292,7 +224,7 @@ namespace R2RDump
}
if (StackBaseRegister != 0xffffffff)
- sb.AppendLine($"\tStackBaseRegister: {(Amd64Registers)StackBaseRegister}");
+ sb.AppendLine($"\tStackBaseRegister: {(Registers)StackBaseRegister}");
if (_machine == Machine.Amd64)
{
sb.AppendLine($"\tWants Report Only Leaf: {_wantsReportOnlyLeaf}");
@@ -322,9 +254,12 @@ namespace R2RDump
sb.AppendLine($"\tSlotTable:");
sb.Append(SlotTable.ToString());
sb.AppendLine($"\tTransitions:");
- foreach (GcTransition trans in Transitions.Values)
+ foreach (List<BaseGcTransition> transList in Transitions.Values)
{
- sb.AppendLine(trans.ToString());
+ foreach (GcTransition trans in transList)
+ {
+ sb.AppendLine("\t\t" + trans.ToString());
+ }
}
sb.AppendLine($"\tSize: {Size} bytes");
@@ -398,7 +333,7 @@ namespace R2RDump
return (readyToRunMajorVersion == 1) ? 1 : GCINFO_VERSION;
}
- public Dictionary<int, GcTransition> GetTranstions(byte[] image, ref int bitOffset)
+ public Dictionary<int, List<BaseGcTransition>> GetTranstions(byte[] image, ref int bitOffset)
{
int totalInterruptibleLength = 0;
if (NumInterruptibleRanges == 0)
@@ -417,7 +352,7 @@ namespace R2RDump
int numBitsPerPointer = (int)NativeReader.DecodeVarLengthUnsigned(image, _gcInfoTypes.POINTER_SIZE_ENCBASE, ref bitOffset);
if (numBitsPerPointer == 0)
{
- return new Dictionary<int, GcTransition>();
+ return new Dictionary<int, List<BaseGcTransition>>();
}
int[] chunkPointers = new int[numChunks];
@@ -468,7 +403,7 @@ namespace R2RDump
while (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
{
int transitionOffset = NativeReader.ReadBits(image, _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ref bitOffset) + normChunkBaseCodeOffset;
- transitions.Add(new GcTransition(transitionOffset, slotId, isLive, currentChunk));
+ transitions.Add(new GcTransition(transitionOffset, slotId, isLive, currentChunk, SlotTable));
isLive = !isLive;
}
slotId++;
@@ -545,9 +480,9 @@ namespace R2RDump
return slotId;
}
- private Dictionary<int, GcTransition> UpdateTransitionCodeOffset(List<GcTransition> transitions)
+ private Dictionary<int, List<BaseGcTransition>> UpdateTransitionCodeOffset(List<GcTransition> transitions)
{
- Dictionary<int, GcTransition> updatedTransitions = new Dictionary<int, GcTransition>();
+ Dictionary<int, List<BaseGcTransition>> updatedTransitions = new Dictionary<int, List<BaseGcTransition>>();
int cumInterruptibleLength = 0;
using (IEnumerator<InterruptibleRange> interruptibleRangesIter = InterruptibleRanges.GetEnumerator())
{
@@ -566,7 +501,11 @@ namespace R2RDump
codeOffset = transition.CodeOffset + (int)currentRange.StartOffset - cumInterruptibleLength;
}
transition.CodeOffset = codeOffset;
- updatedTransitions[codeOffset] = transition;
+ if (!updatedTransitions.ContainsKey(codeOffset))
+ {
+ updatedTransitions[codeOffset] = new List<BaseGcTransition>();
+ }
+ updatedTransitions[codeOffset].Add(transition);
}
}
return updatedTransitions;
diff --git a/src/tools/r2rdump/GCSlotTable.cs b/src/tools/r2rdump/Amd64/GcSlotTable.cs
index 3be9a3bac6..a2bec0fc4e 100644
--- a/src/tools/r2rdump/GCSlotTable.cs
+++ b/src/tools/r2rdump/Amd64/GcSlotTable.cs
@@ -8,11 +8,11 @@ using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml.Serialization;
-namespace R2RDump
+namespace R2RDump.Amd64
{
public class GcSlotTable
{
- public struct GcSlot
+ public class GcSlot
{
[XmlAttribute("Index")]
public int Index { get; set; }
@@ -20,6 +20,8 @@ namespace R2RDump
public GcStackSlot StackSlot { get; set; }
public GcSlotFlags Flags { get; set; }
+ public GcSlot() { }
+
public GcSlot(int index, int registerNumber, GcStackSlot stack, GcSlotFlags flags, bool isUntracked = false)
{
Index = index;
diff --git a/src/tools/r2rdump/Amd64/GcTransition.cs b/src/tools/r2rdump/Amd64/GcTransition.cs
new file mode 100644
index 0000000000..0fb1e7fa84
--- /dev/null
+++ b/src/tools/r2rdump/Amd64/GcTransition.cs
@@ -0,0 +1,67 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.Amd64
+{
+ public struct InterruptibleRange
+ {
+ [XmlAttribute("Index")]
+ public uint Index { get; set; }
+ public uint StartOffset { get; set; }
+ public uint StopOffset { get; set; }
+ public InterruptibleRange(uint index, uint start, uint stop)
+ {
+ Index = index;
+ StartOffset = start;
+ StopOffset = stop;
+ }
+ }
+
+ public class GcTransition : BaseGcTransition
+ {
+ public int SlotId { get; set; }
+ public bool IsLive { get; set; }
+ public int ChunkId { get; set; }
+ public string SlotState { get; set; }
+
+ public GcTransition() { }
+
+ public GcTransition(int codeOffset, int slotId, bool isLive, int chunkId, GcSlotTable slotTable)
+ {
+ CodeOffset = codeOffset;
+ SlotId = slotId;
+ IsLive = isLive;
+ ChunkId = chunkId;
+ SlotState = GetSlotState(slotTable);
+ }
+
+ public override string ToString()
+ {
+ return SlotState;
+ }
+
+ public string GetSlotState(GcSlotTable slotTable)
+ {
+ GcSlotTable.GcSlot slot = slotTable.GcSlots[SlotId];
+ string slotStr = "";
+ if (slot.StackSlot == null)
+ {
+ slotStr = Enum.GetName(typeof(Registers), slot.RegisterNumber);
+ }
+ else
+ {
+ slotStr = $"sp{slot.StackSlot.SpOffset:+#;-#;+0}";
+ }
+ string isLiveStr = "live";
+ if (!IsLive)
+ isLiveStr = "dead";
+ return $"{slotStr} is {isLiveStr}";
+ }
+ }
+}
diff --git a/src/tools/r2rdump/Amd64/Registers.cs b/src/tools/r2rdump/Amd64/Registers.cs
new file mode 100644
index 0000000000..e55bef83b4
--- /dev/null
+++ b/src/tools/r2rdump/Amd64/Registers.cs
@@ -0,0 +1,30 @@
+// 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.Text;
+
+namespace R2RDump.Amd64
+{
+ public enum Registers
+ {
+ EAX = 0,
+ ECX = 1,
+ EDX = 2,
+ EBX = 3,
+ ESP = 4,
+ EBP = 5,
+ ESI = 6,
+ EDI = 7,
+ E8 = 8,
+ E9 = 9,
+ E10 = 10,
+ E11 = 11,
+ E12 = 12,
+ E13 = 13,
+ E14 = 14,
+ E15 = 15,
+ }
+}
diff --git a/src/tools/r2rdump/Amd64/UnwindInfo.cs b/src/tools/r2rdump/Amd64/UnwindInfo.cs
index 1bc8308ca6..6c4755d8bb 100644
--- a/src/tools/r2rdump/Amd64/UnwindInfo.cs
+++ b/src/tools/r2rdump/Amd64/UnwindInfo.cs
@@ -71,7 +71,7 @@ namespace R2RDump.Amd64
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 Registers FrameRegister { get; set; } //4 bits
public byte FrameOffset { get; set; } //4 bits
public UnwindCode[] UnwindCode { get; set; }
public uint PersonalityRoutineRVA { get; set; }
@@ -86,7 +86,7 @@ namespace R2RDump.Amd64
SizeOfProlog = NativeReader.ReadByte(image, ref offset);
CountOfUnwindCodes = NativeReader.ReadByte(image, ref offset);
byte frameRegisterAndOffset = NativeReader.ReadByte(image, ref offset);
- FrameRegister = (Amd64Registers)(frameRegisterAndOffset & 15);
+ FrameRegister = (Registers)(frameRegisterAndOffset & 15);
FrameOffset = (byte)(frameRegisterAndOffset >> 4);
UnwindCode = new UnwindCode[CountOfUnwindCodes];
@@ -150,7 +150,7 @@ namespace R2RDump.Amd64
switch (UnwindCode[i].UnwindOp)
{
case UnwindOpCodes.UWOP_PUSH_NONVOL:
- sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+ sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
break;
case UnwindOpCodes.UWOP_ALLOC_LARGE:
sb.Append($"\t\tOpInfo: {UnwindCode[i].OpInfo} - ");
@@ -198,7 +198,7 @@ namespace R2RDump.Amd64
break;
case UnwindOpCodes.UWOP_SAVE_NONVOL:
{
- sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+ sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
i++;
uint offset = UnwindCode[i].FrameOffset * 8;
sb.AppendLine($"\t\tScaled Offset: {UnwindCode[i].FrameOffset} * 8 = {offset} = 0x{offset:X5}");
@@ -206,7 +206,7 @@ namespace R2RDump.Amd64
break;
case UnwindOpCodes.UWOP_SAVE_NONVOL_FAR:
{
- sb.AppendLine($"\t\tOpInfo: {(Amd64Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+ sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
i++;
uint offset = UnwindCode[i].FrameOffset;
i++;
diff --git a/src/tools/r2rdump/GCInfoTypes.cs b/src/tools/r2rdump/GCInfoTypes.cs
index a285ebba5a..0cc8513952 100644
--- a/src/tools/r2rdump/GCInfoTypes.cs
+++ b/src/tools/r2rdump/GCInfoTypes.cs
@@ -8,6 +8,62 @@ using System.Text;
namespace R2RDump
{
+ enum InfoHdrAdjustConstants
+ {
+ // Constants
+ SET_FRAMESIZE_MAX = 7,
+ SET_ARGCOUNT_MAX = 8,
+ SET_PROLOGSIZE_MAX = 16,
+ SET_EPILOGSIZE_MAX = 10,
+ SET_EPILOGCNT_MAX = 4,
+ SET_UNTRACKED_MAX = 3,
+ SET_RET_KIND_MAX = 4,
+ ADJ_ENCODING_MAX = 0x7f,
+ MORE_BYTES_TO_FOLLOW = 0x80
+ };
+
+ //
+ // Enum to define codes that are used to incrementally adjust the InfoHdr structure.
+ // First set of opcodes
+ enum InfoHdrAdjust
+ {
+ SET_FRAMESIZE = 0, // 0x00
+ SET_ARGCOUNT = SET_FRAMESIZE + InfoHdrAdjustConstants.SET_FRAMESIZE_MAX + 1, // 0x08
+ SET_PROLOGSIZE = SET_ARGCOUNT + InfoHdrAdjustConstants.SET_ARGCOUNT_MAX + 1, // 0x11
+ SET_EPILOGSIZE = SET_PROLOGSIZE + InfoHdrAdjustConstants.SET_PROLOGSIZE_MAX + 1, // 0x22
+ SET_EPILOGCNT = SET_EPILOGSIZE + InfoHdrAdjustConstants.SET_EPILOGSIZE_MAX + 1, // 0x2d
+ SET_UNTRACKED = SET_EPILOGCNT + (InfoHdrAdjustConstants.SET_EPILOGCNT_MAX + 1) * 2, // 0x37
+
+ FIRST_FLIP = SET_UNTRACKED + InfoHdrAdjustConstants.SET_UNTRACKED_MAX + 1,
+
+ FLIP_EDI_SAVED = FIRST_FLIP, // 0x3b
+ FLIP_ESI_SAVED, // 0x3c
+ FLIP_EBX_SAVED, // 0x3d
+ FLIP_EBP_SAVED, // 0x3e
+ FLIP_EBP_FRAME, // 0x3f
+ FLIP_INTERRUPTIBLE, // 0x40
+ FLIP_DOUBLE_ALIGN, // 0x41
+ FLIP_SECURITY, // 0x42
+ FLIP_HANDLERS, // 0x43
+ FLIP_LOCALLOC, // 0x44
+ FLIP_EDITnCONTINUE, // 0x45
+ FLIP_VAR_PTR_TABLE_SZ, // 0x46 Flip whether a table-size exits after the header encoding
+ FFFF_UNTRACKED_CNT, // 0x47 There is a count (>SET_UNTRACKED_MAX) after the header encoding
+ FLIP_VARARGS, // 0x48
+ FLIP_PROF_CALLBACKS, // 0x49
+ FLIP_HAS_GS_COOKIE, // 0x4A - The offset of the GuardStack cookie follows after the header encoding
+ FLIP_SYNC, // 0x4B
+ FLIP_HAS_GENERICS_CONTEXT,// 0x4C
+ FLIP_GENERICS_CONTEXT_IS_METHODDESC,// 0x4D
+ FLIP_REV_PINVOKE_FRAME, // 0x4E
+ NEXT_OPCODE, // 0x4F -- see next Adjustment enumeration
+ NEXT_FOUR_START = 0x50,
+ NEXT_FOUR_FRAMESIZE = 0x50,
+ NEXT_FOUR_ARGCOUNT = 0x60,
+ NEXT_THREE_PROLOGSIZE = 0x70,
+ NEXT_THREE_EPILOGSIZE = 0x78
+ };
+
public class GcInfoTypes
{
private Machine _target;
diff --git a/src/tools/r2rdump/NativeReader.cs b/src/tools/r2rdump/NativeReader.cs
index c55d48ba55..654fe843f0 100644
--- a/src/tools/r2rdump/NativeReader.cs
+++ b/src/tools/r2rdump/NativeReader.cs
@@ -322,5 +322,30 @@ namespace R2RDump
}
return value;
}
+
+ public static int DecodeSigned(byte[] image, ref int start)
+ {
+ int size = 1;
+ byte data = image[start++];
+ byte first = data;
+ int value = data & 0x3f;
+ while ((data & 0x80) != 0)
+ {
+ size++;
+ data = image[start++];
+ value <<= 7;
+ value += data & 0x7f;
+ }
+ if ((first & 0x40) != 0)
+ value = -value;
+
+ return value;
+ }
+
+ public static uint DecodeUDelta(byte[] image, ref int start, uint lastValue)
+ {
+ uint delta = DecodeUnsignedGc(image, ref start);
+ return lastValue + delta;
+ }
}
}
diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs
index 9341c44164..8034956078 100644
--- a/src/tools/r2rdump/R2RDump.cs
+++ b/src/tools/r2rdump/R2RDump.cs
@@ -15,6 +15,8 @@ namespace R2RDump
public abstract class Dumper
{
internal R2RReader _r2r;
+ internal TextWriter _writer;
+
internal bool _raw;
internal bool _header;
internal bool _disasm;
@@ -22,9 +24,15 @@ namespace R2RDump
internal bool _unwind;
internal bool _gc;
internal bool _sectionContents;
- internal TextWriter _writer;
+ /// <summary>
+ /// Run right before printing output
+ /// </summary>
abstract internal void Begin();
+
+ /// <summary>
+ /// Run right after printing output
+ /// </summary>
abstract internal void End();
abstract internal void WriteDivider(string title);
abstract internal void WriteSubDivider();
@@ -42,6 +50,7 @@ namespace R2RDump
class R2RDump
{
+ // Options set by user specifying what to dump
private bool _help;
private IReadOnlyList<string> _inputFilenames = Array.Empty<string>();
private string _outputFilename = null;
@@ -66,6 +75,9 @@ namespace R2RDump
{
}
+ /// <summary>
+ /// Parse commandline options
+ /// </summary>
private ArgumentSyntax ParseCommandLine(string[] args)
{
bool verbose = false;
@@ -132,13 +144,17 @@ namespace R2RDump
return int.TryParse(arg, out n);
}
+ /// <summary>
+ /// Outputs a warning message
+ /// </summary>
+ /// <param name="warning">The warning message to output</param>
public static void WriteWarning(string warning)
{
Console.WriteLine($"Warning: {warning}");
}
// <summary>
- /// For each query in the list of queries, search for all methods matching the query by name, signature or id
+ /// For each query in the list of queries, dump all methods matching the query by name, signature or id
/// </summary>
/// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
/// <param name="title">The title to print, "R2R Methods by Query" or "R2R Methods by Keyword"</param>
@@ -162,7 +178,7 @@ namespace R2RDump
}
// <summary>
- /// For each query in the list of queries, search for all sections by the name or value of the ReadyToRunSectionType enum
+ /// For each query in the list of queries, dump all sections by the name or value of the ReadyToRunSectionType enum
/// </summary>
/// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
/// <param name="queries">The names/values to search for</param>
@@ -184,7 +200,7 @@ namespace R2RDump
}
// <summary>
- /// For each query in the list of queries, search for a runtime function by id.
+ /// For each query in the list of queries, dump a runtime function by id.
/// The method containing the runtime function gets outputted, along with the single runtime function that was searched
/// </summary>
/// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
@@ -218,7 +234,7 @@ namespace R2RDump
_dumper.Begin();
- if (_queries.Count == 0 && _keywords.Count == 0 && _runtimeFunctions.Count == 0 && _sections.Count == 0) //dump all sections and methods
+ if (_queries.Count == 0 && _keywords.Count == 0 && _runtimeFunctions.Count == 0 && _sections.Count == 0) //dump all sections and methods if no queries specified
{
_dumper.WriteDivider("R2R Header");
_dumper.DumpHeader(true);
@@ -228,7 +244,7 @@ namespace R2RDump
_dumper.DumpAllMethods();
}
}
- else //dump queried sections/methods/runtimeFunctions
+ else //dump queried sections, methods and runtimeFunctions
{
if (_header)
{
@@ -245,7 +261,7 @@ namespace R2RDump
}
/// <summary>
- /// Returns true if the name/signature/id of <param>method</param> matches <param>query</param>
+ /// Returns true if the name, signature or id of <param>method</param> matches <param>query</param>
/// </summary>
/// <param name="exact">Specifies exact or partial match</param>
/// <remarks>Case-insensitive and ignores whitespace</remarks>
diff --git a/src/tools/r2rdump/R2RImportSection.cs b/src/tools/r2rdump/R2RImportSection.cs
index 1029d73893..065c092ff4 100644
--- a/src/tools/r2rdump/R2RImportSection.cs
+++ b/src/tools/r2rdump/R2RImportSection.cs
@@ -95,7 +95,8 @@ namespace R2RDump
/// RVA of optional auxiliary data (typically GC info)
/// </summary>
public int AuxiliaryDataRVA { get; set; }
- public GcInfo AuxiliaryData { 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)
{
@@ -113,7 +114,14 @@ namespace R2RDump
AuxiliaryData = null;
if (AuxiliaryDataRVA != 0)
{
- AuxiliaryData = new GcInfo(image, auxDataOffset, machine, majorVersion);
+ if (machine == Machine.Amd64)
+ {
+ AuxiliaryData = new Amd64.GcInfo(image, auxDataOffset, machine, majorVersion);
+ }
+ else if (machine == Machine.I386)
+ {
+ AuxiliaryData = new x86.GcInfo(image, auxDataOffset, machine, majorVersion);
+ }
}
}
diff --git a/src/tools/r2rdump/R2RMethod.cs b/src/tools/r2rdump/R2RMethod.cs
index fa8ba27c04..3650e03346 100644
--- a/src/tools/r2rdump/R2RMethod.cs
+++ b/src/tools/r2rdump/R2RMethod.cs
@@ -18,6 +18,28 @@ namespace R2RDump
public int Size { get; set; }
}
+ public abstract class BaseGcTransition
+ {
+ [XmlAttribute("Index")]
+ public int CodeOffset { get; set; }
+
+ public BaseGcTransition() { }
+
+ public BaseGcTransition(int codeOffset)
+ {
+ CodeOffset = codeOffset;
+ }
+ }
+
+ public abstract class BaseGcInfo
+ {
+ public int Size { get; set; }
+ public int Offset { get; set; }
+ public int CodeLength { get; set; }
+ [XmlIgnore]
+ public Dictionary<int, List<BaseGcTransition>> Transitions { get; set; }
+ }
+
public class RuntimeFunction
{
/// <summary>
@@ -56,7 +78,7 @@ namespace R2RDump
public RuntimeFunction() { }
- public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, int codeOffset, R2RMethod method, BaseUnwindInfo unwindInfo, GcInfo gcInfo)
+ public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, int codeOffset, R2RMethod method, BaseUnwindInfo unwindInfo, BaseGcInfo gcInfo)
{
Id = id;
StartAddress = startRva;
@@ -152,7 +174,7 @@ namespace R2RDump
public int EntryPointRuntimeFunctionId { get; set; }
[XmlIgnore]
- public GcInfo GcInfo { get; set; }
+ public BaseGcInfo GcInfo { get; set; }
public FixupCell[] Fixups { get; set; }
diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs
index a2a1754fe6..0251568d3f 100644
--- a/src/tools/r2rdump/R2RReader.cs
+++ b/src/tools/r2rdump/R2RReader.cs
@@ -13,26 +13,6 @@ using System.Xml.Serialization;
namespace R2RDump
{
- public enum Amd64Registers
- {
- EAX = 0,
- ECX = 1,
- EDX = 2,
- EBX = 3,
- ESP = 4,
- EBP = 5,
- ESI = 6,
- EDI = 7,
- E8 = 8,
- E9 = 9,
- E10 = 10,
- E11 = 11,
- E12 = 12,
- E13 = 13,
- E14 = 14,
- E15 = 15,
- }
-
/// <summary>
/// This structure represents a single precode fixup cell decoded from the
/// nibble-oriented per-method fixup blob. Each method entrypoint fixup
@@ -292,7 +272,7 @@ namespace R2RDump
if (runtimeFunctionId == -1)
continue;
curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize;
- GcInfo gcInfo = null;
+ BaseGcInfo gcInfo = null;
int codeOffset = 0;
do
{
@@ -311,7 +291,7 @@ namespace R2RDump
unwindInfo = new Amd64.UnwindInfo(Image, unwindOffset);
if (isEntryPoint[runtimeFunctionId])
{
- gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
+ gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
}
}
else if (Machine == Machine.I386)
@@ -319,7 +299,7 @@ namespace R2RDump
unwindInfo = new x86.UnwindInfo(Image, unwindOffset);
if (isEntryPoint[runtimeFunctionId])
{
- //gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
+ gcInfo = new x86.GcInfo(Image, unwindOffset, Machine, R2RHeader.MajorVersion);
}
}
diff --git a/src/tools/r2rdump/TextDumper.cs b/src/tools/r2rdump/TextDumper.cs
index 41bd9fae80..cf3d942d4f 100644
--- a/src/tools/r2rdump/TextDumper.cs
+++ b/src/tools/r2rdump/TextDumper.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
@@ -161,7 +162,7 @@ namespace R2RDump
_writer.Write(rtf.UnwindInfo);
if (_raw)
{
- DumpBytes(rtf.UnwindRVA, (uint)((Amd64.UnwindInfo)rtf.UnwindInfo).Size);
+ DumpBytes(rtf.UnwindRVA, (uint)rtf.UnwindInfo.Size);
}
}
SkipLine();
@@ -179,7 +180,10 @@ namespace R2RDump
_writer.Write(instr);
if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
{
- _writer.WriteLine($"\t\t\t\t{rtf.Method.GcInfo.Transitions[codeOffset].GetSlotState(rtf.Method.GcInfo.SlotTable)}");
+ foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
+ {
+ _writer.WriteLine($"\t\t\t\t{transition.ToString()}");
+ }
}
CoreDisTools.ClearOutputBuffer();
diff --git a/src/tools/r2rdump/XmlDumper.cs b/src/tools/r2rdump/XmlDumper.cs
index 3676baad2f..10cde54277 100644
--- a/src/tools/r2rdump/XmlDumper.cs
+++ b/src/tools/r2rdump/XmlDumper.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
@@ -156,9 +157,12 @@ namespace R2RDump
methodNode.AppendChild(gcNode);
Serialize(method.GcInfo, gcNode);
- foreach (GcInfo.GcTransition transition in method.GcInfo.Transitions.Values)
+ foreach (List<BaseGcTransition> transitionList in method.GcInfo.Transitions.Values)
{
- Serialize(transition, gcNode);
+ foreach (BaseGcTransition transition in transitionList)
+ {
+ Serialize(transition, gcNode);
+ }
}
if (_raw)
@@ -215,17 +219,20 @@ namespace R2RDump
{
int rtfOffset = 0;
int codeOffset = rtf.CodeOffset;
- Dictionary<int, GcInfo.GcTransition> transitions = rtf.Method.GcInfo.Transitions;
- GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable;
+
while (rtfOffset < rtf.Size)
{
string instr;
int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);
- AddXMLNode("offset"+codeOffset, instr, parentNode, $"{codeOffset}");
- if (transitions.ContainsKey(codeOffset))
+ AddXMLNode("offset" + codeOffset, instr, parentNode, $"{codeOffset}");
+
+ if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
{
- AddXMLNode("Transition", transitions[codeOffset].GetSlotState(slotTable), parentNode, $"{codeOffset}");
+ foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
+ {
+ AddXMLNode("Transition", transition.ToString(), parentNode, $"{codeOffset}");
+ }
}
CoreDisTools.ClearOutputBuffer();
diff --git a/src/tools/r2rdump/x86/CallPattern.cs b/src/tools/r2rdump/x86/CallPattern.cs
new file mode 100644
index 0000000000..cc8a3c30cd
--- /dev/null
+++ b/src/tools/r2rdump/x86/CallPattern.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace R2RDump.x86
+{
+ class CallPattern
+ {
+ public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint regMask, out uint argMask, out uint codeDelta)
+ {
+ uint val = callPatternTable[pattern];
+ byte[] fld = BitConverter.GetBytes(val);
+ argCnt = fld[0];
+ regMask = fld[1]; // EBP,EBX,ESI,EDI
+ argMask = fld[2];
+ codeDelta = fld[3];
+ }
+
+ public static uint[] callCommonDelta = { 6, 8, 10, 12 };
+
+ private static uint[] callPatternTable =
+ {
+ 0x0a000200, // 30109
+ 0x0c000200, // 22970
+ 0x0c000201, // 19005
+ 0x0a000300, // 12193
+ 0x0c000300, // 10614
+ 0x0e000200, // 10253
+ 0x10000200, // 9746
+ 0x0b000200, // 9698
+ 0x0d000200, // 9625
+ 0x08000200, // 8909
+ 0x0c000301, // 8522
+ 0x11000200, // 7382
+ 0x0e000300, // 7357
+ 0x12000200, // 7139
+ 0x10000300, // 7062
+ 0x11000300, // 6970
+ 0x0a000201, // 6842
+ 0x0a000100, // 6803
+ 0x0f000200, // 6795
+ 0x13000200, // 6559
+ 0x08000300, // 6079
+ 0x15000200, // 5874
+ 0x0d000201, // 5492
+ 0x0c000100, // 5193
+ 0x0d000300, // 5165
+ 0x23000200, // 5143
+ 0x1b000200, // 5035
+ 0x14000200, // 4872
+ 0x0f000300, // 4850
+ 0x0a000700, // 4781
+ 0x09000200, // 4560
+ 0x12000300, // 4496
+ 0x16000200, // 4180
+ 0x07000200, // 4021
+ 0x09000300, // 4012
+ 0x0c000700, // 3988
+ 0x0c000600, // 3946
+ 0x0e000100, // 3823
+ 0x1a000200, // 3764
+ 0x18000200, // 3744
+ 0x17000200, // 3736
+ 0x1f000200, // 3671
+ 0x13000300, // 3559
+ 0x0a000600, // 3214
+ 0x0e000600, // 3109
+ 0x08000201, // 2984
+ 0x0b000300, // 2928
+ 0x0a000301, // 2859
+ 0x07000100, // 2826
+ 0x13000100, // 2782
+ 0x09000301, // 2644
+ 0x19000200, // 2638
+ 0x11000700, // 2618
+ 0x21000200, // 2518
+ 0x0d000202, // 2484
+ 0x10000100, // 2480
+ 0x0f000600, // 2413
+ 0x14000300, // 2363
+ 0x0c000500, // 2362
+ 0x08000301, // 2285
+ 0x20000200, // 2245
+ 0x10000700, // 2240
+ 0x0f000100, // 2236
+ 0x1e000200, // 2214
+ 0x0c000400, // 2193
+ 0x16000300, // 2171
+ 0x12000600, // 2132
+ 0x22000200, // 2011
+ 0x1d000200, // 2011
+ 0x0c000f00, // 1996
+ 0x0e000700, // 1971
+ 0x0a000400, // 1970
+ 0x09000201, // 1932
+ 0x10000600, // 1903
+ 0x15000300, // 1847
+ 0x0a000101, // 1814
+ 0x0a000b00, // 1771
+ 0x0c000601, // 1737
+ 0x09000700, // 1737
+ 0x07000300, // 1684
+ };
+ }
+}
diff --git a/src/tools/r2rdump/x86/GcInfo.cs b/src/tools/r2rdump/x86/GcInfo.cs
new file mode 100644
index 0000000000..e0896510fd
--- /dev/null
+++ b/src/tools/r2rdump/x86/GcInfo.cs
@@ -0,0 +1,496 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.x86
+{
+ public class GcInfo : BaseGcInfo
+ {
+ const uint byref_OFFSET_FLAG = 0x1;
+
+ public InfoHdrSmall Header { get; set; }
+ public GcSlotTable SlotTable { get; set; }
+
+ public GcInfo() { }
+
+ public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
+ {
+ Offset = offset;
+
+ CodeLength = (int)NativeReader.DecodeUnsignedGc(image, ref offset);
+
+ Header = InfoHdrDecoder.DecodeHeader(image, ref offset, CodeLength);
+
+ SlotTable = new GcSlotTable(image, Header, ref offset);
+
+ Transitions = new Dictionary<int, List<BaseGcTransition>>();
+ if (Header.Interruptible)
+ {
+ GetTransitionsFullyInterruptible(image, ref offset);
+ }
+ else if (Header.EbpFrame)
+ {
+ GetTransitionsEbpFrame(image, ref offset);
+ }
+ else
+ {
+ GetTransitionsNoEbp(image, ref offset);
+ }
+
+ Size = offset - Offset;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendLine($"\tCodeLength: {CodeLength} bytes");
+ sb.AppendLine($"\tInfoHdr:");
+ sb.AppendLine($"{Header}");
+ sb.AppendLine($"{SlotTable}");
+
+ sb.AppendLine($"\tSize: {Size} bytes");
+
+ return sb.ToString();
+ }
+
+ private void AddNewTransition(BaseGcTransition transition)
+ {
+ if (!Transitions.ContainsKey(transition.CodeOffset))
+ {
+ Transitions[transition.CodeOffset] = new List<BaseGcTransition>();
+ }
+ Transitions[transition.CodeOffset].Add(transition);
+ }
+
+ private void ArgEncoding(byte[] image, ref uint isPop, ref uint argOffs, ref uint argCnt, ref uint curOffs, ref bool isThis, ref bool iptr)
+ {
+ if (isPop != 0)
+ {
+ // A Pop of 0, means little-delta
+
+ if (argOffs != 0)
+ {
+ AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt - argOffs, Action.POP, Header.EbpFrame));
+ }
+ }
+ else
+ {
+ AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argOffs + 1, Action.PUSH, Header.EbpFrame, isThis, iptr));
+ isThis = false;
+ iptr = false;
+ }
+ }
+
+ private void GetTransitionsFullyInterruptible(byte[] image, ref int offset)
+ {
+ uint argCnt = 0;
+ bool isThis = false;
+ bool iptr = false;
+ uint curOffs = 0;
+
+ while (true)
+ {
+ uint isPop;
+ uint argOffs;
+ uint val = image[offset++];
+
+ if ((val & 0x80) == 0)
+ {
+ /* A small 'regPtr' encoding */
+
+ curOffs += val & 0x7;
+
+ Action isLive = Action.LIVE;
+ if ((val & 0x40) == 0)
+ isLive = Action.DEAD;
+ AddNewTransition(new GcTransitionRegister((int)curOffs, (Registers)((val >> 3) & 7), isLive, isThis, iptr));
+
+ isThis = false;
+ iptr = false;
+ continue;
+ }
+
+ /* This is probably an argument push/pop */
+
+ argOffs = (val & 0x38) >> 3;
+
+ /* 6 [110] and 7 [111] are reserved for other encodings */
+
+ if (argOffs < 6)
+ {
+ /* A small argument encoding */
+
+ curOffs += (val & 0x07);
+ isPop = (val & 0x40);
+
+ ArgEncoding(image, ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr);
+
+ continue;
+ }
+ else if (argOffs == 6)
+ {
+ if ((val & 0x40) != 0)
+ {
+ curOffs += (((val & 0x07) + 1) << 3);
+ }
+ else
+ {
+ // non-ptr arg push
+
+ curOffs += (val & 0x07);
+ argCnt++;
+ AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.PUSH, Header.EbpFrame, false, false, false));
+ }
+
+ continue;
+ }
+
+ // argOffs was 7 [111] which is reserved for the larger encodings
+ switch (val)
+ {
+ case 0xFF:
+ return;
+ case 0xBC:
+ isThis = true;
+ break;
+ case 0xBF:
+ iptr = true;
+ break;
+ case 0xB8:
+ val = NativeReader.DecodeUnsignedGc(image, ref offset);
+ curOffs += val;
+ break;
+ case 0xF8:
+ case 0xFC:
+ isPop = val & 0x04;
+ argOffs = NativeReader.DecodeUnsignedGc(image, ref offset);
+ ArgEncoding(image, ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr);
+ break;
+ case 0xFD:
+ argOffs = NativeReader.DecodeUnsignedGc(image, ref offset);
+ AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.KILL, Header.EbpFrame));
+ break;
+ case 0xF9:
+ argOffs = NativeReader.DecodeUnsignedGc(image, ref offset);
+ argCnt += argOffs;
+ break;
+ default:
+ throw new BadImageFormatException($"Unexpected special code {val}");
+ }
+ }
+ }
+
+ private void GetTransitionsEbpFrame(byte[] image, ref int offset)
+ {
+ while (true)
+ {
+ uint argMask = 0, byrefArgMask = 0;
+ uint regMask, byrefRegMask = 0;
+
+ uint argCnt = 0;
+ int argOffset = offset;
+ uint argTabSize;
+
+ uint val, nxt;
+ uint curOffs = 0;
+
+ // Get the next byte and check for a 'special' entry
+ uint encType = image[offset++];
+ GcTransitionCall transition = null;
+
+ switch (encType)
+ {
+ default:
+ // A tiny or small call entry
+ val = encType;
+
+ if ((val & 0x80) == 0x00)
+ {
+ if ((val & 0x0F) != 0)
+ {
+ // A tiny call entry
+
+ curOffs += (val & 0x0F);
+ regMask = (val & 0x70) >> 4;
+ argMask = 0;
+ }
+ else
+ {
+ Registers reg;
+ if ((val & 0x10) != 0)
+ reg = Registers.EDI;
+ else if ((val & 0x20) != 0)
+ reg = Registers.ESI;
+ else if ((val & 0x40) != 0)
+ reg = Registers.EBX;
+ else
+ throw new BadImageFormatException("Invalid register");
+ transition = new GcTransitionCall((int)curOffs);
+ transition.CallRegisters.Add(new GcTransitionCall.CallRegister(reg, false));
+ AddNewTransition(transition);
+
+ continue;
+ }
+ }
+ else
+ {
+ // A small call entry
+ curOffs += (val & 0x7F);
+ val = image[offset++];
+ regMask = val >> 5;
+ argMask = val & 0x1F;
+ }
+ break;
+
+ case 0xFD: // medium encoding
+ argMask = image[offset++];
+ val = image[offset++];
+ argMask |= (val & 0xF0) << 4;
+ nxt = image[offset++];
+ curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4);
+ regMask = nxt >> 5; // EBX,ESI,EDI
+ break;
+
+ case 0xF9: // medium encoding with byrefs
+ curOffs += image[offset++];
+ val = image[offset++];
+ argMask = val & 0x1F;
+ regMask = val >> 5;
+ val = image[offset++];
+ byrefArgMask = val & 0x1F;
+ byrefRegMask = val >> 5;
+ break;
+ case 0xFE: // large encoding
+ case 0xFA: // large encoding with byrefs
+ val = image[offset++];
+ regMask = val & 0x7;
+ byrefRegMask = val >> 4;
+
+ curOffs += NativeReader.ReadUInt32(image, ref offset);
+ argMask = NativeReader.ReadUInt32(image, ref offset);
+ if (encType == 0xFA) // read byrefArgMask
+ {
+ byrefArgMask = NativeReader.ReadUInt32(image, ref offset);
+ }
+ break;
+ case 0xFB: // huge encoding
+ val = image[offset++];
+ regMask = val & 0x7;
+ byrefRegMask = val >> 4;
+ curOffs = NativeReader.ReadUInt32(image, ref offset);
+ argCnt = NativeReader.ReadUInt32(image, ref offset);
+ argTabSize = NativeReader.ReadUInt32(image, ref offset);
+ argOffset = offset;
+ offset += (int)argTabSize;
+ break;
+ case 0xFF:
+ return;
+ }
+
+ /*
+ Here we have the following values:
+
+ curOffs ... the code offset of the call
+ regMask ... mask of live pointer register variables
+ argMask ... bitmask of pushed pointer arguments
+ byrefRegMask ... byref qualifier for regMask
+ byrefArgMask ... byrer qualifier for argMask
+ */
+ transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, regMask, byrefRegMask);
+ AddNewTransition(transition);
+
+ if (argCnt != 0)
+ {
+ do
+ {
+ val = NativeReader.DecodeUnsignedGc(image, ref argOffset);
+
+ uint stkOffs = val & ~byref_OFFSET_FLAG;
+ uint lowBit = val & byref_OFFSET_FLAG;
+ transition.PtrArgs.Add(new GcTransitionCall.PtrArg(stkOffs, lowBit));
+ }
+ while (--argCnt > 0);
+ }
+ else
+ {
+ transition.ArgMask = argMask;
+ if (byrefArgMask != 0)
+ transition.IArgs = byrefArgMask;
+ }
+ }
+ }
+
+ private void SaveCallTransition(byte[] image, ref int offset, uint val, uint curOffs, uint callRegMask, bool callPndTab, uint callPndTabCnt, uint callPndMask, uint lastSkip, ref uint imask)
+ {
+ uint iregMask, iargMask;
+ iregMask = imask & 0xF;
+ iargMask = imask >> 4;
+
+ GcTransitionCall transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, callRegMask, iregMask);
+ AddNewTransition(transition);
+
+ if (callPndTab)
+ {
+ for (int i = 0; i < callPndTabCnt; i++)
+ {
+ uint pndOffs = NativeReader.DecodeUnsignedGc(image, ref offset);
+
+ uint stkOffs = val & ~byref_OFFSET_FLAG;
+ uint lowBit = val & byref_OFFSET_FLAG;
+ transition.PtrArgs.Add(new GcTransitionCall.PtrArg(pndOffs, 0));
+ }
+ }
+ else
+ {
+ if (callPndMask != 0)
+ transition.ArgMask = callPndMask;
+ if (iargMask != 0)
+ transition.IArgs = iargMask;
+ }
+
+ imask = lastSkip = 0;
+ }
+
+ private void GetTransitionsNoEbp(byte[] image, ref int offset)
+ {
+ uint curOffs = 0;
+ uint lastSkip = 0;
+ uint imask = 0;
+
+ for (; ; )
+ {
+ uint val = image[offset++];
+
+ if ((val & 0x80) == 0)
+ {
+ if ((val & 0x40) == 0)
+ {
+ if ((val & 0x20) == 0)
+ {
+ // push 000DDDDD push one item, 5-bit delta
+ curOffs += val & 0x1F;
+ AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.PUSH));
+ }
+ else
+ {
+ // push 00100000 [pushCount] ESP push multiple items
+ uint pushCount = NativeReader.DecodeUnsignedGc(image, ref offset);
+ AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.PUSH, false, false, (int)pushCount));
+ }
+ }
+ else
+ {
+ uint popSize;
+ uint skip;
+
+ if ((val & 0x3f) == 0)
+ {
+ //
+ // skip 01000000 [Delta] Skip arbitrary sized delta
+ //
+ skip = NativeReader.DecodeUnsignedGc(image, ref offset);
+ curOffs += skip;
+ lastSkip = skip;
+ }
+ else
+ {
+ // pop 01CCDDDD pop CC items, 4-bit delta
+ popSize = (val & 0x30) >> 4;
+ skip = val & 0x0f;
+ curOffs += skip;
+
+ if (popSize > 0)
+ {
+ AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.POP, false, false, (int)popSize));
+ }
+ else
+ lastSkip = skip;
+ }
+ }
+ }
+ else
+ {
+ uint callArgCnt;
+ uint callRegMask;
+ bool callPndTab = false;
+ uint callPndMask = 0;
+ uint callPndTabCnt = 0, callPndTabSize = 0;
+
+ switch ((val & 0x70) >> 4)
+ {
+ default:
+ //
+ // call 1PPPPPPP Call Pattern, P=[0..79]
+ //
+ CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip);
+ curOffs += lastSkip;
+ SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask);
+ break;
+
+ case 5:
+ //
+ // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC,
+ // ArgMask=MMM Delta=commonDelta[DD]
+ //
+ callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
+ val = image[offset++];
+ callPndMask = (val & 0x7);
+ callArgCnt = (val >> 3) & 0x7;
+ lastSkip = CallPattern.callCommonDelta[val >> 6];
+ curOffs += lastSkip;
+ SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask);
+ break;
+ case 6:
+ //
+ // call 1110RRRR [ArgCnt] [ArgMask]
+ // Call ArgCnt,RegMask=RRR,ArgMask
+ //
+ callRegMask = val & 0xf; // EBP,EBX,ESI,EDI
+ callArgCnt = NativeReader.DecodeUnsignedGc(image, ref offset);
+ callPndMask = NativeReader.DecodeUnsignedGc(image, ref offset);
+ SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask);
+ break;
+ case 7:
+ switch (val & 0x0C)
+ {
+ case 0x00:
+ // iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask
+ imask = NativeReader.DecodeUnsignedGc(image, ref offset);
+ AddNewTransition(new IPtrMask((int)curOffs, imask));
+ break;
+
+ case 0x04:
+ AddNewTransition(new CalleeSavedRegister((int)curOffs, (CalleeSavedRegisters)(val & 0x3)));
+ break;
+
+ case 0x08:
+ val = image[offset++];
+ callRegMask = val & 0xF;
+ imask = val >> 4;
+ lastSkip = NativeReader.ReadUInt32(image, ref offset);
+ curOffs += lastSkip;
+ callArgCnt = NativeReader.ReadUInt32(image, ref offset);
+ callPndTabCnt = NativeReader.ReadUInt32(image, ref offset);
+ callPndTabSize = NativeReader.ReadUInt32(image, ref offset);
+ callPndTab = true;
+ SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask);
+ break;
+ case 0x0C:
+ return;
+ default:
+ throw new BadImageFormatException("Invalid GC encoding");
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/tools/r2rdump/x86/GcSlotTable.cs b/src/tools/r2rdump/x86/GcSlotTable.cs
new file mode 100644
index 0000000000..d5486d8142
--- /dev/null
+++ b/src/tools/r2rdump/x86/GcSlotTable.cs
@@ -0,0 +1,186 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.x86
+{
+ public class GcSlotTable
+ {
+ public class GcSlot
+ {
+ [XmlAttribute("Index")]
+ public int Index { get; set; }
+ public string Register { get; set; }
+ public int StackOffset { get; set; }
+ public int LowBits { get; set; }
+ public GcSlotFlags Flags { get; set; }
+
+ public int BeginOffset { get; set; }
+ public int EndOffset { get; set; }
+
+ public GcSlot() { }
+
+ public GcSlot(int index, string reg, int stkOffs, int lowBits, GcSlotFlags flags)
+ {
+ Index = index;
+ Register = reg;
+ StackOffset = stkOffs;
+ LowBits = lowBits;
+ Flags = flags;
+
+ BeginOffset = -1;
+ EndOffset = -1;
+ }
+
+ public GcSlot(int index, string reg, int beginOffs, int endOffs, int varOffs, int lowBits, GcSlotFlags flags)
+ {
+ Index = index;
+ Register = $"E{reg}P";
+ StackOffset = varOffs;
+ LowBits = lowBits;
+ Flags = flags;
+
+ BeginOffset = beginOffs;
+ EndOffset = endOffs;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ if (Flags == GcSlotFlags.GC_SLOT_UNTRACKED)
+ {
+ if (StackOffset < 0)
+ {
+ sb.AppendLine($"\t\t\t[{Register}-{-StackOffset}]");
+ }
+ else
+ {
+ sb.AppendLine($"\t\t\t[{Register}+{StackOffset}]");
+ }
+ }
+ else
+ {
+ sb.AppendLine($"\t\t\tBeginOffset: {BeginOffset}");
+ sb.AppendLine($"\t\t\tEndOffset: {EndOffset}");
+ if (Register.Equals("BP"))
+ {
+ sb.AppendLine($"\t\t\t[{Register}-{-StackOffset}]");
+ }
+ else
+ {
+ sb.AppendLine($"\t\t\t[{Register}+{-StackOffset}]");
+ }
+ }
+
+ sb.AppendLine($"\t\t\tFlags: {Flags}");
+
+ sb.Append($"\t\t\tLowBits: ");
+ if (Flags == GcSlotFlags.GC_SLOT_UNTRACKED)
+ {
+ if((LowBits & pinned_OFFSET_FLAG) != 0) sb.Append("pinned ");
+ if ((LowBits & byref_OFFSET_FLAG) != 0) sb.Append("byref ");
+ }
+ sb.AppendLine();
+
+ return sb.ToString();
+ }
+ }
+
+ private const uint OFFSET_MASK = 0x3;
+ private const uint byref_OFFSET_FLAG = 0x1; // the offset is an interior ptr
+ private const uint pinned_OFFSET_FLAG = 0x2; // the offset is a pinned ptr
+
+ public List<GcSlot> GcSlots { get; set; }
+
+ public GcSlotTable() { }
+
+ public GcSlotTable(byte[] image, InfoHdrSmall header, ref int offset)
+ {
+ GcSlots = new List<GcSlot>();
+
+ DecodeUntracked(image, header, ref offset);
+ DecodeFrameVariableLifetimeTable(image, header, ref offset);
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendLine($"\t\tGcSlots:");
+ sb.AppendLine($"\t\t\t-------------------------");
+ foreach (GcSlot slot in GcSlots)
+ {
+ sb.Append(slot.ToString());
+ sb.AppendLine($"\t\t\t-------------------------");
+ }
+
+ return sb.ToString();
+ }
+
+ private void DecodeUntracked(byte[] image, InfoHdrSmall header, ref int offset)
+ {
+ uint calleeSavedRegs = 0;
+ if (header.DoubleAlign)
+ {
+ calleeSavedRegs = 0;
+ if (header.EdiSaved) calleeSavedRegs++;
+ if (header.EsiSaved) calleeSavedRegs++;
+ if (header.EbxSaved) calleeSavedRegs++;
+ }
+
+ uint count = header.UntrackedCnt;
+ int lastStkOffs = 0;
+ while (count-- > 0)
+ {
+ int stkOffsDelta;
+ int lowBits;
+
+ char reg = header.EbpFrame ? 'B' : 'S';
+
+ stkOffsDelta = NativeReader.DecodeSigned(image, ref offset);
+ int stkOffs = lastStkOffs - stkOffsDelta;
+ lastStkOffs = stkOffs;
+
+ lowBits = (int)OFFSET_MASK & stkOffs;
+ stkOffs = (int)((uint)stkOffs & ~OFFSET_MASK);
+
+ if (header.DoubleAlign &&
+ (uint)stkOffs >= sizeof(int) * (header.FrameSize + calleeSavedRegs))
+ {
+ reg = 'B';
+ stkOffs -= sizeof(int) * (int)(header.FrameSize + calleeSavedRegs);
+ }
+
+ GcSlots.Add(new GcSlot(GcSlots.Count, $"E{reg}P", stkOffs, lowBits, GcSlotFlags.GC_SLOT_UNTRACKED));
+ }
+ }
+
+ private void DecodeFrameVariableLifetimeTable(byte[] image, InfoHdrSmall header, ref int offset)
+ {
+ uint count = header.VarPtrTableSize;
+ uint curOffs = 0;
+ while (count-- > 0)
+ {
+ uint varOffs = NativeReader.DecodeUnsignedGc(image, ref offset);
+ uint begOffs = NativeReader.DecodeUDelta(image, ref offset, curOffs);
+ uint endOffs = NativeReader.DecodeUDelta(image, ref offset, begOffs);
+
+
+ uint lowBits = varOffs & 0x3;
+ varOffs &= ~OFFSET_MASK;
+
+ curOffs = begOffs;
+
+ string reg = header.EbpFrame ? "BP" : "SP";
+ GcSlots.Add(new GcSlot(GcSlots.Count, reg, (int)begOffs, (int)endOffs, (int)varOffs, (int)lowBits, GcSlotFlags.GC_SLOT_BASE));
+ }
+ }
+ }
+}
diff --git a/src/tools/r2rdump/x86/GcTransition.cs b/src/tools/r2rdump/x86/GcTransition.cs
new file mode 100644
index 0000000000..a103d746a1
--- /dev/null
+++ b/src/tools/r2rdump/x86/GcTransition.cs
@@ -0,0 +1,283 @@
+// 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.Text;
+
+namespace R2RDump.x86
+{
+ public enum Action
+ {
+ POP = 0x00,
+ PUSH = 0x01,
+ KILL = 0x02,
+ LIVE = 0x03,
+ DEAD = 0x04
+ }
+
+ public class CalleeSavedRegister : BaseGcTransition
+ {
+ public CalleeSavedRegisters Register { get; set; }
+
+ public CalleeSavedRegister() { }
+
+ public CalleeSavedRegister(int codeOffset, CalleeSavedRegisters reg)
+ : base(codeOffset)
+ {
+ Register = reg;
+ }
+
+ public override string ToString()
+ {
+ return $"thisptr in {Register}";
+ }
+ }
+
+ public class IPtrMask : BaseGcTransition
+ {
+ public uint IMask { get; set; }
+
+ public IPtrMask() { }
+
+ public IPtrMask(int codeOffset, uint imask)
+ : base(codeOffset)
+ {
+ IMask = imask;
+ }
+
+ public override string ToString()
+ {
+ return $"iptrMask: {IMask}";
+ }
+ }
+
+ public class GcTransitionRegister : BaseGcTransition
+ {
+ public Registers Register { get; set; }
+ public Action IsLive { get; set; }
+ public int PushCountOrPopSize { get; set; }
+ public bool IsThis { get; set; }
+ public bool Iptr { get; set; }
+
+ public GcTransitionRegister() { }
+
+ public GcTransitionRegister(int codeOffset, Registers reg, Action isLive, bool isThis = false, bool iptr = false, int pushCountOrPopSize = -1)
+ : base(codeOffset)
+ {
+ Register = reg;
+ IsLive = isLive;
+ PushCountOrPopSize = pushCountOrPopSize;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ if (IsLive == Action.LIVE)
+ {
+ sb.Append($"reg {Register} becoming live");
+ }
+ else if (IsLive == Action.DEAD)
+ {
+ sb.Append($"reg {Register} becoming dead");
+ }
+ else
+ {
+ sb.Append((IsLive == Action.PUSH ? "push" : "pop") + $" {Register}");
+ if (PushCountOrPopSize != -1)
+ sb.Append($" {PushCountOrPopSize}");
+ }
+
+ if (IsThis)
+ sb.Append(" 'this'");
+ if (Iptr)
+ sb.Append(" (iptr)");
+
+ return sb.ToString();
+ }
+ }
+
+ public class GcTransitionPointer : BaseGcTransition
+ {
+ private bool _isEbpFrame;
+ public uint ArgOffset { get; set; }
+ public uint ArgCount { get; set; }
+ public Action Act { get; set; }
+ public bool IsPtr { get; set; }
+ public bool IsThis { get; set; }
+ public bool Iptr { get; set; }
+
+ public GcTransitionPointer() { }
+
+ public GcTransitionPointer(int codeOffset, uint argOffs, uint argCnt, Action act, bool isEbpFrame, bool isThis = false, bool iptr = false, bool isPtr = true)
+ : base(codeOffset)
+ {
+ _isEbpFrame = isEbpFrame;
+ CodeOffset = codeOffset;
+ ArgOffset = argOffs;
+ ArgCount = argCnt;
+ Act = act;
+ IsPtr = isPtr;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ if (Act == Action.KILL)
+ {
+ sb.Append($"kill args {ArgOffset}");
+ }
+ else
+ {
+ if (Act == Action.POP)
+ {
+ sb.Append($"pop ");
+ }
+ else
+ {
+ sb.Append($"push ");
+ }
+ if (IsPtr)
+ {
+ sb.Append($"{ArgOffset}");
+ if (!_isEbpFrame)
+ {
+ sb.Append($" args ({ArgCount})");
+ }
+ else if (Act == Action.POP)
+ {
+ sb.Append(" ptrs");
+ }
+
+ if (IsThis)
+ sb.Append(" 'this'");
+ if (Iptr)
+ sb.Append(" (iptr)");
+ }
+ else
+ {
+ sb.Append("non-pointer");
+ sb.Append($" ({ArgCount})");
+ }
+ }
+
+ return sb.ToString();
+ }
+ }
+
+ public class GcTransitionCall : BaseGcTransition
+ {
+ public struct CallRegister
+ {
+ public Registers Register { get; set; }
+ public bool IsByRef { get; set; }
+
+ public CallRegister(Registers reg, bool isByRef)
+ {
+ Register = reg;
+ IsByRef = isByRef;
+ }
+ }
+
+ public struct PtrArg
+ {
+ public uint StackOffset { get; set; }
+ public uint LowBit { get; set; }
+
+ public PtrArg(uint stackOffset, uint lowBit)
+ {
+ StackOffset = stackOffset;
+ LowBit = lowBit;
+ }
+ }
+
+ public List<CallRegister> CallRegisters { get; set; }
+ public List<PtrArg> PtrArgs { get; set; }
+ public uint ArgMask { get; set; }
+ public uint IArgs { get; set; }
+
+ public GcTransitionCall() { }
+
+ public GcTransitionCall(int codeOffset)
+ : base(codeOffset)
+ {
+ CallRegisters = new List<CallRegister>();
+ PtrArgs = new List<PtrArg>();
+ ArgMask = 0;
+ IArgs = 0;
+ }
+
+ public GcTransitionCall(int codeOffset, bool isEbpFrame, uint regMask, uint byRefRegMask)
+ : base(codeOffset)
+ {
+ CallRegisters = new List<CallRegister>();
+ PtrArgs = new List<PtrArg>();
+ if ((regMask & 1) != 0)
+ {
+ Registers reg = Registers.EDI;
+ bool isByRef = (byRefRegMask & 1) != 0;
+ CallRegisters.Add(new CallRegister(reg, isByRef));
+ }
+ if ((regMask & 2) != 0)
+ {
+ Registers reg = Registers.ESI;
+ bool isByRef = (byRefRegMask & 2) != 0;
+ CallRegisters.Add(new CallRegister(reg, isByRef));
+ }
+ if ((regMask & 4) != 0)
+ {
+ Registers reg = Registers.EBX;
+ bool isByRef = (byRefRegMask & 4) != 0;
+ CallRegisters.Add(new CallRegister(reg, isByRef));
+ }
+ if (!isEbpFrame)
+ {
+ if ((regMask & 8) != 0)
+ {
+ Registers reg = Registers.EBP;
+ CallRegisters.Add(new CallRegister(reg, false));
+ }
+ }
+ ArgMask = 0;
+ IArgs = 0;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append("call [ ");
+ foreach (CallRegister reg in CallRegisters)
+ {
+ sb.Append($"{reg.Register}");
+ if (reg.IsByRef)
+ sb.Append("(byref)");
+ sb.Append(" ");
+ }
+
+ if (PtrArgs.Count > 0)
+ {
+ sb.Append(" ] ptrArgs=[ ");
+ foreach (PtrArg ptrArg in PtrArgs)
+ {
+ sb.Append($"{ptrArg.StackOffset}");
+ if (ptrArg.LowBit != 0)
+ sb.Append("i");
+ sb.Append(" ");
+ }
+ sb.Append(" ]");
+ }
+ else
+ {
+ sb.Append($" ] argMask={ArgMask}");
+ if (IArgs != 0)
+ sb.Append($" (iargs={IArgs})");
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/tools/r2rdump/x86/InfoHdr.cs b/src/tools/r2rdump/x86/InfoHdr.cs
new file mode 100644
index 0000000000..000e167e2a
--- /dev/null
+++ b/src/tools/r2rdump/x86/InfoHdr.cs
@@ -0,0 +1,503 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.x86
+{
+ public struct InfoHdrSmall
+ {
+ private const uint INVALID_GS_COOKIE_OFFSET = 0;
+ private const uint INVALID_SYNC_OFFSET = 0;
+
+ public uint PrologSize { get; set; }
+ public uint EpilogSize { get; set; }
+ public byte EpilogCount { get; set; }
+ public bool EpilogAtEnd { get; set; }
+ public bool EdiSaved { get; set; } // which callee-saved regs are pushed onto stack
+ public bool EsiSaved { get; set; }
+ public bool EbxSaved { get; set; }
+ public bool EbpSaved { get; set; }
+ public bool EbpFrame { get; set; } // locals accessed relative to ebp
+ public bool Interruptible { get; set; } // is intr. at all points (except prolog/epilog), not just call-sites
+ public bool DoubleAlign { get; set; } // uses double-aligned stack (ebpFrame will be false)
+ public bool Security { get; set; } // has slot for security object
+ public bool Handlers { get; set; } // has callable handlers
+ public bool Localloc { get; set; } // uses localloc
+ public bool EditNcontinue { get; set; } // was JITed in EnC mode
+ public bool Varargs { get; set; } // function uses varargs calling convention
+ public bool ProfCallbacks { get; set; }
+ public byte GenericsContext { get; set; }// function reports a generics context parameter is present
+ public byte GenericsContextIsMethodDesc { get; set; }
+ public ReturnKinds ReturnKind { get; set; } // Available GcInfo v2 onwards, previously undefined
+ public ushort ArgCount { get; set; }
+ public uint FrameSize { get; set; }
+ public uint UntrackedCnt { get; set; }
+ public uint VarPtrTableSize { get; set; }
+
+ public uint GsCookieOffset { get; set; }
+ public uint SyncStartOffset { get; set; }
+ public uint SyncEndOffset { get; set; }
+ public uint RevPInvokeOffset { get; set; }
+
+ public bool HasArgTabOffset { get; set; }
+ public uint ArgTabOffset { get; set; }
+ [XmlIgnore]
+ public List<int> Epilogs { get; set; }
+
+ public InfoHdrSmall(uint prologSize, uint epilogSize, byte epilogCount, byte epilogAtEnd, byte ediSaved, byte esiSaved, byte ebxSaved, byte ebpSaved, byte ebpFrame,
+ byte interruptible, byte doubleAlign, byte security, byte handlers, byte localloc, byte editNcontinue, byte varargs, byte profCallbacks,
+ byte genericsContext, byte genericsContextIsMethodDesc, byte returnKind, ushort argCount, uint frameSize, uint untrackedCnt, uint varPtrTableSize)
+ {
+ PrologSize = prologSize;
+ EpilogSize = epilogSize;
+ EpilogCount = epilogCount;
+ EpilogAtEnd = epilogAtEnd == 1;
+ EdiSaved = ediSaved == 1;
+ EsiSaved = esiSaved == 1;
+ EbxSaved = ebxSaved == 1;
+ EbpSaved = ebpSaved == 1;
+ EbpFrame = ebpFrame == 1;
+ Interruptible = interruptible == 1;
+ DoubleAlign = doubleAlign == 1;
+ Security = security == 1;
+ Handlers = handlers == 1;
+ Localloc = localloc == 1;
+ EditNcontinue = editNcontinue == 1;
+ Varargs = varargs == 1;
+ ProfCallbacks = profCallbacks == 1;
+ GenericsContext = genericsContext;
+ GenericsContextIsMethodDesc = genericsContextIsMethodDesc;
+ ReturnKind = (ReturnKinds)returnKind;
+ ArgCount = argCount;
+ FrameSize = frameSize;
+ UntrackedCnt = untrackedCnt;
+ VarPtrTableSize = varPtrTableSize;
+
+ GsCookieOffset = 0;
+ SyncStartOffset = 0;
+ SyncEndOffset = 0;
+ RevPInvokeOffset = 0;
+
+ HasArgTabOffset = false;
+ ArgTabOffset = 0;
+ Epilogs = null;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendLine($"\t\tPrologSize: {PrologSize}");
+ sb.AppendLine($"\t\tEpilogSize: {EpilogSize}");
+ sb.AppendLine($"\t\tEpilogCount: {EpilogCount}");
+ sb.Append("\t\tEpilogAtEnd: ");
+ sb.AppendLine(EpilogAtEnd ? "yes" : "no");
+
+ sb.Append($"\t\tCallee-saved regs = ");
+ if (EdiSaved) sb.Append("EDI");
+ if (EsiSaved) sb.Append("ESI");
+ if (EbxSaved) sb.Append("EBX");
+ if (EbpSaved) sb.Append("EBP");
+ sb.AppendLine();
+
+ sb.Append($"\t\tEbpFrame: ");
+ sb.AppendLine(EbpFrame ? "yes" : "no");
+ sb.Append($"\t\tFully Interruptible: ");
+ sb.AppendLine(Interruptible ? "yes" : "no");
+ sb.Append($"\t\tDoubleAlign: ");
+ sb.AppendLine(DoubleAlign ? "yes" : "no");
+ sb.AppendLine($"\t\tArguments Size: {ArgCount} DWORDs");
+ sb.AppendLine($"\t\tStack Frame Size: {FrameSize} DWORDs");
+ sb.AppendLine($"\t\tUntrackedCnt: {UntrackedCnt}");
+ sb.AppendLine($"\t\tVarPtrTableSize: {VarPtrTableSize}");
+
+ if (Security) sb.AppendLine($"\t\tSecurity Check Obj: yes");
+ if (Handlers) sb.AppendLine($"\t\tHandlers: yes");
+ if (Localloc) sb.AppendLine($"\t\tLocalloc: yes");
+ if (EditNcontinue) sb.AppendLine($"\t\tEditNcontinue: yes");
+ if (Varargs) sb.AppendLine($"\t\tVarargs: yes");
+ if (ProfCallbacks) sb.AppendLine($"\t\tProfCallbacks: yes");
+
+ sb.AppendLine($"\t\tGenericsContext: {GenericsContext}");
+ sb.AppendLine($"\t\tGenericsContextIsMethodDesc: {GenericsContextIsMethodDesc}");
+ sb.AppendLine($"\t\tReturnKind: {ReturnKind}");
+ sb.AppendLine($"\t\tRevPInvokeOffset: {RevPInvokeOffset}");
+
+ if (GsCookieOffset != INVALID_GS_COOKIE_OFFSET)
+ {
+ sb.Append("\t\tGuardStack cookie = [");
+ sb.Append(EbpFrame ? "EBP-" : "ESP+");
+ sb.AppendLine($"{GsCookieOffset}]\n");
+ }
+ if (SyncStartOffset != INVALID_GS_COOKIE_OFFSET)
+ {
+ sb.AppendLine($"\t\tSync region = [{SyncStartOffset},{SyncEndOffset}]");
+ }
+
+ sb.Append($"\t\tEpilogs:");
+ foreach (int epilog in Epilogs)
+ {
+ sb.AppendLine($" {epilog}");
+ }
+ if (HasArgTabOffset)
+ sb.AppendLine($"\t\tArgTabOffset: {ArgTabOffset}");
+
+ return sb.ToString();
+ }
+ };
+
+ public class InfoHdrDecoder {
+
+ private const uint HAS_VARPTR = 0xFFFFFFFF;
+ private const uint HAS_UNTRACKED = 0xFFFFFFFF;
+ private const uint HAS_GS_COOKIE_OFFSET = 0xFFFFFFFF;
+ private const uint HAS_SYNC_OFFSET = 0xFFFFFFFF;
+ private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF;
+ private const uint YES = HAS_VARPTR;
+
+ public static InfoHdrSmall GetInfoHdr(byte encoding)
+ {
+ return _infoHdrShortcut[encoding];
+ }
+
+ public static InfoHdrSmall DecodeHeader(byte[] image, ref int offset, int codeLength)
+ {
+ byte nextByte = image[offset++];
+ byte encoding = (byte)(nextByte & 0x7f);
+ InfoHdrSmall header = GetInfoHdr(encoding);
+ while ((nextByte & (uint)InfoHdrAdjustConstants.MORE_BYTES_TO_FOLLOW) != 0)
+ {
+ nextByte = image[offset++];
+ encoding = (byte)(nextByte & (uint)InfoHdrAdjustConstants.ADJ_ENCODING_MAX);
+
+ if (encoding < (uint)InfoHdrAdjust.NEXT_FOUR_START)
+ {
+ if (encoding < (uint)InfoHdrAdjust.SET_ARGCOUNT)
+ {
+ header.FrameSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_FRAMESIZE);
+ }
+ else if (encoding < (uint)InfoHdrAdjust.SET_PROLOGSIZE)
+ {
+ header.ArgCount = (byte)(encoding - (uint)InfoHdrAdjust.SET_ARGCOUNT);
+ }
+ else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGSIZE)
+ {
+ header.PrologSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_PROLOGSIZE);
+ }
+ else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGCNT)
+ {
+ header.EpilogSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_EPILOGSIZE);
+ }
+ else if (encoding < (uint)InfoHdrAdjust.SET_UNTRACKED)
+ {
+ header.EpilogCount = (byte)((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) / 2);
+ header.EpilogAtEnd = ((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) & 1) == 1 ? true : false;
+ }
+ else if (encoding < (uint)InfoHdrAdjust.FIRST_FLIP)
+ {
+ header.UntrackedCnt = (byte)(encoding - (uint)InfoHdrAdjust.SET_UNTRACKED);
+ }
+ else
+ {
+ switch (encoding)
+ {
+ case (byte)InfoHdrAdjust.FLIP_EDI_SAVED:
+ header.EdiSaved = !header.EdiSaved;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_ESI_SAVED:
+ header.EsiSaved = !header.EsiSaved;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_EBX_SAVED:
+ header.EbxSaved = !header.EbxSaved;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_EBP_SAVED:
+ header.EbpSaved = !header.EbpSaved;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_EBP_FRAME:
+ header.EbpFrame = !header.EbpFrame;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_INTERRUPTIBLE:
+ header.Interruptible = !header.Interruptible;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_DOUBLE_ALIGN:
+ header.DoubleAlign = !header.DoubleAlign;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_SECURITY:
+ header.Security = !header.Security;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_HANDLERS:
+ header.Handlers = !header.Handlers;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_LOCALLOC:
+ header.Localloc = !header.Localloc;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_EDITnCONTINUE:
+ header.EditNcontinue = !header.EditNcontinue;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_VAR_PTR_TABLE_SZ:
+ header.VarPtrTableSize ^= HAS_VARPTR;
+ break;
+ case (byte)InfoHdrAdjust.FFFF_UNTRACKED_CNT:
+ header.UntrackedCnt = HAS_UNTRACKED;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_VARARGS:
+ header.Varargs = !header.Varargs;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_PROF_CALLBACKS:
+ header.ProfCallbacks = !header.ProfCallbacks;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_HAS_GENERICS_CONTEXT:
+ header.GenericsContext ^= 1;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_GENERICS_CONTEXT_IS_METHODDESC:
+ header.GenericsContextIsMethodDesc ^= 1;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_HAS_GS_COOKIE:
+ header.GsCookieOffset ^= HAS_GS_COOKIE_OFFSET;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_SYNC:
+ header.SyncStartOffset ^= HAS_SYNC_OFFSET;
+ break;
+ case (byte)InfoHdrAdjust.FLIP_REV_PINVOKE_FRAME:
+ header.RevPInvokeOffset ^= HAS_REV_PINVOKE_FRAME_OFFSET;
+ break;
+
+ case (byte)InfoHdrAdjust.NEXT_OPCODE:
+ encoding = (byte)(image[offset++] & (int)InfoHdrAdjustConstants.ADJ_ENCODING_MAX);
+ // encoding here always corresponds to codes in InfoHdrAdjust2 set
+
+ if (encoding < (int)InfoHdrAdjustConstants.SET_RET_KIND_MAX)
+ {
+ header.ReturnKind = (ReturnKinds)encoding;
+ }
+ else
+ {
+ throw new BadImageFormatException("Unexpected gcinfo header encoding");
+ }
+ break;
+ default:
+ throw new BadImageFormatException("Unexpected gcinfo header encoding");
+ }
+ }
+ }
+ else
+ {
+ byte lowBits;
+ switch (encoding >> 4)
+ {
+ case 5:
+ lowBits = (byte)(encoding & 0xf);
+ header.FrameSize <<= 4;
+ header.FrameSize += lowBits;
+ break;
+ case 6:
+ lowBits = (byte)(encoding & 0xf);
+ header.ArgCount <<= 4;
+ header.ArgCount += lowBits;
+ break;
+ case 7:
+ if ((encoding & 0x8) == 0)
+ {
+ lowBits = (byte)(encoding & 0x7);
+ header.PrologSize <<= 3;
+ header.PrologSize += lowBits;
+ }
+ else
+ {
+ lowBits = (byte)(encoding & 0x7);
+ header.EpilogSize <<= 3;
+ header.EpilogSize += lowBits;
+ }
+ break;
+ default:
+ throw new BadImageFormatException("Unexpected gcinfo header encoding");
+ }
+ }
+ }
+
+ if (header.UntrackedCnt == HAS_UNTRACKED)
+ {
+ header.HasArgTabOffset = true;
+ header.UntrackedCnt = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+ if (header.VarPtrTableSize == HAS_VARPTR)
+ {
+ header.HasArgTabOffset = true;
+ header.VarPtrTableSize = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+ if (header.GsCookieOffset == HAS_GS_COOKIE_OFFSET)
+ {
+ header.GsCookieOffset = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+ if (header.SyncStartOffset == HAS_SYNC_OFFSET)
+ {
+ header.SyncStartOffset = NativeReader.DecodeUnsignedGc(image, ref offset);
+ header.SyncEndOffset = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+ if (header.RevPInvokeOffset == HAS_REV_PINVOKE_FRAME_OFFSET)
+ {
+ header.RevPInvokeOffset = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+
+ header.Epilogs = new List<int>();
+ if (header.EpilogCount > 1 || (header.EpilogCount != 0 && !header.EpilogAtEnd))
+ {
+ uint offs = 0;
+
+ for (int i = 0; i < header.EpilogCount; i++)
+ {
+ offs = NativeReader.DecodeUDelta(image, ref offset, offs);
+ header.Epilogs.Add((int)offs);
+ }
+ }
+ else
+ {
+ if (header.EpilogCount != 0)
+ header.Epilogs.Add(codeLength - (int)header.EpilogSize);
+ }
+
+ if (header.HasArgTabOffset)
+ {
+ header.ArgTabOffset = NativeReader.DecodeUnsignedGc(image, ref offset);
+ }
+
+ return header;
+ }
+
+ private static InfoHdrSmall[] _infoHdrShortcut = {
+ new InfoHdrSmall( 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1139 00
+ new InfoHdrSmall( 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 128738 01
+ new InfoHdrSmall( 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3696 02
+ new InfoHdrSmall( 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 402 03
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 4259 04
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 3379 05
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 2058 06
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0 ), // 728 07
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 984 08
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0 ), // 606 09
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0 ), // 1110 0a
+ new InfoHdrSmall( 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 0 ), // 414 0b
+ new InfoHdrSmall( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1553 0c
+ new InfoHdrSmall( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, YES ), // 584 0d
+ new InfoHdrSmall( 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 2182 0e
+ new InfoHdrSmall( 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3445 0f
+ new InfoHdrSmall( 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1369 10
+ new InfoHdrSmall( 1, 2, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 515 11
+ new InfoHdrSmall( 1, 2, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 21127 12
+ new InfoHdrSmall( 1, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3517 13
+ new InfoHdrSmall( 1, 2, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 750 14
+ new InfoHdrSmall( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1876 15
+ new InfoHdrSmall( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1665 16
+ new InfoHdrSmall( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 729 17
+ new InfoHdrSmall( 1, 4, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 484 18
+ new InfoHdrSmall( 1, 4, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 331 19
+ new InfoHdrSmall( 2, 3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 361 1a
+ new InfoHdrSmall( 2, 3, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 964 1b
+ new InfoHdrSmall( 2, 3, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3713 1c
+ new InfoHdrSmall( 2, 3, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 466 1d
+ new InfoHdrSmall( 2, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1325 1e
+ new InfoHdrSmall( 2, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 712 1f
+ new InfoHdrSmall( 2, 3, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 588 20
+ new InfoHdrSmall( 2, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 20542 21
+ new InfoHdrSmall( 2, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 3802 22
+ new InfoHdrSmall( 2, 3, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 798 23
+ new InfoHdrSmall( 2, 5, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1900 24
+ new InfoHdrSmall( 2, 5, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 385 25
+ new InfoHdrSmall( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1617 26
+ new InfoHdrSmall( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1743 27
+ new InfoHdrSmall( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 909 28
+ new InfoHdrSmall( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0 ), // 602 29
+ new InfoHdrSmall( 2, 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 352 2a
+ new InfoHdrSmall( 2, 6, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 657 2b
+ new InfoHdrSmall( 2, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 1283 2c
+ new InfoHdrSmall( 2, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1286 2d
+ new InfoHdrSmall( 3, 4, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1495 2e
+ new InfoHdrSmall( 3, 4, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1989 2f
+ new InfoHdrSmall( 3, 4, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1154 30
+ new InfoHdrSmall( 3, 4, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 9300 31
+ new InfoHdrSmall( 3, 4, 2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 392 32
+ new InfoHdrSmall( 3, 4, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1720 33
+ new InfoHdrSmall( 3, 6, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1246 34
+ new InfoHdrSmall( 3, 6, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 800 35
+ new InfoHdrSmall( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1179 36
+ new InfoHdrSmall( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 1368 37
+ new InfoHdrSmall( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 349 38
+ new InfoHdrSmall( 3, 6, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0 ), // 505 39
+ new InfoHdrSmall( 3, 6, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 629 3a
+ new InfoHdrSmall( 3, 8, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 2, YES ), // 365 3b
+ new InfoHdrSmall( 4, 5, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 487 3c
+ new InfoHdrSmall( 4, 5, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 1752 3d
+ new InfoHdrSmall( 4, 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1959 3e
+ new InfoHdrSmall( 4, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 2436 3f
+ new InfoHdrSmall( 4, 5, 2, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 861 40
+ new InfoHdrSmall( 4, 7, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1459 41
+ new InfoHdrSmall( 4, 7, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 950 42
+ new InfoHdrSmall( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 1491 43
+ new InfoHdrSmall( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 ), // 879 44
+ new InfoHdrSmall( 4, 7, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ), // 408 45
+ new InfoHdrSmall( 5, 4, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 4870 46
+ new InfoHdrSmall( 5, 6, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 359 47
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 915 48
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 ), // 412 49
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1288 4a
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 1591 4b
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, YES ), // 361 4c
+ new InfoHdrSmall( 5, 6, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0 ), // 623 4d
+ new InfoHdrSmall( 5, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 ), // 1239 4e
+ new InfoHdrSmall( 6, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 457 4f
+ new InfoHdrSmall( 6, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 606 50
+ new InfoHdrSmall( 6, 4, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 1073 51
+ new InfoHdrSmall( 6, 4, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 508 52
+ new InfoHdrSmall( 6, 6, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 330 53
+ new InfoHdrSmall( 6, 6, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 1709 54
+ new InfoHdrSmall( 6, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 1164 55
+ new InfoHdrSmall( 7, 4, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), // 556 56
+ new InfoHdrSmall( 7, 5, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 529 57
+ new InfoHdrSmall( 7, 5, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 1423 58
+ new InfoHdrSmall( 7, 8, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 2455 59
+ new InfoHdrSmall( 7, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 956 5a
+ new InfoHdrSmall( 7, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, YES ), // 1399 5b
+ new InfoHdrSmall( 7, 8, 2, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, YES ), // 587 5c
+ new InfoHdrSmall( 7, 10, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 1, YES ), // 743 5d
+ new InfoHdrSmall( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0 ), // 1004 5e
+ new InfoHdrSmall( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, YES ), // 487 5f
+ new InfoHdrSmall( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0 ), // 337 60
+ new InfoHdrSmall( 7, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, YES ), // 361 61
+ new InfoHdrSmall( 8, 3, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 560 62
+ new InfoHdrSmall( 8, 6, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 1377 63
+ new InfoHdrSmall( 9, 4, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 877 64
+ new InfoHdrSmall( 9, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ), // 3041 65
+ new InfoHdrSmall( 9, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 349 66
+ new InfoHdrSmall( 10, 5, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0 ), // 2061 67
+ new InfoHdrSmall( 10, 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ), // 577 68
+ new InfoHdrSmall( 11, 6, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0 ), // 1195 69
+ new InfoHdrSmall( 12, 5, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ), // 491 6a
+ new InfoHdrSmall( 13, 8, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, YES ), // 627 6b
+ new InfoHdrSmall( 13, 8, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0 ), // 1099 6c
+ new InfoHdrSmall( 13, 10, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 1, YES ), // 488 6d
+ new InfoHdrSmall( 14, 7, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 574 6e
+ new InfoHdrSmall( 16, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 1281 6f
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, YES ), // 1881 70
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 339 71
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 ), // 2594 72
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 ), // 339 73
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 2107 74
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 2372 75
+ new InfoHdrSmall( 16, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, YES ), // 1078 76
+ new InfoHdrSmall( 16, 7, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, YES ), // 384 77
+ new InfoHdrSmall( 16, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, YES ), // 1541 78
+ new InfoHdrSmall( 16, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, YES ), // 975 79
+ new InfoHdrSmall( 19, 7, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 546 7a
+ new InfoHdrSmall( 24, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, YES ), // 675 7b
+ new InfoHdrSmall( 45, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), // 902 7c
+ new InfoHdrSmall( 51, 7, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, YES ), // 432 7d
+ new InfoHdrSmall( 51, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, YES ), // 361 7e
+ new InfoHdrSmall( 51, 7, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0 ), // 703 7f
+ };
+ }
+}
diff --git a/src/tools/r2rdump/x86/Registers.cs b/src/tools/r2rdump/x86/Registers.cs
new file mode 100644
index 0000000000..13b0d8dba2
--- /dev/null
+++ b/src/tools/r2rdump/x86/Registers.cs
@@ -0,0 +1,30 @@
+// 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.Text;
+
+namespace R2RDump.x86
+{
+ public enum Registers
+ {
+ EAX = 0x00,
+ ECX = 0x01,
+ EDX = 0x02,
+ EBX = 0x03,
+ ESP = 0x04,
+ EBP = 0x05,
+ ESI = 0x06,
+ EDI = 0x07,
+ };
+
+ public enum CalleeSavedRegisters
+ {
+ EDI = 0x00,
+ ESI = 0x01,
+ EBX = 0x02,
+ EBP = 0x03,
+ };
+}