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