R2RDump - ARM and ARM64 UnwindInfo (#19618)
authorAmy <amycmyu@gmail.com>
Fri, 24 Aug 2018 16:25:42 +0000 (09:25 -0700)
committerGitHub <noreply@github.com>
Fri, 24 Aug 2018 16:25:42 +0000 (09:25 -0700)
* UnwindInfo for Arm

* ARM64 unwindInfo, change Machine.Arm to Machine.ArmThumb2

* Fix incorrect location of UnwindCode and GcTransition in the disasm

* Use x64 GcInfo for Arm and Arm64

* Cleanup, update readme

12 files changed:
src/tools/r2rdump/Amd64/GcInfo.cs
src/tools/r2rdump/Amd64/GcTransition.cs
src/tools/r2rdump/Arm/Registers.cs [new file with mode: 0644]
src/tools/r2rdump/Arm/UnwindInfo.cs [new file with mode: 0644]
src/tools/r2rdump/Arm64/Registers.cs [new file with mode: 0644]
src/tools/r2rdump/Arm64/UnwindInfo.cs [new file with mode: 0644]
src/tools/r2rdump/CoreDisTools.cs
src/tools/r2rdump/GCInfoTypes.cs
src/tools/r2rdump/R2RMethod.cs
src/tools/r2rdump/R2RReader.cs
src/tools/r2rdump/README.md
src/tools/r2rdump/TextDumper.cs

index acc1605..b493428 100644 (file)
@@ -229,14 +229,26 @@ namespace R2RDump.Amd64
                 sb.AppendLine($"\tGenericsInstContextStackSlot: caller.sp{GenericsInstContextStackSlot:+#;-#;+0}");
             }
 
-            if (StackBaseRegister != 0xffffffff)
-                sb.AppendLine($"\tStackBaseRegister: {(Registers)StackBaseRegister}");
             if (_machine == Machine.Amd64)
             {
+                if (StackBaseRegister != 0xffffffff)
+                    sb.AppendLine($"\tStackBaseRegister: {(Amd64.Registers)StackBaseRegister}");
                 sb.AppendLine($"\tWants Report Only Leaf: {_wantsReportOnlyLeaf}");
             }
-            else if (_machine == Machine.Arm || _machine == Machine.Arm64)
+            else if (_machine == Machine.ArmThumb2 || _machine == Machine.Arm64)
             {
+                if (StackBaseRegister != 0xffffffff)
+                {
+                    if (_machine == Machine.ArmThumb2)
+                    {
+                        sb.AppendLine($"\tStackBaseRegister: {(Arm.Registers)StackBaseRegister}");
+                    }
+                    else
+                    {
+                        sb.AppendLine($"\tStackBaseRegister: {(Arm64.Registers)StackBaseRegister}");
+                    }
+                }
+
                 sb.AppendLine($"\tHas Tailcalls: {_wantsReportOnlyLeaf}");
             }
 
@@ -415,7 +427,7 @@ namespace R2RDump.Amd64
                     while (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
                     {
                         int transitionOffset = NativeReader.ReadBits(image, _gcInfoTypes.NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ref bitOffset) + normChunkBaseCodeOffset;
-                        transitions.Add(new GcTransition(transitionOffset, slotId, isLive, currentChunk, SlotTable));
+                        transitions.Add(new GcTransition(transitionOffset, slotId, isLive, currentChunk, SlotTable, _machine));
                         isLive = !isLive;
                     }
                     slotId++;
@@ -517,6 +529,7 @@ namespace R2RDump.Amd64
                         currentRangeLength = (int)(currentRange.StopOffset - currentRange.StartOffset);
                         codeOffset = transition.CodeOffset + (int)currentRange.StartOffset - cumInterruptibleLength;
                     }
