R2RDump - Documentation (#19497)
authorAmy <amycmyu@gmail.com>
Fri, 17 Aug 2018 00:05:23 +0000 (17:05 -0700)
committerGitHub <noreply@github.com>
Fri, 17 Aug 2018 00:05:23 +0000 (17:05 -0700)
* Add comments

* Add README

* Use href links, disable disasm

23 files changed:
src/tools/r2rdump/Amd64/GcInfo.cs
src/tools/r2rdump/Amd64/GcSlotTable.cs
src/tools/r2rdump/Amd64/Registers.cs
src/tools/r2rdump/Amd64/UnwindInfo.cs
src/tools/r2rdump/GCInfoTypes.cs
src/tools/r2rdump/NativeArray.cs
src/tools/r2rdump/NativeHashtable.cs
src/tools/r2rdump/NativeReader.cs
src/tools/r2rdump/R2RDump.cs
src/tools/r2rdump/R2RHeader.cs
src/tools/r2rdump/R2RImportSection.cs
src/tools/r2rdump/R2RMethod.cs
src/tools/r2rdump/R2RReader.cs
src/tools/r2rdump/R2RSection.cs
src/tools/r2rdump/README.md [new file with mode: 0644]
src/tools/r2rdump/TextDumper.cs
src/tools/r2rdump/XmlDumper.cs
src/tools/r2rdump/x86/CallPattern.cs
src/tools/r2rdump/x86/GcInfo.cs
src/tools/r2rdump/x86/GcSlotTable.cs
src/tools/r2rdump/x86/InfoHdr.cs
src/tools/r2rdump/x86/Registers.cs
src/tools/r2rdump/x86/UnwindInfo.cs

index df4b48f9a9115ddb0c4b10af33490ae2e5a5e8da..acc160566d163ceeb3f6b454af1329639d2b54e2 100644 (file)
@@ -12,6 +12,9 @@ namespace R2RDump.Amd64
 {
     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,
@@ -81,6 +84,9 @@ namespace R2RDump.Amd64
 
         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;
@@ -253,19 +259,14 @@ namespace R2RDump.Amd64
             }
             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;
@@ -305,6 +306,9 @@ namespace R2RDump.Amd64
             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>();
@@ -333,6 +337,9 @@ namespace R2RDump.Amd64
             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;
@@ -348,22 +355,25 @@ namespace R2RDump.Amd64
                 }
             }
 
-            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)
@@ -375,7 +385,7 @@ namespace R2RDump.Amd64
                     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;
@@ -386,20 +396,22 @@ namespace R2RDump.Amd64
                     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;
@@ -410,8 +422,8 @@ namespace R2RDump.Amd64
                 }
             }
 
+            // convert normCodeOffsetDelta to the actual CodeOffset
             transitions.Sort((s1, s2) => s1.CodeOffset.CompareTo(s2.CodeOffset));
-
             return UpdateTransitionCodeOffset(transitions);
         }
 
@@ -440,6 +452,7 @@ namespace R2RDump.Amd64
             }
             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)
@@ -456,6 +469,7 @@ namespace R2RDump.Amd64
         {
             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++;
             }
@@ -480,10 +494,13 @@ namespace R2RDump.Amd64
             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();
index a2bec0fc4e68bba03803763d2ec413a9791cdfa1..1a30eaec3c62beb27d5d787589470be27d8e8138 100644 (file)
@@ -64,6 +64,9 @@ namespace R2RDump.Amd64
 
         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)
index e55bef83b4c69a309b6265e3f29ad388e91bbda2..349d3d05f8c9da5dfb0c4620314588d5015941fa 100644 (file)
@@ -8,6 +8,9 @@ using System.Text;
 
 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,
index 6c4755d8bbd2516072541e2dad0b664a462f3a79..c595bab68ca485b079690d8e058da16bbcec02a8 100644 (file)
@@ -7,6 +7,9 @@ using System.Xml.Serialization;
 
 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,
@@ -23,6 +26,9 @@ namespace R2RDump.Amd64
         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,
@@ -31,6 +37,9 @@ namespace R2RDump.Amd64
         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")]
@@ -62,6 +71,9 @@ 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_INFO
+    /// </summary>
     public class UnwindInfo : BaseUnwindInfo
     {
         private const int _sizeofUnwindCode = 2;
@@ -78,6 +90,9 @@ namespace R2RDump.Amd64
 
         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);
@@ -140,6 +155,9 @@ namespace R2RDump.Amd64
             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();
index 0cc85139526a151f75ad2bee6e92da31d76a68f6..d42fd374228005192f66762d282abc25500ce915 100644 (file)
@@ -8,6 +8,9 @@ using System.Text;
 
 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
