Fix roslyn install with AOT disabled.
[mono-project.git] / mono / metadata / assembly.c
blob61b4c3416523eb3a19ba77dc0ae51ad0e1a181ce
1 /*
2 * assembly.c: Routines for loading assemblies.
3 *
4 * Author:
5 * Miguel de Icaza (miguel@ximian.com)
7 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
8 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
13 #include <stdio.h>
14 #include <glib.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include "assembly.h"
19 #include "assembly-internals.h"
20 #include "image.h"
21 #include "image-internals.h"
22 #include "object-internals.h"
23 #include <mono/metadata/loader.h>
24 #include <mono/metadata/tabledefs.h>
25 #include <mono/metadata/custom-attrs-internals.h>
26 #include <mono/metadata/metadata-internals.h>
27 #include <mono/metadata/profiler-private.h>
28 #include <mono/metadata/class-internals.h>
29 #include <mono/metadata/domain-internals.h>
30 #include <mono/metadata/reflection-internals.h>
31 #include <mono/metadata/mono-endian.h>
32 #include <mono/metadata/mono-debug.h>
33 #include <mono/io-layer/io-layer.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 PLATFORM_MACOSX
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 } AssemblyVersionMap;
65 /* the default search path is empty, the first slot is replaced with the computed value */
66 static const char*
67 default_path [] = {
68 NULL,
69 NULL,
70 NULL
73 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
74 static char **assemblies_path = NULL;
76 /* Contains the list of directories that point to auxiliary GACs */
77 static char **extra_gac_paths = NULL;
79 #ifndef DISABLE_ASSEMBLY_REMAPPING
80 /* The list of system assemblies what will be remapped to the running
81 * runtime version. WARNING: this list must be sorted.
82 * The integer number is an index in the MonoRuntimeInfo structure, whose
83 * values can be found in domain.c - supported_runtimes. Look there
84 * to understand what remapping will be made.
86 * .NET version can be found at https://github.com/dotnet/coreclr/blob/master/src/inc/fxretarget.h#L99
89 static const AssemblyVersionMap framework_assemblies [] = {
90 {"Accessibility", 0},
91 {"Commons.Xml.Relaxng", 0},
92 {"I18N", 0},
93 {"I18N.CJK", 0},
94 {"I18N.MidEast", 0},
95 {"I18N.Other", 0},
96 {"I18N.Rare", 0},
97 {"I18N.West", 0},
98 {"Microsoft.Build.Engine", 2, NULL, TRUE},
99 {"Microsoft.Build.Framework", 2, NULL, TRUE},
100 {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
101 {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
102 {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
103 {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
104 {"Microsoft.VisualBasic", 1},
105 {"Microsoft.VisualC", 1},
106 {"Mono.Cairo", 0},
107 {"Mono.CompilerServices.SymbolWriter", 0},
108 {"Mono.Data", 0},
109 {"Mono.Data.SybaseClient", 0},
110 {"Mono.Data.Tds", 0},
111 {"Mono.Data.TdsClient", 0},
112 {"Mono.GetOptions", 0},
113 {"Mono.Http", 0},
114 {"Mono.Posix", 0},
115 {"Mono.Security", 0},
116 {"Mono.Security.Win32", 0},
117 {"Mono.Xml.Ext", 0},
118 {"Novell.Directory.Ldap", 0},
119 {"PEAPI", 0},
120 {"System", 0},
121 {"System.ComponentModel.Composition", 2},
122 {"System.ComponentModel.DataAnnotations", 2},
123 {"System.Configuration", 0},
124 {"System.Configuration.Install", 0},
125 {"System.Core", 2},
126 {"System.Data", 0},
127 {"System.Data.Linq", 2},
128 {"System.Data.OracleClient", 0},
129 {"System.Data.Services", 2},
130 {"System.Data.Services.Client", 2},
131 {"System.Data.SqlXml", 0},
132 {"System.Design", 0},
133 {"System.DirectoryServices", 0},
134 {"System.Drawing", 0},
135 {"System.Drawing.Design", 0},
136 {"System.EnterpriseServices", 0},
137 {"System.IdentityModel", 3},
138 {"System.IdentityModel.Selectors", 3},
139 {"System.Management", 0},
140 {"System.Messaging", 0},
141 {"System.Net", 2},
142 {"System.Runtime.Remoting", 0},
143 {"System.Runtime.Serialization", 3},
144 {"System.Runtime.Serialization.Formatters.Soap", 0},
145 {"System.Security", 0},
146 {"System.ServiceModel", 3},
147 {"System.ServiceModel.Web", 2},
148 {"System.ServiceProcess", 0},
149 {"System.Transactions", 0},
150 {"System.Web", 0},
151 {"System.Web.Abstractions", 2},
152 {"System.Web.DynamicData", 2},
153 {"System.Web.Extensions", 2},
154 {"System.Web.Mobile", 0},
155 {"System.Web.Routing", 2},
156 {"System.Web.Services", 0},
157 {"System.Windows.Forms", 0},
158 {"System.Xml", 0},
159 {"System.Xml.Linq", 2},
160 {"WindowsBase", 3},
161 {"mscorlib", 0}
163 #endif
166 * keeps track of loaded assemblies
168 static GList *loaded_assemblies = NULL;
169 static MonoAssembly *corlib;
171 #if defined(__native_client__)
173 /* On Native Client, allow mscorlib to be loaded from memory */
174 /* instead of loaded off disk. If these are not set, default */
175 /* mscorlib loading will take place */
177 /* NOTE: If mscorlib data is passed to mono in this way then */
178 /* it needs to remain allocated during the use of mono. */
180 static void *corlibData = NULL;
181 static size_t corlibSize = 0;
183 void
184 mono_set_corlib_data (void *data, size_t size)
186 corlibData = data;
187 corlibSize = size;
190 #endif
192 static char* unquote (const char *str);
194 /* This protects loaded_assemblies and image->references */
195 #define mono_assemblies_lock() mono_os_mutex_lock (&assemblies_mutex)
196 #define mono_assemblies_unlock() mono_os_mutex_unlock (&assemblies_mutex)
197 static mono_mutex_t assemblies_mutex;
199 /* If defined, points to the bundled assembly information */
200 const MonoBundledAssembly **bundles;
202 static mono_mutex_t assembly_binding_mutex;
204 /* Loaded assembly binding info */
205 static GSList *loaded_assembly_bindings = NULL;
207 /* Class lazy loading functions */
208 static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, System.Runtime.CompilerServices, InternalsVisibleToAttribute)
209 static MonoAssembly*
210 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload);
211 static MonoAssembly*
212 mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly);
213 static MonoBoolean
214 mono_assembly_is_in_gac (const gchar *filanem);
216 static MonoAssembly*
217 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly);
219 static gchar*
220 encode_public_tok (const guchar *token, gint32 len)
222 const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
223 gchar *res;
224 int i;
226 res = (gchar *)g_malloc (len * 2 + 1);
227 for (i = 0; i < len; i++) {
228 res [i * 2] = allowed [token [i] >> 4];
229 res [i * 2 + 1] = allowed [token [i] & 0xF];
231 res [len * 2] = 0;
232 return res;
236 * mono_public_tokens_are_equal:
237 * @pubt1: first public key token
238 * @pubt2: second public key token
240 * Compare two public key tokens and return #TRUE is they are equal and #FALSE
241 * otherwise.
243 gboolean
244 mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
246 return memcmp (pubt1, pubt2, 16) == 0;
250 * mono_set_assemblies_path:
251 * @path: list of paths that contain directories where Mono will look for assemblies
253 * Use this method to override the standard assembly lookup system and
254 * override any assemblies coming from the GAC. This is the method
255 * that supports the MONO_PATH variable.
257 * Notice that MONO_PATH and this method are really a very bad idea as
258 * it prevents the GAC from working and it prevents the standard
259 * resolution mechanisms from working. Nonetheless, for some debugging
260 * situations and bootstrapping setups, this is useful to have.
262 void
263 mono_set_assemblies_path (const char* path)
265 char **splitted, **dest;
267 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
268 if (assemblies_path)
269 g_strfreev (assemblies_path);
270 assemblies_path = dest = splitted;
271 while (*splitted) {
272 char *tmp = *splitted;
273 if (*tmp)
274 *dest++ = mono_path_canonicalize (tmp);
275 g_free (tmp);
276 splitted++;
278 *dest = *splitted;
280 if (g_getenv ("MONO_DEBUG") == NULL)
281 return;
283 splitted = assemblies_path;
284 while (*splitted) {
285 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
286 g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
288 splitted++;
292 /* Native Client can't get this info from an environment variable so */
293 /* it's passed in to the runtime, or set manually by embedding code. */
294 #ifdef __native_client__
295 char* nacl_mono_path = NULL;
296 #endif
298 static void
299 check_path_env (void)
301 const char* path;
302 path = g_getenv ("MONO_PATH");
303 #ifdef __native_client__
304 if (!path)
305 path = nacl_mono_path;
306 #endif
307 if (!path || assemblies_path != NULL)
308 return;
310 mono_set_assemblies_path(path);
313 static void
314 check_extra_gac_path_env (void) {
315 const char *path;
316 char **splitted, **dest;
318 path = g_getenv ("MONO_GAC_PREFIX");
319 if (!path)
320 return;
322 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
323 if (extra_gac_paths)
324 g_strfreev (extra_gac_paths);
325 extra_gac_paths = dest = splitted;
326 while (*splitted){
327 if (**splitted)
328 *dest++ = *splitted;
329 splitted++;
331 *dest = *splitted;
333 if (g_getenv ("MONO_DEBUG") == NULL)
334 return;
336 while (*splitted) {
337 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
338 g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted);
340 splitted++;
344 static gboolean
345 assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
347 if (!info || !info->name)
348 return FALSE;
350 if (strcmp (info->name, aname->name))
351 return FALSE;
353 if (info->major != aname->major || info->minor != aname->minor)
354 return FALSE;
356 if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0]))
357 return FALSE;
359 if (info->culture && aname->culture && strcmp (info->culture, aname->culture))
360 return FALSE;
362 if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
363 return FALSE;
365 return TRUE;
368 static void
369 mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
371 if (!info)
372 return;
374 g_free (info->name);
375 g_free (info->culture);
378 static void
379 get_publisher_policy_info (MonoImage *image, MonoAssemblyName *aname, MonoAssemblyBindingInfo *binding_info)
381 MonoTableInfo *t;
382 guint32 cols [MONO_MANIFEST_SIZE];
383 const gchar *filename;
384 gchar *subpath, *fullpath;
386 t = &image->tables [MONO_TABLE_MANIFESTRESOURCE];
387 /* MS Impl. accepts policy assemblies with more than
388 * one manifest resource, and only takes the first one */
389 if (t->rows < 1) {
390 binding_info->is_valid = FALSE;
391 return;
394 mono_metadata_decode_row (t, 0, cols, MONO_MANIFEST_SIZE);
395 if ((cols [MONO_MANIFEST_IMPLEMENTATION] & MONO_IMPLEMENTATION_MASK) != MONO_IMPLEMENTATION_FILE) {
396 binding_info->is_valid = FALSE;
397 return;
400 filename = mono_metadata_string_heap (image, cols [MONO_MANIFEST_NAME]);
401 g_assert (filename != NULL);
403 subpath = g_path_get_dirname (image->name);
404 fullpath = g_build_path (G_DIR_SEPARATOR_S, subpath, filename, NULL);
405 mono_config_parse_publisher_policy (fullpath, binding_info);
406 g_free (subpath);
407 g_free (fullpath);
409 /* Define the optional elements/attributes before checking */
410 if (!binding_info->culture)
411 binding_info->culture = g_strdup ("");
413 /* Check that the most important elements/attributes exist */
414 if (!binding_info->name || !binding_info->public_key_token [0] || !binding_info->has_old_version_bottom ||
415 !binding_info->has_new_version || !assembly_binding_maps_name (binding_info, aname)) {
416 mono_assembly_binding_info_free (binding_info);
417 binding_info->is_valid = FALSE;
418 return;
421 binding_info->is_valid = TRUE;
424 static int
425 compare_versions (AssemblyVersionSet *v, MonoAssemblyName *aname)
427 if (v->major > aname->major)
428 return 1;
429 else if (v->major < aname->major)
430 return -1;
432 if (v->minor > aname->minor)
433 return 1;
434 else if (v->minor < aname->minor)
435 return -1;
437 if (v->build > aname->build)
438 return 1;
439 else if (v->build < aname->build)
440 return -1;
442 if (v->revision > aname->revision)
443 return 1;
444 else if (v->revision < aname->revision)
445 return -1;
447 return 0;
450 static gboolean
451 check_policy_versions (MonoAssemblyBindingInfo *info, MonoAssemblyName *name)
453 if (!info->is_valid)
454 return FALSE;
456 /* If has_old_version_top doesn't exist, we don't have an interval */
457 if (!info->has_old_version_top) {
458 if (compare_versions (&info->old_version_bottom, name) == 0)
459 return TRUE;
461 return FALSE;
464 /* Check that the version defined by name is valid for the interval */
465 if (compare_versions (&info->old_version_top, name) < 0)
466 return FALSE;
468 /* We should be greater or equal than the small version */
469 if (compare_versions (&info->old_version_bottom, name) > 0)
470 return FALSE;
472 return TRUE;
476 * mono_assembly_names_equal:
477 * @l: first assembly
478 * @r: second assembly.
480 * Compares two MonoAssemblyNames and returns whether they are equal.
482 * This compares the names, the cultures, the release version and their
483 * public tokens.
485 * Returns: TRUE if both assembly names are equal.
487 gboolean
488 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
490 if (!l->name || !r->name)
491 return FALSE;
493 if (strcmp (l->name, r->name))
494 return FALSE;
496 if (l->culture && r->culture && strcmp (l->culture, r->culture))
497 return FALSE;
499 if (l->major != r->major || l->minor != r->minor ||
500 l->build != r->build || l->revision != r->revision)
501 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)))
502 return FALSE;
504 if (!l->public_key_token [0] || !r->public_key_token [0])
505 return TRUE;
507 if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token))
508 return FALSE;
510 return TRUE;
513 static MonoAssembly *
514 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly)
516 int i;
517 char *fullpath;
518 MonoAssembly *result;
520 for (i = 0; search_path [i]; ++i) {
521 fullpath = g_build_filename (search_path [i], basename, NULL);
522 result = mono_assembly_open_full (fullpath, status, refonly);
523 g_free (fullpath);
524 if (result)
525 return result;
527 return NULL;
531 * mono_assembly_setrootdir:
532 * @root_dir: The pathname of the root directory where we will locate assemblies
534 * This routine sets the internal default root directory for looking up
535 * assemblies.
537 * This is used by Windows installations to compute dynamically the
538 * place where the Mono assemblies are located.
541 void
542 mono_assembly_setrootdir (const char *root_dir)
545 * Override the MONO_ASSEMBLIES directory configured at compile time.
547 /* Leak if called more than once */
548 default_path [0] = g_strdup (root_dir);
552 * mono_assembly_getrootdir:
554 * Obtains the root directory used for looking up assemblies.
556 * Returns: a string with the directory, this string should not be freed.
558 G_CONST_RETURN gchar *
559 mono_assembly_getrootdir (void)
561 return default_path [0];
565 * mono_native_getrootdir:
567 * Obtains the root directory used for looking up native libs (.so, .dylib).
569 * Returns: a string with the directory, this string should be freed by
570 * the caller.
572 gchar *
573 mono_native_getrootdir (void)
575 gchar* fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), mono_config_get_reloc_lib_dir(), NULL);
576 return fullpath;
580 * mono_set_dirs:
581 * @assembly_dir: the base directory for assemblies
582 * @config_dir: the base directory for configuration files
584 * This routine is used internally and by developers embedding
585 * the runtime into their own applications.
587 * There are a number of cases to consider: Mono as a system-installed
588 * package that is available on the location preconfigured or Mono in
589 * a relocated location.
591 * If you are using a system-installed Mono, you can pass NULL
592 * to both parameters. If you are not, you should compute both
593 * directory values and call this routine.
595 * The values for a given PREFIX are:
597 * assembly_dir: PREFIX/lib
598 * config_dir: PREFIX/etc
600 * Notice that embedders that use Mono in a relocated way must
601 * compute the location at runtime, as they will be in control
602 * of where Mono is installed.
604 void
605 mono_set_dirs (const char *assembly_dir, const char *config_dir)
607 if (assembly_dir == NULL)
608 assembly_dir = mono_config_get_assemblies_dir ();
609 if (config_dir == NULL)
610 config_dir = mono_config_get_cfg_dir ();
611 mono_assembly_setrootdir (assembly_dir);
612 mono_set_config_dir (config_dir);
615 #ifndef HOST_WIN32
617 static char *
618 compute_base (char *path)
620 char *p = strrchr (path, '/');
621 if (p == NULL)
622 return NULL;
624 /* Not a well known Mono executable, we are embedded, cant guess the base */
625 if (strcmp (p, "/mono") && strcmp (p, "/mono-boehm") && strcmp (p, "/mono-sgen") && strcmp (p, "/pedump") && strcmp (p, "/monodis"))
626 return NULL;
628 *p = 0;
629 p = strrchr (path, '/');
630 if (p == NULL)
631 return NULL;
633 if (strcmp (p, "/bin") != 0)
634 return NULL;
635 *p = 0;
636 return path;
639 static void
640 fallback (void)
642 mono_set_dirs (mono_config_get_assemblies_dir (), mono_config_get_cfg_dir ());
645 static G_GNUC_UNUSED void
646 set_dirs (char *exe)
648 char *base;
649 char *config, *lib, *mono;
650 struct stat buf;
651 const char *bindir;
654 * Only /usr prefix is treated specially
656 bindir = mono_config_get_bin_dir ();
657 g_assert (bindir);
658 if (strncmp (exe, bindir, strlen (bindir)) == 0 || (base = compute_base (exe)) == NULL){
659 fallback ();
660 return;
663 config = g_build_filename (base, "etc", NULL);
664 lib = g_build_filename (base, "lib", NULL);
665 mono = g_build_filename (lib, "mono/4.5", NULL); // FIXME: stop hardcoding 4.5 here
666 if (stat (mono, &buf) == -1)
667 fallback ();
668 else {
669 mono_set_dirs (lib, config);
672 g_free (config);
673 g_free (lib);
674 g_free (mono);
677 #endif /* HOST_WIN32 */
680 * mono_set_rootdir:
682 * Registers the root directory for the Mono runtime, for Linux and Solaris 10,
683 * this auto-detects the prefix where Mono was installed.
685 void
686 mono_set_rootdir (void)
688 #if defined(HOST_WIN32) || (defined(PLATFORM_MACOSX) && !defined(TARGET_ARM))
689 gchar *bindir, *installdir, *root, *name, *resolvedname, *config;
691 #ifdef HOST_WIN32
692 name = mono_get_module_file_name ((HMODULE) &__ImageBase);
693 #else
696 * _NSGetExecutablePath may return -1 to indicate buf is not large
697 * enough, but we ignore that case to avoid having to do extra dynamic
698 * allocation for the path and hope that 4096 is enough - this is
699 * ok in the Linux/Solaris case below at least...
702 gchar buf[4096];
703 guint buf_size = sizeof (buf);
705 name = NULL;
706 if (_NSGetExecutablePath (buf, &buf_size) == 0)
707 name = g_strdup (buf);
709 if (name == NULL) {
710 fallback ();
711 return;
714 #endif
716 resolvedname = mono_path_resolve_symlinks (name);
718 bindir = g_path_get_dirname (resolvedname);
719 installdir = g_path_get_dirname (bindir);
720 root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
722 config = g_build_filename (root, "..", "etc", NULL);
723 #ifdef HOST_WIN32
724 mono_set_dirs (root, config);
725 #else
726 if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS))
727 mono_set_dirs (root, config);
728 else
729 fallback ();
730 #endif
732 g_free (config);
733 g_free (root);
734 g_free (installdir);
735 g_free (bindir);
736 g_free (name);
737 g_free (resolvedname);
738 #elif defined(DISABLE_MONO_AUTODETECTION)
739 fallback ();
740 #else
741 char buf [4096];
742 int s;
743 char *str;
745 /* Linux style */
746 s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
748 if (s != -1){
749 buf [s] = 0;
750 set_dirs (buf);
751 return;
754 /* Solaris 10 style */
755 str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
756 s = readlink (str, buf, sizeof (buf)-1);
757 g_free (str);
758 if (s != -1){
759 buf [s] = 0;
760 set_dirs (buf);
761 return;
763 fallback ();
764 #endif
768 * mono_assemblies_init:
770 * Initialize global variables used by this module.
772 void
773 mono_assemblies_init (void)
776 * Initialize our internal paths if we have not been initialized yet.
777 * This happens when embedders use Mono.
779 if (mono_assembly_getrootdir () == NULL)
780 mono_set_rootdir ();
782 check_path_env ();
783 check_extra_gac_path_env ();
785 mono_os_mutex_init_recursive (&assemblies_mutex);
786 mono_os_mutex_init (&assembly_binding_mutex);
789 static void
790 mono_assembly_binding_lock (void)
792 mono_locks_os_acquire (&assembly_binding_mutex, AssemblyBindingLock);
795 static void
796 mono_assembly_binding_unlock (void)
798 mono_locks_os_release (&assembly_binding_mutex, AssemblyBindingLock);
801 gboolean
802 mono_assembly_fill_assembly_name_full (MonoImage *image, MonoAssemblyName *aname, gboolean copyBlobs)
804 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
805 guint32 cols [MONO_ASSEMBLY_SIZE];
806 gint32 machine, flags;
808 if (!t->rows)
809 return FALSE;
811 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
813 aname->hash_len = 0;
814 aname->hash_value = NULL;
815 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
816 if (copyBlobs)
817 aname->name = g_strdup (aname->name);
818 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
819 if (copyBlobs)
820 aname->culture = g_strdup (aname->culture);
821 aname->flags = cols [MONO_ASSEMBLY_FLAGS];
822 aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
823 aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
824 aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
825 aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
826 aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
827 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
828 guchar* token = (guchar *)g_malloc (8);
829 gchar* encoded;
830 const gchar* pkey;
831 int len;
833 pkey = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
834 len = mono_metadata_decode_blob_size (pkey, &pkey);
835 aname->public_key = (guchar*)pkey;
837 mono_digest_get_public_token (token, aname->public_key, len);
838 encoded = encode_public_tok (token, 8);
839 g_strlcpy ((char*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
841 g_free (encoded);
842 g_free (token);
844 else {
845 aname->public_key = NULL;
846 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
849 if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
850 aname->public_key = (guchar*)mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
851 if (copyBlobs) {
852 const gchar *pkey_end;
853 int len = mono_metadata_decode_blob_size ((const gchar*) aname->public_key, &pkey_end);
854 pkey_end += len; /* move to end */
855 size_t size = pkey_end - (const gchar*)aname->public_key;
856 guchar *tmp = g_new (guchar, size);
857 memcpy (tmp, aname->public_key, size);
858 aname->public_key = tmp;
862 else
863 aname->public_key = 0;
865 machine = ((MonoCLIImageInfo*)(image->image_info))->cli_header.coff.coff_machine;
866 flags = ((MonoCLIImageInfo*)(image->image_info))->cli_cli_header.ch_flags;
867 switch (machine) {
868 case COFF_MACHINE_I386:
869 /* https://bugzilla.xamarin.com/show_bug.cgi?id=17632 */
870 if (flags & (CLI_FLAGS_32BITREQUIRED|CLI_FLAGS_PREFERRED32BIT))
871 aname->arch = MONO_PROCESSOR_ARCHITECTURE_X86;
872 else if ((flags & 0x70) == 0x70)
873 aname->arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
874 else
875 aname->arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
876 break;
877 case COFF_MACHINE_IA64:
878 aname->arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
879 break;
880 case COFF_MACHINE_AMD64:
881 aname->arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
882 break;
883 case COFF_MACHINE_ARM:
884 aname->arch = MONO_PROCESSOR_ARCHITECTURE_ARM;
885 break;
886 default:
887 break;
890 return TRUE;
893 gboolean
894 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
896 return mono_assembly_fill_assembly_name_full (image, aname, FALSE);
900 * mono_stringify_assembly_name:
901 * @aname: the assembly name.
903 * Convert @aname into its string format. The returned string is dynamically
904 * allocated and should be freed by the caller.
906 * Returns: a newly allocated string with a string representation of
907 * the assembly name.
909 char*
910 mono_stringify_assembly_name (MonoAssemblyName *aname)
912 const char *quote = (aname->name && g_ascii_isspace (aname->name [0])) ? "\"" : "";
914 return g_strdup_printf (
915 "%s%s%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
916 quote, aname->name, quote,
917 aname->major, aname->minor, aname->build, aname->revision,
918 aname->culture && *aname->culture? aname->culture: "neutral",
919 aname->public_key_token [0] ? (char *)aname->public_key_token : "null",
920 (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
923 static gchar*
924 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
926 const gchar *public_tok;
927 int len;
929 public_tok = mono_metadata_blob_heap (image, key_index);
930 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
932 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
933 guchar token [8];
934 mono_digest_get_public_token (token, (guchar*)public_tok, len);
935 return encode_public_tok (token, 8);
938 return encode_public_tok ((guchar*)public_tok, len);
942 * mono_assembly_addref:
943 * @assemnly: the assembly to reference
945 * This routine increments the reference count on a MonoAssembly.
946 * The reference count is reduced every time the method mono_assembly_close() is
947 * invoked.
949 void
950 mono_assembly_addref (MonoAssembly *assembly)
952 InterlockedIncrement (&assembly->ref_count);
956 * CAUTION: This table must be kept in sync with
957 * ivkm/reflect/Fusion.cs
960 #define SILVERLIGHT_KEY "7cec85d7bea7798e"
961 #define WINFX_KEY "31bf3856ad364e35"
962 #define ECMA_KEY "b77a5c561934e089"
963 #define MSFINAL_KEY "b03f5f7f11d50a3a"
964 #define COMPACTFRAMEWORK_KEY "969db8053d3322ac"
966 typedef struct {
967 const char *name;
968 const char *from;
969 const char *to;
970 } KeyRemapEntry;
972 static KeyRemapEntry key_remap_table[] = {
973 { "CustomMarshalers", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
974 { "Microsoft.CSharp", WINFX_KEY, MSFINAL_KEY },
975 { "Microsoft.VisualBasic", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
976 { "System", SILVERLIGHT_KEY, ECMA_KEY },
977 { "System", COMPACTFRAMEWORK_KEY, ECMA_KEY },
978 { "System.ComponentModel.Composition", WINFX_KEY, ECMA_KEY },
979 { "System.ComponentModel.DataAnnotations", "ddd0da4d3e678217", WINFX_KEY },
980 { "System.Core", SILVERLIGHT_KEY, ECMA_KEY },
981 { "System.Core", COMPACTFRAMEWORK_KEY, ECMA_KEY },
982 { "System.Data", COMPACTFRAMEWORK_KEY, ECMA_KEY },
983 { "System.Data.DataSetExtensions", COMPACTFRAMEWORK_KEY, ECMA_KEY },
984 { "System.Drawing", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
985 { "System.Messaging", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
986 // FIXME: MS uses MSFINAL_KEY for .NET 4.5
987 { "System.Net", SILVERLIGHT_KEY, MSFINAL_KEY },
988 { "System.Numerics", WINFX_KEY, ECMA_KEY },
989 { "System.Runtime.Serialization", SILVERLIGHT_KEY, ECMA_KEY },
990 { "System.Runtime.Serialization", COMPACTFRAMEWORK_KEY, ECMA_KEY },
991 { "System.ServiceModel", WINFX_KEY, ECMA_KEY },
992 { "System.ServiceModel", COMPACTFRAMEWORK_KEY, ECMA_KEY },
993 { "System.ServiceModel.Web", SILVERLIGHT_KEY, WINFX_KEY },
994 { "System.Web.Services", COMPACTFRAMEWORK_KEY, MSFINAL_KEY },
995 { "System.Windows", SILVERLIGHT_KEY, MSFINAL_KEY },
996 { "System.Windows.Forms", COMPACTFRAMEWORK_KEY, ECMA_KEY },
997 { "System.Xml", SILVERLIGHT_KEY, ECMA_KEY },
998 { "System.Xml", COMPACTFRAMEWORK_KEY, ECMA_KEY },
999 { "System.Xml.Linq", WINFX_KEY, ECMA_KEY },
1000 { "System.Xml.Linq", COMPACTFRAMEWORK_KEY, ECMA_KEY },
1001 { "System.Xml.Serialization", WINFX_KEY, ECMA_KEY }
1004 static void
1005 remap_keys (MonoAssemblyName *aname)
1007 int i;
1008 for (i = 0; i < G_N_ELEMENTS (key_remap_table); i++) {
1009 const KeyRemapEntry *entry = &key_remap_table [i];
1011 if (strcmp (aname->name, entry->name) ||
1012 !mono_public_tokens_are_equal (aname->public_key_token, (const unsigned char*) entry->from))
1013 continue;
1015 memcpy (aname->public_key_token, entry->to, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1017 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1018 "Remapped public key token of retargetable assembly %s from %s to %s",
1019 aname->name, entry->from, entry->to);
1020 return;
1024 static MonoAssemblyName *
1025 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
1027 const MonoRuntimeInfo *current_runtime;
1028 int pos, first, last;
1030 if (aname->name == NULL) return aname;
1032 current_runtime = mono_get_runtime_info ();
1034 if (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) {
1035 const AssemblyVersionSet* vset;
1037 /* Remap to current runtime */
1038 vset = &current_runtime->version_sets [0];
1040 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1041 dest_aname->major = vset->major;
1042 dest_aname->minor = vset->minor;
1043 dest_aname->build = vset->build;
1044 dest_aname->revision = vset->revision;
1045 dest_aname->flags &= ~ASSEMBLYREF_RETARGETABLE_FLAG;
1047 /* Remap assembly name */
1048 if (!strcmp (aname->name, "System.Net"))
1049 dest_aname->name = g_strdup ("System");
1051 remap_keys (dest_aname);
1053 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1054 "The request to load the retargetable assembly %s v%d.%d.%d.%d was remapped to %s v%d.%d.%d.%d",
1055 aname->name,
1056 aname->major, aname->minor, aname->build, aname->revision,
1057 dest_aname->name,
1058 vset->major, vset->minor, vset->build, vset->revision
1061 return dest_aname;
1064 #ifndef DISABLE_ASSEMBLY_REMAPPING
1065 first = 0;
1066 last = G_N_ELEMENTS (framework_assemblies) - 1;
1068 while (first <= last) {
1069 int res;
1070 pos = first + (last - first) / 2;
1071 res = strcmp (aname->name, framework_assemblies[pos].assembly_name);
1072 if (res == 0) {
1073 const AssemblyVersionSet* vset;
1074 int index = framework_assemblies[pos].version_set_index;
1075 g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
1076 vset = &current_runtime->version_sets [index];
1078 if (aname->major == vset->major && aname->minor == vset->minor &&
1079 aname->build == vset->build && aname->revision == vset->revision)
1080 return aname;
1082 if (framework_assemblies[pos].only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0)
1083 return aname;
1085 if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
1086 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1087 "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
1088 aname->name,
1089 aname->major, aname->minor, aname->build, aname->revision,
1090 vset->major, vset->minor, vset->build, vset->revision
1093 memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
1094 dest_aname->major = vset->major;
1095 dest_aname->minor = vset->minor;
1096 dest_aname->build = vset->build;
1097 dest_aname->revision = vset->revision;
1098 if (framework_assemblies[pos].new_assembly_name != NULL) {
1099 dest_aname->name = framework_assemblies[pos].new_assembly_name;
1100 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
1101 "The assembly name %s was remapped to %s",
1102 aname->name,
1103 dest_aname->name);
1105 return dest_aname;
1106 } else if (res < 0) {
1107 last = pos - 1;
1108 } else {
1109 first = pos + 1;
1112 #endif
1114 return aname;
1118 * mono_assembly_get_assemblyref:
1119 * @image: pointer to the MonoImage to extract the information from.
1120 * @index: index to the assembly reference in the image.
1121 * @aname: pointer to a `MonoAssemblyName` that will hold the returned value.
1123 * Fills out the @aname with the assembly name of the @index assembly reference in @image.
1125 void
1126 mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname)
1128 MonoTableInfo *t;
1129 guint32 cols [MONO_ASSEMBLYREF_SIZE];
1130 const char *hash;
1132 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1134 mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
1136 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
1137 aname->hash_len = mono_metadata_decode_blob_size (hash, &hash);
1138 aname->hash_value = hash;
1139 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
1140 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
1141 aname->flags = cols [MONO_ASSEMBLYREF_FLAGS];
1142 aname->major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
1143 aname->minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
1144 aname->build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
1145 aname->revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
1147 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
1148 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname->flags);
1149 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1150 g_free (token);
1151 } else {
1152 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1156 void
1157 mono_assembly_load_reference (MonoImage *image, int index)
1159 MonoAssembly *reference;
1160 MonoAssemblyName aname;
1161 MonoImageOpenStatus status;
1164 * image->references is shared between threads, so we need to access
1165 * it inside a critical section.
1167 mono_assemblies_lock ();
1168 if (!image->references) {
1169 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
1171 image->references = g_new0 (MonoAssembly *, t->rows + 1);
1172 image->nreferences = t->rows;
1174 reference = image->references [index];
1175 mono_assemblies_unlock ();
1176 if (reference)
1177 return;
1179 mono_assembly_get_assemblyref (image, index, &aname);
1181 if (image->assembly && image->assembly->ref_only) {
1182 /* We use the loaded corlib */
1183 if (!strcmp (aname.name, "mscorlib"))
1184 reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE);
1185 else {
1186 reference = mono_assembly_loaded_full (&aname, TRUE);
1187 if (!reference)
1188 /* Try a postload search hook */
1189 reference = mono_assembly_invoke_search_hook_internal (&aname, image->assembly, TRUE, TRUE);
1193 * Here we must advice that the error was due to
1194 * a non loaded reference using the ReflectionOnly api
1196 if (!reference)
1197 reference = (MonoAssembly *)REFERENCE_MISSING;
1198 } else {
1199 /* we first try without setting the basedir: this can eventually result in a ResolveAssembly
1200 * event which is the MS .net compatible behaviour (the assemblyresolve_event3.cs test has been fixed
1201 * accordingly, it would fail on the MS runtime before).
1202 * The second load attempt has the basedir set to keep compatibility with the old mono behavior, for
1203 * example bug-349190.2.cs and who knows how much more code in the wild.
1205 reference = mono_assembly_load_full_internal (&aname, image->assembly, NULL, &status, FALSE);
1206 if (!reference && image->assembly)
1207 reference = mono_assembly_load_full_internal (&aname, image->assembly, image->assembly->basedir, &status, FALSE);
1210 if (reference == NULL){
1211 char *extra_msg;
1213 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
1214 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 : "" );
1215 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
1216 extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
1217 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
1218 extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
1219 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
1220 extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
1221 } else {
1222 extra_msg = g_strdup ("");
1225 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n"
1226 " Assembly: %s (assemblyref_index=%d)\n"
1227 " Version: %d.%d.%d.%d\n"
1228 " Public Key: %s\n%s",
1229 image->name, aname.name, index,
1230 aname.major, aname.minor, aname.build, aname.revision,
1231 strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
1232 g_free (extra_msg);
1236 mono_assemblies_lock ();
1237 if (reference == NULL) {
1238 /* Flag as not found */
1239 reference = (MonoAssembly *)REFERENCE_MISSING;
1242 if (!image->references [index]) {
1243 if (reference != REFERENCE_MISSING){
1244 mono_assembly_addref (reference);
1245 if (image->assembly)
1246 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s[%p] -> %s[%p]: %d",
1247 image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count);
1248 } else {
1249 if (image->assembly)
1250 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s[%p].",
1251 image->assembly->aname.name, image->assembly);
1254 image->references [index] = reference;
1256 mono_assemblies_unlock ();
1258 if (image->references [index] != reference) {
1259 /* Somebody loaded it before us */
1260 mono_assembly_close (reference);
1265 * mono_assembly_load_references:
1266 * @image:
1267 * @status:
1268 * @deprecated: There is no reason to use this method anymore, it does nothing
1270 * This method is now a no-op, it does nothing other than setting the @status to #MONO_IMAGE_OK
1272 void
1273 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
1275 /* This is a no-op now but it is part of the embedding API so we can't remove it */
1276 *status = MONO_IMAGE_OK;
1279 typedef struct AssemblyLoadHook AssemblyLoadHook;
1280 struct AssemblyLoadHook {
1281 AssemblyLoadHook *next;
1282 MonoAssemblyLoadFunc func;
1283 gpointer user_data;
1286 AssemblyLoadHook *assembly_load_hook = NULL;
1288 void
1289 mono_assembly_invoke_load_hook (MonoAssembly *ass)
1291 AssemblyLoadHook *hook;
1293 for (hook = assembly_load_hook; hook; hook = hook->next) {
1294 hook->func (ass, hook->user_data);
1298 void
1299 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
1301 AssemblyLoadHook *hook;
1303 g_return_if_fail (func != NULL);
1305 hook = g_new0 (AssemblyLoadHook, 1);
1306 hook->func = func;
1307 hook->user_data = user_data;
1308 hook->next = assembly_load_hook;
1309 assembly_load_hook = hook;
1312 static void
1313 free_assembly_load_hooks (void)
1315 AssemblyLoadHook *hook, *next;
1317 for (hook = assembly_load_hook; hook; hook = next) {
1318 next = hook->next;
1319 g_free (hook);
1323 typedef struct AssemblySearchHook AssemblySearchHook;
1324 struct AssemblySearchHook {
1325 AssemblySearchHook *next;
1326 MonoAssemblySearchFunc func;
1327 gboolean refonly;
1328 gboolean postload;
1329 gpointer user_data;
1332 AssemblySearchHook *assembly_search_hook = NULL;
1334 static MonoAssembly*
1335 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload)
1337 AssemblySearchHook *hook;
1339 for (hook = assembly_search_hook; hook; hook = hook->next) {
1340 if ((hook->refonly == refonly) && (hook->postload == postload)) {
1341 MonoAssembly *ass;
1343 * A little explanation is in order here.
1345 * The default postload search hook needs to know the requesting assembly to report it to managed code.
1346 * The embedding API exposes a search hook that doesn't take such argument.
1348 * The original fix would call the default search hook before all the registered ones and pass
1349 * the requesting assembly to it. It works but broke a very suddle embedding API aspect that some users
1350 * rely on. Which is the ordering between user hooks and the default runtime hook.
1352 * Registering the hook after mono_jit_init would let your hook run before the default one and
1353 * when using it to handle non standard app layouts this could save your app from a massive amount
1354 * of syscalls that the default hook does when probing all sorts of places. Slow targets with horrible IO
1355 * are all using this trick and if we broke this assumption they would be very disapointed at us.
1357 * So what's the fix? We register the default hook using regular means and special case it when iterating
1358 * over the registered hooks. This preserves ordering and enables managed resolve hooks to get the requesting
1359 * assembly.
1361 if (hook->func == (void*)mono_domain_assembly_postload_search)
1362 ass = mono_domain_assembly_postload_search (aname, requesting, refonly);
1363 else
1364 ass = hook->func (aname, hook->user_data);
1365 if (ass)
1366 return ass;
1370 return NULL;
1373 MonoAssembly*
1374 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
1376 return mono_assembly_invoke_search_hook_internal (aname, NULL, FALSE, FALSE);
1379 static void
1380 mono_install_assembly_search_hook_internal (MonoAssemblySearchFunc func, gpointer user_data, gboolean refonly, gboolean postload)
1382 AssemblySearchHook *hook;
1384 g_return_if_fail (func != NULL);
1386 hook = g_new0 (AssemblySearchHook, 1);
1387 hook->func = func;
1388 hook->user_data = user_data;
1389 hook->refonly = refonly;
1390 hook->postload = postload;
1391 hook->next = assembly_search_hook;
1392 assembly_search_hook = hook;
1395 void
1396 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1398 mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE);
1401 static void
1402 free_assembly_search_hooks (void)
1404 AssemblySearchHook *hook, *next;
1406 for (hook = assembly_search_hook; hook; hook = next) {
1407 next = hook->next;
1408 g_free (hook);
1412 void
1413 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1415 mono_install_assembly_search_hook_internal (func, user_data, TRUE, FALSE);
1418 void
1419 mono_install_assembly_postload_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1421 mono_install_assembly_search_hook_internal (func, user_data, FALSE, TRUE);
1424 void
1425 mono_install_assembly_postload_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
1427 mono_install_assembly_search_hook_internal (func, user_data, TRUE, TRUE);
1430 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
1431 struct AssemblyPreLoadHook {
1432 AssemblyPreLoadHook *next;
1433 MonoAssemblyPreLoadFunc func;
1434 gpointer user_data;
1437 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
1438 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
1440 static MonoAssembly *
1441 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
1443 AssemblyPreLoadHook *hook;
1444 MonoAssembly *assembly;
1446 for (hook = assembly_preload_hook; hook; hook = hook->next) {
1447 assembly = hook->func (aname, assemblies_path, hook->user_data);
1448 if (assembly != NULL)
1449 return assembly;
1452 return NULL;
1455 static MonoAssembly *
1456 invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
1458 AssemblyPreLoadHook *hook;
1459 MonoAssembly *assembly;
1461 for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
1462 assembly = hook->func (aname, assemblies_path, hook->user_data);
1463 if (assembly != NULL)
1464 return assembly;
1467 return NULL;
1470 void
1471 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1473 AssemblyPreLoadHook *hook;
1475 g_return_if_fail (func != NULL);
1477 hook = g_new0 (AssemblyPreLoadHook, 1);
1478 hook->func = func;
1479 hook->user_data = user_data;
1480 hook->next = assembly_preload_hook;
1481 assembly_preload_hook = hook;
1484 void
1485 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
1487 AssemblyPreLoadHook *hook;
1489 g_return_if_fail (func != NULL);
1491 hook = g_new0 (AssemblyPreLoadHook, 1);
1492 hook->func = func;
1493 hook->user_data = user_data;
1494 hook->next = assembly_refonly_preload_hook;
1495 assembly_refonly_preload_hook = hook;
1498 static void
1499 free_assembly_preload_hooks (void)
1501 AssemblyPreLoadHook *hook, *next;
1503 for (hook = assembly_preload_hook; hook; hook = next) {
1504 next = hook->next;
1505 g_free (hook);
1508 for (hook = assembly_refonly_preload_hook; hook; hook = next) {
1509 next = hook->next;
1510 g_free (hook);
1514 static gchar *
1515 absolute_dir (const gchar *filename)
1517 gchar *cwd;
1518 gchar *mixed;
1519 gchar **parts;
1520 gchar *part;
1521 GList *list, *tmp;
1522 GString *result;
1523 gchar *res;
1524 gint i;
1526 if (g_path_is_absolute (filename)) {
1527 part = g_path_get_dirname (filename);
1528 res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL);
1529 g_free (part);
1530 return res;
1533 cwd = g_get_current_dir ();
1534 mixed = g_build_filename (cwd, filename, NULL);
1535 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
1536 g_free (mixed);
1537 g_free (cwd);
1539 list = NULL;
1540 for (i = 0; (part = parts [i]) != NULL; i++) {
1541 if (!strcmp (part, "."))
1542 continue;
1544 if (!strcmp (part, "..")) {
1545 if (list && list->next) /* Don't remove root */
1546 list = g_list_delete_link (list, list);
1547 } else {
1548 list = g_list_prepend (list, part);
1552 result = g_string_new ("");
1553 list = g_list_reverse (list);
1555 /* Ignores last data pointer, which should be the filename */
1556 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next){
1557 if (tmp->data)
1558 g_string_append_printf (result, "%s%c", (char *) tmp->data,
1559 G_DIR_SEPARATOR);
1562 res = result->str;
1563 g_string_free (result, FALSE);
1564 g_list_free (list);
1565 g_strfreev (parts);
1566 if (*res == '\0') {
1567 g_free (res);
1568 return g_strdup (".");
1571 return res;
1574 /**
1575 * mono_assembly_open_from_bundle:
1576 * @filename: Filename requested
1577 * @status: return status code
1579 * This routine tries to open the assembly specified by `filename' from the
1580 * defined bundles, if found, returns the MonoImage for it, if not found
1581 * returns NULL
1583 MonoImage *
1584 mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
1586 int i;
1587 char *name;
1588 gchar *lowercase_filename;
1589 MonoImage *image = NULL;
1590 gboolean is_satellite = FALSE;
1592 * we do a very simple search for bundled assemblies: it's not a general
1593 * purpose assembly loading mechanism.
1596 if (!bundles)
1597 return NULL;
1599 lowercase_filename = g_utf8_strdown (filename, -1);
1600 is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
1601 g_free (lowercase_filename);
1602 name = g_path_get_basename (filename);
1603 mono_assemblies_lock ();
1604 for (i = 0; !image && bundles [i]; ++i) {
1605 if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) {
1606 image = mono_image_open_from_data_with_name ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, name);
1607 break;
1610 mono_assemblies_unlock ();
1611 if (image) {
1612 mono_image_addref (image);
1613 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name);
1614 g_free (name);
1615 return image;
1617 g_free (name);
1618 return NULL;
1622 * mono_assemblies_open_full:
1623 * @filename: the file to load
1624 * @status: return status code
1625 * @refonly: Whether this assembly is being opened in "reflection-only" mode.
1627 * This loads an assembly from the specified @filename. The @filename allows
1628 * a local URL (starting with a file:// prefix). If a file prefix is used, the
1629 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
1630 * is treated as a local path.
1632 * First, an attempt is made to load the assembly from the bundled executable (for those
1633 * deployments that have been done with the `mkbundle` tool or for scenarios where the
1634 * assembly has been registered as an embedded assembly). If this is not the case, then
1635 * the assembly is loaded from disk using `api:mono_image_open_full`.
1637 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
1638 * the assembly is made.
1640 * If @refonly is set to true, then the assembly is loaded purely for inspection with
1641 * the `System.Reflection` API.
1643 * Returns: NULL on error, with the @status set to an error code, or a pointer
1644 * to the assembly.
1646 MonoAssembly *
1647 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
1649 MonoImage *image;
1650 MonoAssembly *ass;
1651 MonoImageOpenStatus def_status;
1652 gchar *fname;
1653 gchar *new_fname;
1654 gboolean loaded_from_bundle;
1656 g_return_val_if_fail (filename != NULL, NULL);
1658 if (!status)
1659 status = &def_status;
1660 *status = MONO_IMAGE_OK;
1662 if (strncmp (filename, "file://", 7) == 0) {
1663 GError *error = NULL;
1664 gchar *uri = (gchar *) filename;
1665 gchar *tmpuri;
1668 * MS allows file://c:/... and fails on file://localhost/c:/...
1669 * They also throw an IndexOutOfRangeException if "file://"
1671 if (uri [7] != '/')
1672 uri = g_strdup_printf ("file:///%s", uri + 7);
1674 tmpuri = uri;
1675 uri = mono_escape_uri_string (tmpuri);
1676 fname = g_filename_from_uri (uri, NULL, &error);
1677 g_free (uri);
1679 if (tmpuri != filename)
1680 g_free (tmpuri);
1682 if (error != NULL) {
1683 g_warning ("%s\n", error->message);
1684 g_error_free (error);
1685 fname = g_strdup (filename);
1687 } else {
1688 fname = g_strdup (filename);
1691 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1692 "Assembly Loader probing location: '%s'.", fname);
1694 new_fname = NULL;
1695 if (!mono_assembly_is_in_gac (fname)) {
1696 MonoError error;
1697 new_fname = mono_make_shadow_copy (fname, &error);
1698 if (!is_ok (&error)) {
1699 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1700 "Assembly Loader shadow copy error: %s.", mono_error_get_message (&error));
1701 mono_error_cleanup (&error);
1702 *status = MONO_IMAGE_IMAGE_INVALID;
1703 g_free (fname);
1704 return NULL;
1707 if (new_fname && new_fname != fname) {
1708 g_free (fname);
1709 fname = new_fname;
1710 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1711 "Assembly Loader shadow-copied assembly to: '%s'.", fname);
1714 image = NULL;
1716 // If VM built with mkbundle
1717 loaded_from_bundle = FALSE;
1718 if (bundles != NULL) {
1719 image = mono_assembly_open_from_bundle (fname, status, refonly);
1720 loaded_from_bundle = image != NULL;
1723 if (!image)
1724 image = mono_image_open_full (fname, status, refonly);
1726 if (!image){
1727 if (*status == MONO_IMAGE_OK)
1728 *status = MONO_IMAGE_ERROR_ERRNO;
1729 g_free (fname);
1730 return NULL;
1733 if (image->assembly) {
1734 /* Already loaded by another appdomain */
1735 mono_assembly_invoke_load_hook (image->assembly);
1736 mono_image_close (image);
1737 g_free (fname);
1738 return image->assembly;
1741 ass = mono_assembly_load_from_full (image, fname, status, refonly);
1743 if (ass) {
1744 if (!loaded_from_bundle)
1745 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
1746 "Assembly Loader loaded assembly from location: '%s'.", filename);
1747 if (!refonly)
1748 mono_config_for_assembly (ass->image);
1751 /* Clear the reference added by mono_image_open */
1752 mono_image_close (image);
1754 g_free (fname);
1756 return ass;
1759 static void
1760 free_item (gpointer val, gpointer user_data)
1762 g_free (val);
1766 * mono_assembly_load_friends:
1767 * @ass: an assembly
1769 * Load the list of friend assemblies that are allowed to access
1770 * the assembly's internal types and members. They are stored as assembly
1771 * names in custom attributes.
1773 * This is an internal method, we need this because when we load mscorlib
1774 * we do not have the internals visible cattr loaded yet,
1775 * so we need to load these after we initialize the runtime.
1777 * LOCKING: Acquires the assemblies lock plus the loader lock.
1779 void
1780 mono_assembly_load_friends (MonoAssembly* ass)
1782 MonoError error;
1783 int i;
1784 MonoCustomAttrInfo* attrs;
1785 GSList *list;
1787 if (ass->friend_assembly_names_inited)
1788 return;
1790 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, &error);
1791 mono_error_assert_ok (&error);
1792 if (!attrs) {
1793 mono_assemblies_lock ();
1794 ass->friend_assembly_names_inited = TRUE;
1795 mono_assemblies_unlock ();
1796 return;
1799 mono_assemblies_lock ();
1800 if (ass->friend_assembly_names_inited) {
1801 mono_assemblies_unlock ();
1802 return;
1804 mono_assemblies_unlock ();
1806 list = NULL;
1808 * We build the list outside the assemblies lock, the worse that can happen
1809 * is that we'll need to free the allocated list.
1811 for (i = 0; i < attrs->num_attrs; ++i) {
1812 MonoCustomAttrEntry *attr = &attrs->attrs [i];
1813 MonoAssemblyName *aname;
1814 const gchar *data;
1815 /* Do some sanity checking */
1816 if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ())
1817 continue;
1818 if (attr->data_size < 4)
1819 continue;
1820 data = (const char*)attr->data;
1821 /* 0xFF means null string, see custom attr format */
1822 if (data [0] != 1 || data [1] != 0 || (data [2] & 0xFF) == 0xFF)
1823 continue;
1824 mono_metadata_decode_value (data + 2, &data);
1825 aname = g_new0 (MonoAssemblyName, 1);
1826 /*g_print ("friend ass: %s\n", data);*/
1827 if (mono_assembly_name_parse_full (data, aname, TRUE, NULL, NULL)) {
1828 list = g_slist_prepend (list, aname);
1829 } else {
1830 g_free (aname);
1833 mono_custom_attrs_free (attrs);
1835 mono_assemblies_lock ();
1836 if (ass->friend_assembly_names_inited) {
1837 mono_assemblies_unlock ();
1838 g_slist_foreach (list, free_item, NULL);
1839 g_slist_free (list);
1840 return;
1842 ass->friend_assembly_names = list;
1844 /* Because of the double checked locking pattern above */
1845 mono_memory_barrier ();
1846 ass->friend_assembly_names_inited = TRUE;
1847 mono_assemblies_unlock ();
1850 struct HasReferenceAssemblyAttributeIterData {
1851 gboolean has_attr;
1854 static gboolean
1855 has_reference_assembly_attribute_iterator (MonoImage *image, guint32 typeref_scope_token, const char *nspace, const char *name, guint32 method_token, gpointer user_data)
1857 gboolean stop_scanning = FALSE;
1858 struct HasReferenceAssemblyAttributeIterData *iter_data = (struct HasReferenceAssemblyAttributeIterData*)user_data;
1860 if (!strcmp (name, "ReferenceAssemblyAttribute") && !strcmp (nspace, "System.Runtime.CompilerServices")) {
1861 /* Note we don't check the assembly name, same as coreCLR. */
1862 iter_data->has_attr = TRUE;
1863 stop_scanning = TRUE;
1866 return stop_scanning;
1870 * mono_assembly_has_reference_assembly_attribute:
1871 * @assembly: a MonoAssembly
1872 * @error: set on error.
1874 * Returns TRUE if @assembly has the System.Runtime.CompilerServices.ReferenceAssemblyAttribute set.
1875 * On error returns FALSE and sets @error.
1877 gboolean
1878 mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
1880 mono_error_init (error);
1883 * This might be called during assembly loading, so do everything using the low-level
1884 * metadata APIs.
1887 struct HasReferenceAssemblyAttributeIterData iter_data = { FALSE };
1889 mono_assembly_metadata_foreach_custom_attr (assembly, &has_reference_assembly_attribute_iterator, &iter_data);
1891 return iter_data.has_attr;
1895 * mono_assembly_open:
1896 * @filename: Opens the assembly pointed out by this name
1897 * @status: return status code
1899 * This loads an assembly from the specified @filename. The @filename allows
1900 * a local URL (starting with a file:// prefix). If a file prefix is used, the
1901 * filename is interpreted as a URL, and the filename is URL-decoded. Otherwise the file
1902 * is treated as a local path.
1904 * First, an attempt is made to load the assembly from the bundled executable (for those
1905 * deployments that have been done with the `mkbundle` tool or for scenarios where the
1906 * assembly has been registered as an embedded assembly). If this is not the case, then
1907 * the assembly is loaded from disk using `api:mono_image_open_full`.
1909 * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
1910 * the assembly is made.
1912 * Return: a pointer to the MonoAssembly if @filename contains a valid
1913 * assembly or NULL on error. Details about the error are stored in the
1914 * @status variable.
1916 MonoAssembly *
1917 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
1919 return mono_assembly_open_full (filename, status, FALSE);
1923 * mono_assembly_load_from_full:
1924 * @image: Image to load the assembly from
1925 * @fname: assembly name to associate with the assembly
1926 * @status: returns the status condition
1927 * @refonly: Whether this assembly is being opened in "reflection-only" mode.
1929 * If the provided @image has an assembly reference, it will process the given
1930 * image as an assembly with the given name.
1932 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
1934 * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be
1935 * set to #MONO_IMAGE_OK; or NULL on error.
1937 * If there is an error loading the assembly the @status will indicate the
1938 * reason with @status being set to `MONO_IMAGE_INVALID` if the
1939 * image did not contain an assembly reference table.
1941 MonoAssembly *
1942 mono_assembly_load_from_full (MonoImage *image, const char*fname,
1943 MonoImageOpenStatus *status, gboolean refonly)
1945 MonoAssembly *ass, *ass2;
1946 char *base_dir;
1948 if (!image->tables [MONO_TABLE_ASSEMBLY].rows) {
1949 /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */
1950 *status = MONO_IMAGE_IMAGE_INVALID;
1951 return NULL;
1954 #if defined (HOST_WIN32)
1956 gchar *tmp_fn;
1957 int i;
1959 tmp_fn = g_strdup (fname);
1960 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
1961 if (tmp_fn [i] == '/')
1962 tmp_fn [i] = '\\';
1965 base_dir = absolute_dir (tmp_fn);
1966 g_free (tmp_fn);
1968 #else
1969 base_dir = absolute_dir (fname);
1970 #endif
1973 * Create assembly struct, and enter it into the assembly cache
1975 ass = g_new0 (MonoAssembly, 1);
1976 ass->basedir = base_dir;
1977 ass->ref_only = refonly;
1978 ass->image = image;
1980 mono_profiler_assembly_event (ass, MONO_PROFILE_START_LOAD);
1982 mono_assembly_fill_assembly_name (image, &ass->aname);
1984 if (mono_defaults.corlib && strcmp (ass->aname.name, "mscorlib") == 0) {
1985 // MS.NET doesn't support loading other mscorlibs
1986 g_free (ass);
1987 g_free (base_dir);
1988 mono_image_addref (mono_defaults.corlib);
1989 *status = MONO_IMAGE_OK;
1990 return mono_defaults.corlib->assembly;
1993 /* Add a non-temporary reference because of ass->image */
1994 mono_image_addref (image);
1996 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s[%p] -> %s[%p]: %d", ass->aname.name, ass, image->name, image, image->ref_count);
1999 * The load hooks might take locks so we can't call them while holding the
2000 * assemblies lock.
2002 if (ass->aname.name) {
2003 ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, NULL, refonly, FALSE);
2004 if (ass2) {
2005 g_free (ass);
2006 g_free (base_dir);
2007 mono_image_close (image);
2008 *status = MONO_IMAGE_OK;
2009 return ass2;
2013 /* We need to check for ReferenceAssmeblyAttribute before we
2014 * mark the assembly as loaded and before we fire the load
2015 * hook. Otherwise mono_domain_fire_assembly_load () in
2016 * appdomain.c will cache a mapping from the assembly name to
2017 * this image and we won't be able to look for a different
2018 * candidate. */
2020 if (!refonly) {
2021 MonoError refasm_error;
2022 if (mono_assembly_has_reference_assembly_attribute (ass, &refasm_error)) {
2023 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name);
2024 g_free (ass);
2025 g_free (base_dir);
2026 mono_image_close (image);
2027 *status = MONO_IMAGE_IMAGE_INVALID;
2028 return NULL;
2030 mono_error_cleanup (&refasm_error);
2033 mono_assemblies_lock ();
2035 if (image->assembly) {
2037 * This means another thread has already loaded the assembly, but not yet
2038 * called the load hooks so the search hook can't find the assembly.
2040 mono_assemblies_unlock ();
2041 ass2 = image->assembly;
2042 g_free (ass);
2043 g_free (base_dir);
2044 mono_image_close (image);
2045 *status = MONO_IMAGE_OK;
2046 return ass2;
2049 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name);
2051 image->assembly = ass;
2053 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
2054 mono_assemblies_unlock ();
2056 #ifdef HOST_WIN32
2057 if (image->is_module_handle)
2058 mono_image_fixup_vtable (image);
2059 #endif
2061 mono_assembly_invoke_load_hook (ass);
2063 mono_profiler_assembly_loaded (ass, MONO_PROFILE_OK);
2065 return ass;
2069 * mono_assembly_load_from:
2070 * @image: Image to load the assembly from
2071 * @fname: assembly name to associate with the assembly
2072 * @status: return status code
2074 * If the provided @image has an assembly reference, it will process the given
2075 * image as an assembly with the given name.
2077 * Most likely you want to use the `api:mono_assembly_load_full` method instead.
2079 * This is equivalent to calling `api:mono_assembly_load_from_full` with the
2080 * @refonly parameter set to FALSE.
2081 * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be
2082 * set to #MONO_IMAGE_OK; or NULL on error.
2084 * If there is an error loading the assembly the @status will indicate the
2085 * reason with @status being set to `MONO_IMAGE_INVALID` if the
2086 * image did not contain an assembly reference table.
2089 MonoAssembly *
2090 mono_assembly_load_from (MonoImage *image, const char *fname,
2091 MonoImageOpenStatus *status)
2093 return mono_assembly_load_from_full (image, fname, status, FALSE);
2097 * mono_assembly_name_free:
2098 * @aname: assembly name to free
2100 * Frees the provided assembly name object.
2101 * (it does not frees the object itself, only the name members).
2103 void
2104 mono_assembly_name_free (MonoAssemblyName *aname)
2106 if (aname == NULL)
2107 return;
2109 g_free ((void *) aname->name);
2110 g_free ((void *) aname->culture);
2111 g_free ((void *) aname->hash_value);
2112 g_free ((guint8*) aname->public_key);
2115 static gboolean
2116 parse_public_key (const gchar *key, gchar** pubkey, gboolean *is_ecma)
2118 const gchar *pkey;
2119 gchar header [16], val, *arr;
2120 gint i, j, offset, bitlen, keylen, pkeylen;
2122 keylen = strlen (key) >> 1;
2123 if (keylen < 1)
2124 return FALSE;
2126 /* allow the ECMA standard key */
2127 if (strcmp (key, "00000000000000000400000000000000") == 0) {
2128 if (pubkey) {
2129 *pubkey = g_strdup (key);
2130 *is_ecma = TRUE;
2132 return TRUE;
2134 *is_ecma = FALSE;
2135 val = g_ascii_xdigit_value (key [0]) << 4;
2136 val |= g_ascii_xdigit_value (key [1]);
2137 switch (val) {
2138 case 0x00:
2139 if (keylen < 13)
2140 return FALSE;
2141 val = g_ascii_xdigit_value (key [24]);
2142 val |= g_ascii_xdigit_value (key [25]);
2143 if (val != 0x06)
2144 return FALSE;
2145 pkey = key + 24;
2146 break;
2147 case 0x06:
2148 pkey = key;
2149 break;
2150 default:
2151 return FALSE;
2154 /* We need the first 16 bytes
2155 * to check whether this key is valid or not */
2156 pkeylen = strlen (pkey) >> 1;
2157 if (pkeylen < 16)
2158 return FALSE;
2160 for (i = 0, j = 0; i < 16; i++) {
2161 header [i] = g_ascii_xdigit_value (pkey [j++]) << 4;
2162 header [i] |= g_ascii_xdigit_value (pkey [j++]);
2165 if (header [0] != 0x06 || /* PUBLICKEYBLOB (0x06) */
2166 header [1] != 0x02 || /* Version (0x02) */
2167 header [2] != 0x00 || /* Reserved (word) */
2168 header [3] != 0x00 ||
2169 (guint)(read32 (header + 8)) != 0x31415352) /* DWORD magic = RSA1 */
2170 return FALSE;
2172 /* Based on this length, we _should_ be able to know if the length is right */
2173 bitlen = read32 (header + 12) >> 3;
2174 if ((bitlen + 16 + 4) != pkeylen)
2175 return FALSE;
2177 /* parsing is OK and the public key itself is not requested back */
2178 if (!pubkey)
2179 return TRUE;
2181 /* Encode the size of the blob */
2182 offset = 0;
2183 if (keylen <= 127) {
2184 arr = (gchar *)g_malloc (keylen + 1);
2185 arr [offset++] = keylen;
2186 } else {
2187 arr = (gchar *)g_malloc (keylen + 2);
2188 arr [offset++] = 0x80; /* 10bs */
2189 arr [offset++] = keylen;
2192 for (i = offset, j = 0; i < keylen + offset; i++) {
2193 arr [i] = g_ascii_xdigit_value (key [j++]) << 4;
2194 arr [i] |= g_ascii_xdigit_value (key [j++]);
2197 *pubkey = arr;
2199 return TRUE;
2202 static gboolean
2203 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)
2205 gint major, minor, build, revision;
2206 gint len;
2207 gint version_parts;
2208 gchar *pkey, *pkeyptr, *encoded, tok [8];
2210 memset (aname, 0, sizeof (MonoAssemblyName));
2212 if (version) {
2213 version_parts = sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision);
2214 if (version_parts < 2 || version_parts > 4)
2215 return FALSE;
2217 /* FIXME: we should set build & revision to -1 (instead of 0)
2218 if these are not set in the version string. That way, later on,
2219 we can still determine if these were specified. */
2220 aname->major = major;
2221 aname->minor = minor;
2222 if (version_parts >= 3)
2223 aname->build = build;
2224 else
2225 aname->build = 0;
2226 if (version_parts == 4)
2227 aname->revision = revision;
2228 else
2229 aname->revision = 0;
2232 aname->flags = flags;
2233 aname->arch = arch;
2234 aname->name = g_strdup (name);
2236 if (culture) {
2237 if (g_ascii_strcasecmp (culture, "neutral") == 0)
2238 aname->culture = g_strdup ("");
2239 else
2240 aname->culture = g_strdup (culture);
2243 if (token && strncmp (token, "null", 4) != 0) {
2244 char *lower;
2246 /* the constant includes the ending NULL, hence the -1 */
2247 if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) {
2248 mono_assembly_name_free (aname);
2249 return FALSE;
2251 lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
2252 g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH);
2253 g_free (lower);
2256 if (key) {
2257 gboolean is_ecma;
2258 if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey, &is_ecma)) {
2259 mono_assembly_name_free (aname);
2260 return FALSE;
2263 if (is_ecma) {
2264 if (save_public_key)
2265 aname->public_key = (guint8*)pkey;
2266 else
2267 g_free (pkey);
2268 g_strlcpy ((gchar*)aname->public_key_token, "b77a5c561934e089", MONO_PUBLIC_KEY_TOKEN_LENGTH);
2269 return TRUE;
2272 len = mono_metadata_decode_blob_size ((const gchar *) pkey, (const gchar **) &pkeyptr);
2273 // We also need to generate the key token
2274 mono_digest_get_public_token ((guchar*) tok, (guint8*) pkeyptr, len);
2275 encoded = encode_public_tok ((guchar*) tok, 8);
2276 g_strlcpy ((gchar*)aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
2277 g_free (encoded);
2279 if (save_public_key)
2280 aname->public_key = (guint8*) pkey;
2281 else
2282 g_free (pkey);
2285 return TRUE;
2288 static gboolean
2289 parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname)
2291 gchar **parts;
2292 gboolean res;
2294 parts = g_strsplit (dirname, "_", 3);
2295 if (!parts || !parts[0] || !parts[1] || !parts[2]) {
2296 g_strfreev (parts);
2297 return FALSE;
2300 res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, 0, aname, FALSE);
2301 g_strfreev (parts);
2302 return res;
2305 static gboolean
2306 split_key_value (const gchar *pair, gchar **key, guint32 *keylen, gchar **value)
2308 char *eqsign = strchr (pair, '=');
2309 if (!eqsign) {
2310 *key = NULL;
2311 *keylen = 0;
2312 *value = NULL;
2313 return FALSE;
2316 *key = (gchar*)pair;
2317 *keylen = eqsign - *key;
2318 while (*keylen > 0 && g_ascii_isspace ((*key) [*keylen - 1]))
2319 (*keylen)--;
2320 *value = g_strstrip (eqsign + 1);
2321 return TRUE;
2324 gboolean
2325 mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined)
2327 gchar *dllname;
2328 gchar *dllname_uq;
2329 gchar *version = NULL;
2330 gchar *version_uq;
2331 gchar *culture = NULL;
2332 gchar *culture_uq;
2333 gchar *token = NULL;
2334 gchar *token_uq;
2335 gchar *key = NULL;
2336 gchar *key_uq;
2337 gchar *retargetable = NULL;
2338 gchar *retargetable_uq;
2339 gchar *procarch;
2340 gchar *procarch_uq;
2341 gboolean res;
2342 gchar *value, *part_name;
2343 guint32 part_name_len;
2344 gchar **parts;
2345 gchar **tmp;
2346 gboolean version_defined;
2347 gboolean token_defined;
2348 guint32 flags = 0;
2349 guint32 arch = MONO_PROCESSOR_ARCHITECTURE_NONE;
2351 if (!is_version_defined)
2352 is_version_defined = &version_defined;
2353 *is_version_defined = FALSE;
2354 if (!is_token_defined)
2355 is_token_defined = &token_defined;
2356 *is_token_defined = FALSE;
2358 parts = tmp = g_strsplit (name, ",", 6);
2359 if (!tmp || !*tmp) {
2360 g_strfreev (tmp);
2361 return FALSE;
2364 dllname = g_strstrip (*tmp);
2366 tmp++;
2368 while (*tmp) {
2369 if (!split_key_value (g_strstrip (*tmp), &part_name, &part_name_len, &value))
2370 goto cleanup_and_fail;
2372 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Version", part_name_len)) {
2373 *is_version_defined = TRUE;
2374 version = value;
2375 if (strlen (version) == 0) {
2376 goto cleanup_and_fail;
2378 tmp++;
2379 continue;
2382 if (part_name_len == 7 && !g_ascii_strncasecmp (part_name, "Culture", part_name_len)) {
2383 culture = value;
2384 if (strlen (culture) == 0) {
2385 goto cleanup_and_fail;
2387 tmp++;
2388 continue;
2391 if (part_name_len == 14 && !g_ascii_strncasecmp (part_name, "PublicKeyToken", part_name_len)) {
2392 *is_token_defined = TRUE;
2393 token = value;
2394 if (strlen (token) == 0) {
2395 goto cleanup_and_fail;
2397 tmp++;
2398 continue;
2401 if (part_name_len == 9 && !g_ascii_strncasecmp (part_name, "PublicKey", part_name_len)) {
2402 key = value;
2403 if (strlen (key) == 0) {
2404 goto cleanup_and_fail;
2406 tmp++;
2407 continue;
2410 if (part_name_len == 12 && !g_ascii_strncasecmp (part_name, "Retargetable", part_name_len)) {
2411 retargetable = value;
2412 retargetable_uq = unquote (retargetable);
2413 if (retargetable_uq != NULL)
2414 retargetable = retargetable_uq;
2416 if (!g_ascii_strcasecmp (retargetable, "yes")) {
2417 flags |= ASSEMBLYREF_RETARGETABLE_FLAG;
2418 } else if (g_ascii_strcasecmp (retargetable, "no")) {
2419 g_free (retargetable_uq);
2420 goto cleanup_and_fail;
2423 g_free (retargetable_uq);
2424 tmp++;
2425 continue;
2428 if (part_name_len == 21 && !g_ascii_strncasecmp (part_name, "ProcessorArchitecture", part_name_len)) {
2429 procarch = value;
2430 procarch_uq = unquote (procarch);
2431 if (procarch_uq != NULL)
2432 procarch = procarch_uq;
2434 if (!g_ascii_strcasecmp (procarch, "MSIL"))
2435 arch = MONO_PROCESSOR_ARCHITECTURE_MSIL;
2436 else if (!g_ascii_strcasecmp (procarch, "X86"))
2437 arch = MONO_PROCESSOR_ARCHITECTURE_X86;
2438 else if (!g_ascii_strcasecmp (procarch, "IA64"))
2439 arch = MONO_PROCESSOR_ARCHITECTURE_IA64;
2440 else if (!g_ascii_strcasecmp (procarch, "AMD64"))
2441 arch = MONO_PROCESSOR_ARCHITECTURE_AMD64;
2442 else {
2443 g_free (procarch_uq);
2444 goto cleanup_and_fail;
2447 g_free (procarch_uq);
2448 tmp++;
2449 continue;
2452 g_strfreev (parts);
2453 return FALSE;
2456 /* if retargetable flag is set, then we must have a fully qualified name */
2457 if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) {
2458 goto cleanup_and_fail;
2461 dllname_uq = unquote (dllname);
2462 version_uq = unquote (version);
2463 culture_uq = unquote (culture);
2464 token_uq = unquote (token);
2465 key_uq = unquote (key);
2467 res = build_assembly_name (
2468 dllname_uq == NULL ? dllname : dllname_uq,
2469 version_uq == NULL ? version : version_uq,
2470 culture_uq == NULL ? culture : culture_uq,
2471 token_uq == NULL ? token : token_uq,
2472 key_uq == NULL ? key : key_uq,
2473 flags, arch, aname, save_public_key);
2475 g_free (dllname_uq);
2476 g_free (version_uq);
2477 g_free (culture_uq);
2478 g_free (token_uq);
2479 g_free (key_uq);
2481 g_strfreev (parts);
2482 return res;
2484 cleanup_and_fail:
2485 g_strfreev (parts);
2486 return FALSE;
2489 static char*
2490 unquote (const char *str)
2492 gint slen;
2493 const char *end;
2495 if (str == NULL)
2496 return NULL;
2498 slen = strlen (str);
2499 if (slen < 2)
2500 return NULL;
2502 if (*str != '\'' && *str != '\"')
2503 return NULL;
2505 end = str + slen - 1;
2506 if (*str != *end)
2507 return NULL;
2509 return g_strndup (str + 1, slen - 2);
2513 * mono_assembly_name_parse:
2514 * @name: name to parse
2515 * @aname: the destination assembly name
2517 * Parses an assembly qualified type name and assigns the name,
2518 * version, culture and token to the provided assembly name object.
2520 * Returns: TRUE if the name could be parsed.
2522 gboolean
2523 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
2525 return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL);
2529 * mono_assembly_name_new:
2530 * @name: name to parse
2532 * Allocate a new MonoAssemblyName and fill its values from the
2533 * passed @name.
2535 * Returns: a newly allocated structure or NULL if there was any failure.
2537 MonoAssemblyName*
2538 mono_assembly_name_new (const char *name)
2540 MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1);
2541 if (mono_assembly_name_parse (name, aname))
2542 return aname;
2543 g_free (aname);
2544 return NULL;
2547 const char*
2548 mono_assembly_name_get_name (MonoAssemblyName *aname)
2550 return aname->name;
2553 const char*
2554 mono_assembly_name_get_culture (MonoAssemblyName *aname)
2556 return aname->culture;
2559 mono_byte*
2560 mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname)
2562 if (aname->public_key_token [0])
2563 return aname->public_key_token;
2564 return NULL;
2567 uint16_t
2568 mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision)
2570 if (minor)
2571 *minor = aname->minor;
2572 if (build)
2573 *build = aname->build;
2574 if (revision)
2575 *revision = aname->revision;
2576 return aname->major;
2579 static MonoAssembly*
2580 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
2582 gchar *fullpath = NULL;
2583 GDir *dirhandle;
2584 const char* direntry;
2585 MonoAssemblyName gac_aname;
2586 gint major=-1, minor=0, build=0, revision=0;
2587 gboolean exact_version;
2589 dirhandle = g_dir_open (basepath, 0, NULL);
2590 if (!dirhandle)
2591 return NULL;
2593 exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0;
2595 while ((direntry = g_dir_read_name (dirhandle))) {
2596 gboolean match = TRUE;
2598 if(!parse_assembly_directory_name (aname->name, direntry, &gac_aname))
2599 continue;
2601 if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0)
2602 match = FALSE;
2604 if (match && strlen ((char*)aname->public_key_token) > 0 &&
2605 !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token))
2606 match = FALSE;
2608 if (match) {
2609 if (exact_version) {
2610 match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor &&
2611 aname->build == gac_aname.build && aname->revision == gac_aname.revision);
2613 else if (gac_aname.major < major)
2614 match = FALSE;
2615 else if (gac_aname.major == major) {
2616 if (gac_aname.minor < minor)
2617 match = FALSE;
2618 else if (gac_aname.minor == minor) {
2619 if (gac_aname.build < build)
2620 match = FALSE;
2621 else if (gac_aname.build == build && gac_aname.revision <= revision)
2622 match = FALSE;
2627 if (match) {
2628 major = gac_aname.major;
2629 minor = gac_aname.minor;
2630 build = gac_aname.build;
2631 revision = gac_aname.revision;
2632 g_free (fullpath);
2633 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
2636 mono_assembly_name_free (&gac_aname);
2639 g_dir_close (dirhandle);
2641 if (fullpath == NULL)
2642 return NULL;
2643 else {
2644 MonoAssembly *res = mono_assembly_open (fullpath, status);
2645 g_free (fullpath);
2646 return res;
2651 * mono_assembly_load_with_partial_name:
2652 * @name: an assembly name that is then parsed by `api:mono_assembly_name_parse`.
2653 * @status: return status code
2655 * Loads a Mono Assembly from a name. The name is parsed using `api:mono_assembly_name_parse`,
2656 * so it might contain a qualified type name, version, culture and token.
2658 * This will load the assembly from the file whose name is derived from the assembly name
2659 * by appending the .dll extension.
2661 * The assembly is loaded from either one of the extra Global Assembly Caches specified
2662 * by the extra GAC paths (specified by the `MONO_GAC_PREFIX` environment variable) or
2663 * if that fails from the GAC.
2665 * Returns: NULL on failure, or a pointer to a MonoAssembly on success.
2667 MonoAssembly*
2668 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
2670 MonoError error;
2671 MonoAssembly *res;
2672 MonoAssemblyName *aname, base_name;
2673 MonoAssemblyName mapped_aname;
2674 gchar *fullname, *gacpath;
2675 gchar **paths;
2677 memset (&base_name, 0, sizeof (MonoAssemblyName));
2678 aname = &base_name;
2680 if (!mono_assembly_name_parse (name, aname))
2681 return NULL;
2684 * If no specific version has been requested, make sure we load the
2685 * correct version for system assemblies.
2687 if ((aname->major | aname->minor | aname->build | aname->revision) == 0)
2688 aname = mono_assembly_remap_version (aname, &mapped_aname);
2690 res = mono_assembly_loaded (aname);
2691 if (res) {
2692 mono_assembly_name_free (aname);
2693 return res;
2696 res = invoke_assembly_preload_hook (aname, assemblies_path);
2697 if (res) {
2698 res->in_gac = FALSE;
2699 mono_assembly_name_free (aname);
2700 return res;
2703 fullname = g_strdup_printf ("%s.dll", aname->name);
2705 if (extra_gac_paths) {
2706 paths = extra_gac_paths;
2707 while (!res && *paths) {
2708 gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL);
2709 res = probe_for_partial_name (gacpath, fullname, aname, status);
2710 g_free (gacpath);
2711 paths++;
2715 if (res) {
2716 res->in_gac = TRUE;
2717 g_free (fullname);
2718 mono_assembly_name_free (aname);
2719 return res;
2722 gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL);
2723 res = probe_for_partial_name (gacpath, fullname, aname, status);
2724 g_free (gacpath);
2726 g_free (fullname);
2727 mono_assembly_name_free (aname);
2729 if (res)
2730 res->in_gac = TRUE;
2731 else {
2732 MonoDomain *domain = mono_domain_get ();
2734 res = mono_try_assembly_resolve (domain, name, NULL, FALSE, &error);
2735 if (!is_ok (&error)) {
2736 mono_error_cleanup (&error);
2737 if (*status == MONO_IMAGE_OK)
2738 *status = MONO_IMAGE_IMAGE_INVALID;
2742 return res;
2745 static MonoBoolean
2746 mono_assembly_is_in_gac (const gchar *filename)
2748 const gchar *rootdir;
2749 gchar *gp;
2750 gchar **paths;
2752 if (filename == NULL)
2753 return FALSE;
2755 for (paths = extra_gac_paths; paths && *paths; paths++) {
2756 if (strstr (*paths, filename) != *paths)
2757 continue;
2759 gp = (gchar *) (filename + strlen (*paths));
2760 if (*gp != G_DIR_SEPARATOR)
2761 continue;
2762 gp++;
2763 if (strncmp (gp, "lib", 3))
2764 continue;
2765 gp += 3;
2766 if (*gp != G_DIR_SEPARATOR)
2767 continue;
2768 gp++;
2769 if (strncmp (gp, "mono", 4))
2770 continue;
2771 gp += 4;
2772 if (*gp != G_DIR_SEPARATOR)
2773 continue;
2774 gp++;
2775 if (strncmp (gp, "gac", 3))
2776 continue;
2777 gp += 3;
2778 if (*gp != G_DIR_SEPARATOR)
2779 continue;
2781 return TRUE;
2784 rootdir = mono_assembly_getrootdir ();
2785 if (strstr (filename, rootdir) != filename)
2786 return FALSE;
2788 gp = (gchar *) (filename + strlen (rootdir));
2789 if (*gp != G_DIR_SEPARATOR)
2790 return FALSE;
2791 gp++;
2792 if (strncmp (gp, "mono", 4))
2793 return FALSE;
2794 gp += 4;
2795 if (*gp != G_DIR_SEPARATOR)
2796 return FALSE;
2797 gp++;
2798 if (strncmp (gp, "gac", 3))
2799 return FALSE;
2800 gp += 3;
2801 if (*gp != G_DIR_SEPARATOR)
2802 return FALSE;
2803 return TRUE;
2806 static MonoImage*
2807 mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
2809 MonoImage *image;
2810 gchar *filename, *pname, *name, *culture, *version, *fullpath, *subpath;
2811 gchar **paths;
2812 gint32 len;
2814 if (strstr (aname->name, ".dll")) {
2815 len = strlen (aname->name) - 4;
2816 name = (gchar *)g_malloc (len + 1);
2817 strncpy (name, aname->name, len);
2818 name[len] = 0;
2819 } else
2820 name = g_strdup (aname->name);
2822 if (aname->culture)
2823 culture = g_utf8_strdown (aname->culture, -1);
2824 else
2825 culture = g_strdup ("");
2827 pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name);
2828 version = g_strdup_printf ("0.0.0.0_%s_%s", culture, aname->public_key_token);
2829 g_free (name);
2830 g_free (culture);
2832 filename = g_strconcat (pname, ".dll", NULL);
2833 subpath = g_build_path (G_DIR_SEPARATOR_S, pname, version, filename, NULL);
2834 g_free (pname);
2835 g_free (version);
2836 g_free (filename);
2838 image = NULL;
2839 if (extra_gac_paths) {
2840 paths = extra_gac_paths;
2841 while (!image && *paths) {
2842 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths,
2843 "lib", "mono", "gac", subpath, NULL);
2844 image = mono_image_open (fullpath, NULL);
2845 g_free (fullpath);
2846 paths++;
2850 if (image) {
2851 g_free (subpath);
2852 return image;
2855 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
2856 "mono", "gac", subpath, NULL);
2857 image = mono_image_open (fullpath, NULL);
2858 g_free (subpath);
2859 g_free (fullpath);
2861 return image;
2864 static MonoAssemblyName*
2865 mono_assembly_bind_version (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname, MonoAssemblyName *dest_name)
2867 memcpy (dest_name, aname, sizeof (MonoAssemblyName));
2868 dest_name->major = info->new_version.major;
2869 dest_name->minor = info->new_version.minor;
2870 dest_name->build = info->new_version.build;
2871 dest_name->revision = info->new_version.revision;
2873 return dest_name;
2876 /* LOCKING: assembly_binding lock must be held */
2877 static MonoAssemblyBindingInfo*
2878 search_binding_loaded (MonoAssemblyName *aname)
2880 GSList *tmp;
2882 for (tmp = loaded_assembly_bindings; tmp; tmp = tmp->next) {
2883 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)tmp->data;
2884 if (assembly_binding_maps_name (info, aname))
2885 return info;
2888 return NULL;
2891 static inline gboolean
2892 info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right)
2894 if (left->major != right->major || left->minor != right->minor ||
2895 left->build != right->build || left->revision != right->revision)
2896 return FALSE;
2898 return TRUE;
2901 static inline gboolean
2902 info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right)
2904 if (left->has_old_version_bottom != right->has_old_version_bottom)
2905 return FALSE;
2907 if (left->has_old_version_top != right->has_old_version_top)
2908 return FALSE;
2910 if (left->has_new_version != right->has_new_version)
2911 return FALSE;
2913 if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom))
2914 return FALSE;
2916 if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top))
2917 return FALSE;
2919 if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version))
2920 return FALSE;
2922 return TRUE;
2925 /* LOCKING: assumes all the necessary locks are held */
2926 static void
2927 assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data)
2929 MonoAssemblyBindingInfo *info_copy;
2930 GSList *tmp;
2931 MonoAssemblyBindingInfo *info_tmp;
2932 MonoDomain *domain = (MonoDomain*)user_data;
2934 if (!domain)
2935 return;
2937 for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) {
2938 info_tmp = (MonoAssemblyBindingInfo *)tmp->data;
2939 if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp))
2940 return;
2943 info_copy = (MonoAssemblyBindingInfo *)mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo));
2944 memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo));
2945 if (info->name)
2946 info_copy->name = mono_mempool_strdup (domain->mp, info->name);
2947 if (info->culture)
2948 info_copy->culture = mono_mempool_strdup (domain->mp, info->culture);
2950 domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy);
2953 static int
2954 get_version_number (int major, int minor)
2956 return major * 256 + minor;
2959 static inline gboolean
2960 info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
2962 int aname_version_number = get_version_number (aname->major, aname->minor);
2963 if (!info->has_old_version_bottom)
2964 return FALSE;
2966 if (get_version_number (info->old_version_bottom.major, info->old_version_bottom.minor) > aname_version_number)
2967 return FALSE;
2969 if (info->has_old_version_top && get_version_number (info->old_version_top.major, info->old_version_top.minor) < aname_version_number)
2970 return FALSE;
2972 /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */
2973 info->major = aname->major;
2974 info->minor = aname->minor;
2976 return TRUE;
2979 /* LOCKING: Assumes that we are already locked - both loader and domain locks */
2980 static MonoAssemblyBindingInfo*
2981 get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname)
2983 MonoAssemblyBindingInfo *info;
2984 GSList *list;
2986 if (!domain->assembly_bindings)
2987 return NULL;
2989 info = NULL;
2990 for (list = domain->assembly_bindings; list; list = list->next) {
2991 info = (MonoAssemblyBindingInfo *)list->data;
2992 if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname))
2993 break;
2994 info = NULL;
2997 if (info) {
2998 if (info->name && info->public_key_token [0] && info->has_old_version_bottom &&
2999 info->has_new_version && assembly_binding_maps_name (info, aname))
3000 info->is_valid = TRUE;
3001 else
3002 info->is_valid = FALSE;
3005 return info;
3008 static MonoAssemblyName*
3009 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name)
3011 MonoError error;
3012 MonoAssemblyBindingInfo *info, *info2;
3013 MonoImage *ppimage;
3014 MonoDomain *domain;
3016 if (aname->public_key_token [0] == 0)
3017 return aname;
3019 domain = mono_domain_get ();
3021 mono_assembly_binding_lock ();
3022 info = search_binding_loaded (aname);
3023 mono_assembly_binding_unlock ();
3025 if (!info) {
3026 mono_domain_lock (domain);
3027 info = get_per_domain_assembly_binding_info (domain, aname);
3028 mono_domain_unlock (domain);
3031 if (info) {
3032 if (!check_policy_versions (info, aname))
3033 return aname;
3035 mono_assembly_bind_version (info, aname, dest_name);
3036 return dest_name;
3039 if (domain && domain->setup && domain->setup->configuration_file) {
3040 mono_domain_lock (domain);
3041 if (!domain->assembly_bindings_parsed) {
3042 gchar *domain_config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error);
3043 /* expect this to succeed because mono_domain_set_options_from_config () did
3044 * the same thing when the domain was created. */
3045 mono_error_assert_ok (&error);
3047 gchar *domain_config_file_path = mono_portability_find_file (domain_config_file_name, TRUE);
3049 if (!domain_config_file_path)
3050 domain_config_file_path = domain_config_file_name;
3052 mono_config_parse_assembly_bindings (domain_config_file_path, aname->major, aname->minor, domain, assembly_binding_info_parsed);
3053 domain->assembly_bindings_parsed = TRUE;
3054 if (domain_config_file_name != domain_config_file_path)
3055 g_free (domain_config_file_name);
3056 g_free (domain_config_file_path);
3059 info2 = get_per_domain_assembly_binding_info (domain, aname);
3061 if (info2) {
3062 info = (MonoAssemblyBindingInfo *)g_memdup (info2, sizeof (MonoAssemblyBindingInfo));
3063 info->name = g_strdup (info2->name);
3064 info->culture = g_strdup (info2->culture);
3065 info->domain_id = domain->domain_id;
3068 mono_domain_unlock (domain);
3071 if (!info) {
3072 info = g_new0 (MonoAssemblyBindingInfo, 1);
3073 info->major = aname->major;
3074 info->minor = aname->minor;
3077 if (!info->is_valid) {
3078 ppimage = mono_assembly_load_publisher_policy (aname);
3079 if (ppimage) {
3080 get_publisher_policy_info (ppimage, aname, info);
3081 mono_image_close (ppimage);
3085 /* Define default error value if needed */
3086 if (!info->is_valid) {
3087 info->name = g_strdup (aname->name);
3088 info->culture = g_strdup (aname->culture);
3089 g_strlcpy ((char *)info->public_key_token, (const char *)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3092 mono_assembly_binding_lock ();
3093 info2 = search_binding_loaded (aname);
3094 if (info2) {
3095 /* This binding was added by another thread
3096 * before us */
3097 mono_assembly_binding_info_free (info);
3098 g_free (info);
3100 info = info2;
3101 } else
3102 loaded_assembly_bindings = g_slist_prepend (loaded_assembly_bindings, info);
3104 mono_assembly_binding_unlock ();
3106 if (!info->is_valid || !check_policy_versions (info, aname))
3107 return aname;
3109 mono_assembly_bind_version (info, aname, dest_name);
3110 return dest_name;
3114 * mono_assembly_load_from_gac
3116 * @aname: The assembly name object
3118 static MonoAssembly*
3119 mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
3121 MonoAssembly *result = NULL;
3122 gchar *name, *version, *culture, *fullpath, *subpath;
3123 gint32 len;
3124 gchar **paths;
3125 char *pubtok;
3127 if (aname->public_key_token [0] == 0) {
3128 return NULL;
3131 if (strstr (aname->name, ".dll")) {
3132 len = strlen (filename) - 4;
3133 name = (gchar *)g_malloc (len + 1);
3134 strncpy (name, aname->name, len);
3135 name[len] = 0;
3136 } else {
3137 name = g_strdup (aname->name);
3140 if (aname->culture) {
3141 culture = g_utf8_strdown (aname->culture, -1);
3142 } else {
3143 culture = g_strdup ("");
3146 pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
3147 version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
3148 aname->minor, aname->build, aname->revision,
3149 culture, pubtok);
3150 g_free (pubtok);
3152 subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
3153 g_free (name);
3154 g_free (version);
3155 g_free (culture);
3157 if (extra_gac_paths) {
3158 paths = extra_gac_paths;
3159 while (!result && *paths) {
3160 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
3161 result = mono_assembly_open_full (fullpath, status, refonly);
3162 g_free (fullpath);
3163 paths++;
3167 if (result) {
3168 result->in_gac = TRUE;
3169 g_free (subpath);
3170 return result;
3173 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
3174 "mono", "gac", subpath, NULL);
3175 result = mono_assembly_open_full (fullpath, status, refonly);
3176 g_free (fullpath);
3178 if (result)
3179 result->in_gac = TRUE;
3181 g_free (subpath);
3183 return result;
3186 MonoAssembly*
3187 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
3189 char *corlib_file;
3190 MonoAssemblyName *aname;
3192 if (corlib) {
3193 /* g_print ("corlib already loaded\n"); */
3194 return corlib;
3197 // In native client, Corlib is embedded in the executable as static variable corlibData
3198 #if defined(__native_client__)
3199 if (corlibData != NULL && corlibSize != 0) {
3200 int status = 0;
3201 /* First "FALSE" instructs mono not to make a copy. */
3202 /* Second "FALSE" says this is not just a ref. */
3203 MonoImage* image = mono_image_open_from_data_full (corlibData, corlibSize, FALSE, &status, FALSE);
3204 if (image == NULL || status != 0)
3205 g_print("mono_image_open_from_data_full failed: %d\n", status);
3206 corlib = mono_assembly_load_from_full (image, "mscorlib", &status, FALSE);
3207 if (corlib == NULL || status != 0)
3208 g_print ("mono_assembly_load_from_full failed: %d\n", status);
3209 if (corlib)
3210 return corlib;
3212 #endif
3214 // A nonstandard preload hook may provide a special mscorlib assembly
3215 aname = mono_assembly_name_new ("mscorlib.dll");
3216 corlib = invoke_assembly_preload_hook (aname, assemblies_path);
3217 mono_assembly_name_free (aname);
3218 g_free (aname);
3219 if (corlib != NULL)
3220 goto return_corlib_and_facades;
3222 // This unusual directory layout can occur if mono is being built and run out of its own source repo
3223 if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
3224 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE);
3225 if (corlib)
3226 goto return_corlib_and_facades;
3229 /* Normal case: Load corlib from mono/<version> */
3230 corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
3231 if (assemblies_path) { // Custom assemblies path
3232 corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE);
3233 if (corlib) {
3234 g_free (corlib_file);
3235 goto return_corlib_and_facades;
3238 corlib = load_in_path (corlib_file, default_path, status, FALSE);
3239 g_free (corlib_file);
3241 return_corlib_and_facades:
3242 if (corlib && !strcmp (runtime->framework_version, "4.5")) // FIXME: stop hardcoding 4.5 here
3243 default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir);
3245 return corlib;
3248 static MonoAssembly*
3249 prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly)
3251 MonoError refasm_error;
3252 mono_error_init (&refasm_error);
3253 if (candidate && !refonly && mono_assembly_has_reference_assembly_attribute (candidate, &refasm_error)) {
3254 candidate = NULL;
3256 mono_error_cleanup (&refasm_error);
3257 return candidate;
3261 MonoAssembly*
3262 mono_assembly_load_full_nosearch (MonoAssemblyName *aname,
3263 const char *basedir,
3264 MonoImageOpenStatus *status,
3265 gboolean refonly)
3267 MonoAssembly *result;
3268 char *fullpath, *filename;
3269 MonoAssemblyName maped_aname;
3270 MonoAssemblyName maped_name_pp;
3271 int ext_index;
3272 const char *ext;
3273 int len;
3275 aname = mono_assembly_remap_version (aname, &maped_aname);
3277 /* Reflection only assemblies don't get assembly binding */
3278 if (!refonly)
3279 aname = mono_assembly_apply_binding (aname, &maped_name_pp);
3281 result = mono_assembly_loaded_full (aname, refonly);
3282 if (result)
3283 return result;
3285 result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path);
3286 if (result) {
3287 result->in_gac = FALSE;
3288 return result;
3291 /* Currently we retrieve the loaded corlib for reflection
3292 * only requests, like a common reflection only assembly
3294 if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) {
3295 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
3298 len = strlen (aname->name);
3299 for (ext_index = 0; ext_index < 2; ext_index ++) {
3300 ext = ext_index == 0 ? ".dll" : ".exe";
3301 if (len > 4 && (!strcmp (aname->name + len - 4, ".dll") || !strcmp (aname->name + len - 4, ".exe"))) {
3302 filename = g_strdup (aname->name);
3303 /* Don't try appending .dll/.exe if it already has one of those extensions */
3304 ext_index++;
3305 } else {
3306 filename = g_strconcat (aname->name, ext, NULL);
3309 result = mono_assembly_load_from_gac (aname, filename, status, refonly);
3310 if (result) {
3311 g_free (filename);
3312 return result;
3315 if (basedir) {
3316 fullpath = g_build_filename (basedir, filename, NULL);
3317 result = mono_assembly_open_full (fullpath, status, refonly);
3318 g_free (fullpath);
3319 if (result) {
3320 result->in_gac = FALSE;
3321 g_free (filename);
3322 return result;
3326 result = load_in_path (filename, default_path, status, refonly);
3327 if (result)
3328 result->in_gac = FALSE;
3329 g_free (filename);
3330 if (result)
3331 return result;
3334 return result;
3337 MonoAssembly*
3338 mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *requesting, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
3340 MonoAssembly *result = mono_assembly_load_full_nosearch (aname, basedir, status, refonly);
3342 if (!result) {
3343 /* Try a postload search hook */
3344 result = mono_assembly_invoke_search_hook_internal (aname, requesting, refonly, TRUE);
3345 result = prevent_reference_assembly_from_running (result, refonly);
3347 return result;
3351 * mono_assembly_load_full:
3352 * @aname: A MonoAssemblyName with the assembly name to load.
3353 * @basedir: A directory to look up the assembly at.
3354 * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation
3355 * @refonly: Whether this assembly is being opened in "reflection-only" mode.
3357 * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it
3358 * attempts to load the assembly from that directory before probing the standard locations.
3360 * If the assembly is being opened in reflection-only mode (@refonly set to TRUE) then no
3361 * assembly binding takes place.
3363 * Returns: the assembly referenced by @aname loaded or NULL on error. On error the
3364 * value pointed by status is updated with an error code.
3366 MonoAssembly*
3367 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
3369 return mono_assembly_load_full_internal (aname, NULL, basedir, status, refonly);
3373 * mono_assembly_load:
3374 * @aname: A MonoAssemblyName with the assembly name to load.
3375 * @basedir: A directory to look up the assembly at.
3376 * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation
3378 * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it
3379 * attempts to load the assembly from that directory before probing the standard locations.
3381 * Returns: the assembly referenced by @aname loaded or NULL on error. On error the
3382 * value pointed by status is updated with an error code.
3384 MonoAssembly*
3385 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
3387 return mono_assembly_load_full_internal (aname, NULL, basedir, status, FALSE);
3391 * mono_assembly_loaded_full:
3392 * @aname: an assembly to look for.
3393 * @refonly: Whether this assembly is being opened in "reflection-only" mode.
3395 * This is used to determine if the specified assembly has been loaded
3396 * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to
3397 * a `MonoAssembly` that matches the `MonoAssemblyName` specified.
3399 MonoAssembly*
3400 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
3402 MonoAssembly *res;
3403 MonoAssemblyName maped_aname;
3405 aname = mono_assembly_remap_version (aname, &maped_aname);
3407 res = mono_assembly_invoke_search_hook_internal (aname, NULL, refonly, FALSE);
3409 return res;
3413 * mono_assembly_loaded:
3414 * @aname: an assembly to look for.
3416 * This is used to determine if the specified assembly has been loaded
3418 * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to
3419 * a `MonoAssembly` that matches the `MonoAssemblyName` specified.
3421 MonoAssembly*
3422 mono_assembly_loaded (MonoAssemblyName *aname)
3424 return mono_assembly_loaded_full (aname, FALSE);
3427 void
3428 mono_assembly_release_gc_roots (MonoAssembly *assembly)
3430 if (assembly == NULL || assembly == REFERENCE_MISSING)
3431 return;
3433 if (assembly_is_dynamic (assembly)) {
3434 int i;
3435 MonoDynamicImage *dynimg = (MonoDynamicImage *)assembly->image;
3436 for (i = 0; i < dynimg->image.module_count; ++i)
3437 mono_dynamic_image_release_gc_roots ((MonoDynamicImage *)dynimg->image.modules [i]);
3438 mono_dynamic_image_release_gc_roots (dynimg);
3443 * Returns whether mono_assembly_close_finish() must be called as
3444 * well. See comment for mono_image_close_except_pools() for why we
3445 * unload in two steps.
3447 gboolean
3448 mono_assembly_close_except_image_pools (MonoAssembly *assembly)
3450 GSList *tmp;
3451 g_return_val_if_fail (assembly != NULL, FALSE);
3453 if (assembly == REFERENCE_MISSING)
3454 return FALSE;
3456 /* Might be 0 already */
3457 if (InterlockedDecrement (&assembly->ref_count) > 0)
3458 return FALSE;
3460 mono_profiler_assembly_event (assembly, MONO_PROFILE_START_UNLOAD);
3462 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly);
3464 mono_debug_close_image (assembly->image);
3466 mono_assemblies_lock ();
3467 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
3468 mono_assemblies_unlock ();
3470 assembly->image->assembly = NULL;
3472 if (!mono_image_close_except_pools (assembly->image))
3473 assembly->image = NULL;
3475 for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
3476 MonoAssemblyName *fname = (MonoAssemblyName *)tmp->data;
3477 mono_assembly_name_free (fname);
3478 g_free (fname);
3480 g_slist_free (assembly->friend_assembly_names);
3481 g_free (assembly->basedir);
3483 mono_profiler_assembly_event (assembly, MONO_PROFILE_END_UNLOAD);
3485 return TRUE;
3488 void
3489 mono_assembly_close_finish (MonoAssembly *assembly)
3491 g_assert (assembly && assembly != REFERENCE_MISSING);
3493 if (assembly->image)
3494 mono_image_close_finish (assembly->image);
3496 if (assembly_is_dynamic (assembly)) {
3497 g_free ((char*)assembly->aname.culture);
3498 } else {
3499 g_free (assembly);
3504 * mono_assembly_close:
3505 * @assembly: the assembly to release.
3507 * This method releases a reference to the @assembly. The assembly is
3508 * only released when all the outstanding references to it are released.
3510 void
3511 mono_assembly_close (MonoAssembly *assembly)
3513 if (mono_assembly_close_except_image_pools (assembly))
3514 mono_assembly_close_finish (assembly);
3517 MonoImage*
3518 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
3520 MonoError error;
3521 MonoImage *result = mono_assembly_load_module_checked (assembly, idx, &error);
3522 mono_error_assert_ok (&error);
3523 return result;
3526 MONO_API MonoImage*
3527 mono_assembly_load_module_checked (MonoAssembly *assembly, uint32_t idx, MonoError *error)
3529 return mono_image_load_file_for_image_checked (assembly->image, idx, error);
3534 * mono_assembly_foreach:
3535 * @func: function to invoke for each assembly loaded
3536 * @user_data: data passed to the callback
3538 * Invokes the provided @func callback for each assembly loaded into
3539 * the runtime. The first parameter passed to the callback is the
3540 * `MonoAssembly*`, and the second parameter is the @user_data.
3542 * This is done for all assemblies loaded in the runtime, not just
3543 * those loaded in the current application domain.
3545 void
3546 mono_assembly_foreach (GFunc func, gpointer user_data)
3548 GList *copy;
3551 * We make a copy of the list to avoid calling the callback inside the
3552 * lock, which could lead to deadlocks.
3554 mono_assemblies_lock ();
3555 copy = g_list_copy (loaded_assemblies);
3556 mono_assemblies_unlock ();
3558 g_list_foreach (loaded_assemblies, func, user_data);
3560 g_list_free (copy);
3564 * mono_assemblies_cleanup:
3566 * Free all resources used by this module.
3568 void
3569 mono_assemblies_cleanup (void)
3571 GSList *l;
3573 mono_os_mutex_destroy (&assemblies_mutex);
3574 mono_os_mutex_destroy (&assembly_binding_mutex);
3576 for (l = loaded_assembly_bindings; l; l = l->next) {
3577 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
3579 mono_assembly_binding_info_free (info);
3580 g_free (info);
3582 g_slist_free (loaded_assembly_bindings);
3584 free_assembly_load_hooks ();
3585 free_assembly_search_hooks ();
3586 free_assembly_preload_hooks ();
3589 /*LOCKING takes the assembly_binding lock*/
3590 void
3591 mono_assembly_cleanup_domain_bindings (guint32 domain_id)
3593 GSList **iter;
3595 mono_assembly_binding_lock ();
3596 iter = &loaded_assembly_bindings;
3597 while (*iter) {
3598 GSList *l = *iter;
3599 MonoAssemblyBindingInfo *info = (MonoAssemblyBindingInfo *)l->data;
3601 if (info->domain_id == domain_id) {
3602 *iter = l->next;
3603 mono_assembly_binding_info_free (info);
3604 g_free (info);
3605 g_slist_free_1 (l);
3606 } else {
3607 iter = &l->next;
3610 mono_assembly_binding_unlock ();
3614 * Holds the assembly of the application, for
3615 * System.Diagnostics.Process::MainModule
3617 static MonoAssembly *main_assembly=NULL;
3619 void
3620 mono_assembly_set_main (MonoAssembly *assembly)
3622 main_assembly = assembly;
3626 * mono_assembly_get_main:
3628 * Returns: the assembly for the application, the first assembly that is loaded by the VM
3630 MonoAssembly *
3631 mono_assembly_get_main (void)
3633 return (main_assembly);
3637 * mono_assembly_get_image:
3638 * @assembly: The assembly to retrieve the image from
3640 * Returns: the MonoImage associated with this assembly.
3642 MonoImage*
3643 mono_assembly_get_image (MonoAssembly *assembly)
3645 return assembly->image;
3649 * mono_assembly_get_name:
3650 * @assembly: The assembly to retrieve the name from
3652 * The returned name's lifetime is the same as @assembly's.
3654 * Returns: the MonoAssemblyName associated with this assembly.
3656 MonoAssemblyName *
3657 mono_assembly_get_name (MonoAssembly *assembly)
3659 return &assembly->aname;
3662 void
3663 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
3665 bundles = assemblies;
3668 #define MONO_DECLSEC_FORMAT_10 0x3C
3669 #define MONO_DECLSEC_FORMAT_20 0x2E
3670 #define MONO_DECLSEC_FIELD 0x53
3671 #define MONO_DECLSEC_PROPERTY 0x54
3673 #define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"")
3674 #define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute")
3675 #define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1)
3676 #define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification")
3677 #define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1)
3679 static gboolean
3680 mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding)
3682 int len;
3683 switch (*p++) {
3684 case MONO_DECLSEC_PROPERTY:
3685 break;
3686 case MONO_DECLSEC_FIELD:
3687 default:
3688 *abort_decoding = TRUE;
3689 return FALSE;
3690 break;
3693 if (*p++ != MONO_TYPE_BOOLEAN) {
3694 *abort_decoding = TRUE;
3695 return FALSE;
3698 /* property name length */
3699 len = mono_metadata_decode_value (p, &p);
3701 if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) {
3702 p += len;
3703 return *p;
3705 p += len + 1;
3707 *resp = p;
3708 return FALSE;
3711 static gboolean
3712 mono_assembly_try_decode_skip_verification (const char *p, const char *endn)
3714 int i, j, num, len, params_len;
3716 if (*p == MONO_DECLSEC_FORMAT_10) {
3717 gsize read, written;
3718 char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL);
3719 if (res) {
3720 gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL;
3721 g_free (res);
3722 return found;
3724 return FALSE;
3726 if (*p++ != MONO_DECLSEC_FORMAT_20)
3727 return FALSE;
3729 /* number of encoded permission attributes */
3730 num = mono_metadata_decode_value (p, &p);
3731 for (i = 0; i < num; ++i) {
3732 gboolean is_valid = FALSE;
3733 gboolean abort_decoding = FALSE;
3735 /* attribute name length */
3736 len = mono_metadata_decode_value (p, &p);
3738 /* We don't really need to fully decode the type. Comparing the name is enough */
3739 is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE);
3741 p += len;
3743 /*size of the params table*/
3744 params_len = mono_metadata_decode_value (p, &p);
3745 if (is_valid) {
3746 const char *params_end = p + params_len;
3748 /* number of parameters */
3749 len = mono_metadata_decode_value (p, &p);
3751 for (j = 0; j < len; ++j) {
3752 if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding))
3753 return TRUE;
3754 if (abort_decoding)
3755 break;
3757 p = params_end;
3758 } else {
3759 p += params_len;
3763 return FALSE;
3767 gboolean
3768 mono_assembly_has_skip_verification (MonoAssembly *assembly)
3770 MonoTableInfo *t;
3771 guint32 cols [MONO_DECL_SECURITY_SIZE];
3772 const char *blob;
3773 int i, len;
3775 if (MONO_SECMAN_FLAG_INIT (assembly->skipverification))
3776 return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification);
3778 t = &assembly->image->tables [MONO_TABLE_DECLSECURITY];
3780 for (i = 0; i < t->rows; ++i) {
3781 mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE);
3782 if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY)
3783 continue;
3784 if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN)
3785 continue;
3787 blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]);
3788 len = mono_metadata_decode_blob_size (blob, &blob);
3789 if (!len)
3790 continue;
3792 if (mono_assembly_try_decode_skip_verification (blob, blob + len)) {
3793 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE);
3794 return TRUE;
3798 MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE);
3799 return FALSE;