diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index b2c176ae0..3d085f25d 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -58,6 +58,11 @@ namespace ICSharpCode.Decompiler.Disassembler /// public bool ShowMetadataTokensInBase10 { get; set; } + /// + /// Show raw RVA offset and bytes before each instruction. + /// + public bool ShowRawRVAOffsetAndBytes { get; set; } + /// /// Optional provider for sequence points. /// @@ -128,14 +133,14 @@ namespace ICSharpCode.Decompiler.Disassembler blob.Reset(); HashSet branchTargets = GetBranchTargets(blob); blob.Reset(); - WriteStructureBody(new ILStructure(module, handle, genericContext, body), branchTargets, ref blob); + WriteStructureBody(new ILStructure(module, handle, genericContext, body), branchTargets, ref blob, methodDefinition.RelativeVirtualAddress + headerSize); } else { while (blob.RemainingBytes > 0) { cancellationToken.ThrowIfCancellationRequested(); - WriteInstruction(output, metadata, handle, ref blob); + WriteInstruction(output, metadata, handle, ref blob, methodDefinition.RelativeVirtualAddress); } WriteExceptionHandlers(module, handle, body); } @@ -281,7 +286,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Indent(); } - void WriteStructureBody(ILStructure s, HashSet branchTargets, ref BlobReader body) + void WriteStructureBody(ILStructure s, HashSet branchTargets, ref BlobReader body, int methodRva) { bool isFirstInstructionInStructure = true; bool prevInstructionWasBranch = false; @@ -294,7 +299,7 @@ namespace ICSharpCode.Decompiler.Disassembler { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); - WriteStructureBody(child, branchTargets, ref body); + WriteStructureBody(child, branchTargets, ref body, methodRva); WriteStructureFooter(child); } else @@ -305,7 +310,7 @@ namespace ICSharpCode.Decompiler.Disassembler } var currentOpCode = ILParser.DecodeOpCode(ref body); body.Offset = offset; // reset IL stream - WriteInstruction(output, metadata, s.MethodHandle, ref body); + WriteInstruction(output, metadata, s.MethodHandle, ref body, methodRva); prevInstructionWasBranch = currentOpCode.IsBranch() || currentOpCode.IsReturn() || currentOpCode == ILOpCode.Throw @@ -338,7 +343,7 @@ namespace ICSharpCode.Decompiler.Disassembler } } - protected virtual void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodDefinition, ref BlobReader blob) + protected virtual void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) { int offset = blob.Offset; if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) @@ -363,10 +368,11 @@ namespace ICSharpCode.Decompiler.Disassembler } } ILOpCode opCode = ILParser.DecodeOpCode(ref blob); - output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); - output.Write(": "); if (opCode.IsDefined()) { + WriteRVA(blob, offset + methodRva, opCode); + output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); + output.Write(": "); WriteOpCode(opCode); switch (opCode.GetOperandType()) { @@ -469,12 +475,40 @@ namespace ICSharpCode.Decompiler.Disassembler WriteMetadataToken(userString, metadataToken, spaceBefore: true); break; case OperandType.Switch: + var tmp = blob; int[] targets = ILParser.DecodeSwitchTargets(ref blob); - output.Write(" ("); + if (ShowRawRVAOffsetAndBytes) + { + output.WriteLine(" ("); + } + else + { + output.Write(" ("); + } + tmp.ReadInt32(); for (int i = 0; i < targets.Length; i++) { if (i > 0) - output.Write(", "); + { + if (ShowRawRVAOffsetAndBytes) + { + output.WriteLine(","); + } + else + { + output.Write(", "); + } + } + if (ShowRawRVAOffsetAndBytes) + { + output.Write("/* "); + output.Write($"{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}{tmp.ReadByte():X2}"); + output.Write(" */ "); + } + if (ShowRawRVAOffsetAndBytes) + { + output.Write(" "); + } output.WriteLocalReference($"IL_{targets[i]:x4}", targets[i]); } output.Write(")"); @@ -484,11 +518,11 @@ namespace ICSharpCode.Decompiler.Disassembler int index = blob.ReadUInt16(); if (opCode == ILOpCode.Ldloc || opCode == ILOpCode.Ldloca || opCode == ILOpCode.Stloc) { - DisassemblerHelpers.WriteVariableReference(output, metadata, methodDefinition, index); + DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); } else { - DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, index); + DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); } break; case OperandType.ShortVariable: @@ -496,11 +530,11 @@ namespace ICSharpCode.Decompiler.Disassembler index = blob.ReadByte(); if (opCode == ILOpCode.Ldloc_s || opCode == ILOpCode.Ldloca_s || opCode == ILOpCode.Stloc_s) { - DisassemblerHelpers.WriteVariableReference(output, metadata, methodDefinition, index); + DisassemblerHelpers.WriteVariableReference(output, metadata, methodHandle, index); } else { - DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, index); + DisassemblerHelpers.WriteParameterReference(output, metadata, methodHandle, index); } break; } @@ -510,8 +544,22 @@ namespace ICSharpCode.Decompiler.Disassembler ushort opCodeValue = (ushort)opCode; if (opCodeValue > 0xFF) { + if (ShowRawRVAOffsetAndBytes) + { + output.Write("/* "); + output.Write($"0x{offset + methodRva:X8} {(ushort)opCode >> 8:X2}"); + output.Write(" */ "); + } + output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); + output.Write(": "); // split 16-bit value into two emitbyte directives output.WriteLine($".emitbyte 0x{(byte)(opCodeValue >> 8):x}"); + if (ShowRawRVAOffsetAndBytes) + { + output.Write("/* "); + output.Write($"0x{offset + methodRva + 1:X8} {(ushort)opCode & 0xFF:X2}"); + output.Write(" */ "); + } // add label output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset + 1), offset + 1, isDefinition: true); output.Write(": "); @@ -519,12 +567,49 @@ namespace ICSharpCode.Decompiler.Disassembler } else { + if (ShowRawRVAOffsetAndBytes) + { + output.Write("/* "); + output.Write($"0x{offset + methodRva:X8} {(ushort)opCode & 0xFF:X2}"); + output.Write(" */ "); + } + output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); + output.Write(": "); output.Write($".emitbyte 0x{(byte)opCodeValue:x}"); } } output.WriteLine(); } + void WriteRVA(BlobReader blob, int offset, ILOpCode opCode) + { + if (ShowRawRVAOffsetAndBytes) + { + output.Write("/* "); + var tmp = blob; + if (opCode == ILOpCode.Switch) + { + tmp.ReadInt32(); + } + else + { + ILParser.SkipOperand(ref tmp, opCode); + } + output.Write($"0x{offset:X8} {(ushort)opCode:X2}"); + int appendSpaces = (ushort)opCode > 0xFF ? 14 : 16; + while (blob.Offset < tmp.Offset) + { + output.Write($"{blob.ReadByte():X2}"); + appendSpaces -= 2; + } + if (appendSpaces > 0) + { + output.Write(new string(' ', appendSpaces)); + } + output.Write(" */ "); + } + } + private void WriteOpCode(ILOpCode opCode) { var opCodeInfo = new OpCodeInfo(opCode, opCode.GetDisplayName()); diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 861ba7fb2..67cd3ee9b 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -61,6 +61,11 @@ namespace ICSharpCode.Decompiler.Disassembler set => methodBodyDisassembler.ShowMetadataTokensInBase10 = value; } + public bool ShowRawRVAOffsetAndBytes { + get => methodBodyDisassembler.ShowRawRVAOffsetAndBytes; + set => methodBodyDisassembler.ShowRawRVAOffsetAndBytes = value; + } + public IDebugInfoProvider DebugInfo { get => methodBodyDisassembler.DebugInfo; set => methodBodyDisassembler.DebugInfo = value; diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs index 5bea5a8a3..2fa6a32f8 100644 --- a/ILSpy/Languages/CSharpILMixedLanguage.cs +++ b/ILSpy/Languages/CSharpILMixedLanguage.cs @@ -56,6 +56,7 @@ namespace ICSharpCode.ILSpy options.CancellationToken) { ShowMetadataTokens = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens, ShowMetadataTokensInBase10 = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10, + ShowRawRVAOffsetAndBytes = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowRawOffsetsAndBytesBeforeInstruction, ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions }; } @@ -109,7 +110,7 @@ namespace ICSharpCode.ILSpy } } - protected override void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodDefinition, ref BlobReader blob) + protected override void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) { int index = sequencePoints.BinarySearch(blob.Offset, seq => seq.Offset); if (index >= 0) @@ -143,7 +144,7 @@ namespace ICSharpCode.ILSpy highlightingOutput?.EndSpan(); } } - base.WriteInstruction(output, metadata, methodDefinition, ref blob); + base.WriteInstruction(output, metadata, methodHandle, ref blob, methodRva); } HighlightingColor gray = new HighlightingColor { Foreground = new SimpleHighlightingBrush(Colors.DarkGray) }; diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index 9e20cd280..d31068de4 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -61,6 +61,7 @@ namespace ICSharpCode.ILSpy ShowSequencePoints = options.DecompilerSettings.ShowDebugInfo, ShowMetadataTokens = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens, ShowMetadataTokensInBase10 = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10, + ShowRawRVAOffsetAndBytes = Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowRawOffsetsAndBytesBeforeInstruction, ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions }; } diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs index 97f54c8b6..bd5f5bc26 100644 --- a/ILSpy/Options/DisplaySettings.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -286,6 +286,19 @@ namespace ICSharpCode.ILSpy.Options } } + private bool showRawOffsetsAndBytesBeforeInstruction; + + public bool ShowRawOffsetsAndBytesBeforeInstruction { + get { return showRawOffsetsAndBytesBeforeInstruction; } + set { + if (showRawOffsetsAndBytesBeforeInstruction != value) + { + showRawOffsetsAndBytesBeforeInstruction = value; + OnPropertyChanged(); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; @@ -305,6 +318,7 @@ namespace ICSharpCode.ILSpy.Options this.HighlightMatchingBraces = s.highlightMatchingBraces; this.HighlightCurrentLine = s.highlightCurrentLine; this.HideEmptyMetadataTables = s.hideEmptyMetadataTables; + this.ShowRawOffsetsAndBytesBeforeInstruction = s.showRawOffsetsAndBytesBeforeInstruction; this.StyleWindowTitleBar = s.styleWindowTitleBar; } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index 08be65bc4..4d5a665f3 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -82,6 +82,7 @@ + diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index ccfeb4d2d..87d6aa4f3 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -123,6 +123,7 @@ namespace ICSharpCode.ILSpy.Options s.HighlightMatchingBraces = (bool?)e.Attribute("HighlightMatchingBraces") ?? true; s.HighlightCurrentLine = (bool?)e.Attribute("HighlightCurrentLine") ?? false; s.HideEmptyMetadataTables = (bool?)e.Attribute("HideEmptyMetadataTables") ?? true; + s.ShowRawOffsetsAndBytesBeforeInstruction = (bool?)e.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false; s.StyleWindowTitleBar = (bool?)e.Attribute("StyleWindowTitleBar") ?? false; return s; @@ -150,6 +151,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("HighlightMatchingBraces", s.HighlightMatchingBraces); section.SetAttributeValue("HighlightCurrentLine", s.HighlightCurrentLine); section.SetAttributeValue("HideEmptyMetadataTables", s.HideEmptyMetadataTables); + section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", s.ShowRawOffsetsAndBytesBeforeInstruction); section.SetAttributeValue("StyleWindowTitleBar", s.StyleWindowTitleBar); XElement existingElement = root.Element("DisplaySettings"); diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index dde98f9aa..0d803c490 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -2417,6 +2417,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Show raw offsets and bytes before each instruction. + /// + public static string ShowRawOffsetsAndBytesBeforeInstruction { + get { + return ResourceManager.GetString("ShowRawOffsetsAndBytesBeforeInstruction", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show state after this step. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index e00992246..e89e582d7 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -825,6 +825,9 @@ Do you want to continue? Show only public types and members + + Show raw offsets and bytes before each instruction + Show state after this step