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