Read slot table, dump GcInfo
authorAmy Yu <amycmyu@gmail.com>
Wed, 6 Jun 2018 17:38:57 +0000 (10:38 -0700)
committerAmy Yu <amycmyu@gmail.com>
Wed, 6 Jun 2018 22:02:43 +0000 (15:02 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/fe757e679cc46abe0f67853eb2c42dd0c78035a6

src/coreclr/src/tools/r2rdump/GCInfo.cs
src/coreclr/src/tools/r2rdump/GCInfoTypes.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/GCSlotTable.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/NativeReader.cs
src/coreclr/src/tools/r2rdump/R2RDump.cs
src/coreclr/src/tools/r2rdump/R2RMethod.cs
src/coreclr/src/tools/r2rdump/R2RReader.cs

index f6dcb3b..c225fe4 100644 (file)
@@ -2,11 +2,14 @@
 // 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;
 
 namespace R2RDump
 {
-    class GCInfo
+    class GcInfo
     {
         public enum GcInfoHeaderFlags
         {
@@ -46,6 +49,17 @@ namespace R2RDump
             RT_Illegal = 0xFF
         };
 
+        public struct InterruptibleRange
+        {
+            public uint StartOffset { get; }
+            public uint StopOffset { get; }
+            public InterruptibleRange(uint start, uint stop)
+            {
+                StartOffset = start;
+                StopOffset = stop;
+            }
+        }
+
         private const int GCINFO_VERSION = 2;
         private const int MIN_GCINFO_VERSION_WITH_RETURN_KIND = 2;
         private const int MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME = 2;
@@ -65,52 +79,13 @@ namespace R2RDump
         public uint SizeOfStackOutgoingAndScratchArea { get; }
         public uint NumSafePoints { get; }
         public uint NumInterruptibleRanges { get; }
+        public IEnumerable<uint> SafePointOffsets { get; }
+        public IEnumerable<InterruptibleRange> InterruptibleRanges { get; }
+        public GcSlotTable SlotTable { get; }
 
-        public GCInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
+        public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
         {
-            int sizeOfReturnKindSlim = 2;
-            int sizeOfReturnKindFat = 2;
-            int codeLengthEncBase = 8;
-            int normPrologSizeEncBase = 5;
-            int securityObjectStackSlotEncBase = 6;
-            int gsCookieStackSlotEncBase = 6;
-            int pspSymStackSlotEncBase = 6;
-            int genericsInstContextStackSlotEncBase = 6;
-            int stackBaseRegisterEncBase = 3;
-            int sizeOfEditAndContinuePreservedAreaEncBase = 4;
-            int reversePinvokeFrameEncBase = 6;
-            int sizeOfStackAreaEncBase = 3;
-            int numSafePointsEncBase = 3;
-            int numInterruptibleRangesEncBase = 1;
-            switch (machine)
-            {
-                case Machine.Amd64:
-                    sizeOfReturnKindFat = 4;
-                    numSafePointsEncBase = 2;
-                    break;
-                case Machine.Arm:
-                    codeLengthEncBase = 7;
-                    securityObjectStackSlotEncBase = 5;
-                    gsCookieStackSlotEncBase = 5;
-                    pspSymStackSlotEncBase = 5;
-                    genericsInstContextStackSlotEncBase = 5;
-                    stackBaseRegisterEncBase = 1;
-                    sizeOfEditAndContinuePreservedAreaEncBase = 3;
-                    reversePinvokeFrameEncBase = 5;
-                    numInterruptibleRangesEncBase = 2;
-                    break;
-                case Machine.Arm64:
-                    sizeOfReturnKindFat = 4;
-                    stackBaseRegisterEncBase = 2;
-                    break;
-                case Machine.I386:
-                    codeLengthEncBase = 6;
-                    normPrologSizeEncBase = 4;
-                    sizeOfEditAndContinuePreservedAreaEncBase = 3;
-                    sizeOfStackAreaEncBase = 6;
-                    numSafePointsEncBase = 4;
-                    break;
-            }
+            GcInfoTypes gcInfoTypes = new GcInfoTypes(machine);
 
             SecurityObjectStackSlot = -1;
             GSCookieStackSlot = -1;
@@ -124,6 +99,7 @@ namespace R2RDump
             GcInfoHeaderFlags headerFlags;
             Version = ReadyToRunVersionToGcInfoVersion(majorVersion);
             int bitOffset = offset * 8;
+            int initOffset = bitOffset;
             bool slimHeader = (NativeReader.ReadBits(image, 1, ref bitOffset) == 0);
 
             if (slimHeader)
@@ -144,17 +120,17 @@ namespace R2RDump
 
             if (Version >= MIN_GCINFO_VERSION_WITH_RETURN_KIND) // IsReturnKindAvailable
             {
-                int returnKindBits = (slimHeader) ? sizeOfReturnKindSlim : sizeOfReturnKindFat;
+                int returnKindBits = (slimHeader) ? gcInfoTypes.SIZE_OF_RETURN_KIND_SLIM : gcInfoTypes.SIZE_OF_RETURN_KIND_FAT;
                 ReturnKind = (ReturnKinds)NativeReader.ReadBits(image, returnKindBits, ref bitOffset);
             }
 
-            CodeLength = DenormalizeCodeLength(machine, (int)NativeReader.DecodeVarLengthUnsigned(image, codeLengthEncBase, ref bitOffset));
+            CodeLength = DenormalizeCodeLength(machine, (int)NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.CODE_LENGTH_ENCBASE, ref bitOffset));
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GS_COOKIE) != 0)
             {
                 // Decode prolog/epilog information
-                uint normPrologSize = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset) + 1;
-                uint normEpilogSize = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset);
+                uint normPrologSize = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset) + 1;
+                uint normEpilogSize = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset);
 
                 ValidRangeStart = normPrologSize;
                 ValidRangeEnd = (uint)CodeLength - normEpilogSize;
