Implement GC ref map parsing and display in R2RDump (dotnet/coreclr#21509)
authorTomáš Rylek <trylek@microsoft.com>
Sat, 15 Dec 2018 00:40:33 +0000 (01:40 +0100)
committerGitHub <noreply@github.com>
Sat, 15 Dec 2018 00:40:33 +0000 (01:40 +0100)
As part of my work on CPAOT implementation of GC ref map info
I have implemented decoder of the info so that it can be displayed
next to the import cell signatures.

This also uncovered one possible cause of R2RDump GcInfo-related
crashes that were observed by Andon and myself. It looks like Amy
in her initial implementation confused the various GC encodings
and used GC info to parse the import section auxiliary data which
actually contains the GC ref map info.

Thanks

Tomas

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

src/coreclr/src/tools/r2rdump/CoreDisTools.cs
src/coreclr/src/tools/r2rdump/GCRefMap.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/R2RImportSection.cs
src/coreclr/src/tools/r2rdump/R2RMethod.cs
src/coreclr/src/tools/r2rdump/R2RReader.cs
src/coreclr/src/tools/r2rdump/R2RSection.cs
src/coreclr/src/tools/r2rdump/TextDumper.cs
src/coreclr/src/tools/r2rdump/TransitionBlock.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/XmlDumper.cs

index d6a82f1..925b88b 100644 (file)
@@ -156,13 +156,17 @@ namespace R2RDump
                             line[colon + 2] == ' ')
                         {
                             colon += 3;
-                        }
-                        nakedInstruction.Append(new string(' ', 32) + line.Substring(colon).TrimStart());
+                        }   
+
+                        nakedInstruction.Append($"{(rtfOffset + rtf.CodeOffset),8:x4}:");
+                        nakedInstruction.Append("  ");
+                        nakedInstruction.Append(line.Substring(colon).TrimStart());
                         nakedInstruction.Append('\n');
                     }
                     else
                     {
-                        nakedInstruction.Append(line);
+                        nakedInstruction.Append(' ', 7);
+                        nakedInstruction.Append(line.TrimStart());
                         nakedInstruction.Append('\n');
                     }
                 }
@@ -172,7 +176,6 @@ namespace R2RDump
             switch (_reader.Machine)
             {
                 case Machine.Amd64:
-                case Machine.IA64:
                     ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
                     break;
 
@@ -312,19 +315,19 @@ namespace R2RDump
             {
                 sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
                 int target = rtf.StartAddress + rtfOffset + instrSize + offset;
-                ReplaceRelativeOffset(ref instruction, target);
+                ReplaceRelativeOffset(ref instruction, target, rtf);
             }
             else if (instrSize == 5 && IsIntel1ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
             {
                 int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 1);
                 int target = rtf.StartAddress + rtfOffset + instrSize + offset;
-                ReplaceRelativeOffset(ref instruction, target);
+                ReplaceRelativeOffset(ref instruction, target, rtf);
             }
             else if (instrSize == 6 && IsIntel2ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
             {
                 int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 2);
                 int target = rtf.StartAddress + rtfOffset + instrSize + offset;
-                ReplaceRelativeOffset(ref instruction, target);
+                ReplaceRelativeOffset(ref instruction, target, rtf);
             }
         }
 
@@ -401,7 +404,7 @@ namespace R2RDump
         /// </summary>
         /// <param name="instruction"></param>
         /// <param name="target"></param>
-        private void ReplaceRelativeOffset(ref string instruction, int target)
+        private void ReplaceRelativeOffset(ref string instruction, int target, RuntimeFunction rtf)
         {
             int numberEnd = instruction.IndexOf('\n');
             int number = numberEnd;
@@ -417,7 +420,12 @@ namespace R2RDump
 
             StringBuilder translated = new StringBuilder();
             translated.Append(instruction, 0, number);
-            translated.AppendFormat("0x{0:x4}", target);
+            int outputOffset = target;
+            if (_options.Naked)
+            {
+                outputOffset -= rtf.StartAddress;
+            }
+            translated.AppendFormat("0x{0:x4}", outputOffset);
             translated.Append(instruction, numberEnd, instruction.Length - numberEnd);
             instruction = translated.ToString();
         }
