R2RDump - Unwind Info (#18248)
authorAmy <amycmyu@gmail.com>
Wed, 6 Jun 2018 21:19:56 +0000 (14:19 -0700)
committerZach Montoya <zamont@microsoft.com>
Wed, 6 Jun 2018 21:19:56 +0000 (14:19 -0700)
* Extract unwind info from image

* Use typeDef.Namespace to get full namespace, move to separate function

* Avoid passing R2RReader to R2RMethod and RuntimeFunction constructors

* Dump unwind info

* Changes to unwindInfo output format

src/tools/r2rdump/R2RDump.cs
src/tools/r2rdump/R2RMethod.cs
src/tools/r2rdump/R2RReader.cs
src/tools/r2rdump/UnwindInfo.cs [new file with mode: 0644]

index ea078e0..b287fa0 100644 (file)
@@ -24,6 +24,7 @@ namespace R2RDump
         private bool _diff = false;
         private long _disassembler;
         private bool _types = false;
+        private bool _unwind = false;
         private TextWriter _writer;
 
         private R2RDump()
@@ -49,6 +50,7 @@ namespace R2RDump
                 syntax.DefineOptionList("r|runtimefunction", ref _runtimeFunctions, ArgStringToInt, "Get one runtime function by id or relative virtual address");
                 syntax.DefineOptionList("s|section", ref _sections, "Get section by keyword");
                 syntax.DefineOption("types", ref _types, "Dump available types");
+                syntax.DefineOption("unwind", ref _unwind, "Dump unwindInfo");
                 syntax.DefineOption("diff", ref _diff, "Compare two R2R images (not yet implemented)"); // not yet implemented
             });
 
@@ -108,6 +110,7 @@ namespace R2RDump
             {
                 DumpBytes(r2r, r2r.R2RHeader.RelativeVirtualAddress, (uint)r2r.R2RHeader.Size);
             }
+            _writer.WriteLine();
             if (dumpSections)
             {
                 WriteDivider("R2R Sections");
@@ -118,6 +121,7 @@ namespace R2RDump
                     DumpSection(r2r, section);
                 }
             }
+            _writer.WriteLine();
         }
 
         /// <summary>
@@ -165,6 +169,11 @@ namespace R2RDump
             {
                 DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size);
             }
+            if (_unwind)
+            {
+                _writer.WriteLine("UnwindInfo:");
+                _writer.Write(rtf.UnwindInfo);
+            }
             _writer.WriteLine();
         }
 
index 95cf892..ea439ff 100644 (file)
@@ -43,7 +43,9 @@ namespace R2RDump
         /// </summary>
         public R2RMethod Method { get; }
 
-        public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method)
+        public UnwindInfo UnwindInfo { get; }
+
+        public RuntimeFunction(int id, int startRva, int endRva, int unwindRva, R2RMethod method, UnwindInfo unwindInfo)
         {
             Id = id;
             StartAddress = startRva;
@@ -52,6 +54,7 @@ namespace R2RDump
                 Size = -1;
             UnwindRVA = unwindRva;
             Method = method;
+            UnwindInfo = unwindInfo;
         }
 
         public override string ToString()
@@ -132,7 +135,7 @@ namespace R2RDump
         /// <summary>
         /// Maps all the generic parameters to the type in the instance
         /// </summary>
-        Dictionary<string, string> _genericParamInstanceMap;
+        private Dictionary<string, string> _genericParamInstanceMap;
 
         [Flags]
         public enum EncodeMethodSigFlags
@@ -172,7 +175,7 @@ namespace R2RDump
         /// <summary>
         /// Extracts the method signature from the metadata by rid
         /// </summary>
