Import signature parsing and general improvements in R2RDump (dotnet/coreclr#19813)
authorTomáš Rylek <trylek@microsoft.com>
Fri, 7 Sep 2018 20:56:05 +0000 (22:56 +0200)
committerGitHub <noreply@github.com>
Fri, 7 Sep 2018 20:56:05 +0000 (22:56 +0200)
* Import signature parsing and general improvements in R2RDump

GcInfo - fixed a bug where the machine architecture was stored in
the instance field later than it was used in the calculation of
SlotTable which subsequently ended up with the wrong register set
(ARM64 instead of AMD64).

GcTransition - changed register set selector to a switch as the
previous implementation was incorrect for AMD64.

UnwindInfo - bug fix: the 4-alignment padding should come before
the personality routine, not after it. I have also slightly
simplified the calculation of the padding size.

CoreDisTools - added support for annotating indirect calls with
symbol information for the import cells; fixed formatting of
short relative jumps and added some code comments.

DisassemblingTypeProvider - bumped up some visibilities and made
a few generalizations letting me use this helper in the general
signature parser.

R2RImportSection - replaced binary signature samples with the
actual parsed representation.

R2RReader - I removed special-casing for eager import signatures
that seems wrong - I don't see any equivalent code in the
CoreCLR runtime. Please let me know if anyone is aware of a reason
why this was put here in the first place.

TextDumper and elsewhere - I have removed all tabs from the
R2RDump tool output.

R2RConstants - I have merged the various CoreCLR and R2R constants
in this file - previously they were interspersed in multiple
classes - and I added a bunch of new stuff needed by the signature
parser.

R2RSignature - comprises the newly added R2R signature parser.

Thanks

Tomas

* Slight modification in signature formatting to make it easier to read

I have swapped the ordering of the signature content with the
signature name - based on analyzing actual disassembly produced
by the code I came to the conclusion that the parsed signature
is generally more important than the cell fixup type which is
often obvious just based on the code context.

Thanks

Tomas

* Add dump of unwind info and RVA translation for near jumps / calls

* Add code comment on GetInstruction return value per PR feedback

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

12 files changed:
src/coreclr/src/tools/r2rdump/Amd64/GcInfo.cs
src/coreclr/src/tools/r2rdump/Amd64/GcTransition.cs
src/coreclr/src/tools/r2rdump/Amd64/UnwindInfo.cs
src/coreclr/src/tools/r2rdump/CoreDisTools.cs
src/coreclr/src/tools/r2rdump/DisassemblingTypeProvider.cs
src/coreclr/src/tools/r2rdump/R2RConstants.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/R2RDump.cs
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/R2RSignature.cs [new file with mode: 0644]
src/coreclr/src/tools/r2rdump/TextDumper.cs

index b493428..7ab9394 100644 (file)
@@ -91,6 +91,7 @@ namespace R2RDump.Amd64
         {
             Offset = offset;
             _gcInfoTypes = new GcInfoTypes(machine);
+            _machine = machine;
 
             SecurityObjectStackSlot = -1;
             GSCookieStackSlot = -1;
@@ -190,8 +191,6 @@ namespace R2RDump.Amd64
             Transitions = GetTranstions(image, ref bitOffset);
 
             Size = bitOffset - startBitOffset;
-
-            _machine = machine;
         }
 
         public override string ToString()
index ef99641..26d629f 100644 (file)
@@ -53,14 +53,23 @@ namespace R2RDump.Amd64
             string slotStr = "";
             if (slot.StackSlot == null)
             {
-                Type regType = typeof(Amd64.Registers);
-                if (machine == Machine.ArmThumb2)
+                Type regType;
+                switch (machine)
                 {
-                    regType = typeof(Arm.Registers);
-                }
-                else
-                {
-                    regType = typeof(Arm64.Registers);
+                    case Machine.ArmThumb2:
+                        regType = typeof(Arm.Registers);
+                        break;
+
+                    case Machine.Arm64:
+                        regType = typeof(Arm64.Registers);
+                        break;
+
+                    case Machine.Amd64:
+                        regType = typeof(Amd64.Registers);
+                        break;
+
+                    default:
+                        throw new NotImplementedException();
                 }
                 slotStr = Enum.GetName(regType, slot.RegisterNumber);
             }
index 5b5568e..50b422e 100644 (file)
@@ -133,11 +133,13 @@ namespace R2RDump.Amd64
                 UnwindCodes[UnwindCodeArray[i].CodeOffset].Add(UnwindCodeArray[i]);
             }
 
-            PersonalityRoutineRVA = NativeReader.ReadUInt32(image, ref offset);
-
             Size = _offsetofUnwindCode + CountOfUnwindCodes * _sizeofUnwindCode;
-            int alignmentPad = ((Size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - Size;
-            Size += (alignmentPad + sizeof(uint));
+            int alignmentPad = -Size & 3;
+            Size += alignmentPad + sizeof(uint);
+
+            // Personality routine RVA must be at 4-aligned address
+            offset += alignmentPad;
+            PersonalityRoutineRVA = NativeReader.ReadUInt32(image, ref offset);
         }
 
         public override string ToString()
index d1eb8c2..2b21ac2 100644 (file)
@@ -80,21 +80,34 @@ namespace R2RDump
         }
     }
 
+    /// <summary>
+    /// Helper class for converting machine instructions to textual representation.
+    /// </summary>
     public class Disassembler : IDisposable
     {
-        private readonly IntPtr _disasm;
-
-        private readonly byte[] _image;
+        /// <summary>
+        /// R2R reader is used to access architecture info, the PE image data and symbol table.
+        /// </summary>
+        private readonly R2RReader _reader;
 
-        private readonly Machine _machine;
+        /// <summary>
+        /// COM interface to the native disassembler in the CoreDisTools.dll library.
+        /// </summary>
+        private readonly IntPtr _disasm;
 
-        public Disassembler(byte[] image, Machine machine)
+        /// <summary>
+        /// Store the R2R reader and construct the disassembler for the appropriate architecture.
+        /// </summary>
+        /// <param name="reader"></param>
+        public Disassembler(R2RReader reader)
         {
-            _disasm = CoreDisTools.GetDisasm(machine);
-            _image = image;
-            _machine = machine;
+            _reader = reader;
+            _disasm = CoreDisTools.GetDisasm(_reader.Machine);
         }
 
+        /// <summary>
+        /// Shut down the native disassembler interface.
+        /// </summary>
         public void Dispose()
         {
             if (_disasm != IntPtr.Zero)
@@ -103,6 +116,14 @@ namespace R2RDump
             }
         }
 
+        /// <summary>
+        /// Parse a single instruction and return the RVA of the next instruction.
+        /// </summary>
+        /// <param name="rtf">Runtime function to parse</param>
+        /// <param name="imageOffset">Offset within the PE image byte array</param>
+        /// <param name="rtfOffset">Instruction offset within the runtime function</param>
+        /// <param name="instruction">Output text representation of the instruction</param>
+        /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns>
         public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction)
         {
             if (_disasm == IntPtr.Zero)
@@ -111,9 +132,10 @@ namespace R2RDump
                 return rtf.Size;
             }
 
-            int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _image, out instruction);
+            int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction);
+            instruction = instruction.Replace('\t', ' ');
 
-            switch (_machine)
+            switch (_reader.Machine)
             {
                 case Machine.Amd64:
                 case Machine.IA64:
@@ -139,6 +161,14 @@ namespace R2RDump
 
         const string RelIPTag = "[rip ";
 
+        /// <summary>
+        /// Translate RIP-relative offsets to RVA's and convert cell addresses to symbol names
+        /// </summary>
+        /// <param name="rtf">Runtime function</param>
+        /// <param name="imageOffset">Offset within the image byte array</param>
+        /// <param name="rtfOffset">Offset within the runtime function</param>
+        /// <param name="instrSize">Instruction size</param>
+        /// <param name="instruction">Textual representation of the instruction</param>
         private void ProbeX64Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
         {
             int relip = instruction.IndexOf(RelIPTag);
@@ -166,10 +196,112 @@ namespace R2RDump
                             offset = -offset;
                         }
                         int target = rtf.StartAddress + rtfOffset + instrSize + offset;
-                        instruction = instruction.Substring(0, start) + $@"[0x{target:x4}]" + instruction.Substring(relip);
+                        int newline = instruction.LastIndexOf('\n');
+                        StringBuilder translated = new StringBuilder();
+                        translated.Append(instruction, 0, start);
+                        translated.AppendFormat("[0x{0:x4}]", target);
+                        translated.Append(instruction, relip, newline - relip);
+                        String targetName;
+                        if (_reader.ImportCellNames.TryGetValue(target, out targetName))
+                        {
+                            int fill = 61 - translated.Length;
+                            if (fill > 0)
+                            {
+                                translated.Append(' ', fill);
+                            }
+                            translated.Append(" // ");
+                            translated.Append(targetName);
+                        }
+                        translated.Append(instruction, newline, instruction.Length - newline);
+                        instruction = translated.ToString();
                     }
                 }
             }