@@ -162,7 +138,7 @@ namespace R2RDump
             else if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_SECURITY_OBJECT) != 0 || (headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE)
             {
                 // Decode prolog information
-                ValidRangeStart = NativeReader.DecodeVarLengthUnsigned(image, normPrologSizeEncBase, ref bitOffset) + 1;
+                ValidRangeStart = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NORM_PROLOG_SIZE_ENCBASE, ref bitOffset) + 1;
                 // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd
                 ValidRangeEnd = ValidRangeStart + 1;
             }
@@ -170,22 +146,22 @@ namespace R2RDump
             // Decode the offset to the security object.
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_SECURITY_OBJECT) != 0)
             {
-                SecurityObjectStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, securityObjectStackSlotEncBase, ref bitOffset));
+                SecurityObjectStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.SECURITY_OBJECT_STACK_SLOT_ENCBASE, ref bitOffset));
             }
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GS_COOKIE) != 0)
             {
-                GSCookieStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gsCookieStackSlotEncBase, ref bitOffset));
+                GSCookieStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.GS_COOKIE_STACK_SLOT_ENCBASE, ref bitOffset));
             }
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_PSP_SYM) != 0)
             {
-                PSPSymStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, pspSymStackSlotEncBase, ref bitOffset));
+                PSPSymStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.PSP_SYM_STACK_SLOT_ENCBASE, ref bitOffset));
             }
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GcInfoHeaderFlags.GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE)
             {
-                GenericsInstContextStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, genericsInstContextStackSlotEncBase, ref bitOffset));
+                GenericsInstContextStackSlot = DenormalizeStackSlot(machine, NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE, ref bitOffset));
             }
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_STACK_BASE_REGISTER) != 0)
@@ -196,18 +172,18 @@ namespace R2RDump
                 }
                 else
                 {
-                    StackBaseRegister = DenormalizeStackBaseRegister(machine, NativeReader.DecodeVarLengthUnsigned(image, stackBaseRegisterEncBase, ref bitOffset));
+                    StackBaseRegister = DenormalizeStackBaseRegister(machine, NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.STACK_BASE_REGISTER_ENCBASE, ref bitOffset));
                 }
             }
 
             if ((headerFlags & GcInfoHeaderFlags.GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS) != 0)
             {
-                SizeOfEditAndContinuePreservedArea = NativeReader.DecodeVarLengthUnsigned(image, sizeOfEditAndContinuePreservedAreaEncBase, ref bitOffset);
+                SizeOfEditAndContinuePreservedArea = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, ref bitOffset);
             }
 
             if (hasReversePInvokeFrame)
             {
-                ReversePInvokeFrameStackSlot = NativeReader.DecodeVarLengthSigned(image, reversePinvokeFrameEncBase, ref bitOffset);
+                ReversePInvokeFrameStackSlot = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.REVERSE_PINVOKE_FRAME_ENCBASE, ref bitOffset);
             }
 
             // FIXED_STACK_PARAMETER_SCRATCH_AREA
