{
public class GcInfo : BaseGcInfo
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfodecoder.h">src/inc/gcinfodecoder.h</a> GcInfoHeaderFlags
+ /// </summary>
private enum GcInfoHeaderFlags
{
GC_INFO_IS_VARARG = 0x1,
public GcInfo() { }
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcInfoDecoder::GcInfoDecoder</a>
+ /// </summary>
public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
{
Offset = offset;
}
sb.AppendLine($"\tSlotTable:");
sb.Append(SlotTable.ToString());
- sb.AppendLine($"\tTransitions:");
- foreach (List<BaseGcTransition> transList in Transitions.Values)
- {
- foreach (GcTransition trans in transList)
- {
- sb.AppendLine("\t\t" + trans.ToString());
- }
- }
sb.AppendLine($"\tSize: {Size} bytes");
return sb.ToString();
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcInfoDecoder::GcInfoDecoder</a>
+ /// </summary>
private void ParseHeaderFlags(byte[] image, ref int bitOffset)
{
GcInfoHeaderFlags headerFlags;
return safePoints;
}
+ /// <summary>
+ /// based on beginning of <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcInfoDecoder::EnumerateLiveSlots</a>
+ /// </summary>
private List<InterruptibleRange> EnumerateInterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset)
{
List<InterruptibleRange> ranges = new List<InterruptibleRange>();
return (readyToRunMajorVersion == 1) ? 1 : GCINFO_VERSION;
}
+ /// <summary>
+ /// based on end of <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcInfoDecoder::EnumerateLiveSlots and GcInfoEncoder::Build</a>
+ /// </summary>
public Dictionary<int, List<BaseGcTransition>> GetTranstions(byte[] image, ref int bitOffset)
{
int totalInterruptibleLength = 0;
}
}
- int numChunks = (totalInterruptibleLength + _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK; //=2
+ int numChunks = (totalInterruptibleLength + _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK;
int numBitsPerPointer = (int)NativeReader.DecodeVarLengthUnsigned(image, _gcInfoTypes.POINTER_SIZE_ENCBASE, ref bitOffset);
if (numBitsPerPointer == 0)
{
return new Dictionary<int, List<BaseGcTransition>>();
}
+ // get offsets of each chunk
int[] chunkPointers = new int[numChunks];
for (int i = 0; i < numChunks; i++)
{
chunkPointers[i] = NativeReader.ReadBits(image, numBitsPerPointer, ref bitOffset);
}
+
+ // Offset to m_Info2 containing all the info on register liveness
int info2Offset = (int)Math.Ceiling(bitOffset / 8.0) * 8;
List<GcTransition> transitions = new List<GcTransition>();
- bool[] liveAtEnd = new bool[SlotTable.GcSlots.Count - SlotTable.NumUntracked];
+ bool[] liveAtEnd = new bool[SlotTable.GcSlots.Count - SlotTable.NumUntracked]; // true if slot is live at the end of the chunk
for (int currentChunk = 0; currentChunk < numChunks; currentChunk++)
{
if (chunkPointers[currentChunk] == 0)
bitOffset = info2Offset + chunkPointers[currentChunk] - 1;
}
- int couldBeLiveOffset = bitOffset;
+ int couldBeLiveOffset = bitOffset; // points to the couldBeLive bit array (array of bits indicating the slot changed state in the chunk)
int slotId = 0;
bool fSimple = (NativeReader.ReadBits(image, 1, ref couldBeLiveOffset) == 0);
bool fSkipFirst = false;
slotId = -1;
}
- uint numCouldBeLiveSlots = GetNumCouldBeLiveSlots(image, ref bitOffset);
+ uint numCouldBeLiveSlots = GetNumCouldBeLiveSlots(image, ref bitOffset); // count the number of set bits in the couldBeLive array
- int finalStateOffset = bitOffset;
- bitOffset += (int)numCouldBeLiveSlots;
+ int finalStateOffset = bitOffset; // points to the finalState bit array (array of bits indicating if the slot is live at the end of the chunk)
+ bitOffset += (int)numCouldBeLiveSlots; // points to the array of code offsets
- int normChunkBaseCodeOffset = currentChunk * _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK;
+ int normChunkBaseCodeOffset = currentChunk * _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK; // the sum of the sizes of all preceeding chunks
for (int i = 0; i < numCouldBeLiveSlots; i++)
{
+ // get the index of the next couldBeLive slot
slotId = GetNextSlotId(image, fSimple, fSkipFirst, slotId, ref couldBeLiveCnt, ref couldBeLiveOffset);
+ // set the liveAtEnd for the slot at slotId
bool isLive = !liveAtEnd[slotId];
liveAtEnd[slotId] = (NativeReader.ReadBits(image, 1, ref finalStateOffset) != 0);
- // Read transitions
+ // Read all the code offsets where the slot at slotId changed state
while (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
{
int transitionOffset = NativeReader.ReadBits(image, _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ref bitOffset) + normChunkBaseCodeOffset;
}
}
+ // convert normCodeOffsetDelta to the actual CodeOffset
transitions.Sort((s1, s2) => s1.CodeOffset.CompareTo(s2.CodeOffset));
-
return UpdateTransitionCodeOffset(transitions);
}
}
else
{
+ // count the number of set bits in the couldBeLive bit array
foreach (var slot in SlotTable.GcSlots)
{
if (slot.Flags == GcSlotFlags.GC_SLOT_UNTRACKED)
{
if (fSimple)
{
+ // Get the slotId by iterating through the couldBeLive bit array. The slotId is the index of the next set bit
while (NativeReader.ReadBits(image, 1, ref couldBeLiveOffset) == 0)
slotId++;
}
return slotId;
}
+ /// <summary>
+ /// convert normCodeOffsetDelta to the actual CodeOffset
+ /// </summary>
private Dictionary<int, List<BaseGcTransition>> UpdateTransitionCodeOffset(List<GcTransition> transitions)
{
Dictionary<int, List<BaseGcTransition>> updatedTransitions = new Dictionary<int, List<BaseGcTransition>>();
- int cumInterruptibleLength = 0;
+ int cumInterruptibleLength = 0; // the sum of the lengths of all preceeding interruptible ranges
using (IEnumerator<InterruptibleRange> interruptibleRangesIter = InterruptibleRanges.GetEnumerator())
{
interruptibleRangesIter.MoveNext();
public GcSlotTable() { }
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcSlotDecoder::DecodeSlotTable</a>
+ /// </summary>
public GcSlotTable(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, ref int bitOffset)
{
if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
namespace R2RDump.Amd64
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/gcdumpnonx86.cpp">src\gcdump\gcdumpnonx86.cpp</a> GetRegName
+ /// </summary>
public enum Registers
{
EAX = 0,
namespace R2RDump.Amd64
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/win64unwind.h">src\inc\win64unwind.h</a> _UNWIND_OP_CODES
+ /// </summary>
public enum UnwindOpCodes
{
UWOP_PUSH_NONVOL = 0,
UWOP_SET_FPREG_LARGE,
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/win64unwind.h">src\inc\win64unwind.h</a> _UNWIND_OP_CODES
+ /// </summary>
public enum UnwindFlags
{
UNW_FLAG_NHANDLER = 0x0,
UNW_FLAG_CHAININFO = 0x4,
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/win64unwind.h">src\inc\win64unwind.h</a> _UNWIND_CODE
+ /// </summary>
public struct UnwindCode
{
[XmlAttribute("Index")]
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/win64unwind.h">src\inc\win64unwind.h</a> _UNWIND_INFO
+ /// </summary>
public class UnwindInfo : BaseUnwindInfo
{
private const int _sizeofUnwindCode = 2;
public UnwindInfo() { }
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/zap/zapcode.cpp">ZapUnwindData::Save</a>
+ /// </summary>
public UnwindInfo(byte[] image, int offset)
{
byte versionAndFlags = NativeReader.ReadByte(image, ref offset);
return sb.ToString();
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/jit/unwindamd64.cpp">src\jit\unwindamd64.cpp</a> DumpUnwindInfo
+ /// </summary>
private string GetUnwindCode(ref int i)
{
StringBuilder sb = new StringBuilder();
namespace R2RDump
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfotypes.h">src\inc\gcinfotypes.h</a> infoHdrAdjustConstants
+ /// </summary>
enum InfoHdrAdjustConstants
{
// Constants
MORE_BYTES_TO_FOLLOW = 0x80
};
- //
- // Enum to define codes that are used to incrementally adjust the InfoHdr structure.
- // First set of opcodes
+ /// <summary>
+ /// Enum to define codes that are used to incrementally adjust the InfoHdr structure.
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfotypes.h">src\inc\gcinfotypes.h</a> infoHdrAdjustConstants
+ /// </summary>
enum InfoHdrAdjust
{
SET_FRAMESIZE = 0, // 0x00
NEXT_THREE_EPILOGSIZE = 0x78
};
+ /// <summary>
+ /// based on macros defined in <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfotypes.h">src\inc\gcinfotypes.h</a>
+ /// </summary>
public class GcInfoTypes
{
private Machine _target;
namespace R2RDump
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">NativeFormat::NativeArray</a>
+ /// </summary>
class NativeArray
{
private const int _blockSize = 16;
namespace R2RDump
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">NativeFormat::NativeParser</a>
+ /// </summary>
struct NativeParser
{
/// <summary>
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">NativeFormat::NativeHashtable</a>
+ /// </summary>
struct NativeHashtable
{
private byte[] _image;
// <summary>
/// Decode variable length numbers
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfodecoder.h">src\inc\gcinfodecoder.h</a> DecodeVarLengthUnsigned
/// </summary>
/// <param name="image">PE image</param>
/// <param name="len">Number of bits to read</param>
}
}
+ // <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfodecoder.h">src\inc\gcinfodecoder.h</a> DecodeVarLengthSigned
+ /// </summary>
public static int DecodeVarLengthSigned(byte[] image, int len, ref int bitOffset)
{
int numEncodings = (1 << len);
}
}
+ // <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">src\vm\nativeformatreader.h</a> DecodeUnsigned
+ /// </summary>
public static uint DecodeUnsigned(byte[] image, uint offset, ref uint pValue)
{
if (offset >= image.Length)
return offset;
}
+ // <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/nativeformatreader.h">src\vm\nativeformatreader.h</a> DecodeSigned
+ /// </summary>
public static uint DecodeSigned(byte[] image, uint offset, ref int pValue)
{
if (offset >= image.Length)
return offset;
}
+ // <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/nidump.cpp">src\debug\daccess\nidump.cpp</a> DacSigUncompressData and DacSigUncompressBigData
+ /// </summary>
public static uint ReadCompressedData(byte[] image, ref int start)
{
int off = start;
}
/// <summary>
- /// Based on decodeUnsigned in gcdecoder.cpp, used by the x86 gcdump and unwindinfo
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> decodeUnsigned
/// </summary>
public static uint DecodeUnsignedGc(byte[] image, ref int start)
{
return value;
}
- public static int DecodeSigned(byte[] image, ref int start)
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> decodeSigned
+ /// </summary>
+ public static int DecodeSignedGc(byte[] image, ref int start)
{
int size = 1;
byte data = image[start++];
return value;
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> decodeUDelta
+ /// </summary>
public static uint DecodeUDelta(byte[] image, ref int start, uint lastValue)
{
uint delta = DecodeUnsignedGc(image, ref start);
foreach (string filename in _inputFilenames)
{
+ // parse the ReadyToRun image
R2RReader r2r = new R2RReader(filename);
if (_disasm)
_dumper = new TextDumper(r2r, _writer, _raw, _header, _disasm, disassembler, _unwind, _gc, _sectionContents);
}
+ // output the ReadyToRun info
Dump(r2r);
}
}
namespace R2RDump
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/readytorun.h">src/inc/readytorun.h</a> READYTORUN_HEADER
+ /// </summary>
public class R2RHeader
{
[Flags]
namespace R2RDump
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/readytorun.h">src/inc/readytorun.h</a> READYTORUN_IMPORT_SECTION
+ /// </summary>
public struct R2RImportSection
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportType
+ /// </summary>
public enum CorCompileImportType
{
CORCOMPILE_IMPORT_TYPE_UNKNOWN = 0,
CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD = 6,
};
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportFlags
+ /// </summary>
public enum CorCompileImportFlags
{
CORCOMPILE_IMPORT_FLAGS_UNKNOWN = 0x0000,
public Dictionary<int, List<BaseGcTransition>> Transitions { get; set; }
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/pal/inc/pal.h">src/pal/inc/pal.h</a> _RUNTIME_FUNCTION
+ /// </summary>
public class RuntimeFunction
{
/// <summary>
/// </summary>
public int UnwindRVA { get; set; }
+ /// <summary>
+ /// The start offset of the runtime function with is non-zero for methods with multiple runtime functions
+ /// </summary>
public int CodeOffset { get; set; }
/// <summary>
SignatureString = GetSignature();
}
+ /// <summary>
+ /// Initialize map of generic parameters names to the type in the instance
+ /// </summary>
private void InitGenericInstances(GenericParameterHandleCollection genericParams, GenericElementTypes[] instanceArgs, uint[] tok)
{
if (instanceArgs.Length != genericParams.Count || tok.Length != genericParams.Count)
for (int i = 0; i < instanceArgs.Length; i++)
{
- string key = _mdReader.GetString(_mdReader.GetGenericParameter(genericParams.ElementAt(i)).Name);
- string name = instanceArgs[i].ToString();
+ string key = _mdReader.GetString(_mdReader.GetGenericParameter(genericParams.ElementAt(i)).Name); // name of the generic param, eg. "T"
+ string type = instanceArgs[i].ToString(); // type of the generic param instance
if (instanceArgs[i] == GenericElementTypes.ValueType)
{
var t = _mdReader.GetTypeDefinition(MetadataTokens.TypeDefinitionHandle((int)tok[i]));
- name = _mdReader.GetString(t.Name);
+ type = _mdReader.GetString(t.Name); // name of the struct
}
- _genericParamInstanceMap[key] = name;
+ _genericParamInstanceMap[key] = type;
}
}
+ /// <summary>
+ /// Returns a string with format DeclaringType.Name<GenericTypes,...>(ArgTypes,...)
+ /// </summary>
private string GetSignature()
{
StringBuilder sb = new StringBuilder();
{
int runtimeFunctionId;
FixupCell[] fixups;
- GetEntryPointInfoFromOffset(offset, out runtimeFunctionId, out fixups);
+ GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixups);
R2RMethod method = new R2RMethod(R2RMethods.Count, _mdReader, rid, runtimeFunctionId, null, null, fixups);
if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length)
int runtimeFunctionId;
FixupCell[] fixups;
- GetEntryPointInfoFromOffset((int)curParser.Offset, out runtimeFunctionId, out fixups);
+ GetRuntimeFunctionIndexFromOffset((int)curParser.Offset, out runtimeFunctionId, out fixups);
R2RMethod method = new R2RMethod(R2RMethods.Count, _mdReader, rid, runtimeFunctionId, args, tokens, fixups);
if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length)
{
/// <summary>
/// Get the RVAs of the runtime functions for each method
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/zap/zapcode.cpp">ZapUnwindInfo::Save</a>
/// </summary>
private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset, int runtimeFunctionSize)
{
}
}
+ /// <summary>
+ /// Iterates through a native hashtable to get all RIDs
+ /// </summary>
private void ParseAvailableTypes()
{
if (!R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES))
}
}
+ /// <summary>
+ /// Converts the bytes in the compiler identifier section to characters in a string
+ /// </summary>
private string ParseCompilerIdentifier()
{
if (!R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_COMPILER_IDENTIFIER))
return Encoding.UTF8.GetString(identifier);
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/zap/zapimport.cpp">ZapImportSectionsTable::Save</a>
+ /// </summary>
private void ParseImportSections()
{
if (!R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_IMPORT_SECTIONS))
/// <summary>
/// Reads the method entrypoint from the offset. Used for non-generic methods
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/nidump.cpp">NativeImageDumper::DumpReadyToRunMethods</a>
/// </summary>
- private void GetEntryPointInfoFromOffset(int offset, out int runtimeFunctionIndex, out FixupCell[] fixupCells)
+ private void GetRuntimeFunctionIndexFromOffset(int offset, out int runtimeFunctionIndex, out FixupCell[] fixupCells)
{
fixupCells = null;
{
public struct R2RSection
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/readytorun.h">src/inc/readytorun.h</a> ReadyToRunSectionType
+ /// </summary>
public enum SectionType
{
READYTORUN_SECTION_COMPILER_IDENTIFIER = 100,
--- /dev/null
+# R2RDump
+
+Parses and outputs the contents of a ReadyToRun image
+
+## Usage
+
+dotnet R2RDump.dll --in <path to ReadyToRun image>
+
+* -o, --out <arg>
+ - Output file path. Dumps everything to the specified file except help message and error messages
+* -x, --xml
+ - Output in XML format
+* --raw
+ - Dump the raw bytes of each section or runtime function
+* --header
+ - Dump R2R header
+* -d, --disasm
+ - Show disassembly of methods or runtime functions
+* -q, --query <arg>...
+ - Query method by exact name, signature, row id or token
+* -k, --keyword <arg>...
+ - Search method by keyword
+* -r, --runtimefunction <arg>...
+ - Get one runtime function by id or relative virtual address
+* -s, --section <arg>...
+ - Get section by keyword
+* --unwind
+ - Dump unwindInfo
+* --gc
+ - Dump gcInfo and slot table
+* --sc
+ - Dump section contents
+* -v, --verbose
+ - Dump raw bytes, disassembly, unwindInfo, gcInfo and section contents
+
+## ReadyToRun Format
+
+### READYTORUN_SECTION_COMPILER_IDENTIFIER
+
+A string describing the compiler.
+
+Eg. "CoreCLR 4.5.30319.0 __BUILDMACHINE__"
+
+### READYTORUN_SECTION_IMPORT_SECTIONS
+
+A struct described in [READYTORUN_IMPORT_SECTION](../../inc/readytorun.h)
+
+### READYTORUN_SECTION_RUNTIME_FUNCTIONS
+
+A array of RVAs. For x64, each RuntimeFunction has RVAs to the start of the assembly, end of the assembly, and start of the UnwindInfo. For x86, each RuntimeFunction has RVAs to the start of the assembly, and start of the UnwindInfo.
+
+### READYTORUN_SECTION_METHODDEF_ENTRYPOINTS
+
+A [NativeArray](NativeArray.cs) used for finding the index of the entrypoint RuntimeFunction for each method. The NativeArray is index by is the rowId-1 of a method. Each element in the NativeArray is an offset pointing to the RuntimeFunction index.
+
+### READYTORUN_SECTION_AVAILABLE_TYPES
+
+A [NativeHashtable](NativeHashtable.cs) mapping type hashcodes of types defined in the program to the rowIds. The hashcode is calculated with [ComputeNameHashCode](../../vm/typehashingalgorithms.h)(namespace) ^ [ComputeNameHashCode](../../vm/typehashingalgorithms.h)(name)
+
+### READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS
+
+A [NativeHashtable](NativeHashtable.cs) mapping type hashcodes of generic instances to the (methodFlags, methodRowId, list of types, runtimeFunctionId). Each type in the list of types corresponds to a generic type in the method.
+
+Eg. GenericMethod<S, T>(T arg1, S arg2) instantiated for <int, UserDefinedStruct> is in the hashtable as:
+
+(hashcode) -> (methodFlags) (methodRowId) (number of generic types) (Int32) (ValueType) (RowId of UserDefinedStruct) (offset to RuntimeFunctionId)
+
+### UnwindInfo
+
+A struct described in [_UNWIND_INFO](../../inc/win64unwind.h). Each RuntimeFunction has its own UnwindInfo.
+
+For x86, it contains only an encoded function length
+
+For x64, it contains a bit field followed by an array of unwind codes ([_UNWIND_CODE](../../inc/win64unwind.h)) and finally padding to make it byte aligned
+
+### GcInfo
+
+Written into the ReadyToRun image right after UnwindInfo. Contains a header, GcSlots and GcTransitions (register liveness).
+
+The x64 GcInfo is written in crossgen by [GcInfoEncoder::Build](../../gcinfo/gcinfoencoder.cpp) and decoded similar to [GcInfoDecoder::EnumerateLiveSlots](../../vm/gcinfodecoder.cpp). The x86 gcInfo is written by [GCInfo::gcMakeRegPtrTable](../../jit/gcencode.cpp) and decoded similar to [GCDump::DumpGCTable](../../gcdump/i386/gcdumpx86.cpp)
+
+Contains the code length followed by the header, GcSlots, and finally GcTransitions
+
+The header contains flags indicating which properties are in the GcInfo. GcSlots gives details on the registers or stack pointer offsets are used in the method. GcTransitions give the CodeOffsets (which line in the assembly code) where GcSlots (excluding untracked slots) become live or dead
+
+In x64, GcTransitions are grouped into chunks where each chunk covers NUM_NORM_CODE_OFFSETS_PER_CHUNK lines of assembly code. The following format is used:
+> Array of offsets pointing to each chunk
+
+> Padding to make it byte aligned
+
+> For each chunk:
+>> 1 bit indicating if it's RLE encoded
+
+>> Array of bits indicating if each slot changed state in the chunk (ie. false if the slot is not used in the chunk). R2RDump uses this to calculate NumCouldBeLiveSlots and obtain slotIds
+
+>> Array of bits indicating if each slot is live at the end of the chunk
+
+>> For each slot that changed state in the chunk:
+>>> Array of elements consisting of a bit set to 1 and the normCodeOffsetDelta indicating all the code offsets where the slot changed state in the chunk. CodeOffset = normCodeOffsetDelta + normChunkBaseCodeOffset + currentRangeStartOffset - cumInterruptibleLength, where normChunkBaseCodeOffset is the sum of the sizes of all preceeding chunks, currentRangeStartOffset is the start offset of the interruptible range that the transition falls under and cumInterruptibleLength is the sum of the lengths of interruptible ranges that came before it
+
+
+# Todo
+
+* Support R2RDump on ARM and ARM64 (https://github.com/dotnet/coreclr/issues/19089)
+
+* Parse R2RSections: READYTORUN_SECTION_EXCEPTION_INFO, READYTORUN_SECTION_DEBUG_INFO, READYTORUN_SECTION_DELAYLOAD_METHODCALL_THUNKS, READYTORUN_SECTION_INLINING_INFO, READYTORUN_SECTION_PROFILEDATA_INFO
SkipLine();
}
+ /// <summary>
+ /// Dumps disassembly and register liveness
+ /// </summary>
internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
{
int rtfOffset = 0;
}
break;
case R2RSection.SectionType.READYTORUN_SECTION_METHODDEF_ENTRYPOINTS:
- NativeArray methodEntryPoints = new NativeArray(_r2r.Image, (uint)_r2r.GetOffset(section.RelativeVirtualAddress));
+ NativeArray methodEntryPoints = new NativeArray(_r2r.Image, (uint)_r2r.GetOffset(section.RelativeVirtualAddress));
_writer.Write(methodEntryPoints.ToString());
break;
case R2RSection.SectionType.READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS:
int rtfIndex = 0;
while (rtfOffset < rtfEndOffset)
{
- uint rva = NativeReader.ReadUInt32(_r2r.Image, ref rtfOffset);
- _writer.WriteLine($"{rtfIndex}: 0x{rva:X8}");
+ int startRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset);
+ int endRva = -1;
+ if (_r2r.Machine == Machine.Amd64)
+ {
+ endRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset);
+ }
+ int unwindRva = NativeReader.ReadInt32(_r2r.Image, ref rtfOffset);
+ _writer.WriteLine($"Index: {rtfIndex}");
+ _writer.WriteLine($"\tStartRva: 0x{startRva:X8}");
+ if (endRva != -1)
+ _writer.WriteLine($"\tEndRva: 0x{endRva:X8}");
+ _writer.WriteLine($"\tUnwindRva: 0x{unwindRva:X8}");
rtfIndex++;
}
break;
}
}
+ /// <summary>
+ /// Dumps disassembly and register liveness
+ /// </summary>
internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode)
{
int rtfOffset = 0;
{
class CallPattern
{
+
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> decodeCallPattern
+ /// </summary>
public static void DecodeCallPattern(uint pattern, out uint argCnt, out uint regMask, out uint argMask, out uint codeDelta)
{
uint val = callPatternTable[pattern];
codeDelta = fld[3];
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> callCommonDelta
+ /// </summary>
public static uint[] callCommonDelta = { 6, 8, 10, 12 };
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> callPatternTable
+ /// </summary>
private static uint[] callPatternTable =
{
0x0a000200, // 30109
public GcInfo() { }
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
{
Offset = offset;
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
private void GetTransitionsFullyInterruptible(byte[] image, ref int offset)
{
uint argCnt = 0;
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
private void GetTransitionsEbpFrame(byte[] image, ref int offset)
{
while (true)
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
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;
return sb.ToString();
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
private void DecodeUntracked(byte[] image, InfoHdrSmall header, ref int offset)
{
uint calleeSavedRegs = 0;
char reg = header.EbpFrame ? 'B' : 'S';
- stkOffsDelta = NativeReader.DecodeSigned(image, ref offset);
+ stkOffsDelta = NativeReader.DecodeSignedGc(image, ref offset);
int stkOffs = lastStkOffs - stkOffsDelta;
lastStkOffs = stkOffs;
}
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a>
+ /// </summary>
private void DecodeFrameVariableLifetimeTable(byte[] image, InfoHdrSmall header, ref int offset)
{
uint count = header.VarPtrTableSize;
namespace R2RDump.x86
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfotypes.h">src\inc\gcinfotypes.h</a> InfoHdrSmall
+ /// </summary>
public struct InfoHdrSmall
{
private const uint INVALID_GS_COOKIE_OFFSET = 0;
private const uint HAS_REV_PINVOKE_FRAME_OFFSET = 0xFFFFFFFF;
private const uint YES = HAS_VARPTR;
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcinfotypes.h">src\inc\gcinfotypes.h</a> GetInfoHdr
+ /// </summary>
public static InfoHdrSmall GetInfoHdr(byte encoding)
{
return _infoHdrShortcut[encoding];
}
+ /// <summary>
+ /// Initialize the GcInfo header
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> DecodeHeader and <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpInfoHdr</a>
+ /// </summary>
public static InfoHdrSmall DecodeHeader(byte[] image, ref int offset, int codeLength)
{
byte nextByte = image[offset++];
return header;
}
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> infoHdrShortcut
+ /// </summary>
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
namespace R2RDump.x86
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">src\gcdump\i386\gcdumpx86.cpp</a> RegName
+ /// </summary>
public enum Registers
{
EAX = 0x00,
EDI = 0x07,
};
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">src\gcdump\i386\gcdumpx86.cpp</a> CalleeSavedRegName
+ /// </summary>
public enum CalleeSavedRegisters
{
EDI = 0x00,
namespace R2RDump.x86
{
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/win64unwind.h">src\inc\win64unwind.h</a> _UNWIND_INFO
+ /// </summary>
public class UnwindInfo : BaseUnwindInfo
{
public uint FunctionLength { get; set; }