Improvements for naked R2RDump output (#21317)
authorTomáš Rylek <trylek@microsoft.com>
Mon, 3 Dec 2018 19:33:49 +0000 (20:33 +0100)
committerGitHub <noreply@github.com>
Mon, 3 Dec 2018 19:33:49 +0000 (20:33 +0100)
The initial impulse for this change was removing native offsets
from debug info that caused noise in naked R2R comparisons. Fixing
this required propagation of DumpOptions to some more call sites
prompting me to unify dumping logic to always use TextWriter
as the output medium. I have also fixed some minor bugs I noticed
around the naked disassembly.

Thanks

Tomas

src/tools/r2rdump/CoreDisTools.cs
src/tools/r2rdump/DebugInfo.cs
src/tools/r2rdump/EHInfo.cs
src/tools/r2rdump/R2RMethod.cs
src/tools/r2rdump/TextDumper.cs

index 19117f7..d6a82f1 100644 (file)
@@ -157,11 +157,13 @@ namespace R2RDump
                         {
                             colon += 3;
                         }
-                        nakedInstruction.AppendLine(new string(' ', 32) + line.Substring(colon).TrimStart());
+                        nakedInstruction.Append(new string(' ', 32) + line.Substring(colon).TrimStart());
+                        nakedInstruction.Append('\n');
                     }
                     else
                     {
-                        nakedInstruction.AppendLine(line);
+                        nakedInstruction.Append(line);
+                        nakedInstruction.Append('\n');
                     }
                 }
                 instruction = nakedInstruction.ToString();
@@ -306,11 +308,6 @@ namespace R2RDump
         /// <param name="instruction">Textual representation of the instruction</param>
         private void ProbeCommonIntelQuirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
         {
-            if (_options.Naked)
-            {
-                // Don't relocate relative offsets in naked mode
-                return;
-            }
             if (instrSize == 2 && IsIntelJumpInstructionWithByteOffset(imageOffset + rtfOffset))
             {
                 sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
index eacc848..f4d8f5c 100644 (file)
@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Reflection.PortableExecutable;
 using System.Text;
 
@@ -52,76 +53,77 @@ namespace R2RDump
             }
         }
 
-        public override string ToString()
+        public void WriteTo(TextWriter writer, DumpOptions dumpOptions)
         {
-            StringBuilder sb = new StringBuilder();
-
             if (_boundsList.Count > 0)
-                sb.AppendLine("Debug Info");
+                writer.WriteLine("Debug Info");
 
-            sb.AppendLine("\tBounds:");
+            writer.WriteLine("\tBounds:");
             for (int i = 0; i < _boundsList.Count; ++i)
             {
-                sb.AppendLine($"\tNative Offset: 0x{_boundsList[i].NativeOffset:X}, IL Offset: 0x{_boundsList[i].ILOffset:X}, Source Types: {_boundsList[i].SourceTypes}");
+                writer.Write('\t');
+                if (!dumpOptions.Naked)
+                {
+                    writer.Write($"Native Offset: 0x{_boundsList[i].NativeOffset:X}, ");
+                }
+                writer.WriteLine($"IL Offset: 0x{_boundsList[i].ILOffset:X}, Source Types: {_boundsList[i].SourceTypes}");
             }
 
-            sb.AppendLine("");
+            writer.WriteLine("");
 
             if (_variablesList.Count > 0)
-                sb.AppendLine("\tVariable Locations:");
+                writer.WriteLine("\tVariable Locations:");
 
             for (int i = 0; i < _variablesList.Count; ++i)
             {
                 var varLoc = _variablesList[i];
-                sb.AppendLine($"\tVariable Number: {varLoc.VariableNumber}");
-                sb.AppendLine($"\tStart Offset: 0x{varLoc.StartOffset:X}");
-                sb.AppendLine($"\tEnd Offset: 0x{varLoc.EndOffset:X}");
-                sb.AppendLine($"\tLoc Type: {varLoc.VariableLocation.VarLocType}");
+                writer.WriteLine($"\tVariable Number: {varLoc.VariableNumber}");
+                writer.WriteLine($"\tStart Offset: 0x{varLoc.StartOffset:X}");
+                writer.WriteLine($"\tEnd Offset: 0x{varLoc.EndOffset:X}");
+                writer.WriteLine($"\tLoc Type: {varLoc.VariableLocation.VarLocType}");
 
                 switch (varLoc.VariableLocation.VarLocType)
                 {
                     case VarLocType.VLT_REG:
                     case VarLocType.VLT_REG_FP:
                     case VarLocType.VLT_REG_BYREF:
-                        sb.AppendLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
                         break;
                     case VarLocType.VLT_STK:
                     case VarLocType.VLT_STK_BYREF:
-                        sb.AppendLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
-                        sb.AppendLine($"\tStack Offset: {varLoc.VariableLocation.Data2}");
+                        writer.WriteLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tStack Offset: {varLoc.VariableLocation.Data2}");
                         break;
                     case VarLocType.VLT_REG_REG:
-                        sb.AppendLine($"\tRegister 1: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
-                        sb.AppendLine($"\tRegister 2: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
+                        writer.WriteLine($"\tRegister 1: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tRegister 2: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
                         break;
                     case VarLocType.VLT_REG_STK:
-                        sb.AppendLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
-                        sb.AppendLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
-                        sb.AppendLine($"\tStack Offset: {varLoc.VariableLocation.Data3}");
+                        writer.WriteLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
+                        writer.WriteLine($"\tStack Offset: {varLoc.VariableLocation.Data3}");
                         break;
                     case VarLocType.VLT_STK_REG:
-                        sb.AppendLine($"\tStack Offset: {varLoc.VariableLocation.Data1}");
-                        sb.AppendLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
-                        sb.AppendLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data3)}");                        
+                        writer.WriteLine($"\tStack Offset: {varLoc.VariableLocation.Data1}");
+                        writer.WriteLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data2)}");
+                        writer.WriteLine($"\tRegister: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data3)}");                        
                         break;
                     case VarLocType.VLT_STK2:
-                        sb.AppendLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
-                        sb.AppendLine($"\tStack Offset: {varLoc.VariableLocation.Data2}");
+                        writer.WriteLine($"\tBase Register: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tStack Offset: {varLoc.VariableLocation.Data2}");
                         break;
                     case VarLocType.VLT_FPSTK:
-                        sb.AppendLine($"\tOffset: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tOffset: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
                         break;
                     case VarLocType.VLT_FIXED_VA:
-                        sb.AppendLine($"\tOffset: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
+                        writer.WriteLine($"\tOffset: {GetPlatformSpecificRegister(_machine, varLoc.VariableLocation.Data1)}");
                         break;
                     default:
                         throw new BadImageFormatException("Unexpected var loc type");
                 }
 
-                sb.AppendLine("");
+                writer.WriteLine("");
             }
-
-            return sb.ToString();
         }
 
         /// <summary>
index 818b343..4d1ce25 100644 (file)
@@ -100,35 +100,35 @@ namespace R2RDump
         }
 
         /// <summary>
