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.

307 lines
9.6 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. // Copyright (c) 2018 Siegfried Pammer
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. // #define STRESS
  19. using System;
  20. using System.Collections.Generic;
  21. using System.ComponentModel.Composition;
  22. using System.Diagnostics;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Reflection.Metadata;
  26. using System.Reflection.PortableExecutable;
  27. using System.Runtime.CompilerServices;
  28. using ICSharpCode.AvalonEdit.Highlighting;
  29. using ICSharpCode.Decompiler;
  30. using ICSharpCode.Decompiler.Disassembler;
  31. using ICSharpCode.Decompiler.Metadata;
  32. using ICSharpCode.Decompiler.Solution;
  33. using ICSharpCode.Decompiler.TypeSystem;
  34. using ICSharpCode.ILSpyX;
  35. using ILCompiler.Reflection.ReadyToRun;
  36. namespace ICSharpCode.ILSpy.ReadyToRun
  37. {
  38. #if STRESS
  39. class DummyOutput : ITextOutput
  40. {
  41. public string IndentationString { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
  42. public void Indent()
  43. {
  44. }
  45. public void MarkFoldEnd()
  46. {
  47. }
  48. public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false)
  49. {
  50. }
  51. public void Unindent()
  52. {
  53. }
  54. public void Write(char ch)
  55. {
  56. }
  57. public void Write(string text)
  58. {
  59. }
  60. public void WriteLine()
  61. {
  62. }
  63. public void WriteLocalReference(string text, object reference, bool isDefinition = false)
  64. {
  65. }
  66. public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false)
  67. {
  68. }
  69. public void WriteReference(PEFile module, Handle handle, string text, string protocol = "decompile", bool isDefinition = false)
  70. {
  71. }
  72. public void WriteReference(IType type, string text, bool isDefinition = false)
  73. {
  74. }
  75. public void WriteReference(IMember member, string text, bool isDefinition = false)
  76. {
  77. }
  78. }
  79. #endif
  80. [Export(typeof(Language))]
  81. internal class ReadyToRunLanguage : Language
  82. {
  83. private static readonly ConditionalWeakTable<PEFile, ReadyToRunReaderCacheEntry> readyToRunReaders = new ConditionalWeakTable<PEFile, ReadyToRunReaderCacheEntry>();
  84. public override string Name => "ReadyToRun";
  85. public override string FileExtension {
  86. get { return ".asm"; }
  87. }
  88. public override void WriteCommentLine(ITextOutput output, string comment)
  89. {
  90. output.WriteLine("; " + comment);
  91. }
  92. public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
  93. {
  94. PEFile module = assembly.GetPEFileAsync().GetAwaiter().GetResult();
  95. ReadyToRunReaderCacheEntry cacheEntry = GetReader(assembly, module);
  96. if (cacheEntry.readyToRunReader == null)
  97. {
  98. WriteCommentLine(output, cacheEntry.failureReason);
  99. }
  100. else
  101. {
  102. ReadyToRunReader reader = cacheEntry.readyToRunReader;
  103. WriteCommentLine(output, $"Machine : {reader.Machine}");
  104. WriteCommentLine(output, $"OperatingSystem : {reader.OperatingSystem}");
  105. WriteCommentLine(output, $"CompilerIdentifier : {reader.CompilerIdentifier}");
  106. if (reader.OwnerCompositeExecutable != null)
  107. {
  108. WriteCommentLine(output, $"OwnerCompositeExecutable : {reader.OwnerCompositeExecutable}");
  109. }
  110. }
  111. return base.DecompileAssembly(assembly, output, options);
  112. }
  113. public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options)
  114. {
  115. PEFile module = method.ParentModule.PEFile;
  116. ReadyToRunReaderCacheEntry cacheEntry = GetReader(module.GetLoadedAssembly(), module);
  117. if (cacheEntry.readyToRunReader == null)
  118. {
  119. WriteCommentLine(output, cacheEntry.failureReason);
  120. }
  121. else
  122. {
  123. ReadyToRunReader reader = cacheEntry.readyToRunReader;
  124. int bitness = -1;
  125. if (reader.Machine == Machine.Amd64)
  126. {
  127. bitness = 64;
  128. }
  129. else
  130. {
  131. Debug.Assert(reader.Machine == Machine.I386);
  132. bitness = 32;
  133. }
  134. if (cacheEntry.methodMap == null)
  135. {
  136. IEnumerable<ReadyToRunMethod> readyToRunMethods = null;
  137. if (cacheEntry.compositeReadyToRunReader == null)
  138. {
  139. readyToRunMethods = reader.Methods;
  140. }
  141. else
  142. {
  143. readyToRunMethods = cacheEntry.compositeReadyToRunReader.Methods
  144. .Where(m => {
  145. MetadataReader mr = m.ComponentReader.MetadataReader;
  146. return string.Equals(mr.GetString(mr.GetAssemblyDefinition().Name), method.ParentModule.Name, StringComparison.OrdinalIgnoreCase);
  147. });
  148. }
  149. cacheEntry.methodMap = readyToRunMethods.ToList()
  150. .GroupBy(m => m.MethodHandle)
  151. .ToDictionary(g => g.Key, g => g.ToArray());
  152. }
  153. var displaySettings = MainWindow.Instance.CurrentDisplaySettings;
  154. bool showMetadataTokens = displaySettings.ShowMetadataTokens;
  155. bool showMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10;
  156. #if STRESS
  157. ITextOutput originalOutput = output;
  158. output = new DummyOutput();
  159. {
  160. foreach (var readyToRunMethod in reader.Methods)
  161. {
  162. #else
  163. if (cacheEntry.methodMap.TryGetValue(method.MetadataToken, out var methods))
  164. {
  165. foreach (var readyToRunMethod in methods)
  166. {
  167. #endif
  168. foreach (RuntimeFunction runtimeFunction in readyToRunMethod.RuntimeFunctions)
  169. {
  170. PEFile file = null;
  171. ReadyToRunReader disassemblingReader = null;
  172. if (cacheEntry.compositeReadyToRunReader == null)
  173. {
  174. disassemblingReader = reader;
  175. file = method.ParentModule.PEFile;
  176. }
  177. else
  178. {
  179. disassemblingReader = cacheEntry.compositeReadyToRunReader;
  180. file = ((IlSpyAssemblyMetadata)readyToRunMethod.ComponentReader).Module;
  181. }
  182. new ReadyToRunDisassembler(output, disassemblingReader, runtimeFunction).Disassemble(file, bitness, (ulong)runtimeFunction.StartAddress, showMetadataTokens, showMetadataTokensInBase10);
  183. }
  184. }
  185. }
  186. #if STRESS
  187. output = originalOutput;
  188. output.WriteLine("Passed");
  189. #endif
  190. }
  191. }
  192. public override RichText GetRichTextTooltip(IEntity entity)
  193. {
  194. return Languages.ILLanguage.GetRichTextTooltip(entity);
  195. }
  196. private ReadyToRunReaderCacheEntry GetReader(LoadedAssembly assembly, PEFile module)
  197. {
  198. ReadyToRunReaderCacheEntry result;
  199. lock (readyToRunReaders)
  200. {
  201. if (!readyToRunReaders.TryGetValue(module, out result))
  202. {
  203. result = new ReadyToRunReaderCacheEntry();
  204. try
  205. {
  206. result.readyToRunReader = new ReadyToRunReader(new ReadyToRunAssemblyResolver(assembly), new StandaloneAssemblyMetadata(module.Reader), module.Reader, module.FileName);
  207. if (result.readyToRunReader.Machine != Machine.Amd64 && result.readyToRunReader.Machine != Machine.I386)
  208. {
  209. result.failureReason = $"Architecture {result.readyToRunReader.Machine} is not currently supported.";
  210. result.readyToRunReader = null;
  211. }
  212. else if (result.readyToRunReader.OwnerCompositeExecutable != null)
  213. {
  214. string compositePath = Path.Combine(Path.GetDirectoryName(module.FileName), result.readyToRunReader.OwnerCompositeExecutable);
  215. result.compositeReadyToRunReader = new ReadyToRunReader(new ReadyToRunAssemblyResolver(assembly), compositePath);
  216. }
  217. }
  218. catch (BadImageFormatException e)
  219. {
  220. result.failureReason = e.Message;
  221. }
  222. readyToRunReaders.Add(module, result);
  223. }
  224. }
  225. return result;
  226. }
  227. private class ReadyToRunAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver
  228. {
  229. private LoadedAssembly loadedAssembly;
  230. private Decompiler.Metadata.IAssemblyResolver assemblyResolver;
  231. public ReadyToRunAssemblyResolver(LoadedAssembly loadedAssembly)
  232. {
  233. this.loadedAssembly = loadedAssembly;
  234. assemblyResolver = loadedAssembly.GetAssemblyResolver();
  235. }
  236. public IAssemblyMetadata FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile)
  237. {
  238. return GetAssemblyMetadata(assemblyResolver.Resolve(new Decompiler.Metadata.AssemblyReference(metadataReader, assemblyReferenceHandle)));
  239. }
  240. public IAssemblyMetadata FindAssembly(string simpleName, string parentFile)
  241. {
  242. return GetAssemblyMetadata(assemblyResolver.ResolveModule(loadedAssembly.GetPEFileOrNull(), simpleName + ".dll"));
  243. }
  244. private IAssemblyMetadata GetAssemblyMetadata(PEFile module)
  245. {
  246. if (module.Reader == null)
  247. {
  248. return null;
  249. }
  250. else
  251. {
  252. return new IlSpyAssemblyMetadata(module);
  253. }
  254. }
  255. }
  256. private class IlSpyAssemblyMetadata : StandaloneAssemblyMetadata
  257. {
  258. public PEFile Module { get; private set; }
  259. public IlSpyAssemblyMetadata(PEFile module) : base(module.Reader)
  260. {
  261. Module = module;
  262. }
  263. }
  264. private class ReadyToRunReaderCacheEntry
  265. {
  266. public ReadyToRunReader readyToRunReader;
  267. public ReadyToRunReader compositeReadyToRunReader;
  268. public string failureReason;
  269. public Dictionary<EntityHandle, ReadyToRunMethod[]> methodMap;
  270. }
  271. }
  272. }