-        public R2RMethod(byte[] image, MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok)
+        public R2RMethod(MetadataReader mdReader, uint rid, int entryPointId, GenericElementTypes[] instanceArgs, uint[] tok)
         {
             Token = _mdtMethodDef | rid;
             Rid = rid;
@@ -188,22 +191,7 @@ namespace R2RDump
             BlobReader signatureReader = mdReader.GetBlobReader(_methodDef.Signature);
 
             TypeDefinitionHandle declaringTypeHandle = _methodDef.GetDeclaringType();
-            TypeDefinition declaringTypeDef;
-            do
-            {
-                declaringTypeDef = mdReader.GetTypeDefinition(declaringTypeHandle);
-                DeclaringType = mdReader.GetString(declaringTypeDef.Name) + "." + DeclaringType;
-                declaringTypeHandle = declaringTypeDef.GetDeclaringType();
-            }
-            while (!declaringTypeHandle.IsNil);
-
-            NamespaceDefinitionHandle namespaceHandle = declaringTypeDef.NamespaceDefinition;
-            while (!namespaceHandle.IsNil)
-            {
-                NamespaceDefinition namespaceDef = mdReader.GetNamespaceDefinition(namespaceHandle);
-                DeclaringType = mdReader.GetString(namespaceDef.Name) + "." + DeclaringType;
-                namespaceHandle = namespaceDef.Parent;
-            }
+            DeclaringType = R2RReader.GetTypeDefFullName(mdReader, declaringTypeHandle);
 
             SignatureHeader signatureHeader = signatureReader.ReadSignatureHeader();
             IsGeneric = signatureHeader.IsGeneric;
@@ -253,7 +241,7 @@ namespace R2RDump
         {
             StringBuilder sb = new StringBuilder();
 
-            sb.AppendFormat($"{DeclaringType}{Name}");
+            sb.AppendFormat($"{DeclaringType}.{Name}");
 
             if (IsGeneric)
             {
index 5bad29f..4c1a5ad 100644 (file)
@@ -140,7 +140,7 @@ namespace R2RDump
                 int offset = 0;
                 if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
                 {
-                    R2RMethod method = new R2RMethod(Image, _mdReader, rid, GetEntryPointIdFromOffset(offset), null, null);
+                    R2RMethod method = new R2RMethod(_mdReader, rid, GetEntryPointIdFromOffset(offset), null, null);
 
                     if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length)
                     {
@@ -184,7 +184,7 @@ namespace R2RDump
 
                     uint id = curParser.GetUnsigned();
                     id = id >> 1;
-                    R2RMethod method = new R2RMethod(Image, _mdReader, rid, (int)id, args, tokens);
+                    R2RMethod method = new R2RMethod(_mdReader, rid, (int)id, args, tokens);
                     if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length)
                     {
                         isEntryPoint[method.EntryPointRuntimeFunctionId] = true;
@@ -216,8 +216,9 @@ namespace R2RDump
                         endRva = NativeReader.ReadInt32(Image, ref curOffset);
                     }
                     int unwindRva = NativeReader.ReadInt32(Image, ref curOffset);
+                    int unwindOffset = GetOffset(unwindRva);
 
-                    method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method));
+                    method.RuntimeFunctions.Add(new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, method, new UnwindInfo(Image, unwindOffset)));
                     runtimeFunctionId++;
                 }
                 while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]);
@@ -237,14 +238,7 @@ namespace R2RDump
                 uint rid = curParser.GetUnsigned();
                 rid = rid >> 1;
                 TypeDefinitionHandle typeDefHandle = MetadataTokens.TypeDefinitionHandle((int)rid);
-
-                TypeDefinition typeDef = _mdReader.GetTypeDefinition(typeDefHandle);
-                string name = _mdReader.GetString(typeDef.Name);
-                if (!typeDef.Namespace.IsNil)
-                {
-                    name = _mdReader.GetString(typeDef.Namespace) + "." + name;
-                }
-                AvailableTypes.Add(name);
+                AvailableTypes.Add(GetTypeDefFullName(_mdReader, typeDefHandle));
                 curParser = allEntriesEnum.GetNext();
             }
         }
@@ -261,6 +255,24 @@ namespace R2RDump
         }
 
         /// <summary>
+        /// Get the full name of a type, including parent classes and namespace
+        /// </summary>
+        public static string GetTypeDefFullName(MetadataReader mdReader, TypeDefinitionHandle handle)
+        {
+            TypeDefinition typeDef;
+            string typeStr = "";
+            do
+            {
+                typeDef = mdReader.GetTypeDefinition(handle);
+                typeStr = "." + mdReader.GetString(typeDef.Name) + typeStr;
+                handle = typeDef.GetDeclaringType();
+            }
+            while (!handle.IsNil);
+
+            return mdReader.GetString(typeDef.Namespace) + typeStr;
+        }
+
+        /// <summary>
         /// Reads the method entrypoint from the offset. Used for non-generic methods
         /// </summary>
         private int GetEntryPointIdFromOffset(int offset)