-        /// Emit a textual representation of the EH info to a given string builder.
+        /// Emit a textual representation of the EH info to a given text writer.
         /// </summary>
-        /// <param name="stringBuilder">Output builder for the textual representation</param>
+        /// <param name="writer">Output writer for the textual representation</param>
         /// <param name="methodRva">Starting RVA of the runtime function is used to display the try / handler info as RVA intervals</param>
-        public void WriteTo(StringBuilder stringBuilder, int methodRva)
+        public void WriteTo(TextWriter writer, int methodRva)
         {
-            stringBuilder.Append($@"Flags {(uint)Flags:X2} ");
-            stringBuilder.Append($@"TryOff {TryOffset:X4} (RVA {(TryOffset + methodRva):X4}) ");
-            stringBuilder.Append($@"TryEnd {TryEnd:X4} (RVA {(TryEnd + methodRva):X4}) ");
-            stringBuilder.Append($@"HndOff {HandlerOffset:X4} (RVA {(HandlerOffset + methodRva):X4}) ");
-            stringBuilder.Append($@"HndEnd {HandlerEnd:X4} (RVA {(HandlerEnd + methodRva):X4}) ");
-            stringBuilder.Append($@"ClsFlt {ClassTokenOrFilterOffset:X4}");
+            writer.Write($@"Flags {(uint)Flags:X2} ");
+            writer.Write($@"TryOff {TryOffset:X4} (RVA {(TryOffset + methodRva):X4}) ");
+            writer.Write($@"TryEnd {TryEnd:X4} (RVA {(TryEnd + methodRva):X4}) ");
+            writer.Write($@"HndOff {HandlerOffset:X4} (RVA {(HandlerOffset + methodRva):X4}) ");
+            writer.Write($@"HndEnd {HandlerEnd:X4} (RVA {(HandlerEnd + methodRva):X4}) ");
+            writer.Write($@"ClsFlt {ClassTokenOrFilterOffset:X4}");
 
             switch (Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_KIND_MASK)
             {
                 case CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_NONE:
-                    stringBuilder.AppendFormat(" CATCH: {0}", ClassName ?? "null");
+                    writer.Write($" CATCH: {0}", ClassName ?? "null");
                     break;
 
                 case CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_FILTER:
-                    stringBuilder.AppendFormat(" FILTER (RVA {0:X4})", ClassTokenOrFilterOffset + methodRva);
+                    writer.Write($" FILTER (RVA {0:X4})", ClassTokenOrFilterOffset + methodRva);
                     break;
 
                 case CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_FINALLY:
-                    stringBuilder.AppendFormat(" FINALLY");
+                    writer.Write(" FINALLY");
                     break;
 
                 case CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_FAULT:
-                    stringBuilder.AppendFormat(" FAULT");
+                    writer.Write(" FAULT");
                     break;
 
                 default:
@@ -137,7 +137,7 @@ namespace R2RDump
 
             if ((Flags & CorExceptionFlag.COR_ILEXCEPTION_CLAUSE_DUPLICATED) != (CorExceptionFlag)0)
             {
-                stringBuilder.Append(" DUPLICATED");
+                writer.Write(" DUPLICATED");
             }
         }
     }