+            else if (instrSize == 2 && IsIntelJumpInstructionWithByteOffset(imageOffset + rtfOffset))
+            {
+                sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
+                int target = rtf.StartAddress + rtfOffset + instrSize + offset;
+                ReplaceRelativeOffset(ref instruction, target);
+            }
+            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);
+            }
+            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);
+            }
+        }
+
+        /// <summary>
+        /// Replace relative offset in the disassembled instruction with the true target RVA.
+        /// </summary>
+        /// <param name="instruction"></param>
+        /// <param name="target"></param>
+        private void ReplaceRelativeOffset(ref string instruction, int target)
+        {
+            int numberEnd = instruction.IndexOf('\n');
+            int number = numberEnd;
+            while (number > 0)
+            {
+                char c = instruction[number - 1];
+                if (c >= ' ' && !Char.IsDigit(c) && c != '-')
+                {
+                    break;
+                }
+                number--;
+            }
+
+            StringBuilder translated = new StringBuilder();
+            translated.Append(instruction, 0, number);
+            translated.AppendFormat("0x{0:x4}", target);
+            translated.Append(instruction, numberEnd, instruction.Length - numberEnd);
+            instruction = translated.ToString();
+        }
+
+        /// <summary>
+        /// Returns true when this is one of the x86 / amd64 opcodes used for branch instructions
+        /// with single-byte offset.
+        /// </summary>
+        /// <param name="imageOffset">Offset within the PE image byte array</param>
+        private bool IsIntelJumpInstructionWithByteOffset(int imageOffset)
+        {
+            byte opCode = _reader.Image[imageOffset];
+            return
+                (opCode >= 0x70 && opCode <= 0x7F) // short conditional jumps
+                || opCode == 0xE3 // JCXZ
+                || opCode == 0xEB // JMP
+                ;
+        }
+
+        /// <summary>
+        /// Returns true when this is one of the x86 / amd64 near jump / call opcodes
+        /// with signed 4-byte offset.
+        /// </summary>
+        /// <param name="imageOffset">Offset within the PE image byte array</param>
+        private bool IsIntel1ByteJumpInstructionWithIntOffset(int imageOffset)
+        {
+            byte opCode = _reader.Image[imageOffset];
+            return opCode == 0xE8 // call near
+                || opCode == 0xE9 // jmp near
+                ;
+        }
+
+        /// <summary>
+        /// Returns true when this is one of the x86 / amd64 conditional near jump
+        /// opcodes with signed 4-byte offset.
+        /// </summary>
+        /// <param name="imageOffset">Offset within the PE image byte array</param>
+        private bool IsIntel2ByteJumpInstructionWithIntOffset(int imageOffset)
+        {
+            byte opCode1 = _reader.Image[imageOffset];
+            byte opCode2 = _reader.Image[imageOffset + 1];
+            return opCode1 == 0x0F &&
+                (opCode2 >= 0x80 && opCode2 <= 0x8F); // near conditional jumps
         }
     }
 }
