R2RDump - Interweave x64 unwind codes into disasm (dotnet/coreclr#19577)
authorAmy <amycmyu@gmail.com>
Wed, 22 Aug 2018 02:18:33 +0000 (19:18 -0700)
committerGitHub <noreply@github.com>
Wed, 22 Aug 2018 02:18:33 +0000 (19:18 -0700)
* Interweave unwind codes into disasm, fix incorrect x64 register names

* Fix xml dump errors

Commit migrated from https://github.com/dotnet/coreclr/commit/1c2bb89a1b8f6f49167657bccabdf9d0d25756e6

src/coreclr/src/tools/r2rdump/Amd64/Registers.cs
src/coreclr/src/tools/r2rdump/Amd64/UnwindInfo.cs
src/coreclr/src/tools/r2rdump/R2RImportSection.cs
src/coreclr/src/tools/r2rdump/R2RReader.cs
src/coreclr/src/tools/r2rdump/README.md
src/coreclr/src/tools/r2rdump/TextDumper.cs

index 349d3d0..cc1cad3 100644 (file)
@@ -13,21 +13,21 @@ namespace R2RDump.Amd64
     /// </summary>
     public enum Registers
     {
-        EAX = 0,
-        ECX = 1,
-        EDX = 2,
-        EBX = 3,
-        ESP = 4,
-        EBP = 5,
-        ESI = 6,
-        EDI = 7,
-        E8 = 8,
-        E9 = 9,
-        E10 = 10,
-        E11 = 11,
-        E12 = 12,
-        E13 = 13,
-        E14 = 14,
-        E15 = 15,
+        RAX = 0,
+        RCX = 1,
+        RDX = 2,
+        RBX = 3,
+        RSP = 4,
+        RBP = 5,
+        RSI = 6,
+        RDI = 7,
+        R8 = 8,
+        R9 = 9,
+        R10 = 10,
+        R11 = 11,
+        R12 = 12,
+        R13 = 13,
+        R14 = 14,
+        R15 = 15,
     }
 }
index c595bab..5b5568e 100644 (file)
@@ -2,6 +2,7 @@
 // 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.Collections.Generic;
 using System.Text;
 using System.Xml.Serialization;
 
@@ -40,19 +41,26 @@ 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_CODE
     /// </summary>
-    public struct UnwindCode
+    public class UnwindCode
     {
         [XmlAttribute("Index")]
         public int Index { get; set; }
 
         public byte CodeOffset { get; set; }
         public UnwindOpCodes UnwindOp { get; set; } //4 bits
+
         public byte OpInfo { get; set; } //4 bits
+        public string OpInfoStr { get; set; } //4 bits
 
         public byte OffsetLow { get; set; }
         public byte OffsetHigh { get; set; } //4 bits
 
         public uint FrameOffset { get; set; }
+        public int NextFrameOffset { get; set; }
+
+        public bool IsOpInfo { get; set; }
+
+        public UnwindCode() { }
 
         public UnwindCode(byte[] image, int index, ref int offset)
         {
@@ -68,6 +76,9 @@ namespace R2RDump.Amd64
             OffsetHigh = OpInfo;
 
             FrameOffset = NativeReader.ReadUInt16(image, ref offset);
+            NextFrameOffset = -1;
+
+            IsOpInfo = false;
         }
     }
 
@@ -85,7 +96,9 @@ namespace R2RDump.Amd64
         public byte CountOfUnwindCodes { get; set; }
         public Registers FrameRegister { get; set; } //4 bits
         public byte FrameOffset { get; set; } //4 bits
-        public UnwindCode[] UnwindCode { get; set; }
+        public UnwindCode[] UnwindCodeArray { get; set; }
+        [XmlIgnore]
+        public Dictionary<int, List<UnwindCode>> UnwindCodes { get; set; }
         public uint PersonalityRoutineRVA { get; set; }
 
         public UnwindInfo() { }
@@ -104,10 +117,20 @@ namespace R2RDump.Amd64
             FrameRegister = (Registers)(frameRegisterAndOffset & 15);
             FrameOffset = (byte)(frameRegisterAndOffset >> 4);
 
