Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

<ItemGroup>
<PackageReference Include="Iced" Version="1.6.0" />
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.8-alpha" />
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.9-alpha" />
</ItemGroup>

<Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences">
Expand Down
156 changes: 149 additions & 7 deletions ILSpy.ReadyToRun/ReadyToRunLanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,85 @@ public override void WriteCommentLine(ITextOutput output, string comment)
output.WriteLine("; " + comment);
}

private Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> WriteDebugInfo(ReadyToRunMethod readyToRunMethod, ITextOutput output)
{
Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> debugInfoDict = new Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>>();
IReadOnlyList<RuntimeFunction> 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<ValueTuple<DebugInfo, NativeVarInfo>>();
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<ValueTuple<DebugInfo, NativeVarInfo>>();
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<ulong, UnwindCode> WriteUnwindInfo(RuntimeFunction runtimeFunction, ITextOutput output)

{
Dictionary<ulong, UnwindCode> unwindCodes = new Dictionary<ulong, UnwindCode>();
if (runtimeFunction.UnwindInfo is UnwindInfo amd64UnwindInfo) {
Expand All @@ -129,27 +204,32 @@ private Dictionary<ulong, UnwindCode> 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;
}

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<ulong, UnwindCode> unwindInfo = null;
if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) {
unwindInfo = WriteUnwindInfo(runtimeFunction, output);
}

bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null);
Dictionary<VarLocType, HashSet<ValueTuple<DebugInfo, NativeVarInfo>>> 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;
Expand All @@ -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) {
Expand All @@ -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();
Expand All @@ -213,6 +296,66 @@ private static void DecorateUnwindInfo(ITextOutput output, Dictionary<ulong, Unw
}
}

private static void DecorateDebugInfo(ITextOutput output, Instruction instr, Dictionary<VarLocType, HashSet<(DebugInfo debugInfo, NativeVarInfo varLoc)>> debugInfoDict, ulong baseInstrIP)
{
if (debugInfoDict != null) {
InstructionInfoFactory factory = new InstructionInfoFactory();
InstructionInfo info = factory.GetInfo(instr);
HashSet<ValueTuple<DebugInfo, NativeVarInfo>> stkSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
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}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to show hex for stackOffset to be consistent with the disassembly.

}
}
}
}
HashSet<ValueTuple<DebugInfo, NativeVarInfo>> regSet = new HashSet<ValueTuple<DebugInfo, NativeVarInfo>>();
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 &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to double-check with this, I think the StartOffset should be inclusive, the EndOffset I am not sure.

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;
Expand Down Expand Up @@ -246,7 +389,6 @@ private static void DecorateCallSite(PEFile currentFile, ITextOutput output, Rea
output.WriteLine(reader.ImportCellNames[importCellAddress]);
break;
}

output.WriteLine();
} else {
output.WriteLine();
Expand Down
2 changes: 1 addition & 1 deletion ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
<TextBlock Grid.Row="1" Margin="3" Text="{x:Static properties:Resources.ShowUnwindInfo}"/>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowUnwindInfo}" />
<TextBlock Grid.Row="2" Margin="3" Text="{x:Static properties:Resources.ShowDebugInfo}"/>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding DebugIsChecked}" />
<CheckBox Grid.Row="2" Grid.Column="1" Margin="3" IsChecked="{Binding IsShowDebugInfo}" />
</Grid>
</UserControl>
14 changes: 13 additions & 1 deletion ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
}
}

Expand All @@ -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;

Expand Down
24 changes: 18 additions & 6 deletions ILSpy.ReadyToRun/ReadyToRunOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public static string GetDisassemblyFormat(ILSpySettings settings)
}

public static bool GetIsShowUnwindInfo(ILSpySettings settings)

{
if (settings == null) {
settings = ILSpySettings.Load();
Expand All @@ -58,20 +57,33 @@ 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);
} else {
root.Add(section);
}
}


}

}