@@ -217,11 +193,11 @@ namespace R2RDump
             }
             else
             {
-                SizeOfStackOutgoingAndScratchArea = DenormalizeSizeOfStackArea(machine, NativeReader.DecodeVarLengthUnsigned(image, sizeOfStackAreaEncBase, ref bitOffset));
+                SizeOfStackOutgoingAndScratchArea = DenormalizeSizeOfStackArea(machine, NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.SIZE_OF_STACK_AREA_ENCBASE, ref bitOffset));
             }
 
-            // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
-            NumSafePoints = NativeReader.DecodeVarLengthUnsigned(image, numSafePointsEncBase, ref bitOffset);
+            // PARTIALLY_intERRUPTIBLE_GC_SUPPORTED
+            NumSafePoints = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_SAFE_POINTS_ENCBASE, ref bitOffset);
 
             if (slimHeader)
             {
@@ -229,26 +205,61 @@ namespace R2RDump
             }
             else
             {
-                NumInterruptibleRanges = NativeReader.DecodeVarLengthUnsigned(image, numInterruptibleRangesEncBase, ref bitOffset);
+                NumInterruptibleRanges = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_INTERRUPTIBLE_RANGES_ENCBASE, ref bitOffset);
             }
 
-            // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
-            /*if (NumSafePoints != 0)
-            {
-                SafePointIndex = FindSafePoint(image, machine, InstructionOffset, ref bitOffset);
-            }
-            else
-            {
-                SafePointIndex = 0;
-            }
+            // PARTIALLY_intERRUPTIBLE_GC_SUPPORTED
+            SafePointOffsets = EnumerateSafePoints(image, ref bitOffset);
 
             uint numBitsPerOffset = CeilOfLog2(CodeLength);
-            bitOffset += (int)(NumSafePoints* numBitsPerOffset);
-            
+            bitOffset += (int)(NumSafePoints * numBitsPerOffset);
 
+            InterruptibleRanges = EnumerateinterruptibleRanges(image, gcInfoTypes.INTERRUPTIBLE_RANGE_DELTA1_ENCBASE, gcInfoTypes.INTERRUPTIBLE_RANGE_DELTA2_ENCBASE, ref bitOffset);
+
+            SlotTable = new GcSlotTable(image, machine, gcInfoTypes, ref bitOffset);
+        }
 
-            EnumerateInterruptibleRanges(&SetIsInterruptibleCB, this);*/
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            string tab = "    ";
+
+            sb.AppendLine($"{tab}Version: {Version}");
+            sb.AppendLine($"{tab}CodeLength: {CodeLength}");
+            sb.AppendLine($"{tab}ReturnKind: {Enum.GetName(typeof(ReturnKinds), ReturnKind)}");
+            sb.AppendLine($"{tab}ValidRangeStart: {ValidRangeStart}");
+            sb.AppendLine($"{tab}ValidRangeEnd: {ValidRangeEnd}");
+            if (SecurityObjectStackSlot != -1)
+                sb.AppendLine($"{tab}SecurityObjectStackSlot: {SecurityObjectStackSlot}");
+            if (GSCookieStackSlot != -1)
+                sb.AppendLine($"{tab}GSCookieStackSlot: {GSCookieStackSlot}");
+            if (PSPSymStackSlot != -1)
+                sb.AppendLine($"{tab}PSPSymStackSlot: {PSPSymStackSlot}");
+            if (GenericsInstContextStackSlot != -1)
+                sb.AppendLine($"{tab}GenericsInstContextStackSlot: {GenericsInstContextStackSlot}");
+            if (StackBaseRegister != 0xffffffff)
+                sb.AppendLine($"{tab}StackBaseRegister: {StackBaseRegister}");
+            if (SizeOfEditAndContinuePreservedArea != 0xffffffff)
+                sb.AppendLine($"{tab}SizeOfEditAndContinuePreservedArea: {SizeOfEditAndContinuePreservedArea}");
+            if (ReversePInvokeFrameStackSlot != -1)
+                sb.AppendLine($"{tab}ReversePInvokeFrameStackSlot: {ReversePInvokeFrameStackSlot}");
+            sb.AppendLine($"{tab}SizeOfStackOutgoingAndScratchArea: {SizeOfStackOutgoingAndScratchArea}");
+            sb.AppendLine($"{tab}NumSafePoints: {NumSafePoints}");
+            sb.AppendLine($"{tab}NumInterruptibleRanges: {NumInterruptibleRanges}");
+            sb.AppendLine($"{tab}SafePointOffsets:");
+            foreach (uint offset in SafePointOffsets)
+            {
+                sb.AppendLine($"{tab}{tab}{offset}");
+            }
+            sb.AppendLine($"{tab}SafePointOffsets:");
+            foreach (InterruptibleRange range in InterruptibleRanges)
+            {
+                sb.AppendLine($"{tab}{tab}start:{range.StartOffset}, end:{range.StopOffset}");
+            }
+            sb.AppendLine($"{tab}SlotTable:");
+            sb.Append(SlotTable.ToString());
 