diff --git a/src/coreclr/src/tools/r2rdump/GCRefMap.cs b/src/coreclr/src/tools/r2rdump/GCRefMap.cs
new file mode 100644 (file)
index 0000000..55c06ad
--- /dev/null
@@ -0,0 +1,229 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace R2RDump
+{
+    public enum CORCOMPILE_GCREFMAP_TOKENS
+    {
+        GCREFMAP_SKIP = 0,
+        GCREFMAP_REF = 1,
+        GCREFMAP_INTERIOR = 2,
+        GCREFMAP_METHOD_PARAM = 3,
+        GCREFMAP_TYPE_PARAM = 4,
+        GCREFMAP_VASIG_COOKIE = 5,
+    }
+
+    public struct GCRefMapEntry
+    {
+        public readonly int Offset;
+        public readonly CORCOMPILE_GCREFMAP_TOKENS Token;
+
+        public GCRefMapEntry(int offset, CORCOMPILE_GCREFMAP_TOKENS token)
+        {
+            Offset = offset;
+            Token = token;
+        }
+    }
+
+    public class GCRefMap
+    {
+        public const int GCREFMAP_LOOKUP_STRIDE = 1024;
+
+        public const uint InvalidStackPop = ~0u;
+
+        public readonly uint StackPop;
+        public readonly GCRefMapEntry[] Entries;
+
+        public GCRefMap(uint stackPop, GCRefMapEntry[] entries)
+        {
+            StackPop = stackPop;
+            Entries = entries;
+        }
+
+        public void WriteTo(TextWriter writer)
+        {
+            if (StackPop != InvalidStackPop)
+            {
+                writer.Write(@"POP(0x{StackPop:X}) ");
+            }
+            for (int entryIndex = 0; entryIndex < Entries.Length; entryIndex++)
+            {
+                GCRefMapEntry entry = Entries[entryIndex];
+                if (entryIndex == 0 || entry.Token != Entries[entryIndex - 1].Token)
+                {
+                    if (entryIndex != 0)
+                    {
+                        writer.Write(") ");
+                    }
+                    switch (entry.Token)
+                    {
+                        case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF:
+                            writer.Write("R");
+                            break;
+                        case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR:
+                            writer.Write("I");
+                            break;
+                        case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_METHOD_PARAM:
+                            writer.Write("M");
+                            break;
+                        case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_TYPE_PARAM:
+                            writer.Write("T");
+                            break;
+                        case CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_VASIG_COOKIE:
+                            writer.Write("V");
+                            break;
+                        default:
+                            throw new NotImplementedException();
+                    }
+                    writer.Write("(");
+                }
+                else
+                {
+                    writer.Write(" ");
+                }
+                writer.Write($"{entry.Offset:X2}"); 
+            }
+            writer.Write(")");
+        }
+    }
+
+    /// <summary>
+    /// Helper class for decoding the bit-oriented GC ref map format.
+    /// </summary>
+    public class GCRefMapDecoder
+    {
+        private readonly R2RReader _reader;
+        private int _offset;
+        private int _pendingByte;
+        private int _pos;
+
+        public GCRefMapDecoder(R2RReader reader, int offset)
+        {
+            _reader = reader;
+            _offset = offset;
+            _pendingByte = 0x80;
+            _pos = 0;
+        }
+
+        public int GetBit()
+        {
+            int x = _pendingByte;
+            if ((x & 0x80) != 0)
+            {
+                x = _reader.Image[_offset++];
+                x |= ((x & 0x80) << 7);
+            }
+            _pendingByte = x >> 1;
+            return x & 1;
+        }
+
+        public int GetTwoBit()
+        {
+            int result = GetBit();
+            result |= GetBit() << 1;
+            return result;
+        }
+
+        public int GetInt()
+        {
+            int result = 0;
+
+            int bit = 0;
+            do
+            {
+                result |= GetBit() << (bit++);
+                result |= GetBit() << (bit++);
+                result |= GetBit() << (bit++);
+            }
+            while (GetBit() != 0);
+
+            return result;
+        }
+
+        public bool AtEnd()
+        {
+            return _pendingByte == 0;
+        }
+
+        public int GetOffset()
+        {
+            return _offset;
+        }
+
+        public uint ReadStackPop()
+        {
+            Debug.Assert(_reader.Architecture == Architecture.X86);
+
+            int x = GetTwoBit();
+
+            if (x == 3)
+                x = GetInt() + 3;
+
+            return (uint)x;
+        }
+
+        public int CurrentPos()
+        {
+            return _pos;
+        }
+
+        public CORCOMPILE_GCREFMAP_TOKENS ReadToken()
+        {
+            int val = GetTwoBit();
+            if (val == 3)
+            {
+                int ext = GetInt();
+                if ((ext & 1) == 0)
+                {
+                    _pos += (ext >> 1) + 4;
+                    return CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_SKIP;
+                }
+                else
+                {
+                    _pos++;
+                    return (CORCOMPILE_GCREFMAP_TOKENS)((ext >> 1) + 3);
+                }
+            }
+            _pos++;
+            return (CORCOMPILE_GCREFMAP_TOKENS)val;
+        }
+
+        public GCRefMap ReadMap()
+        {
+            TransitionBlock transitionBlock = TransitionBlock.FromReader(_reader);
+
+            List<GCRefMapEntry> entries = new List<GCRefMapEntry>();
+            uint stackPop = GCRefMap.InvalidStackPop;
+
+            if (_reader.Architecture == Architecture.X86)
+            {
+                stackPop = ReadStackPop();
+            }
+
+            while (!AtEnd())
+            {
+                int pos = CurrentPos();
+                CORCOMPILE_GCREFMAP_TOKENS token = ReadToken();
+                if (token != CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_SKIP)
+                {
+                    int offset = transitionBlock.OffsetFromGCRefMapPos(pos);
+                    entries.Add(new GCRefMapEntry(offset, token));
+                }
+            }
+
+            if (stackPop != GCRefMap.InvalidStackPop || entries.Count > 0)
+            {
+                return new GCRefMap(stackPop, entries.ToArray());
+            }
+
+            return null;
+        }
+    }
+}
index 4216f23..8e5e32e 100644 (file)
@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Reflection.PortableExecutable;
 using System.Text;
 using System.Xml.Serialization;