+
                     transition.CodeOffset = codeOffset;
                     if (!updatedTransitions.ContainsKey(codeOffset))
                     {
index 0fb1e7f..ef99641 100644 (file)
@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Reflection.PortableExecutable;
 using System.Text;
 using System.Xml.Serialization;
 
@@ -32,13 +33,13 @@ namespace R2RDump.Amd64
 
         public GcTransition() { }
 
-        public GcTransition(int codeOffset, int slotId, bool isLive, int chunkId, GcSlotTable slotTable)
+        public GcTransition(int codeOffset, int slotId, bool isLive, int chunkId, GcSlotTable slotTable, Machine machine)
         {
             CodeOffset = codeOffset;
             SlotId = slotId;
             IsLive = isLive;
             ChunkId = chunkId;
-            SlotState = GetSlotState(slotTable);
+            SlotState = GetSlotState(slotTable, machine);
         }
 
         public override string ToString()
@@ -46,13 +47,22 @@ namespace R2RDump.Amd64
             return SlotState;
         }
 
-        public string GetSlotState(GcSlotTable slotTable)
+        public string GetSlotState(GcSlotTable slotTable, Machine machine)
         {
             GcSlotTable.GcSlot slot = slotTable.GcSlots[SlotId];
             string slotStr = "";
             if (slot.StackSlot == null)
             {
-                slotStr = Enum.GetName(typeof(Registers), slot.RegisterNumber);
+                Type regType = typeof(Amd64.Registers);
+                if (machine == Machine.ArmThumb2)
+                {
+                    regType = typeof(Arm.Registers);
+                }
+                else
+                {
+                    regType = typeof(Arm64.Registers);
+                }
+                slotStr = Enum.GetName(regType, slot.RegisterNumber);
             }
             else
             {
diff --git a/src/tools/r2rdump/Arm/Registers.cs b/src/tools/r2rdump/Arm/Registers.cs
new file mode 100644 (file)
index 0000000..2985633
--- /dev/null
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace R2RDump.Arm
+{
+    /// <summary>
+    /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/jit/unwindarm.cpp">src/jit/unwindarm.cpp</a> mapRegNumToDwarfReg
+    /// </summary>
+    public enum Registers
+    {
+        R0,
+        R1,
+        R2,
+        R3,
+        R4,
+        R5,
+        R6,
+        R7,
+        R8,
+        R9,
+        R10,
+        R11,
+        R12,
+        R13,
+        R14,
+        R15,
+        F0,
+        F1,
+        F2,
+        F3,
+        F4,
+        F5,
+        F6,
+        F7,
+        F8,
+        F9,
+        F10,
+        F11,
+        F12,
+        F13,
+        F14,
+        F15,
+        F16,
+        F17,
+        F18,
+        F19,
+        F20,
+        F21,
+        F22,
+        F23,
+        F24,
+        F25,
+        F26,
+        F27,
+        F28,
+        F29,
+        F30,
+        F31,
+    }
+}
diff --git a/src/tools/r2rdump/Arm/UnwindInfo.cs b/src/tools/r2rdump/Arm/UnwindInfo.cs
new file mode 100644 (file)
index 0000000..1c5a353
--- /dev/null
@@ -0,0 +1,167 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.Arm
+{
+    public class Epilog
+    {
+        [XmlAttribute("Index")]
+        public int Index { get; set; }
+
+        public uint EpilogStartOffset { get; set; }
+        public uint Res { get; set; }
+        public uint Condition { get; set; }
+        public uint EpilogStartIndex { get; set; }
+        public uint EpilogStartOffsetFromMainFunctionBegin { get; set; }
+
+        public Epilog() { }
+
+        public Epilog(int index, int dw, uint startOffset)
+        {
+            Index = index;
+
+            EpilogStartOffset = UnwindInfo.ExtractBits(dw, 0, 18);
+            Res = UnwindInfo.ExtractBits(dw, 18, 2);
+            Condition = UnwindInfo.ExtractBits(dw, 20, 4);
+            EpilogStartIndex = UnwindInfo.ExtractBits(dw, 24, 8);
+
+            // Note that epilogStartOffset for a funclet is the offset from the beginning
+            // of the current funclet, not the offset from the beginning of the main function.
+            // To help find it when looking through JitDump output, also show the offset from
+            // the beginning of the main function.
+            EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 2 + startOffset;
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.AppendLine($"\t\tEpilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 2:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}");
+            sb.AppendLine($"\t\tCondition: {Condition} (0x{Condition:X})" + ((Condition == 0xE) ? " (always)" : ""));
+            sb.Append($"\t\tEpilog Start Index: {EpilogStartIndex} (0x{EpilogStartIndex:X})");
+            return sb.ToString();
+        }
+    }
+
+    public class UnwindCode
+    {
+        [XmlAttribute("Index")]
+        public int Index { get; set; }
+
+        public UnwindCode() { }
+
+        public UnwindCode(int index)
+        {
+            Index = index;
+        }
+    }
+
+    /// <summary>
+    /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/jit/unwindarm.cpp">src/jit/unwindarm.cpp</a> DumpUnwindInfo
+    /// </summary>
+    public class UnwindInfo : BaseUnwindInfo
+    {
+        public uint CodeWords { get; set; }
+        public uint EpilogCount { get; set; }
+        public uint FBit { get; set; }
+        public uint EBit { get; set; }
+        public uint XBit { get; set; }
+        public uint Vers { get; set; }
+        public uint FunctionLength { get; set; }
+
+        public uint ExtendedCodeWords { get; set; }
+        public uint ExtendedEpilogCount { get; set; }
+
+        public Epilog[] Epilogs { get; set; }
+
+        public UnwindInfo() { }
+
+        public UnwindInfo(byte[] image, int offset)
+        {
+            uint startOffset = (uint)offset;
+
+            int dw = NativeReader.ReadInt32(image, ref offset);
+            CodeWords = ExtractBits(dw, 28, 4);
+            EpilogCount = ExtractBits(dw, 23, 5);
+            FBit = ExtractBits(dw, 22, 1);
+            EBit = ExtractBits(dw, 21, 1);
+            XBit = ExtractBits(dw, 20, 1);
+            Vers = ExtractBits(dw, 18, 2);
+            FunctionLength = ExtractBits(dw, 0, 18) * 2;
+
+            if (CodeWords == 0 && EpilogCount == 0)
+            {
+                // We have an extension word specifying a larger number of Code Words or Epilog Counts
+                // than can be specified in the header word.
+                dw = NativeReader.ReadInt32(image, ref offset);
+                ExtendedCodeWords = ExtractBits(dw, 16, 8);
+                ExtendedEpilogCount = ExtractBits(dw, 0, 16);
+            }
+
+            bool[] epilogStartAt = new bool[256]; // One byte per possible epilog start index; initialized to false
+            
+            if (EBit == 0)
+            {
+                Epilogs = new Epilog[EpilogCount];
+                if (EpilogCount != 0)
+                {
+                    for (int scope = 0; scope < EpilogCount; scope++)
+                    {
+                        dw = NativeReader.ReadInt32(image, ref offset);
+                        Epilogs[scope] = new Epilog(scope, dw, startOffset);
+                        epilogStartAt[Epilogs[scope].EpilogStartIndex] = true; // an epilog starts at this offset in the unwind codes
+                    }
+                }
+            }
+            else
+            {
+                Epilogs = new Epilog[0];
+                epilogStartAt[EpilogCount] = true; // the one and only epilog starts its unwind codes at this offset
+            }
+
+            Size = offset - (int)startOffset + (int)CodeWords * 4;
+            int alignmentPad = ((Size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - Size;
+            Size += (alignmentPad + sizeof(uint));
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.AppendLine($"\tCodeWords: {CodeWords}");
+            sb.AppendLine($"\tEpilogCount: {EpilogCount}");
+            sb.AppendLine($"\tFBit: {FBit}");
+            sb.AppendLine($"\tEBit: {EBit}");
+            sb.AppendLine($"\tXBit: {XBit}");
+            sb.AppendLine($"\tVers: {Vers}");
+            sb.AppendLine($"\tFunctionLength: {FunctionLength}");
+            if (CodeWords == 0 && EpilogCount == 0)
+            {
+                sb.AppendLine("\t---- Extension word ----");
+                sb.AppendLine($"\tExtended Code Words: {CodeWords}");
+                sb.AppendLine($"\tExtended Epilog Count: {EpilogCount}");
+            }
+            if (EpilogCount == 0)
+            {
+                sb.AppendLine("\tNo epilogs");
+            }
+            else
+            {
+                for (int i = 0; i < Epilogs.Length; i++)
+                {
+                    sb.AppendLine("\t\t-------------------------");
+                    sb.AppendLine(Epilogs[i].ToString());
+                    sb.AppendLine("\t\t-------------------------");
+                }
+            }
+            return sb.ToString();
+        }
+
+        internal static uint ExtractBits(int dw, int start, int length)
+        {
+            return (uint)((dw >> start) & ((1 << length) - 1));
+        }
+    }
+}
diff --git a/src/tools/r2rdump/Arm64/Registers.cs b/src/tools/r2rdump/Arm64/Registers.cs
new file mode 100644 (file)
index 0000000..d205808
--- /dev/null
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace R2RDump.Arm64
+{
+    public enum Registers
+    {
+        X0,
+        X1,
+        X2,
+        X3,
+        X4,
+        X5,
+        X6,
+        X7,
+        X8,
+        X9,
+        X10,
+        X11,
+        X12,
+        X13,
+        X14,
+        X15,
+        X16,
+        X17,
+        X18,
+        X19,
+        X20,
+        X21,
+        X22,
+        X23,
+        X24,
+        X25,
+        X26,
+        X27,
+        X28,
+        X29,
+        X30,
+    }
+}
diff --git a/src/tools/r2rdump/Arm64/UnwindInfo.cs b/src/tools/r2rdump/Arm64/UnwindInfo.cs
new file mode 100644 (file)
index 0000000..45acb43
--- /dev/null
@@ -0,0 +1,167 @@
+// 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.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump.Arm64
+{
+    public class Epilog
+    {
+        [XmlAttribute("Index")]
+        public int Index { get; set; }
+
+        public uint EpilogStartOffset { get; set; }
+        public uint Res { get; set; }
+        public uint Condition { get; set; }
+        public uint EpilogStartIndex { get; set; }
+        public uint EpilogStartOffsetFromMainFunctionBegin { get; set; }
+
+        public Epilog() { }
+
+        public Epilog(int index, int dw, uint startOffset)
+        {
+            Index = index;
+
+            EpilogStartOffset = UnwindInfo.ExtractBits(dw, 0, 18);
+            Res = UnwindInfo.ExtractBits(dw, 18, 4);
+            Condition = UnwindInfo.ExtractBits(dw, 20, 4);
+            EpilogStartIndex = UnwindInfo.ExtractBits(dw, 22, 10);
+
+            // Note that epilogStartOffset for a funclet is the offset from the beginning
+            // of the current funclet, not the offset from the beginning of the main function.
+            // To help find it when looking through JitDump output, also show the offset from
+            // the beginning of the main function.
+            EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 4 + startOffset;
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.AppendLine($"\t\tEpilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 4:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}");
+            sb.AppendLine($"\t\tCondition: {Condition} (0x{Condition:X})" + ((Condition == 0xE) ? " (always)" : ""));
+            sb.Append($"\t\tEpilog Start Index: {EpilogStartIndex} (0x{EpilogStartIndex:X})");
+            return sb.ToString();
+        }
+    }
+
+    public class UnwindCode
+    {
+        [XmlAttribute("Index")]
+        public int Index { get; set; }
+
+        public UnwindCode() { }
+
+        public UnwindCode(int index)
+        {
+            Index = index;
+
+        }
+    }
+
+    /// <summary>
+    /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/jit/unwindarm.cpp">src/jit/unwindarm.cpp</a> DumpUnwindInfo
+    /// </summary>
+    public class UnwindInfo : BaseUnwindInfo
+    {
+        public uint CodeWords { get; set; }
+        public uint EpilogCount { get; set; }
+        public uint EBit { get; set; }
+        public uint XBit { get; set; }
+        public uint Vers { get; set; }
+        public uint FunctionLength { get; set; }
+
+        public uint ExtendedCodeWords { get; set; }
+        public uint ExtendedEpilogCount { get; set; }
+
+        public Epilog[] Epilogs { get; set; }
+
+        public UnwindInfo() { }
+
+        public UnwindInfo(byte[] image, int offset)
+        {
+            uint startOffset = (uint)offset;
+
+            int dw = NativeReader.ReadInt32(image, ref offset);
+            CodeWords = ExtractBits(dw, 27, 5);
+            EpilogCount = ExtractBits(dw, 22, 5);
+            EBit = ExtractBits(dw, 21, 1);
+            XBit = ExtractBits(dw, 20, 1);
+            Vers = ExtractBits(dw, 18, 2);
+            FunctionLength = ExtractBits(dw, 0, 18) * 4;
+
+            if (CodeWords == 0 && EpilogCount == 0)
+            {
+                // We have an extension word specifying a larger number of Code Words or Epilog Counts
+                // than can be specified in the header word.
+                dw = NativeReader.ReadInt32(image, ref offset);
+                ExtendedCodeWords = ExtractBits(dw, 16, 8);
+                ExtendedEpilogCount = ExtractBits(dw, 0, 16);
+            }
+
+            bool[] epilogStartAt = new bool[1024]; // One byte per possible epilog start index; initialized to false
+            
+            if (EBit == 0)
+            {
+                Epilogs = new Epilog[EpilogCount];
+                if (EpilogCount != 0)
+                {
+                    for (int scope = 0; scope < EpilogCount; scope++)
+                    {
+                        dw = NativeReader.ReadInt32(image, ref offset);
+                        Epilogs[scope] = new Epilog(scope, dw, startOffset);
+                        epilogStartAt[Epilogs[scope].EpilogStartIndex] = true; // an epilog starts at this offset in the unwind codes
+                    }
+                }
+            }
+            else
+            {
+                Epilogs = new Epilog[0];
+                epilogStartAt[EpilogCount] = true; // the one and only epilog starts its unwind codes at this offset
+            }
+
+            
+
+            Size = offset - (int)startOffset + (int)CodeWords * 4;
+            int alignmentPad = ((Size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - Size;
+            Size += (alignmentPad + sizeof(uint));
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.AppendLine($"\tCodeWords: {CodeWords}");
+            sb.AppendLine($"\tEpilogCount: {EpilogCount}");
+            sb.AppendLine($"\tEBit: {EBit}");
+            sb.AppendLine($"\tXBit: {XBit}");
+            sb.AppendLine($"\tVers: {Vers}");
+            sb.AppendLine($"\tFunctionLength: {FunctionLength}");
+            if (CodeWords == 0 && EpilogCount == 0)
+            {
+                sb.AppendLine("\t---- Extension word ----");
+                sb.AppendLine($"\tExtended Code Words: {CodeWords}");
+                sb.AppendLine($"\tExtended Epilog Count: {EpilogCount}");
+            }
+            if (EpilogCount == 0)
+            {
+                sb.AppendLine("\tNo epilogs");
+            }
+            else
+            {
+                for (int i = 0; i < Epilogs.Length; i++)
+                {
+                    sb.AppendLine("\t\t-------------------------");
+                    sb.AppendLine(Epilogs[i].ToString());
+                    sb.AppendLine("\t\t-------------------------");
+                }
+            }
+            return sb.ToString();
+        }
+
+        internal static uint ExtractBits(int dw, int start, int length)
+        {
+            return (uint)((dw >> start) & ((1 << length) - 1));
+        }
+    }
+}
index 214be53..d1eb8c2 100644 (file)
@@ -72,6 +72,9 @@ namespace R2RDump
                 case Machine.ArmThumb2:
                     target = TargetArch.Target_Thumb;
                     break;
+                default:
+                    R2RDump.WriteWarning($"{machine} not supported on CoreDisTools");
+                    return IntPtr.Zero;
             }
             return InitBufferedDisasm(target);
         }
@@ -102,6 +105,12 @@ namespace R2RDump
 
         public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction)
         {
+            if (_disasm == IntPtr.Zero)
+            {
+                instruction = "";
+                return rtf.Size;
+            }
+
             int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _image, out instruction);
 
             switch (_machine)
index d42fd37..10bad28 100644 (file)
@@ -116,7 +116,7 @@ namespace R2RDump
                     SIZE_OF_RETURN_KIND_FAT = 4;
                     NUM_SAFE_POINTS_ENCBASE = 2;
                     break;
-                case Machine.Arm:
+                case Machine.ArmThumb2:
                     CODE_LENGTH_ENCBASE = 7;
                     SECURITY_OBJECT_STACK_SLOT_ENCBASE = 5;
                     GS_COOKIE_STACK_SLOT_ENCBASE = 5;
@@ -157,7 +157,7 @@ namespace R2RDump
         {
             switch (_target)
             {
-                case Machine.Arm:
+                case Machine.ArmThumb2:
                     return (x << 1);
                 case Machine.Arm64:
                     return (x << 2);
@@ -171,7 +171,7 @@ namespace R2RDump
             {
                 case Machine.Amd64:
                     return (x << 3);
-                case Machine.Arm:
+                case Machine.ArmThumb2:
                     return (x << 2);
                 case Machine.Arm64:
                     return (x << 3);
@@ -185,7 +185,7 @@ namespace R2RDump
             {
                 case Machine.Amd64:
                     return (x ^ 5);
-                case Machine.Arm:
+                case Machine.ArmThumb2:
                     return ((x ^ 7) + 4);
                 case Machine.Arm64:
                     return (x ^ 29);
@@ -199,7 +199,7 @@ namespace R2RDump
             {
                 case Machine.Amd64:
                     return (x << 3);
-                case Machine.Arm:
+                case Machine.ArmThumb2:
                     return (x << 2);
                 case Machine.Arm64:
                     return (x << 3);
index de66550..f462b55 100644 (file)
@@ -99,6 +99,14 @@ namespace R2RDump
             {
                 Size = (int)((x86.UnwindInfo)unwindInfo).FunctionLength;
             }
+            else if (unwindInfo is Arm.UnwindInfo)
+            {
+                Size = (int)((Arm.UnwindInfo)unwindInfo).FunctionLength;
+            }
+            else if (unwindInfo is Arm64.UnwindInfo)
+            {
+                Size = (int)((Arm64.UnwindInfo)unwindInfo).FunctionLength;
+            }
             else if (gcInfo != null)
             {
                 Size = gcInfo.CodeLength;
index 80664fc..d6b39a9 100644 (file)
@@ -329,6 +329,22 @@ namespace R2RDump
                             gcInfo = new x86.GcInfo(Image, unwindOffset, Machine, R2RHeader.MajorVersion);
                         }
                     }
+                    else if (Machine == Machine.ArmThumb2)
+                    {
+                        unwindInfo = new Arm.UnwindInfo(Image, unwindOffset);
+                        if (isEntryPoint[runtimeFunctionId])
+                        {
+                            gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion); // Arm and Arm64 use the same GcInfo format as x64
+                        }
+                    }
+                    else if (Machine == Machine.Arm64)
+                    {
+                        unwindInfo = new Arm64.UnwindInfo(Image, unwindOffset);
+                        if (isEntryPoint[runtimeFunctionId])
+                        {
+                            gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
+                        }
+                    }
 
                     RuntimeFunction rtf = new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, codeOffset, method, unwindInfo, gcInfo);
                     method.RuntimeFunctions.Add(rtf);
index bbd2858..5f022aa 100644 (file)
@@ -45,11 +45,11 @@ Eg. "CoreCLR 4.5.30319.0 __BUILDMACHINE__"
 
 ### READYTORUN_SECTION_IMPORT_SECTIONS
 
-A struct described in [READYTORUN_IMPORT_SECTION](../../inc/readytorun.h)
+A struct described in [READYTORUN_IMPORT_SECTION](../../inc/readytorun.h). Currently not parsed correctly
 
 ### 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.
+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/Arm/Arm64, each RuntimeFunction has RVAs to the start of the assembly, and start of the UnwindInfo.
 
 ### READYTORUN_SECTION_METHODDEF_ENTRYPOINTS
 
@@ -73,7 +73,7 @@ A struct described in [_UNWIND_INFO](../../inc/win64unwind.h). Each RuntimeFunct
 
 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
+For x64, Arm and Arm64, 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
 
 The unwind data info structure is used to record the effects a function has on the stack pointer and where the nonvolatile registers are saved on the stack (see https://msdn.microsoft.com/en-us/library/0kd71y96.aspx)
 
@@ -81,13 +81,13 @@ The unwind data info structure is used to record the effects a function has on t
 
 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)
+The x64/Arm/Arm64 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 that 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:
+In x64/Arm/Arm64, 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
@@ -107,6 +107,10 @@ In x64, GcTransitions are grouped into chunks where each chunk covers NUM_NORM_C
 
 * 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
+* Fix issue with invalid machine type in COFF header (https://github.com/dotnet/coreclr/issues/19592)
+
+* Parse R2RSections: READYTORUN_SECTION_EXCEPTION_INFO, READYTORUN_SECTION_DEBUG_INFO, READYTORUN_SECTION_DELAYLOAD_METHODCALL_THUNKS, READYTORUN_SECTION_INLINING_INFO, READYTORUN_SECTION_PROFILEDATA_INFO (https://github.com/dotnet/coreclr/issues/19616)
 
 * Reenable R2RDumpTests after making it less fragile
+
+* Fix issue with disasm on Arm (https://github.com/dotnet/coreclr/issues/19637)
index df04562..1406fd2 100644 (file)
@@ -120,7 +120,7 @@ namespace R2RDump
             WriteSubDivider();
             _writer.WriteLine(method.ToString());
 
-            if (_gc)
+            if (_gc && method.GcInfo != null)
             {
                 _writer.WriteLine("GcInfo:");
                 _writer.Write(method.GcInfo);
@@ -180,8 +180,6 @@ namespace R2RDump
                 string instr;
                 int instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);
 
-                _writer.Write(instr);
-
                 if (_r2r.Machine == Machine.Amd64 && ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset))
                 {
                     List<Amd64.UnwindCode> codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
@@ -204,6 +202,11 @@ namespace R2RDump
                     }
                 }
 
+                /* According to https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx and src/vm/gcinfodecoder.cpp
+                 * UnwindCode and GcTransition CodeOffsets are encoded with a -1 adjustment (that is, it's the offset of the start of the next instruction)
+                 */
+                _writer.Write(instr);
+
                 CoreDisTools.ClearOutputBuffer();
                 rtfOffset += instrSize;
                 codeOffset += instrSize;
@@ -311,7 +314,7 @@ namespace R2RDump
                                 _writer.WriteLine("Signature Bytes:");
                                 DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int));
                             }
-                            if (importSection.AuxiliaryDataRVA != 0)
+                            if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryData != null)
                             {
                                 _writer.WriteLine("AuxiliaryData Bytes:");
                                 DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size);