+            return sb.ToString();
         }
 
         private int DenormalizeCodeLength(Machine target, int x)
@@ -316,44 +327,35 @@ namespace R2RDump
             return result;
         }
 
-
-        private uint FindSafePoint(byte[] image, Machine target, uint breakOffset, ref int currentPos)
+        private IEnumerable<uint> EnumerateSafePoints(byte[] image, ref int bitOffset)
         {
-            if (NumSafePoints == 0)
-                return 0;
-
-            int savedPos = currentPos;
+            List<uint> safePoints = new List<uint>();
             uint numBitsPerOffset = CeilOfLog2(CodeLength);
-            uint result = NumSafePoints;
+            for (int i = 0; i < NumSafePoints; i++)
+            {
+                uint normOffset = (uint)NativeReader.ReadBits(image, (int)numBitsPerOffset, ref bitOffset);
+                safePoints.Add(normOffset);
+            }
+            return safePoints;
+        }
+
+        private IEnumerable<InterruptibleRange> EnumerateinterruptibleRanges(byte[] image, int interruptibleRangeDelta1EncBase, int interruptibleRangeDelta2EncBase, ref int bitOffset)
+        {
+            List<InterruptibleRange> ranges = new List<InterruptibleRange>();
+            uint lastinterruptibleRangeStopOffset = 0;
 
-            // Safepoints are encoded with a -1 adjustment
-            // but normalizing them masks off the low order bit
-            // Thus only bother looking if the address is odd
-            if ((target != Machine.Arm && target != Machine.Arm64) || (breakOffset & 1) != 0)
+            for (uint i = 0; i < NumInterruptibleRanges; i++)
             {
-                int low = 0;
-                int high = (int)NumSafePoints;
+                uint normStartDelta = NativeReader.DecodeVarLengthUnsigned(image, interruptibleRangeDelta1EncBase, ref bitOffset);
+                uint normStopDelta = NativeReader.DecodeVarLengthUnsigned(image, interruptibleRangeDelta2EncBase, ref bitOffset) + 1;
 
-                while (low < high)
-                {
-                    int mid = (low + high) / 2;
-                    currentPos = (int)(savedPos + mid * numBitsPerOffset);
-                    uint normOffset = (uint)NativeReader.ReadBits(image, (int)numBitsPerOffset, ref currentPos);
-                    if (normOffset == breakOffset)
-                    {
-                        result = (uint)mid;
-                        break;
-                    }
-
-                    if (normOffset < breakOffset)
-                        low = mid + 1;
-                    else
-                        high = mid;
-                }
-            }
+                uint rangeStartOffset = lastinterruptibleRangeStopOffset + normStartDelta;
+                uint rangeStopOffset = rangeStartOffset + normStopDelta;
+                ranges.Add(new InterruptibleRange(rangeStartOffset, rangeStopOffset));
 
-            currentPos = (int)(savedPos + NumSafePoints * numBitsPerOffset);
-            return result;
+                lastinterruptibleRangeStopOffset = rangeStopOffset;
+            }
+            return ranges;
         }
 
         /// <summary>