@@ -15,7 +16,7 @@ namespace R2RDump
     /// </summary>
     public struct R2RImportSection
     {
-        public struct ImportSectionEntry
+        public class ImportSectionEntry
         {
             [XmlAttribute("Index")]
             public int Index { get; set; }
@@ -24,6 +25,8 @@ namespace R2RDump
             public long Section { get; set; }
             public uint SignatureRVA { get; set; }
             public string Signature { get; set; }
+            public GCRefMap GCRefMap { get; set; }
+
             public ImportSectionEntry(int index, int startOffset, int startRVA, long section, uint signatureRVA, string signature)
             {
                 Index = index;
@@ -34,15 +37,22 @@ namespace R2RDump
                 Signature = signature;
             }
 
-            public override string ToString()
+            public void WriteTo(TextWriter writer, DumpOptions options)
             {
-                StringBuilder builder = new StringBuilder();
-                builder.AppendFormat("+{0:X4}", StartOffset);
-                builder.AppendFormat(" ({0:X4})", StartRVA);
-                builder.AppendFormat("  Section: 0x{0:X8}", Section);
-                builder.AppendFormat("  SignatureRVA: 0x{0:X8}", SignatureRVA);
-                builder.AppendFormat("  {0}", Signature);
-                return builder.ToString();
+                if (!options.Naked)
+                {
+                    writer.Write($"+{StartOffset:X4}");
+                    writer.Write($" ({StartRVA:X4})");
+                    writer.Write($"  Section: 0x{Section:X8}");
+                    writer.Write($"  SignatureRVA: 0x{SignatureRVA:X8}");
+                    writer.Write("   ");
+                }
+                writer.Write(Signature);
+                if (GCRefMap != null)
+                {
+                    writer.Write(" -- ");
+                    GCRefMap.WriteTo(writer);
+                }
             }
         }
 
@@ -80,10 +90,23 @@ namespace R2RDump
         /// RVA of optional auxiliary data (typically GC info)
         /// </summary>
         public int AuxiliaryDataRVA { get; set; }
-        [XmlIgnore]
-        public BaseGcInfo AuxiliaryData { get; set; }
 
-        public R2RImportSection(int index, byte[] image, int rva, int size, CorCompileImportFlags flags, byte type, byte entrySize, int signatureRVA, List<ImportSectionEntry> entries, int auxDataRVA, int auxDataOffset, Machine machine, ushort majorVersion)
+        public int AuxiliaryDataSize { get; set; }
+
+        public R2RImportSection(
+            int index, 
+            R2RReader reader, 
+            int rva, 
+            int size, 
+            CorCompileImportFlags flags, 
+            byte type, 
+            byte entrySize, 
+            int signatureRVA, 
+            List<ImportSectionEntry> entries, 
+            int auxDataRVA, 
+            int auxDataOffset, 
+            Machine machine, 
+            ushort majorVersion)
         {
             Index = index;
             SectionRVA = rva;
@@ -96,36 +119,36 @@ namespace R2RDump
             Entries = entries;
 
             AuxiliaryDataRVA = auxDataRVA;
-            AuxiliaryData = null;
+            AuxiliaryDataSize = 0;
             if (AuxiliaryDataRVA != 0)
             {
-                if (machine == Machine.Amd64)
-                {
-                    AuxiliaryData = new Amd64.GcInfo(image, auxDataOffset, machine, majorVersion);
-                }
-                else if (machine == Machine.I386)
+                int startOffset = auxDataOffset + BitConverter.ToInt32(reader.Image, auxDataOffset);
+
+                for (int i = 0; i < Entries.Count; i++)
                 {
-                    AuxiliaryData = new x86.GcInfo(image, auxDataOffset, machine, majorVersion);
+                    GCRefMapDecoder decoder = new GCRefMapDecoder(reader, startOffset);
+                    Entries[i].GCRefMap = decoder.ReadMap();
+                    startOffset = decoder.GetOffset();
                 }
+
+                AuxiliaryDataSize = startOffset - auxDataOffset;
             }
         }
 
+        public void WriteTo(TextWriter writer)
+        {
+            writer.WriteLine($"SectionRVA: 0x{SectionRVA:X8} ({SectionRVA})");
+            writer.WriteLine($"SectionSize: {SectionSize} bytes");
+            writer.WriteLine($"Flags: {Flags}");
+            writer.WriteLine($"Type: {Type}");
+            writer.WriteLine($"EntrySize: {EntrySize}");
+            writer.WriteLine($"SignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
+            writer.WriteLine($"AuxiliaryDataRVA: 0x{AuxiliaryDataRVA:X8} ({AuxiliaryDataRVA})");
+        }
+
         public override string ToString()
         {
-            StringBuilder sb = new StringBuilder();
-            sb.AppendLine($"SectionRVA: 0x{SectionRVA:X8} ({SectionRVA})");
-            sb.AppendLine($"SectionSize: {SectionSize} bytes");
-            sb.AppendLine($"Flags: {Flags}");
-            sb.AppendLine($"Type: {Type}");
-            sb.AppendLine($"EntrySize: {EntrySize}");
-            sb.AppendLine($"SignatureRVA: 0x{SignatureRVA:X8} ({SignatureRVA})");
-            sb.AppendLine($"AuxiliaryDataRVA: 0x{AuxiliaryDataRVA:X8} ({AuxiliaryDataRVA})");
-            if (AuxiliaryDataRVA != 0 && AuxiliaryData != null)
-            {
-                sb.AppendLine("AuxiliaryData:");
-                sb.AppendLine(AuxiliaryData.ToString());
-            }
-            return sb.ToString();
+            throw new NotImplementedException();
         }
     }
 }
