Browse Source

Address code review feedbacks

pull/1872/head
Andrew Au 6 years ago
parent
commit
4cd068ef8d
  1. 129
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs

129
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -18,6 +18,7 @@
using System; using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -31,37 +32,28 @@ using ILCompiler.Reflection.ReadyToRun;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
[Export(typeof(Language))] [Export(typeof(Language))]
class ReadyToRunLanguage : Language internal class ReadyToRunLanguage : Language
{ {
private static readonly ConditionalWeakTable<PEFile, R2RReader> r2rReaders = new ConditionalWeakTable<PEFile, R2RReader>(); private static readonly ConditionalWeakTable<PEFile, R2RReaderCacheEntry> r2rReaders = new ConditionalWeakTable<PEFile, R2RReaderCacheEntry>();
public override string Name => "ReadyToRun"; public override string Name => "ReadyToRun";
public override string FileExtension { public override string FileExtension {
get { return ".asm"; } get { return ".asm"; }
} }
private R2RReader GetReader(LoadedAssembly assembly, PEFile module)
{
R2RReader result;
lock (r2rReaders) {
if (!r2rReaders.TryGetValue(module, out result)) {
// TODO: avoid eager parsing
result = new R2RReader(new R2RAssemblyResolver(assembly), module.Metadata, module.Reader, module.FileName);
r2rReaders.Add(module, result);
}
}
return result;
}
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{ {
PEFile module = assembly.GetPEFileOrNull(); PEFile module = assembly.GetPEFileOrNull();
R2RReader reader = GetReader(assembly, module); R2RReaderCacheEntry r2rReaderCacheEntry = GetReader(assembly, module);
if (r2rReaderCacheEntry.r2rReader == null) {
WriteCommentLine(output, "TODO - display ready to run information"); WriteCommentLine(output, r2rReaderCacheEntry.failureReason);
// TODO: display other header information } else {
foreach (var method in reader.R2RMethods) { R2RReader reader = r2rReaderCacheEntry.r2rReader;
WriteCommentLine(output, method.SignatureString); WriteCommentLine(output, "TODO - display ready to run information");
// TODO: display other header information
foreach (var method in reader.R2RMethods) {
WriteCommentLine(output, method.SignatureString);
}
} }
return base.DecompileAssembly(assembly, output, options); return base.DecompileAssembly(assembly, output, options);
@ -70,68 +62,64 @@ namespace ICSharpCode.ILSpy
public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
{ {
PEFile module = method.ParentModule.PEFile; PEFile module = method.ParentModule.PEFile;
R2RReader reader = GetReader(module.GetLoadedAssembly(), module); R2RReaderCacheEntry r2rReaderCacheEntry = GetReader(module.GetLoadedAssembly(), module);
int bitness = -1; if (r2rReaderCacheEntry.r2rReader == null) {
if (reader.Machine == Machine.Amd64) { WriteCommentLine(output, r2rReaderCacheEntry.failureReason);
bitness = 64; } else {
} else if (reader.Machine == Machine.I386) { R2RReader reader = r2rReaderCacheEntry.r2rReader;
bitness = 32; int bitness = -1;
} if (reader.Machine == Machine.Amd64) {
else { bitness = 64;
// TODO: Architecture other than x86/amd64 } else {
throw new NotImplementedException(""); Debug.Assert(reader.Machine == Machine.I386);
} bitness = 32;
foreach (var m in reader.R2RMethods) { }
if (m.MethodHandle == method.MetadataToken) { foreach (var m in reader.R2RMethods) {
// TODO: Indexing if (m.MethodHandle == method.MetadataToken) {
foreach (RuntimeFunction runtimeFunction in m.RuntimeFunctions) { // TODO: Indexing
WriteCommentLine(output, m.SignatureString); foreach (RuntimeFunction runtimeFunction in m.RuntimeFunctions) {
byte[] code = new byte[runtimeFunction.Size]; WriteCommentLine(output, m.SignatureString);
for (int i = 0; i < runtimeFunction.Size; i++) { byte[] code = new byte[runtimeFunction.Size];
code[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; for (int i = 0; i < runtimeFunction.Size; i++) {
code[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i];
}
Disassemble(output, code, bitness, (ulong)runtimeFunction.StartAddress);
output.WriteLine();
} }
DecoderFormatterExample(output, code, bitness, (ulong)runtimeFunction.StartAddress);
output.WriteLine();
} }
} }
} }
} }
private void DecoderFormatterExample(ITextOutput output, byte[] exampleCode, int exampleCodeBitness, ulong exampleCodeRIP) public override void WriteCommentLine(ITextOutput output, string comment)
{
output.WriteLine("; " + comment);
}
private void Disassemble(ITextOutput output, byte[] codeBytes, int bitness, ulong address)
{ {
// TODO: Decorate the disassembly with Unwind, GC and debug info // TODO: Decorate the disassembly with Unwind, GC and debug info
// You can also pass in a hex string, eg. "90 91 929394", or you can use your own CodeReader
// reading data from a file or memory etc
var codeBytes = exampleCode;
var codeReader = new ByteArrayCodeReader(codeBytes); var codeReader = new ByteArrayCodeReader(codeBytes);
var decoder = Decoder.Create(exampleCodeBitness, codeReader); var decoder = Decoder.Create(bitness, codeReader);
decoder.IP = exampleCodeRIP; decoder.IP = address;
ulong endRip = decoder.IP + (uint)codeBytes.Length; ulong endRip = decoder.IP + (uint)codeBytes.Length;
// This list is faster than List<Instruction> since it uses refs to the Instructions
// instead of copying them (each Instruction is 32 bytes in size). It has a ref indexer,
// and a ref iterator. Add() uses 'in' (ref readonly).
var instructions = new InstructionList(); var instructions = new InstructionList();
while (decoder.IP < endRip) { while (decoder.IP < endRip) {
// The method allocates an uninitialized element at the end of the list and
// returns a reference to it which is initialized by Decode().
decoder.Decode(out instructions.AllocUninitializedElement()); decoder.Decode(out instructions.AllocUninitializedElement());
} }
// Formatters: Masm*, Nasm*, Gas* (AT&T) and Intel* (XED)
// TODO: DecompilationOptions? // TODO: DecompilationOptions?
var formatter = new NasmFormatter(); var formatter = new NasmFormatter();
formatter.Options.DigitSeparator = "`"; formatter.Options.DigitSeparator = "`";
formatter.Options.FirstOperandCharIndex = 10; formatter.Options.FirstOperandCharIndex = 10;
var tempOutput = new StringBuilderFormatterOutput(); var tempOutput = new StringBuilderFormatterOutput();
// Use InstructionList's ref iterator (C# 7.3) to prevent copying 32 bytes every iteration
foreach (var instr in instructions) { foreach (var instr in instructions) {
// Don't use instr.ToString(), it allocates more, uses masm syntax and default options
formatter.Format(instr, tempOutput); formatter.Format(instr, tempOutput);
output.Write(instr.IP.ToString("X16")); output.Write(instr.IP.ToString("X16"));
output.Write(" "); output.Write(" ");
int instrLen = instr.ByteLength; int instrLen = instr.ByteLength;
int byteBaseIndex = (int)(instr.IP - exampleCodeRIP); int byteBaseIndex = (int)(instr.IP - address);
for (int i = 0; i < instrLen; i++) for (int i = 0; i < instrLen; i++)
output.Write(codeBytes[byteBaseIndex + i].ToString("X2")); output.Write(codeBytes[byteBaseIndex + i].ToString("X2"));
int missingBytes = 10 - instrLen; int missingBytes = 10 - instrLen;
@ -142,6 +130,28 @@ namespace ICSharpCode.ILSpy
} }
} }
private R2RReaderCacheEntry GetReader(LoadedAssembly assembly, PEFile module)
{
R2RReaderCacheEntry result;
lock (r2rReaders) {
if (!r2rReaders.TryGetValue(module, out result)) {
result = new R2RReaderCacheEntry();
try {
// TODO: avoid eager parsing
result.r2rReader = new R2RReader(new R2RAssemblyResolver(assembly), module.Metadata, module.Reader, module.FileName);
if (result.r2rReader.Machine != Machine.Amd64 && result.r2rReader.Machine != Machine.I386) {
result.failureReason = $"Architecture {result.r2rReader.Machine} is not currently supported.";
result.r2rReader = null;
}
} catch (BadImageFormatException e) {
result.failureReason = e.Message;
}
r2rReaders.Add(module, result);
}
}
return result;
}
private class R2RAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver private class R2RAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver
{ {
private LoadedAssembly loadedAssembly; private LoadedAssembly loadedAssembly;
@ -162,9 +172,10 @@ namespace ICSharpCode.ILSpy
} }
} }
public override void WriteCommentLine(ITextOutput output, string comment) private class R2RReaderCacheEntry
{ {
output.WriteLine("; " + comment); public R2RReader r2rReader;
public string failureReason;
} }
} }
} }
Loading…
Cancel
Save