[loader] Use appdomain base dir and loadfrom asmctx hooks
[mono-project.git] / mono / metadata / assembly.c
blob87bbec07e2dd27d618a22a4af43e7b77d6a6dd7c
1 /**
2 * \file
3 * Routines for loading assemblies.
4 *
5 * Author:
6 * Miguel de Icaza (miguel@ximian.com)
8 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
14 #include <stdio.h>
15 #include <glib.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include "assembly.h"
20 #include "assembly-internals.h"
21 #include "image.h"
22 #include "image-internals.h"
23 #include "object-internals.h"
24 #include <mono/metadata/loader.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/custom-attrs-internals.h>
27 #include <mono/metadata/metadata-internals.h>
28 #include <mono/metadata/profiler-private.h>
29 #include <mono/metadata/class-internals.h>
30 #include <mono/metadata/domain-internals.h>
31 #include <mono/metadata/reflection-internals.h>
32 #include <mono/metadata/mono-endian.h>
33 #include <mono/metadata/mono-debug.h>
34 #include <mono/utils/mono-uri.h>
35 #include <mono/metadata/mono-config.h>
36 #include <mono/metadata/mono-config-dirs.h>
37 #include <mono/utils/mono-digest.h>
38 #include <mono/utils/mono-logger-internals.h>
39 #include <mono/utils/mono-path.h>
40 #include <mono/metadata/reflection.h>
41 #include <mono/metadata/coree.h>
42 #include <mono/metadata/cil-coff.h>
43 #include <mono/utils/mono-io-portability.h>
44 #include <mono/utils/atomic.h>
45 #include <mono/utils/mono-os-mutex.h>
47 #ifndef HOST_WIN32
48 #include <sys/types.h>
49 #include <unistd.h>
50 #include <sys/stat.h>
51 #endif
53 #ifdef HOST_DARWIN
54 #include <mach-o/dyld.h>
55 #endif
57 /* AssemblyVersionMap: an assembly name, the assembly version set on which it is based, the assembly name it is replaced with and whether only versions lower than the current runtime version should be remapped */
58 typedef struct {
59 const char* assembly_name;
60 guint8 version_set_index;
61 const char* new_assembly_name;
62 gboolean only_lower_versions;
63 gboolean framework_facade_assembly;
64 } AssemblyVersionMap;
66 /* the default search path is empty, the first slot is replaced with the computed value */
67 static const char*
68 default_path [] = {
69 NULL,
70 NULL,
71 NULL
74 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
75 static char **assemblies_path = NULL;
77 /* Contains the list of directories that point to auxiliary GACs */
78 static char **extra_gac_paths = NULL;
80 #ifndef DISABLE_DESKTOP_LOADER
82 #define FACADE_ASSEMBLY(str) {str, 0, NULL, FALSE, TRUE}
84 static GHashTable* assembly_remapping_table;
85 /* The list of system assemblies what will be remapped to the running
86 * runtime version.
87 * This list is stored in @assembly_remapping_table during initialization.
88 * Keep it sorted just to make maintenance easier.
90 * The integer number is an index in the MonoRuntimeInfo structure, whose
91 * values can be found in domain.c - supported_runtimes. Look there
92 * to understand what remapping will be made.
94 * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99
97 static const AssemblyVersionMap framework_assemblies [] = {
98 {"Accessibility", 0},
99 {"Commons.Xml.Relaxng", 0},
100 {"I18N", 0},
101 {"I18N.CJK", 0},
102 {"I18N.MidEast", 0},
103 {"I18N.Other", 0},
104 {"I18N.Rare", 0},
105 {"I18N.West", 0},
106 {"Microsoft.Build.Engine", 2, NULL, TRUE},
107 {"Microsoft.Build.Framework", 2, NULL, TRUE},
108 {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
109 {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
110 {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
111 {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
112 {"Microsoft.VisualBasic", 1},
113 {"Microsoft.VisualC", 1},
114 FACADE_ASSEMBLY ("Microsoft.Win32.Primitives"),
115 FACADE_ASSEMBLY ("Microsoft.Win32.Registry"),
116 FACADE_ASSEMBLY ("Microsoft.Win32.Registry.AccessControl"),
117 {"Mono.Cairo", 0},
118 {"Mono.CompilerServices.SymbolWriter", 0},
119 {"Mono.Data", 0},
120 {"Mono.Data.SybaseClient", 0},
121 {"Mono.Data.Tds", 0},
122 {"Mono.Data.TdsClient", 0},
123 {"Mono.GetOptions", 0},
124 {"Mono.Http", 0},
125 {"Mono.Posix", 0},
126 {"Mono.Security", 0},
127 {"Mono.Security.Win32", 0},
128 {"Mono.Xml.Ext", 0},
129 {"Novell.Directory.Ldap", 0},
130 {"PEAPI", 0},
131 {"System", 0},
132 FACADE_ASSEMBLY ("System.AppContext"),
133 FACADE_ASSEMBLY ("System.Collections"),
134 FACADE_ASSEMBLY ("System.Collections.Concurrent"),
135 FACADE_ASSEMBLY ("System.Collections.NonGeneric"),
136 FACADE_ASSEMBLY ("System.Collections.Specialized"),
137 FACADE_ASSEMBLY ("System.ComponentModel"),
138 FACADE_ASSEMBLY ("System.ComponentModel.Annotations"),
139 {"System.ComponentModel.Composition", 2},
140 {"System.ComponentModel.DataAnnotations", 2},
141 FACADE_ASSEMBLY ("System.ComponentModel.EventBasedAsync"),
142 FACADE_ASSEMBLY ("System.ComponentModel.Primitives"),
143 FACADE_ASSEMBLY ("System.ComponentModel.TypeConverter"),
144 {"System.Configuration", 0},
145 {"System.Configuration.Install", 0},
146 FACADE_ASSEMBLY ("System.Console"),
147 {"System.Core", 2},
148 {"System.Data", 0},
149 FACADE_ASSEMBLY ("System.Data.Common"),
150 {"System.Data.Linq", 2},
151 {"System.Data.OracleClient", 0},
152 {"System.Data.Services", 2},
153 {"System.Data.Services.Client", 2},
154 FACADE_ASSEMBLY ("System.Data.SqlClient"),
155 {"System.Data.SqlXml", 0},
156 {"System.Design", 0},
157 FACADE_ASSEMBLY ("System.Diagnostics.Contracts"),
158 FACADE_ASSEMBLY ("System.Diagnostics.Debug"),
159 FACADE_ASSEMBLY ("System.Diagnostics.FileVersionInfo"),
160 FACADE_ASSEMBLY ("System.Diagnostics.Process"),
161 FACADE_ASSEMBLY ("System.Diagnostics.StackTrace"),
162 FACADE_ASSEMBLY ("System.Diagnostics.TextWriterTraceListener"),
163 FACADE_ASSEMBLY ("System.Diagnostics.Tools"),
164 FACADE_ASSEMBLY ("System.Diagnostics.TraceEvent"),
165 FACADE_ASSEMBLY ("System.Diagnostics.TraceSource"),
166 FACADE_ASSEMBLY ("System.Diagnostics.Tracing"),
167 {"System.DirectoryServices", 0},
168 {"System.Drawing", 0},
169 {"System.Drawing.Design", 0},
170 FACADE_ASSEMBLY ("System.Drawing.Primitives"),
171 FACADE_ASSEMBLY ("System.Dynamic.Runtime"),
172 {"System.EnterpriseServices", 0},
173 FACADE_ASSEMBLY ("System.Globalization"),
174 FACADE_ASSEMBLY ("System.Globalization.Calendars"),
175 FACADE_ASSEMBLY ("System.Globalization.Extensions"),
176 {"System.IdentityModel", 3},
177 {"System.IdentityModel.Selectors", 3},
178 FACADE_ASSEMBLY ("System.IO"),
179 {"System.IO.Compression", 2},
180 FACADE_ASSEMBLY ("System.IO.Compression.ZipFile"),
181 FACADE_ASSEMBLY ("System.IO.FileSystem"),
182 FACADE_ASSEMBLY ("System.IO.FileSystem.AccessControl"),
183 FACADE_ASSEMBLY ("System.IO.FileSystem.DriveInfo"),
184 FACADE_ASSEMBLY ("System.IO.FileSystem.Primitives"),
185 FACADE_ASSEMBLY ("System.IO.FileSystem.Watcher"),
186 FACADE_ASSEMBLY ("System.IO.IsolatedStorage"),
187 FACADE_ASSEMBLY ("System.IO.MemoryMappedFiles"),
188 FACADE_ASSEMBLY ("System.IO.Packaging"),
189 FACADE_ASSEMBLY ("System.IO.Pipes"),
190 FACADE_ASSEMBLY ("System.IO.UnmanagedMemoryStream"),
191 FACADE_ASSEMBLY ("System.Linq"),
192 FACADE_ASSEMBLY ("System.Linq.Expressions"),
193 FACADE_ASSEMBLY ("System.Linq.Parallel"),
194 FACADE_ASSEMBLY ("System.Linq.Queryable"),
195 {"System.Management", 0},
196 {"System.Messaging", 0},
197 {"System.Net", 2},
198 FACADE_ASSEMBLY ("System.Net.AuthenticationManager"),
199 FACADE_ASSEMBLY ("System.Net.Cache"),
200 {"System.Net.Http", 4},
201 {"System.Net.Http.Rtc", 0},
202 FACADE_ASSEMBLY ("System.Net.HttpListener"),
203 FACADE_ASSEMBLY ("System.Net.Mail"),
204 FACADE_ASSEMBLY ("System.Net.NameResolution"),
205 FACADE_ASSEMBLY ("System.Net.NetworkInformation"),
206 FACADE_ASSEMBLY ("System.Net.Ping"),
207 FACADE_ASSEMBLY ("System.Net.Primitives"),
208 FACADE_ASSEMBLY ("System.Net.Requests"),
209 FACADE_ASSEMBLY ("System.Net.Security"),
210 FACADE_ASSEMBLY ("System.Net.ServicePoint"),
211 FACADE_ASSEMBLY ("System.Net.Sockets"),
212 FACADE_ASSEMBLY ("System.Net.Utilities"),
213 FACADE_ASSEMBLY ("System.Net.WebHeaderCollection"),
214 FACADE_ASSEMBLY ("System.Net.WebSockets"),
215 FACADE_ASSEMBLY ("System.Net.WebSockets.Client"),
216 {"System.Numerics", 3},
217 {"System.Numerics.Vectors", 3},
218 FACADE_ASSEMBLY ("System.ObjectModel"),
219 FACADE_ASSEMBLY ("System.Reflection"),
220 FACADE_ASSEMBLY ("System.Reflection.DispatchProxy"),
221 FACADE_ASSEMBLY ("System.Reflection.Emit"),
222 FACADE_ASSEMBLY ("System.Reflection.Emit.ILGeneration"),
223 FACADE_ASSEMBLY ("System.Reflection.Emit.Lightweight"),
224 FACADE_ASSEMBLY ("System.Reflection.Extensions"),
225 FACADE_ASSEMBLY ("System.Reflection.Primitives"),
226 FACADE_ASSEMBLY ("System.Reflection.TypeExtensions"),
227 FACADE_ASSEMBLY ("System.Resources.ReaderWriter"),
228 FACADE_ASSEMBLY ("System.Resources.ResourceManager"),
229 FACADE_ASSEMBLY ("System.Runtime"),
230 FACADE_ASSEMBLY ("System.Runtime.CompilerServices.VisualC"),
231 FACADE_ASSEMBLY ("System.Runtime.Extensions"),
232 FACADE_ASSEMBLY ("System.Runtime.Handles"),
233 FACADE_ASSEMBLY ("System.Runtime.InteropServices"),
234 FACADE_ASSEMBLY ("System.Runtime.InteropServices.RuntimeInformation"),
235 FACADE_ASSEMBLY ("System.Runtime.InteropServices.WindowsRuntime"),
236 FACADE_ASSEMBLY ("System.Runtime.Loader"),
237 FACADE_ASSEMBLY ("System.Runtime.Numerics"),
238 {"System.Runtime.Remoting", 0},
239 {"System.Runtime.Serialization", 3},
240 FACADE_ASSEMBLY ("System.Runtime.Serialization.Formatters"),
241 {"System.Runtime.Serialization.Formatters.Soap", 0},
242 FACADE_ASSEMBLY ("System.Runtime.Serialization.Json"),
243 FACADE_ASSEMBLY ("System.Runtime.Serialization.Primitives"),
244 FACADE_ASSEMBLY ("System.Runtime.Serialization.Xml"),
245 {"System.Security", 0},
246 FACADE_ASSEMBLY ("System.Security.AccessControl"),
247 FACADE_ASSEMBLY ("System.Security.Claims"),
248 FACADE_ASSEMBLY ("System.Security.Cryptography.Algorithms"),
249 FACADE_ASSEMBLY ("System.Security.Cryptography.Cng"),
250 FACADE_ASSEMBLY ("System.Security.Cryptography.Csp"),
251 FACADE_ASSEMBLY ("System.Security.Cryptography.DeriveBytes"),
252 FACADE_ASSEMBLY ("System.Security.Cryptography.Encoding"),
253 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption"),
254 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Aes"),
255 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDiffieHellman"),
256 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.ECDsa"),
257 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Hashing"),
258 FACADE_ASSEMBLY ("System.Security.Cryptography.Encryption.Hashing.Algorithms"),
259 FACADE_ASSEMBLY ("System.Security.Cryptography.OpenSsl"),
260 FACADE_ASSEMBLY ("System.Security.Cryptography.Pkcs"),
261 FACADE_ASSEMBLY ("System.Security.Cryptography.Primitives"),
262 FACADE_ASSEMBLY ("System.Security.Cryptography.ProtectedData"),
263 FACADE_ASSEMBLY ("System.Security.Cryptography.RSA"),
264 FACADE_ASSEMBLY ("System.Security.Cryptography.RandomNumberGenerator"),
265 FACADE_ASSEMBLY ("System.Security.Cryptography.X509Certificates"),
266 FACADE_ASSEMBLY ("System.Security.Principal"),
267 FACADE_ASSEMBLY ("System.Security.Principal.Windows"),
268 FACADE_ASSEMBLY ("System.Security.SecureString"),
269 {"System.ServiceModel", 3},
270 FACADE_ASSEMBLY ("System.ServiceModel.Duplex"),
271 FACADE_ASSEMBLY ("System.ServiceModel.Http"),
272 FACADE_ASSEMBLY ("System.ServiceModel.NetTcp"),
273 FACADE_ASSEMBLY ("System.ServiceModel.Primitives"),
274 FACADE_ASSEMBLY ("System.ServiceModel.Security"),
275 {"System.ServiceModel.Web", 2},
276 {"System.ServiceProcess", 0},
277 FACADE_ASSEMBLY ("System.ServiceProcess.ServiceController"),
278 FACADE_ASSEMBLY ("System.Text.Encoding"),
279 FACADE_ASSEMBLY ("System.Text.Encoding.CodePages"),
280 FACADE_ASSEMBLY ("System.Text.Encoding.Extensions"),
281 FACADE_ASSEMBLY ("System.Text.RegularExpressions"),
282 FACADE_ASSEMBLY ("System.Threading"),
283 FACADE_ASSEMBLY ("System.Threading.AccessControl"),
284 FACADE_ASSEMBLY ("System.Threading.Overlapped"),
285 FACADE_ASSEMBLY ("System.Threading.Tasks"),
286 FACADE_ASSEMBLY ("System.Threading.Tasks.Parallel"),
287 FACADE_ASSEMBLY ("System.Threading.Thread"),
288 FACADE_ASSEMBLY ("System.Threading.ThreadPool"),
289 FACADE_ASSEMBLY ("System.Threading.Timer"),
290 {"System.Transactions", 0},
291 FACADE_ASSEMBLY ("System.ValueTuple"),
292 {"System.Web", 0},
293 {"System.Web.Abstractions", 2},
294 {"System.Web.DynamicData", 2},
295 {"System.Web.Extensions", 2},
296 {"System.Web.Mobile", 0},
297 {"System.Web.Routing", 2},
298 {"System.Web.Services", 0},
299 {"System.Windows", 0},
300 {"System.Windows.Forms", 0},
301 {"System.Xml", 0},
302 {"System.Xml.Linq", 2},
303 FACADE_ASSEMBLY ("System.Xml.ReaderWriter"),
304 {"System.Xml.Serialization", 0},
305 FACADE_ASSEMBLY ("System.Xml.XDocument"),
306 FACADE_ASSEMBLY ("System.Xml.XPath"),
307 FACADE_ASSEMBLY ("System.Xml.XPath.XmlDocument"),
308 FACADE_ASSEMBLY ("System.Xml.XPath.XDocument"),
309 FACADE_ASSEMBLY ("System.Xml.XmlDocument"),
310 FACADE_ASSEMBLY ("System.Xml.XmlSerializer"),
311 FACADE_ASSEMBLY ("System.Xml.Xsl.Primitives"),
312 {"WindowsBase", 3},
313 {"mscorlib", 0},
314 FACADE_ASSEMBLY ("netstandard"),
316 #endif
319 * keeps track of loaded assemblies
321 static GList *loaded_assemblies = NULL;
322 static MonoAssembly *corlib;
324 static char* unquote (const char *str);
326 /* This protects loaded_assemblies and image->references */
327 #define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex)
328 #define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex)
329 static mono_mutex_t assemblies_mutex;
331 /* If defined, points to the bundled assembly information */
332 static const MonoBundledAssembly **bundles;
334 static mono_mutex_t assembly_binding_mutex;
336 /* Loaded assembly binding info */
337 static GSList *loaded_assembly_bindings = NULL;
339 /* Class lazy loading functions */
340 static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute")
341 static MonoAssembly*
342 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload);
343 static MonoAssembly*
344 mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status);
345 static MonoAssembly*
346 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname, const char *basedir, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status);
347 static MonoAssembly*
348 mono_assembly_load_full_nodomain (MonoAssemblyName *aname, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status);
349 static MonoAssembly*
350 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *status);
351 static MonoAssembly*
352 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status);
354 static MonoBoolean
355 mono_assembly_is_in_gac (const gchar *filanem);
356 static MonoAssemblyName*
357 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name);
359 static MonoAssembly*
360 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly);
362 /* Assembly name matching */
363 static gboolean
364 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
365 static gboolean
366 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name);
368 static const char *
369 mono_asmctx_get_name (const MonoAssemblyContext *asmctx);
371 static gboolean
372 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly, gpointer user_data, MonoAssemblyContextKind *out_asmctx);
374 static gchar*
375 encode_public_tok (const guchar *token, gint32 len)
377 const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
378 gchar *res;
379 int i;
381 res = (gchar *)g_malloc (len * 2 + 1);
382 for (i = 0; i < len; i++) {
383 res [i * 2] = allowed [token [i] >> 4];
384 res [i * 2 + 1] = allowed [token [i] & 0xF];
386 res [len * 2] = 0;
387 return res;
391 * mono_public_tokens_are_equal:
392 * \param pubt1 first public key token
393 * \param pubt2 second public key token
395 * Compare two public key tokens and return TRUE is they are equal and FALSE
396 * otherwise.
398 gboolean
399 mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
401 return memcmp (pubt1, pubt2, 16) == 0;
405 * mono_set_assemblies_path:
406 * \param path list of paths that contain directories where Mono will look for assemblies
408 * Use this method to override the standard assembly lookup system and
409 * override any assemblies coming from the GAC. This is the method
410 * that supports the \c MONO_PATH variable.
412 * Notice that \c MONO_PATH and this method are really a very bad idea as
413 * it prevents the GAC from working and it prevents the standard
414 * resolution mechanisms from working. Nonetheless, for some debugging
415 * situations and bootstrapping setups, this is useful to have.
417 void
418 mono_set_assemblies_path (const char* path)
420 char **splitted, **dest;
422 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
423 if (assemblies_path)
424 g_strfreev (assemblies_path);
425 assemblies_path = dest = splitted;
426 while (*splitted) {
427 char *tmp = *splitted;
428 if (*tmp)
429 *dest++ = mono_path_canonicalize (tmp);
430 g_free (tmp);
431 splitted++;
433 *dest = *splitted;
435 if (g_hasenv ("MONO_DEBUG"))
436 return;
438 splitted = assemblies_path;
439 while (*splitted) {
440 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
441 g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
443 splitted++;
447 static void
448 check_path_env (void)
450 if (assemblies_path != NULL)
451 return;
453 char* path = g_getenv ("MONO_PATH");
454 if (!path)
455 return;
457 mono_set_assemblies_path(path);
458 g_free (path);
461 static void
462 check_extra_gac_path_env (void)
464 char *path;
465 char **splitted, **dest;
467 path = g_getenv ("MONO_GAC_PREFIX");
468 if (!path)
469 return;
471 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
472 g_free (path);
474 if (extra_gac_paths)
475 g_strfreev (extra_gac_paths);
476 extra_gac_paths = dest = splitted;
477 while (*splitted){
478 if (**splitted)
479 *dest++ = *splitted;
480 splitted++;
482 *dest = *splitted;
484 if (!g_hasenv ("MONO_DEBUG"))
485 return;
487 while (*splitted) {
488 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
489 g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted);
491 splitted++;
495 static gboolean
496 assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
498 if (!info || !info->name)
499 return FALSE;
501 if (strcmp (info->name, aname->name))
502 return FALSE;
504 if (info->major != aname->major || info->minor != aname->minor)
505 return FALSE;
507 if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0]))
508 return FALSE;
510 if (info->culture && aname->culture && strcmp (info->culture, aname->culture))
511 return FALSE;
513 if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
514 return FALSE;
516 return TRUE;
519 static void
520 mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
522 if (!info)
523 return;
525 g_free (info->name);
526 g_free (info->culture);
529 static void
530 get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info)
532 MonoTableInfo *t;
533 guint32 cols [MONO_MANIFEST_SIZE];
534 const gchar *filename;
535 gchar *subpath, *fullpath;
537 t = &image->tables [MONO_TABLE_MANIFESTRESOURCE];
538 /* MS Impl. accepts policy assemblies with more than
539 * one manifest resource, and only takes the first one */
540 if (t->rows < 1) {
541 binding_info->is_valid = FALSE;
542 return;
545 mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE);
546 if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) {
547 binding_info->is_valid = FALSE;
548 return;
551 filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]);
552 g_assert (filename != NULL);
554 subpath = g_path_get_dirname (image->name);
555 fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL);
556 mono_config_parse_publisher_policy (fullpath, binding_info);
557 g_free (subpath);
558 g_free (fullpath);
560 /* Define the optional elements/attributes before checking */
561 if (!binding_info->culture)
562 binding_info->culture = g_strdup ("");
564 /* Check that the most important elements/attributes exist */
565 if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom ||
566 !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) {
567 mono_assembly_binding_info_free (binding_info);
568 binding_info->is_valid = FALSE;
569 return;
572 binding_info->is_valid = TRUE;
575 static int
576 compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname)
578 if (v->major > aname->major)
579 return 1;
580 else if (v->major < aname->major)
581 return -1;
583 if (v->minor > aname->minor)
584 return 1;
585 else if (v->minor < aname->minor)
586 return -1;
588 if (v->build > aname->build)
589 return 1;
590 else if (v->build < aname->build)
591 return -1;
593 if (v->revision > aname->revision)
594 return 1;
595 else if (v->revision < aname->revision)
596 return -1;
598 return 0;
601 static gboolean
602 check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name)
604 if (!info->is_valid)
605 return FALSE;
607 /* If has_old_version_top doesn't exist, we don't have an interval */
608 if (!info->has_old_version_top) {
609 if (compare_versions (&info->old_version_bottom, name) == 0)
610 return TRUE;
612 return FALSE;
615 /* Check that the version defined by name is valid for the interval */
616 if (compare_versions (&info->old_version_top, name) < 0)
617 return FALSE;
619 /* We should be greater or equal than the small version */
620 if (compare_versions (&info->old_version_bottom, name) > 0)
621 return FALSE;
623 return TRUE;
627 * mono_assembly_names_equal:
628 * \param l first assembly
629 * \param r second assembly.
631 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
633 * This compares the names, the cultures, the release version and their
634 * public tokens.
636 * \returns TRUE if both assembly names are equal.
638 gboolean
639 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
641 return mono_assembly_names_equal_flags (l, r, MONO_ANAME_EQ_NONE);
645 * mono_assembly_names_equal_flags:
646 * \param l first assembly name
647 * \param r second assembly name
648 * \param flags flags that affect what is compared.
650 * Compares two \c MonoAssemblyName instances and returns whether they are equal.
652 * This compares the simple names and cultures and optionally the versions and
653 * public key tokens, depending on the \c flags.
655 * \returns TRUE if both assembly names are equal.
657 gboolean
658 mono_assembly_names_equal_flags (MonoAssemblyName *l, MonoAssemblyName *r, MonoAssemblyNameEqFlags flags)
660 if (!l->name || !r->name)
661 return FALSE;
663 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) != 0 && g_strcasecmp (l->name, r->name))
664 return FALSE;
666 if ((flags & MONO_ANAME_EQ_IGNORE_CASE) == 0 && strcmp (l->name, r->name))
667 return FALSE;
669 if (l->culture && r->culture && strcmp (l->culture, r->culture))
670 return FALSE;
672 if ((l->major != r->major || l->minor != r->minor ||
673 l->build != r->build || l->revision != r->revision) &&
674 (flags & MONO_ANAME_EQ_IGNORE_VERSION) == 0)
675 if (! ((l->major == 0 && l->minor == 0 && l->build == 0 && l->revision == 0) || (r->major == 0 && r->minor == 0 && r->build == 0 && r->revision == 0)))
676 return FALSE;
678 if (!l->public_key_token [0] || !r->public_key_token [0] || (flags & MONO_ANAME_EQ_IGNORE_PUBKEY) != 0)
679 return TRUE;
681 if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token))
682 return FALSE;
684 return TRUE;
688 * assembly_names_compare_versions:
689 * \param l left assembly name
690 * \param r right assembly name
691 * \param maxcomps how many version components to compare, or -1 to compare all.
693 * \returns a negative if \p l is a lower version than \p r; a positive value
694 * if \p r is a lower version than \p l, or zero if \p l and \p r are equal
695 * versions (comparing upto \p maxcomps components).
697 * Components are \c major, \c minor, \c revision, and \c build. \p maxcomps 1 means just compare
698 * majors. 2 means majors then minors. etc.
700 static int
701 assembly_names_compare_versions (MonoAssemblyName *l, MonoAssemblyName *r, int maxcomps)
703 int i = 0;
704 if (maxcomps < 0) maxcomps = 4;
705 #define CMP(field) do { \
706 if (l-> field < r-> field && i < maxcomps) return -1; \
707 if (l-> field > r-> field && i < maxcomps) return 1; \
708 } while (0)
709 CMP (major);
710 ++i;
711 CMP (minor);
712 ++i;
713 CMP (revision);
714 ++i;
715 CMP (build);
716 #undef CMP
717 return 0;
720 static MonoAssembly *
721 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoAssemblyContextKind asmctx, MonoAssemblyCandidatePredicate predicate, gpointer user_data)
723 int i;
724 char *fullpath;
725 MonoAssembly *result;
727 for (i = 0; search_path [i]; ++i) {
728 fullpath = g_build_filename (search_path [i], basename, NULL);
729 result = mono_assembly_open_predicate (fullpath, asmctx, predicate, user_data, NULL, status);
730 g_free (fullpath);
731 if (result)
732 return result;
734 return NULL;
738 * mono_assembly_setrootdir:
739 * \param root_dir The pathname of the root directory where we will locate assemblies
741 * This routine sets the internal default root directory for looking up
742 * assemblies.
744 * This is used by Windows installations to compute dynamically the
745 * place where the Mono assemblies are located.
748 void
749 mono_assembly_setrootdir (const char *root_dir)
752 * Override the MONO_ASSEMBLIES directory configured at compile time.
754 /* Leak if called more than once */
755 default_path [0] = g_strdup (root_dir);
759 * mono_assembly_getrootdir:
761 * Obtains the root directory used for looking up assemblies.
763 * Returns: a string with the directory, this string should not be freed.
765 G_CONST_RETURN gchar *
766 mono_assembly_getrootdir (void)
768 return default_path [0];
772 * mono_native_getrootdir:
774 * Obtains the root directory used for looking up native libs (.so, .dylib).
776 * Returns: a string with the directory, this string should be freed by
777 * the caller.
779 gchar *
780 mono_native_getrootdir (void)
782 gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL);
783 return fullpath;
787 * mono_set_dirs:
788 * \param assembly_dir the base directory for assemblies
789 * \param config_dir the base directory for configuration files
791 * This routine is used internally and by developers embedding
792 * the runtime into their own applications.
794 * There are a number of cases to consider: Mono as a system-installed
795 * package that is available on the location preconfigured or Mono in
796 * a relocated location.
798 * If you are using a system-installed Mono, you can pass NULL
799 * to both parameters. If you are not, you should compute both
800 * directory values and call this routine.
802 * The values for a given PREFIX are:
804 * assembly_dir: PREFIX/lib
805 * config_dir: PREFIX/etc
807 * Notice that embedders that use Mono in a relocated way must
808 * compute the location at runtime, as they will be in control
809 * of where Mono is installed.
811 void
812 mono_set_dirs (const char *assembly_dir, const char *config_dir)
814 if (assembly_dir == NULL)
815 assembly_dir = mono_config_get_assemblies_dir ();
816 if (config_dir == NULL)
817 config_dir = mono_config_get_cfg_dir ();
818 mono_assembly_setrootdir (assembly_dir);
819 mono_set_config_dir (config_dir);
822 #ifndef HOST_WIN32
824 static char *
825 compute_base (char *path)
827 char *p = strrchr (path, '/');
828 if (p == NULL)
829 return NULL;
831 /* Not a well known Mono executable, we are embedded, cant guess the base */
832 if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis"))
833 return NULL;
835 *p = 0;
836 p = strrchr (path, '/');
837 if (p == NULL)
838 return NULL;
840 if (strcmp (p, "/bin") != 0)
841 return NULL;
842 *p = 0;
843 return path;
846 static void
847 fallback (void)
849 mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ());
852 static G_GNUC_UNUSED void
853 set_dirs (char *exe)
855 char *base;
856 char *config, *lib, *mono;
857 struct stat buf;
858 const char *bindir;
861 * Only /usr prefix is treated specially
863 bindir = mono_config_get_bin_dir ();
864 g_assert (bindir);
865 if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){
866 fallback ();
867 return;
870 config = g_build_filename (base, "etc", NULL);
871 lib = g_build_filename (base, "lib", NULL);
872 mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here
873 if (stat (mono, &buf) == -1)
874 fallback ();
875 else {
876 mono_set_dirs (lib, config);
879 g_free (config);
880 g_free (lib);
881 g_free (mono);
884 #endif /* HOST_WIN32 */
887 * mono_set_rootdir:
889 * Registers the root directory for the Mono runtime, for Linux and Solaris 10,
890 * this auto-detects the prefix where Mono was installed.
892 void
893 mono_set_rootdir (void)
895 #if defined(HOST_WIN32) || (defined(HOST_DARWIN) && !defined(TARGET_ARM))
896 gchar *bindir, *installdir, *root, *name, *resolvedname, *config;
898 #ifdef HOST_WIN32
899 name = mono_get_module_file_name ((HMODULE) &__ImageBase);
900 #else
903 * _NSGetExecutablePath may return -1 to indicate buf is not large
904 * enough, but we ignore that case to avoid having to do extra dynamic
905 * allocation for the path and hope that 4096 is enough - this is
906 * ok in the Linux/Solaris case below at least...
909 gchar buf[4096];
910 guint buf_size = sizeof (buf);
912 name = NULL;
913 if (_NSGetExecutablePath (buf, &buf_size) == 0)
914 name = g_strdup (buf);
916 if (name == NULL) {
917 fallback ();
918 return;
921 #endif
923 resolvedname = mono_path_resolve_symlinks (name);
925 bindir = g_path_get_dirname (resolvedname);
926 installdir = g_path_get_dirname (bindir);
927 root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
929 config = g_build_filename (root, "..", "etc", NULL);
930 #ifdef HOST_WIN32
931 mono_set_dirs (root, config);
932 #else
933 if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS))
934 mono_set_dirs (root, config);
935 else
936 fallback ();
937 #endif
939 g_free (config);
940 g_free (root);
941 g_free (installdir);
942 g_free (bindir);
943 g_free (name);
944 g_free (resolvedname);
945 #elif defined(DISABLE_MONO_AUTODETECTION)
946 fallback ();
947 #else
948 char buf [4096];
949 int s;
950 char *str;
952 /* Linux style */
953 s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
955 if (s != -1){
956 buf [s] = 0;
957 set_dirs (buf);
958 return;
961 /* Solaris 10 style */
962 str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
963 s = readlink (str, buf, sizeof (buf)-1);
964 g_free (str);
965 if (s != -1){
966 buf [s] = 0;
967 set_dirs (buf);
968 return;
970 fallback ();
971 #endif
975 * mono_assemblies_init:
977 * Initialize global variables used by this module.
979 void
980 mono_assemblies_init (void)
983 * Initialize our internal paths if we have not been initialized yet.
984 * This happens when embedders use Mono.
986 if (mono_assembly_getrootdir () == NULL)
987 mono_set_rootdir ();
989 check_path_env ();
990 check_extra_gac_path_env ();
992 mono_os_mutex_init_recursive (&assemblies_mutex);
993 mono_os_mutex_init (&assembly_binding_mutex);
995 #ifndef DISABLE_DESKTOP_LOADER
996 assembly_remapping_table = g_hash_table_new (g_str_hash, g_str_equal);
998 int i;
999 for (i = 0; i < G_N_ELEMENTS (framework_assemblies) - 1; ++i)
1000 g_hash_table_insert (assembly_remapping_table, (void*)framework_assemblies [i].assembly_name, (void*)&framework_assemblies [i]);
1002 #endif
1003 mono_install_assembly_asmctx_from_path_hook (assembly_loadfrom_asmctx_from_path, NULL);
1007 static void
1008 mono_assembly_binding_lock (void)
1010 mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock);
1013 static void
1014 mono_assembly_binding_unlock (void)
1016 mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock);
1019 gboolean
1020 mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs)
1022 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
1023 guint32 cols [MONO_ASSEMBLY_SIZE];
1024 gint32 machine, flags;
1026 if (!t->rows)
1027 return FALSE;
1029 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
1031 aname->hash_len = 0;
1032 aname->hash_value = NULL;
1033 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
1034 if (copyBlobs)
1035 aname->name = g_strdup (aname->name);
1036 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
1037 if (copyBlobs)
1038 aname->culture = g_strdup (aname->culture);
1039 aname->flags = cols [MONO_ASSEMBLY_FLAGS];
1040 aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
1041 aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
1042 aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
1043 aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
1044 aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
1045 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1046 guchar* token = (guchar *)g_malloc (8);
1047 gchar* encoded;
1048 const gchar* pkey;
1049 int len;
1051 pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1052 len = mono_metadata_decode_blob_size (pkey, &pkey);
1053 aname->public_key = (guchar*)pkey;
1055 mono_digest_get_public_token (token, aname->public_key, len);
1056 encoded = encode_public_tok (token, 8);
1057 g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1059 g_free (encoded);
1060 g_free (token);
1062 else {
1063 aname->public_key = NULL;
1064 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1067 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
1068 aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
1069 if (copyBlobs) {
1070 const gchar *pkey_end;
1071 int len = mono_metadata_decode_blob_size ((const gchar*) aname->public_key, &pkey_end);
1072 pkey_end += len; /* move to end */
1073 size_t size = pkey_end - (const gchar*)aname->public_key;
1074 guchar *tmp = g_new (guchar, size);
1075 memcpy (tmp, aname->public_key, size);
1076 aname->public_key = tmp;
1080 else
1081 aname->public_key = 0;
1083 machine = ((MonoCLIImageInfo*)(image->image_info))->cli_header.coff.coff_machine;
1084 flags = ((MonoCLIImageInfo*)(image->image_info))->cli_cli_header.ch_flags;
1085 switch (machine) {
1086 case COFF_MACHINE_I386:
1087 /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */
1088 if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT))
1089 aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86;
1090 else if ((flags & 0x70) == 0x70)
1091 aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
1092 else
1093 aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
1094 break;
1095 case COFF_MACHINE_IA64:
1096 aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
1097 break;
1098 case COFF_MACHINE_AMD64:
1099 aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
1100 break;
1101 case COFF_MACHINE_ARM:
1102 aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM;
1103 break;
1104 default:
1105 break;
1108 return TRUE;
1112 * mono_assembly_fill_assembly_name:
1113 * \param image Image
1114 * \param aname Name
1115 * \returns TRUE if successful
1117 gboolean
1118 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
1120 return mono_assembly_fill_assembly_name_full (image, aname, FALSE);
1124 * mono_stringify_assembly_name:
1125 * \param aname the assembly name.
1127 * Convert \p aname into its string format. The returned string is dynamically
1128 * allocated and should be freed by the caller.
1130 * \returns a newly allocated string with a string representation of
1131 * the assembly name.
1133 char*
1134 mono_stringify_assembly_name (MonoAssemblyName *aname)
1136 const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : "";
1138 return g_strdup_printf (
1139 "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
1140 quote, aname->name, quote,
1141 aname->major, aname->minor, aname->build, aname->revision,
1142 aname->culture && *aname->culture? aname->culture: "neutral",
1143 aname->public_key_token [0] ? (char *)aname->public_key_token : "null",
1144 (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
1147 static gchar*
1148 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
1150 const gchar *public_tok;
1151 int len;
1153 public_tok = mono_metadata_blob_heap (image, key_index);
1154 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1156 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1157 guchar token [8];
1158 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1159 return encode_public_tok (token, 8);
1162 return encode_public_tok ((guchar*)public_tok, len);
1165 static gchar*
1166 assemblyref_public_tok_checked (MonoImage *image, guint32 key_index, guint32 flags, MonoError *error)
1168 const gchar *public_tok;
1169 int len;
1171 public_tok = mono_metadata_blob_heap_checked (image, key_index, error);
1172 return_val_if_nok (error, NULL);
1173 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
1175 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
1176 guchar token [8];
1177 mono_digest_get_public_token (token, (guchar*)public_tok, len);
1178 return encode_public_tok (token, 8);
1180 return encode_public_tok ((guchar*)public_tok, len);
1184 * mono_assembly_addref:
1185 * \param assembly the assembly to reference
1187 * This routine increments the reference count on a MonoAssembly.
1188 * The reference count is reduced every time the method mono_assembly_close() is
1189 * invoked.
1191 void
1192 mono_assembly_addref (MonoAssembly *assembly)
1194 mono_atomic_inc_i32 (&assembly->ref_count);
1198 * CAUTION: This table must be kept in sync with
1199 * ivkm/reflect/Fusion.cs
1202 #define SILVERLIGHT_KEY "7cec85d7bea7798e"
1203 #define WINFX_KEY "31bf3856ad364e35"
1204 #define ECMA_KEY "b77a5c561934e089"
1205 #define MSFINAL_KEY "b03f5f7f11d50a3a"
1206 #define COMPACTFRAMEWORK_KEY "969db8053d3322ac"
1208 typedef struct {
1209 const char *name;
1210 const char *from;
1211 const char *to;
1212 } KeyRemapEntry;
1214 static KeyRemapEntry key_remap_table[] = {
1215 { "CustomMarshalers", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1216 { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY },
1217 { "Microsoft.VisualBasic", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1218 { "System", SILVERLIGHT_KEY, ECMA_KEY },
1219 { "System", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1220 { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY },
1221 { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY },
1222 { "System.Core", SILVERLIGHT_KEY, ECMA_KEY },
1223 { "System.Core", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1224 { "System.Data", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1225 { "System.Data.DataSetExtensions", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1226 { "System.Drawing", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1227 { "System.Messaging", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1228 // FIXME: MS uses MSFINAL_KEY for .NET 4.5
1229 { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY },
1230 { "System.Numerics", WINFX_KEY, ECMA_KEY },
1231 { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY },
1232 { "System.Runtime.Serialization", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1233 { "System.ServiceModel", WINFX_KEY, ECMA_KEY },
1234 { "System.ServiceModel", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1235 { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY },
1236 { "System.Web.Services", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
1237 { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY },
1238 { "System.Windows.Forms", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1239 { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY },
1240 { "System.Xml", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1241 { "System.Xml.Linq", WINFX_KEY, ECMA_KEY },
1242 { "System.Xml.Linq", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1243 { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY }
1246 static void
1247 remap_keys (MonoAssemblyName *aname)
1249 int i;
1250 for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) {
1251 const KeyRemapEntry *entry = &key_remap_table [i];
1253 if (strcmp (aname->name, entry->name) ||
1254 !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from))
1255 continue;
1257 memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1259 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1260 "Remapped public key token of retargetable assembly %s from %s to %s",
1261 aname->name, entry->from, entry->to);
1262 return;
1266 static MonoAssemblyName *
1267 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
1269 const MonoRuntimeInfo *current_runtime;
1271 if (aname->name == NULL) return aname;
1273 current_runtime = mono_get_runtime_info ();
1275 if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) {
1276 const AssemblyVersionSet* vset;
1278 /* Remap to current runtime */
1279 vset = &current_runtime->version_sets [0];
1281 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1282 dest_aname->major = vset->major;
1283 dest_aname->minor = vset->minor;
1284 dest_aname->build = vset->build;
1285 dest_aname->revision = vset->revision;
1286 dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG;
1288 /* Remap assembly name */
1289 if (!strcmp (aname->name, "System.Net"))
1290 dest_aname->name = g_strdup ("System");
1292 remap_keys (dest_aname);
1294 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1295 "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d",
1296 aname->name,
1297 aname->major, aname->minor, aname->build, aname->revision,
1298 dest_aname->name,
1299 vset->major, vset->minor, vset->build, vset->revision
1302 return dest_aname;
1305 #ifndef DISABLE_DESKTOP_LOADER
1306 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, aname->name);
1307 if (vmap) {
1308 const AssemblyVersionSet* vset;
1309 int index = vmap->version_set_index;
1310 g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
1311 vset = &current_runtime->version_sets [index];
1313 if (vmap->framework_facade_assembly) {
1314 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly %s is a framework Facade asseembly",
1315 aname->name);
1316 return aname;
1319 if (aname->major == vset->major && aname->minor == vset->minor &&
1320 aname->build == vset->build && aname->revision == vset->revision) {
1321 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Found assembly remapping for %s and was for the same version %d.%d.%d.%d",
1322 aname->name,
1323 aname->major, aname->minor, aname->build, aname->revision);
1324 return aname;
1327 if (vmap->only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0) {
1328 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY,
1329 "Found lower-versions-only assembly remaping to load %s %d.%d.%d.%d but mapping has %d.%d.%d.%d",
1330 aname->name,
1331 aname->major, aname->minor, aname->build, aname->revision,
1332 vset->major, vset->minor, vset->build, vset->revision
1334 return aname;
1337 if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
1338 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1339 "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
1340 aname->name,
1341 aname->major, aname->minor, aname->build, aname->revision,
1342 vset->major, vset->minor, vset->build, vset->revision
1345 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1346 dest_aname->major = vset->major;
1347 dest_aname->minor = vset->minor;
1348 dest_aname->build = vset->build;
1349 dest_aname->revision = vset->revision;
1350 if (vmap->new_assembly_name != NULL) {
1351 dest_aname->name = vmap->new_assembly_name;
1352 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1353 "The assembly name %s was remapped to %s",
1354 aname->name,
1355 dest_aname->name);
1357 return dest_aname;
1359 #endif
1361 return aname;
1365 * mono_assembly_get_assemblyref:
1366 * \param image pointer to the \c MonoImage to extract the information from.
1367 * \param index index to the assembly reference in the image.
1368 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1370 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1372 void
1373 mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname)
1375 MonoTableInfo *t;
1376 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1377 const char *hash;
1379 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1381 mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
1383 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
1384 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1385 aname->hash_value = hash;
1386 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
1387 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
1388 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1389 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1390 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1391 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1392 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1394 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1395 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags);
1396 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1397 g_free (token);
1398 } else {
1399 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1403 static MonoAssembly*
1404 load_reference_by_aname_refonly_asmctx (MonoAssemblyName *aname, MonoAssembly *assm, MonoImageOpenStatus *status)
1406 MonoAssembly *reference = NULL;
1407 g_assert (assm != NULL);
1408 *status = MONO_IMAGE_OK;
1410 /* We use the loaded corlib */
1411 if (!strcmp (aname->name, "mscorlib"))
1412 reference = mono_assembly_load_full_internal (aname, assm, assm->basedir, MONO_ASMCTX_DEFAULT, status);
1413 else {
1414 reference = mono_assembly_loaded_full (aname, TRUE);
1415 if (!reference)
1416 /* Try a postload search hook */
1417 reference = mono_assembly_invoke_search_hook_internal (aname, assm, TRUE, TRUE);
1421 * Here we must advice that the error was due to
1422 * a non loaded reference using the ReflectionOnly api
1424 if (!reference)
1425 reference = (MonoAssembly *)REFERENCE_MISSING;
1427 return reference;
1430 static MonoAssembly*
1431 load_reference_by_aname_default_asmctx (MonoAssemblyName *aname, MonoAssembly *assm, MonoImageOpenStatus *status)
1433 MonoAssembly *reference = NULL;
1434 *status = MONO_IMAGE_OK;
1436 /* we first try without setting the basedir: this can eventually result in a ResolveAssembly
1437 * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed
1438 * accordingly, it would fail on the MS runtime before).
1439 * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for
1440 * example bug-349190.2.cs and who knows how much more code in the wild.
1442 reference = mono_assembly_load_full_internal (aname, assm, NULL, MONO_ASMCTX_DEFAULT, status);
1443 if (!reference && assm)
1444 reference = mono_assembly_load_full_internal (aname, assm, assm->basedir, MONO_ASMCTX_DEFAULT, status);
1446 return reference;
1449 static MonoAssembly*
1450 load_reference_by_aname_loadfrom_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1452 MonoAssembly *reference = NULL;
1453 /* Just like default search, but look in the requesting assembly basedir right away */
1454 reference = mono_assembly_load_full_internal (aname, requesting, requesting->basedir, MONO_ASMCTX_LOADFROM, status);
1455 return reference;
1459 static MonoAssembly*
1460 load_reference_by_aname_individual_asmctx (MonoAssemblyName *aname, MonoAssembly *requesting, MonoImageOpenStatus *status)
1462 /* For an individual assembly, all references must already be loaded or
1463 * else we fire the assembly resolve event - similar to refonly - but
1464 * subject to remaping and binding.
1467 MonoAssembly *reference = NULL;
1468 *status = MONO_IMAGE_OK;
1469 MonoAssemblyName maped_aname;
1470 MonoAssemblyName maped_name_pp;
1472 aname = mono_assembly_remap_version (aname, &maped_aname);
1473 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
1475 reference = mono_assembly_loaded_full (aname, FALSE);
1476 /* Still try to load from application base directory, MONO_PATH or the
1477 * GAC. This is consistent with what .NET Framework (4.7) actually
1478 * does, rather than what the documentation implies: If `LoadFile` is
1479 * used to load an assembly into "no context"/individual assembly
1480 * context, the runtime will still load assemblies from the GAC or the
1481 * application base directory (e.g. `System.Runtime` will be loaded if
1482 * it wasn't already).
1483 * Moreover, those referenced assemblies are loaded in the default context.
1485 if (!reference)
1486 reference = mono_assembly_load_full_internal (aname, requesting, NULL, MONO_ASMCTX_DEFAULT, status);
1487 if (!reference)
1488 reference = (MonoAssembly*)REFERENCE_MISSING;
1489 return reference;
1493 * mono_assembly_get_assemblyref:
1494 * \param image pointer to the \c MonoImage to extract the information from.
1495 * \param index index to the assembly reference in the image.
1496 * \param aname pointer to a \c MonoAssemblyName that will hold the returned value.
1497 * \param error set on error
1499 * Fills out the \p aname with the assembly name of the \p index assembly reference in \p image.
1501 * \returns TRUE on success, otherwise sets \p error and returns FALSE
1503 gboolean
1504 mono_assembly_get_assemblyref_checked (MonoImage *image, int index, MonoAssemblyName *aname, MonoError *error)
1506 MonoTableInfo *t;
1507 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1508 const char *hash;
1510 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1512 if (!mono_metadata_decode_row_checked (image, t, index, cols, MONO_ASSEMBLYREF_SIZE, error))
1513 return FALSE;
1515 hash = mono_metadata_blob_heap_checked (image, cols [MONO_ASSEMBLYREF_HASH_VALUE], error);
1516 return_val_if_nok (error, FALSE);
1517 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1518 aname->hash_value = hash;
1519 aname->name = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_NAME], error);
1520 return_val_if_nok (error, FALSE);
1521 aname->culture = mono_metadata_string_heap_checked (image, cols [MONO_ASSEMBLYREF_CULTURE], error);
1522 return_val_if_nok (error, FALSE);
1523 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1524 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1525 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1526 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1527 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1528 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1529 gchar *token = assemblyref_public_tok_checked (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags, error);
1530 return_val_if_nok (error, FALSE);
1531 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1532 g_free (token);
1533 } else {
1534 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1536 return TRUE;
1540 * mono_assembly_load_reference:
1542 void
1543 mono_assembly_load_reference (MonoImage *image, int index)
1545 MonoAssembly *reference;
1546 MonoAssemblyName aname;
1547 MonoImageOpenStatus status;
1550 * image->references is shared between threads, so we need to access
1551 * it inside a critical section.
1553 mono_assemblies_lock ();
1554 if (!image->references) {
1555 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1557 image->references = g_new0 (MonoAssembly *, t->rows + 1);
1558 image->nreferences = t->rows;
1560 reference = image->references [index];
1561 mono_assemblies_unlock ();
1562 if (reference)
1563 return;
1565 mono_assembly_get_assemblyref (image, index, &aname);
1567 if (image->assembly) {
1568 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
1569 char *aname_str = mono_stringify_assembly_name (&aname);
1570 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Loading reference %d of %s asmctx %s, looking for %s",
1571 index, image->name, mono_asmctx_get_name (&image->assembly->context),
1572 aname_str);
1573 g_free (aname_str);
1575 switch (mono_asmctx_get_kind (&image->assembly->context)) {
1576 case MONO_ASMCTX_DEFAULT:
1577 reference = load_reference_by_aname_default_asmctx (&aname, image->assembly, &status);
1578 break;
1579 case MONO_ASMCTX_REFONLY:
1580 reference = load_reference_by_aname_refonly_asmctx (&aname, image->assembly, &status);
1581 break;
1582 case MONO_ASMCTX_LOADFROM:
1583 reference = load_reference_by_aname_loadfrom_asmctx (&aname, image->assembly, &status);
1584 break;
1585 case MONO_ASMCTX_INDIVIDUAL:
1586 reference = load_reference_by_aname_individual_asmctx (&aname, image->assembly, &status);
1587 break;
1588 default:
1589 g_error ("Unexpected assembly load context kind %d for image %s.", mono_asmctx_get_kind (&image->assembly->context), image->name);
1590 break;
1592 } else {
1593 /* FIXME: can we establish that image->assembly is never NULL and this code is dead? */
1594 reference = load_reference_by_aname_default_asmctx (&aname, image->assembly, &status);
1597 if (reference == NULL){
1598 char *extra_msg;
1600 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
1601 extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly != NULL ? image->assembly->basedir : "" );
1602 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
1603 extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
1604 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
1605 extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
1606 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
1607 extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
1608 } else {
1609 extra_msg = g_strdup ("");
1612 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n"
1613 " Assembly: %s (assemblyref_index=%d)\n"
1614 " Version: %d.%d.%d.%d\n"
1615 " Public Key: %s\n%s",
1616 image->name, aname.name, index,
1617 aname.major, aname.minor, aname.build, aname.revision,
1618 strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
1619 g_free (extra_msg);
1623 mono_assemblies_lock ();
1624 if (reference == NULL) {
1625 /* Flag as not found */
1626 reference = (MonoAssembly *)REFERENCE_MISSING;
1629 if (!image->references [index]) {
1630 if (reference != REFERENCE_MISSING){
1631 mono_assembly_addref (reference);
1632 if (image->assembly)
1633 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d",
1634 image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count);
1635 } else {
1636 if (image->assembly)
1637 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p].",
1638 image->assembly->aname.name, image->assembly);
1641 image->references [index] = reference;
1643 mono_assemblies_unlock ();
1645 if (image->references [index] != reference) {
1646 /* Somebody loaded it before us */
1647 mono_assembly_close (reference);
1652 * mono_assembly_load_references:
1653 * \param image
1654 * \param status
1655 * \deprecated There is no reason to use this method anymore, it does nothing
1657 * This method is now a no-op, it does nothing other than setting the \p status to \c MONO_IMAGE_OK
1659 void
1660 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
1662 /* This is a no-op now but it is part of the embedding API so we can't remove it */
1663 *status = MONO_IMAGE_OK;
1666 typedef struct AssemblyLoadHook AssemblyLoadHook;
1667 struct AssemblyLoadHook {
1668 AssemblyLoadHook *next;
1669 MonoAssemblyLoadFunc func;
1670 gpointer user_data;
1673 static AssemblyLoadHook *assembly_load_hook = NULL;
1676 * mono_assembly_invoke_load_hook:
1678 void
1679 mono_assembly_invoke_load_hook (MonoAssembly *ass)
1681 AssemblyLoadHook *hook;
1683 for (hook = assembly_load_hook; hook; hook = hook->next) {
1684 hook->func (ass, hook->user_data);
1689 * mono_install_assembly_load_hook:
1691 void
1692 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
1694 AssemblyLoadHook *hook;
1696 g_return_if_fail (func != NULL);
1698 hook = g_new0 (AssemblyLoadHook, 1);
1699 hook->func = func;
1700 hook->user_data = user_data;
1701 hook->next = assembly_load_hook;
1702 assembly_load_hook = hook;
1705 static void
1706 free_assembly_load_hooks (void)
1708 AssemblyLoadHook *hook, *next;
1710 for (hook = assembly_load_hook; hook; hook = next) {
1711 next = hook->next;
1712 g_free (hook);
1716 typedef struct AssemblySearchHook AssemblySearchHook;
1717 struct AssemblySearchHook {
1718 AssemblySearchHook *next;
1719 MonoAssemblySearchFunc func;
1720 gboolean refonly;
1721 gboolean postload;
1722 gpointer user_data;
1725 static AssemblySearchHook *assembly_search_hook = NULL;
1727 static MonoAssembly*
1728 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload)
1730 AssemblySearchHook *hook;
1732 for (hook = assembly_search_hook; hook; hook = hook->next) {
1733 if ((hook->refonly == refonly) && (hook->postload == postload)) {
1734 MonoAssembly *ass;
1736 * A little explanation is in order here.
1738 * The default postload search hook needs to know the requesting assembly to report it to managed code.
1739 * The embedding API exposes a search hook that doesn't take such argument.
1741 * The original fix would call the default search hook before all the registered ones and pass
1742 * the requesting assembly to it. It works but broke a very suddle embedding API aspect that some users
1743 * rely on. Which is the ordering between user hooks and the default runtime hook.
1745 * Registering the hook after mono_jit_init would let your hook run before the default one and
1746 * when using it to handle non standard app layouts this could save your app from a massive amount
1747 * of syscalls that the default hook does when probing all sorts of places. Slow targets with horrible IO
1748 * are all using this trick and if we broke this assumption they would be very disapointed at us.
1750 * So what's the fix? We register the default hook using regular means and special case it when iterating
1751 * over the registered hooks. This preserves ordering and enables managed resolve hooks to get the requesting
1752 * assembly.
1754 if (hook->func == (void*)mono_domain_assembly_postload_search)
1755 ass = mono_domain_assembly_postload_search (aname, requesting, refonly);
1756 else
1757 ass = hook->func (aname, hook->user_data);
1758 if (ass)
1759 return ass;
1763 return NULL;
1767 * mono_assembly_invoke_search_hook:
1769 MonoAssembly*
1770 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
1772 return mono_assembly_invoke_search_hook_internal (aname, NULL, FALSE, FALSE);
1775 static void
1776 mono_install_assembly_search_hook_internal (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload)
1778 AssemblySearchHook *hook;
1780 g_return_if_fail (func != NULL);
1782 hook = g_new0 (AssemblySearchHook, 1);
1783 hook->func = func;
1784 hook->user_data = user_data;
1785 hook->refonly = refonly;
1786 hook->postload = postload;
1787 hook->next = assembly_search_hook;
1788 assembly_search_hook = hook;
1792 * mono_install_assembly_search_hook:
1794 void
1795 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1797 mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE);
1800 static void
1801 free_assembly_search_hooks (void)
1803 AssemblySearchHook *hook, *next;
1805 for (hook = assembly_search_hook; hook; hook = next) {
1806 next = hook->next;
1807 g_free (hook);
1812 * mono_install_assembly_refonly_search_hook:
1814 void
1815 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1817 mono_install_assembly_search_hook_internal (func, user_data, TRUE, FALSE);
1821 * mono_install_assembly_postload_search_hook:
1823 void
1824 mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1826 mono_install_assembly_search_hook_internal (func, user_data, FALSE, TRUE);
1829 void
1830 mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1832 mono_install_assembly_search_hook_internal (func, user_data, TRUE, TRUE);
1835 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
1836 struct AssemblyPreLoadHook {
1837 AssemblyPreLoadHook *next;
1838 MonoAssemblyPreLoadFunc func;
1839 gpointer user_data;
1842 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
1843 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
1845 static MonoAssembly *
1846 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
1848 AssemblyPreLoadHook *hook;
1849 MonoAssembly *assembly;
1851 for (hook = assembly_preload_hook; hook; hook = hook->next) {
1852 assembly = hook->func (aname, assemblies_path, hook->user_data);
1853 if (assembly != NULL)
1854 return assembly;
1857 return NULL;
1860 static MonoAssembly *
1861 invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
1863 AssemblyPreLoadHook *hook;
1864 MonoAssembly *assembly;
1866 for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
1867 assembly = hook->func (aname, assemblies_path, hook->user_data);
1868 if (assembly != NULL)
1869 return assembly;
1872 return NULL;
1876 * mono_install_assembly_preload_hook:
1878 void
1879 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1881 AssemblyPreLoadHook *hook;
1883 g_return_if_fail (func != NULL);
1885 hook = g_new0 (AssemblyPreLoadHook, 1);
1886 hook->func = func;
1887 hook->user_data = user_data;
1888 hook->next = assembly_preload_hook;
1889 assembly_preload_hook = hook;
1893 * mono_install_assembly_refonly_preload_hook:
1895 void
1896 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1898 AssemblyPreLoadHook *hook;
1900 g_return_if_fail (func != NULL);
1902 hook = g_new0 (AssemblyPreLoadHook, 1);
1903 hook->func = func;
1904 hook->user_data = user_data;
1905 hook->next = assembly_refonly_preload_hook;
1906 assembly_refonly_preload_hook = hook;
1909 static void
1910 free_assembly_preload_hooks (void)
1912 AssemblyPreLoadHook *hook, *next;
1914 for (hook = assembly_preload_hook; hook; hook = next) {
1915 next = hook->next;
1916 g_free (hook);
1919 for (hook = assembly_refonly_preload_hook; hook; hook = next) {
1920 next = hook->next;
1921 g_free (hook);
1925 typedef struct AssemblyAsmCtxFromPathHook AssemblyAsmCtxFromPathHook;
1926 struct AssemblyAsmCtxFromPathHook {
1927 AssemblyAsmCtxFromPathHook *next;
1928 MonoAssemblyAsmCtxFromPathFunc func;
1929 gpointer user_data;
1932 static AssemblyAsmCtxFromPathHook *assembly_asmctx_from_path_hook = NULL;
1935 * mono_install_assembly_asmctx_from_path_hook:
1937 * \param func Hook function
1938 * \param user_data User data
1940 * Installs a hook function \p func that when called with an absolute path name
1941 * returns \c TRUE and writes to \c out_asmctx if an assembly that name would
1942 * be found by that asmctx. The hooks are called in the order from most
1943 * recently added to oldest.
1946 void
1947 mono_install_assembly_asmctx_from_path_hook (MonoAssemblyAsmCtxFromPathFunc func, gpointer user_data)
1949 g_return_if_fail (func != NULL);
1951 AssemblyAsmCtxFromPathHook *hook = g_new0 (AssemblyAsmCtxFromPathHook, 1);
1952 hook->func = func;
1953 hook->user_data = user_data;
1954 hook->next = assembly_asmctx_from_path_hook;
1955 assembly_asmctx_from_path_hook = hook;
1959 * mono_assembly_invoke_asmctx_from_path_hook:
1961 * \param absfname absolute path name
1962 * \param requesting_assembly the \c MonoAssembly that requested the load, may be \c NULL
1963 * \param out_asmctx assembly context kind, written on output
1965 * Invokes hooks to find the assembly context that would have searched for the
1966 * given assembly name. Writes to \p out_asmctx the assembly context kind from
1967 * the first hook to return \c TRUE. \returns \c TRUE if any hook wrote to \p
1968 * out_asmctx, or \c FALSE otherwise.
1970 static gboolean
1971 assembly_invoke_asmctx_from_path_hook (const char *absfname, MonoAssembly *requesting_assembly, MonoAssemblyContextKind *out_asmctx)
1973 g_assert (absfname);
1974 g_assert (out_asmctx);
1975 AssemblyAsmCtxFromPathHook *hook;
1977 for (hook = assembly_asmctx_from_path_hook; hook; hook = hook->next) {
1978 *out_asmctx = MONO_ASMCTX_INDIVIDUAL;
1979 if (hook->func (absfname, requesting_assembly, hook->user_data, out_asmctx))
1980 return TRUE;
1982 return FALSE;
1986 static void
1987 free_assembly_asmctx_from_path_hooks (void)
1989 AssemblyAsmCtxFromPathHook *hook, *next;
1991 for (hook = assembly_asmctx_from_path_hook; hook; hook = next) {
1992 next = hook->next;
1993 g_free (hook);
1997 static gchar *
1998 absolute_dir (const gchar *filename)
2000 gchar *cwd;
2001 gchar *mixed;
2002 gchar **parts;
2003 gchar *part;
2004 GList *list, *tmp;
2005 GString *result;
2006 gchar *res;
2007 gint i;
2009 if (g_path_is_absolute (filename)) {
2010 part = g_path_get_dirname (filename);
2011 res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL);
2012 g_free (part);
2013 return res;
2016 cwd = g_get_current_dir ();
2017 mixed = g_build_filename (cwd, filename, NULL);
2018 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
2019 g_free (mixed);
2020 g_free (cwd);
2022 list = NULL;
2023 for (i = 0; (part = parts [i]) != NULL; i++) {
2024 if (!strcmp (part, "."))
2025 continue;
2027 if (!strcmp (part, "..")) {
2028 if (list && list->next) /* Don't remove root */
2029 list = g_list_delete_link (list, list);
2030 } else {
2031 list = g_list_prepend (list, part);
2035 result = g_string_new ("");
2036 list = g_list_reverse (list);
2038 /* Ignores last data pointer, which should be the filename */
2039 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){
2040 if (tmp->data)
2041 g_string_append_printf (result, "%s%c", (char *) tmp->data,
2042 G_DIR_SEPARATOR);
2045 res = result->str;
2046 g_string_free (result, FALSE);
2047 g_list_free (list);
2048 g_strfreev (parts);
2049 if (*res == '\0') {
2050 g_free (res);
2051 return g_strdup (".");
2054 return res;
2057 /**
2058 * mono_assembly_open_from_bundle:
2059 * \param filename Filename requested
2060 * \param status return status code
2062 * This routine tries to open the assembly specified by \p filename from the
2063 * defined bundles, if found, returns the MonoImage for it, if not found
2064 * returns NULL
2066 MonoImage *
2067 mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2069 int i;
2070 char *name;
2071 gchar *lowercase_filename;
2072 MonoImage *image = NULL;
2073 gboolean is_satellite = FALSE;
2075 * we do a very simple search for bundled assemblies: it's not a general
2076 * purpose assembly loading mechanism.
2079 if (!bundles)
2080 return NULL;
2082 lowercase_filename = g_utf8_strdown (filename, -1);
2083 is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
2084 g_free (lowercase_filename);
2085 name = g_path_get_basename (filename);
2086 mono_assemblies_lock ();
2087 for (i = 0; !image && bundles [i]; ++i) {
2088 if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) {
2089 image = mono_image_open_from_data_with_name ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, name);
2090 break;
2093 mono_assemblies_unlock ();
2094 if (image) {
2095 mono_image_addref (image);
2096 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name);
2097 g_free (name);
2098 return image;
2100 g_free (name);
2101 return NULL;
2105 * mono_assembly_open_full:
2106 * \param filename the file to load
2107 * \param status return status code
2108 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2110 * This loads an assembly from the specified \p filename. The \p filename allows
2111 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2112 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2113 * is treated as a local path.
2115 * First, an attempt is made to load the assembly from the bundled executable (for those
2116 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2117 * assembly has been registered as an embedded assembly). If this is not the case, then
2118 * the assembly is loaded from disk using `api:mono_image_open_full`.
2120 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2121 * the assembly is made.
2123 * If \p refonly is set to true, then the assembly is loaded purely for inspection with
2124 * the \c System.Reflection API.
2126 * \returns NULL on error, with the \p status set to an error code, or a pointer
2127 * to the assembly.
2129 MonoAssembly *
2130 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
2132 return mono_assembly_open_a_lot (filename, status, refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT);
2135 MonoAssembly *
2136 mono_assembly_open_a_lot (const char *filename, MonoImageOpenStatus *status, MonoAssemblyContextKind asmctx)
2138 return mono_assembly_open_predicate (filename, asmctx, NULL, NULL, NULL, status);
2141 static gboolean
2142 assembly_loadfrom_asmctx_from_path (const char *filename, MonoAssembly *requesting_assembly,
2143 gpointer user_data, MonoAssemblyContextKind *out_asmctx) {
2144 if (requesting_assembly && mono_asmctx_get_kind (&requesting_assembly->context) == MONO_ASMCTX_LOADFROM) {
2145 if (mono_path_filename_in_basedir (filename, requesting_assembly->basedir)) {
2146 *out_asmctx = MONO_ASMCTX_LOADFROM;
2147 return TRUE;
2150 return FALSE;
2153 MonoAssembly *
2154 mono_assembly_open_predicate (const char *filename, MonoAssemblyContextKind asmctx,
2155 MonoAssemblyCandidatePredicate predicate,
2156 gpointer user_data,
2157 MonoAssembly *requesting_assembly,
2158 MonoImageOpenStatus *status)
2160 MonoImage *image;
2161 MonoAssembly *ass;
2162 MonoImageOpenStatus def_status;
2163 gchar *fname;
2164 gchar *new_fname;
2165 gboolean loaded_from_bundle;
2167 g_return_val_if_fail (filename != NULL, NULL);
2169 if (!status)
2170 status = &def_status;
2171 *status = MONO_IMAGE_OK;
2173 if (strncmp (filename, "file://", 7) == 0) {
2174 GError *gerror = NULL;
2175 gchar *uri = (gchar *) filename;
2176 gchar *tmpuri;
2179 * MS allows file://c:/... and fails on file://localhost/c:/...
2180 * They also throw an IndexOutOfRangeException if "file://"
2182 if (uri [7] != '/')
2183 uri = g_strdup_printf ("file:///%s", uri + 7);
2185 tmpuri = uri;
2186 uri = mono_escape_uri_string (tmpuri);
2187 fname = g_filename_from_uri (uri, NULL, &gerror);
2188 g_free (uri);
2190 if (tmpuri != filename)
2191 g_free (tmpuri);
2193 if (gerror != NULL) {
2194 g_warning ("%s\n", gerror->message);
2195 g_error_free (gerror);
2196 fname = g_strdup (filename);
2198 } else {
2199 fname = g_strdup (filename);
2202 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2203 "Assembly Loader probing location: '%s'.", fname);
2205 new_fname = NULL;
2206 if (!mono_assembly_is_in_gac (fname)) {
2207 ERROR_DECL (error);
2208 new_fname = mono_make_shadow_copy (fname, error);
2209 if (!is_ok (error)) {
2210 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2211 "Assembly Loader shadow copy error: %s.", mono_error_get_message (error));
2212 mono_error_cleanup (error);
2213 *status = MONO_IMAGE_IMAGE_INVALID;
2214 g_free (fname);
2215 return NULL;
2218 if (asmctx != MONO_ASMCTX_REFONLY) {
2219 MonoAssemblyContextKind out_asmctx;
2220 /* If the path belongs to the appdomain base dir or the
2221 * base dir of the requesting assembly, load the
2222 * assembly in the corresponding asmctx.
2224 if (assembly_invoke_asmctx_from_path_hook (fname, requesting_assembly, &out_asmctx))
2225 asmctx = out_asmctx;
2227 } else {
2228 if (asmctx != MONO_ASMCTX_REFONLY) {
2229 /* GAC assemblies always in default context or refonly context. */
2230 asmctx = MONO_ASMCTX_DEFAULT;
2233 if (new_fname && new_fname != fname) {
2234 g_free (fname);
2235 fname = new_fname;
2236 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2237 "Assembly Loader shadow-copied assembly to: '%s'.", fname);
2240 image = NULL;
2242 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
2243 /* for LoadFrom(string), LoadFile(string) and Load(byte[]), allow them
2244 * to load problematic images. Not sure if ReflectionOnlyLoad(string)
2245 * and ReflectionOnlyLoadFrom(string) should also be allowed - let's
2246 * say, yes.
2248 const gboolean load_from_context = asmctx == MONO_ASMCTX_LOADFROM || asmctx == MONO_ASMCTX_INDIVIDUAL || asmctx == MONO_ASMCTX_REFONLY;
2250 // If VM built with mkbundle
2251 loaded_from_bundle = FALSE;
2252 if (bundles != NULL) {
2253 image = mono_assembly_open_from_bundle (fname, status, refonly);
2254 loaded_from_bundle = image != NULL;
2257 if (!image)
2258 image = mono_image_open_a_lot (fname, status, refonly, load_from_context);
2260 if (!image){
2261 if (*status == MONO_IMAGE_OK)
2262 *status = MONO_IMAGE_ERROR_ERRNO;
2263 g_free (fname);
2264 return NULL;
2267 if (asmctx == MONO_ASMCTX_LOADFROM || asmctx == MONO_ASMCTX_INDIVIDUAL) {
2268 MonoAssembly *redirected_asm = NULL;
2269 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2270 if ((redirected_asm = chain_redirections_loadfrom (image, &new_status))) {
2271 mono_image_close (image);
2272 image = redirected_asm->image;
2273 mono_image_addref (image); /* so that mono_image_close, below, has something to do */
2274 /* fall thru to if (image->assembly) below */
2275 } else if (new_status != MONO_IMAGE_OK) {
2276 *status = new_status;
2277 mono_image_close (image);
2278 g_free (fname);
2279 return NULL;
2283 if (image->assembly) {
2284 /* We want to return the MonoAssembly that's already loaded,
2285 * but if we're using the strict assembly loader, we also need
2286 * to check that the previously loaded assembly matches the
2287 * predicate. It could be that we previously loaded a
2288 * different version that happens to have the filename that
2289 * we're currently probing. */
2290 if (mono_loader_get_strict_strong_names () &&
2291 predicate && !predicate (image->assembly, user_data)) {
2292 mono_image_close (image);
2293 g_free (fname);
2294 return NULL;
2295 } else {
2296 /* Already loaded by another appdomain */
2297 mono_assembly_invoke_load_hook (image->assembly);
2298 mono_image_close (image);
2299 g_free (fname);
2300 return image->assembly;
2304 ass = mono_assembly_load_from_predicate (image, fname, asmctx, predicate, user_data, status);
2306 if (ass) {
2307 if (!loaded_from_bundle)
2308 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
2309 "Assembly Loader loaded assembly from location: '%s'.", filename);
2310 if (!refonly)
2311 mono_config_for_assembly (ass->image);
2314 /* Clear the reference added by mono_image_open */
2315 mono_image_close (image);
2317 g_free (fname);
2319 return ass;
2322 static void
2323 free_item (gpointer val, gpointer user_data)
2325 g_free (val);
2329 * mono_assembly_load_friends:
2330 * \param ass an assembly
2332 * Load the list of friend assemblies that are allowed to access
2333 * the assembly's internal types and members. They are stored as assembly
2334 * names in custom attributes.
2336 * This is an internal method, we need this because when we load mscorlib
2337 * we do not have the internals visible cattr loaded yet,
2338 * so we need to load these after we initialize the runtime.
2340 * LOCKING: Acquires the assemblies lock plus the loader lock.
2342 void
2343 mono_assembly_load_friends (MonoAssembly* ass)
2345 ERROR_DECL (error);
2346 int i;
2347 MonoCustomAttrInfo* attrs;
2348 GSList *list;
2350 if (ass->friend_assembly_names_inited)
2351 return;
2353 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, error);
2354 mono_error_assert_ok (error);
2355 if (!attrs) {
2356 mono_assemblies_lock ();
2357 ass->friend_assembly_names_inited = TRUE;
2358 mono_assemblies_unlock ();
2359 return;
2362 mono_assemblies_lock ();
2363 if (ass->friend_assembly_names_inited) {
2364 mono_assemblies_unlock ();
2365 return;
2367 mono_assemblies_unlock ();
2369 list = NULL;
2371 * We build the list outside the assemblies lock, the worse that can happen
2372 * is that we'll need to free the allocated list.
2374 for (i = 0; i < attrs->num_attrs; ++i) {
2375 MonoCustomAttrEntry *attr = &attrs->attrs [i];
2376 MonoAssemblyName *aname;
2377 const gchar *data;
2378 uint32_t data_length;
2379 gchar *data_with_terminator;
2380 /* Do some sanity checking */
2381 if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ())
2382 continue;
2383 if (attr->data_size < 4)
2384 continue;
2385 data = (const char*)attr->data;
2386 /* 0xFF means null string, see custom attr format */
2387 if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF)
2388 continue;
2389 data_length = mono_metadata_decode_value (data + 2, &data);
2390 data_with_terminator = (char *)g_memdup (data, data_length + 1);
2391 data_with_terminator[data_length] = 0;
2392 aname = g_new0 (MonoAssemblyName, 1);
2393 /*g_print ("friend ass: %s\n", data);*/
2394 if (mono_assembly_name_parse_full (data_with_terminator, aname, TRUE, NULL, NULL)) {
2395 list = g_slist_prepend (list, aname);
2396 } else {
2397 g_free (aname);
2399 g_free (data_with_terminator);
2401 mono_custom_attrs_free (attrs);
2403 mono_assemblies_lock ();
2404 if (ass->friend_assembly_names_inited) {
2405 mono_assemblies_unlock ();
2406 g_slist_foreach (list, free_item, NULL);
2407 g_slist_free (list);
2408 return;
2410 ass->friend_assembly_names = list;
2412 /* Because of the double checked locking pattern above */
2413 mono_memory_barrier ();
2414 ass->friend_assembly_names_inited = TRUE;
2415 mono_assemblies_unlock ();
2418 struct HasReferenceAssemblyAttributeIterData {
2419 gboolean has_attr;
2422 static gboolean
2423 has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
2425 gboolean stop_scanning = FALSE;
2426 struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data;
2428 if (!strcmp (name, "ReferenceAssemblyAttribute") && !strcmp (nspace, "System.Runtime.CompilerServices")) {
2429 /* Note we don't check the assembly name, same as coreCLR. */
2430 iter_data->has_attr = TRUE;
2431 stop_scanning = TRUE;
2434 return stop_scanning;
2438 * mono_assembly_has_reference_assembly_attribute:
2439 * \param assembly a MonoAssembly
2440 * \param error set on error.
2442 * \returns TRUE if \p assembly has the \c System.Runtime.CompilerServices.ReferenceAssemblyAttribute set.
2443 * On error returns FALSE and sets \p error.
2445 gboolean
2446 mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
2448 g_assert (assembly && assembly->image);
2449 /* .NET Framework appears to ignore the attribute on dynamic
2450 * assemblies, so don't call this function for dynamic assemblies. */
2451 g_assert (!image_is_dynamic (assembly->image));
2452 error_init (error);
2455 * This might be called during assembly loading, so do everything using the low-level
2456 * metadata APIs.
2459 struct HasReferenceAssemblyAttributeIterData iter_data = { FALSE };
2461 mono_assembly_metadata_foreach_custom_attr (assembly, &has_reference_assembly_attribute_iterator, &iter_data);
2463 return iter_data.has_attr;
2467 * chain_redirections_loadfrom:
2468 * \param image a MonoImage that we wanted to load using LoadFrom context
2469 * \param status set if there was an error opening the redirected image
2471 * Check if we need to open a different image instead of the given one for some reason.
2472 * Returns NULL and sets status to \c MONO_IMAGE_OK if the given image was good.
2474 * Otherwise returns the assembly that we opened instead or sets status if
2475 * there was a problem opening the redirected image.
2478 MonoAssembly*
2479 chain_redirections_loadfrom (MonoImage *image, MonoImageOpenStatus *out_status)
2481 MonoImageOpenStatus status = MONO_IMAGE_OK;
2482 MonoAssembly *redirected = NULL;
2484 redirected = mono_assembly_binding_applies_to_image (image, &status);
2485 if (redirected || status != MONO_IMAGE_OK) {
2486 *out_status = status;
2487 return redirected;
2490 redirected = mono_problematic_image_reprobe (image, &status);
2491 if (redirected || status != MONO_IMAGE_OK) {
2492 *out_status = status;
2493 return redirected;
2496 *out_status = MONO_IMAGE_OK;
2497 return NULL;
2501 * mono_assembly_binding_applies_to_image:
2502 * \param image The image whose assembly name we should check
2503 * \param status sets on error;
2505 * Get the \c MonoAssemblyName from the given \p image metadata and apply binding redirects to it.
2506 * If the resulting name is different from the name in the image, load that \c MonoAssembly instead
2508 * \returns the loaded \c MonoAssembly, or NULL if no binding redirection applied.
2511 MonoAssembly*
2512 mono_assembly_binding_applies_to_image (MonoImage* image, MonoImageOpenStatus *status)
2514 /* This is a "fun" one now.
2515 * For LoadFrom ("/basedir/some.dll") or LoadFile("/basedir/some.dll") or Load(byte[])),
2516 * apparently what we're meant to do is:
2517 * 1. probe the assembly name from some.dll (or the byte array)
2518 * 2. apply binding redirects
2519 * 3. If we get some other different name, drop this image and use
2520 * the binding redirected name to probe.
2521 * 4. Return the new assembly.
2523 MonoAssemblyName probed_aname, dest_name;
2524 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2525 if (*status == MONO_IMAGE_OK)
2526 *status = MONO_IMAGE_IMAGE_INVALID;
2527 return NULL;
2529 MonoAssembly *result_ass = NULL;
2530 MonoAssemblyName *result_name = &probed_aname;
2531 result_name = mono_assembly_apply_binding (result_name, &dest_name);
2532 if (result_name != &probed_aname && !mono_assembly_names_equal (result_name, &probed_aname)) {
2533 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2534 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2535 char *result_fullname = mono_stringify_assembly_name (result_name);
2536 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Request to load from %s in (%s) remapped to %s", probed_fullname, image->name, result_fullname);
2537 g_free (probed_fullname);
2538 g_free (result_fullname);
2540 const char *new_basedir = NULL; /* FIXME: null? - do a test of this */
2541 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT; /* FIXME: default? or? */
2542 MonoAssembly *new_requesting = NULL; /* this seems okay */
2543 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2545 result_ass = mono_assembly_load_full_internal (result_name, new_requesting, new_basedir, new_asmctx, &new_status);
2547 if (result_ass && new_status == MONO_IMAGE_OK) {
2548 g_assert (result_ass->image->assembly != NULL);
2549 } else {
2550 *status = new_status;
2553 mono_assembly_name_free (&probed_aname);
2554 return result_ass;
2558 * mono_problematic_image_reprobe:
2559 * \param image A MonoImage
2560 * \param status set on error
2562 * If the given image is problematic for mono (see image.c), then try to load
2563 * by assembly name in the default context (which should succeed with Mono's
2564 * own implementations of those assemblies).
2566 * Returns NULL and sets status to MONO_IMAGE_OK if no redirect is needed.
2568 * Otherwise returns the assembly we were redirected to, or NULL and sets a
2569 * non-ok status on failure.
2571 * IMPORTANT NOTE: Don't call this if \c image was found by probing the search
2572 * path, you will end up in a loop and a stack overflow.
2574 MonoAssembly*
2575 mono_problematic_image_reprobe (MonoImage *image, MonoImageOpenStatus *status)
2577 if (G_LIKELY (!mono_is_problematic_image (image))) {
2578 *status = MONO_IMAGE_OK;
2579 return NULL;
2581 MonoAssemblyName probed_aname;
2582 if (!mono_assembly_fill_assembly_name_full (image, &probed_aname, TRUE)) {
2583 *status = MONO_IMAGE_IMAGE_INVALID;
2584 return NULL;
2586 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
2587 char *probed_fullname = mono_stringify_assembly_name (&probed_aname);
2588 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Requested to load from problematic image %s, probing instead for assembly with name %s", image->name, probed_fullname);
2589 g_free (probed_fullname);
2591 const char *new_basedir = NULL;
2592 MonoAssemblyContextKind new_asmctx = MONO_ASMCTX_DEFAULT;
2593 MonoAssembly *new_requesting = NULL;
2594 MonoImageOpenStatus new_status = MONO_IMAGE_OK;
2595 // Note: this interacts with mono_image_open_a_lot (). If the path from
2596 // which we tried to load the problematic image is among the probing
2597 // paths, the MonoImage will be in the hash of loaded images and we
2598 // would just get it back again here, except for the code there that
2599 // mitigates the situation. Instead
2600 MonoAssembly *result_ass = mono_assembly_load_full_internal (&probed_aname, new_requesting, new_basedir, new_asmctx, &new_status);
2602 if (! (result_ass && new_status == MONO_IMAGE_OK)) {
2603 *status = new_status;
2605 mono_assembly_name_free (&probed_aname);
2606 return result_ass;
2609 * mono_assembly_open:
2610 * \param filename Opens the assembly pointed out by this name
2611 * \param status return status code
2613 * This loads an assembly from the specified \p filename. The \p filename allows
2614 * a local URL (starting with a \c file:// prefix). If a file prefix is used, the
2615 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
2616 * is treated as a local path.
2618 * First, an attempt is made to load the assembly from the bundled executable (for those
2619 * deployments that have been done with the \c mkbundle tool or for scenarios where the
2620 * assembly has been registered as an embedded assembly). If this is not the case, then
2621 * the assembly is loaded from disk using `api:mono_image_open_full`.
2623 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
2624 * the assembly is made.
2626 * \returns a pointer to the \c MonoAssembly if \p filename contains a valid
2627 * assembly or NULL on error. Details about the error are stored in the
2628 * \p status variable.
2630 MonoAssembly *
2631 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
2633 return mono_assembly_open_predicate (filename, MONO_ASMCTX_DEFAULT, NULL, NULL, NULL, status);
2637 * mono_assembly_load_from_full:
2638 * \param image Image to load the assembly from
2639 * \param fname assembly name to associate with the assembly
2640 * \param status returns the status condition
2641 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
2643 * If the provided \p image has an assembly reference, it will process the given
2644 * image as an assembly with the given name.
2646 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2648 * Returns: A valid pointer to a \c MonoAssembly* on success and the \p status will be
2649 * set to \c MONO_IMAGE_OK; or NULL on error.
2651 * If there is an error loading the assembly the \p status will indicate the
2652 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
2653 * image did not contain an assembly reference table.
2655 MonoAssembly *
2656 mono_assembly_load_from_full (MonoImage *image, const char*fname,
2657 MonoImageOpenStatus *status, gboolean refonly)
2659 return mono_assembly_load_from_predicate (image, fname, refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT, NULL, NULL, status);
2662 MonoAssembly *
2663 mono_assembly_load_from_predicate (MonoImage *image, const char *fname,
2664 MonoAssemblyContextKind asmctx,
2665 MonoAssemblyCandidatePredicate predicate,
2666 gpointer user_data,
2667 MonoImageOpenStatus *status)
2669 MonoAssembly *ass, *ass2;
2670 char *base_dir;
2672 if (!image->tables [MONO_TABLE_ASSEMBLY].rows) {
2673 /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */
2674 *status = MONO_IMAGE_IMAGE_INVALID;
2675 return NULL;
2678 #if defined (HOST_WIN32)
2680 gchar *tmp_fn;
2681 int i;
2683 tmp_fn = g_strdup (fname);
2684 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
2685 if (tmp_fn [i] == '/')
2686 tmp_fn [i] = '\\';
2689 base_dir = absolute_dir (tmp_fn);
2690 g_free (tmp_fn);
2692 #else
2693 base_dir = absolute_dir (fname);
2694 #endif
2697 * Create assembly struct, and enter it into the assembly cache
2699 ass = g_new0 (MonoAssembly, 1);
2700 ass->basedir = base_dir;
2701 ass->context.kind = asmctx;
2702 ass->image = image;
2704 MONO_PROFILER_RAISE (assembly_loading, (ass));
2706 mono_assembly_fill_assembly_name (image, &ass->aname);
2708 if (mono_defaults.corlib && strcmp (ass->aname.name, "mscorlib") == 0) {
2709 // MS.NET doesn't support loading other mscorlibs
2710 g_free (ass);
2711 g_free (base_dir);
2712 mono_image_addref (mono_defaults.corlib);
2713 *status = MONO_IMAGE_OK;
2714 return mono_defaults.corlib->assembly;
2717 /* Add a non-temporary reference because of ass->image */
2718 mono_image_addref (image);
2720 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s[%p] (asmctx %s) -> %s[%p]: %d", ass->aname.name, ass, mono_asmctx_get_name (&ass->context), image->name, image, image->ref_count);
2723 * The load hooks might take locks so we can't call them while holding the
2724 * assemblies lock.
2726 if (ass->aname.name && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2727 /* FIXME: I think individual context should probably also look for an existing MonoAssembly here, we just need to pass the asmctx to the search hook so that it does a filename match (I guess?) */
2728 ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, NULL, asmctx == MONO_ASMCTX_REFONLY, FALSE);
2729 if (ass2) {
2730 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image %s[%p] reusing existing assembly %s[%p]", ass->aname.name, ass, ass2->aname.name, ass2);
2731 g_free (ass);
2732 g_free (base_dir);
2733 mono_image_close (image);
2734 *status = MONO_IMAGE_OK;
2735 return ass2;
2739 /* We need to check for ReferenceAssmeblyAttribute before we
2740 * mark the assembly as loaded and before we fire the load
2741 * hook. Otherwise mono_domain_fire_assembly_load () in
2742 * appdomain.c will cache a mapping from the assembly name to
2743 * this image and we won't be able to look for a different
2744 * candidate. */
2746 if (asmctx != MONO_ASMCTX_REFONLY) {
2747 ERROR_DECL_VALUE (refasm_error);
2748 if (mono_assembly_has_reference_assembly_attribute (ass, &refasm_error)) {
2749 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name);
2750 g_free (ass);
2751 g_free (base_dir);
2752 mono_image_close (image);
2753 *status = MONO_IMAGE_IMAGE_INVALID;
2754 return NULL;
2756 mono_error_cleanup (&refasm_error);
2759 if (predicate && !predicate (ass, user_data)) {
2760 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate returned FALSE, skipping '%s' (%s)\n", ass->aname.name, image->name);
2761 g_free (ass);
2762 g_free (base_dir);
2763 mono_image_close (image);
2764 *status = MONO_IMAGE_IMAGE_INVALID;
2765 return NULL;
2768 mono_assemblies_lock ();
2770 /* If an assembly is loaded into an individual context, always return a
2771 * new MonoAssembly, even if another assembly with the same name has
2772 * already been loaded.
2774 if (image->assembly && asmctx != MONO_ASMCTX_INDIVIDUAL) {
2776 * This means another thread has already loaded the assembly, but not yet
2777 * called the load hooks so the search hook can't find the assembly.
2779 mono_assemblies_unlock ();
2780 ass2 = image->assembly;
2781 g_free (ass);
2782 g_free (base_dir);
2783 mono_image_close (image);
2784 *status = MONO_IMAGE_OK;
2785 return ass2;
2788 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name);
2790 /* If asmctx is INDIVIDUAL, image->assembly might not be NULL, so don't
2791 * overwrite it. */
2792 if (image->assembly == NULL)
2793 image->assembly = ass;
2795 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
2796 mono_assemblies_unlock ();
2798 #ifdef HOST_WIN32
2799 if (image->is_module_handle)
2800 mono_image_fixup_vtable (image);
2801 #endif
2803 mono_assembly_invoke_load_hook (ass);
2805 MONO_PROFILER_RAISE (assembly_loaded, (ass));
2807 return ass;
2811 * mono_assembly_load_from:
2812 * \param image Image to load the assembly from
2813 * \param fname assembly name to associate with the assembly
2814 * \param status return status code
2816 * If the provided \p image has an assembly reference, it will process the given
2817 * image as an assembly with the given name.
2819 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2821 * This is equivalent to calling `api:mono_assembly_load_from_full` with the
2822 * \p refonly parameter set to FALSE.
2823 * \returns A valid pointer to a \c MonoAssembly* on success and then \p status will be
2824 * set to \c MONO_IMAGE_OK; or NULL on error.
2826 * If there is an error loading the assembly the \p status will indicate the
2827 * reason with \p status being set to \c MONO_IMAGE_INVALID if the
2828 * image did not contain an assembly reference table.
2831 MonoAssembly *
2832 mono_assembly_load_from (MonoImage *image, const char *fname,
2833 MonoImageOpenStatus *status)
2835 return mono_assembly_load_from_full (image, fname, status, FALSE);
2839 * mono_assembly_name_free:
2840 * \param aname assembly name to free
2842 * Frees the provided assembly name object.
2843 * (it does not frees the object itself, only the name members).
2845 void
2846 mono_assembly_name_free (MonoAssemblyName *aname)
2848 if (aname == NULL)
2849 return;
2851 g_free ((void *) aname->name);
2852 g_free ((void *) aname->culture);
2853 g_free ((void *) aname->hash_value);
2854 g_free ((guint8*) aname->public_key);
2857 static gboolean
2858 parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma)
2860 const gchar *pkey;
2861 gchar header [16], val, *arr, *endp;
2862 gint i, j, offset, bitlen, keylen, pkeylen;
2864 //both pubkey and is_ecma are required arguments
2865 g_assert (pubkey && is_ecma);
2867 keylen = strlen (key) >> 1;
2868 if (keylen < 1)
2869 return FALSE;
2871 /* allow the ECMA standard key */
2872 if (strcmp (key, "00000000000000000400000000000000") == 0) {
2873 *pubkey = NULL;
2874 *is_ecma = TRUE;
2875 return TRUE;
2877 *is_ecma = FALSE;
2878 val = g_ascii_xdigit_value (key [0]) << 4;
2879 val |= g_ascii_xdigit_value (key [1]);
2880 switch (val) {
2881 case 0x00:
2882 if (keylen < 13)
2883 return FALSE;
2884 val = g_ascii_xdigit_value (key [24]);
2885 val |= g_ascii_xdigit_value (key [25]);
2886 if (val != 0x06)
2887 return FALSE;
2888 pkey = key + 24;
2889 break;
2890 case 0x06:
2891 pkey = key;
2892 break;
2893 default:
2894 return FALSE;
2897 /* We need the first 16 bytes
2898 * to check whether this key is valid or not */
2899 pkeylen = strlen (pkey) >> 1;
2900 if (pkeylen < 16)
2901 return FALSE;
2903 for (i = 0, j = 0; i < 16; i++) {
2904 header [i] = g_ascii_xdigit_value (pkey [j++]) << 4;
2905 header [i] |= g_ascii_xdigit_value (pkey [j++]);
2908 if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */
2909 header [1] != 0x02 || /* Version (0x02) */
2910 header [2] != 0x00 || /* Reserved (word) */
2911 header [3] != 0x00 ||
2912 (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */
2913 return FALSE;
2915 /* Based on this length, we _should_ be able to know if the length is right */
2916 bitlen = read32 (header + 12) >> 3;
2917 if ((bitlen + 16 + 4) != pkeylen)
2918 return FALSE;
2920 arr = (gchar *)g_malloc (keylen + 4);
2921 /* Encode the size of the blob */
2922 mono_metadata_encode_value (keylen, &arr[0], &endp);
2923 offset = (gint)(endp-arr);
2925 for (i = offset, j = 0; i < keylen + offset; i++) {
2926 arr [i] = g_ascii_xdigit_value (key [j++]) << 4;
2927 arr [i] |= g_ascii_xdigit_value (key [j++]);
2930 *pubkey = arr;
2932 return TRUE;
2935 static gboolean
2936 build_assembly_name (const char *name, const char *version, const char *culture, const char *token, const char *key, guint32 flags, guint32 arch, MonoAssemblyName *aname, gboolean save_public_key)
2938 gint major, minor, build, revision;
2939 gint len;
2940 gint version_parts;
2941 gchar *pkeyptr, *encoded, tok [8];
2943 memset (aname, 0, sizeof (MonoAssemblyName));
2945 if (version) {
2946 version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision);
2947 if (version_parts < 2 || version_parts > 4)
2948 return FALSE;
2950 /* FIXME: we should set build & revision to -1 (instead of 0)
2951 if these are not set in the version string. That way, later on,
2952 we can still determine if these were specified. */
2953 aname->major = major;
2954 aname->minor = minor;
2955 if (version_parts >= 3)
2956 aname->build = build;
2957 else
2958 aname->build = 0;
2959 if (version_parts == 4)
2960 aname->revision = revision;
2961 else
2962 aname->revision = 0;
2965 aname->flags = flags;
2966 aname->arch = arch;
2967 aname->name = g_strdup (name);
2969 if (culture) {
2970 if (g_ascii_strcasecmp (culture, "neutral") == 0)
2971 aname->culture = g_strdup ("");
2972 else
2973 aname->culture = g_strdup (culture);
2976 if (token && strncmp (token, "null", 4) != 0) {
2977 char *lower;
2979 /* the constant includes the ending NULL, hence the -1 */
2980 if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) {
2981 mono_assembly_name_free (aname);
2982 return FALSE;
2984 lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
2985 g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH);
2986 g_free (lower);
2989 if (key) {
2990 gboolean is_ecma = FALSE;
2991 gchar *pkey = NULL;
2992 if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) {
2993 mono_assembly_name_free (aname);
2994 return FALSE;
2997 if (is_ecma) {
2998 g_assert (pkey == NULL);
2999 aname->public_key = NULL;
3000 g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH);
3001 return TRUE;
3004 len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr);
3005 // We also need to generate the key token
3006 mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len);
3007 encoded = encode_public_tok ((guchar*) tok, 8);
3008 g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3009 g_free (encoded);
3011 if (save_public_key)
3012 aname->public_key = (guint8*) pkey;
3013 else
3014 g_free (pkey);
3017 return TRUE;
3020 static gboolean
3021 parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname)
3023 gchar **parts;
3024 gboolean res;
3026 parts = g_strsplit (dirname, "_", 3);
3027 if (!parts || !parts[0] || !parts[1] || !parts[2]) {
3028 g_strfreev (parts);
3029 return FALSE;
3032 res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE);
3033 g_strfreev (parts);
3034 return res;
3037 static gboolean
3038 split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value)
3040 char *eqsign = strchr (pair, '=');
3041 if (!eqsign) {
3042 *key = NULL;
3043 *keylen = 0;
3044 *value = NULL;
3045 return FALSE;
3048 *key = (gchar*)pair;
3049 *keylen = eqsign - *key;
3050 while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1]))
3051 (*keylen)--;
3052 *value = g_strstrip (eqsign + 1);
3053 return TRUE;
3056 gboolean
3057 mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined)
3059 gchar *dllname;
3060 gchar *dllname_uq;
3061 gchar *version = NULL;
3062 gchar *version_uq;
3063 gchar *culture = NULL;
3064 gchar *culture_uq;
3065 gchar *token = NULL;
3066 gchar *token_uq;
3067 gchar *key = NULL;
3068 gchar *key_uq;
3069 gchar *retargetable = NULL;
3070 gchar *retargetable_uq;
3071 gchar *procarch;
3072 gchar *procarch_uq;
3073 gboolean res;
3074 gchar *value, *part_name;
3075 guint32 part_name_len;
3076 gchar **parts;
3077 gchar **tmp;
3078 gboolean version_defined;
3079 gboolean token_defined;
3080 guint32 flags = 0;
3081 guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
3083 if (!is_version_defined)
3084 is_version_defined = &version_defined;
3085 *is_version_defined = FALSE;
3086 if (!is_token_defined)
3087 is_token_defined = &token_defined;
3088 *is_token_defined = FALSE;
3090 parts = tmp = g_strsplit (name, ",", 6);
3091 if (!tmp || !*tmp) {
3092 g_strfreev (tmp);
3093 return FALSE;
3096 dllname = g_strstrip (*tmp);
3098 tmp++;
3100 while (*tmp) {
3101 if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value))
3102 goto cleanup_and_fail;
3104 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) {
3105 *is_version_defined = TRUE;
3106 version = value;
3107 if (strlen (version) == 0) {
3108 goto cleanup_and_fail;
3110 tmp++;
3111 continue;
3114 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) {
3115 culture = value;
3116 if (strlen (culture) == 0) {
3117 goto cleanup_and_fail;
3119 tmp++;
3120 continue;
3123 if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) {
3124 *is_token_defined = TRUE;
3125 token = value;
3126 if (strlen (token) == 0) {
3127 goto cleanup_and_fail;
3129 tmp++;
3130 continue;
3133 if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) {
3134 key = value;
3135 if (strlen (key) == 0) {
3136 goto cleanup_and_fail;
3138 tmp++;
3139 continue;
3142 if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) {
3143 retargetable = value;
3144 retargetable_uq = unquote (retargetable);
3145 if (retargetable_uq != NULL)
3146 retargetable = retargetable_uq;
3148 if (!g_ascii_strcasecmp (retargetable, "yes")) {
3149 flags |= ASSEMBLYREF_RETARGETABLE_FLAG;
3150 } else if (g_ascii_strcasecmp (retargetable, "no")) {
3151 g_free (retargetable_uq);
3152 goto cleanup_and_fail;
3155 g_free (retargetable_uq);
3156 tmp++;
3157 continue;
3160 if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) {
3161 procarch = value;
3162 procarch_uq = unquote (procarch);
3163 if (procarch_uq != NULL)
3164 procarch = procarch_uq;
3166 if (!g_ascii_strcasecmp (procarch, "MSIL"))
3167 arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
3168 else if (!g_ascii_strcasecmp (procarch, "X86"))
3169 arch = MONO_PROCESSOR_ARCHITECTURE_X86;
3170 else if (!g_ascii_strcasecmp (procarch, "IA64"))
3171 arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
3172 else if (!g_ascii_strcasecmp (procarch, "AMD64"))
3173 arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
3174 else {
3175 g_free (procarch_uq);
3176 goto cleanup_and_fail;
3179 g_free (procarch_uq);
3180 tmp++;
3181 continue;
3184 g_strfreev (parts);
3185 return FALSE;
3188 /* if retargetable flag is set, then we must have a fully qualified name */
3189 if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) {
3190 goto cleanup_and_fail;
3193 dllname_uq = unquote (dllname);
3194 version_uq = unquote (version);
3195 culture_uq = unquote (culture);
3196 token_uq = unquote (token);
3197 key_uq = unquote (key);
3199 res = build_assembly_name (
3200 dllname_uq == NULL ? dllname : dllname_uq,
3201 version_uq == NULL ? version : version_uq,
3202 culture_uq == NULL ? culture : culture_uq,
3203 token_uq == NULL ? token : token_uq,
3204 key_uq == NULL ? key : key_uq,
3205 flags, arch, aname, save_public_key);
3207 g_free (dllname_uq);
3208 g_free (version_uq);
3209 g_free (culture_uq);
3210 g_free (token_uq);
3211 g_free (key_uq);
3213 g_strfreev (parts);
3214 return res;
3216 cleanup_and_fail:
3217 g_strfreev (parts);
3218 return FALSE;
3221 static char*
3222 unquote (const char *str)
3224 gint slen;
3225 const char *end;
3227 if (str == NULL)
3228 return NULL;
3230 slen = strlen (str);
3231 if (slen < 2)
3232 return NULL;
3234 if (*str != '\'' && *str != '\"')
3235 return NULL;
3237 end = str + slen - 1;
3238 if (*str != *end)
3239 return NULL;
3241 return g_strndup (str + 1, slen - 2);
3245 * mono_assembly_name_parse:
3246 * \param name name to parse
3247 * \param aname the destination assembly name
3249 * Parses an assembly qualified type name and assigns the name,
3250 * version, culture and token to the provided assembly name object.
3252 * \returns TRUE if the name could be parsed.
3254 gboolean
3255 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
3257 return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL);
3261 * mono_assembly_name_new:
3262 * \param name name to parse
3264 * Allocate a new \c MonoAssemblyName and fill its values from the
3265 * passed \p name.
3267 * \returns a newly allocated structure or NULL if there was any failure.
3269 MonoAssemblyName*
3270 mono_assembly_name_new (const char *name)
3272 MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1);
3273 if (mono_assembly_name_parse (name, aname))
3274 return aname;
3275 g_free (aname);
3276 return NULL;
3280 * mono_assembly_name_get_name:
3282 const char*
3283 mono_assembly_name_get_name (MonoAssemblyName *aname)
3285 return aname->name;
3289 * mono_assembly_name_get_culture:
3291 const char*
3292 mono_assembly_name_get_culture (MonoAssemblyName *aname)
3294 return aname->culture;
3298 * mono_assembly_name_get_pubkeytoken:
3300 mono_byte*
3301 mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname)
3303 if (aname->public_key_token [0])
3304 return aname->public_key_token;
3305 return NULL;
3309 * mono_assembly_name_get_version:
3311 uint16_t
3312 mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision)
3314 if (minor)
3315 *minor = aname->minor;
3316 if (build)
3317 *build = aname->build;
3318 if (revision)
3319 *revision = aname->revision;
3320 return aname->major;
3323 static MonoAssembly*
3324 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
3326 gchar *fullpath = NULL;
3327 GDir *dirhandle;
3328 const char* direntry;
3329 MonoAssemblyName gac_aname;
3330 gint major=-1, minor=0, build=0, revision=0;
3331 gboolean exact_version;
3333 dirhandle = g_dir_open (basepath, 0, NULL);
3334 if (!dirhandle)
3335 return NULL;
3337 exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0;
3339 while ((direntry = g_dir_read_name (dirhandle))) {
3340 gboolean match = TRUE;
3342 if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname))
3343 continue;
3345 if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0)
3346 match = FALSE;
3348 if (match && strlen ((char*)aname->public_key_token) > 0 &&
3349 !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token))
3350 match = FALSE;
3352 if (match) {
3353 if (exact_version) {
3354 match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor &&
3355 aname->build == gac_aname.build && aname->revision == gac_aname.revision);
3357 else if (gac_aname.major < major)
3358 match = FALSE;
3359 else if (gac_aname.major == major) {
3360 if (gac_aname.minor < minor)
3361 match = FALSE;
3362 else if (gac_aname.minor == minor) {
3363 if (gac_aname.build < build)
3364 match = FALSE;
3365 else if (gac_aname.build == build && gac_aname.revision <= revision)
3366 match = FALSE;
3371 if (match) {
3372 major = gac_aname.major;
3373 minor = gac_aname.minor;
3374 build = gac_aname.build;
3375 revision = gac_aname.revision;
3376 g_free (fullpath);
3377 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
3380 mono_assembly_name_free (&gac_aname);
3383 g_dir_close (dirhandle);
3385 if (fullpath == NULL)
3386 return NULL;
3387 else {
3388 MonoAssembly *res = mono_assembly_open_predicate (fullpath, MONO_ASMCTX_DEFAULT, NULL, NULL, NULL, status);
3389 g_free (fullpath);
3390 return res;
3395 * mono_assembly_load_with_partial_name:
3396 * \param name an assembly name that is then parsed by `api:mono_assembly_name_parse`.
3397 * \param status return status code
3399 * Loads a \c MonoAssembly from a name. The name is parsed using `api:mono_assembly_name_parse`,
3400 * so it might contain a qualified type name, version, culture and token.
3402 * This will load the assembly from the file whose name is derived from the assembly name
3403 * by appending the \c .dll extension.
3405 * The assembly is loaded from either one of the extra Global Assembly Caches specified
3406 * by the extra GAC paths (specified by the \c MONO_GAC_PREFIX environment variable) or
3407 * if that fails from the GAC.
3409 * \returns NULL on failure, or a pointer to a \c MonoAssembly on success.
3411 MonoAssembly*
3412 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
3414 ERROR_DECL (error);
3415 MonoAssembly *res;
3416 MonoAssemblyName *aname, base_name;
3417 MonoAssemblyName mapped_aname;
3418 gchar *fullname, *gacpath;
3419 gchar **paths;
3421 memset (&base_name, 0, sizeof (MonoAssemblyName));
3422 aname = &base_name;
3424 if (!mono_assembly_name_parse (name, aname))
3425 return NULL;
3428 * If no specific version has been requested, make sure we load the
3429 * correct version for system assemblies.
3431 if ((aname->major | aname->minor | aname->build | aname->revision) == 0)
3432 aname = mono_assembly_remap_version (aname, &mapped_aname);
3434 res = mono_assembly_loaded (aname);
3435 if (res) {
3436 mono_assembly_name_free (aname);
3437 return res;
3440 res = invoke_assembly_preload_hook (aname, assemblies_path);
3441 if (res) {
3442 res->in_gac = FALSE;
3443 mono_assembly_name_free (aname);
3444 return res;
3447 fullname = g_strdup_printf ("%s.dll", aname->name);
3449 if (extra_gac_paths) {
3450 paths = extra_gac_paths;
3451 while (!res && *paths) {
3452 gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL);
3453 res = probe_for_partial_name (gacpath, fullname, aname, status);
3454 g_free (gacpath);
3455 paths++;
3459 if (res) {
3460 res->in_gac = TRUE;
3461 g_free (fullname);
3462 mono_assembly_name_free (aname);
3463 return res;
3466 gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL);
3467 res = probe_for_partial_name (gacpath, fullname, aname, status);
3468 g_free (gacpath);
3470 g_free (fullname);
3471 mono_assembly_name_free (aname);
3473 if (res)
3474 res->in_gac = TRUE;
3475 else {
3476 MonoDomain *domain = mono_domain_get ();
3478 res = mono_try_assembly_resolve (domain, name, NULL, FALSE, error);
3479 if (!is_ok (error)) {
3480 mono_error_cleanup (error);
3481 if (*status == MONO_IMAGE_OK)
3482 *status = MONO_IMAGE_IMAGE_INVALID;
3486 return res;
3489 static MonoBoolean
3490 mono_assembly_is_in_gac (const gchar *filename)
3492 const gchar *rootdir;
3493 gchar *gp;
3494 gchar **paths;
3496 if (filename == NULL)
3497 return FALSE;
3499 for (paths = extra_gac_paths; paths && *paths; paths++) {
3500 if (strstr (*paths, filename) != *paths)
3501 continue;
3503 gp = (gchar *) (filename + strlen (*paths));
3504 if (*gp != G_DIR_SEPARATOR)
3505 continue;
3506 gp++;
3507 if (strncmp (gp, "lib", 3))
3508 continue;
3509 gp += 3;
3510 if (*gp != G_DIR_SEPARATOR)
3511 continue;
3512 gp++;
3513 if (strncmp (gp, "mono", 4))
3514 continue;
3515 gp += 4;
3516 if (*gp != G_DIR_SEPARATOR)
3517 continue;
3518 gp++;
3519 if (strncmp (gp, "gac", 3))
3520 continue;
3521 gp += 3;
3522 if (*gp != G_DIR_SEPARATOR)
3523 continue;
3525 return TRUE;
3528 rootdir = mono_assembly_getrootdir ();
3529 if (strstr (filename, rootdir) != filename)
3530 return FALSE;
3532 gp = (gchar *) (filename + strlen (rootdir));
3533 if (*gp != G_DIR_SEPARATOR)
3534 return FALSE;
3535 gp++;
3536 if (strncmp (gp, "mono", 4))
3537 return FALSE;
3538 gp += 4;
3539 if (*gp != G_DIR_SEPARATOR)
3540 return FALSE;
3541 gp++;
3542 if (strncmp (gp, "gac", 3))
3543 return FALSE;
3544 gp += 3;
3545 if (*gp != G_DIR_SEPARATOR)
3546 return FALSE;
3547 return TRUE;
3550 static MonoImage*
3551 mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
3553 MonoImage *image;
3554 gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath;
3555 gchar **paths;
3556 gint32 len;
3558 if (strstr (aname->name, ".dll")) {
3559 len = strlen (aname->name) - 4;
3560 name = (gchar *)g_malloc (len + 1);
3561 memcpy (name, aname->name, len);
3562 name[len] = 0;
3563 } else
3564 name = g_strdup (aname->name);
3566 if (aname->culture)
3567 culture = g_utf8_strdown (aname->culture, -1);
3568 else
3569 culture = g_strdup ("");
3571 pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name);
3572 version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token);
3573 g_free (name);
3574 g_free (culture);
3576 filename = g_strconcat (pname, ".dll", NULL);
3577 subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL);
3578 g_free (pname);
3579 g_free (version);
3580 g_free (filename);
3582 image = NULL;
3583 if (extra_gac_paths) {
3584 paths = extra_gac_paths;
3585 while (!image && *paths) {
3586 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths,
3587 "lib", "mono", "gac", subpath, NULL);
3588 image = mono_image_open (fullpath, NULL);
3589 g_free (fullpath);
3590 paths++;
3594 if (image) {
3595 g_free (subpath);
3596 return image;
3599 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
3600 "mono", "gac", subpath, NULL);
3601 image = mono_image_open (fullpath, NULL);
3602 g_free (subpath);
3603 g_free (fullpath);
3605 return image;
3608 static MonoAssemblyName*
3609 mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3611 memcpy (dest_name, aname, sizeof (MonoAssemblyName));
3612 dest_name->major = info->new_version.major;
3613 dest_name->minor = info->new_version.minor;
3614 dest_name->build = info->new_version.build;
3615 dest_name->revision = info->new_version.revision;
3617 return dest_name;
3620 /* LOCKING: assembly_binding lock must be held */
3621 static MonoAssemblyBindingInfo*
3622 search_binding_loaded (MonoAssemblyName *aname)
3624 GSList *tmp;
3626 for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) {
3627 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data;
3628 if (assembly_binding_maps_name (info, aname))
3629 return info;
3632 return NULL;
3635 static inline gboolean
3636 info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right)
3638 if (left->major != right->major || left->minor != right->minor ||
3639 left->build != right->build || left->revision != right->revision)
3640 return FALSE;
3642 return TRUE;
3645 static inline gboolean
3646 info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right)
3648 if (left->has_old_version_bottom != right->has_old_version_bottom)
3649 return FALSE;
3651 if (left->has_old_version_top != right->has_old_version_top)
3652 return FALSE;
3654 if (left->has_new_version != right->has_new_version)
3655 return FALSE;
3657 if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom))
3658 return FALSE;
3660 if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top))
3661 return FALSE;
3663 if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version))
3664 return FALSE;
3666 return TRUE;
3669 /* LOCKING: assumes all the necessary locks are held */
3670 static void
3671 assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data)
3673 MonoAssemblyBindingInfo *info_copy;
3674 GSList *tmp;
3675 MonoAssemblyBindingInfo *info_tmp;
3676 MonoDomain *domain = (MonoDomain*)user_data;
3678 if (!domain)
3679 return;
3681 if (info->has_new_version && mono_assembly_is_problematic_version (info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision)) {
3682 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Discarding assembly binding to problematic version %s v%d.%d.%d.%d",
3683 info->name, info->new_version.major, info->new_version.minor, info->new_version.build, info->new_version.revision);
3684 return;
3687 for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) {
3688 info_tmp = (MonoAssemblyBindingInfo *)tmp->data;
3689 if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp))
3690 return;
3693 info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo));
3694 memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo));
3695 if (info->name)
3696 info_copy->name = mono_mempool_strdup (domain->mp, info->name);
3697 if (info->culture)
3698 info_copy->culture = mono_mempool_strdup (domain->mp, info->culture);
3700 domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy);
3703 static int
3704 get_version_number (int major, int minor)
3706 return major * 256 + minor;
3709 static inline gboolean
3710 info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
3712 int aname_version_number = get_version_number (aname->major, aname->minor);
3713 if (!info->has_old_version_bottom)
3714 return FALSE;
3716 if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number)
3717 return FALSE;
3719 if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number)
3720 return FALSE;
3722 /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */
3723 info->major = aname->major;
3724 info->minor = aname->minor;
3726 return TRUE;
3729 /* LOCKING: Assumes that we are already locked - both loader and domain locks */
3730 static MonoAssemblyBindingInfo*
3731 get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname)
3733 MonoAssemblyBindingInfo *info;
3734 GSList *list;
3736 if (!domain->assembly_bindings)
3737 return NULL;
3739 info = NULL;
3740 for (list = domain->assembly_bindings; list; list = list->next) {
3741 info = (MonoAssemblyBindingInfo *)list->data;
3742 if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname))
3743 break;
3744 info = NULL;
3747 if (info) {
3748 if (info->name && info->public_key_token [0] && info->has_old_version_bottom &&
3749 info->has_new_version && assembly_binding_maps_name (info, aname))
3750 info->is_valid = TRUE;
3751 else
3752 info->is_valid = FALSE;
3755 return info;
3758 void
3759 mono_domain_parse_assembly_bindings (MonoDomain *domain, int amajor, int aminor, gchar *domain_config_file_name)
3761 if (domain->assembly_bindings_parsed)
3762 return;
3763 mono_domain_lock (domain);
3764 if (!domain->assembly_bindings_parsed) {
3766 gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE);
3768 if (!domain_config_file_path)
3769 domain_config_file_path = domain_config_file_name;
3771 mono_config_parse_assembly_bindings (domain_config_file_path, amajor, aminor, domain, assembly_binding_info_parsed);
3772 domain->assembly_bindings_parsed = TRUE;
3773 if (domain_config_file_name != domain_config_file_path)
3774 g_free (domain_config_file_path);
3777 mono_domain_unlock (domain);
3780 static MonoAssemblyName*
3781 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3783 ERROR_DECL (error);
3784 MonoAssemblyBindingInfo *info, *info2;
3785 MonoImage *ppimage;
3786 MonoDomain *domain;
3788 if (aname->public_key_token [0] == 0)
3789 return aname;
3791 domain = mono_domain_get ();
3793 mono_assembly_binding_lock ();
3794 info = search_binding_loaded (aname);
3795 mono_assembly_binding_unlock ();
3797 if (!info) {
3798 mono_domain_lock (domain);
3799 info = get_per_domain_assembly_binding_info (domain, aname);
3800 mono_domain_unlock (domain);
3803 if (info) {
3804 if (!check_policy_versions (info, aname))
3805 return aname;
3807 mono_assembly_bind_version (info, aname, dest_name);
3808 return dest_name;
3811 if (domain && domain->setup && domain->setup->configuration_file) {
3812 gchar *domain_config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, error);
3813 /* expect this to succeed because mono_domain_set_options_from_config () did
3814 * the same thing when the domain was created. */
3815 mono_error_assert_ok (error);
3816 mono_domain_parse_assembly_bindings (domain, aname->major, aname->minor, domain_config_file_name);
3817 g_free (domain_config_file_name);
3819 mono_domain_lock (domain);
3820 info2 = get_per_domain_assembly_binding_info (domain, aname);
3822 if (info2) {
3823 info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo));
3824 info->name = g_strdup (info2->name);
3825 info->culture = g_strdup (info2->culture);
3826 info->domain_id = domain->domain_id;
3829 mono_domain_unlock (domain);
3833 if (!info) {
3834 info = g_new0 (MonoAssemblyBindingInfo, 1);
3835 info->major = aname->major;
3836 info->minor = aname->minor;
3839 if (!info->is_valid) {
3840 ppimage = mono_assembly_load_publisher_policy (aname);
3841 if (ppimage) {
3842 get_publisher_policy_info (ppimage, aname, info);
3843 mono_image_close (ppimage);
3847 /* Define default error value if needed */
3848 if (!info->is_valid) {
3849 info->name = g_strdup (aname->name);
3850 info->culture = g_strdup (aname->culture);
3851 g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3854 mono_assembly_binding_lock ();
3855 info2 = search_binding_loaded (aname);
3856 if (info2) {
3857 /* This binding was added by another thread
3858 * before us */
3859 mono_assembly_binding_info_free (info);
3860 g_free (info);
3862 info = info2;
3863 } else
3864 loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info);
3866 mono_assembly_binding_unlock ();
3868 if (!info->is_valid || !check_policy_versions (info, aname))
3869 return aname;
3871 mono_assembly_bind_version (info, aname, dest_name);
3872 return dest_name;
3876 * mono_assembly_load_from_gac
3878 * \param aname The assembly name object
3880 static MonoAssembly*
3881 mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
3883 MonoAssembly *result = NULL;
3884 gchar *name, *version, *culture, *fullpath, *subpath;
3885 gint32 len;
3886 gchar **paths;
3887 char *pubtok;
3889 if (aname->public_key_token [0] == 0) {
3890 return NULL;
3893 if (strstr (aname->name, ".dll")) {
3894 len = strlen (filename) - 4;
3895 name = (gchar *)g_malloc (len + 1);
3896 memcpy (name, aname->name, len);
3897 name[len] = 0;
3898 } else {
3899 name = g_strdup (aname->name);
3902 if (aname->culture) {
3903 culture = g_utf8_strdown (aname->culture, -1);
3904 } else {
3905 culture = g_strdup ("");
3908 pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3909 version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
3910 aname->minor, aname->build, aname->revision,
3911 culture, pubtok);
3912 g_free (pubtok);
3914 subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
3915 g_free (name);
3916 g_free (version);
3917 g_free (culture);
3919 const MonoAssemblyContextKind asmctx = refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT;
3921 if (extra_gac_paths) {
3922 paths = extra_gac_paths;
3923 while (!result && *paths) {
3924 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
3925 result = mono_assembly_open_predicate (fullpath, asmctx, NULL, NULL, NULL, status);
3926 g_free (fullpath);
3927 paths++;
3931 if (result) {
3932 result->in_gac = TRUE;
3933 g_free (subpath);
3934 return result;
3937 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
3938 "mono", "gac", subpath, NULL);
3939 result = mono_assembly_open_predicate (fullpath, asmctx, NULL, NULL, NULL, status);
3940 g_free (fullpath);
3942 if (result)
3943 result->in_gac = TRUE;
3945 g_free (subpath);
3947 return result;
3950 MonoAssembly*
3951 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
3953 char *corlib_file;
3954 MonoAssemblyName *aname;
3956 if (corlib) {
3957 /* g_print ("corlib already loaded\n"); */
3958 return corlib;
3961 // A nonstandard preload hook may provide a special mscorlib assembly
3962 aname = mono_assembly_name_new ("mscorlib.dll");
3963 corlib = invoke_assembly_preload_hook (aname, assemblies_path);
3964 mono_assembly_name_free (aname);
3965 g_free (aname);
3966 if (corlib != NULL)
3967 goto return_corlib_and_facades;
3969 // This unusual directory layout can occur if mono is being built and run out of its own source repo
3970 if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
3971 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, MONO_ASMCTX_DEFAULT, NULL, NULL);
3972 if (corlib)
3973 goto return_corlib_and_facades;
3976 /* Normal case: Load corlib from mono/<version> */
3977 corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
3978 if (assemblies_path) { // Custom assemblies path
3979 corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, MONO_ASMCTX_DEFAULT, NULL, NULL);
3980 if (corlib) {
3981 g_free (corlib_file);
3982 goto return_corlib_and_facades;
3985 corlib = load_in_path (corlib_file, default_path, status, MONO_ASMCTX_DEFAULT, NULL, NULL);
3986 g_free (corlib_file);
3988 return_corlib_and_facades:
3989 if (corlib && !strcmp (runtime->framework_version, "4.5")) // FIXME: stop hardcoding 4.5 here
3990 default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir);
3992 return corlib;
3995 static MonoAssembly*
3996 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly)
3998 ERROR_DECL_VALUE (refasm_error);
3999 error_init (&refasm_error);
4000 if (candidate && !refonly) {
4001 /* .NET Framework seems to not check for ReferenceAssemblyAttribute on dynamic assemblies */
4002 if (!image_is_dynamic (candidate->image) &&
4003 mono_assembly_has_reference_assembly_attribute (candidate, &refasm_error))
4004 candidate = NULL;
4006 mono_error_cleanup (&refasm_error);
4007 return candidate;
4010 gboolean
4011 mono_assembly_candidate_predicate_sn_same_name (MonoAssembly *candidate, gpointer ud)
4013 MonoAssemblyName *wanted_name = (MonoAssemblyName*)ud;
4014 MonoAssemblyName *candidate_name = &candidate->aname;
4016 g_assert (wanted_name != NULL);
4017 g_assert (candidate_name != NULL);
4019 if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY)) {
4020 char * s = mono_stringify_assembly_name (wanted_name);
4021 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted = %s\n", s);
4022 g_free (s);
4023 s = mono_stringify_assembly_name (candidate_name);
4024 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate = %s\n", s);
4025 g_free (s);
4029 /* Wanted name has no token, not strongly named: always matches. */
4030 if (0 == wanted_name->public_key_token [0]) {
4031 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: wanted has no token, returning TRUE\n");
4032 return TRUE;
4035 /* Candidate name has no token, not strongly named: never matches */
4036 if (0 == candidate_name->public_key_token [0]) {
4037 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate has no token, returning FALSE\n");
4038 return FALSE;
4041 return exact_sn_match (wanted_name, candidate_name) ||
4042 framework_assembly_sn_match (wanted_name, candidate_name);
4045 gboolean
4046 exact_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4048 gboolean result = mono_assembly_names_equal (wanted_name, candidate_name);
4050 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s\n",
4051 result ? "match, returning TRUE" : "don't match, returning FALSE");
4052 return result;
4056 gboolean
4057 framework_assembly_sn_match (MonoAssemblyName *wanted_name, MonoAssemblyName *candidate_name)
4059 #ifndef DISABLE_DESKTOP_LOADER
4060 const AssemblyVersionMap *vmap = (AssemblyVersionMap *)g_hash_table_lookup (assembly_remapping_table, wanted_name->name);
4061 if (vmap) {
4062 if (!vmap->framework_facade_assembly) {
4063 /* If the wanted name is a framework assembly, it's enough for the name/version/culture to match. If the assembly was remapped, the public key token is likely unrelated. */
4064 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_PUBKEY);
4065 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring the public key token)", result ? "match, returning TRUE" : "don't match, returning FALSE");
4066 return result;
4067 } else {
4068 /* For facades, the name and public key token should
4069 * match, but the version doesn't matter as long as the
4070 * candidate is not older. */
4071 gboolean result = mono_assembly_names_equal_flags (wanted_name, candidate_name, MONO_ANAME_EQ_IGNORE_VERSION);
4072 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate and wanted names %s (ignoring version)", result ? "match" : "don't match, returning FALSE");
4073 if (result) {
4074 // compare major of candidate and wanted
4075 int c = assembly_names_compare_versions (candidate_name, wanted_name, 1);
4076 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Predicate: candidate major version is %s wanted major version, returning %s\n", c == 0 ? "the same as" : (c < 0 ? "lower than" : "greater than"),
4077 (c >= 0) ? "TRUE" : "FALSE");
4078 return (c >= 0); // don't accept a candidate that's older than wanted.
4079 } else {
4080 return FALSE;
4084 #endif
4085 return FALSE;
4088 MonoAssembly*
4089 mono_assembly_load_full_nosearch (MonoAssemblyName *aname,
4090 const char *basedir,
4091 MonoAssemblyContextKind asmctx,
4092 MonoImageOpenStatus *status)
4095 MonoAssembly *result;
4096 MonoAssemblyName maped_aname;
4097 MonoAssemblyName maped_name_pp;
4099 aname = mono_assembly_remap_version (aname, &maped_aname);
4101 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
4103 /* Reflection only assemblies don't get assembly binding */
4104 if (!refonly)
4105 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
4107 result = mono_assembly_loaded_full (aname, refonly);
4108 if (result)
4109 return result;
4111 result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path);
4112 if (result) {
4113 result->in_gac = FALSE;
4114 return result;
4117 return mono_assembly_load_full_gac_base_default (aname, basedir, asmctx, status);
4120 /* Like mono_assembly_load_full_nosearch, but don't ask the preload look (ie,
4121 * the appdomain) to run. Just looks in the gac, the specified base dir or the
4122 * default_path. Does NOT look in the appdomain application base or in the
4123 * MONO_PATH.
4125 MonoAssembly*
4126 mono_assembly_load_full_gac_base_default (MonoAssemblyName *aname,
4127 const char *basedir,
4128 MonoAssemblyContextKind asmctx,
4129 MonoImageOpenStatus *status)
4131 MonoAssembly *result;
4132 char *fullpath, *filename;
4133 int ext_index;
4134 const char *ext;
4135 int len;
4137 /* Currently we retrieve the loaded corlib for reflection
4138 * only requests, like a common reflection only assembly
4140 if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) {
4141 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
4144 MonoAssemblyCandidatePredicate predicate = NULL;
4145 void* predicate_ud = NULL;
4146 #if !defined(DISABLE_DESKTOP_LOADER)
4147 if (G_LIKELY (mono_loader_get_strict_strong_names ())) {
4148 predicate = &mono_assembly_candidate_predicate_sn_same_name;
4149 predicate_ud = aname;
4151 #endif
4153 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
4155 len = strlen (aname->name);
4156 for (ext_index = 0; ext_index < 2; ext_index ++) {
4157 ext = ext_index == 0 ? ".dll" : ".exe";
4158 if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) {
4159 filename = g_strdup (aname->name);
4160 /* Don't try appending .dll/.exe if it already has one of those extensions */
4161 ext_index++;
4162 } else {
4163 filename = g_strconcat (aname->name, ext, NULL);
4166 result = mono_assembly_load_from_gac (aname, filename, status, refonly);
4167 if (result) {
4168 g_free (filename);
4169 return result;
4172 if (basedir) {
4173 fullpath = g_build_filename (basedir, filename, NULL);
4174 result = mono_assembly_open_predicate (fullpath, asmctx, predicate, predicate_ud, NULL, status);
4175 g_free (fullpath);
4176 if (result) {
4177 result->in_gac = FALSE;
4178 g_free (filename);
4179 return result;
4183 result = load_in_path (filename, default_path, status, asmctx, predicate, predicate_ud);
4184 if (result)
4185 result->in_gac = FALSE;
4186 g_free (filename);
4187 if (result)
4188 return result;
4191 return result;
4195 * Try to load the assembly without looking in the domain:
4196 * tries the MONO_PATH, then the gac, and the default_path.
4198 MonoAssembly*
4199 mono_assembly_load_full_nodomain (MonoAssemblyName *aname,
4200 MonoAssemblyContextKind asmctx,
4201 MonoImageOpenStatus *status)
4203 MonoAssembly *result = NULL;
4204 result = mono_assembly_load_from_assemblies_path (assemblies_path, aname, asmctx);
4205 if (!result)
4206 result = mono_assembly_load_full_gac_base_default (aname, NULL, asmctx, status);
4207 return result;
4210 MonoAssembly*
4211 mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoAssemblyContextKind asmctx, MonoImageOpenStatus *status)
4213 MonoAssembly *result = mono_assembly_load_full_nosearch (aname, basedir, asmctx, status);
4214 const gboolean refonly = asmctx == MONO_ASMCTX_REFONLY;
4216 if (!result) {
4217 /* Try a postload search hook */
4218 result = mono_assembly_invoke_search_hook_internal (aname, requesting, refonly, TRUE);
4219 result = prevent_reference_assembly_from_running (result, refonly);
4221 return result;
4225 * mono_assembly_load_full:
4226 * \param aname A MonoAssemblyName with the assembly name to load.
4227 * \param basedir A directory to look up the assembly at.
4228 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4229 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4231 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4232 * attempts to load the assembly from that directory before probing the standard locations.
4234 * If the assembly is being opened in reflection-only mode (\p refonly set to TRUE) then no
4235 * assembly binding takes place.
4237 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4238 * value pointed by \p status is updated with an error code.
4240 MonoAssembly*
4241 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
4243 return mono_assembly_load_full_internal (aname, NULL, basedir, refonly ? MONO_ASMCTX_REFONLY : MONO_ASMCTX_DEFAULT, status);
4247 * mono_assembly_load:
4248 * \param aname A MonoAssemblyName with the assembly name to load.
4249 * \param basedir A directory to look up the assembly at.
4250 * \param status a pointer to a MonoImageOpenStatus to return the status of the load operation
4252 * Loads the assembly referenced by \p aname, if the value of \p basedir is not NULL, it
4253 * attempts to load the assembly from that directory before probing the standard locations.
4255 * \returns the assembly referenced by \p aname loaded or NULL on error. On error the
4256 * value pointed by \p status is updated with an error code.
4258 MonoAssembly*
4259 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
4261 return mono_assembly_load_full_internal (aname, NULL, basedir, MONO_ASMCTX_DEFAULT, status);
4265 * mono_assembly_loaded_full:
4266 * \param aname an assembly to look for.
4267 * \param refonly Whether this assembly is being opened in "reflection-only" mode.
4269 * This is used to determine if the specified assembly has been loaded
4270 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4271 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4273 MonoAssembly*
4274 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
4276 MonoAssembly *res;
4277 MonoAssemblyName maped_aname;
4279 aname = mono_assembly_remap_version (aname, &maped_aname);
4281 res = mono_assembly_invoke_search_hook_internal (aname, NULL, refonly, FALSE);
4283 return res;
4287 * mono_assembly_loaded:
4288 * \param aname an assembly to look for.
4290 * This is used to determine if the specified assembly has been loaded
4292 * \returns NULL If the given \p aname assembly has not been loaded, or a pointer to
4293 * a \c MonoAssembly that matches the \c MonoAssemblyName specified.
4295 MonoAssembly*
4296 mono_assembly_loaded (MonoAssemblyName *aname)
4298 return mono_assembly_loaded_full (aname, FALSE);
4301 void
4302 mono_assembly_release_gc_roots (MonoAssembly *assembly)
4304 if (assembly == NULL || assembly == REFERENCE_MISSING)
4305 return;
4307 if (assembly_is_dynamic (assembly)) {
4308 int i;
4309 MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image;
4310 for (i = 0; i < dynimg->image.module_count; ++i)
4311 mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]);
4312 mono_dynamic_image_release_gc_roots (dynimg);
4317 * Returns whether mono_assembly_close_finish() must be called as
4318 * well. See comment for mono_image_close_except_pools() for why we
4319 * unload in two steps.
4321 gboolean
4322 mono_assembly_close_except_image_pools (MonoAssembly *assembly)
4324 GSList *tmp;
4325 g_return_val_if_fail (assembly != NULL, FALSE);
4327 if (assembly == REFERENCE_MISSING)
4328 return FALSE;
4330 /* Might be 0 already */
4331 if (mono_atomic_dec_i32 (&assembly->ref_count) > 0)
4332 return FALSE;
4334 MONO_PROFILER_RAISE (assembly_unloading, (assembly));
4336 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly);
4338 mono_debug_close_image (assembly->image);
4340 mono_assemblies_lock ();
4341 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
4342 mono_assemblies_unlock ();
4344 assembly->image->assembly = NULL;
4346 if (!mono_image_close_except_pools (assembly->image))
4347 assembly->image = NULL;
4349 for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
4350 MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data;
4351 mono_assembly_name_free (fname);
4352 g_free (fname);
4354 g_slist_free (assembly->friend_assembly_names);
4355 g_free (assembly->basedir);
4357 MONO_PROFILER_RAISE (assembly_unloaded, (assembly));
4359 return TRUE;
4362 void
4363 mono_assembly_close_finish (MonoAssembly *assembly)
4365 g_assert (assembly && assembly != REFERENCE_MISSING);
4367 if (assembly->image)
4368 mono_image_close_finish (assembly->image);
4370 if (assembly_is_dynamic (assembly)) {
4371 g_free ((char*)assembly->aname.culture);
4372 } else {
4373 g_free (assembly);
4378 * mono_assembly_close:
4379 * \param assembly the assembly to release.
4381 * This method releases a reference to the \p assembly. The assembly is
4382 * only released when all the outstanding references to it are released.
4384 void
4385 mono_assembly_close (MonoAssembly *assembly)
4387 if (mono_assembly_close_except_image_pools (assembly))
4388 mono_assembly_close_finish (assembly);
4392 * mono_assembly_load_module:
4394 MonoImage*
4395 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
4397 ERROR_DECL (error);
4398 MonoImage *result = mono_assembly_load_module_checked (assembly, idx, error);
4399 mono_error_assert_ok (error);
4400 return result;
4403 MONO_API MonoImage*
4404 mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error)
4406 return mono_image_load_file_for_image_checked (assembly->image, idx, error);
4411 * mono_assembly_foreach:
4412 * \param func function to invoke for each assembly loaded
4413 * \param user_data data passed to the callback
4415 * Invokes the provided \p func callback for each assembly loaded into
4416 * the runtime. The first parameter passed to the callback is the
4417 * \c MonoAssembly*, and the second parameter is the \p user_data.
4419 * This is done for all assemblies loaded in the runtime, not just
4420 * those loaded in the current application domain.
4422 void
4423 mono_assembly_foreach (GFunc func, gpointer user_data)
4425 GList *copy;
4428 * We make a copy of the list to avoid calling the callback inside the
4429 * lock, which could lead to deadlocks.
4431 mono_assemblies_lock ();
4432 copy = g_list_copy (loaded_assemblies);
4433 mono_assemblies_unlock ();
4435 g_list_foreach (loaded_assemblies, func, user_data);
4437 g_list_free (copy);
4441 * mono_assemblies_cleanup:
4443 * Free all resources used by this module.
4445 void
4446 mono_assemblies_cleanup (void)
4448 GSList *l;
4450 mono_os_mutex_destroy (&assemblies_mutex);
4451 mono_os_mutex_destroy (&assembly_binding_mutex);
4453 for (l = loaded_assembly_bindings; l; l = l->next) {
4454 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4456 mono_assembly_binding_info_free (info);
4457 g_free (info);
4459 g_slist_free (loaded_assembly_bindings);
4461 free_assembly_asmctx_from_path_hooks ();
4462 free_assembly_load_hooks ();
4463 free_assembly_search_hooks ();
4464 free_assembly_preload_hooks ();
4467 /*LOCKING takes the assembly_binding lock*/
4468 void
4469 mono_assembly_cleanup_domain_bindings (guint32 domain_id)
4471 GSList **iter;
4473 mono_assembly_binding_lock ();
4474 iter = &loaded_assembly_bindings;
4475 while (*iter) {
4476 GSList *l = *iter;
4477 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
4479 if (info->domain_id == domain_id) {
4480 *iter = l->next;
4481 mono_assembly_binding_info_free (info);
4482 g_free (info);
4483 g_slist_free_1 (l);
4484 } else {
4485 iter = &l->next;
4488 mono_assembly_binding_unlock ();
4492 * Holds the assembly of the application, for
4493 * System.Diagnostics.Process::MainModule
4495 static MonoAssembly *main_assembly=NULL;
4498 * mono_assembly_set_main:
4500 void
4501 mono_assembly_set_main (MonoAssembly *assembly)
4503 main_assembly = assembly;
4507 * mono_assembly_get_main:
4509 * Returns: the assembly for the application, the first assembly that is loaded by the VM
4511 MonoAssembly *
4512 mono_assembly_get_main (void)
4514 return (main_assembly);
4518 * mono_assembly_get_image:
4519 * \param assembly The assembly to retrieve the image from
4521 * \returns the \c MonoImage associated with this assembly.
4523 MonoImage*
4524 mono_assembly_get_image (MonoAssembly *assembly)
4526 return assembly->image;
4530 * mono_assembly_get_name:
4531 * \param assembly The assembly to retrieve the name from
4533 * The returned name's lifetime is the same as \p assembly's.
4535 * \returns the \c MonoAssemblyName associated with this assembly.
4537 MonoAssemblyName *
4538 mono_assembly_get_name (MonoAssembly *assembly)
4540 return &assembly->aname;
4544 * mono_register_bundled_assemblies:
4546 void
4547 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
4549 bundles = assemblies;
4552 #define MONO_DECLSEC_FORMAT_10 0x3C
4553 #define MONO_DECLSEC_FORMAT_20 0x2E
4554 #define MONO_DECLSEC_FIELD 0x53
4555 #define MONO_DECLSEC_PROPERTY 0x54
4557 #define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"")
4558 #define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute")
4559 #define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1)
4560 #define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification")
4561 #define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1)
4563 static gboolean
4564 mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding)
4566 int len;
4567 switch (*p++) {
4568 case MONO_DECLSEC_PROPERTY:
4569 break;
4570 case MONO_DECLSEC_FIELD:
4571 default:
4572 *abort_decoding = TRUE;
4573 return FALSE;
4574 break;
4577 if (*p++ != MONO_TYPE_BOOLEAN) {
4578 *abort_decoding = TRUE;
4579 return FALSE;
4582 /* property name length */
4583 len = mono_metadata_decode_value (p, &p);
4585 if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) {
4586 p += len;
4587 return *p;
4589 p += len + 1;
4591 *resp = p;
4592 return FALSE;
4595 static gboolean
4596 mono_assembly_try_decode_skip_verification (const char *p, const char *endn)
4598 int i, j, num, len, params_len;
4600 if (*p == MONO_DECLSEC_FORMAT_10) {
4601 gsize read, written;
4602 char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL);
4603 if (res) {
4604 gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL;
4605 g_free (res);
4606 return found;
4608 return FALSE;
4610 if (*p++ != MONO_DECLSEC_FORMAT_20)
4611 return FALSE;
4613 /* number of encoded permission attributes */
4614 num = mono_metadata_decode_value (p, &p);
4615 for (i = 0; i < num; ++i) {
4616 gboolean is_valid = FALSE;
4617 gboolean abort_decoding = FALSE;
4619 /* attribute name length */
4620 len = mono_metadata_decode_value (p, &p);
4622 /* We don't really need to fully decode the type. Comparing the name is enough */
4623 is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE);
4625 p += len;
4627 /*size of the params table*/
4628 params_len = mono_metadata_decode_value (p, &p);
4629 if (is_valid) {
4630 const char *params_end = p + params_len;
4632 /* number of parameters */
4633 len = mono_metadata_decode_value (p, &p);
4635 for (j = 0; j < len; ++j) {
4636 if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding))
4637 return TRUE;
4638 if (abort_decoding)
4639 break;
4641 p = params_end;
4642 } else {
4643 p += params_len;
4647 return FALSE;
4651 gboolean
4652 mono_assembly_has_skip_verification (MonoAssembly *assembly)
4654 MonoTableInfo *t;
4655 guint32 cols [MONO_DECL_SECURITY_SIZE];
4656 const char *blob;
4657 int i, len;
4659 if (MONO_SECMAN_FLAG_INIT (assembly->skipverification))
4660 return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification);
4662 t = &assembly->image->tables [MONO_TABLE_DECLSECURITY];
4664 for (i = 0; i < t->rows; ++i) {
4665 mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE);
4666 if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY)
4667 continue;
4668 if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN)
4669 continue;
4671 blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]);
4672 len = mono_metadata_decode_blob_size (blob, &blob);
4673 if (!len)
4674 continue;
4676 if (mono_assembly_try_decode_skip_verification (blob, blob + len)) {
4677 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE);
4678 return TRUE;
4682 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE);
4683 return FALSE;
4686 MonoAssemblyContextKind
4687 mono_asmctx_get_kind (const MonoAssemblyContext *ctx)
4689 return ctx->kind;
4692 static const char *
4693 mono_asmctx_get_name (const MonoAssemblyContext *asmctx)
4695 static const char* names [] = {
4696 "DEFAULT",
4697 "REFONLY",
4698 "LOADFROM",
4699 "INDIVIDIUAL",
4701 g_assert (asmctx->kind >= 0 && asmctx->kind <= MONO_ASMCTX_LAST);
4702 return names [asmctx->kind];