index 6078e6f..27f2185 100644 (file)
@@ -139,8 +139,11 @@ namespace R2RDump
 
         public void WriteTo(TextWriter writer, DumpOptions options)
         {
-            writer.WriteLine($"Id: {Id}");
-            writer.WriteLine($"StartAddress: 0x{StartAddress:X8}");
+            if (!options.Naked)
+            {
+                writer.WriteLine($"Id: {Id}");
+                writer.WriteLine($"StartAddress: 0x{StartAddress:X8}");
+            }
             if (Size == -1)
             {
                 writer.WriteLine("Size: Unavailable");
@@ -149,7 +152,10 @@ namespace R2RDump
             {
                 writer.WriteLine($"Size: {Size} bytes");
             }
-            writer.WriteLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+            if (!options.Naked)
+            {
+                writer.WriteLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+            }
             if (UnwindInfo is Amd64.UnwindInfo amd64UnwindInfo)
             {
                 string parsedFlags = "";
@@ -175,7 +181,10 @@ namespace R2RDump
                 writer.WriteLine($"CountOfUnwindCodes: {amd64UnwindInfo.CountOfUnwindCodes}");
                 writer.WriteLine($"FrameRegister:      {amd64UnwindInfo.FrameRegister}");
                 writer.WriteLine($"FrameOffset:        0x{amd64UnwindInfo.FrameOffset}");
-                writer.WriteLine($"PersonalityRVA:     0x{amd64UnwindInfo.PersonalityRoutineRVA:X4}");
+                if (!options.Naked)
+                {
+                    writer.WriteLine($"PersonalityRVA:     0x{amd64UnwindInfo.PersonalityRoutineRVA:X4}");
+                }
 
                 for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++)
                 {
@@ -375,7 +384,10 @@ namespace R2RDump
 
             writer.WriteLine($"Handle: 0x{MetadataTokens.GetToken(R2RReader.MetadataReader, MethodHandle):X8}");
             writer.WriteLine($"Rid: {MetadataTokens.GetRowNumber(R2RReader.MetadataReader, MethodHandle)}");
-            writer.WriteLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}");
+            if (!options.Naked)
+            {
+                writer.WriteLine($"EntryPointRuntimeFunctionId: {EntryPointRuntimeFunctionId}");
+            }
             writer.WriteLine($"Number of RuntimeFunctions: {RuntimeFunctions.Count}");
             if (Fixups != null)
             {
@@ -388,7 +400,12 @@ namespace R2RDump
 
                 foreach (FixupCell cell in fixups)
                 {
-                    writer.WriteLine($"    TableIndex {cell.TableIndex}, Offset {cell.CellOffset:X4}: {cell.Signature}");
+                    writer.Write("    ");
+                    if (!options.Naked)
+                    {
+                        writer.WriteLine($"TableIndex {cell.TableIndex}, Offset {cell.CellOffset:X4}: ");
+                    }
+                    writer.WriteLine(cell.Signature);
                 }
             }
         }
index 9d20d61..b74e9b2 100644 (file)
@@ -8,6 +8,7 @@ using System.IO;
 using System.Reflection.Metadata;
 using System.Reflection.Metadata.Ecma335;
 using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
 using System.Text;
 using System.Xml.Serialization;
 
@@ -107,9 +108,22 @@ namespace R2RDump
         /// </summary>
         public Machine Machine { get; set; }
 
+        /// <summary>
+        /// Targeting operating system for the R2R executable
+        /// </summary>
         public OperatingSystem OS { get; set; }
 
         /// <summary>
+        /// Targeting processor architecture of the R2R executable
+        /// </summary>
+        public Architecture Architecture { get; set; }
+
+        /// <summary>
+        /// Pointer size in bytes for the target architecture
+        /// </summary>
+        public int PointerSize { get; set; }
+
+        /// <summary>
         /// The preferred address of the first byte of image when loaded into memory; 
         /// must be a multiple of 64K.
         /// </summary>
@@ -195,6 +209,36 @@ namespace R2RDump
                 {
                     throw new BadImageFormatException($"Invalid Machine: {machine}");
                 }
+
+                switch (Machine)
+                {
+                    case Machine.I386:
+                        Architecture = Architecture.X86;
+                        PointerSize = 4;
+                        break;
+
+                    case Machine.Amd64:
+                        Architecture = Architecture.X64;
+                        PointerSize = 8;
+                        break;
+
+                    case Machine.Arm:
+                    case Machine.Thumb:
+                    case Machine.ArmThumb2:
+                        Architecture = Architecture.Arm;
+                        PointerSize = 4;
+                        break;
+
+                    case Machine.Arm64:
+                        Architecture = Architecture.Arm64;
+                        PointerSize = 8;
+                        break;
+
+                    default:
+                        throw new NotImplementedException(Machine.ToString());
+                }
+
+
                 ImageBase = PEReader.PEHeaders.PEHeader.ImageBase;
 
                 // initialize R2RHeader
@@ -555,7 +599,6 @@ namespace R2RDump
                             break;
 
                         case Machine.Amd64:
-                        case Machine.IA64:
                         case Machine.Arm64:
                             entrySize = 8;
                             break;
@@ -594,7 +637,7 @@ namespace R2RDump
                 {
                     auxDataOffset = GetOffset(auxDataRVA);
                 }
-                ImportSections.Add(new R2RImportSection(ImportSections.Count, Image, rva, size, flags, type, entrySize, signatureRVA, entries, auxDataRVA, auxDataOffset, Machine, R2RHeader.MajorVersion));
+                ImportSections.Add(new R2RImportSection(ImportSections.Count, this, rva, size, flags, type, entrySize, signatureRVA, entries, auxDataRVA, auxDataOffset, Machine, R2RHeader.MajorVersion));
             }
         }
 