-            UnwindCode = new UnwindCode[CountOfUnwindCodes];
+            UnwindCodeArray = new UnwindCode[CountOfUnwindCodes];
+            UnwindCodes = new Dictionary<int, List<UnwindCode>>();
+            for (int i = 0; i < CountOfUnwindCodes; i++)
+            {
+                UnwindCodeArray[i] = new UnwindCode(image, i, ref offset);
+            }
             for (int i = 0; i < CountOfUnwindCodes; i++)
             {
-                UnwindCode[i] = new UnwindCode(image, i, ref offset);
+                ParseUnwindCode(ref i);
+                if (!UnwindCodes.ContainsKey(UnwindCodeArray[i].CodeOffset))
+                {
+                    UnwindCodes[UnwindCodeArray[i].CodeOffset] = new List<UnwindCode>();
+                }
+                UnwindCodes[UnwindCodeArray[i].CodeOffset].Add(UnwindCodeArray[i]);
             }
 
             PersonalityRoutineRVA = NativeReader.ReadUInt32(image, ref offset);
@@ -146,7 +169,15 @@ namespace R2RDump.Amd64
             sb.AppendLine($"\t\t------------------");
             for (int i = 0; i < CountOfUnwindCodes; i++)
             {
-                sb.Append(GetUnwindCode(ref i));
+                if (!UnwindCodeArray[i].IsOpInfo)
+                    continue;
+                sb.AppendLine($"\t\tCodeOffset: 0x{UnwindCodeArray[i].CodeOffset:X2}");
+                sb.AppendLine($"\t\tUnwindOp: {UnwindCodeArray[i].UnwindOp}({(byte)UnwindCodeArray[i].UnwindOp})");
+                sb.AppendLine($"\t\tOpInfo: {UnwindCodeArray[i].OpInfoStr}");
+                if (UnwindCodeArray[i].NextFrameOffset != -1)
+                {
+                    sb.AppendLine($"\t\tFrameOffset: {UnwindCodeArray[i].NextFrameOffset}");
+                }
                 sb.AppendLine($"\t\t------------------");
             }
             sb.AppendLine($"\tPersonalityRoutineRVA: 0x{PersonalityRoutineRVA:X8}");
@@ -158,57 +189,53 @@ namespace R2RDump.Amd64
         /// <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)