@@ -189,12 +189,12 @@ namespace R2RDump
         /// <summary>
         /// Emit the textual representation of the EH info into a given writer.
         /// </summary>
-        public void WriteTo(StringBuilder stringBuilder)
+        public void WriteTo(TextWriter writer)
         {
             foreach (EHClause ehClause in EHClauses)
             {
-                ehClause.WriteTo(stringBuilder, MethodRVA);
-                stringBuilder.AppendLine();
+                ehClause.WriteTo(writer, MethodRVA);
+                writer.WriteLine();
             }
         }
     }
index 592f123..6078e6f 100644 (file)
@@ -137,21 +137,19 @@ namespace R2RDump
             EHInfo = ehInfo;
         }
 
-        public override string ToString()
+        public void WriteTo(TextWriter writer, DumpOptions options)
         {
-            StringBuilder sb = new StringBuilder();
-
-            sb.AppendLine($"Id: {Id}");
-            sb.AppendLine($"StartAddress: 0x{StartAddress:X8}");
+            writer.WriteLine($"Id: {Id}");
+            writer.WriteLine($"StartAddress: 0x{StartAddress:X8}");
             if (Size == -1)
             {
-                sb.AppendLine("Size: Unavailable");
+                writer.WriteLine("Size: Unavailable");
             }
             else
             {
-                sb.AppendLine($"Size: {Size} bytes");
+                writer.WriteLine($"Size: {Size} bytes");
             }
-            sb.AppendLine($"UnwindRVA: 0x{UnwindRVA:X8}");
+            writer.WriteLine($"UnwindRVA: 0x{UnwindRVA:X8}");
             if (UnwindInfo is Amd64.UnwindInfo amd64UnwindInfo)
             {
                 string parsedFlags = "";
@@ -171,40 +169,38 @@ namespace R2RDump
                 {
                     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}");
+                writer.WriteLine($"Version:            {amd64UnwindInfo.Version}");
+                writer.WriteLine($"Flags:              0x{amd64UnwindInfo.Flags:X2}{parsedFlags}");
+                writer.WriteLine($"SizeOfProlog:       0x{amd64UnwindInfo.SizeOfProlog:X4}");
+                writer.WriteLine($"CountOfUnwindCodes: {amd64UnwindInfo.CountOfUnwindCodes}");
+                writer.WriteLine($"FrameRegister:      {amd64UnwindInfo.FrameRegister}");
+                writer.WriteLine($"FrameOffset:        0x{amd64UnwindInfo.FrameOffset}");
+                writer.WriteLine($"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();
+                    writer.Write($"UnwindCode[{unwindCode.Index}]: ");
+                    writer.Write($"CodeOffset 0x{unwindCode.CodeOffset:X4} ");
+                    writer.Write($"FrameOffset 0x{unwindCode.FrameOffset:X4} ");
+                    writer.Write($"NextOffset 0x{unwindCode.NextFrameOffset} ");
+                    writer.Write($"Op {unwindCode.OpInfoStr}");
+                    writer.WriteLine();
                 }
             }
-            sb.AppendLine();
+            writer.WriteLine();
 
             if (EHInfo != null)
             {
-                sb.AppendLine($@"EH info @ {EHInfo.EHInfoRVA:X4}, #clauses = {EHInfo.EHClauses.Length}");
-                EHInfo.WriteTo(sb);
-                sb.AppendLine();
+                writer.WriteLine($@"EH info @ {EHInfo.EHInfoRVA:X4}, #clauses = {EHInfo.EHClauses.Length}");
+                EHInfo.WriteTo(writer);
+                writer.WriteLine();
             }
 
             if (DebugInfo != null)
             {
-                sb.AppendLine(DebugInfo.ToString());
+                DebugInfo.WriteTo(writer, options);
             }
-
-            return sb.ToString();
         }
     }
 
index df1bef5..da45a71 100644 (file)
@@ -149,7 +149,7 @@ namespace R2RDump
         internal override void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode = null)
         {
             _writer.WriteLine(rtf.Method.SignatureString);
-            _writer.Write($"{rtf}");
+            rtf.WriteTo(_writer, _options);
 
             if (_options.Disasm)
             {