index 125d040..0c715cc 100644 (file)
@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Text;
 using System.Xml.Serialization;
 
@@ -52,13 +53,19 @@ namespace R2RDump
             Size = size;
         }
 
+        public void WriteTo(TextWriter writer, DumpOptions options)
+        {
+            writer.WriteLine($"Type:  {Enum.GetName(typeof(SectionType), Type)} ({Type:D})");
+            if (!options.Naked)
+            {
+                writer.WriteLine($"RelativeVirtualAddress: 0x{RelativeVirtualAddress:X8}");
+            }
+            writer.WriteLine($"Size: {Size} bytes");
+        }
+
         public override string ToString()
         {
-            StringBuilder sb = new StringBuilder();
-            sb.AppendLine($"Type:  {Enum.GetName(typeof(SectionType), Type)} ({Type:D})");
-            sb.AppendLine($"RelativeVirtualAddress: 0x{RelativeVirtualAddress:X8}");
-            sb.AppendLine($"Size: {Size} bytes");
-            return sb.ToString();
+            throw new NotImplementedException();
         }
     }
 }
index da45a71..f31c903 100644 (file)
@@ -71,7 +71,7 @@ namespace R2RDump
 
                 foreach (R2RSection section in NormalizedSections())
                 {
-                    DumpSection(section);
+                    DumpSection(section, parentNode: null);
                 }
             }
             SkipLine();