+        private void ParseUnwindCode(ref int i)
         {
-            StringBuilder sb = new StringBuilder();
-
-            sb.AppendLine($"\t\tCodeOffset: 0x{UnwindCode[i].CodeOffset:X2}");
-            sb.AppendLine($"\t\tUnwindOp: {UnwindCode[i].UnwindOp}({(byte)UnwindCode[i].UnwindOp})");
-
-            switch (UnwindCode[i].UnwindOp)
+            UnwindCode code = UnwindCodeArray[i];
+            code.IsOpInfo = true;
+            switch (code.UnwindOp)
             {
                 case UnwindOpCodes.UWOP_PUSH_NONVOL:
-                    sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+                    code.OpInfoStr = $"{(Registers)code.OpInfo}({code.OpInfo})";
                     break;
                 case UnwindOpCodes.UWOP_ALLOC_LARGE:
-                    sb.Append($"\t\tOpInfo: {UnwindCode[i].OpInfo} - ");
-                    if (UnwindCode[i].OpInfo == 0)
+                    code.OpInfoStr = $"{code.OpInfo} - ";
+                    if (code.OpInfo == 0)
                     {
                         i++;
-                        sb.AppendLine("Scaled small");
-                        uint frameOffset = UnwindCode[i].FrameOffset * 8;
-                        sb.AppendLine($"\t\tFrameOffset: {UnwindCode[i].FrameOffset} * 8 = {frameOffset} = 0x{frameOffset:X5})");
+                        UnwindCodeArray[i].OpInfoStr += "Scaled small";
+                        code.NextFrameOffset = (int)UnwindCodeArray[i].FrameOffset * 8;
                     }
-                    else if (UnwindCode[i].OpInfo == 1)
+                    else if (code.OpInfo == 1)
                     {
                         i++;
-                        sb.AppendLine("Unscaled large");
-                        uint offset = UnwindCode[i].FrameOffset;
+                        UnwindCodeArray[i].OpInfoStr += "Unscaled large";
+                        uint offset = UnwindCodeArray[i].FrameOffset;
                         i++;
-                        offset = ((UnwindCode[i].FrameOffset << 16) | offset);
-                        sb.AppendLine($"\t\tFrameOffset: 0x{offset:X8})");
+                        offset = ((UnwindCodeArray[i].FrameOffset << 16) | offset);
+                        code.NextFrameOffset = (int)offset;
                     }
                     else
                     {
-                        sb.AppendLine("Unknown");
+                        code.OpInfoStr += "Unknown";
                     }
                     break;
                 case UnwindOpCodes.UWOP_ALLOC_SMALL:
-                    int opInfo = UnwindCode[i].OpInfo * 8 + 8;
-                    sb.AppendLine($"\t\tOpInfo: {UnwindCode[i].OpInfo} * 8 + 8 = {opInfo} = 0x{opInfo:X2}");
+                    int opInfo = code.OpInfo * 8 + 8;
+                    code.OpInfoStr = $"{opInfo}";
                     break;
                 case UnwindOpCodes.UWOP_SET_FPREG:
-                    sb.AppendLine($"\t\tOpInfo: Unused({UnwindCode[i].OpInfo})");
+                    code.OpInfoStr = $"Unused({code.OpInfo})";
                     break;
                 case UnwindOpCodes.UWOP_SET_FPREG_LARGE:
                     {
-                        sb.AppendLine($"\t\tOpInfo: Unused({UnwindCode[i].OpInfo})");
+                        code.OpInfoStr = $"Unused({code.OpInfo})";
                         i++;
-                        uint offset = UnwindCode[i].FrameOffset;
+                        uint offset = UnwindCodeArray[i].FrameOffset;
                         i++;
-                        offset = ((UnwindCode[i].FrameOffset << 16) | offset);
-                        sb.AppendLine($"\t\tScaled Offset: {offset} * 16 = {offset * 16} = 0x{(offset * 16):X8}");
-                        if ((UnwindCode[i].FrameOffset & 0xF0000000) != 0)
+                        offset = ((UnwindCodeArray[i].FrameOffset << 16) | offset);
+                        code.NextFrameOffset = (int)offset * 16;
+                        if ((UnwindCodeArray[i].FrameOffset & 0xF0000000) != 0)
                         {
                             R2RDump.WriteWarning("Illegal unwindInfo unscaled offset: too large");
                         }
@@ -216,54 +243,41 @@ namespace R2RDump.Amd64
                     break;
                 case UnwindOpCodes.UWOP_SAVE_NONVOL:
                     {
-                        sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+                        code.OpInfoStr = $"{(Registers)code.OpInfo}({code.OpInfo})";
                         i++;
-                        uint offset = UnwindCode[i].FrameOffset * 8;
-                        sb.AppendLine($"\t\tScaled Offset: {UnwindCode[i].FrameOffset} * 8 = {offset} = 0x{offset:X5}");
+                        uint offset = UnwindCodeArray[i].FrameOffset * 8;
+                        code.NextFrameOffset = (int)offset;
                     }
                     break;
                 case UnwindOpCodes.UWOP_SAVE_NONVOL_FAR:
                     {
-                        sb.AppendLine($"\t\tOpInfo: {(Registers)UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+                        code.OpInfoStr = $"{(Registers)code.OpInfo}({code.OpInfo})";
                         i++;
-                        uint offset = UnwindCode[i].FrameOffset;
+                        uint offset = UnwindCodeArray[i].FrameOffset;
                         i++;
-                        offset = ((UnwindCode[i].FrameOffset << 16) | offset);
-                        sb.AppendLine($"\t\tUnscaled Large Offset: 0x{offset:X8}");
+                        offset = ((UnwindCodeArray[i].FrameOffset << 16) | offset);
+                        code.NextFrameOffset = (int)offset;
                     }
                     break;
                 case UnwindOpCodes.UWOP_SAVE_XMM128:
                     {
-                        sb.AppendLine($"\t\tOpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+                        code.OpInfoStr = $"XMM{code.OpInfo}({code.OpInfo})";
                         i++;
-                        uint offset = UnwindCode[i].FrameOffset * 16;
-                        sb.AppendLine($"\t\tScaled Offset: {UnwindCode[i].FrameOffset} * 16 = {offset} = 0x{offset:X5}");
+                        uint offset = UnwindCodeArray[i].FrameOffset * 16;
+                        code.NextFrameOffset = (int)offset;
                     }
                     break;
-
                 case UnwindOpCodes.UWOP_SAVE_XMM128_FAR:
                     {
-                        sb.AppendLine($"\t\tOpInfo: XMM{UnwindCode[i].OpInfo}({UnwindCode[i].OpInfo})");
+                        code.OpInfoStr = $"XMM{code.OpInfo}({code.OpInfo})";
                         i++;
-                        uint offset = UnwindCode[i].FrameOffset;
+                        uint offset = UnwindCodeArray[i].FrameOffset;
                         i++;
-                        offset = ((UnwindCode[i].FrameOffset << 16) | offset);
-                        sb.AppendLine($"\t\tUnscaled Large Offset: 0x{offset:X8}");
+                        offset = ((UnwindCodeArray[i].FrameOffset << 16) | offset);
+                        code.NextFrameOffset = (int)offset;
                     }
                     break;
-                case UnwindOpCodes.UWOP_EPILOG:
-                case UnwindOpCodes.UWOP_SPARE_CODE:
-                case UnwindOpCodes.UWOP_PUSH_MACHFRAME:
-                default:
-                    sb.AppendLine($"\t\tOpInfo: {UnwindCode[i].OpInfo}");
-                    sb.AppendLine();
-                    sb.AppendLine($"\t\tOffsetLow: {UnwindCode[i].OffsetLow}");
-                    sb.AppendLine($"\t\tOffsetHigh: {UnwindCode[i].OffsetHigh}");
-                    sb.AppendLine();
-                    sb.AppendLine($"\t\tFrameOffset: {FrameOffset}");
-                    break;
             }
-            return sb.ToString();
         }
     }
 }
index 2b278d8..4128aec 100644 (file)
@@ -144,7 +144,7 @@ namespace R2RDump
             sb.AppendLine($"EntrySize: {EntrySize}");
             sb.AppendLine($"SignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
             sb.AppendLine($"AuxiliaryDataRVA: 0x{AuxiliaryDataRVA:X8} ({AuxiliaryDataRVA})");
-            if (AuxiliaryDataRVA != 0)
+            if (AuxiliaryDataRVA != 0 && AuxiliaryData != null)
             {
                 sb.AppendLine("AuxiliaryData:");
                 sb.AppendLine(AuxiliaryData.ToString());
index fedc68e..417b0c8 100644 (file)
@@ -130,8 +130,8 @@ namespace R2RDump
                 Machine = PEReader.PEHeaders.CoffHeader.Machine;
                                if (!Machine.IsDefined(typeof(Machine), Machine))
                 {
-                    Machine = Machine.Amd64;
                     R2RDump.WriteWarning($"Invalid Machine: {Machine}");
+                    Machine = Machine.Amd64;
                 }
                 ImageBase = PEReader.PEHeaders.PEHeader.ImageBase;
 
index 3690373..bbd2858 100644 (file)
@@ -75,6 +75,8 @@ 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
 
+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)
+
 ### GcInfo
 
 Written into the ReadyToRun image right after UnwindInfo. Contains a header, GcSlots and GcTransitions (register liveness).
index 1ecaec8..df04562 100644 (file)
@@ -181,6 +181,21 @@ namespace R2RDump
                 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];
+                    foreach (Amd64.UnwindCode code in codes)
+                    {
+                        _writer.Write($"\t\t\t\t{code.UnwindOp} {code.OpInfoStr}");
+                        if (code.NextFrameOffset != -1)
+                        {
+                            _writer.WriteLine($" - {code.NextFrameOffset}");
+                        }
+                        _writer.WriteLine();
+                    }
+                }
+
                 if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                 {
                     foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])