diff --git a/src/coreclr/src/tools/r2rdump/GCInfoTypes.cs b/src/coreclr/src/tools/r2rdump/GCInfoTypes.cs
new file mode 100644 (file)
index 0000000..46354f8
--- /dev/null
@@ -0,0 +1,83 @@
+// 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.Reflection.PortableExecutable;
+
+namespace R2RDump
+{
+    class GcInfoTypes
+    {
+        public int SIZE_OF_RETURN_KIND_SLIM { get; } = 2;
+        public int SIZE_OF_RETURN_KIND_FAT { get; } = 2;
+        public int CODE_LENGTH_ENCBASE { get; } = 8;
+        public int NORM_PROLOG_SIZE_ENCBASE { get; } = 5;
+        public int SECURITY_OBJECT_STACK_SLOT_ENCBASE { get; } = 6;
+        public int GS_COOKIE_STACK_SLOT_ENCBASE { get; } = 6;
+        public int PSP_SYM_STACK_SLOT_ENCBASE { get; } = 6;
+        public int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE { get; } = 6;
+        public int STACK_BASE_REGISTER_ENCBASE { get; } = 3;
+        public int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE { get; } = 4;
+        public int REVERSE_PINVOKE_FRAME_ENCBASE { get; } = 6;
+        public int SIZE_OF_STACK_AREA_ENCBASE { get; } = 3;
+        public int NUM_SAFE_POINTS_ENCBASE { get; } = 3;
+        public int NUM_INTERRUPTIBLE_RANGES_ENCBASE { get; } = 1;
+        public int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE { get; } = 6;
+        public int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE { get; } = 6;
+
+        public int MAX_PREDECODED_SLOTS { get; } = 64;
+        public int NUM_REGISTERS_ENCBASE { get; } = 2;
+        public int NUM_STACK_SLOTS_ENCBASE { get; } = 2;
+        public int NUM_UNTRACKED_SLOTS_ENCBASE { get; } = 1;
+        public int REGISTER_ENCBASE { get; } = 3;
+        public int REGISTER_DELTA_ENCBASE { get; } = 2;
+        public int STACK_SLOT_ENCBASE { get; } = 6;
+        public int STACK_SLOT_DELTA_ENCBASE { get; } = 4;
+
+        public GcInfoTypes(Machine machine)
+        {
+            switch (machine)
+            {
+                case Machine.Amd64:
+                    SIZE_OF_RETURN_KIND_FAT = 4;
+                    NUM_SAFE_POINTS_ENCBASE = 2;
+                    break;
+                case Machine.Arm:
+                    CODE_LENGTH_ENCBASE = 7;
+                    SECURITY_OBJECT_STACK_SLOT_ENCBASE = 5;
+                    GS_COOKIE_STACK_SLOT_ENCBASE = 5;
+                    PSP_SYM_STACK_SLOT_ENCBASE = 5;
+                    GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 5;
+                    STACK_BASE_REGISTER_ENCBASE = 1;
+                    SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3;
+                    REVERSE_PINVOKE_FRAME_ENCBASE = 5;
+                    NUM_INTERRUPTIBLE_RANGES_ENCBASE = 2;
+                    INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 4;
+                    NUM_STACK_SLOTS_ENCBASE = 3;
+                    NUM_UNTRACKED_SLOTS_ENCBASE = 3;
+                    REGISTER_ENCBASE = 2;
+                    REGISTER_DELTA_ENCBASE = 1;
+                    break;
+                case Machine.Arm64:
+                    SIZE_OF_RETURN_KIND_FAT = 4;
+                    STACK_BASE_REGISTER_ENCBASE = 2;
+                    NUM_REGISTERS_ENCBASE = 3;
+                    break;
+                case Machine.I386:
+                    CODE_LENGTH_ENCBASE = 6;
+                    NORM_PROLOG_SIZE_ENCBASE = 4;
+                    SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3;
+                    SIZE_OF_STACK_AREA_ENCBASE = 6;
+                    NUM_SAFE_POINTS_ENCBASE = 4;
+                    INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 5;
+                    INTERRUPTIBLE_RANGE_DELTA2_ENCBASE = 5;
+                    NUM_REGISTERS_ENCBASE = 3;
+                    NUM_STACK_SLOTS_ENCBASE = 5;
+                    NUM_UNTRACKED_SLOTS_ENCBASE = 5;
+                    REGISTER_DELTA_ENCBASE = 3;
+                    break;
+            }
+        }
+            
+}
+}
diff --git a/src/coreclr/src/tools/r2rdump/GCSlotTable.cs b/src/coreclr/src/tools/r2rdump/GCSlotTable.cs
new file mode 100644 (file)
index 0000000..3229069
--- /dev/null
@@ -0,0 +1,207 @@
+// 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;
+
+namespace R2RDump
+{
+    class GcSlotTable
+    {
+        public struct GcSlot
+        {
+            public int RegisterNumber { get; }
+            public GcStackSlot StackSlot { get; }
+            public GcSlotFlags Flags { get; }
+
+            public GcSlot(int registerNumber, GcStackSlot stack, GcSlotFlags flags)
+            {
+                RegisterNumber = registerNumber;
+                StackSlot = stack;
+                Flags = flags;
+            }
+
+            public override string ToString()
+            {
+                StringBuilder sb = new StringBuilder();
+                string tab = "    ";
+
+                if (StackSlot != null)
+                {
+                    sb.AppendLine($"{tab}{tab}{tab}Stack:");
+                    sb.AppendLine(StackSlot.ToString());
+                }
+                else
+                {
+                    sb.AppendLine($"{tab}{tab}{tab}RegisterNumber: {RegisterNumber}");
+                }
+                sb.AppendLine($"{tab}{tab}{tab}Flags: {Flags}");
+
+                return sb.ToString();
+            }
+        }
+
+        public enum GcSlotFlags
+        {
+            GC_SLOT_BASE = 0x0,
+            GC_SLOT_INTERIOR = 0x1,
+            GC_SLOT_PINNED = 0x2,
+            GC_SLOT_UNTRACKED = 0x4,
+
+            // For internal use by the encoder/decoder
+            GC_SLOT_IS_REGISTER = 0x8,
+            GC_SLOT_IS_DELETED = 0x10,
+        };
+
+        public enum GcStackSlotBase
+        {
+            GC_CALLER_SP_REL = 0x0,
+            GC_SP_REL = 0x1,
+            GC_FRAMEREG_REL = 0x2,
+
+            GC_SPBASE_FIRST = GC_CALLER_SP_REL,
+            GC_SPBASE_LAST = GC_FRAMEREG_REL,
+        };
+
+        public class GcStackSlot
+        {
+            public int SpOffset { get; }
+            public GcStackSlotBase Base { get; }
+            public GcStackSlot(int spOffset, GcStackSlotBase stackSlotBase)
+            {
+                SpOffset = spOffset;
+                Base = stackSlotBase;
+            }
+
+            public override string ToString()
+            {
+                StringBuilder sb = new StringBuilder();
+                string tab = "    ";
+
+                sb.AppendLine($"{tab}{tab}{tab}{tab}SpOffset: {SpOffset}");
+                sb.Append($"{tab}{tab}{tab}{tab}Base: {Enum.GetName(typeof(GcStackSlotBase), Base)}");
+
+                return sb.ToString();
+            }
+        };
+
+        public uint NumRegisters { get; }
+        public uint NumStackSlots { get; }
+        public uint NumUntracked { get; }
+        public uint NumSlots { get; }
+        public List<GcSlot> GcSlots { get; }
+
+        public GcSlotTable(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, ref int bitOffset)
+        {
+            if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+            {
+                NumRegisters = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_REGISTERS_ENCBASE, ref bitOffset);
+            }
+            if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+            {
+                NumStackSlots = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_STACK_SLOTS_ENCBASE, ref bitOffset);
+                NumUntracked = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.NUM_UNTRACKED_SLOTS_ENCBASE, ref bitOffset);
+            }
+            NumSlots = NumRegisters + NumStackSlots + NumUntracked;
+
+            GcSlots = new List<GcSlot>();
+            if (NumRegisters > 0)
+            {
+                DecodeRegisters(image, gcInfoTypes, ref bitOffset);
+            }
+            if ((NumStackSlots > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+            {
+                DecodeStackSlots(image, machine, gcInfoTypes, NumStackSlots, ref bitOffset);
+            }
+            if ((NumUntracked > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+            {
+                DecodeStackSlots(image, machine, gcInfoTypes, NumUntracked, ref bitOffset);
+            }
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            string tab = "    ";
+
+            sb.AppendLine($"{tab}{tab}NumSlots({NumSlots}) = NumRegisters({NumRegisters}) + NumStackSlots({NumStackSlots}) + NumUntracked({NumUntracked})");
+            sb.AppendLine($"{tab}{tab}GcSlots:");
+            sb.AppendLine($"{tab}{tab}{tab}-------------------------");
+            foreach (GcSlot slot in GcSlots)
+            {
+                sb.Append(slot.ToString());
+                sb.AppendLine($"{tab}{tab}{tab}-------------------------");
+            }
+
+            return sb.ToString();
+        }
+
+        private int DenormalizeStackSlot(Machine target, int x)
+        {
+            switch (target)
+            {
+                case Machine.Amd64:
+                    return (x << 3);
+                case Machine.Arm:
+                    return (x << 2);
+                case Machine.Arm64:
+                    return (x << 3);
+            }
+            return x;
+        }
+
+        private void DecodeRegisters(byte[] image, GcInfoTypes gcInfoTypes, ref int bitOffset)
+        {
+            // We certainly predecode the first register
+            uint regNum = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_ENCBASE, ref bitOffset);
+            GcSlotFlags flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+            GcSlots.Add(new GcSlot((int)regNum, null, flags));
+
+            for (int i = 1; i < NumRegisters && i < gcInfoTypes.MAX_PREDECODED_SLOTS; i++)
+            {
+                if ((uint)flags != 0)
+                {
+                    regNum = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_ENCBASE, ref bitOffset);
+                    flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+                }
+                else
+                {
+                    uint regDelta = NativeReader.DecodeVarLengthUnsigned(image, gcInfoTypes.REGISTER_DELTA_ENCBASE, ref bitOffset) + 1;
+                    regNum += regDelta;
+                }
+                GcSlots.Add(new GcSlot((int)regNum, null, flags));
+            }
+        }
+
+        private void DecodeStackSlots(byte[] image, Machine machine, GcInfoTypes gcInfoTypes, uint nSlots, ref int bitOffset)
+        {
+            // We have stack slots left and more room to predecode
+            GcStackSlotBase spBase = (GcStackSlotBase)NativeReader.ReadBits(image, 2, ref bitOffset);
+            int normSpOffset = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_ENCBASE, ref bitOffset);
+            int spOffset = DenormalizeStackSlot(machine, normSpOffset);
+            GcSlotFlags flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+            GcSlots.Add(new GcSlot(-1, new GcStackSlot(spOffset, spBase), flags));
+
+            for (int i = 1; i < nSlots && GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS; i++)
+            {
+                spBase = (GcStackSlotBase)NativeReader.ReadBits(image, 2, ref bitOffset);
+                if ((uint)flags != 0)
+                {
+                    normSpOffset = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_ENCBASE, ref bitOffset);
+                    spOffset = DenormalizeStackSlot(machine, normSpOffset);
+                    flags = (GcSlotFlags)NativeReader.ReadBits(image, 2, ref bitOffset);
+                }
+                else
+                {
+                    int normSpOffsetDelta = NativeReader.DecodeVarLengthSigned(image, gcInfoTypes.STACK_SLOT_DELTA_ENCBASE, ref bitOffset);
+                    normSpOffset += normSpOffsetDelta;
+                    spOffset = DenormalizeStackSlot(machine, normSpOffset);
+                }
+                GcSlots.Add(new GcSlot(-1, new GcStackSlot(spOffset, spBase), flags));
+            }
+        }
+    }
+}
index eb5c777..bcd3d48 100644 (file)
@@ -117,6 +117,8 @@ namespace R2RDump
                     val ^= extraBits;
                 }
             }
+            int mask = (1 << numBits) - 1;
+            val &= mask;
             bitOffset += numBits;
             return val;
         }
index b287fa0..06ae90f 100644 (file)
@@ -11,20 +11,21 @@ namespace R2RDump
 {
     class R2RDump
     {
-        private bool _help = false;
+        private bool _help;
         private IReadOnlyList<string> _inputFilenames = Array.Empty<string>();
         private string _outputFilename = null;
-        private bool _raw = false;
-        private bool _header = false;
-        private bool _disasm = false;
+        private bool _raw;
+        private bool _header;
+        private bool _disasm;
         private IReadOnlyList<string> _queries = Array.Empty<string>();
         private IReadOnlyList<string> _keywords = Array.Empty<string>();
         private IReadOnlyList<int> _runtimeFunctions = Array.Empty<int>();
         private IReadOnlyList<string> _sections = Array.Empty<string>();
-        private bool _diff = false;
+        private bool _diff;
         private long _disassembler;
-        private bool _types = false;
-        private bool _unwind = false;
+        private bool _types;
+        private bool _unwind;
+        private bool _gc;
         private TextWriter _writer;
 
         private R2RDump()
@@ -51,6 +52,7 @@ namespace R2RDump
                 syntax.DefineOptionList("s|section", ref _sections, "Get section by keyword");
                 syntax.DefineOption("types", ref _types, "Dump available types");
                 syntax.DefineOption("unwind", ref _unwind, "Dump unwindInfo");
+                syntax.DefineOption("gc", ref _gc, "Dump gcInfo and slot table");
                 syntax.DefineOption("diff", ref _diff, "Compare two R2R images (not yet implemented)"); // not yet implemented
             });
 