@@ -83,7 +83,7 @@ namespace R2RDump
         internal override void DumpSection(R2RSection section, XmlNode parentNode = null)
         {
             WriteSubDivider();
-            _writer.WriteLine(section.ToString());
+            section.WriteTo(_writer, _options);
 
             if (_options.Raw)
             {
@@ -92,7 +92,7 @@ namespace R2RDump
             }
             if (_options.SectionContents)
             {
-                DumpSectionContents(section);
+                DumpSectionContents(section, parentNode);
                 SkipLine();
             }
         }
@@ -178,6 +178,8 @@ namespace R2RDump
         /// </summary>
         internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
         {
+            int indent = (_options.Naked ? 11 : 32);
+            string indentString = new string(' ', indent);
             int rtfOffset = 0;
             int codeOffset = rtf.CodeOffset;
             while (rtfOffset < rtf.Size)
@@ -190,10 +192,10 @@ namespace R2RDump
                     List<Amd64.UnwindCode> codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
                     foreach (Amd64.UnwindCode code in codes)
                     {
-                        _writer.Write($"                                {code.UnwindOp} {code.OpInfoStr}");
+                        _writer.Write($"{indentString}{code.UnwindOp} {code.OpInfoStr}");
                         if (code.NextFrameOffset != -1)
                         {
-                            _writer.WriteLine($"                                {code.NextFrameOffset}");
+                            _writer.WriteLine($"{indentString}{code.NextFrameOffset}");
                         }
                         _writer.WriteLine();
                     }
@@ -203,7 +205,7 @@ namespace R2RDump
                 {
                     foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                     {
-                        _writer.WriteLine($"                                {transition.ToString()}");
+                        _writer.WriteLine($"{indentString}{transition}");
                     }
                 }
 
@@ -326,7 +328,7 @@ namespace R2RDump
                     {
                         foreach (R2RImportSection importSection in _r2r.ImportSections)
                         {
-                            _writer.Write(importSection.ToString());
+                            importSection.WriteTo(_writer);
                             if (_options.Raw && importSection.Entries.Count != 0)
                             {
                                 if (importSection.SectionRVA != 0)
@@ -339,15 +341,16 @@ namespace R2RDump
                                     _writer.WriteLine("Signature Bytes:");
                                     DumpBytes(importSection.SignatureRVA, (uint)importSection.Entries.Count * sizeof(int));
                                 }
-                                if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryData != null)
+                                if (importSection.AuxiliaryDataRVA != 0 && importSection.AuxiliaryDataSize != 0)
                                 {
                                     _writer.WriteLine("AuxiliaryData Bytes:");
-                                    DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size);
+                                    DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryDataSize);
                                 }
                             }
                             foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)
                             {
-                                _writer.WriteLine(entry.ToString());
+                                entry.WriteTo(_writer, _options);
+                                _writer.WriteLine();
                             }
                             _writer.WriteLine();
                         }
