diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj index 878beb5d62..9e27fa5b56 100644 --- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -75,7 +75,7 @@ - + diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index 2bfb3bccfc..2d6271028a 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -103,10 +103,85 @@ public override void WriteCommentLine(ITextOutput output, string comment) output.WriteLine("; " + comment); } + private Dictionary> WriteDebugInfo(ReadyToRunMethod readyToRunMethod, ITextOutput output) + { + Dictionary> debugInfoDict = new Dictionary>(); + IReadOnlyList runTimeList = readyToRunMethod.RuntimeFunctions; + foreach (RuntimeFunction runtimeFunction in runTimeList) { + DebugInfo debugInfo = runtimeFunction.DebugInfo; + if (debugInfo != null && debugInfo.BoundsList.Count > 0) { + for (int i = 0; i < debugInfo.VariablesList.Count; ++i) { + var varLoc = debugInfo.VariablesList[i]; + try { + var typeSet = new HashSet>(); + bool found = debugInfoDict.TryGetValue(varLoc.VariableLocation.VarLocType, out typeSet); + if (found) { + (DebugInfo debugInfo, NativeVarInfo varLoc) newTuple = (debugInfo, varLoc); + typeSet.Add(newTuple); + } else { + typeSet = new HashSet>(); + debugInfoDict.Add(varLoc.VariableLocation.VarLocType, typeSet); + (DebugInfo debugInfo, NativeVarInfo varLoc) newTuple = (debugInfo, varLoc); + typeSet.Add(newTuple); + } + } catch (ArgumentNullException) { + output.WriteLine("Failed to find hash set of Debug info type"); + } + if (varLoc.VariableLocation.VarLocType != VarLocType.VLT_REG && varLoc.VariableLocation.VarLocType != VarLocType.VLT_STK + && varLoc.VariableLocation.VarLocType != VarLocType.VLT_STK_BYREF) { + output.WriteLine($" Variable Number: {varLoc.VariableNumber}"); + output.WriteLine($" Start Offset: 0x{varLoc.StartOffset:X}"); + output.WriteLine($" End Offset: 0x{varLoc.EndOffset:X}"); + output.WriteLine($" Loc Type: {varLoc.VariableLocation.VarLocType}"); + switch (varLoc.VariableLocation.VarLocType) { + case VarLocType.VLT_REG: + case VarLocType.VLT_REG_FP: + case VarLocType.VLT_REG_BYREF: + output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + break; + case VarLocType.VLT_STK: + case VarLocType.VLT_STK_BYREF: + output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data2}"); + break; + case VarLocType.VLT_REG_REG: + output.WriteLine($" Register 1: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + output.WriteLine($" Register 2: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}"); + break; + case VarLocType.VLT_REG_STK: + output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}"); + output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data3}"); + break; + case VarLocType.VLT_STK_REG: + output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data1}"); + output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data2)}"); + output.WriteLine($" Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data3)}"); + break; + case VarLocType.VLT_STK2: + output.WriteLine($" Base Register: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + output.WriteLine($" Stack Offset: {varLoc.VariableLocation.Data2}"); + break; + case VarLocType.VLT_FPSTK: + output.WriteLine($" Offset: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + break; + case VarLocType.VLT_FIXED_VA: + output.WriteLine($" Offset: {DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varLoc.VariableLocation.Data1)}"); + break; + default: + output.WriteLine("WRN: Unexpected variable location type"); + break; + } + output.WriteLine(""); + } + } + } + } + return debugInfoDict; + } private Dictionary WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output) - { Dictionary unwindCodes = new Dictionary(); if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) { @@ -129,7 +204,6 @@ private Dictionary WriteUnwindInfo(RuntimeFunction runtimeFun WriteCommentLine(output, $"FrameRegister: {((amd64UnwindInfo.FrameRegister == 0) ? "none" : amd64UnwindInfo.FrameRegister.ToString())}"); for (int unwindCodeIndex = 0; unwindCodeIndex < amd64UnwindInfo.CountOfUnwindCodes; unwindCodeIndex++) { unwindCodes.Add((ulong)(amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex].CodeOffset), amd64UnwindInfo.UnwindCodeArray[unwindCodeIndex]); - } } return unwindCodes; @@ -137,19 +211,25 @@ private Dictionary WriteUnwindInfo(RuntimeFunction runtimeFun private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10) { + // TODO: Decorate the disassembly with GCInfo WriteCommentLine(output, readyToRunMethod.SignatureString); + Dictionary unwindInfo = null; if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) { unwindInfo = WriteUnwindInfo(runtimeFunction, output); } + bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null); + Dictionary>> debugInfo = null; + if (isShowDebugInfo) { + debugInfo = WriteDebugInfo(readyToRunMethod, output); + } byte[] codeBytes = new byte[runtimeFunction.Size]; for (int i = 0; i < runtimeFunction.Size; i++) { codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; } - // TODO: Decorate the disassembly with GC and debug info var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Decoder.Create(bitness, codeReader); decoder.IP = address; @@ -174,7 +254,7 @@ private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReade ulong baseInstrIP = instructions[0].IP; foreach (var instr in instructions) { int byteBaseIndex = (int)(instr.IP - address); - if (runtimeFunction.DebugInfo != null) { + if (isShowDebugInfo && runtimeFunction.DebugInfo != null) { foreach (var bound in runtimeFunction.DebugInfo.BoundsList) { if (bound.NativeOffset == byteBaseIndex) { if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog) { @@ -191,14 +271,17 @@ private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReade output.Write(instr.IP.ToString("X16")); output.Write(" "); int instrLen = instr.Length; - for (int i = 0; i < instrLen; i++) + for (int i = 0; i < instrLen; i++) { output.Write(codeBytes[byteBaseIndex + i].ToString("X2")); + } int missingBytes = 10 - instrLen; - for (int i = 0; i < missingBytes; i++) + for (int i = 0; i < missingBytes; i++) { output.Write(" "); + } output.Write(" "); output.Write(tempOutput.ToStringAndReset()); DecorateUnwindInfo(output, unwindInfo, baseInstrIP, instr); + DecorateDebugInfo(output, instr, debugInfo, baseInstrIP); DecorateCallSite(currentFile, output, reader, showMetadataTokens, showMetadataTokensInBase10, instr); } output.WriteLine(); @@ -213,6 +296,66 @@ private static void DecorateUnwindInfo(ITextOutput output, Dictionary> debugInfoDict, ulong baseInstrIP) + { + if (debugInfoDict != null) { + InstructionInfoFactory factory = new InstructionInfoFactory(); + InstructionInfo info = factory.GetInfo(instr); + HashSet> stkSet = new HashSet>(); + if (debugInfoDict.ContainsKey(VarLocType.VLT_STK)) { + stkSet.UnionWith(debugInfoDict[VarLocType.VLT_STK]); + } + if (debugInfoDict.ContainsKey(VarLocType.VLT_STK_BYREF)) { + stkSet.UnionWith(debugInfoDict[VarLocType.VLT_STK_BYREF]); + } + if (stkSet != null) { + foreach (UsedMemory usedMemInfo in info.GetUsedMemory()) { //for each time a [register +- value] is used + foreach ((DebugInfo debugInfo, NativeVarInfo varLoc) tuple in stkSet) { //for each VLT_STK variable + var debugInfo = tuple.debugInfo; + var varInfo = tuple.varLoc; + int stackOffset = varInfo.VariableLocation.Data2; + ulong adjOffset; + bool negativeOffset; + if (stackOffset < 0) { + int absValue = -1 * stackOffset; + adjOffset = ulong.MaxValue - (ulong)absValue + 1; + negativeOffset = true; + } else { + adjOffset = (ulong)stackOffset; + negativeOffset = false; + } + if (varInfo.StartOffset < instr.IP - baseInstrIP && varInfo.EndOffset > instr.IP - baseInstrIP && + DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varInfo.VariableLocation.Data1) == usedMemInfo.Base.ToString() && + adjOffset == usedMemInfo.Displacement) { + output.Write($"; [{usedMemInfo.Base.ToString()}{(negativeOffset ? '-' : '+')}{Math.Abs(stackOffset)}] = {varInfo.Variable.Type} {varInfo.Variable.Index}"); + } + } + } + } + HashSet> regSet = new HashSet>(); + if (debugInfoDict.ContainsKey(VarLocType.VLT_REG)) { + regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG]); + } + if (debugInfoDict.ContainsKey(VarLocType.VLT_REG_BYREF)) { + regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG_BYREF]); + } + if (debugInfoDict.ContainsKey(VarLocType.VLT_REG_FP)) { + regSet.UnionWith(debugInfoDict[VarLocType.VLT_REG_FP]); + } + if (regSet != null) { + foreach (UsedRegister usedMemInfo in info.GetUsedRegisters()) { + foreach ((DebugInfo debugInfo, NativeVarInfo varLoc) tuple in regSet) { + var debugInfo = tuple.debugInfo; + var varInfo = tuple.varLoc; + if (varInfo.StartOffset < instr.IP - baseInstrIP && varInfo.EndOffset > instr.IP - baseInstrIP && + DebugInfo.GetPlatformSpecificRegister(debugInfo.Machine, varInfo.VariableLocation.Data1) == usedMemInfo.Register.ToString()) { + output.Write($"; {usedMemInfo.Register.ToString()} = {varInfo.Variable.Type} {varInfo.Variable.Index}"); + } + } + } + } + } + } private static void DecorateCallSite(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, bool showMetadataTokens, bool showMetadataTokensInBase10, Instruction instr) { int importCellAddress = (int)instr.IPRelativeMemoryAddress; @@ -246,7 +389,6 @@ private static void DecorateCallSite(PEFile currentFile, ITextOutput output, Rea output.WriteLine(reader.ImportCellNames[importCellAddress]); break; } - output.WriteLine(); } else { output.WriteLine(); diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml index c3e28eaf0d..c11450ef70 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml +++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml @@ -17,6 +17,6 @@ - + \ No newline at end of file diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs index 59ee6d7346..7abcb69a1c 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs +++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs @@ -36,6 +36,7 @@ public void Load(ILSpySettings settings) Options s = new Options(); s.DisassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(settings); s.IsShowUnwindInfo = ReadyToRunOptions.GetIsShowUnwindInfo(settings); + s.IsShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(settings); this.DataContext = s; } @@ -48,7 +49,7 @@ public void LoadDefaults() public void Save(XElement root) { Options s = (Options)this.DataContext; - ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo); + ReadyToRunOptions.SetDisassemblyOptions(root, s.DisassemblyFormat, s.IsShowUnwindInfo, s.IsShowDebugInfo); } } @@ -71,6 +72,17 @@ public bool IsShowUnwindInfo { } } + private bool isShowDebugInfo; + + public bool IsShowDebugInfo { + get { + return isShowDebugInfo; + } + set { + isShowDebugInfo = value; + OnPropertyChanged(nameof(IsShowDebugInfo)); + } + } private string disassemblyFormat; diff --git a/ILSpy.ReadyToRun/ReadyToRunOptions.cs b/ILSpy.ReadyToRun/ReadyToRunOptions.cs index 5eddd67891..f82535e630 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptions.cs +++ b/ILSpy.ReadyToRun/ReadyToRunOptions.cs @@ -43,7 +43,6 @@ public static string GetDisassemblyFormat(ILSpySettings settings) } public static bool GetIsShowUnwindInfo(ILSpySettings settings) - { if (settings == null) { settings = ILSpySettings.Load(); @@ -58,11 +57,27 @@ public static bool GetIsShowUnwindInfo(ILSpySettings settings) } } - public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool IsShowUnwindInfo) + public static bool GetIsShowDebugInfo(ILSpySettings settings) + { + if (settings == null) { + settings = ILSpySettings.Load(); + } + XElement e = settings[ns + "ReadyToRunOptions"]; + XAttribute a = e.Attribute("IsShowDebugInfo"); + + if (a == null) { + return true; + } else { + return (bool)a; + } + } + + public static void SetDisassemblyOptions(XElement root, string disassemblyFormat, bool isShowUnwindInfo, bool isShowDebugInfo) { XElement section = new XElement(ns + "ReadyToRunOptions"); section.SetAttributeValue("DisassemblyFormat", disassemblyFormat); - section.SetAttributeValue("IsShowUnwindInfo", IsShowUnwindInfo); + section.SetAttributeValue("IsShowUnwindInfo", isShowUnwindInfo); + section.SetAttributeValue("IsShowDebugInfo", isShowDebugInfo); XElement existingElement = root.Element(ns + "ReadyToRunOptions"); if (existingElement != null) { existingElement.ReplaceWith(section); @@ -70,8 +85,5 @@ public static void SetDisassemblyOptions(XElement root, string disassemblyFormat root.Add(section); } } - - } - }