index 46db0fe..75c0326 100644 (file)
@@ -11,7 +11,7 @@ using System.Text;
 
 namespace R2RDump
 {
-    internal class DisassemblingGenericContext
+    public class DisassemblingGenericContext
     {
         public DisassemblingGenericContext(string[] typeParameters, string[] methodParameters)
         {
@@ -25,7 +25,7 @@ namespace R2RDump
 
     // Test implementation of ISignatureTypeProvider<TType, TGenericContext> that uses strings in ilasm syntax as TType.
     // A real provider in any sort of perf constraints would not want to allocate strings freely like this, but it keeps test code simple.
-    internal class DisassemblingTypeProvider : ISignatureTypeProvider<string, DisassemblingGenericContext>
+    public class DisassemblingTypeProvider : ISignatureTypeProvider<string, DisassemblingGenericContext>
     {
         public virtual string GetPrimitiveType(PrimitiveTypeCode typeCode)
         {
@@ -69,7 +69,7 @@ namespace R2RDump
                     return "[" + reader.GetString(assemblyReference.Name) + "]" + name;
 
                 case HandleKind.TypeReference:
-                    return GetTypeFromReference(reader, (TypeReferenceHandle)scope) + "/" + name;
+                    return GetTypeFromReference(reader, (TypeReferenceHandle)scope) + "+" + name;
 
                 default:
                     return name;
@@ -100,8 +100,7 @@ namespace R2RDump
         {
             if (index >= genericContext.MethodParameters.Length)
             {
-                R2RDump.WriteWarning("GenericMethodParameters index out of bounds");
-                return "";
+                return "!!" + index.ToString();
             }
             return genericContext.MethodParameters[index];
         }
@@ -110,8 +109,7 @@ namespace R2RDump
         {
             if (index >= genericContext.TypeParameters.Length)
             {
-                R2RDump.WriteWarning("GenericTypeParameter index out of bounds");
-                return "";
+                return "!" + index.ToString();
             }
             return genericContext.TypeParameters[index];
         }
diff --git a/src/coreclr/src/tools/r2rdump/R2RConstants.cs b/src/coreclr/src/tools/r2rdump/R2RConstants.cs
new file mode 100644 (file)
index 0000000..bf19230
--- /dev/null
@@ -0,0 +1,354 @@
+// 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;
+
+namespace R2RDump
+{
+    /// <summary>
+    /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportType
+    /// </summary>
+    public enum CorCompileImportType
+    {
+        CORCOMPILE_IMPORT_TYPE_UNKNOWN = 0,
+        CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD = 1,
+        CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH = 2,
+        CORCOMPILE_IMPORT_TYPE_STRING_HANDLE = 3,
+        CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE = 4,
+        CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE = 5,
+        CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD = 6,
+    };
+
+    /// <summary>
+    /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportFlags
+    /// </summary>
+    public enum CorCompileImportFlags
+    {
+        CORCOMPILE_IMPORT_FLAGS_UNKNOWN = 0x0000,
+        CORCOMPILE_IMPORT_FLAGS_EAGER = 0x0001,   // Section at module load time.
+        CORCOMPILE_IMPORT_FLAGS_CODE = 0x0002,   // Section contains code.
+        CORCOMPILE_IMPORT_FLAGS_PCODE = 0x0004,   // Section contains pointers to code.
+    };
+
+    /// <summary>
+    /// Constants for method and field encoding
+    /// </summary>
+    [Flags]
+    public enum ReadyToRunMethodSigFlags : byte
+    {
+        READYTORUN_METHOD_SIG_None = 0x00,
+        READYTORUN_METHOD_SIG_UnboxingStub = 0x01,
+        READYTORUN_METHOD_SIG_InstantiatingStub = 0x02,
+        READYTORUN_METHOD_SIG_MethodInstantiation = 0x04,
+        READYTORUN_METHOD_SIG_SlotInsteadOfToken = 0x08,
+        READYTORUN_METHOD_SIG_MemberRefToken = 0x10,
+        READYTORUN_METHOD_SIG_Constrained = 0x20,
+        READYTORUN_METHOD_SIG_OwnerType = 0x40,
+    }
+
+    [Flags]
+    public enum ReadyToRunFieldSigFlags : byte
+    {
+        READYTORUN_FIELD_SIG_IndexInsteadOfToken = 0x08,
+        READYTORUN_FIELD_SIG_MemberRefToken = 0x10,
+        READYTORUN_FIELD_SIG_OwnerType = 0x40,
+    }
+
+    [Flags]
+    public enum ReadyToRunTypeLayoutFlags : byte
+    {
+        READYTORUN_LAYOUT_HFA = 0x01,
+        READYTORUN_LAYOUT_Alignment = 0x02,
+        READYTORUN_LAYOUT_Alignment_Native = 0x04,
+        READYTORUN_LAYOUT_GCLayout = 0x08,
+        READYTORUN_LAYOUT_GCLayout_Empty = 0x10,
+    }
+
+    public enum ReadyToRunFixupKind
+    {
+        READYTORUN_FIXUP_ThisObjDictionaryLookup = 0x07,
+        READYTORUN_FIXUP_TypeDictionaryLookup = 0x08,
+        READYTORUN_FIXUP_MethodDictionaryLookup = 0x09,
+
+        READYTORUN_FIXUP_TypeHandle = 0x10,
+        READYTORUN_FIXUP_MethodHandle = 0x11,
+        READYTORUN_FIXUP_FieldHandle = 0x12,
+
+        READYTORUN_FIXUP_MethodEntry = 0x13, /* For calling a method entry point */
+        READYTORUN_FIXUP_MethodEntry_DefToken = 0x14, /* Smaller version of MethodEntry - method is def token */
+        READYTORUN_FIXUP_MethodEntry_RefToken = 0x15, /* Smaller version of MethodEntry - method is ref token */
+
+        READYTORUN_FIXUP_VirtualEntry = 0x16, /* For invoking a virtual method */
+        READYTORUN_FIXUP_VirtualEntry_DefToken = 0x17, /* Smaller version of VirtualEntry - method is def token */
+        READYTORUN_FIXUP_VirtualEntry_RefToken = 0x18, /* Smaller version of VirtualEntry - method is ref token */
+        READYTORUN_FIXUP_VirtualEntry_Slot = 0x19, /* Smaller version of VirtualEntry - type & slot */
+
+        READYTORUN_FIXUP_Helper = 0x1A, /* Helper */
+        READYTORUN_FIXUP_StringHandle = 0x1B, /* String handle */
+
+        READYTORUN_FIXUP_NewObject = 0x1C, /* Dynamically created new helper */
+        READYTORUN_FIXUP_NewArray = 0x1D,
+
+        READYTORUN_FIXUP_IsInstanceOf = 0x1E, /* Dynamically created casting helper */
+        READYTORUN_FIXUP_ChkCast = 0x1F,
+
+        READYTORUN_FIXUP_FieldAddress = 0x20, /* For accessing a cross-module static fields */
+        READYTORUN_FIXUP_CctorTrigger = 0x21, /* Static constructor trigger */
+
+        READYTORUN_FIXUP_StaticBaseNonGC = 0x22, /* Dynamically created static base helpers */
+        READYTORUN_FIXUP_StaticBaseGC = 0x23,
+        READYTORUN_FIXUP_ThreadStaticBaseNonGC = 0x24,
+        READYTORUN_FIXUP_ThreadStaticBaseGC = 0x25,
+
+        READYTORUN_FIXUP_FieldBaseOffset = 0x26, /* Field base offset */
+        READYTORUN_FIXUP_FieldOffset = 0x27, /* Field offset */
+
+        READYTORUN_FIXUP_TypeDictionary = 0x28,
+        READYTORUN_FIXUP_MethodDictionary = 0x29,
+
+        READYTORUN_FIXUP_Check_TypeLayout = 0x2A, /* size, alignment, HFA, reference map */
+        READYTORUN_FIXUP_Check_FieldOffset = 0x2B,
+
+        READYTORUN_FIXUP_DelegateCtor = 0x2C, /* optimized delegate ctor */
+        READYTORUN_FIXUP_DeclaringTypeHandle = 0x2D,
+    }
+
+    //
+    // Intrinsics and helpers
+    //
+
+    [Flags]
+    public enum ReadyToRunHelper
+    {
+        READYTORUN_HELPER_Invalid = 0x00,
+
+        // Not a real helper - handle to current module passed to delay load helpers.
+        READYTORUN_HELPER_Module = 0x01,
+        READYTORUN_HELPER_GSCookie = 0x02,
+
+        //
+        // Delay load helpers
+        //
+
+        // All delay load helpers use custom calling convention:
+        // - scratch register - address of indirection cell. 0 = address is inferred from callsite.
+        // - stack - section index, module handle
+        READYTORUN_HELPER_DelayLoad_MethodCall = 0x08,
+
+        READYTORUN_HELPER_DelayLoad_Helper = 0x10,
+        READYTORUN_HELPER_DelayLoad_Helper_Obj = 0x11,
+        READYTORUN_HELPER_DelayLoad_Helper_ObjObj = 0x12,
+
+        // JIT helpers
+
+        // Exception handling helpers
+        READYTORUN_HELPER_Throw = 0x20,
+        READYTORUN_HELPER_Rethrow = 0x21,
+        READYTORUN_HELPER_Overflow = 0x22,
+        READYTORUN_HELPER_RngChkFail = 0x23,
+        READYTORUN_HELPER_FailFast = 0x24,
+        READYTORUN_HELPER_ThrowNullRef = 0x25,
+        READYTORUN_HELPER_ThrowDivZero = 0x26,
+
+        // Write barriers
+        READYTORUN_HELPER_WriteBarrier = 0x30,
+        READYTORUN_HELPER_CheckedWriteBarrier = 0x31,
+        READYTORUN_HELPER_ByRefWriteBarrier = 0x32,
+
+        // Array helpers
+        READYTORUN_HELPER_Stelem_Ref = 0x38,
+        READYTORUN_HELPER_Ldelema_Ref = 0x39,
+
+        READYTORUN_HELPER_MemSet = 0x40,
+        READYTORUN_HELPER_MemCpy = 0x41,
+
+        // Get string handle lazily
+        READYTORUN_HELPER_GetString = 0x50,
+
+        // Used by /Tuning for Profile optimizations
+        READYTORUN_HELPER_LogMethodEnter = 0x51,
+
+        // Reflection helpers
+        READYTORUN_HELPER_GetRuntimeTypeHandle = 0x54,
+        READYTORUN_HELPER_GetRuntimeMethodHandle = 0x55,
+        READYTORUN_HELPER_GetRuntimeFieldHandle = 0x56,
+
+        READYTORUN_HELPER_Box = 0x58,
+        READYTORUN_HELPER_Box_Nullable = 0x59,
+        READYTORUN_HELPER_Unbox = 0x5A,
+        READYTORUN_HELPER_Unbox_Nullable = 0x5B,
+        READYTORUN_HELPER_NewMultiDimArr = 0x5C,
+        READYTORUN_HELPER_NewMultiDimArr_NonVarArg = 0x5D,
+
+        // Helpers used with generic handle lookup cases
+        READYTORUN_HELPER_NewObject = 0x60,
+        READYTORUN_HELPER_NewArray = 0x61,
+        READYTORUN_HELPER_CheckCastAny = 0x62,
+        READYTORUN_HELPER_CheckInstanceAny = 0x63,
+        READYTORUN_HELPER_GenericGcStaticBase = 0x64,
+        READYTORUN_HELPER_GenericNonGcStaticBase = 0x65,
+        READYTORUN_HELPER_GenericGcTlsBase = 0x66,
+        READYTORUN_HELPER_GenericNonGcTlsBase = 0x67,
+        READYTORUN_HELPER_VirtualFuncPtr = 0x68,
+
+        // Long mul/div/shift ops
+        READYTORUN_HELPER_LMul = 0xC0,
+        READYTORUN_HELPER_LMulOfv = 0xC1,
+        READYTORUN_HELPER_ULMulOvf = 0xC2,
+        READYTORUN_HELPER_LDiv = 0xC3,
+        READYTORUN_HELPER_LMod = 0xC4,
+        READYTORUN_HELPER_ULDiv = 0xC5,
+        READYTORUN_HELPER_ULMod = 0xC6,
+        READYTORUN_HELPER_LLsh = 0xC7,
+        READYTORUN_HELPER_LRsh = 0xC8,
+        READYTORUN_HELPER_LRsz = 0xC9,
+        READYTORUN_HELPER_Lng2Dbl = 0xCA,
+        READYTORUN_HELPER_ULng2Dbl = 0xCB,
+
+        // 32-bit division helpers
+        READYTORUN_HELPER_Div = 0xCC,
+        READYTORUN_HELPER_Mod = 0xCD,
+        READYTORUN_HELPER_UDiv = 0xCE,
+        READYTORUN_HELPER_UMod = 0xCF,
+
+        // Floating point conversions
+        READYTORUN_HELPER_Dbl2Int = 0xD0,
+        READYTORUN_HELPER_Dbl2IntOvf = 0xD1,
+        READYTORUN_HELPER_Dbl2Lng = 0xD2,
+        READYTORUN_HELPER_Dbl2LngOvf = 0xD3,
+        READYTORUN_HELPER_Dbl2UInt = 0xD4,
+        READYTORUN_HELPER_Dbl2UIntOvf = 0xD5,
+        READYTORUN_HELPER_Dbl2ULng = 0xD6,
+        READYTORUN_HELPER_Dbl2ULngOvf = 0xD7,
+
+        // Floating point ops
+        READYTORUN_HELPER_DblRem = 0xE0,
+        READYTORUN_HELPER_FltRem = 0xE1,
+        READYTORUN_HELPER_DblRound = 0xE2,
+        READYTORUN_HELPER_FltRound = 0xE3,
+
+        // Personality rountines
+        READYTORUN_HELPER_PersonalityRoutine = 0xF0,
+        READYTORUN_HELPER_PersonalityRoutineFilterFunclet = 0xF1,
+
+        //
+        // Deprecated/legacy
+        //
+
+        // JIT32 x86-specific write barriers
+        READYTORUN_HELPER_WriteBarrier_EAX = 0x100,
+        READYTORUN_HELPER_WriteBarrier_EBX = 0x101,
+        READYTORUN_HELPER_WriteBarrier_ECX = 0x102,
+        READYTORUN_HELPER_WriteBarrier_ESI = 0x103,
+        READYTORUN_HELPER_WriteBarrier_EDI = 0x104,
+        READYTORUN_HELPER_WriteBarrier_EBP = 0x105,
+        READYTORUN_HELPER_CheckedWriteBarrier_EAX = 0x106,
+        READYTORUN_HELPER_CheckedWriteBarrier_EBX = 0x107,
+        READYTORUN_HELPER_CheckedWriteBarrier_ECX = 0x108,
+        READYTORUN_HELPER_CheckedWriteBarrier_ESI = 0x109,
+        READYTORUN_HELPER_CheckedWriteBarrier_EDI = 0x10A,
+        READYTORUN_HELPER_CheckedWriteBarrier_EBP = 0x10B,
+
+        // JIT32 x86-specific exception handling
+        READYTORUN_HELPER_EndCatch = 0x110,
+
+        // A flag to indicate that a helper call uses VSD
+        READYTORUN_HELPER_FLAG_VSD = 0x10000000,
+    }
+
+    public enum CorElementType : byte
+    {
+        Invalid = 0,
+        ELEMENT_TYPE_VOID = 1,
+        ELEMENT_TYPE_BOOLEAN = 2,
+        ELEMENT_TYPE_CHAR = 3,
+        ELEMENT_TYPE_I1 = 4,
+        ELEMENT_TYPE_U1 = 5,
+        ELEMENT_TYPE_I2 = 6,
+        ELEMENT_TYPE_U2 = 7,
+        ELEMENT_TYPE_I4 = 8,
+        ELEMENT_TYPE_U4 = 9,
+        ELEMENT_TYPE_I8 = 10,
+        ELEMENT_TYPE_U8 = 11,
+        ELEMENT_TYPE_R4 = 12,
+        ELEMENT_TYPE_R8 = 13,
+        ELEMENT_TYPE_STRING = 14,
+        ELEMENT_TYPE_PTR = 15,
+        ELEMENT_TYPE_BYREF = 16,
+        ELEMENT_TYPE_VALUETYPE = 17,
+        ELEMENT_TYPE_CLASS = 18,
+        ELEMENT_TYPE_VAR = 19,
+        ELEMENT_TYPE_ARRAY = 20,
+        ELEMENT_TYPE_GENERICINST = 21,
+        ELEMENT_TYPE_TYPEDBYREF = 22,
+        ELEMENT_TYPE_I = 24,
+        ELEMENT_TYPE_U = 25,
+        ELEMENT_TYPE_FNPTR = 27,
+        ELEMENT_TYPE_OBJECT = 28,
+        ELEMENT_TYPE_SZARRAY = 29,
+        ELEMENT_TYPE_MVAR = 30,
+        ELEMENT_TYPE_CMOD_REQD = 31,
+        ELEMENT_TYPE_CMOD_OPT = 32,
+
+        // ZapSig encoding for ELEMENT_TYPE_VAR and ELEMENT_TYPE_MVAR. It is always followed
+        // by the RID of a GenericParam token, encoded as a compressed integer.
+        ELEMENT_TYPE_VAR_ZAPSIG = 0x3b,
+
+        // ZapSig encoding for an array MethodTable to allow it to remain such after decoding
+        // (rather than being transformed into the TypeHandle representing that array)
+        //
+        // The element is always followed by ELEMENT_TYPE_SZARRAY or ELEMENT_TYPE_ARRAY
+        ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG = 0x3c,
+
+        // ZapSig encoding for native value types in IL stubs. IL stub signatures may contain
+        // ELEMENT_TYPE_INTERNAL followed by ParamTypeDesc with ELEMENT_TYPE_VALUETYPE element
+        // type. It acts like a modifier to the underlying structure making it look like its
+        // unmanaged view (size determined by unmanaged layout, blittable, no GC pointers).
+        // 
+        // ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG is used when encoding such types to NGEN images.
+        // The signature looks like this: ET_NATIVE_VALUETYPE_ZAPSIG ET_VALUETYPE <token>.
+        // See code:ZapSig.GetSignatureForTypeHandle and code:SigPointer.GetTypeHandleThrowing
+        // where the encoding/decoding takes place.
+        ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG = 0x3d,
+
+        ELEMENT_TYPE_CANON_ZAPSIG = 0x3e,     // zapsig encoding for [mscorlib]System.__Canon
+        ELEMENT_TYPE_MODULE_ZAPSIG = 0x3f,     // zapsig encoding for external module id#
+
+        ELEMENT_TYPE_HANDLE = 64,
+        ELEMENT_TYPE_SENTINEL = 65,
+        ELEMENT_TYPE_PINNED = 69,
+    }
+
+    public enum CorTokenType
+    {
+        mdtModule = 0x00000000,
+        mdtTypeRef = 0x01000000,
+        mdtTypeDef = 0x02000000,
+        mdtFieldDef = 0x04000000,
+        mdtMethodDef = 0x06000000,
+        mdtParamDef = 0x08000000,
+        mdtInterfaceImpl = 0x09000000,
+        mdtMemberRef = 0x0a000000,
+        mdtCustomAttribute = 0x0c000000,
+        mdtPermission = 0x0e000000,
+        mdtSignature = 0x11000000,
+        mdtEvent = 0x14000000,
+        mdtProperty = 0x17000000,
+        mdtMethodImpl = 0x19000000,
+        mdtModuleRef = 0x1a000000,
+        mdtTypeSpec = 0x1b000000,
+        mdtAssembly = 0x20000000,
+        mdtAssemblyRef = 0x23000000,
+        mdtFile = 0x26000000,
+        mdtExportedType = 0x27000000,
+        mdtManifestResource = 0x28000000,
+        mdtGenericParam = 0x2a000000,
+        mdtMethodSpec = 0x2b000000,
+        mdtGenericParamConstraint = 0x2c000000,
+
+        mdtString = 0x70000000,
+        mdtName = 0x71000000,
+        mdtBaseType = 0x72000000,
+    }     
+}
index e83931c..34b86b7 100644 (file)
@@ -405,7 +405,7 @@ namespace R2RDump
                     {
                         if (r2r.InputArchitectureSupported() && r2r.DisassemblerArchitectureSupported())
                         {
-                            disassembler = new Disassembler(r2r.Image, r2r.Machine);
+                            disassembler = new Disassembler(r2r);
                         }
                         else
                         {
index 4128aec..7ae73cb 100644 (file)
@@ -15,31 +15,6 @@ namespace R2RDump
     /// </summary>
     public struct R2RImportSection
     {
-        /// <summary>
-        /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportType
-        /// </summary>
-        public enum CorCompileImportType
-        {
-            CORCOMPILE_IMPORT_TYPE_UNKNOWN = 0,
-            CORCOMPILE_IMPORT_TYPE_EXTERNAL_METHOD = 1,
-            CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH = 2,
-            CORCOMPILE_IMPORT_TYPE_STRING_HANDLE = 3,
-            CORCOMPILE_IMPORT_TYPE_TYPE_HANDLE = 4,
-            CORCOMPILE_IMPORT_TYPE_METHOD_HANDLE = 5,
-            CORCOMPILE_IMPORT_TYPE_VIRTUAL_METHOD = 6,
-        };
-
-        /// <summary>
-        /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/corcompile.h">src/inc/corcompile.h</a> CorCompileImportFlags
-        /// </summary>
-        public enum CorCompileImportFlags
-        {
-            CORCOMPILE_IMPORT_FLAGS_UNKNOWN = 0x0000,
-            CORCOMPILE_IMPORT_FLAGS_EAGER = 0x0001,   // Section at module load time.
-            CORCOMPILE_IMPORT_FLAGS_CODE = 0x0002,   // Section contains code.
-            CORCOMPILE_IMPORT_FLAGS_PCODE = 0x0004,   // Section contains pointers to code.
-        };
-
         public struct ImportSectionEntry
         {
             [XmlAttribute("Index")]
@@ -47,26 +22,24 @@ namespace R2RDump
             public int StartOffset { get; set; }
             public long Section { get; set; }
             public uint SignatureRVA { get; set; }
-            public byte[] SignatureSample { get; set; }
-            public ImportSectionEntry(int index, int startOffset, long section, uint signatureRVA, byte[] signatureSample)
+            public string Signature { get; set; }
+            public ImportSectionEntry(int index, int startOffset, long section, uint signatureRVA, string signature)
             {
                 Index = index;
                 StartOffset = startOffset;
                 Section = section;
                 SignatureRVA = signatureRVA;
-                SignatureSample = signatureSample;
+                Signature = signature;
             }
 
             public override string ToString()
             {
-                StringBuilder sb = new StringBuilder();
-                sb.Append($@"+{StartOffset:X4}  Section: 0x{Section:X8}  SignatureRVA: 0x{SignatureRVA:X8}  ");
-                foreach (byte b in SignatureSample)
-                {
-                    sb.AppendFormat("{0:X2} ", b);
-                }
-                sb.Append("...");
-                return sb.ToString();
+                StringBuilder builder = new StringBuilder();
+                builder.AppendFormat("+{0:X4}", StartOffset);
+                builder.AppendFormat("  Section: 0x{0:X8}", Section);
+                builder.AppendFormat("  SignatureRVA: 0x{0:X8}", SignatureRVA);
+                builder.AppendFormat("  {0}", Signature);
+                return builder.ToString();
             }
         }
 
index 1d4b00e..7f5ae69 100644 (file)
@@ -134,6 +134,45 @@ namespace R2RDump
                 sb.AppendLine($"Size: {Size} bytes");
             }
             sb.AppendLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+            if (UnwindInfo is Amd64.UnwindInfo amd64UnwindInfo)
+            {
+                string parsedFlags = "";
+                if ((amd64UnwindInfo.Flags & (int)Amd64.UnwindFlags.UNW_FLAG_EHANDLER) != 0)
+                {
+                    parsedFlags += " EHANDLER";
+                }
+                if ((amd64UnwindInfo.Flags & (int)Amd64.UnwindFlags.UNW_FLAG_UHANDLER) != 0)
+                {
+                    parsedFlags += " UHANDLER";
+                }
+                if ((amd64UnwindInfo.Flags & (int)Amd64.UnwindFlags.UNW_FLAG_CHAININFO) != 0)
+                {
+                    parsedFlags += " CHAININFO";
+                }
+                if (parsedFlags.Length == 0)
+                {
+                    parsedFlags = " NHANDLER";
+                }
+                sb.AppendLine($"Version:            {amd64UnwindInfo.Version}");
+                sb.AppendLine($"Flags:              0x{amd64UnwindInfo.Flags:X2}{parsedFlags}");
+                sb.AppendLine($"SizeOfProlog:       0x{amd64UnwindInfo.SizeOfProlog:X4}");
+                sb.AppendLine($"CountOfUnwindCodes: {amd64UnwindInfo.CountOfUnwindCodes}");
+                sb.AppendLine($"FrameRegister:      {amd64UnwindInfo.FrameRegister}");
+                sb.AppendLine($"FrameOffset:        0x{amd64UnwindInfo.FrameOffset}");
+                sb.AppendLine($"PersonalityRVA:     0x{amd64UnwindInfo.PersonalityRoutineRVA:X4}");
+
+                for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++)
+                {
+                    Amd64.UnwindCode unwindCode = amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex];
+                    sb.Append($"UnwindCode[{unwindCode.Index}]: ");
+                    sb.Append($"CodeOffset 0x{unwindCode.CodeOffset:X4} ");
+                    sb.Append($"FrameOffset 0x{unwindCode.FrameOffset:X4} ");
+                    sb.Append($"NextOffset 0x{unwindCode.NextFrameOffset} ");
+                    sb.Append($"Op {unwindCode.OpInfoStr}");
+                    sb.AppendLine();
+                }
+            }
+            sb.AppendLine();
 
             return sb.ToString();
         }
@@ -201,47 +240,12 @@ namespace R2RDump
         /// </summary>
         private Dictionary<string, string> _genericParamInstanceMap;
 
-        [Flags]
-        public enum EncodeMethodSigFlags
-        {
-            NONE = 0x00,
-            ENCODE_METHOD_SIG_UnboxingStub = 0x01,
-            ENCODE_METHOD_SIG_InstantiatingStub = 0x02,
-            ENCODE_METHOD_SIG_MethodInstantiation = 0x04,
-            ENCODE_METHOD_SIG_SlotInsteadOfToken = 0x08,
-            ENCODE_METHOD_SIG_MemberRefToken = 0x10,
-            ENCODE_METHOD_SIG_Constrained = 0x20,
-            ENCODE_METHOD_SIG_OwnerType = 0x40,
-        };
-
-        public enum GenericElementTypes
-        {
-            __Canon = 0x3e,
-            Void = 0x01,
-            Boolean = 0x02,
-            Char = 0x03,
-            Int8 = 0x04,
-            UInt8 = 0x05,
-            Int16 = 0x06,
-            UInt16 = 0x07,
-            Int32 = 0x08,
-            UInt32 = 0x09,
-            Int64 = 0x0a,
-            UInt64 = 0x0b,
-            Float = 0x0c,
-            Double = 0x0d,
-            String = 0x0e,
-            ValueType = 0x11,
-            Object = 0x1c,
-            Array = 0x1d,
-        };
-
         public R2RMethod() { }
 
         /// <summary>
         /// Extracts the method signature from the metadata by rid
         /// </summary>
-        public R2RMethod(int index, MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok, FixupCell[] fixups)
+        public R2RMethod(int index, MetadataReader mdReader, uint rid, int entryPointId, CorElementType[] instanceArgs, uint[] tok, FixupCell[] fixups)
         {
             Index = index;
             Token = _mdtMethodDef | rid;
@@ -258,7 +262,7 @@ namespace R2RDump
             BlobReader signatureReader = mdReader.GetBlobReader(_methodDef.Signature);
 
             TypeDefinitionHandle declaringTypeHandle = _methodDef.GetDeclaringType();
-            DeclaringType = R2RReader.GetTypeDefFullName(mdReader, declaringTypeHandle);
+            DeclaringType = MetadataNameFormatter.FormatHandle(mdReader, declaringTypeHandle);
 
             SignatureHeader signatureHeader = signatureReader.ReadSignatureHeader();
             IsGeneric = signatureHeader.IsGeneric;
@@ -288,7 +292,7 @@ namespace R2RDump
         /// <summary>
         /// Initialize map of generic parameters names to the type in the instance
         /// </summary>
-        private void InitGenericInstances(GenericParameterHandleCollection genericParams, GenericElementTypes[] instanceArgs, uint[] tok)
+        private void InitGenericInstances(GenericParameterHandleCollection genericParams, CorElementType[] instanceArgs, uint[] tok)
         {
             if (instanceArgs.Length != genericParams.Count || tok.Length != genericParams.Count)
             {
@@ -299,7 +303,7 @@ namespace R2RDump
             {
                 string key = _mdReader.GetString(_mdReader.GetGenericParameter(genericParams.ElementAt(i)).Name); // name of the generic param, eg. "T"
                 string type = instanceArgs[i].ToString(); // type of the generic param instance
-                if (instanceArgs[i] == GenericElementTypes.ValueType)
+                if (instanceArgs[i] == CorElementType.ELEMENT_TYPE_VALUETYPE)
                 {
                     var t = _mdReader.GetTypeDefinition(MetadataTokens.TypeDefinitionHandle((int)tok[i]));
                     type = _mdReader.GetString(t.Name); // name of the struct
index d73fdc8..83d2a80 100644 (file)
@@ -117,8 +117,16 @@ namespace R2RDump
         /// </summary>
         public string CompilerIdentifier { get; }
 
+        /// <summary>
+        /// List of import sections present in the R2R executable.
+        /// </summary>
         public IList<R2RImportSection> ImportSections { get; }
 
+        /// <summary>
+        /// Map from import cell addresses to their symbolic names.
+        /// </summary>
+        public Dictionary<int, string> ImportCellNames { get; }
+
         public unsafe R2RReader() { }
 
         /// <summary>
@@ -177,6 +185,7 @@ namespace R2RDump
                     {
                         int runtimeFunctionSize = CalculateRuntimeFunctionSize();
                         R2RSection runtimeFunctionSection = R2RHeader.Sections[R2RSection.SectionType.READYTORUN_SECTION_RUNTIME_FUNCTIONS];
+
                         uint nRuntimeFunctions = (uint)(runtimeFunctionSection.Size / runtimeFunctionSize);
                         int runtimeFunctionOffset = GetOffset(runtimeFunctionSection.RelativeVirtualAddress);
                         bool[] isEntryPoint = new bool[nRuntimeFunctions];
@@ -193,6 +202,7 @@ namespace R2RDump
                     CompilerIdentifier = ParseCompilerIdentifier();
 
                     ImportSections = new List<R2RImportSection>();
+                    ImportCellNames = new Dictionary<int, string>();
                     ParseImportSections();
                 }
             }
@@ -277,15 +287,15 @@ namespace R2RDump
             {
                 uint methodFlags = curParser.GetCompressedData();
                 uint rid = curParser.GetCompressedData();
-                if ((methodFlags & (byte)R2RMethod.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation) != 0)
+                if ((methodFlags & (byte)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
                 {
                     uint nArgs = curParser.GetCompressedData();
-                    R2RMethod.GenericElementTypes[] args = new R2RMethod.GenericElementTypes[nArgs];
+                    CorElementType[] args = new CorElementType[nArgs];
                     uint[] tokens = new uint[nArgs];
                     for (int i = 0; i < nArgs; i++)
                     {
-                        args[i] = (R2RMethod.GenericElementTypes)curParser.GetByte();
-                        if (args[i] == R2RMethod.GenericElementTypes.ValueType)
+                        args[i] = (CorElementType)curParser.GetByte();
+                        if (args[i] == CorElementType.ELEMENT_TYPE_VALUETYPE)
                         {
                             tokens[i] = curParser.GetCompressedData();
                             tokens[i] = (tokens[i] >> 2);
@@ -401,7 +411,7 @@ namespace R2RDump
                     continue;
 
                 TypeDefinitionHandle typeDefHandle = MetadataTokens.TypeDefinitionHandle((int)rid);
-                string typeDefName = GetTypeDefFullName(MetadataReader, typeDefHandle);
+                string typeDefName = MetadataNameFormatter.FormatHandle(MetadataReader, typeDefHandle);
                 ExportedTypeHandle exportedTypeHandle = MetadataTokens.ExportedTypeHandle((int)rid);
                 string exportedTypeName = GetExportedTypeFullName(MetadataReader, exportedTypeHandle);
                 if (typeDefName == null && exportedTypeName == null)
@@ -457,7 +467,7 @@ namespace R2RDump
                 int sectionOffset = GetOffset(rva);
                 int startOffset = sectionOffset;
                 int size = NativeReader.ReadInt32(Image, ref offset);
-                R2RImportSection.CorCompileImportFlags flags = (R2RImportSection.CorCompileImportFlags)NativeReader.ReadUInt16(Image, ref offset);
+                CorCompileImportFlags flags = (CorCompileImportFlags)NativeReader.ReadUInt16(Image, ref offset);
                 byte type = NativeReader.ReadByte(Image, ref offset);
                 byte entrySize = NativeReader.ReadByte(Image, ref offset);
                 if (entrySize == 0)
@@ -492,39 +502,15 @@ namespace R2RDump
                     signatureOffset = GetOffset(signatureRVA);
                 }
                 List<R2RImportSection.ImportSectionEntry> entries = new List<R2RImportSection.ImportSectionEntry>();
-                switch (flags)
+                for (int i = 0; i < entryCount; i++)
                 {
-                    case R2RImportSection.CorCompileImportFlags.CORCOMPILE_IMPORT_FLAGS_EAGER:
-                        {
-                            int tempSignatureOffset = signatureOffset;
-                            int firstSigRva = NativeReader.ReadInt32(Image, ref tempSignatureOffset);
-                            uint sigRva = 0;
-                            while (sigRva != firstSigRva)
-                            {
-                                int entryOffset = sectionOffset - startOffset;
-                                sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
-                                long section = NativeReader.ReadInt64(Image, ref sectionOffset);
-                                int sigOff = GetOffset((int)sigRva);
-                                int sigSampleLength = Math.Min(8, Image.Length - sigOff);
-                                byte[] signatureSample = new byte[sigSampleLength];
-                                Array.Copy(Image, sigOff, signatureSample, 0, sigSampleLength);
-                                entries.Add(new R2RImportSection.ImportSectionEntry(entries.Count, entryOffset, section, sigRva, signatureSample));
-                            }
-                        }
-                        break;
-                    default:
-                        for (int i = 0; i < entryCount; i++)
-                        {
-                            int entryOffset = sectionOffset - startOffset;
-                            long section = NativeReader.ReadInt64(Image, ref sectionOffset);
-                            uint sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
-                            int sigOff = GetOffset((int)sigRva);
-                            int sigSampleLength = Math.Min(8, Image.Length - sigOff);
-                            byte[] signatureSample = new byte[sigSampleLength];
-                            Array.Copy(Image, sigOff, signatureSample, 0, sigSampleLength);
-                            entries.Add(new R2RImportSection.ImportSectionEntry(entries.Count, entryOffset, section, sigRva, signatureSample));
-                        }
-                        break;
+                    int entryOffset = sectionOffset - startOffset;
+                    long section = NativeReader.ReadInt64(Image, ref sectionOffset);
+                    uint sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
+                    int sigOffset = GetOffset((int)sigRva);
+                    string cellName = MetadataNameFormatter.FormatSignature(this, sigOffset);
+                    entries.Add(new R2RImportSection.ImportSectionEntry(entries.Count, entryOffset, section, sigRva, cellName));
+                    ImportCellNames.Add(rva + entrySize * i, cellName);
                 }
 
                 int auxDataRVA = NativeReader.ReadInt32(Image, ref offset);
@@ -553,33 +539,6 @@ namespace R2RDump
         }
 
         /// <summary>
-        /// Get the full name of a type, including parent classes and namespace
-        /// </summary>
-        public static string GetTypeDefFullName(MetadataReader mdReader, TypeDefinitionHandle handle)
-        {
-            string typeNamespace = "";
-            string typeStr = "";
-            do
-            {
-                try
-                {
-                    TypeDefinition typeDef = mdReader.GetTypeDefinition(handle);
-                    typeStr = "." + mdReader.GetString(typeDef.Name) + typeStr;
-                    handle = typeDef.GetDeclaringType();
-                    if (handle.IsNil)
-                        typeNamespace = mdReader.GetString(typeDef.Namespace);
-                }
-                catch (BadImageFormatException)
-                {
-                    return null;
-                }
-            }
-            while (!handle.IsNil);
-
-            return typeNamespace + typeStr;
-        }
-
-        /// <summary>
         /// Get the full name of an ExportedType, including namespace
         /// </summary>
         public static string GetExportedTypeFullName(MetadataReader mdReader, ExportedTypeHandle handle)
diff --git a/src/coreclr/src/tools/r2rdump/R2RSignature.cs b/src/coreclr/src/tools/r2rdump/R2RSignature.cs
new file mode 100644 (file)
index 0000000..2522200
--- /dev/null
@@ -0,0 +1,1227 @@
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+using System.Text;
+using System.Xml.Serialization;
+
+namespace R2RDump
+{
+    /// <summary>
+    /// Helper class for converting metadata tokens into their textual representation.
+    /// </summary>
+    public class MetadataNameFormatter : DisassemblingTypeProvider
+    {
+        /// <summary>
+        /// Metadata reader used for the purpose of metadata-based name formatting.
+        /// </summary>
+        private readonly MetadataReader _metadataReader;
+
+        public MetadataNameFormatter(MetadataReader metadataReader)
+        {
+            _metadataReader = metadataReader;
+        }
+
+        /// <summary>
+        /// Construct the textual representation of a given metadata handle.
+        /// </summary>
+        /// <param name="metadataReader">Metadata reader corresponding to the handle</param>
+        /// <param name="handle">Metadata handle to parse</param>
+        /// <param name="namespaceQualified">Include namespace in type names</param>
+        public static string FormatHandle(MetadataReader metadataReader, Handle handle, bool namespaceQualified = true)
+        {
+            MetadataNameFormatter formatter = new MetadataNameFormatter(metadataReader);
+            return formatter.EmitHandleName(handle, namespaceQualified);
+        }
+
+        public static string FormatSignature(R2RReader r2rReader, int imageOffset)
+        {
+            SignatureDecoder decoder = new SignatureDecoder(r2rReader, imageOffset);
+            string result = decoder.ReadR2RSignature();
+            return result;
+        }
+
+        /// <summary>
+        /// Emit a given token to a specified string builder.
+        /// </summary>
+        /// <param name="methodToken">ECMA token to provide string representation for</param>
+        private string EmitHandleName(Handle handle, bool namespaceQualified)
+        {
+            switch (handle.Kind)
+            {
+                case HandleKind.MemberReference:
+                    return EmitMemberReferenceName((MemberReferenceHandle)handle);
+
+                case HandleKind.MethodSpecification:
+                    return EmitMethodSpecificationName((MethodSpecificationHandle)handle);
+
+                case HandleKind.MethodDefinition:
+                    return EmitMethodDefinitionName((MethodDefinitionHandle)handle);
+
+                case HandleKind.TypeReference:
+                    return EmitTypeReferenceName((TypeReferenceHandle)handle, namespaceQualified);
+
+                case HandleKind.TypeSpecification:
+                    return EmitTypeSpecificationName((TypeSpecificationHandle)handle, namespaceQualified);
+
+                case HandleKind.TypeDefinition:
+                    return EmitTypeDefinitionName((TypeDefinitionHandle)handle, namespaceQualified);
+
+                default:
+                    throw new NotImplementedException();
+            }
+        }
+
+        /// <summary>
+        /// Emit a method specification.
+        /// </summary>
+        /// <param name="methodSpecHandle">Method specification handle</param>
+        private string EmitMethodSpecificationName(MethodSpecificationHandle methodSpecHandle)
+        {
+            MethodSpecification methodSpec = _metadataReader.GetMethodSpecification(methodSpecHandle);
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
+            return EmitHandleName(methodSpec.Method, namespaceQualified: true) + methodSpec.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
+        }
+
+        /// <summary>
+        /// Emit a method reference.
+        /// </summary>
+        /// <param name="memberRefHandle">Member reference handle</param>
+        private string EmitMemberReferenceName(MemberReferenceHandle memberRefHandle)
+        {
+            MemberReference methodRef = _metadataReader.GetMemberReference(memberRefHandle);
+            StringBuilder builder = new StringBuilder();
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
+            MethodSignature<String> methodSig = methodRef.DecodeMethodSignature<string, DisassemblingGenericContext>(this, genericContext);
+            builder.Append(methodSig.ReturnType);
+            builder.Append(" ");
+            builder.Append(EmitContainingTypeAndMethodName(methodRef));
+            builder.Append(EmitMethodSignature(methodSig));
+            return builder.ToString();
+        }
+
+        /// <summary>
+        /// Emit a method definition.
+        /// </summary>
+        /// <param name="methodSpecHandle">Method definition handle</param>
+        private string EmitMethodDefinitionName(MethodDefinitionHandle methodDefinitionHandle)
+        {
+            MethodDefinition methodDef = _metadataReader.GetMethodDefinition(methodDefinitionHandle);
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
+            MethodSignature<string> methodSig = methodDef.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
+            StringBuilder builder = new StringBuilder();
+            builder.Append(methodSig.ReturnType);
+            builder.Append(" ");
+            builder.Append(EmitHandleName(methodDef.GetDeclaringType(), namespaceQualified: true));
+            builder.Append(".");
+            builder.Append(EmitString(methodDef.Name));
+            builder.Append(EmitMethodSignature(methodSig));
+            return builder.ToString();
+        }
+
+        /// <summary>
+        /// Emit method generic arguments and parameter list.
+        /// </summary>
+        /// <param name="methodSignature">Method signature to format</param>
+        private string EmitMethodSignature(MethodSignature<string> methodSignature)
+        {
+            StringBuilder builder = new StringBuilder();
+            if (methodSignature.GenericParameterCount != 0)
+            {
+                builder.Append("<");
+                bool firstTypeArg = true;
+                for (int typeArgIndex = 0; typeArgIndex < methodSignature.GenericParameterCount; typeArgIndex++)
+                {
+                    if (firstTypeArg)
+                    {
+                        firstTypeArg = false;
+                    }
+                    else
+                    {
+                        builder.Append(", ");
+                    }
+                    builder.Append("!!");
+                    builder.Append(typeArgIndex);
+                }
+                builder.Append(">");
+            }
+            builder.Append("(");
+            bool firstMethodArg = true;
+            foreach (string paramType in methodSignature.ParameterTypes)
+            {
+                if (firstMethodArg)
+                {
+                    firstMethodArg = false;
+                }
+                else
+                {
+                    builder.Append(", ");
+                }
+                builder.Append(paramType);
+            }
+            builder.Append(")");
+            return builder.ToString();
+        }
+
+        /// <summary>
+        /// Emit containing type and method name and extract the method signature from a method reference.
+        /// </summary>
+        /// <param name="methodRef">Method reference to format</param>
+        /// <param name="methodSignature">Output method signature</param>
+        private string EmitContainingTypeAndMethodName(MemberReference methodRef)
+        {
+            return EmitHandleName(methodRef.Parent, namespaceQualified: true) + "." + EmitString(methodRef.Name);
+        }
+
+        /// <summary>
+        /// Emit type reference.
+        /// </summary>
+        /// <param name="typeRefHandle">Type reference handle</param>
+        /// <param name="namespaceQualified">When set to true, include namespace information</param>
+        private string EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool namespaceQualified)
+        {
+            TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle);
+            string typeName = EmitString(typeRef.Name);
+            string output = "";
+            if (typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference)
+            {
+                // Nested type - format enclosing type followed by the nested type
+                return EmitHandleName(typeRef.ResolutionScope, namespaceQualified) + "+" + typeName;
+            }
+            if (namespaceQualified)
+            {
+                output = EmitString(typeRef.Namespace);
+                if (!string.IsNullOrEmpty(output))
+                {
+                    output += ".";
+                }
+            }
+            return output + typeName;
+        }
+
+        /// <summary>
+        /// Emit a type definition.
+        /// </summary>
+        /// <param name="typeDefHandle">Type definition handle</param>
+        /// <param name="namespaceQualified">true = prefix type name with namespace information</param>
+        /// <returns></returns>
+        private string EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, bool namespaceQualified)
+        {
+            TypeDefinition typeDef = _metadataReader.GetTypeDefinition(typeDefHandle);
+            string typeName = EmitString(typeDef.Name);
+            if (typeDef.IsNested)
+            {
+                // Nested type
+                return EmitHandleName(typeDef.GetDeclaringType(), namespaceQualified) + "+" + typeName;
+            }
+
+            string output;
+            if (namespaceQualified)
+            {
+                output = EmitString(typeDef.Namespace);
+                if (!string.IsNullOrEmpty(output))
+                {
+                    output += ".";
+                }
+            }
+            else
+            {
+                output = "";
+            }
+            return output + typeName;
+        }
+
+        /// <summary>
+        /// Emit an arbitrary type specification.
+        /// </summary>
+        /// <param name="typeSpecHandle">Type specification handle</param>
+        /// <param name="namespaceQualified">When set to true, include namespace information</param>
+        private string EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, bool namespaceQualified)
+        {
+            TypeSpecification typeSpec = _metadataReader.GetTypeSpecification(typeSpecHandle);
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(Array.Empty<string>(), Array.Empty<string>());
+            return typeSpec.DecodeSignature<string, DisassemblingGenericContext>(this, genericContext);
+        }
+
+        private string EmitString(StringHandle handle)
+        {
+            return _metadataReader.GetString(handle);
+        }
+    }
+
+    /// <summary>
+    /// Helper class used as state machine for decoding a single signature.
+    /// </summary>
+    public class SignatureDecoder
+    {
+        /// <summary>
+        /// Metadata reader is used to access the embedded MSIL metadata blob in the R2R file.
+        /// </summary>
+        private readonly MetadataReader _metadataReader;
+
+        /// <summary>
+        /// Byte array representing the R2R PE file read from disk.
+        /// </summary>
+        private readonly byte[] _image;
+
+        /// <summary>
+        /// Offset within the image file.
+        /// </summary>
+        private int _offset;
+
+        /// <summary>
+        /// Query signature parser for the current offset.
+        /// </summary>
+        public int Offset => _offset;
+
+        /// <summary>
+        /// Construct the signature decoder by storing the image byte array and offset within the array. 
+        /// </summary>
+        /// <param name="reader">R2RReader object representing the R2R PE file</param>
+        /// <param name="offset">Signature offset within the array</param>
+        public SignatureDecoder(R2RReader reader, int offset)
+        {
+            _image = reader.Image;
+            _metadataReader = reader.MetadataReader;
+            _offset = offset;
+        }
+
+        /// <summary>
+        /// Read a single byte from the signature stream and advances the current offset.
+        /// </summary>
+        public byte ReadByte()
+        {
+            return _image[_offset++];
+        }
+
+        /// <summary>
+        /// Read a single unsigned 32-bit in from the signature stream. Adapted from CorSigUncompressData,
+        /// <a href="">https://github.com/dotnet/coreclr/blob/master/src/inc/cor.h</a>.
+        /// </summary>
+        /// <param name="data"></param>
+        public uint ReadUInt()
+        {
+            // Handle smallest data inline. 
+            byte firstByte = ReadByte();
+            if ((firstByte & 0x80) == 0x00) // 0??? ????
+                return firstByte;
+
+            uint res;
+            // Medium.
+            if ((firstByte & 0xC0) == 0x80)  // 10?? ????
+            {
+                res = ((uint)(firstByte & 0x3f) << 8);
+                res |= ReadByte();
+            }
+            else // 110? ???? 
+            {
+                res = (uint)(firstByte & 0x1f) << 24;
+                res |= (uint)ReadByte() << 16;
+                res |= (uint)ReadByte() << 8;
+                res |= (uint)ReadByte();
+            }
+            return res;
+        }
+
+        /// <summary>
+        /// Read a signed integer from the signature stream. Signed integer is basically encoded
+        /// as an unsigned integer after converting it to the unsigned number 2 * abs(x) + (x &gt;= 0 ? 0 : 1).
+        /// Adapted from CorSigUncompressSignedInt, <a href="">https://github.com/dotnet/coreclr/blob/master/src/inc/cor.h</a>.
+        /// </summary>
+        public int ReadInt()
+        {
+            uint rawData = ReadUInt();
+            int data = (int)(rawData >> 1);
+            return ((rawData & 1) == 0 ? +data : -data);
+        }
+
+        /// <summary>
+        /// Read an encoded token from the stream. This encoding left-shifts the token RID twice and
+        /// fills in the two least-important bits with token type (typeDef, typeRef, typeSpec, baseType).
+        /// </summary>
+        public uint ReadToken()
+        {
+            uint encodedToken = ReadUInt();
+            uint rid = encodedToken >> 2;
+            CorTokenType type;
+            switch (encodedToken & 3)
+            {
+                case 0:
+                    type = CorTokenType.mdtTypeDef;
+                    break;
+
+                case 1:
+                    type = CorTokenType.mdtTypeRef;
+                    break;
+
+                case 2:
+                    type = CorTokenType.mdtTypeSpec;
+                    break;
+
+                case 3:
+                    type = CorTokenType.mdtBaseType;
+                    break;
+
+                default:
+                    // This should never happen
+                    throw new NotImplementedException();
+            }
+            return (uint)type | rid;
+        }
+
+        /// <summary>
+        /// Read a single element type from the signature stream. Adapted from CorSigUncompressElementType,
+        /// <a href="">https://github.com/dotnet/coreclr/blob/master/src/inc/cor.h</a>.
+        /// </summary>
+        /// <returns></returns>
+        public CorElementType ReadElementType()
+        {
+            return (CorElementType)(ReadByte() & 0x7F);
+        }
+
+        /// <summary>
+        /// Decode a R2R import signature. The signature starts with the fixup type followed
+        /// by custom encoding per fixup type.
+        /// </summary>
+        /// <returns></returns>
+        public string ReadR2RSignature()
+        {
+            StringBuilder builder = new StringBuilder();
+            ParseSignature(builder);
+            return builder.ToString();
+        }
+
+        /// <summary>
+        /// Parse the signature into a given output string builder.
+        /// </summary>
+        /// <param name="builder"></param>
+        private void ParseSignature(StringBuilder builder)
+        {
+            uint fixupType = ReadUInt();
+            switch ((ReadyToRunFixupKind)fixupType)
+            {
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup:
+                    builder.Append("THIS_OBJ_DICTIONARY_LOOKUP");
+                    // TODO
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_TypeDictionaryLookup:
+                    builder.Append("TYPE_DICTIONARY_LOOKUP");
+                    // TODO
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionaryLookup:
+                    builder.Append("METHOD_DICTIONARY_LOOKUP");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_TypeHandle:
+                    ParseType(builder);
+                    builder.Append(" (TYPE_HANDLE)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodHandle:
+                    builder.Append("METHOD_HANDLE");
+                    // TODO
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldHandle:
+                    builder.Append("FIELD_HANDLE");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry:
+                    ParseMethod(builder);
+                    builder.Append(" (METHOD_ENTRY)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_DefToken:
+                    ParseMethodDefToken(builder);
+                    builder.Append(" (METHOD_ENTRY_DEF_TOKEN)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_RefToken:
+                    ParseMethodRefToken(builder);
+                    builder.Append(" (METHOD_ENTRY_REF_TOKEN)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry:
+                    ParseMethod(builder);
+                    builder.Append(" (VIRTUAL_ENTRY)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_DefToken:
+                    ParseMethodDefToken(builder);
+                    builder.Append(" (VIRTUAL_ENTRY_DEF_TOKEN)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_RefToken:
+                    ParseMethodRefToken(builder);
+                    builder.Append(" (VIRTUAL_ENTRY_REF_TOKEN)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_Slot:
+                    builder.Append("VIRTUAL_ENTRY_SLOT");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_Helper:
+                    ParseHelper(builder);
+                    builder.Append(" (HELPER)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_StringHandle:
+                    ParseStringHandle(builder);
+                    builder.Append(" (STRING_HANDLE)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_NewObject:
+                    ParseType(builder);
+                    builder.Append(" (NEW_OBJECT)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_NewArray:
+                    ParseType(builder);
+                    builder.Append(" (NEW_ARRAY)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_IsInstanceOf:
+                    ParseType(builder);
+                    builder.Append(" (IS_INSTANCE_OF)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_ChkCast:
+                    ParseType(builder);
+                    builder.Append(" (CHK_CAST)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldAddress:
+                    builder.Append("FIELD_ADDRESS");
+                    // TODO
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_CctorTrigger:
+                    builder.Append("CCTOR_TRIGGER");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_StaticBaseNonGC:
+                    ParseType(builder);
+                    builder.Append(" (STATIC_BASE_NON_GC)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_StaticBaseGC:
+                    ParseType(builder);
+                    builder.Append(" (STATIC_BASE_GC)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_ThreadStaticBaseNonGC:
+                    ParseType(builder);
+                    builder.Append(" (THREAD_STATIC_BASE_NON_GC)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_ThreadStaticBaseGC:
+                    ParseType(builder);
+                    builder.Append(" (THREAD_STATIC_BASE_GC)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldBaseOffset:
+                    builder.Append("FIELD_BASE_OFFSET");
+                    // TODO
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldOffset:
+                    builder.Append("FIELD_OFFSET");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_TypeDictionary:
+                    ParseType(builder);
+                    builder.Append(" (TYPE_DICTIONARY)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionary:
+                    ParseMethod(builder);
+                    builder.Append(" (METHOD_DICTIONARY)");
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_Check_TypeLayout:
+                    ParseType(builder);
+                    builder.Append(" (CHECK_TYPE_LAYOUT)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_Check_FieldOffset:
+                    builder.Append("CHECK_FIELD_OFFSET");
+                    // TODO
+                    break;
+
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_DelegateCtor:
+                    ParseMethod(builder);
+                    builder.Append(" => ");
+                    ParseType(builder);
+                    builder.Append(" (DELEGATE_CTOR)");
+                    break;
+
+                case ReadyToRunFixupKind.READYTORUN_FIXUP_DeclaringTypeHandle:
+                    ParseType(builder);
+                    builder.Append(" (DECLARING_TYPE_HANDLE)");
+                    break;
+
+
+                default:
+                    builder.Append(string.Format("Unknown fixup type: {0:X2}", fixupType));
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Decode a type from the signature stream.
+        /// </summary>
+        /// <param name="builder"></param>
+        private void ParseType(StringBuilder builder)
+        {
+            CorElementType corElemType = ReadElementType();
+            switch (corElemType)
+            {
+                case CorElementType.ELEMENT_TYPE_VOID:
+                    builder.Append("void");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                    builder.Append("bool");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_CHAR:
+                    builder.Append("char");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_I1:
+                    builder.Append("sbyte");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_U1:
+                    builder.Append("byte");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_I2:
+                    builder.Append("short");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_U2:
+                    builder.Append("ushort");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_I4:
+                    builder.Append("int");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_U4:
+                    builder.Append("uint");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_I8:
+                    builder.Append("long");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_U8:
+                    builder.Append("ulong");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_R4:
+                    builder.Append("float");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_R8:
+                    builder.Append("double");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_STRING:
+                    builder.Append("string");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_PTR:
+                    builder.Append("ptr");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_BYREF:
+                    builder.Append("byref");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_VALUETYPE:
+                case CorElementType.ELEMENT_TYPE_CLASS:
+                    ParseTypeToken(builder);
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_VAR:
+                    builder.Append("var");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_ARRAY:
+                    builder.Append("array");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_GENERICINST:
+                    ParseGenericTypeInstance(builder);
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_TYPEDBYREF:
+                    builder.Append("typedbyref");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_I:
+                    builder.Append("IntPtr");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_U:
+                    builder.Append("UIntPtr");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_FNPTR:
+                    builder.Append("fnptr");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_OBJECT:
+                    builder.Append("object");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_SZARRAY:
+                    builder.Append("szarray");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_MVAR:
+                    builder.Append("mvar");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_CMOD_REQD:
+                    builder.Append("cmod_reqd");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_CMOD_OPT:
+                    builder.Append("cmod_opt");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_HANDLE:
+                    builder.Append("handle");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_SENTINEL:
+                    builder.Append("sentinel");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_PINNED:
+                    builder.Append("pinned");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_VAR_ZAPSIG:
+                    builder.Append("var_zapsig");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
+                    builder.Append("native_array_template_zapsig");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+                    builder.Append("native_valuetype_zapsig");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_CANON_ZAPSIG:
+                    builder.Append("canon_zapsig");
+                    break;
+
+                case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG:
+                    builder.Append("module_zapsig");
+                    break;
+
+                default:
+                    throw new NotImplementedException();
+            }
+        }
+        private void ParseGenericTypeInstance(StringBuilder builder)
+        {
+            ParseType(builder);
+            uint typeArgCount = ReadUInt();
+            builder.Append("<");
+            for (uint paramIndex = 0; paramIndex < typeArgCount; paramIndex++)
+            {
+                if (paramIndex > 0)
+                {
+                    builder.Append(", ");
+                }
+                ParseType(builder);
+            }
+            builder.Append(">");
+        }
+
+        private void ParseTypeToken(StringBuilder builder)
+        {
+            uint token = ReadToken();
+            builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)token)));
+        }
+
+        /// <summary>
+        /// Parse an arbitrary method signature.
+        /// </summary>
+        /// <param name="builder">Output string builder to receive the textual signature representation</param>
+        private void ParseMethod(StringBuilder builder)
+        {
+            uint methodFlags = ReadUInt();
+            if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
+            {
+                ParseType(builder);
+            }
+            if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0)
+            {
+                throw new NotImplementedException();
+            }
+            if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0)
+            {
+                ParseMethodRefToken(builder);
+            }
+            else
+            {
+                ParseMethodDefToken(builder);
+            }
+
+            if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
+            {
+                uint typeArgCount = ReadUInt();
+                builder.Append("<");
+                for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
+                {
+                    if (typeArgIndex != 0)
+                    {
+                        builder.Append(", ");
+                    }
+                    ParseType(builder);
+                }
+                builder.Append(">");
+            }
+
+            if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
+            {
+                builder.Append(" @ ");
+                ParseType(builder);
+            }
+        }
+
+        /// <summary>
+        /// Read a methodDef token from the signature and output the corresponding object to the builder.
+        /// </summary>
+        /// <param name="builder">Output string builder</param>
+        private void ParseMethodDefToken(StringBuilder builder)
+        {
+            uint methodDefToken = ReadUInt() | (uint)CorTokenType.mdtMethodDef;
+            builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodDefToken)));
+        }
+
+        /// <summary>
+        /// Read a memberRef token from the signature and output the corresponding object to the builder.
+        /// </summary>
+        /// <param name="builder">Output string builder</param>
+        private void ParseMethodRefToken(StringBuilder builder)
+        {
+            uint methodRefToken = ReadUInt() | (uint)CorTokenType.mdtMemberRef;
+            builder.Append(MetadataNameFormatter.FormatHandle(_metadataReader, MetadataTokens.Handle((int)methodRefToken)));
+        }
+
+        /// <summary>
+        /// Read R2R helper signature.
+        /// </summary>
+        /// <returns></returns>
+        private void ParseHelper(StringBuilder builder)
+        {
+            uint helperType = ReadUInt();
+            if ((helperType & (uint)ReadyToRunHelper.READYTORUN_HELPER_FLAG_VSD) != 0)
+            {
+                builder.Append("VSD_");
+            }
+
+            switch ((ReadyToRunHelper)(helperType & ~(uint)ReadyToRunHelper.READYTORUN_HELPER_FLAG_VSD))
+            {
+                case ReadyToRunHelper.READYTORUN_HELPER_Invalid:
+                    builder.Append("INVALID");
+                    break;
+
+                // Not a real helper - handle to current module passed to delay load helpers.
+                case ReadyToRunHelper.READYTORUN_HELPER_Module:
+                    builder.Append("MODULE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GSCookie:
+                    builder.Append("GC_COOKIE");
+                    break;
+
+
+                //
+                // Delay load helpers
+                //
+
+                // All delay load helpers use custom calling convention:
+                // - scratch register - address of indirection cell. 0 = address is inferred from callsite.
+                // - stack - section index, module handle
+                case ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_MethodCall:
+                    builder.Append("DELAYLOAD_METHODCALL");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper:
+                    builder.Append("DELAYLOAD_HELPER");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper_Obj:
+                    builder.Append("DELAYLOAD_HELPER_OBJ");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper_ObjObj:
+                    builder.Append("DELAYLOAD_HELPER_OBJ_OBJ");
+                    break;
+
+                // JIT helpers
+
+                // Exception handling helpers
+                case ReadyToRunHelper.READYTORUN_HELPER_Throw:
+                    builder.Append("THROW");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Rethrow:
+                    builder.Append("RETHROW");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Overflow:
+                    builder.Append("OVERFLOW");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_RngChkFail:
+                    builder.Append("RNG_CHK_FAIL");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_FailFast:
+                    builder.Append("FAIL_FAST");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ThrowNullRef:
+                    builder.Append("THROW_NULL_REF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ThrowDivZero:
+                    builder.Append("THROW_DIV_ZERO");
+                    break;
+
+                // Write barriers
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier:
+                    builder.Append("WRITE_BARRIER");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier:
+                    builder.Append("CHECKED_WRITE_BARRIER");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ByRefWriteBarrier:
+                    builder.Append("BYREF_WRITE_BARRIER");
+                    break;
+
+                // Array helpers
+                case ReadyToRunHelper.READYTORUN_HELPER_Stelem_Ref:
+                    builder.Append("STELEM_REF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Ldelema_Ref:
+                    builder.Append("LDELEMA_REF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_MemSet:
+                    builder.Append("MEM_SET");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_MemCpy:
+                    builder.Append("MEM_CPY");
+                    break;
+
+                // Get string handle lazily
+                case ReadyToRunHelper.READYTORUN_HELPER_GetString:
+                    builder.Append("GET_STRING");
+                    break;
+
+                // Used by /Tuning for Profile optimizations
+                case ReadyToRunHelper.READYTORUN_HELPER_LogMethodEnter:
+                    builder.Append("LOG_METHOD_ENTER");
+                    break;
+
+                // Reflection helpers
+                case ReadyToRunHelper.READYTORUN_HELPER_GetRuntimeTypeHandle:
+                    builder.Append("GET_RUNTIME_TYPE_HANDLE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GetRuntimeMethodHandle:
+                    builder.Append("GET_RUNTIME_METHOD_HANDLE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GetRuntimeFieldHandle:
+                    builder.Append("GET_RUNTIME_FIELD_HANDLE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Box:
+                    builder.Append("BOX");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Box_Nullable:
+                    builder.Append("BOX_NULLABLE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Unbox:
+                    builder.Append("UNBOX");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Unbox_Nullable:
+                    builder.Append("UNBOX_NULLABLE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_NewMultiDimArr:
+                    builder.Append("NEW_MULTI_DIM_ARR");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_NewMultiDimArr_NonVarArg:
+                    builder.Append("NEW_MULTI_DIM_ARR__NON_VAR_ARG");
+                    break;
+
+                // Helpers used with generic handle lookup cases
+                case ReadyToRunHelper.READYTORUN_HELPER_NewObject:
+                    builder.Append("NEW_OBJECT");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_NewArray:
+                    builder.Append("NEW_ARRAY");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckCastAny:
+                    builder.Append("CHECK_CAST_ANY");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckInstanceAny:
+                    builder.Append("CHECK_INSTANCE_ANY");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GenericGcStaticBase:
+                    builder.Append("GENERIC_GC_STATIC_BASE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GenericNonGcStaticBase:
+                    builder.Append("GENERIC_NON_GC_STATIC_BASE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GenericGcTlsBase:
+                    builder.Append("GENERIC_GC_TLS_BASE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_GenericNonGcTlsBase:
+                    builder.Append("GENERIC_NON_GC_TLS_BASE");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_VirtualFuncPtr:
+                    builder.Append("VIRTUAL_FUNC_PTR");
+                    break;
+
+                // Long mul/div/shift ops
+                case ReadyToRunHelper.READYTORUN_HELPER_LMul:
+                    builder.Append("LMUL");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LMulOfv:
+                    builder.Append("LMUL_OFV");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ULMulOvf:
+                    builder.Append("ULMUL_OVF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LDiv:
+                    builder.Append("LDIV");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LMod:
+                    builder.Append("LMOD");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ULDiv:
+                    builder.Append("ULDIV");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ULMod:
+                    builder.Append("ULMOD");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LLsh:
+                    builder.Append("LLSH");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LRsh:
+                    builder.Append("LRSH");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_LRsz:
+                    builder.Append("LRSZ");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Lng2Dbl:
+                    builder.Append("LNG2DBL");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_ULng2Dbl:
+                    builder.Append("ULNG2DBL");
+                    break;
+
+                // 32-bit division helpers
+                case ReadyToRunHelper.READYTORUN_HELPER_Div:
+                    builder.Append("DIV");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Mod:
+                    builder.Append("MOD");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_UDiv:
+                    builder.Append("UDIV");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_UMod:
+                    builder.Append("UMOD");
+                    break;
+
+                // Floating point conversions
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2Int:
+                    builder.Append("DBL2INT");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2IntOvf:
+                    builder.Append("DBL2INTOVF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2Lng:
+                    builder.Append("DBL2LNG");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2LngOvf:
+                    builder.Append("DBL2LNGOVF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2UInt:
+                    builder.Append("DBL2UINT");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2UIntOvf:
+                    builder.Append("DBL2UINTOVF");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2ULng:
+                    builder.Append("DBL2ULNG");
+                    break;
+
+                case ReadyToRunHelper.READYTORUN_HELPER_Dbl2ULngOvf:
+                    builder.Append("DBL2ULNGOVF");
+                    break;
+
+                // Floating point ops
+                case ReadyToRunHelper.READYTORUN_HELPER_DblRem:
+                    builder.Append("DBL_REM");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_FltRem:
+                    builder.Append("FLT_REM");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_DblRound:
+                    builder.Append("DBL_ROUND");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_FltRound:
+                    builder.Append("FLT_ROUND");
+                    break;
+
+                // Personality rountines
+                case ReadyToRunHelper.READYTORUN_HELPER_PersonalityRoutine:
+                    builder.Append("PERSONALITY_ROUTINE");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_PersonalityRoutineFilterFunclet:
+                    builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET");
+                    break;
+
+                //
+                // Deprecated/legacy
+                //
+
+                // JIT32 x86-specific write barriers
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_EAX:
+                    builder.Append("WRITE_BARRIER_EAX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_EBX:
+                    builder.Append("WRITE_BARRIER_EBX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_ECX:
+                    builder.Append("WRITE_BARRIER_ECX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_ESI:
+                    builder.Append("WRITE_BARRIER_ESI");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_EDI:
+                    builder.Append("WRITE_BARRIER_EDI");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_WriteBarrier_EBP:
+                    builder.Append("WRITE_BARRIER_EBP");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_EAX:
+                    builder.Append("CHECKED_WRITE_BARRIER_EAX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_EBX:
+                    builder.Append("CHECKED_WRITE_BARRIER_EBX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_ECX:
+                    builder.Append("CHECKED_WRITE_BARRIER_ECX");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_ESI:
+                    builder.Append("CHECKED_WRITE_BARRIER_ESI");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_EDI:
+                    builder.Append("CHECKED_WRITE_BARRIER_EDI");
+                    break;
+                case ReadyToRunHelper.READYTORUN_HELPER_CheckedWriteBarrier_EBP:
+                    builder.Append("CHECKED_WRITE_BARRIER_EBP");
+                    break;
+
+                // JIT32 x86-specific exception handling
+                case ReadyToRunHelper.READYTORUN_HELPER_EndCatch:
+                    builder.Append("END_CATCH");
+                    break;
+
+                default:
+                    builder.Append(string.Format("Unknown helper: {0:X2}", helperType));
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Read a string token from the signature stream and convert it to the actual string.
+        /// </summary>
+        /// <returns></returns>
+        private void ParseStringHandle(StringBuilder builder)
+        {
+            uint rid = ReadUInt();
+            UserStringHandle stringHandle = MetadataTokens.UserStringHandle((int)rid);
+            builder.Append(_metadataReader.GetUserString(stringHandle));
+        }
+    }
+}
index e7b249e..2b95b21 100644 (file)
@@ -186,10 +186,10 @@ namespace R2RDump
                     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}");
+                        _writer.Write($"                                {code.UnwindOp} {code.OpInfoStr}");
                         if (code.NextFrameOffset != -1)
                         {
-                            _writer.WriteLine($" - {code.NextFrameOffset}");
+                            _writer.WriteLine($"                                {code.NextFrameOffset}");
                         }
                         _writer.WriteLine();
                     }
@@ -199,7 +199,7 @@ namespace R2RDump
                 {
                     foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                     {
-                        _writer.WriteLine($"\t\t\t\t{transition.ToString()}");
+                        _writer.WriteLine($"                                {transition.ToString()}");
                     }
                 }