@@ -358,18 +361,16 @@ namespace R2RDump
 
         private void DumpNakedImportSections()
         {
-            List<string> importSignatures = new List<string>();
+            List<R2RImportSection.ImportSectionEntry> entries = new List<R2RImportSection.ImportSectionEntry>();
             foreach (R2RImportSection importSection in _r2r.ImportSections)
             {
-                foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)
-                {
-                    importSignatures.Add(entry.Signature);
-                }
+                entries.AddRange(importSection.Entries);
             }
-            importSignatures.Sort();
-            foreach (string sig in importSignatures)
+            entries.Sort((e1, e2) => e1.Signature.CompareTo(e2.Signature));
+            foreach (R2RImportSection.ImportSectionEntry entry in entries)
             {
-                _writer.WriteLine(sig);
+                entry.WriteTo(_writer, _options);
+                _writer.WriteLine();
             }
         }
 
diff --git a/src/coreclr/src/tools/r2rdump/TransitionBlock.cs b/src/coreclr/src/tools/r2rdump/TransitionBlock.cs
new file mode 100644 (file)
index 0000000..46a1724
--- /dev/null
@@ -0,0 +1,151 @@
+// 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.Runtime.InteropServices;
+using System.IO;
+
+namespace R2RDump
+{
+    public abstract class TransitionBlock
+    {
+        public R2RReader _reader;
+
+        public static TransitionBlock FromReader(R2RReader reader)
+        {
+            switch (reader.Architecture)
+            {
+                case Architecture.X86:
+                    return X86TransitionBlock.Instance;
+
+                case Architecture.X64:
+                    return reader.OS == OperatingSystem.Windows ? X64WindowsTransitionBlock.Instance : X64UnixTransitionBlock.Instance;
+
+                case Architecture.Arm:
+                    return ArmTransitionBlock.Instance;
+
+                case Architecture.Arm64:
+                    return Arm64TransitionBlock.Instance;
+
+                default:
+                    throw new NotImplementedException();
+            }
+        }
+
+        public abstract int PointerSize { get; }
+
+        public abstract int NumArgumentRegisters { get; }
+
+        public int SizeOfArgumentRegisters => NumArgumentRegisters * PointerSize;
+
+        public abstract int NumCalleeSavedRegisters { get; }
+
+        public int SizeOfCalleeSavedRegisters => NumCalleeSavedRegisters * PointerSize;
+
+        public abstract int SizeOfTransitionBlock { get; }
+
+        public abstract int OffsetOfArgumentRegisters { get; }
+
+        /// <summary>
+        /// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures
+        /// except for X86 where it's overridden to supply a more complex algorithm.
+        /// </summary>
+        /// <param name="pos"></param>
+        /// <returns></returns>
+        public virtual int OffsetFromGCRefMapPos(int pos)
+        {
+            return OffsetOfArgumentRegisters + pos * PointerSize;
+        }
+
+        /// <summary>
+        /// The transition block should define everything pushed by callee. The code assumes in number of places that
+        /// end of the transition block is caller's stack pointer.
+        /// </summary>
+        public int OffsetOfArgs => SizeOfTransitionBlock;
+
+        private sealed class X86TransitionBlock : TransitionBlock
+        {
+            public static readonly TransitionBlock Instance = new X86TransitionBlock();
+
+            public override int PointerSize => 4;
+            public override int NumArgumentRegisters => 2;
+            public override int NumCalleeSavedRegisters => 4;
+            // Argument registers, callee-save registers, return address
+            public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
+            public override int OffsetOfArgumentRegisters => 0;
+
+            public override int OffsetFromGCRefMapPos(int pos)
+            {
+                if (pos < NumArgumentRegisters)
+                {
+                    return OffsetOfArgumentRegisters + SizeOfArgumentRegisters - (pos + 1) * PointerSize;
+                }
+                else
+                {
+                    return OffsetOfArgs + (pos - NumArgumentRegisters) * PointerSize;
+                }
+            }
+        }
+
+        private sealed class X64WindowsTransitionBlock : TransitionBlock
+        {
+            public static readonly TransitionBlock Instance = new X64WindowsTransitionBlock();
+
+            public override int PointerSize => 8;
+            // RCX, RDX, R8, R9
+            public override int NumArgumentRegisters => 4;
+            // RDI, RSI, RBX, RBP, R12, R13, R14, R15
+            public override int NumCalleeSavedRegisters => 8;
+            // Callee-saved registers, return address
+            public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + PointerSize;
+            public override int OffsetOfArgumentRegisters => SizeOfTransitionBlock;
+        }
+
+        private sealed class X64UnixTransitionBlock : TransitionBlock
+        {
+            public static readonly TransitionBlock Instance = new X64UnixTransitionBlock();
+
+            public override int PointerSize => 8;
+            // RDI, RSI, RDX, RCX, R8, R9
+            public override int NumArgumentRegisters => 6;
+            // R12, R13, R14, R15, RBX, RBP
+            public override int NumCalleeSavedRegisters => 6;
+            // Argument registers, callee-saved registers, return address
+            public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
+            public override int OffsetOfArgumentRegisters => 0;
+        }
+
+        private sealed class ArmTransitionBlock : TransitionBlock
+        {
+            public static readonly TransitionBlock Instance = new ArmTransitionBlock();
+
+            public override int PointerSize => 4;
+            // R0, R1, R2, R3
+            public override int NumArgumentRegisters => 4;
+            // R4, R5, R6, R7, R8, R9, R10, R11, R14
+            public override int NumCalleeSavedRegisters => 9;
+            // Callee-saves, argument registers
+            public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
+            public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters;
+        }
+
+        private sealed class Arm64TransitionBlock : TransitionBlock
+        {
+            public static readonly TransitionBlock Instance = new Arm64TransitionBlock();
+
+            public override int PointerSize => 8;
+            // X0 .. X7
+            public override int NumArgumentRegisters => 8;
+            // X29, X30, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28
+            public override int NumCalleeSavedRegisters => 12;
+            // Callee-saves, padding, m_x8RetBuffReg, argument registers
+            public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + 2 * PointerSize + SizeOfArgumentRegisters;
+            public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters + 2 * PointerSize;
+        }
+    }
+
+}
+
index 3712b89..5f67e27 100644 (file)
@@ -315,7 +315,7 @@ namespace R2RDump
                             }
                             if (importSection.AuxiliaryDataRVA != 0)
                             {
-                                DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryData.Size, importSectionsNode, "AuxiliaryDataBytes");
+                                DumpBytes(importSection.AuxiliaryDataRVA, (uint)importSection.AuxiliaryDataSize, importSectionsNode, "AuxiliaryDataBytes");
                             }
                         }
                         foreach (R2RImportSection.ImportSectionEntry entry in importSection.Entries)