mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
7.8 KiB
251 lines
7.8 KiB
// 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. |
|
|
|
// #define STRESS |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.ComponentModel.Composition; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.PortableExecutable; |
|
using System.Runtime.CompilerServices; |
|
|
|
using ICSharpCode.AvalonEdit.Highlighting; |
|
using ICSharpCode.Decompiler; |
|
using ICSharpCode.Decompiler.Disassembler; |
|
using ICSharpCode.Decompiler.Metadata; |
|
using ICSharpCode.Decompiler.Solution; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.ILSpyX; |
|
|
|
using ILCompiler.Reflection.ReadyToRun; |
|
|
|
namespace ICSharpCode.ILSpy.ReadyToRun |
|
{ |
|
#if STRESS |
|
class DummyOutput : ITextOutput |
|
{ |
|
public string IndentationString { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } |
|
|
|
public void Indent() |
|
{ |
|
} |
|
|
|
public void MarkFoldEnd() |
|
{ |
|
} |
|
|
|
public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false) |
|
{ |
|
} |
|
|
|
public void Unindent() |
|
{ |
|
} |
|
|
|
public void Write(char ch) |
|
{ |
|
} |
|
|
|
public void Write(string text) |
|
{ |
|
} |
|
|
|
public void WriteLine() |
|
{ |
|
} |
|
|
|
public void WriteLocalReference(string text, object reference, bool isDefinition = false) |
|
{ |
|
} |
|
|
|
public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false) |
|
{ |
|
} |
|
|
|
public void WriteReference(PEFile module, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) |
|
{ |
|
} |
|
|
|
public void WriteReference(IType type, string text, bool isDefinition = false) |
|
{ |
|
} |
|
|
|
public void WriteReference(IMember member, string text, bool isDefinition = false) |
|
{ |
|
} |
|
} |
|
#endif |
|
|
|
[Export(typeof(Language))] |
|
internal class ReadyToRunLanguage : Language |
|
{ |
|
private static readonly ConditionalWeakTable<PEFile, ReadyToRunReaderCacheEntry> readyToRunReaders = new ConditionalWeakTable<PEFile, ReadyToRunReaderCacheEntry>(); |
|
|
|
public override string Name => "ReadyToRun"; |
|
|
|
public override string FileExtension { |
|
get { return ".asm"; } |
|
} |
|
|
|
public override void WriteCommentLine(ITextOutput output, string comment) |
|
{ |
|
output.WriteLine("; " + comment); |
|
} |
|
|
|
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) |
|
{ |
|
PEFile module = assembly.GetPEFileAsync().GetAwaiter().GetResult(); |
|
ReadyToRunReaderCacheEntry cacheEntry = GetReader(assembly, module); |
|
if (cacheEntry.readyToRunReader == null) |
|
{ |
|
WriteCommentLine(output, cacheEntry.failureReason); |
|
} |
|
else |
|
{ |
|
ReadyToRunReader reader = cacheEntry.readyToRunReader; |
|
WriteCommentLine(output, reader.Machine.ToString()); |
|
WriteCommentLine(output, reader.OperatingSystem.ToString()); |
|
WriteCommentLine(output, reader.CompilerIdentifier); |
|
WriteCommentLine(output, "TODO - display more header information"); |
|
} |
|
|
|
return base.DecompileAssembly(assembly, output, options); |
|
} |
|
|
|
public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) |
|
{ |
|
PEFile module = method.ParentModule.PEFile; |
|
ReadyToRunReaderCacheEntry cacheEntry = GetReader(module.GetLoadedAssembly(), module); |
|
if (cacheEntry.readyToRunReader == null) |
|
{ |
|
WriteCommentLine(output, cacheEntry.failureReason); |
|
} |
|
else |
|
{ |
|
ReadyToRunReader reader = cacheEntry.readyToRunReader; |
|
int bitness = -1; |
|
if (reader.Machine == Machine.Amd64) |
|
{ |
|
bitness = 64; |
|
} |
|
else |
|
{ |
|
Debug.Assert(reader.Machine == Machine.I386); |
|
bitness = 32; |
|
} |
|
if (cacheEntry.methodMap == null) |
|
{ |
|
cacheEntry.methodMap = reader.Methods.ToList() |
|
.GroupBy(m => m.MethodHandle) |
|
.ToDictionary(g => g.Key, g => g.ToArray()); |
|
} |
|
var displaySettings = MainWindow.Instance.CurrentDisplaySettings; |
|
bool showMetadataTokens = displaySettings.ShowMetadataTokens; |
|
bool showMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10; |
|
#if STRESS |
|
ITextOutput originalOutput = output; |
|
output = new DummyOutput(); |
|
{ |
|
foreach (var readyToRunMethod in reader.Methods) |
|
{ |
|
#else |
|
if (cacheEntry.methodMap.TryGetValue(method.MetadataToken, out var methods)) |
|
{ |
|
foreach (var readyToRunMethod in methods) |
|
{ |
|
#endif |
|
foreach (RuntimeFunction runtimeFunction in readyToRunMethod.RuntimeFunctions) |
|
{ |
|
new ReadyToRunDisassembler(output, reader, runtimeFunction).Disassemble(method.ParentModule.PEFile, bitness, (ulong)runtimeFunction.StartAddress, showMetadataTokens, showMetadataTokensInBase10); |
|
} |
|
} |
|
} |
|
#if STRESS |
|
output = originalOutput; |
|
output.WriteLine("Passed"); |
|
#endif |
|
} |
|
} |
|
|
|
public override RichText GetRichTextTooltip(IEntity entity) |
|
{ |
|
return Languages.ILLanguage.GetRichTextTooltip(entity); |
|
} |
|
|
|
private ReadyToRunReaderCacheEntry GetReader(LoadedAssembly assembly, PEFile module) |
|
{ |
|
ReadyToRunReaderCacheEntry result; |
|
lock (readyToRunReaders) |
|
{ |
|
if (!readyToRunReaders.TryGetValue(module, out result)) |
|
{ |
|
result = new ReadyToRunReaderCacheEntry(); |
|
try |
|
{ |
|
result.readyToRunReader = new ReadyToRunReader(new ReadyToRunAssemblyResolver(assembly), new StandaloneAssemblyMetadata(module.Reader), module.Reader, module.FileName); |
|
if (result.readyToRunReader.Machine != Machine.Amd64 && result.readyToRunReader.Machine != Machine.I386) |
|
{ |
|
result.failureReason = $"Architecture {result.readyToRunReader.Machine} is not currently supported."; |
|
result.readyToRunReader = null; |
|
} |
|
} |
|
catch (BadImageFormatException e) |
|
{ |
|
result.failureReason = e.Message; |
|
} |
|
readyToRunReaders.Add(module, result); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
private class ReadyToRunAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver |
|
{ |
|
private Decompiler.Metadata.IAssemblyResolver assemblyResolver; |
|
|
|
public ReadyToRunAssemblyResolver(LoadedAssembly loadedAssembly) |
|
{ |
|
assemblyResolver = loadedAssembly.GetAssemblyResolver(); |
|
} |
|
|
|
public IAssemblyMetadata FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile) |
|
{ |
|
PEFile module = assemblyResolver.Resolve(new Decompiler.Metadata.AssemblyReference(metadataReader, assemblyReferenceHandle)); |
|
PEReader reader = module?.Reader; |
|
return reader == null ? null : new StandaloneAssemblyMetadata(reader); |
|
} |
|
|
|
public IAssemblyMetadata FindAssembly(string simpleName, string parentFile) |
|
{ |
|
// This is called only for the composite R2R scenario, |
|
// So it will never be called before the feature is released. |
|
throw new NotSupportedException("Composite R2R format is not currently supported"); |
|
} |
|
} |
|
|
|
private class ReadyToRunReaderCacheEntry |
|
{ |
|
public ReadyToRunReader readyToRunReader; |
|
public string failureReason; |
|
public Dictionary<EntityHandle, ReadyToRunMethod[]> methodMap; |
|
} |
|
} |
|
}
|
|
|