Extract gcinfo header, return kind and code length. Use code length as size of the...
authorAmy Yu <amycmyu@gmail.com>
Mon, 4 Jun 2018 23:01:13 +0000 (16:01 -0700)
committerAmy Yu <amycmyu@gmail.com>
Wed, 6 Jun 2018 22:02:03 +0000 (15:02 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/d9abe643288fa61ff19ec88c3e578c0632863b6c

src/coreclr/src/tools/r2rdump/GCInfo.cs
src/coreclr/src/tools/r2rdump/NativeReader.cs
src/coreclr/src/tools/r2rdump/R2RMethod.cs
src/coreclr/src/tools/r2rdump/R2RReader.cs

index 17cc5c69e2fbc70bfe7648f1be661561dce3e292..c40092532fc23e6e17fb8d148e2ffa03405bdaa2 100644 (file)
 // 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 GCInfo
     {
-        public GCInfo(byte[] image, int offset)
+        public enum GcInfoHeaderFlags
+        {
+            GC_INFO_IS_VARARG = 0x1,
+            GC_INFO_HAS_SECURITY_OBJECT = 0x2,
+            GC_INFO_HAS_GS_COOKIE = 0x4,
+            GC_INFO_HAS_PSP_SYM = 0x8,
+            GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK = 0x30,
+            GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE = 0x00,
+            GC_INFO_HAS_GENERICS_INST_CONTEXT_MT = 0x10,
+            GC_INFO_HAS_GENERICS_INST_CONTEXT_MD = 0x20,
+            GC_INFO_HAS_GENERICS_INST_CONTEXT_THIS = 0x30,
+            GC_INFO_HAS_STACK_BASE_REGISTER = 0x40,
+            GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80, // GC_INFO_HAS_TAILCALLS = 0x80, for ARM and ARM64
+            GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100,
+            GC_INFO_REVERSE_PINVOKE_FRAME = 0x200,
+
+            GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9,
+            GC_INFO_FLAGS_BIT_SIZE = 10,
+        };
+
+        public enum ReturnKinds
+        {
+            RT_Scalar = 0,
+            RT_Object = 1,
+            RT_ByRef = 2,
+            RT_Unset = 3,       // Encoding 3 means RT_Float on X86
+            RT_Scalar_Obj = RT_Object << 2 | RT_Scalar,
+            RT_Scalar_ByRef = RT_ByRef << 2 | RT_Scalar,
+
+            RT_Obj_Obj = RT_Object << 2 | RT_Object,
+            RT_Obj_ByRef = RT_ByRef << 2 | RT_Object,
+
+            RT_ByRef_Obj = RT_Object << 2 | RT_ByRef,
+            RT_ByRef_ByRef = RT_ByRef << 2 | RT_ByRef,
+
+            RT_Illegal = 0xFF
+        };
+
+        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;
+
+        public int Version { get; }
+        public int CodeLength { get; }
+        public ReturnKinds ReturnKind { get; }
+
+        public GCInfo(byte[] image, int offset, Machine machine, ushort majorVersion)
         {
+            int sizeOfReturnKindSlim = 2;
+            int sizeOfReturnKindFat = 2;
+            int codeLengthEncBase = 8;
+            switch (machine)
+            {
+                case Machine.Amd64:
+                    sizeOfReturnKindFat = 4;
+                    break;
+                case Machine.Arm:
+                    codeLengthEncBase = 7;
+                    break;
+                case Machine.Arm64:
+                    sizeOfReturnKindFat = 4;
+                    break;
+                case Machine.I386:
+                    codeLengthEncBase = 6;
+                    break;
+            }
 
+            GcInfoHeaderFlags headerFlags;
+            Version = ReadyToRunVersionToGcInfoVersion(majorVersion);
+            int bitOffset = 0;
+            bool slimHeader = (NativeReader.ReadBits(image, 1, ref offset, ref bitOffset) == 0);
+
+            if (slimHeader)
+            {
+                headerFlags = NativeReader.ReadBits(image, 1, ref offset, ref bitOffset) == 1 ? GcInfoHeaderFlags.GC_INFO_HAS_STACK_BASE_REGISTER : 0;
+            }
+            else
+            {
+                int numFlagBits = (int)((Version == 1) ? GcInfoHeaderFlags.GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GcInfoHeaderFlags.GC_INFO_FLAGS_BIT_SIZE);
+                headerFlags = (GcInfoHeaderFlags)NativeReader.ReadBits(image, numFlagBits, ref offset, ref bitOffset);
+            }
+
+            bool hasReversePInvokeFrame = false;
+            if (Version >= MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME) // IsReversePInvokeFrameAvailable
+            {
+                hasReversePInvokeFrame = (headerFlags & GcInfoHeaderFlags.GC_INFO_REVERSE_PINVOKE_FRAME) != 0;
+            }
+
+            if (Version >= MIN_GCINFO_VERSION_WITH_RETURN_KIND) // IsReturnKindAvailable
+            {
+                int returnKindBits = (slimHeader) ? sizeOfReturnKindSlim : sizeOfReturnKindFat;
+                ReturnKind = (ReturnKinds)NativeReader.ReadBits(image, returnKindBits, ref offset, ref bitOffset);
+            }
+
+            CodeLength = DenormalizeCodeLength(machine, (int)NativeReader.DecodeVarLengthUnsigned(image, codeLengthEncBase, ref offset, ref bitOffset));
+        }
+
+        private int DenormalizeCodeLength (Machine target, int x)
+        {
+            if (target == Machine.Arm)
+            {
+                return (x << 1);
+            }
+            if (target == Machine.Arm64)
+            {
+                return (x << 2);
+            }
+            return x;
+        }
+
+        /// <summary>
+        /// GcInfo version is 1 up to ReadyTorun version 1.x. 
+        /// GcInfo version is current from  ReadyToRun version 2.0
+        /// </summary>
+        static int ReadyToRunVersionToGcInfoVersion(int readyToRunMajorVersion)
+        {
+            return (readyToRunMajorVersion == 1) ? 1 : GCINFO_VERSION;
         }
     }
 }
index 59fd2881a6b631fcb7eea2610361a6b6421ceb1e..40c10d039ef65ae1e568b55b1b678ee11a0a0632 100644 (file)
@@ -10,6 +10,8 @@ namespace R2RDump
 {
     class NativeReader
     {
+        private const int BITS_PER_BYTE = 8;
+
         /// <summary>
         /// Extracts a 64bit value from the image byte array
         /// </summary>
@@ -83,7 +85,7 @@ namespace R2RDump
         /// </summary>
         /// <param name="image">PE image</param>
         /// <param name="start">Start index of the value</param>
-        /// /// <remarks>
+        /// <remarks>
         /// The <paramref name="start"/> gets incremented by the size of the value
         /// </remarks>
         public static byte ReadByte(byte[] image, ref int start)
@@ -93,6 +95,51 @@ namespace R2RDump
             return val;
         }
 
+        // <summary>
+        /// Extracts bits from the image byte array
+        /// </summary>
+        /// <remarks>
+        /// The <paramref name="start"/> and <paramref name="bitOffset"/> get incremented by the size of the value
+        /// </remarks>
+        public static int ReadBits(byte[] image, int numBits, ref int start, ref int bitOffset)
+        {
+            int val = image[start] >> bitOffset;
+            bitOffset += numBits;
+            while (bitOffset > BITS_PER_BYTE)
+            {
+                start++;
+                bitOffset -= BITS_PER_BYTE;
+                if (bitOffset > 0)
+                {
+                    int extraBits = image[start] << (numBits - bitOffset);
+                    val ^= extraBits;
+                }
+            }
+            return val;
+        }
+
+        // <summary>
+        /// Decode variable length numbers
+        /// </summary>
+        /// <remarks>
+        /// The <paramref name="start"/> and <paramref name="bitOffset"/> get incremented by the size of the value
+        /// </remarks>
+        public static uint DecodeVarLengthUnsigned(byte[] image, int len, ref int start, ref int bitOffset)
+        {
+            uint numEncodings = (uint)(1 << len);
+            uint result = 0;
+            for (int shift = 0; ; shift += len)
+            {
+                uint currentChunk = (uint)ReadBits(image, len + 1, ref start, ref bitOffset);
+                result |= (currentChunk & (numEncodings - 1)) << shift;
+                if ((currentChunk & numEncodings) == 0)
+                {
+                    // Extension bit is not set, we're done.
+                    return result;
+                }
+            }
+        }
+
         public static uint DecodeUnsigned(byte[] image, uint offset, ref uint pValue)
         {
             if (offset >= image.Length)
index 89288e4dc6711d31e67bb5fd75a88f1b6305d651..45dfd9d96a282c22dbcdf9fe057d997446956895 100644 (file)
@@ -50,13 +50,11 @@ namespace R2RDump
         {
             Id = id;
             StartAddress = startRva;
-            Size = endRva - startRva;
-            if (endRva == -1)
-                Size = -1;
             UnwindRVA = unwindRva;
             Method = method;
             UnwindInfo = unwindInfo;
             GCInfo = gcInfo;
+            Size = gcInfo.CodeLength;
         }
 
         public override string ToString()
index ce0ca699a3b5670732dc0fdc8de7502dfa0f2932..5d16911c4775316596ed68eb1ef3cb2c05716871 100644 (file)
@@ -218,7 +218,10 @@ namespace R2RDump
                     int unwindRva = NativeReader.ReadInt32(Image, ref curOffset);
                     int unwindOffset = GetOffset(unwindRva);
 
-                    method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method, new UnwindInfo(Image, unwindOffset)));
+                    UnwindInfo unwindInfo = new UnwindInfo(Image, unwindOffset);
+                    GCInfo gcInfo = new GCInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
+
+                    method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method, unwindInfo, gcInfo));
                     runtimeFunctionId++;
                 }
                 while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]);