/Mono.Cecil/BaseAssemblyResolver.cs

http://github.com/jbevain/cecil · C# · 407 lines · 320 code · 75 blank · 12 comment · 81 complexity · a1f4211bf15d1bab7d181204016f265d MD5 · raw file

  1. //
  2. // Author:
  3. // Jb Evain (jbevain@gmail.com)
  4. //
  5. // Copyright (c) 2008 - 2015 Jb Evain
  6. // Copyright (c) 2008 - 2011 Novell, Inc.
  7. //
  8. // Licensed under the MIT/X11 license.
  9. //
  10. using System;
  11. using System.Collections.Generic;
  12. using System.IO;
  13. using System.Reflection;
  14. using System.Text;
  15. using Mono.Collections.Generic;
  16. namespace Mono.Cecil {
  17. public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);
  18. public sealed class AssemblyResolveEventArgs : EventArgs {
  19. readonly AssemblyNameReference reference;
  20. public AssemblyNameReference AssemblyReference {
  21. get { return reference; }
  22. }
  23. public AssemblyResolveEventArgs (AssemblyNameReference reference)
  24. {
  25. this.reference = reference;
  26. }
  27. }
  28. #if !NET_CORE
  29. [Serializable]
  30. #endif
  31. public sealed class AssemblyResolutionException : FileNotFoundException {
  32. readonly AssemblyNameReference reference;
  33. public AssemblyNameReference AssemblyReference {
  34. get { return reference; }
  35. }
  36. public AssemblyResolutionException (AssemblyNameReference reference)
  37. : this (reference, null)
  38. {
  39. }
  40. public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException)
  41. : base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException)
  42. {
  43. this.reference = reference;
  44. }
  45. #if !NET_CORE
  46. AssemblyResolutionException (
  47. System.Runtime.Serialization.SerializationInfo info,
  48. System.Runtime.Serialization.StreamingContext context)
  49. : base (info, context)
  50. {
  51. }
  52. #endif
  53. }
  54. public abstract class BaseAssemblyResolver : IAssemblyResolver {
  55. static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
  56. readonly Collection<string> directories;
  57. #if NET_CORE
  58. // Maps file names of available trusted platform assemblies to their full paths.
  59. // Internal for testing.
  60. internal static readonly Lazy<Dictionary<string, string>> TrustedPlatformAssemblies = new Lazy<Dictionary<string, string>> (CreateTrustedPlatformAssemblyMap);
  61. #else
  62. Collection<string> gac_paths;
  63. #endif
  64. public void AddSearchDirectory (string directory)
  65. {
  66. directories.Add (directory);
  67. }
  68. public void RemoveSearchDirectory (string directory)
  69. {
  70. directories.Remove (directory);
  71. }
  72. public string [] GetSearchDirectories ()
  73. {
  74. var directories = new string [this.directories.size];
  75. Array.Copy (this.directories.items, directories, directories.Length);
  76. return directories;
  77. }
  78. public event AssemblyResolveEventHandler ResolveFailure;
  79. protected BaseAssemblyResolver ()
  80. {
  81. directories = new Collection<string> (2) { ".", "bin" };
  82. }
  83. AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
  84. {
  85. if (parameters.AssemblyResolver == null)
  86. parameters.AssemblyResolver = this;
  87. return ModuleDefinition.ReadModule (file, parameters).Assembly;
  88. }
  89. public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
  90. {
  91. return Resolve (name, new ReaderParameters ());
  92. }
  93. public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
  94. {
  95. Mixin.CheckName (name);
  96. Mixin.CheckParameters (parameters);
  97. var assembly = SearchDirectory (name, directories, parameters);
  98. if (assembly != null)
  99. return assembly;
  100. if (name.IsRetargetable) {
  101. // if the reference is retargetable, zero it
  102. name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) {
  103. PublicKeyToken = Empty<byte>.Array,
  104. };
  105. }
  106. #if NET_CORE
  107. assembly = SearchTrustedPlatformAssemblies (name, parameters);
  108. if (assembly != null)
  109. return assembly;
  110. #else
  111. var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
  112. var framework_dirs = on_mono
  113. ? new [] { framework_dir, Path.Combine (framework_dir, "Facades") }
  114. : new [] { framework_dir };
  115. if (IsZero (name.Version)) {
  116. assembly = SearchDirectory (name, framework_dirs, parameters);
  117. if (assembly != null)
  118. return assembly;
  119. }
  120. if (name.Name == "mscorlib") {
  121. assembly = GetCorlib (name, parameters);
  122. if (assembly != null)
  123. return assembly;
  124. }
  125. assembly = GetAssemblyInGac (name, parameters);
  126. if (assembly != null)
  127. return assembly;
  128. assembly = SearchDirectory (name, framework_dirs, parameters);
  129. if (assembly != null)
  130. return assembly;
  131. #endif
  132. if (ResolveFailure != null) {
  133. assembly = ResolveFailure (this, name);
  134. if (assembly != null)
  135. return assembly;
  136. }
  137. throw new AssemblyResolutionException (name);
  138. }
  139. #if NET_CORE
  140. AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters)
  141. {
  142. if (name.IsWindowsRuntime)
  143. return null;
  144. if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path))
  145. return GetAssembly (path, parameters);
  146. return null;
  147. }
  148. static Dictionary<string, string> CreateTrustedPlatformAssemblyMap ()
  149. {
  150. var result = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
  151. string paths;
  152. try {
  153. paths = (string) AppDomain.CurrentDomain.GetData ("TRUSTED_PLATFORM_ASSEMBLIES");
  154. } catch {
  155. paths = null;
  156. }
  157. if (paths == null)
  158. return result;
  159. foreach (var path in paths.Split (Path.PathSeparator))
  160. if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase))
  161. result [Path.GetFileNameWithoutExtension (path)] = path;
  162. return result;
  163. }
  164. #endif
  165. protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
  166. {
  167. var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".exe", ".dll" };
  168. foreach (var directory in directories) {
  169. foreach (var extension in extensions) {
  170. string file = Path.Combine (directory, name.Name + extension);
  171. if (!File.Exists (file))
  172. continue;
  173. try {
  174. return GetAssembly (file, parameters);
  175. } catch (System.BadImageFormatException) {
  176. continue;
  177. }
  178. }
  179. }
  180. return null;
  181. }
  182. static bool IsZero (Version version)
  183. {
  184. return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
  185. }
  186. #if !NET_CORE
  187. AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
  188. {
  189. var version = reference.Version;
  190. var corlib = typeof (object).Assembly.GetName ();
  191. if (corlib.Version == version || IsZero (version))
  192. return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
  193. var path = Directory.GetParent (
  194. Directory.GetParent (
  195. typeof (object).Module.FullyQualifiedName).FullName
  196. ).FullName;
  197. if (on_mono) {
  198. if (version.Major == 1)
  199. path = Path.Combine (path, "1.0");
  200. else if (version.Major == 2) {
  201. if (version.MajorRevision == 5)
  202. path = Path.Combine (path, "2.1");
  203. else
  204. path = Path.Combine (path, "2.0");
  205. } else if (version.Major == 4)
  206. path = Path.Combine (path, "4.0");
  207. else
  208. throw new NotSupportedException ("Version not supported: " + version);
  209. } else {
  210. switch (version.Major) {
  211. case 1:
  212. if (version.MajorRevision == 3300)
  213. path = Path.Combine (path, "v1.0.3705");
  214. else
  215. path = Path.Combine (path, "v1.1.4322");
  216. break;
  217. case 2:
  218. path = Path.Combine (path, "v2.0.50727");
  219. break;
  220. case 4:
  221. path = Path.Combine (path, "v4.0.30319");
  222. break;
  223. default:
  224. throw new NotSupportedException ("Version not supported: " + version);
  225. }
  226. }
  227. var file = Path.Combine (path, "mscorlib.dll");
  228. if (File.Exists (file))
  229. return GetAssembly (file, parameters);
  230. if (on_mono && Directory.Exists (path + "-api")) {
  231. file = Path.Combine (path + "-api", "mscorlib.dll");
  232. if (File.Exists (file))
  233. return GetAssembly (file, parameters);
  234. }
  235. return null;
  236. }
  237. static Collection<string> GetGacPaths ()
  238. {
  239. if (on_mono)
  240. return GetDefaultMonoGacPaths ();
  241. var paths = new Collection<string> (2);
  242. var windir = Environment.GetEnvironmentVariable ("WINDIR");
  243. if (windir == null)
  244. return paths;
  245. paths.Add (Path.Combine (windir, "assembly"));
  246. paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
  247. return paths;
  248. }
  249. static Collection<string> GetDefaultMonoGacPaths ()
  250. {
  251. var paths = new Collection<string> (1);
  252. var gac = GetCurrentMonoGac ();
  253. if (gac != null)
  254. paths.Add (gac);
  255. var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
  256. if (string.IsNullOrEmpty (gac_paths_env))
  257. return paths;
  258. var prefixes = gac_paths_env.Split (Path.PathSeparator);
  259. foreach (var prefix in prefixes) {
  260. if (string.IsNullOrEmpty (prefix))
  261. continue;
  262. var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
  263. if (Directory.Exists (gac_path) && !paths.Contains (gac))
  264. paths.Add (gac_path);
  265. }
  266. return paths;
  267. }
  268. static string GetCurrentMonoGac ()
  269. {
  270. return Path.Combine (
  271. Directory.GetParent (
  272. Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
  273. "gac");
  274. }
  275. AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
  276. {
  277. if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
  278. return null;
  279. if (gac_paths == null)
  280. gac_paths = GetGacPaths ();
  281. if (on_mono)
  282. return GetAssemblyInMonoGac (reference, parameters);
  283. return GetAssemblyInNetGac (reference, parameters);
  284. }
  285. AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
  286. {
  287. for (int i = 0; i < gac_paths.Count; i++) {
  288. var gac_path = gac_paths [i];
  289. var file = GetAssemblyFile (reference, string.Empty, gac_path);
  290. if (File.Exists (file))
  291. return GetAssembly (file, parameters);
  292. }
  293. return null;
  294. }
  295. AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
  296. {
  297. var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
  298. var prefixes = new [] { string.Empty, "v4.0_" };
  299. for (int i = 0; i < gac_paths.Count; i++) {
  300. for (int j = 0; j < gacs.Length; j++) {
  301. var gac = Path.Combine (gac_paths [i], gacs [j]);
  302. var file = GetAssemblyFile (reference, prefixes [i], gac);
  303. if (Directory.Exists (gac) && File.Exists (file))
  304. return GetAssembly (file, parameters);
  305. }
  306. }
  307. return null;
  308. }
  309. static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
  310. {
  311. var gac_folder = new StringBuilder ()
  312. .Append (prefix)
  313. .Append (reference.Version)
  314. .Append ("__");
  315. for (int i = 0; i < reference.PublicKeyToken.Length; i++)
  316. gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
  317. return Path.Combine (
  318. Path.Combine (
  319. Path.Combine (gac, reference.Name), gac_folder.ToString ()),
  320. reference.Name + ".dll");
  321. }
  322. #endif
  323. public void Dispose ()
  324. {
  325. Dispose (true);
  326. GC.SuppressFinalize (this);
  327. }
  328. protected virtual void Dispose (bool disposing)
  329. {
  330. }
  331. }
  332. }