@@ -174,6 +176,11 @@ namespace R2RDump
                 _writer.WriteLine("UnwindInfo:");
                 _writer.Write(rtf.UnwindInfo);
             }
+            if (_gc)
+            {
+                _writer.WriteLine("GcInfo:");
+                _writer.Write(rtf.GcInfo);
+            }
             _writer.WriteLine();
         }
 
index 45dfd9d..115e834 100644 (file)
@@ -44,16 +44,16 @@ namespace R2RDump
         public R2RMethod Method { get; }
 
         public UnwindInfo UnwindInfo { get; }
-        public GCInfo GCInfo { get; }
+        public GcInfo GcInfo { get; }
 
-        public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method, UnwindInfo unwindInfo, GCInfo gcInfo)
+        public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method, UnwindInfo unwindInfo, GcInfo gcInfo)
         {
             Id = id;
             StartAddress = startRva;
             UnwindRVA = unwindRva;
             Method = method;
             UnwindInfo = unwindInfo;
-            GCInfo = gcInfo;
+            GcInfo = gcInfo;
             Size = gcInfo.CodeLength;
         }
 
index 5d16911..f641c73 100644 (file)
@@ -219,7 +219,7 @@ namespace R2RDump
                     int unwindOffset = GetOffset(unwindRva);
 
                     UnwindInfo unwindInfo = new UnwindInfo(Image, unwindOffset);
-                    GCInfo gcInfo = new GCInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
+                    GcInfo gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
 
                     method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method, unwindInfo, gcInfo));
                     runtimeFunctionId++;