diff --git a/src/tools/r2rdump/UnwindInfo.cs b/src/tools/r2rdump/UnwindInfo.cs
new file mode 100644 (file)
index 0000000..ae7952e
--- /dev/null
@@ -0,0 +1,117 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+
+namespace R2RDump
+{
+    struct UnwindCode
+    {
+        public byte CodeOffset { get; }
+        public byte UnwindOp { get; } //4 bits
+        public byte OpInfo { get; } //4 bits
+
+        public byte OffsetLow { get; }
+        public byte OffsetHigh { get; } //4 bits
+
+        public ushort FrameOffset { get; }
+
+        public UnwindCode(byte[] image, ref int offset)
+        {
+            int off = offset;
+            CodeOffset = NativeReader.ReadByte(image, ref off);
+            byte op = NativeReader.ReadByte(image, ref off);
+            UnwindOp = (byte)(op & 15);
+            OpInfo = (byte)(op >> 4);
+
+            OffsetLow = CodeOffset;
+            OffsetHigh = OpInfo;
+
+            FrameOffset = NativeReader.ReadUInt16(image, ref offset);
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            string tab2 = new string(' ', 8);
+            string tab3 = new string(' ', 12);
+
+            sb.AppendLine($"{tab2}{{");
+            sb.AppendLine($"{tab3}CodeOffset: {CodeOffset}");
+            sb.AppendLine($"{tab3}UnwindOp: {UnwindOp}");
+            sb.AppendLine($"{tab3}OpInfo: {OpInfo}");
+            sb.AppendLine($"{tab2}}}");
+            sb.AppendLine($"{tab2}{{");
+            sb.AppendLine($"{tab3}OffsetLow: {OffsetLow}");
+            sb.AppendLine($"{tab3}UnwindOp: {UnwindOp}");
+            sb.AppendLine($"{tab3}OffsetHigh: {OffsetHigh}");
+            sb.AppendLine($"{tab2}}}");
+            sb.AppendLine($"{tab2}FrameOffset: {FrameOffset}");
+            sb.AppendLine($"{tab2}------------------");
+
+            return sb.ToString();
+        }
+    }
+
+    struct UnwindInfo
+    {
+        private const int _sizeofUnwindCode = 2;
+        private const int _offsetofUnwindCode = 4;
+
+        public byte Version { get; } //3 bits
+        public byte Flags { get; } //5 bits
+        public byte SizeOfProlog { get; }
+        public byte CountOfUnwindCodes { get; }
+        public byte FrameRegister { get; } //4 bits
+        public byte FrameOffset { get; } //4 bits
+        public UnwindCode[] UnwindCode { get; }
+        public uint PersonalityRoutineRVA { get; }
+        public int Size { get; }
+
+        public UnwindInfo(byte[] image, int offset)
+        {
+            byte versionAndFlags = NativeReader.ReadByte(image, ref offset);
+            Version = (byte)(versionAndFlags & 7);
+            Flags = (byte)(versionAndFlags >> 3);
+            SizeOfProlog = NativeReader.ReadByte(image, ref offset);
+            CountOfUnwindCodes = NativeReader.ReadByte(image, ref offset);
+            byte frameRegisterAndOffset = NativeReader.ReadByte(image, ref offset);
+            FrameRegister = (byte)(frameRegisterAndOffset & 15);
+            FrameOffset = (byte)(frameRegisterAndOffset >> 4);
+
+            UnwindCode = new UnwindCode[CountOfUnwindCodes];
+            for (int i = 0; i < CountOfUnwindCodes; i++)
+            {
+                UnwindCode[i] = new UnwindCode(image, ref offset);
+            }
+
+            PersonalityRoutineRVA = NativeReader.ReadUInt32(image, ref offset);
+
+            Size = _offsetofUnwindCode + CountOfUnwindCodes * _sizeofUnwindCode + sizeof(uint);
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            string tab = "    ";
+
+            sb.AppendLine($"{tab}Version: {Version}");
+            sb.AppendLine($"{tab}Flags: 0x{Flags:X8}");
+            sb.AppendLine($"{tab}SizeOfProlog: {SizeOfProlog}");
+            sb.AppendLine($"{tab}CountOfUnwindCodes: {CountOfUnwindCodes}");
+            sb.AppendLine($"{tab}FrameRegister: {FrameRegister}");
+            sb.AppendLine($"{tab}FrameOffset: {FrameOffset}");
+            sb.AppendLine($"{tab}Unwind Codes:");
+            sb.AppendLine($"{tab}{tab}------------------");
+            for (int i = 0; i < CountOfUnwindCodes; i++)
+            {
+                sb.Append(UnwindCode[i].ToString());
+            }
+            sb.AppendLine($"{tab}PersonalityRoutineRVA: 0x{PersonalityRoutineRVA:X8}");
+            sb.AppendLine($"{tab}Size: {Size}");
+
+            return sb.ToString();
+        }
+    }
+}