@@ -22,9 +25,10 @@ namespace R2RDump
         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
@@ -64,6 +68,9 @@ namespace R2RDump
         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;
index bb0c746f6e2eb8718eff1b74bafb4fe938e0eccd..0e620858df4d6c97d7cc98330e333e12cc0323b2 100644 (file)
@@ -6,6 +6,9 @@ using System.Text;
 
 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;
index acb4f6dfbe3063e5c778c515ed9035348411e62a..830805eee55605a6f0c37ab322b759ab8080d38c 100644 (file)
@@ -7,6 +7,9 @@ using System.Text;
 
 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>
@@ -76,6 +79,9 @@ namespace R2RDump
         }
     }
 
+    /// <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;
index 654fe843f0746db995183898975bd885daf88bdc..4c09256bd39993a7211ccaff72b949a1f3409e86 100644 (file)
@@ -127,6 +127,7 @@ namespace R2RDump
 
         // <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>
@@ -150,6 +151,9 @@ namespace R2RDump
             }
         }
 
+        // <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);
@@ -169,6 +173,9 @@ namespace R2RDump
             }
         }
 
+        // <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)
@@ -225,6 +232,9 @@ namespace R2RDump
             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)
@@ -281,6 +291,9 @@ namespace R2RDump
             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;
@@ -306,7 +319,7 @@ namespace R2RDump
         }
 
         /// <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)
         {
@@ -323,7 +336,10 @@ namespace R2RDump
             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++];
@@ -342,6 +358,9 @@ namespace R2RDump
             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);
index 8034956078a160745ab901d15665f6aa23bc2a7e..9c57f71fa4fc2fefe82bae933b2d3e0e0962dbcb 100644 (file)
@@ -395,6 +395,7 @@ namespace R2RDump
 
                 foreach (string filename in _inputFilenames)
                 {
+                    // parse the ReadyToRun image
                     R2RReader r2r = new R2RReader(filename);
 
                     if (_disasm)
@@ -411,6 +412,7 @@ namespace R2RDump
                         _dumper = new TextDumper(r2r, _writer, _raw, _header, _disasm, disassembler, _unwind, _gc, _sectionContents);
                     }
 
+                    // output the ReadyToRun info
                     Dump(r2r);
                 }
             }
index 7212d3eff70bb8fbd2f516d5695d99e0b5b70cff..55745f664f75f9f370570226fd38775ad0d86b5b 100644 (file)
@@ -8,6 +8,9 @@ using System.Text;
 
 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]
index 065c092ff4b85e0e7c963fbd1e141e3e6e96c5c2..2b278d8de2d0f736a25f1044fe652a323a3bd998 100644 (file)
@@ -10,8 +10,14 @@ using System.Xml.Serialization;
 
 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,
@@ -23,6 +29,9 @@ namespace R2RDump
             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,
index 3650e033469b675f345f68bc325d9b69cc980caf..de66550e187dcd8105fed2c695c15e93e0471821 100644 (file)
@@ -40,6 +40,9 @@ namespace R2RDump
         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>
@@ -67,6 +70,9 @@ namespace R2RDump
         /// </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>
@@ -267,6 +273,9 @@ namespace R2RDump
             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)
@@ -276,18 +285,21 @@ namespace R2RDump
 
             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();
index 0251568d3f24cbe0813c31ffb0e67438d406d701..18f8b370a4df64818fa412f0f7dc0edce259b147 100644 (file)
@@ -199,7 +199,7 @@ namespace R2RDump
                 {
                     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)
@@ -248,7 +248,7 @@ namespace R2RDump
 
                     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)
                     {
@@ -262,6 +262,7 @@ namespace R2RDump
 
         /// <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)
         {
@@ -312,6 +313,9 @@ namespace R2RDump
             }
         }
 
+        /// <summary>
+        /// Iterates through a native hashtable to get all RIDs
+        /// </summary>
         private void ParseAvailableTypes()
         {
             if (!R2RHeader.Sections.ContainsKey(R2RSection.SectionType.READYTORUN_SECTION_AVAILABLE_TYPES))
@@ -334,6 +338,9 @@ namespace R2RDump
             }
         }
 
+        /// <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))
@@ -347,6 +354,9 @@ namespace R2RDump
             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))
@@ -459,8 +469,9 @@ namespace R2RDump
 
         /// <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;
 
