Browse Source

Fix #1966: Add option to show raw offsets and instruction bytes in IL view.

pull/2491/head
Siegfried Pammer 4 years ago
parent
commit
900d0a4b93
  1. 109
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  2. 5
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  3. 5
      ILSpy/Languages/CSharpILMixedLanguage.cs
  4. 1
      ILSpy/Languages/ILLanguage.cs
  5. 14
      ILSpy/Options/DisplaySettings.cs
  6. 1
      ILSpy/Options/DisplaySettingsPanel.xaml
  7. 2
      ILSpy/Options/DisplaySettingsPanel.xaml.cs
  8. 9
      ILSpy/Properties/Resources.Designer.cs
  9. 3
      ILSpy/Properties/Resources.resx

109
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -58,6 +58,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -58,6 +58,11 @@ namespace ICSharpCode.Decompiler.Disassembler
/// </summary>
public bool ShowMetadataTokensInBase10 { get; set; }
/// <summary>
/// Show raw RVA offset and bytes before each instruction.
/// </summary>
public bool ShowRawRVAOffsetAndBytes { get; set; }
/// <summary>
/// Optional provider for sequence points.
/// </summary>
@ -128,14 +133,14 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -128,14 +133,14 @@ namespace ICSharpCode.Decompiler.Disassembler
blob.Reset();
HashSet<int> 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 @@ -281,7 +286,7 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Indent();
}
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref BlobReader body)
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref BlobReader body, int methodRva)
{
bool isFirstInstructionInStructure = true;
bool prevInstructionWasBranch = false;
@ -294,7 +299,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
if (ShowRawRVAOffsetAndBytes)
{
output.WriteLine(" (");
}
else
{
output.Write(" (");
}
tmp.ReadInt32();
for (int i = 0; i < targets.Length; i++)
{
if (i > 0)
{
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 @@ -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 @@ -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 @@ -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 @@ -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());

5
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -61,6 +61,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -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;

5
ILSpy/Languages/CSharpILMixedLanguage.cs

@ -56,6 +56,7 @@ namespace ICSharpCode.ILSpy @@ -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 @@ -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 @@ -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) };

1
ILSpy/Languages/ILLanguage.cs

@ -61,6 +61,7 @@ namespace ICSharpCode.ILSpy @@ -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
};
}

14
ILSpy/Options/DisplaySettings.cs

@ -286,6 +286,19 @@ namespace ICSharpCode.ILSpy.Options @@ -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 @@ -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;
}
}

1
ILSpy/Options/DisplaySettingsPanel.xaml

@ -82,6 +82,7 @@ @@ -82,6 +82,7 @@
<CheckBox IsChecked="{Binding ExpandMemberDefinitions}" Content="{x:Static properties:Resources.ExpandMemberDefinitionsAfterDecompilation}"></CheckBox>
<CheckBox IsChecked="{Binding ExpandUsingDeclarations}" Content="{x:Static properties:Resources.ExpandUsingDeclarationsAfterDecompilation}"></CheckBox>
<CheckBox IsChecked="{Binding ShowDebugInfo}" Content="{x:Static properties:Resources.ShowInfoFromDebugSymbolsAvailable}"></CheckBox>
<CheckBox IsChecked="{Binding ShowRawOffsetsAndBytesBeforeInstruction}" Content="{x:Static properties:Resources.ShowRawOffsetsAndBytesBeforeInstruction}"></CheckBox>
</StackPanel>
</GroupBox>
<GroupBox Header="{x:Static properties:Resources.TreeViewOptions}">

2
ILSpy/Options/DisplaySettingsPanel.xaml.cs

@ -123,6 +123,7 @@ namespace ICSharpCode.ILSpy.Options @@ -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 @@ -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");

9
ILSpy/Properties/Resources.Designer.cs generated

@ -2417,6 +2417,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -2417,6 +2417,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Show raw offsets and bytes before each instruction.
/// </summary>
public static string ShowRawOffsetsAndBytesBeforeInstruction {
get {
return ResourceManager.GetString("ShowRawOffsetsAndBytesBeforeInstruction", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show state after this step.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -825,6 +825,9 @@ Do you want to continue?</value> @@ -825,6 +825,9 @@ Do you want to continue?</value>
<data name="ShowPublicOnlyTypesMembers" xml:space="preserve">
<value>Show only public types and members</value>
</data>
<data name="ShowRawOffsetsAndBytesBeforeInstruction" xml:space="preserve">
<value>Show raw offsets and bytes before each instruction</value>
</data>
<data name="ShowStateAfterThisStep" xml:space="preserve">
<value>Show state after this step</value>
</data>

Loading…
Cancel
Save