Merge pull request #2202 from mono/revert-2090-mono-4.2.0-branch-bug25480
[mono-project.git] / mono / metadata / appdomain.c
blob230bff2e2ec53a02433c914e8e145559072e47bc
1 /*
2 * appdomain.c: AppDomain functions
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Patrik Torstensson
7 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2012 Xamarin Inc
13 #undef ASSEMBLY_LOAD_DEBUG
14 #include <config.h>
15 #include <glib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef HAVE_SYS_TIME_H
22 #include <sys/time.h>
23 #endif
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #ifdef HAVE_UTIME_H
28 #include <utime.h>
29 #else
30 #ifdef HAVE_SYS_UTIME_H
31 #include <sys/utime.h>
32 #endif
33 #endif
35 #include <mono/metadata/gc-internal.h>
36 #include <mono/metadata/object.h>
37 #include <mono/metadata/domain-internals.h>
38 #include "mono/metadata/metadata-internals.h"
39 #include <mono/metadata/assembly.h>
40 #include <mono/metadata/exception.h>
41 #include <mono/metadata/threads.h>
42 #include <mono/metadata/threadpool-ms.h>
43 #include <mono/metadata/socket-io.h>
44 #include <mono/metadata/tabledefs.h>
45 #include <mono/metadata/gc-internal.h>
46 #include <mono/metadata/mono-gc.h>
47 #include <mono/metadata/marshal.h>
48 #include <mono/metadata/monitor.h>
49 #include <mono/metadata/mono-debug.h>
50 #include <mono/metadata/mono-debug-debugger.h>
51 #include <mono/metadata/attach.h>
52 #include <mono/metadata/file-io.h>
53 #include <mono/metadata/lock-tracer.h>
54 #include <mono/metadata/console-io.h>
55 #include <mono/metadata/threads-types.h>
56 #include <mono/metadata/tokentype.h>
57 #include <mono/metadata/profiler-private.h>
58 #include <mono/utils/mono-uri.h>
59 #include <mono/utils/mono-logger-internal.h>
60 #include <mono/utils/mono-path.h>
61 #include <mono/utils/mono-stdlib.h>
62 #include <mono/utils/mono-io-portability.h>
63 #include <mono/utils/mono-error-internals.h>
64 #include <mono/utils/atomic.h>
65 #include <mono/utils/mono-memory-model.h>
66 #include <mono/utils/mono-threads.h>
67 #ifdef HOST_WIN32
68 #include <direct.h>
69 #endif
72 * This is the version number of the corlib-runtime interface. When
73 * making changes to this interface (by changing the layout
74 * of classes the runtime knows about, changing icall signature or
75 * semantics etc), increment this variable. Also increment the
76 * pair of this variable in mscorlib in:
77 * mcs/class/corlib/System/Environment.cs
79 * Changes which are already detected at runtime, like the addition
80 * of icalls, do not require an increment.
82 #define MONO_CORLIB_VERSION 138
84 typedef struct
86 int runtime_count;
87 int assemblybinding_count;
88 MonoDomain *domain;
89 gchar *filename;
90 } RuntimeConfig;
92 mono_mutex_t mono_delegate_section;
94 static gunichar2 process_guid [36];
95 static gboolean process_guid_set = FALSE;
97 static gboolean no_exec = FALSE;
99 static MonoAssembly *
100 mono_domain_assembly_preload (MonoAssemblyName *aname,
101 gchar **assemblies_path,
102 gpointer user_data);
104 static MonoAssembly *
105 mono_domain_assembly_search (MonoAssemblyName *aname,
106 gpointer user_data);
108 static void
109 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
111 static void
112 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
114 static MonoAppDomain *
115 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup);
117 static char *
118 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error);
120 static MonoLoadFunc load_function = NULL;
122 void
123 mono_install_runtime_load (MonoLoadFunc func)
125 load_function = func;
128 MonoDomain*
129 mono_runtime_load (const char *filename, const char *runtime_version)
131 g_assert (load_function);
132 return load_function (filename, runtime_version);
136 * mono_runtime_set_no_exec:
138 * Instructs the runtime to operate in static mode, i.e. avoid/do not
139 * allow managed code execution. This is useful for running the AOT
140 * compiler on platforms which allow full-aot execution only. This
141 * should be called before mono_runtime_init ().
143 void
144 mono_runtime_set_no_exec (gboolean val)
146 no_exec = val;
150 * mono_runtime_get_no_exec:
152 * If true, then the runtime will not allow managed code execution.
154 gboolean
155 mono_runtime_get_no_exec (void)
157 return no_exec;
160 static void
161 create_domain_objects (MonoDomain *domain)
163 MonoDomain *old_domain = mono_domain_get ();
164 MonoString *arg;
165 MonoVTable *string_vt;
166 MonoClassField *string_empty_fld;
168 if (domain != old_domain) {
169 mono_thread_push_appdomain_ref (domain);
170 mono_domain_set_internal_with_options (domain, FALSE);
174 * Initialize String.Empty. This enables the removal of
175 * the static cctor of the String class.
177 string_vt = mono_class_vtable (domain, mono_defaults.string_class);
178 string_empty_fld = mono_class_get_field_from_name (mono_defaults.string_class, "Empty");
179 g_assert (string_empty_fld);
180 mono_field_static_set_value (string_vt, string_empty_fld, mono_string_intern (mono_string_new (domain, "")));
183 * Create an instance early since we can't do it when there is no memory.
185 arg = mono_string_new (domain, "Out of memory");
186 domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
189 * These two are needed because the signal handlers might be executing on
190 * an alternate stack, and Boehm GC can't handle that.
192 arg = mono_string_new (domain, "A null value was found where an object instance was required");
193 domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
194 arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
195 domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
197 /*The ephemeron tombstone i*/
198 domain->ephemeron_tombstone = mono_object_new (domain, mono_defaults.object_class);
200 if (domain != old_domain) {
201 mono_thread_pop_appdomain_ref ();
202 mono_domain_set_internal_with_options (old_domain, FALSE);
206 * This class is used during exception handling, so initialize it here, to prevent
207 * stack overflows while handling stack overflows.
209 mono_class_init (mono_array_class_get (mono_defaults.int_class, 1));
213 * mono_runtime_init:
214 * @domain: domain returned by mono_init ()
216 * Initialize the core AppDomain: this function will run also some
217 * IL initialization code, so it needs the execution engine to be fully
218 * operational.
220 * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
221 * we know the entry_assembly.
224 void
225 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
226 MonoThreadAttachCB attach_cb)
228 MonoAppDomainSetup *setup;
229 MonoAppDomain *ad;
230 MonoClass *class;
232 mono_portability_helpers_init ();
234 mono_gc_base_init ();
235 mono_monitor_init ();
236 mono_marshal_init ();
238 mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
239 mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
240 mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
241 mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
242 mono_install_assembly_postload_search_hook ((void*)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
243 mono_install_assembly_postload_refonly_search_hook ((void*)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
244 mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
245 mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
247 mono_thread_init (start_cb, attach_cb);
249 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
250 setup = (MonoAppDomainSetup *) mono_object_new_pinned (domain, class);
252 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
253 ad = (MonoAppDomain *) mono_object_new_pinned (domain, class);
254 ad->data = domain;
255 domain->domain = ad;
256 domain->setup = setup;
258 mono_mutex_init_recursive (&mono_delegate_section);
260 mono_thread_attach (domain);
262 mono_type_initialization_init ();
264 if (!mono_runtime_get_no_exec ())
265 create_domain_objects (domain);
267 /* GC init has to happen after thread init */
268 mono_gc_init ();
270 /* contexts use GC handles, so they must be initialized after the GC */
271 mono_context_init (domain);
272 mono_context_set (domain->default_context);
274 #ifndef DISABLE_SOCKETS
275 mono_network_init ();
276 #endif
278 mono_console_init ();
279 mono_attach_init ();
281 mono_locks_tracer_init ();
283 /* mscorlib is loaded before we install the load hook */
284 mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
286 return;
289 static int
290 mono_get_corlib_version (void)
292 MonoClass *klass;
293 MonoClassField *field;
294 MonoObject *value;
296 klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
297 mono_class_init (klass);
298 field = mono_class_get_field_from_name (klass, "mono_corlib_version");
299 if (!field)
300 return -1;
301 if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
302 return -1;
303 value = mono_field_get_value_object (mono_domain_get (), field, NULL);
304 return *(gint32*)((gchar*)value + sizeof (MonoObject));
308 * mono_check_corlib_version
310 * Checks that the corlib that is loaded matches the version of this runtime.
312 * Returns: NULL if the runtime will work with the corlib, or a g_malloc
313 * allocated string with the error otherwise.
315 const char*
316 mono_check_corlib_version (void)
318 int version = mono_get_corlib_version ();
319 if (version != MONO_CORLIB_VERSION)
320 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
321 else
322 return NULL;
326 * mono_context_init:
327 * @domain: The domain where the System.Runtime.Remoting.Context.Context is initialized
329 * Initializes the @domain's default System.Runtime.Remoting's Context.
331 void
332 mono_context_init (MonoDomain *domain)
334 MonoClass *class;
335 MonoAppContext *context;
337 class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
338 context = (MonoAppContext *) mono_object_new_pinned (domain, class);
339 context->domain_id = domain->domain_id;
340 context->context_id = 0;
341 ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (context);
342 domain->default_context = context;
346 * mono_runtime_cleanup:
347 * @domain: unused.
349 * Internal routine.
351 * This must not be called while there are still running threads executing
352 * managed code.
354 void
355 mono_runtime_cleanup (MonoDomain *domain)
357 mono_attach_cleanup ();
359 /* This ends up calling any pending pending (for at most 2 seconds) */
360 mono_gc_cleanup ();
362 mono_thread_cleanup ();
364 #ifndef DISABLE_SOCKETS
365 mono_network_cleanup ();
366 #endif
367 mono_marshal_cleanup ();
369 mono_type_initialization_cleanup ();
371 mono_monitor_cleanup ();
374 static MonoDomainFunc quit_function = NULL;
376 void
377 mono_install_runtime_cleanup (MonoDomainFunc func)
379 quit_function = func;
382 void
383 mono_runtime_quit ()
385 if (quit_function != NULL)
386 quit_function (mono_get_root_domain (), NULL);
390 * mono_domain_create_appdomain:
391 * @friendly_name: The friendly name of the appdomain to create
392 * @configuration_file: The configuration file to initialize the appdomain with
394 * Returns a MonoDomain initialized with the appdomain
396 MonoDomain *
397 mono_domain_create_appdomain (char *friendly_name, char *configuration_file)
399 MonoAppDomain *ad;
400 MonoAppDomainSetup *setup;
401 MonoClass *class;
403 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
404 setup = (MonoAppDomainSetup *) mono_object_new (mono_domain_get (), class);
405 setup->configuration_file = configuration_file != NULL ? mono_string_new (mono_domain_get (), configuration_file) : NULL;
407 ad = mono_domain_create_appdomain_internal (friendly_name, setup);
409 return mono_domain_from_appdomain (ad);
413 * mono_domain_set_config:
414 * @domain: MonoDomain initialized with the appdomain we want to change
415 * @base_dir: new base directory for the appdomain
416 * @config_file_name: path to the new configuration for the app domain
418 * Used to set the system configuration for an appdomain
420 * Without using this, embedded builds will get 'System.Configuration.ConfigurationErrorsException:
421 * Error Initializing the configuration system. ---> System.ArgumentException:
422 * The 'ExeConfigFilename' argument cannot be null.' for some managed calls.
424 void
425 mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name)
427 MONO_OBJECT_SETREF (domain->setup, application_base, mono_string_new (domain, base_dir));
428 MONO_OBJECT_SETREF (domain->setup, configuration_file, mono_string_new (domain, config_file_name));
431 static MonoAppDomainSetup*
432 copy_app_domain_setup (MonoDomain *domain, MonoAppDomainSetup *setup)
434 MonoDomain *caller_domain = mono_domain_get ();
435 MonoClass *ads_class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
436 MonoAppDomainSetup *copy = (MonoAppDomainSetup*)mono_object_new (domain, ads_class);
438 mono_domain_set_internal (domain);
440 MONO_OBJECT_SETREF (copy, application_base, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_base));
441 MONO_OBJECT_SETREF (copy, application_name, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_name));
442 MONO_OBJECT_SETREF (copy, cache_path, mono_marshal_xdomain_copy_value ((MonoObject*)setup->cache_path));
443 MONO_OBJECT_SETREF (copy, configuration_file, mono_marshal_xdomain_copy_value ((MonoObject*)setup->configuration_file));
444 MONO_OBJECT_SETREF (copy, dynamic_base, mono_marshal_xdomain_copy_value ((MonoObject*)setup->dynamic_base));
445 MONO_OBJECT_SETREF (copy, license_file, mono_marshal_xdomain_copy_value ((MonoObject*)setup->license_file));
446 MONO_OBJECT_SETREF (copy, private_bin_path, mono_marshal_xdomain_copy_value ((MonoObject*)setup->private_bin_path));
447 MONO_OBJECT_SETREF (copy, private_bin_path_probe, mono_marshal_xdomain_copy_value ((MonoObject*)setup->private_bin_path_probe));
448 MONO_OBJECT_SETREF (copy, shadow_copy_directories, mono_marshal_xdomain_copy_value ((MonoObject*)setup->shadow_copy_directories));
449 MONO_OBJECT_SETREF (copy, shadow_copy_files, mono_marshal_xdomain_copy_value ((MonoObject*)setup->shadow_copy_files));
450 copy->publisher_policy = setup->publisher_policy;
451 copy->path_changed = setup->path_changed;
452 copy->loader_optimization = setup->loader_optimization;
453 copy->disallow_binding_redirects = setup->disallow_binding_redirects;
454 copy->disallow_code_downloads = setup->disallow_code_downloads;
455 MONO_OBJECT_SETREF (copy, domain_initializer_args, mono_marshal_xdomain_copy_value ((MonoObject*)setup->domain_initializer_args));
456 copy->disallow_appbase_probe = setup->disallow_appbase_probe;
457 MONO_OBJECT_SETREF (copy, application_trust, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_trust));
458 MONO_OBJECT_SETREF (copy, configuration_bytes, mono_marshal_xdomain_copy_value ((MonoObject*)setup->configuration_bytes));
459 MONO_OBJECT_SETREF (copy, serialized_non_primitives, mono_marshal_xdomain_copy_value ((MonoObject*)setup->serialized_non_primitives));
461 mono_domain_set_internal (caller_domain);
463 return copy;
466 static MonoAppDomain *
467 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup)
469 MonoError error;
470 MonoClass *adclass;
471 MonoAppDomain *ad;
472 MonoDomain *data;
473 char *shadow_location;
475 adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
477 /* FIXME: pin all those objects */
478 data = mono_domain_create();
480 ad = (MonoAppDomain *) mono_object_new (data, adclass);
481 ad->data = data;
482 data->domain = ad;
483 data->friendly_name = g_strdup (friendly_name);
485 mono_profiler_appdomain_name (data, data->friendly_name);
487 if (!setup->application_base) {
488 /* Inherit from the root domain since MS.NET does this */
489 MonoDomain *root = mono_get_root_domain ();
490 if (root->setup->application_base)
491 MONO_OBJECT_SETREF (setup, application_base, mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base)));
494 mono_context_init (data);
496 data->setup = copy_app_domain_setup (data, setup);
497 mono_set_private_bin_path_from_config (data);
498 add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
500 #ifndef DISABLE_SHADOW_COPY
501 /*FIXME, guard this for when the debugger is not running */
502 shadow_location = get_shadow_assembly_location_base (data, &error);
503 if (!mono_error_ok (&error))
504 mono_error_raise_exception (&error);
505 g_free (shadow_location);
506 #endif
508 create_domain_objects (data);
510 return ad;
514 * mono_domain_has_type_resolve:
515 * @domain: application domains being looked up
517 * Returns true if the AppDomain.TypeResolve field has been
518 * set.
520 gboolean
521 mono_domain_has_type_resolve (MonoDomain *domain)
523 static MonoClassField *field = NULL;
524 MonoObject *o;
526 if (field == NULL) {
527 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
528 g_assert (field);
531 /*pedump doesn't create an appdomin, so the domain object doesn't exist.*/
532 if (!domain->domain)
533 return FALSE;
535 mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
536 return o != NULL;
540 * mono_domain_try_type_resolve:
541 * @domain: application domainwhere the name where the type is going to be resolved
542 * @name: the name of the type to resolve or NULL.
543 * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
545 * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
546 * the assembly that matches name.
548 * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
550 * Returns: A MonoReflectionAssembly or NULL if not found
552 MonoReflectionAssembly *
553 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
555 MonoClass *klass;
556 void *params [1];
557 static MonoMethod *method = NULL;
559 g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
561 if (method == NULL) {
562 klass = domain->domain->mbr.obj.vtable->klass;
563 g_assert (klass);
565 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
566 if (method == NULL) {
567 g_warning ("Method AppDomain.DoTypeResolve not found.\n");
568 return NULL;
572 if (name)
573 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
574 else
575 *params = tb;
576 return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
580 * mono_domain_owns_vtable_slot:
582 * Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
584 gboolean
585 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
587 gboolean res;
589 mono_domain_lock (domain);
590 res = mono_mempool_contains_addr (domain->mp, vtable_slot);
591 mono_domain_unlock (domain);
592 return res;
596 * mono_domain_set:
597 * @domain: domain
598 * @force: force setting.
600 * Set the current appdomain to @domain. If @force is set, set it even
601 * if it is being unloaded.
603 * Returns:
604 * TRUE on success;
605 * FALSE if the domain is unloaded
607 gboolean
608 mono_domain_set (MonoDomain *domain, gboolean force)
610 if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
611 return FALSE;
613 mono_domain_set_internal (domain);
615 return TRUE;
618 MonoObject *
619 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
621 MonoDomain *add;
622 MonoObject *o;
623 char *str;
625 MONO_CHECK_ARG_NULL (name, NULL);
627 g_assert (ad != NULL);
628 add = ad->data;
629 g_assert (add != NULL);
631 str = mono_string_to_utf8 (name);
633 mono_domain_lock (add);
635 if (!strcmp (str, "APPBASE"))
636 o = (MonoObject *)add->setup->application_base;
637 else if (!strcmp (str, "APP_CONFIG_FILE"))
638 o = (MonoObject *)add->setup->configuration_file;
639 else if (!strcmp (str, "DYNAMIC_BASE"))
640 o = (MonoObject *)add->setup->dynamic_base;
641 else if (!strcmp (str, "APP_NAME"))
642 o = (MonoObject *)add->setup->application_name;
643 else if (!strcmp (str, "CACHE_BASE"))
644 o = (MonoObject *)add->setup->cache_path;
645 else if (!strcmp (str, "PRIVATE_BINPATH"))
646 o = (MonoObject *)add->setup->private_bin_path;
647 else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
648 o = (MonoObject *)add->setup->private_bin_path_probe;
649 else if (!strcmp (str, "SHADOW_COPY_DIRS"))
650 o = (MonoObject *)add->setup->shadow_copy_directories;
651 else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
652 o = (MonoObject *)add->setup->shadow_copy_files;
653 else
654 o = mono_g_hash_table_lookup (add->env, name);
656 mono_domain_unlock (add);
657 g_free (str);
659 if (!o)
660 return NULL;
662 return o;
665 void
666 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
668 MonoDomain *add;
670 MONO_CHECK_ARG_NULL (name,);
672 g_assert (ad != NULL);
673 add = ad->data;
674 g_assert (add != NULL);
676 mono_domain_lock (add);
678 mono_g_hash_table_insert (add->env, name, data);
680 mono_domain_unlock (add);
683 MonoAppDomainSetup *
684 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
686 g_assert (ad != NULL);
687 g_assert (ad->data != NULL);
689 return ad->data->setup;
692 MonoString *
693 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
695 g_assert (ad != NULL);
696 g_assert (ad->data != NULL);
698 return mono_string_new (ad->data, ad->data->friendly_name);
701 MonoAppDomain *
702 ves_icall_System_AppDomain_getCurDomain ()
704 MonoDomain *add = mono_domain_get ();
706 return add->domain;
709 MonoAppDomain *
710 ves_icall_System_AppDomain_getRootDomain ()
712 MonoDomain *root = mono_get_root_domain ();
714 return root->domain;
717 static char*
718 get_attribute_value (const gchar **attribute_names,
719 const gchar **attribute_values,
720 const char *att_name)
722 int n;
723 for (n = 0; attribute_names [n] != NULL; n++) {
724 if (strcmp (attribute_names [n], att_name) == 0)
725 return g_strdup (attribute_values [n]);
727 return NULL;
730 static void
731 start_element (GMarkupParseContext *context,
732 const gchar *element_name,
733 const gchar **attribute_names,
734 const gchar **attribute_values,
735 gpointer user_data,
736 GError **error)
738 RuntimeConfig *runtime_config = user_data;
740 if (strcmp (element_name, "runtime") == 0) {
741 runtime_config->runtime_count++;
742 return;
745 if (strcmp (element_name, "assemblyBinding") == 0) {
746 runtime_config->assemblybinding_count++;
747 return;
750 if (runtime_config->runtime_count != 1 || runtime_config->assemblybinding_count != 1)
751 return;
753 if (strcmp (element_name, "probing") != 0)
754 return;
756 g_free (runtime_config->domain->private_bin_path);
757 runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
758 if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
759 g_free (runtime_config->domain->private_bin_path);
760 runtime_config->domain->private_bin_path = NULL;
761 return;
765 static void
766 end_element (GMarkupParseContext *context,
767 const gchar *element_name,
768 gpointer user_data,
769 GError **error)
771 RuntimeConfig *runtime_config = user_data;
772 if (strcmp (element_name, "runtime") == 0)
773 runtime_config->runtime_count--;
774 else if (strcmp (element_name, "assemblyBinding") == 0)
775 runtime_config->assemblybinding_count--;
778 static void
779 parse_error (GMarkupParseContext *context, GError *error, gpointer user_data)
781 RuntimeConfig *state = user_data;
782 const gchar *msg;
783 const gchar *filename;
785 filename = state && state->filename ? (gchar *) state->filename : "<unknown>";
786 msg = error && error->message ? error->message : "";
787 g_warning ("Error parsing %s: %s", filename, msg);
790 static const GMarkupParser
791 mono_parser = {
792 start_element,
793 end_element,
794 NULL,
795 NULL,
796 parse_error
799 void
800 mono_set_private_bin_path_from_config (MonoDomain *domain)
802 MonoError error;
803 gchar *config_file_name = NULL, *text = NULL, *config_file_path = NULL;
804 gsize len;
805 GMarkupParseContext *context;
806 RuntimeConfig runtime_config;
807 gint offset;
809 if (!domain || !domain->setup || !domain->setup->configuration_file)
810 return;
812 config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error);
813 if (!mono_error_ok (&error)) {
814 mono_error_cleanup (&error);
815 goto free_and_out;
818 config_file_path = mono_portability_find_file (config_file_name, TRUE);
819 if (!config_file_path)
820 config_file_path = config_file_name;
822 if (!g_file_get_contents (config_file_path, &text, &len, NULL))
823 goto free_and_out;
825 runtime_config.runtime_count = 0;
826 runtime_config.assemblybinding_count = 0;
827 runtime_config.domain = domain;
828 runtime_config.filename = config_file_path;
830 offset = 0;
831 if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf')
832 offset = 3; /* Skip UTF-8 BOM */
834 context = g_markup_parse_context_new (&mono_parser, 0, &runtime_config, NULL);
835 if (g_markup_parse_context_parse (context, text + offset, len - offset, NULL))
836 g_markup_parse_context_end_parse (context, NULL);
837 g_markup_parse_context_free (context);
839 free_and_out:
840 g_free (text);
841 if (config_file_name != config_file_path)
842 g_free (config_file_name);
843 g_free (config_file_path);
846 MonoAppDomain *
847 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
849 #ifdef DISABLE_APPDOMAINS
850 mono_set_pending_exception (mono_get_exception_not_supported ("AppDomain creation is not supported on this runtime."));
851 return NULL;
852 #else
853 char *fname = mono_string_to_utf8 (friendly_name);
854 MonoAppDomain *ad = mono_domain_create_appdomain_internal (fname, setup);
856 g_free (fname);
858 return ad;
859 #endif
862 MonoArray *
863 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
865 MonoDomain *domain = ad->data;
866 MonoAssembly* ass;
867 static MonoClass *System_Reflection_Assembly;
868 MonoArray *res;
869 GSList *tmp;
870 int i;
871 GPtrArray *assemblies;
873 if (!System_Reflection_Assembly)
874 System_Reflection_Assembly = mono_class_from_name (
875 mono_defaults.corlib, "System.Reflection", "Assembly");
878 * Make a copy of the list of assemblies because we can't hold the assemblies
879 * lock while creating objects etc.
881 assemblies = g_ptr_array_new ();
882 /* Need to skip internal assembly builders created by remoting */
883 mono_domain_assemblies_lock (domain);
884 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
885 ass = tmp->data;
886 if (refonly != ass->ref_only)
887 continue;
888 if (ass->corlib_internal)
889 continue;
890 g_ptr_array_add (assemblies, ass);
892 mono_domain_assemblies_unlock (domain);
894 res = mono_array_new (domain, System_Reflection_Assembly, assemblies->len);
895 for (i = 0; i < assemblies->len; ++i) {
896 ass = g_ptr_array_index (assemblies, i);
897 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
900 g_ptr_array_free (assemblies, TRUE);
902 return res;
905 MonoReflectionAssembly *
906 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, MonoAssembly *requesting, gboolean refonly)
908 MonoClass *klass;
909 MonoMethod *method;
910 MonoBoolean isrefonly;
911 gpointer params [3];
913 if (mono_runtime_get_no_exec ())
914 return NULL;
916 g_assert (domain != NULL && fname != NULL);
918 klass = domain->domain->mbr.obj.vtable->klass;
919 g_assert (klass);
921 method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
922 if (method == NULL) {
923 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
924 return NULL;
927 isrefonly = refonly ? 1 : 0;
928 params [0] = fname;
929 params [1] = (requesting) ? mono_assembly_get_object (domain, requesting) : NULL;
930 params [2] = &isrefonly;
931 return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
934 MonoAssembly *
935 mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting,
936 gboolean refonly)
938 MonoReflectionAssembly *assembly;
939 MonoDomain *domain = mono_domain_get ();
940 char *aname_str;
941 MonoString *str;
943 aname_str = mono_stringify_assembly_name (aname);
945 /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
946 str = mono_string_new (domain, aname_str);
947 if (!str) {
948 g_free (aname_str);
949 return NULL;
951 assembly = mono_try_assembly_resolve (domain, str, requesting, refonly);
952 g_free (aname_str);
954 if (assembly)
955 return assembly->assembly;
956 else
957 return NULL;
961 * LOCKING: assumes assemblies_lock in the domain is already locked.
963 static void
964 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
966 gint i;
967 GSList *tmp;
968 gboolean destroy_ht = FALSE;
970 if (!ass->aname.name)
971 return;
973 if (!ht) {
974 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
975 destroy_ht = TRUE;
978 /* FIXME: handle lazy loaded assemblies */
979 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
980 g_hash_table_insert (ht, tmp->data, tmp->data);
982 if (!g_hash_table_lookup (ht, ass)) {
983 mono_assembly_addref (ass);
984 g_hash_table_insert (ht, ass, ass);
985 domain->domain_assemblies = g_slist_append (domain->domain_assemblies, ass);
986 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s[%p] added to domain %s, ref_count=%d", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
989 if (ass->image->references) {
990 for (i = 0; ass->image->references [i] != NULL; i++) {
991 if (ass->image->references [i] != REFERENCE_MISSING)
992 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
993 add_assemblies_to_domain (domain, ass->image->references [i], ht);
997 if (destroy_ht)
998 g_hash_table_destroy (ht);
1001 static void
1002 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
1004 static MonoClassField *assembly_load_field;
1005 static MonoMethod *assembly_load_method;
1006 MonoDomain *domain = mono_domain_get ();
1007 MonoReflectionAssembly *ref_assembly;
1008 MonoClass *klass;
1009 gpointer load_value;
1010 void *params [1];
1012 if (!domain->domain)
1013 /* This can happen during startup */
1014 return;
1015 #ifdef ASSEMBLY_LOAD_DEBUG
1016 fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
1017 #endif
1018 klass = domain->domain->mbr.obj.vtable->klass;
1020 mono_domain_assemblies_lock (domain);
1021 add_assemblies_to_domain (domain, assembly, NULL);
1022 mono_domain_assemblies_unlock (domain);
1024 if (assembly_load_field == NULL) {
1025 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
1026 g_assert (assembly_load_field);
1029 mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
1030 if (load_value == NULL) {
1031 /* No events waiting to be triggered */
1032 return;
1035 ref_assembly = mono_assembly_get_object (domain, assembly);
1036 g_assert (ref_assembly);
1038 if (assembly_load_method == NULL) {
1039 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
1040 g_assert (assembly_load_method);
1043 *params = ref_assembly;
1044 mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
1048 * LOCKING: Acquires the domain assemblies lock.
1050 static void
1051 set_domain_search_path (MonoDomain *domain)
1053 MonoError error;
1054 MonoAppDomainSetup *setup;
1055 gchar **tmp;
1056 gchar *search_path = NULL;
1057 gint i;
1058 gint npaths = 0;
1059 gchar **pvt_split = NULL;
1060 GError *gerror = NULL;
1061 gint appbaselen = -1;
1064 * We use the low-level domain assemblies lock, since this is called from
1065 * assembly loads hooks, which means this thread might hold the loader lock.
1067 mono_domain_assemblies_lock (domain);
1069 if (!domain->setup) {
1070 mono_domain_assemblies_unlock (domain);
1071 return;
1074 if ((domain->search_path != NULL) && !domain->setup->path_changed) {
1075 mono_domain_assemblies_unlock (domain);
1076 return;
1078 setup = domain->setup;
1079 if (!setup->application_base) {
1080 mono_domain_assemblies_unlock (domain);
1081 return; /* Must set application base to get private path working */
1084 npaths++;
1086 if (setup->private_bin_path) {
1087 search_path = mono_string_to_utf8_checked (setup->private_bin_path, &error);
1088 if (!mono_error_ok (&error)) { /*FIXME maybe we should bubble up the error.*/
1089 g_warning ("Could not decode AppDomain search path since it contains invalid characters");
1090 mono_error_cleanup (&error);
1091 mono_domain_assemblies_unlock (domain);
1092 return;
1096 if (domain->private_bin_path) {
1097 if (search_path == NULL)
1098 search_path = domain->private_bin_path;
1099 else {
1100 gchar *tmp2 = search_path;
1101 search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
1102 g_free (tmp2);
1106 if (search_path) {
1108 * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
1109 * directories relative to ApplicationBase separated by semicolons (see
1110 * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
1111 * The loop below copes with the fact that some Unix applications may use ':' (or
1112 * System.IO.Path.PathSeparator) as the path search separator. We replace it with
1113 * ';' for the subsequent split.
1115 * The issue was reported in bug #81446
1118 #ifndef TARGET_WIN32
1119 gint slen;
1121 slen = strlen (search_path);
1122 for (i = 0; i < slen; i++)
1123 if (search_path [i] == ':')
1124 search_path [i] = ';';
1125 #endif
1127 pvt_split = g_strsplit (search_path, ";", 1000);
1128 g_free (search_path);
1129 for (tmp = pvt_split; *tmp; tmp++, npaths++);
1132 if (!npaths) {
1133 if (pvt_split)
1134 g_strfreev (pvt_split);
1136 * Don't do this because the first time is called, the domain
1137 * setup is not finished.
1139 * domain->search_path = g_malloc (sizeof (char *));
1140 * domain->search_path [0] = NULL;
1142 mono_domain_assemblies_unlock (domain);
1143 return;
1146 if (domain->search_path)
1147 g_strfreev (domain->search_path);
1149 tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
1150 tmp [npaths] = NULL;
1152 *tmp = mono_string_to_utf8_checked (setup->application_base, &error);
1153 if (!mono_error_ok (&error)) {
1154 mono_error_cleanup (&error);
1155 g_strfreev (pvt_split);
1156 g_free (tmp);
1158 mono_domain_assemblies_unlock (domain);
1159 return;
1162 domain->search_path = tmp;
1164 /* FIXME: is this needed? */
1165 if (strncmp (*tmp, "file://", 7) == 0) {
1166 gchar *file = *tmp;
1167 gchar *uri = *tmp;
1168 gchar *tmpuri;
1170 if (uri [7] != '/')
1171 uri = g_strdup_printf ("file:///%s", uri + 7);
1173 tmpuri = uri;
1174 uri = mono_escape_uri_string (tmpuri);
1175 *tmp = g_filename_from_uri (uri, NULL, &gerror);
1176 g_free (uri);
1178 if (tmpuri != file)
1179 g_free (tmpuri);
1181 if (gerror != NULL) {
1182 g_warning ("%s\n", gerror->message);
1183 g_error_free (gerror);
1184 *tmp = file;
1185 } else {
1186 g_free (file);
1190 for (i = 1; pvt_split && i < npaths; i++) {
1191 if (g_path_is_absolute (pvt_split [i - 1])) {
1192 tmp [i] = g_strdup (pvt_split [i - 1]);
1193 } else {
1194 tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
1197 if (strchr (tmp [i], '.')) {
1198 gchar *reduced;
1199 gchar *freeme;
1201 reduced = mono_path_canonicalize (tmp [i]);
1202 if (appbaselen == -1)
1203 appbaselen = strlen (tmp [0]);
1205 if (strncmp (tmp [0], reduced, appbaselen)) {
1206 g_free (reduced);
1207 g_free (tmp [i]);
1208 tmp [i] = g_strdup ("");
1209 continue;
1212 freeme = tmp [i];
1213 tmp [i] = reduced;
1214 g_free (freeme);
1218 if (setup->private_bin_path_probe != NULL) {
1219 g_free (tmp [0]);
1220 tmp [0] = g_strdup ("");
1223 domain->setup->path_changed = FALSE;
1225 g_strfreev (pvt_split);
1227 mono_domain_assemblies_unlock (domain);
1230 #ifdef DISABLE_SHADOW_COPY
1231 gboolean
1232 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1234 return FALSE;
1237 char *
1238 mono_make_shadow_copy (const char *filename)
1240 return (char *) filename;
1242 #else
1243 static gboolean
1244 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
1246 guint16 *orig, *dest;
1247 gboolean copy_result;
1249 strcpy (src + srclen - tail_len, extension);
1251 if (IS_PORTABILITY_CASE) {
1252 gchar *file = mono_portability_find_file (src, TRUE);
1254 if (file == NULL)
1255 return TRUE;
1257 g_free (file);
1258 } else if (!g_file_test (src, G_FILE_TEST_IS_REGULAR)) {
1259 return TRUE;
1262 orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
1264 strcpy (target + targetlen - tail_len, extension);
1265 dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
1267 DeleteFile (dest);
1268 copy_result = CopyFile (orig, dest, FALSE);
1270 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1271 * overwritten when updated in their original locations. */
1272 if (copy_result)
1273 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1275 g_free (orig);
1276 g_free (dest);
1278 return copy_result;
1281 static gint32
1282 get_cstring_hash (const char *str)
1284 int len, i;
1285 const char *p;
1286 gint32 h = 0;
1288 if (!str || !str [0])
1289 return 0;
1291 len = strlen (str);
1292 p = str;
1293 for (i = 0; i < len; i++) {
1294 h = (h << 5) - h + *p;
1295 p++;
1298 return h;
1302 * Returned memory is malloc'd. Called must free it
1304 static char *
1305 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error)
1307 MonoAppDomainSetup *setup;
1308 char *cache_path, *appname;
1309 char *userdir;
1310 char *location;
1312 mono_error_init (error);
1314 setup = domain->setup;
1315 if (setup->cache_path != NULL && setup->application_name != NULL) {
1316 cache_path = mono_string_to_utf8_checked (setup->cache_path, error);
1317 if (!mono_error_ok (error))
1318 return NULL;
1319 #ifndef TARGET_WIN32
1321 gint i;
1322 for (i = strlen (cache_path) - 1; i >= 0; i--)
1323 if (cache_path [i] == '\\')
1324 cache_path [i] = '/';
1326 #endif
1328 appname = mono_string_to_utf8_checked (setup->application_name, error);
1329 if (!mono_error_ok (error)) {
1330 g_free (cache_path);
1331 return NULL;
1334 location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL);
1335 g_free (appname);
1336 g_free (cache_path);
1337 } else {
1338 userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ());
1339 location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL);
1340 g_free (userdir);
1342 return location;
1345 static char *
1346 get_shadow_assembly_location (const char *filename, MonoError *error)
1348 gint32 hash = 0, hash2 = 0;
1349 char name_hash [9];
1350 char path_hash [30];
1351 char *bname = g_path_get_basename (filename);
1352 char *dirname = g_path_get_dirname (filename);
1353 char *location, *tmploc;
1354 MonoDomain *domain = mono_domain_get ();
1356 mono_error_init (error);
1358 hash = get_cstring_hash (bname);
1359 hash2 = get_cstring_hash (dirname);
1360 g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1361 g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1362 tmploc = get_shadow_assembly_location_base (domain, error);
1363 if (!mono_error_ok (error)) {
1364 g_free (bname);
1365 g_free (dirname);
1366 return NULL;
1369 location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL);
1370 g_free (tmploc);
1371 g_free (bname);
1372 g_free (dirname);
1373 return location;
1376 static gboolean
1377 ensure_directory_exists (const char *filename)
1379 #ifdef HOST_WIN32
1380 gchar *dir_utf8 = g_path_get_dirname (filename);
1381 gunichar2 *p;
1382 gunichar2 *dir_utf16 = NULL;
1383 int retval;
1385 if (!dir_utf8 || !dir_utf8 [0])
1386 return FALSE;
1388 dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1389 g_free (dir_utf8);
1391 if (!dir_utf16)
1392 return FALSE;
1394 p = dir_utf16;
1396 /* make life easy and only use one directory seperator */
1397 while (*p != '\0')
1399 if (*p == '/')
1400 *p = '\\';
1401 p++;
1404 p = dir_utf16;
1406 /* get past C:\ )*/
1407 while (*p++ != '\\')
1411 while (1) {
1412 BOOL bRet = FALSE;
1413 p = wcschr (p, '\\');
1414 if (p)
1415 *p = '\0';
1416 retval = _wmkdir (dir_utf16);
1417 if (retval != 0 && errno != EEXIST) {
1418 g_free (dir_utf16);
1419 return FALSE;
1421 if (!p)
1422 break;
1423 *p++ = '\\';
1426 g_free (dir_utf16);
1427 return TRUE;
1428 #else
1429 char *p;
1430 gchar *dir = g_path_get_dirname (filename);
1431 int retval;
1432 struct stat sbuf;
1434 if (!dir || !dir [0]) {
1435 g_free (dir);
1436 return FALSE;
1439 if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
1440 g_free (dir);
1441 return TRUE;
1444 p = dir;
1445 while (*p == '/')
1446 p++;
1448 while (1) {
1449 p = strchr (p, '/');
1450 if (p)
1451 *p = '\0';
1452 retval = mkdir (dir, 0777);
1453 if (retval != 0 && errno != EEXIST) {
1454 g_free (dir);
1455 return FALSE;
1457 if (!p)
1458 break;
1459 *p++ = '/';
1462 g_free (dir);
1463 return TRUE;
1464 #endif
1467 static gboolean
1468 private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest)
1470 struct stat sbuf_dest;
1471 gchar *stat_src;
1472 gchar *real_src = mono_portability_find_file (src, TRUE);
1474 if (!real_src)
1475 stat_src = (gchar*)src;
1476 else
1477 stat_src = real_src;
1479 if (stat (stat_src, sbuf_src) == -1) {
1480 time_t tnow = time (NULL);
1482 if (real_src)
1483 g_free (real_src);
1485 memset (sbuf_src, 0, sizeof (*sbuf_src));
1486 sbuf_src->st_mtime = tnow;
1487 sbuf_src->st_atime = tnow;
1488 return TRUE;
1491 if (real_src)
1492 g_free (real_src);
1494 if (stat (dest, &sbuf_dest) == -1)
1495 return TRUE;
1497 if (sbuf_src->st_size == sbuf_dest.st_size &&
1498 sbuf_src->st_mtime == sbuf_dest.st_mtime)
1499 return FALSE;
1501 return TRUE;
1504 static gboolean
1505 shadow_copy_create_ini (const char *shadow, const char *filename)
1507 char *dir_name;
1508 char *ini_file;
1509 guint16 *u16_ini;
1510 gboolean result;
1511 guint32 n;
1512 HANDLE *handle;
1513 gchar *full_path;
1515 dir_name = g_path_get_dirname (shadow);
1516 ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL);
1517 g_free (dir_name);
1518 if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) {
1519 g_free (ini_file);
1520 return TRUE;
1523 u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL);
1524 g_free (ini_file);
1525 if (!u16_ini) {
1526 return FALSE;
1528 handle = CreateFile (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
1529 NULL, CREATE_NEW, FileAttributes_Normal, NULL);
1530 g_free (u16_ini);
1531 if (handle == INVALID_HANDLE_VALUE) {
1532 return FALSE;
1535 full_path = mono_path_resolve_symlinks (filename);
1536 result = WriteFile (handle, full_path, strlen (full_path), &n, NULL);
1537 g_free (full_path);
1538 CloseHandle (handle);
1539 return result;
1542 gboolean
1543 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1545 MonoError error;
1546 MonoAppDomainSetup *setup;
1547 gchar *all_dirs;
1548 gchar **dir_ptr;
1549 gchar **directories;
1550 gchar *shadow_status_string;
1551 gchar *base_dir;
1552 gboolean shadow_enabled;
1553 gboolean found = FALSE;
1555 if (domain == NULL)
1556 return FALSE;
1558 setup = domain->setup;
1559 if (setup == NULL || setup->shadow_copy_files == NULL)
1560 return FALSE;
1562 shadow_status_string = mono_string_to_utf8_checked (setup->shadow_copy_files, &error);
1563 if (!mono_error_ok (&error)) {
1564 mono_error_cleanup (&error);
1565 return FALSE;
1567 shadow_enabled = !g_ascii_strncasecmp (shadow_status_string, "true", 4);
1568 g_free (shadow_status_string);
1570 if (!shadow_enabled)
1571 return FALSE;
1573 if (setup->shadow_copy_directories == NULL)
1574 return TRUE;
1576 /* Is dir_name a shadow_copy destination already? */
1577 base_dir = get_shadow_assembly_location_base (domain, &error);
1578 if (!mono_error_ok (&error)) {
1579 mono_error_cleanup (&error);
1580 return FALSE;
1583 if (strstr (dir_name, base_dir)) {
1584 g_free (base_dir);
1585 return TRUE;
1587 g_free (base_dir);
1589 all_dirs = mono_string_to_utf8_checked (setup->shadow_copy_directories, &error);
1590 if (!mono_error_ok (&error)) {
1591 mono_error_cleanup (&error);
1592 return FALSE;
1595 directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000);
1596 dir_ptr = directories;
1597 while (*dir_ptr) {
1598 if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) {
1599 found = TRUE;
1600 break;
1602 dir_ptr++;
1604 g_strfreev (directories);
1605 g_free (all_dirs);
1606 return found;
1610 This function raises exceptions so it can cause as sorts of nasty stuff if called
1611 while holding a lock.
1612 Returns old file name if shadow copy is disabled, new shadow copy file name if successful
1613 or NULL if source file not found.
1614 FIXME bubble up the error instead of raising it here
1616 char *
1617 mono_make_shadow_copy (const char *filename)
1619 MonoError error;
1620 gchar *sibling_source, *sibling_target;
1621 gint sibling_source_len, sibling_target_len;
1622 guint16 *orig, *dest;
1623 guint32 attrs;
1624 char *shadow;
1625 gboolean copy_result;
1626 MonoException *exc;
1627 struct stat src_sbuf;
1628 struct utimbuf utbuf;
1629 char *dir_name = g_path_get_dirname (filename);
1630 MonoDomain *domain = mono_domain_get ();
1631 char *shadow_dir;
1633 set_domain_search_path (domain);
1635 if (!mono_is_shadow_copy_enabled (domain, dir_name)) {
1636 g_free (dir_name);
1637 return (char *) filename;
1640 /* Is dir_name a shadow_copy destination already? */
1641 shadow_dir = get_shadow_assembly_location_base (domain, &error);
1642 if (!mono_error_ok (&error)) {
1643 mono_error_cleanup (&error);
1644 g_free (dir_name);
1645 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (invalid characters in shadow directory name).");
1646 mono_raise_exception (exc);
1649 if (strstr (dir_name, shadow_dir)) {
1650 g_free (shadow_dir);
1651 g_free (dir_name);
1652 return (char *) filename;
1654 g_free (shadow_dir);
1655 g_free (dir_name);
1657 shadow = get_shadow_assembly_location (filename, &error);
1658 if (!mono_error_ok (&error)) {
1659 mono_error_cleanup (&error);
1660 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (invalid characters in file name).");
1661 mono_raise_exception (exc);
1664 if (ensure_directory_exists (shadow) == FALSE) {
1665 g_free (shadow);
1666 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
1667 mono_raise_exception (exc);
1670 if (!private_file_needs_copying (filename, &src_sbuf, shadow))
1671 return (char*) shadow;
1673 orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1674 dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1675 DeleteFile (dest);
1677 /* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather
1678 * let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly)
1679 * and not have it runtime error" */
1680 attrs = GetFileAttributes (orig);
1681 if (attrs == INVALID_FILE_ATTRIBUTES) {
1682 g_free (shadow);
1683 return (char *)filename;
1686 copy_result = CopyFile (orig, dest, FALSE);
1688 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1689 * overwritten when updated in their original locations. */
1690 if (copy_result)
1691 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1693 g_free (dest);
1694 g_free (orig);
1696 if (copy_result == FALSE) {
1697 g_free (shadow);
1699 /* Fix for bug #17251 - if file not found try finding assembly by other means (it is not fatal error) */
1700 if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)
1701 return NULL; /* file not found, shadow copy failed */
1703 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
1704 mono_raise_exception (exc);
1707 /* attempt to copy .mdb, .config if they exist */
1708 sibling_source = g_strconcat (filename, ".config", NULL);
1709 sibling_source_len = strlen (sibling_source);
1710 sibling_target = g_strconcat (shadow, ".config", NULL);
1711 sibling_target_len = strlen (sibling_target);
1713 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1714 if (copy_result == TRUE)
1715 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1717 g_free (sibling_source);
1718 g_free (sibling_target);
1720 if (copy_result == FALSE) {
1721 g_free (shadow);
1722 exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
1723 mono_raise_exception (exc);
1726 /* Create a .ini file containing the original assembly location */
1727 if (!shadow_copy_create_ini (shadow, filename)) {
1728 g_free (shadow);
1729 exc = mono_get_exception_execution_engine ("Failed to create shadow copy .ini file.");
1730 mono_raise_exception (exc);
1733 utbuf.actime = src_sbuf.st_atime;
1734 utbuf.modtime = src_sbuf.st_mtime;
1735 utime (shadow, &utbuf);
1737 return shadow;
1739 #endif /* DISABLE_SHADOW_COPY */
1741 MonoDomain *
1742 mono_domain_from_appdomain (MonoAppDomain *appdomain)
1744 if (appdomain == NULL)
1745 return NULL;
1747 return appdomain->data;
1750 static gboolean
1751 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1752 const gchar *path3, const gchar *path4,
1753 gboolean refonly, gboolean is_private)
1755 gchar *fullpath;
1756 gboolean found = FALSE;
1758 *assembly = NULL;
1759 fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1761 if (IS_PORTABILITY_SET) {
1762 gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE);
1763 if (new_fullpath) {
1764 g_free (fullpath);
1765 fullpath = new_fullpath;
1766 found = TRUE;
1768 } else
1769 found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
1771 if (found)
1772 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1774 g_free (fullpath);
1775 return (*assembly != NULL);
1778 static MonoAssembly *
1779 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1781 MonoAssembly *result = NULL;
1782 gchar **path;
1783 gchar *filename;
1784 const gchar *local_culture;
1785 gint len;
1786 gboolean is_private = FALSE;
1788 if (!culture || *culture == '\0') {
1789 local_culture = "";
1790 } else {
1791 local_culture = culture;
1794 filename = g_strconcat (name, ".dll", NULL);
1795 len = strlen (filename);
1797 for (path = search_path; *path; path++) {
1798 if (**path == '\0') {
1799 is_private = TRUE;
1800 continue; /* Ignore empty ApplicationBase */
1803 /* See test cases in bug #58992 and bug #57710 */
1804 /* 1st try: [culture]/[name].dll (culture may be empty) */
1805 strcpy (filename + len - 4, ".dll");
1806 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1807 break;
1809 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1810 strcpy (filename + len - 4, ".exe");
1811 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1812 break;
1814 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1815 strcpy (filename + len - 4, ".dll");
1816 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1817 break;
1819 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1820 strcpy (filename + len - 4, ".exe");
1821 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1822 break;
1825 g_free (filename);
1826 return result;
1830 * Try loading the assembly from ApplicationBase and PrivateBinPath
1831 * and then from assemblies_path if any.
1832 * LOCKING: This is called from the assembly loading code, which means the caller
1833 * might hold the loader lock. Thus, this function must not acquire the domain lock.
1835 static MonoAssembly *
1836 mono_domain_assembly_preload (MonoAssemblyName *aname,
1837 gchar **assemblies_path,
1838 gpointer user_data)
1840 MonoDomain *domain = mono_domain_get ();
1841 MonoAssembly *result = NULL;
1842 gboolean refonly = GPOINTER_TO_UINT (user_data);
1844 set_domain_search_path (domain);
1846 if (domain->search_path && domain->search_path [0] != NULL) {
1847 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1850 if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1851 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1854 return result;
1858 * Check whenever a given assembly was already loaded in the current appdomain.
1860 static MonoAssembly *
1861 mono_domain_assembly_search (MonoAssemblyName *aname,
1862 gpointer user_data)
1864 MonoDomain *domain = mono_domain_get ();
1865 GSList *tmp;
1866 MonoAssembly *ass;
1867 gboolean refonly = GPOINTER_TO_UINT (user_data);
1869 mono_domain_assemblies_lock (domain);
1870 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1871 ass = tmp->data;
1872 /* Dynamic assemblies can't match here in MS.NET */
1873 if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
1874 continue;
1876 mono_domain_assemblies_unlock (domain);
1877 return ass;
1879 mono_domain_assemblies_unlock (domain);
1881 return NULL;
1884 MonoReflectionAssembly *
1885 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
1887 MonoDomain *domain = mono_domain_get ();
1888 char *name, *filename;
1889 MonoImageOpenStatus status = MONO_IMAGE_OK;
1890 MonoAssembly *ass;
1892 if (fname == NULL) {
1893 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
1894 mono_set_pending_exception (exc);
1895 return NULL;
1898 name = filename = mono_string_to_utf8 (fname);
1900 ass = mono_assembly_open_full (filename, &status, refOnly);
1902 if (!ass) {
1903 MonoException *exc;
1905 if (status == MONO_IMAGE_IMAGE_INVALID)
1906 exc = mono_get_exception_bad_image_format2 (NULL, fname);
1907 else
1908 exc = mono_get_exception_file_not_found2 (NULL, fname);
1909 g_free (name);
1910 mono_set_pending_exception (exc);
1911 return NULL;
1914 g_free (name);
1916 return mono_assembly_get_object (domain, ass);
1919 MonoReflectionAssembly *
1920 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad,
1921 MonoArray *raw_assembly,
1922 MonoArray *raw_symbol_store, MonoObject *evidence,
1923 MonoBoolean refonly)
1925 MonoAssembly *ass;
1926 MonoReflectionAssembly *refass = NULL;
1927 MonoDomain *domain = ad->data;
1928 MonoImageOpenStatus status;
1929 guint32 raw_assembly_len = mono_array_length (raw_assembly);
1930 MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1932 if (!image) {
1933 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
1934 return NULL;
1937 if (raw_symbol_store != NULL)
1938 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
1940 ass = mono_assembly_load_from_full (image, "", &status, refonly);
1943 if (!ass) {
1944 mono_image_close (image);
1945 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
1946 return NULL;
1949 refass = mono_assembly_get_object (domain, ass);
1950 MONO_OBJECT_SETREF (refass, evidence, evidence);
1951 return refass;
1954 MonoReflectionAssembly *
1955 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1957 MonoDomain *domain = ad->data;
1958 MonoImageOpenStatus status = MONO_IMAGE_OK;
1959 MonoAssembly *ass;
1960 MonoAssemblyName aname;
1961 MonoReflectionAssembly *refass = NULL;
1962 gchar *name;
1963 gboolean parsed;
1965 g_assert (assRef != NULL);
1967 name = mono_string_to_utf8 (assRef);
1968 parsed = mono_assembly_name_parse (name, &aname);
1969 g_free (name);
1971 if (!parsed) {
1972 /* This is a parse error... */
1973 if (!refOnly)
1974 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly);
1975 return refass;
1978 ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
1979 mono_assembly_name_free (&aname);
1981 if (!ass) {
1982 /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
1983 if (!refOnly)
1984 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly);
1985 else
1986 refass = NULL;
1987 if (!refass) {
1988 return NULL;
1992 if (refass == NULL)
1993 refass = mono_assembly_get_object (domain, ass);
1995 MONO_OBJECT_SETREF (refass, evidence, evidence);
1996 return refass;
1999 void
2000 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
2002 MonoDomain * domain = mono_domain_get_by_id (domain_id);
2004 if (NULL == domain) {
2005 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
2006 mono_set_pending_exception (exc);
2007 return;
2010 if (domain == mono_get_root_domain ()) {
2011 mono_set_pending_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
2012 return;
2016 * Unloading seems to cause problems when running NUnit/NAnt, hence
2017 * this workaround.
2019 if (g_getenv ("MONO_NO_UNLOAD"))
2020 return;
2021 #ifdef __native_client__
2022 return;
2023 #endif
2025 mono_domain_unload (domain);
2028 gboolean
2029 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
2031 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2033 if (!domain)
2034 return TRUE;
2036 return mono_domain_is_unloading (domain);
2039 gint32
2040 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad,
2041 MonoReflectionAssembly *refass, MonoArray *args)
2043 MonoError error;
2044 MonoImage *image;
2045 MonoMethod *method;
2047 g_assert (refass);
2048 image = refass->assembly->image;
2049 g_assert (image);
2051 method = mono_get_method_checked (image, mono_image_get_entry_point (image), NULL, NULL, &error);
2053 if (!method)
2054 g_error ("No entry point method found in %s due to %s", image->name, mono_error_get_message (&error));
2056 if (!args)
2057 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
2059 return mono_runtime_exec_main (method, (MonoArray *)args, NULL);
2062 gint32
2063 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad)
2065 return ad->data->domain_id;
2068 MonoAppDomain *
2069 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
2071 MonoDomain *old_domain = mono_domain_get();
2073 if (!mono_domain_set (ad->data, FALSE)) {
2074 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2075 return NULL;
2078 return old_domain->domain;
2081 MonoAppDomain *
2082 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
2084 MonoDomain *current_domain = mono_domain_get ();
2085 MonoDomain *domain = mono_domain_get_by_id (domainid);
2087 if (!domain || !mono_domain_set (domain, FALSE)) {
2088 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2089 return NULL;
2092 return current_domain->domain;
2095 void
2096 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
2098 mono_thread_push_appdomain_ref (ad->data);
2101 void
2102 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
2104 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2106 if (!domain) {
2108 * Raise an exception to prevent the managed code from executing a pop
2109 * later.
2111 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2112 return;
2115 mono_thread_push_appdomain_ref (domain);
2118 void
2119 ves_icall_System_AppDomain_InternalPopDomainRef (void)
2121 mono_thread_pop_appdomain_ref ();
2124 MonoAppContext *
2125 ves_icall_System_AppDomain_InternalGetContext ()
2127 return mono_context_get ();
2130 MonoAppContext *
2131 ves_icall_System_AppDomain_InternalGetDefaultContext ()
2133 return mono_domain_get ()->default_context;
2136 MonoAppContext *
2137 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
2139 MonoAppContext *old_context = mono_context_get ();
2141 mono_context_set (mc);
2143 return old_context;
2146 MonoString *
2147 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
2149 MonoDomain* mono_root_domain = mono_get_root_domain ();
2150 mono_domain_lock (mono_root_domain);
2151 if (process_guid_set) {
2152 mono_domain_unlock (mono_root_domain);
2153 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
2155 memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
2156 process_guid_set = TRUE;
2157 mono_domain_unlock (mono_root_domain);
2158 return newguid;
2161 gboolean
2162 mono_domain_is_unloading (MonoDomain *domain)
2164 if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
2165 return TRUE;
2166 else
2167 return FALSE;
2170 static void
2171 clear_cached_vtable (MonoVTable *vtable)
2173 MonoClass *klass = vtable->klass;
2174 MonoDomain *domain = vtable->domain;
2175 MonoClassRuntimeInfo *runtime_info;
2176 void *data;
2178 runtime_info = klass->runtime_info;
2179 if (runtime_info && runtime_info->max_domain >= domain->domain_id)
2180 runtime_info->domain_vtables [domain->domain_id] = NULL;
2181 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2182 mono_gc_free_fixed (data);
2185 static G_GNUC_UNUSED void
2186 zero_static_data (MonoVTable *vtable)
2188 MonoClass *klass = vtable->klass;
2189 void *data;
2191 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2192 mono_gc_bzero_aligned (data, mono_class_data_size (klass));
2195 typedef struct unload_data {
2196 gboolean done;
2197 MonoDomain *domain;
2198 char *failure_reason;
2199 gint32 refcount;
2200 } unload_data;
2202 static void
2203 unload_data_unref (unload_data *data)
2205 gint32 count;
2206 do {
2207 mono_atomic_load_acquire (count, gint32, &data->refcount);
2208 g_assert (count >= 1 && count <= 2);
2209 if (count == 1) {
2210 g_free (data);
2211 return;
2213 } while (InterlockedCompareExchange (&data->refcount, count - 1, count) != count);
2216 static void
2217 deregister_reflection_info_roots_from_list (MonoImage *image)
2219 GSList *list = image->reflection_info_unregister_classes;
2221 while (list) {
2222 MonoClass *class = list->data;
2224 mono_class_free_ref_info (class);
2226 list = list->next;
2229 image->reflection_info_unregister_classes = NULL;
2232 static void
2233 deregister_reflection_info_roots (MonoDomain *domain)
2235 GSList *list;
2237 mono_domain_assemblies_lock (domain);
2238 for (list = domain->domain_assemblies; list; list = list->next) {
2239 MonoAssembly *assembly = list->data;
2240 MonoImage *image = assembly->image;
2241 int i;
2244 * No need to take the image lock here since dynamic images are appdomain bound and
2245 * at this point the mutator is gone. Taking the image lock here would mean
2246 * promoting it from a simple lock to a complex lock, which we better avoid if
2247 * possible.
2249 if (image_is_dynamic (image))
2250 deregister_reflection_info_roots_from_list (image);
2252 for (i = 0; i < image->module_count; ++i) {
2253 MonoImage *module = image->modules [i];
2254 if (module && image_is_dynamic (module))
2255 deregister_reflection_info_roots_from_list (module);
2258 mono_domain_assemblies_unlock (domain);
2261 static guint32 WINAPI
2262 unload_thread_main (void *arg)
2264 unload_data *data = (unload_data*)arg;
2265 MonoDomain *domain = data->domain;
2266 MonoThread *thread;
2267 int i;
2269 /* Have to attach to the runtime so shutdown can wait for this thread */
2270 /* Force it to be attached to avoid racing during shutdown. */
2271 thread = mono_thread_attach_full (mono_get_root_domain (), TRUE);
2274 * FIXME: Abort our parent thread last, so we can return a failure
2275 * indication if aborting times out.
2277 if (!mono_threads_abort_appdomain_threads (domain, -1)) {
2278 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
2279 goto failure;
2282 if (!mono_threadpool_ms_remove_domain_jobs (domain, -1)) {
2283 data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name);
2284 goto failure;
2287 /* Finalize all finalizable objects in the doomed appdomain */
2288 if (!mono_domain_finalize (domain, -1)) {
2289 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
2290 goto failure;
2293 /* Clear references to our vtables in class->runtime_info.
2294 * We also hold the loader lock because we're going to change
2295 * class->runtime_info.
2298 mono_loader_lock (); //FIXME why do we need the loader lock here?
2299 mono_domain_lock (domain);
2300 #ifdef HAVE_SGEN_GC
2302 * We need to make sure that we don't have any remsets
2303 * pointing into static data of the to-be-freed domain because
2304 * at the next collections they would be invalid. So what we
2305 * do is we first zero all static data and then do a minor
2306 * collection. Because all references in the static data will
2307 * now be null we won't do any unnecessary copies and after
2308 * the collection there won't be any more remsets.
2310 for (i = 0; i < domain->class_vtable_array->len; ++i)
2311 zero_static_data (g_ptr_array_index (domain->class_vtable_array, i));
2312 mono_gc_collect (0);
2313 #endif
2314 for (i = 0; i < domain->class_vtable_array->len; ++i)
2315 clear_cached_vtable (g_ptr_array_index (domain->class_vtable_array, i));
2316 deregister_reflection_info_roots (domain);
2318 mono_assembly_cleanup_domain_bindings (domain->domain_id);
2320 mono_domain_unlock (domain);
2321 mono_loader_unlock ();
2323 mono_threads_clear_cached_culture (domain);
2325 domain->state = MONO_APPDOMAIN_UNLOADED;
2327 /* printf ("UNLOADED %s.\n", domain->friendly_name); */
2329 /* remove from the handle table the items related to this domain */
2330 mono_gchandle_free_domain (domain);
2332 mono_domain_free (domain, FALSE);
2334 mono_gc_collect (mono_gc_max_generation ());
2336 mono_atomic_store_release (&data->done, TRUE);
2337 unload_data_unref (data);
2338 mono_thread_detach (thread);
2339 return 0;
2341 failure:
2342 mono_atomic_store_release (&data->done, TRUE);
2343 unload_data_unref (data);
2344 mono_thread_detach (thread);
2345 return 1;
2349 * mono_domain_unload:
2350 * @domain: The domain to unload
2352 * Unloads an appdomain. Follows the process outlined in the comment
2353 * for mono_domain_try_unload.
2355 void
2356 mono_domain_unload (MonoDomain *domain)
2358 MonoObject *exc = NULL;
2359 mono_domain_try_unload (domain, &exc);
2360 if (exc)
2361 mono_raise_exception ((MonoException*)exc);
2364 static guint32
2365 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
2367 guint32 result;
2369 MONO_PREPARE_BLOCKING
2370 result = WaitForSingleObjectEx (handle, timeout, alertable);
2371 MONO_FINISH_BLOCKING
2373 return result;
2377 * mono_domain_unload:
2378 * @domain: The domain to unload
2379 * @exc: Exception information
2381 * Unloads an appdomain. Follows the process outlined in:
2382 * http://blogs.gotdotnet.com/cbrumme
2384 * If doing things the 'right' way is too hard or complex, we do it the
2385 * 'simple' way, which means do everything needed to avoid crashes and
2386 * memory leaks, but not much else.
2388 * It is required to pass a valid reference to the exc argument, upon return
2389 * from this function *exc will be set to the exception thrown, if any.
2391 * If this method is not called from an icall (embedded scenario for instance),
2392 * it must not be called with any managed frames on the stack, since the unload
2393 * process could end up trying to abort the current thread.
2395 void
2396 mono_domain_try_unload (MonoDomain *domain, MonoObject **exc)
2398 HANDLE thread_handle;
2399 MonoAppDomainState prev_state;
2400 MonoMethod *method;
2401 unload_data *thread_data;
2402 MonoNativeThreadId tid;
2403 MonoDomain *caller_domain = mono_domain_get ();
2404 char *name;
2406 /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
2408 /* Atomically change our state to UNLOADING */
2409 prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
2410 MONO_APPDOMAIN_UNLOADING_START,
2411 MONO_APPDOMAIN_CREATED);
2412 if (prev_state != MONO_APPDOMAIN_CREATED) {
2413 switch (prev_state) {
2414 case MONO_APPDOMAIN_UNLOADING_START:
2415 case MONO_APPDOMAIN_UNLOADING:
2416 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded.");
2417 return;
2418 case MONO_APPDOMAIN_UNLOADED:
2419 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.");
2420 return;
2421 default:
2422 g_warning ("Invalid appdomain state %d", prev_state);
2423 g_assert_not_reached ();
2427 mono_domain_set (domain, FALSE);
2428 /* Notify OnDomainUnload listeners */
2429 method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1);
2430 g_assert (method);
2432 mono_runtime_invoke (method, domain->domain, NULL, exc);
2433 if (*exc) {
2434 /* Roll back the state change */
2435 domain->state = MONO_APPDOMAIN_CREATED;
2436 mono_domain_set (caller_domain, FALSE);
2437 return;
2439 mono_domain_set (caller_domain, FALSE);
2441 thread_data = g_new0 (unload_data, 1);
2442 thread_data->domain = domain;
2443 thread_data->failure_reason = NULL;
2444 thread_data->done = FALSE;
2445 thread_data->refcount = 2; /*Must be 2: unload thread + initiator */
2447 /*The managed callback finished successfully, now we start tearing down the appdomain*/
2448 domain->state = MONO_APPDOMAIN_UNLOADING;
2450 * First we create a separate thread for unloading, since
2451 * we might have to abort some threads, including the current one.
2453 thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)unload_thread_main, thread_data, 0, CREATE_SUSPENDED, &tid);
2454 if (thread_handle == NULL)
2455 return;
2456 name = g_strdup_printf ("Unload thread for domain %x", domain);
2457 mono_thread_info_set_name (tid, name);
2458 mono_thread_info_resume (tid);
2459 g_free (name);
2461 /* Wait for the thread */
2462 while (!thread_data->done && guarded_wait (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION) {
2463 if (mono_thread_internal_has_appdomain_ref (mono_thread_internal_current (), domain) && (mono_thread_interruption_requested ())) {
2464 /* The unload thread tries to abort us */
2465 /* The icall wrapper will execute the abort */
2466 CloseHandle (thread_handle);
2467 unload_data_unref (thread_data);
2468 return;
2471 CloseHandle (thread_handle);
2473 if (thread_data->failure_reason) {
2474 /* Roll back the state change */
2475 domain->state = MONO_APPDOMAIN_CREATED;
2477 g_warning ("%s", thread_data->failure_reason);
2479 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain (thread_data->failure_reason);
2481 g_free (thread_data->failure_reason);
2482 thread_data->failure_reason = NULL;
2485 unload_data_unref (thread_data);