index ff39c81b196fdd66971fdb3c42f6dc60dc9ff9a3..125d0406575242160b6e3b29d346f872383e9b13 100644 (file)
@@ -11,6 +11,9 @@ namespace R2RDump
 {
     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,
diff --git a/src/tools/r2rdump/README.md b/src/tools/r2rdump/README.md
new file mode 100644 (file)
index 0000000..ced835b
--- /dev/null
@@ -0,0 +1,106 @@
+# R2RDump
+
+Parses and outputs the contents of a ReadyToRun image
+
+## Usage
+
+dotnet R2RDump.dll --in &lt;path to ReadyToRun image&gt;
+
+* -o, --out &lt;arg&gt;
+       - 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 &lt;arg&gt;...
+       - Query method by exact name, signature, row id or token
+* -k, --keyword &lt;arg&gt;...
+       - Search method by keyword
+* -r, --runtimefunction &lt;arg&gt;...
+       - Get one runtime function by id or relative virtual address
+* -s, --section &lt;arg&gt;...
+       - 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&lt;S, T&gt;(T arg1, S arg2) instantiated for &lt;int, UserDefinedStruct&gt; 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
index cf3d942d4f872e11b03cce25921a588dd02f168f..1ecaec882f256cbd4303aaa8ee80fb080013ec83 100644 (file)
@@ -168,6 +168,9 @@ namespace R2RDump
             SkipLine();
         }
 
+        /// <summary>
+        /// Dumps disassembly and register liveness
+        /// </summary>
         internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
         {
             int rtfOffset = 0;
@@ -244,7 +247,7 @@ namespace R2RDump
                     }
                     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:
@@ -259,8 +262,18 @@ namespace R2RDump
                     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;
index 10cde542771e4177050bec46ba986feb45948699..f260000e1f2d4b8e92382d11671f91ea44402341 100644 (file)
@@ -215,6 +215,9 @@ namespace R2RDump
             }
         }
 
+        /// <summary>
+        /// Dumps disassembly and register liveness
+        /// </summary>
         internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode)
         {
             int rtfOffset = 0;
index cc8a3c30cda912c6ae2a7a9eb21d6fe951e99cce..0a8cb1814aeae8e23a3201a9e7b6c41788fdff0d 100644 (file)
@@ -6,6 +6,10 @@ namespace R2RDump.x86
 {
     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];
@@ -16,8 +20,14 @@ namespace R2RDump.x86
             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
index e0896510fdef98fca255ee23cd4b17cf22dc45e0..da1f074eca562fd82517e93d68be8fb636df6d4a 100644 (file)
@@ -19,6 +19,9 @@ namespace R2RDump.x86
 
         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;
@@ -88,6 +91,9 @@ namespace R2RDump.x86
             }
         }
 
+        /// <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;
@@ -187,6 +193,9 @@ namespace R2RDump.x86
             }
         }
 
+        /// <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)
@@ -327,6 +336,9 @@ namespace R2RDump.x86
             }
         }
 
+        /// <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;
index d5486d814219669ca75cdb93571befa63924d778..fd72da57eb28eea281c79b9bc796f6374eee4d4f 100644 (file)
@@ -124,6 +124,9 @@ namespace R2RDump.x86
             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;
@@ -144,7 +147,7 @@ namespace R2RDump.x86
 
                 char reg = header.EbpFrame ? 'B' : 'S';
 
-                stkOffsDelta = NativeReader.DecodeSigned(image, ref offset);
+                stkOffsDelta = NativeReader.DecodeSignedGc(image, ref offset);
                 int stkOffs = lastStkOffs - stkOffsDelta;
                 lastStkOffs = stkOffs;
 
@@ -162,6 +165,9 @@ namespace R2RDump.x86
             }
         }
 
+        /// <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;
index 000e167e2ab90d80a8c4fa0a26e7455a51e5f998..7bb105254d17c2c761af3930dbb4b57629fd04f7 100644 (file)
@@ -9,6 +9,9 @@ using System.Xml.Serialization;
 
 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;
@@ -160,11 +163,18 @@ namespace R2RDump.x86
         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++];
@@ -369,6 +379,9 @@ namespace R2RDump.x86
             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
index 13b0d8dba2b464629ab4b0318bcebe5e29ee8307..69763b0358154f187548bd0135693b574934c0aa 100644 (file)
@@ -8,6 +8,9 @@ using System.Text;
 
 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,
@@ -20,6 +23,9 @@ namespace R2RDump.x86
         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,
index 7f617f0704455b35bbb5df1af8ac91f0b1f932b1..60fe13dc8e2f927f06e346ecb76b0582da0e8283 100644 (file)
@@ -7,6 +7,9 @@ using System.Xml.Serialization;
 
 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; }