diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj new file mode 100644 index 0000000000..74eb826efc --- /dev/null +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -0,0 +1,65 @@ + + + + + + net472 + ILSpy.ReadyToRun.Plugin + 7.2 + + False + + false + + 6488064 + + + + full + true + True + + + + pdbonly + true + + + + ..\ILSpy\bin\$(Configuration)\ + + + + + False + + + False + + + False + + + + + + ..\..\iced\Iced\bin\Debug\netstandard2.0\Iced.dll + + + ..\..\runtime\artifacts\bin\coreclr\Windows_NT.x64.Debug\ILCompiler.Reflection.ReadyToRun.dll + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy.ReadyToRun/Properties/AssemblyInfo.cs b/ILSpy.ReadyToRun/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..85918694e1 --- /dev/null +++ b/ILSpy.ReadyToRun/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +#region Using directives + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ILSpy.ReadyToRun.Plugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ILSpy.ReadyToRun.Plugin")] +[assembly: AssemblyCopyright("Copyright 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +[assembly: InternalsVisibleTo("ILSpy.ReadyToRun.Tests")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs new file mode 100644 index 0000000000..0d15383f1b --- /dev/null +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel.Composition; +using Iced.Intel; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; +using ICSharpCode.Decompiler.TypeSystem; +using ILCompiler.Reflection.ReadyToRun; + +namespace ICSharpCode.ILSpy +{ + [Export(typeof(Language))] + class ReadyToRunLanguage : Language + { + public override string Name => "ReadyToRun"; + + public override string FileExtension { + get { return ".asm"; } + } + + public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + { + PEFile module = assembly.GetPEFileOrNull(); + // TODO: avoid eager parsing + R2RReader reader = new R2RReader(new R2RAssemblyResolver(), module.Metadata, module.Reader, module.FileName); + + output.WriteLine("// TODO - display ready to run information"); + // TODO: display other header information + foreach (var method in reader.R2RMethods) { + output.WriteLine(method.SignatureString); + } + + return base.DecompileAssembly(assembly, output, options); + } + + public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) + { + PEFile module = method.ParentModule.PEFile; + // TODO: avoid eager parsing in R2RReader + R2RReader reader = new R2RReader(new R2RAssemblyResolver(), module.Metadata, module.Reader, module.FileName); + foreach (var m in reader.R2RMethods) { + if (m.MethodHandle == method.MetadataToken) { + // TODO: Indexing + foreach (RuntimeFunction runtimeFunction in m.RuntimeFunctions) { + byte[] code = new byte[runtimeFunction.Size]; + for (int i = 0; i < runtimeFunction.Size; i++) { + code[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; + } + // TODO: Bitness + // TODO: RIP + DecoderFormatterExample(output, code, 64, 0); + } + + } + } + } + private void DecoderFormatterExample(ITextOutput output, byte[] exampleCode, int exampleCodeBitness, ulong exampleCodeRIP) + { + // 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 decoder = Decoder.Create(exampleCodeBitness, codeReader); + decoder.IP = exampleCodeRIP; + ulong endRip = decoder.IP + (uint)codeBytes.Length; + + // This list is faster than List 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(); + 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()); + } + + // Formatters: Masm*, Nasm*, Gas* (AT&T) and Intel* (XED) + // TODO: DecompilationOptions? + var formatter = new NasmFormatter(); + formatter.Options.DigitSeparator = "`"; + formatter.Options.FirstOperandCharIndex = 10; + var tempOutput = new StringBuilderFormatterOutput(); + // Use InstructionList's ref iterator (C# 7.3) to prevent copying 32 bytes every iteration + foreach (var instr in instructions) { + // Don't use instr.ToString(), it allocates more, uses masm syntax and default options + formatter.Format(instr, tempOutput); + output.Write(instr.IP.ToString("X16")); + output.Write(" "); + int instrLen = instr.ByteLength; + int byteBaseIndex = (int)(instr.IP - exampleCodeRIP); + 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++) + output.Write(" "); + output.Write(" "); + output.WriteLine(tempOutput.ToStringAndReset()); + } + } + + private class R2RAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver + { + public bool Naked => false; + + public bool SignatureBinary => false; + + public bool InlineSignatureBinary => false; + + public string FindAssembly(string name, string filename) + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/ILSpy.sln b/ILSpy.sln index 58bdc2b3e1..1445db3f9b 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.PdbP EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.Tests", "ILSpy.Tests\ILSpy.Tests.csproj", "{B51C6636-B8D1-4200-9869-08F2689DE6C2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.ReadyToRun", "ILSpy.ReadyToRun\ILSpy.ReadyToRun.csproj", "{0313F581-C63B-43BB-AA9B-07615DABD8A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +81,10 @@ Global {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {B51C6636-B8D1-4200-9869-08F2689DE6C2}.Release|Any CPU.Build.0 = Release|Any CPU + {0313F581-C63B-43BB-AA9B-07615DABD8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0313F581-C63B-43BB-AA9B-07615DABD8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0313F581-C63B-43BB-AA9B-07615DABD8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0313F581-C63B-43BB-AA9B-07615DABD8A3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE