Make tabs not look as bad
[mono.git] / mono / metadata / appdomain.c
1 /*
2  * appdomain.c: AppDomain functions
3  *
4  * Authors:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *      Patrik Torstensson
7  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8  *
9  * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
10  */
11 #undef ASSEMBLY_LOAD_DEBUG
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #ifdef HAVE_UTIME_H
22 #include <utime.h>
23 #else
24 #ifdef HAVE_SYS_UTIME_H
25 #include <sys/utime.h>
26 #endif
27 #endif
28
29 #include <mono/metadata/gc-internal.h>
30 #include <mono/metadata/object.h>
31 #include <mono/metadata/domain-internals.h>
32 #include "mono/metadata/metadata-internals.h"
33 #include <mono/metadata/assembly.h>
34 #include <mono/metadata/exception.h>
35 #include <mono/metadata/threads.h>
36 #include <mono/metadata/socket-io.h>
37 #include <mono/metadata/tabledefs.h>
38 #include <mono/metadata/gc-internal.h>
39 #include <mono/metadata/mono-gc.h>
40 #include <mono/metadata/marshal.h>
41 #include <mono/metadata/monitor.h>
42 #include <mono/metadata/threadpool.h>
43 #include <mono/metadata/mono-debug.h>
44 #include <mono/utils/mono-uri.h>
45 #include <mono/utils/mono-logger.h>
46 #include <mono/utils/mono-path.h>
47 #include <mono/utils/mono-stdlib.h>
48 #include <mono/utils/mono-io-portability.h>
49 #ifdef PLATFORM_WIN32
50 #include <direct.h>
51 #endif
52
53 /*
54  * This is the version number of the corlib-runtime interface. When
55  * making changes to this interface (by changing the layout
56  * of classes the runtime knows about, changing icall signature or
57  * semantics etc), increment this variable. Also increment the
58  * pair of this variable in mscorlib in:
59  *       mcs/class/mscorlib/System/Environment.cs
60  *
61  * Changes which are already detected at runtime, like the addition
62  * of icalls, do not require an increment.
63  */
64 #define MONO_CORLIB_VERSION 69
65
66 typedef struct
67 {
68         int runtime_count;
69         int assemblybinding_count;
70         MonoDomain *domain;
71 } RuntimeConfig;
72
73 CRITICAL_SECTION mono_delegate_section;
74
75 static gunichar2 process_guid [36];
76 static gboolean process_guid_set = FALSE;
77
78 static gboolean shutting_down = FALSE;
79
80 static MonoAssembly *
81 mono_domain_assembly_preload (MonoAssemblyName *aname,
82                               gchar **assemblies_path,
83                               gpointer user_data);
84
85 static MonoAssembly *
86 mono_domain_assembly_search (MonoAssemblyName *aname,
87                                                          gpointer user_data);
88
89 static MonoAssembly *
90 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
91                                                                           gpointer user_data);
92
93 static void
94 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
95
96 static void
97 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
98
99 static void
100 mono_domain_unload (MonoDomain *domain);
101
102 static MonoLoadFunc load_function = NULL;
103
104 void
105 mono_install_runtime_load (MonoLoadFunc func)
106 {
107         load_function = func;
108 }
109
110 MonoDomain*
111 mono_runtime_load (const char *filename, const char *runtime_version)
112 {
113         g_assert (load_function);
114         return load_function (filename, runtime_version);
115 }
116
117 /**
118  * mono_runtime_init:
119  * @domain: domain returned by mono_init ()
120  *
121  * Initialize the core AppDomain: this function will run also some
122  * IL initialization code, so it needs the execution engine to be fully 
123  * operational.
124  *
125  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
126  * we know the entry_assembly.
127  *
128  */
129 void
130 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
131                    MonoThreadAttachCB attach_cb)
132 {
133         MonoAppDomainSetup *setup;
134         MonoAppDomain *ad;
135         MonoClass *class;
136         MonoString *arg;
137
138         mono_portability_helpers_init ();
139         
140         mono_gc_base_init ();
141         mono_monitor_init ();
142         mono_thread_pool_init ();
143         mono_marshal_init ();
144
145         mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
146         mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
147         mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
148         mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
149         mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
150         mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
151         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
152         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
153
154         mono_thread_init (start_cb, attach_cb);
155
156         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
157         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
158
159         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
160         ad = (MonoAppDomain *) mono_object_new (domain, class);
161         ad->data = domain;
162         domain->domain = ad;
163         domain->setup = setup;
164
165         InitializeCriticalSection (&mono_delegate_section);
166
167         mono_thread_attach (domain);
168         mono_context_init (domain);
169         mono_context_set (domain->default_context);
170
171         mono_type_initialization_init ();
172
173         
174         /*
175          * Create an instance early since we can't do it when there is no memory.
176          */
177         arg = mono_string_new (domain, "Out of memory");
178         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
179         
180         /* 
181          * These two are needed because the signal handlers might be executing on
182          * an alternate stack, and Boehm GC can't handle that.
183          */
184         arg = mono_string_new (domain, "A null value was found where an object instance was required");
185         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
186         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
187         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
188         
189         /* GC init has to happen after thread init */
190         mono_gc_init ();
191
192         mono_network_init ();
193
194         /* mscorlib is loaded before we install the load hook */
195         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
196         
197         return;
198 }
199
200 static int
201 mono_get_corlib_version (void)
202 {
203         MonoClass *klass;
204         MonoClassField *field;
205         MonoObject *value;
206
207         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
208         mono_class_init (klass);
209         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
210         if (!field)
211                 return -1;
212         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
213                 return -1;
214         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
215         return *(gint32*)((gchar*)value + sizeof (MonoObject));
216 }
217
218 const char*
219 mono_check_corlib_version (void)
220 {
221         int version = mono_get_corlib_version ();
222         if (version != MONO_CORLIB_VERSION)
223                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
224         else
225                 return NULL;
226 }
227
228 void
229 mono_context_init (MonoDomain *domain)
230 {
231         MonoClass *class;
232         MonoAppContext *context;
233
234         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
235         context = (MonoAppContext *) mono_object_new (domain, class);
236         context->domain_id = domain->domain_id;
237         context->context_id = 0;
238         domain->default_context = context;
239 }
240
241 /**
242  * mono_runtime_cleanup:
243  * @domain: unused.
244  *
245  * Internal routine.
246  *
247  * This must not be called while there are still running threads executing
248  * managed code.
249  */
250 void
251 mono_runtime_cleanup (MonoDomain *domain)
252 {
253         shutting_down = TRUE;
254
255         /* This ends up calling any pending pending (for at most 2 seconds) */
256         mono_gc_cleanup ();
257
258         mono_thread_cleanup ();
259
260         mono_network_cleanup ();
261
262         mono_marshal_cleanup ();
263
264         mono_type_initialization_cleanup ();
265
266         mono_monitor_cleanup ();
267
268 #ifndef PLATFORM_WIN32
269         _wapi_cleanup ();
270 #endif
271 }
272
273 static MonoDomainFunc quit_function = NULL;
274
275 void
276 mono_install_runtime_cleanup (MonoDomainFunc func)
277 {
278         quit_function = func;
279 }
280
281 void
282 mono_runtime_quit ()
283 {
284         if (quit_function != NULL)
285                 quit_function (mono_get_root_domain (), NULL);
286 }
287
288 /** 
289  * mono_runtime_set_shutting_down:
290  *
291  * Invoked by System.Environment.Exit to flag that the runtime
292  * is shutting down.
293  */
294 void
295 mono_runtime_set_shutting_down (void)
296 {
297         shutting_down = TRUE;
298 }
299
300 /**
301  * mono_runtime_is_shutting_down:
302  *
303  * Returns whether the runtime has been flagged for shutdown.
304  *
305  * This is consumed by the P:System.Environment.HasShutdownStarted
306  * property.
307  *
308  */
309 gboolean
310 mono_runtime_is_shutting_down (void)
311 {
312         return shutting_down;
313 }
314
315 /**
316  * mono_domain_has_type_resolve:
317  * @domain: application domains being looked up
318  *
319  * Returns true if the AppDomain.TypeResolve field has been
320  * set.
321  */
322 gboolean
323 mono_domain_has_type_resolve (MonoDomain *domain)
324 {
325         static MonoClassField *field = NULL;
326         MonoObject *o;
327
328         if (field == NULL) {
329                 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
330                 g_assert (field);
331         }
332
333         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
334         return o != NULL;
335 }
336
337 /**
338  * mono_domain_try_type_resolve:
339  * @domain: application domainwhere the name where the type is going to be resolved
340  * @name: the name of the type to resolve or NULL.
341  * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
342  *
343  * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
344  * the assembly that matches name.
345  *
346  * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
347  *
348  * Returns: A MonoReflectionAssembly or NULL if not found
349  */
350 MonoReflectionAssembly *
351 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
352 {
353         MonoClass *klass;
354         void *params [1];
355         static MonoMethod *method = NULL;
356
357         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
358
359         if (method == NULL) {
360                 klass = domain->domain->mbr.obj.vtable->klass;
361                 g_assert (klass);
362
363                 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
364                 if (method == NULL) {
365                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
366                         return NULL;
367                 }
368         }
369
370         if (name)
371                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
372         else
373                 *params = tb;
374         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
375 }
376
377 /**
378  * mono_domain_owns_vtable_slot:
379  *
380  *  Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
381  */
382 gboolean
383 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
384 {
385         gboolean res;
386
387         mono_domain_lock (domain);
388         res = mono_mempool_contains_addr (domain->mp, vtable_slot);
389         mono_domain_unlock (domain);
390         return res;
391 }
392
393 /**
394  * mono_domain_set:
395  * @domain: domain
396  * @force: force setting.
397  *
398  * Set the current appdomain to @domain. If @force is set, set it even
399  * if it is being unloaded.
400  *
401  * Returns:
402  *   TRUE on success;
403  *   FALSE if the domain is unloaded
404  */
405 gboolean
406 mono_domain_set (MonoDomain *domain, gboolean force)
407 {
408         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
409                 return FALSE;
410
411         mono_domain_set_internal (domain);
412
413         return TRUE;
414 }
415
416 MonoObject *
417 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
418 {
419         MonoDomain *add;
420         MonoObject *o;
421         char *str;
422
423         MONO_ARCH_SAVE_REGS;
424
425         g_assert (ad != NULL);
426         add = ad->data;
427         g_assert (add != NULL);
428
429         if (name == NULL)
430                 mono_raise_exception (mono_get_exception_argument_null ("name"));
431
432         str = mono_string_to_utf8 (name);
433
434         mono_domain_lock (add);
435
436         if (!strcmp (str, "APPBASE"))
437                 o = (MonoObject *)add->setup->application_base;
438         else if (!strcmp (str, "APP_CONFIG_FILE"))
439                 o = (MonoObject *)add->setup->configuration_file;
440         else if (!strcmp (str, "DYNAMIC_BASE"))
441                 o = (MonoObject *)add->setup->dynamic_base;
442         else if (!strcmp (str, "APP_NAME"))
443                 o = (MonoObject *)add->setup->application_name;
444         else if (!strcmp (str, "CACHE_BASE"))
445                 o = (MonoObject *)add->setup->cache_path;
446         else if (!strcmp (str, "PRIVATE_BINPATH"))
447                 o = (MonoObject *)add->setup->private_bin_path;
448         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
449                 o = (MonoObject *)add->setup->private_bin_path_probe;
450         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
451                 o = (MonoObject *)add->setup->shadow_copy_directories;
452         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
453                 o = (MonoObject *)add->setup->shadow_copy_files;
454         else 
455                 o = mono_g_hash_table_lookup (add->env, name);
456
457         mono_domain_unlock (add);
458         g_free (str);
459
460         if (!o)
461                 return NULL;
462
463         return o;
464 }
465
466 void
467 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
468 {
469         MonoDomain *add;
470
471         MONO_ARCH_SAVE_REGS;
472
473         g_assert (ad != NULL);
474         add = ad->data;
475         g_assert (add != NULL);
476
477         if (name == NULL)
478                 mono_raise_exception (mono_get_exception_argument_null ("name"));
479
480         mono_domain_lock (add);
481
482         mono_g_hash_table_insert (add->env, name, data);
483
484         mono_domain_unlock (add);
485 }
486
487 MonoAppDomainSetup *
488 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
489 {
490         MONO_ARCH_SAVE_REGS;
491
492         g_assert (ad != NULL);
493         g_assert (ad->data != NULL);
494
495         return ad->data->setup;
496 }
497
498 MonoString *
499 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
500 {
501         MONO_ARCH_SAVE_REGS;
502
503         g_assert (ad != NULL);
504         g_assert (ad->data != NULL);
505
506         return mono_string_new (ad->data, ad->data->friendly_name);
507 }
508
509 MonoAppDomain *
510 ves_icall_System_AppDomain_getCurDomain ()
511 {
512         MonoDomain *add = mono_domain_get ();
513
514         MONO_ARCH_SAVE_REGS;
515
516         return add->domain;
517 }
518
519 MonoAppDomain *
520 ves_icall_System_AppDomain_getRootDomain ()
521 {
522         MonoDomain *root = mono_get_root_domain ();
523
524         MONO_ARCH_SAVE_REGS;
525
526         return root->domain;
527 }
528
529 static char*
530 get_attribute_value (const gchar **attribute_names, 
531                      const gchar **attribute_values, 
532                      const char *att_name)
533 {
534         int n;
535         for (n = 0; attribute_names [n] != NULL; n++) {
536                 if (strcmp (attribute_names [n], att_name) == 0)
537                         return g_strdup (attribute_values [n]);
538         }
539         return NULL;
540 }
541
542 static void
543 start_element (GMarkupParseContext *context, 
544                const gchar         *element_name,
545                const gchar        **attribute_names,
546                const gchar        **attribute_values,
547                gpointer             user_data,
548                GError             **error)
549 {
550         RuntimeConfig *runtime_config = user_data;
551         
552         if (strcmp (element_name, "runtime") == 0) {
553                 runtime_config->runtime_count++;
554                 return;
555         }
556
557         if (strcmp (element_name, "assemblyBinding") == 0) {
558                 runtime_config->assemblybinding_count++;
559                 return;
560         }
561         
562         if (runtime_config->runtime_count != 1 || runtime_config->assemblybinding_count != 1)
563                 return;
564
565         if (strcmp (element_name, "probing") != 0)
566                 return;
567
568         g_free (runtime_config->domain->private_bin_path);
569         runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
570         if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
571                 g_free (runtime_config->domain->private_bin_path);
572                 runtime_config->domain->private_bin_path = NULL;
573                 return;
574         }
575 }
576
577 static void
578 end_element (GMarkupParseContext *context,
579              const gchar         *element_name,
580              gpointer             user_data,
581              GError             **error)
582 {
583         RuntimeConfig *runtime_config = user_data;
584         if (strcmp (element_name, "runtime") == 0)
585                 runtime_config->runtime_count--;
586         else if (strcmp (element_name, "assemblyBinding") == 0)
587                 runtime_config->assemblybinding_count--;
588 }
589
590 static const GMarkupParser
591 mono_parser = {
592         start_element,
593         end_element,
594         NULL,
595         NULL,
596         NULL
597 };
598
599 static void
600 mono_set_private_bin_path_from_config (MonoDomain *domain)
601 {
602         gchar *config_file, *text;
603         gsize len;
604         struct stat sbuf;
605         GMarkupParseContext *context;
606         RuntimeConfig runtime_config;
607         
608         if (!domain || !domain->setup || !domain->setup->configuration_file)
609                 return;
610
611         config_file = mono_string_to_utf8 (domain->setup->configuration_file);
612         if (stat (config_file, &sbuf) != 0) {
613                 g_free (config_file);
614                 return;
615         }
616
617         if (!g_file_get_contents (config_file, &text, &len, NULL)) {
618                 g_free (config_file);
619                 return;
620         }
621         g_free (config_file);
622
623         runtime_config.runtime_count = 0;
624         runtime_config.assemblybinding_count = 0;
625         runtime_config.domain = domain;
626         
627         context = g_markup_parse_context_new (&mono_parser, 0, &runtime_config, NULL);
628         if (g_markup_parse_context_parse (context, text, len, NULL))
629                 g_markup_parse_context_end_parse (context, NULL);
630         g_markup_parse_context_free (context);
631         g_free (text);
632 }
633
634 MonoAppDomain *
635 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
636 {
637         MonoClass *adclass;
638         MonoAppDomain *ad;
639         MonoDomain *data;
640         
641         MONO_ARCH_SAVE_REGS;
642
643         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
644
645         /* FIXME: pin all those objects */
646         data = mono_domain_create();
647
648         ad = (MonoAppDomain *) mono_object_new (data, adclass);
649         ad->data = data;
650         data->domain = ad;
651         data->setup = setup;
652         data->friendly_name = mono_string_to_utf8 (friendly_name);
653         // FIXME: The ctor runs in the current domain
654         // FIXME: Initialize null_reference_ex and stack_overflow_ex
655         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
656
657         if (!setup->application_base) {
658                 /* Inherit from the root domain since MS.NET does this */
659                 MonoDomain *root = mono_get_root_domain ();
660                 if (root->setup->application_base)
661                         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)));
662         }
663
664         mono_set_private_bin_path_from_config (data);
665         
666         mono_context_init (data);
667
668         add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
669
670         return ad;
671 }
672
673 MonoArray *
674 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
675 {
676         MonoDomain *domain = ad->data; 
677         MonoAssembly* ass;
678         static MonoClass *System_Reflection_Assembly;
679         MonoArray *res;
680         GSList *tmp;
681         int i;
682         GPtrArray *assemblies;
683
684         MONO_ARCH_SAVE_REGS;
685
686         if (!System_Reflection_Assembly)
687                 System_Reflection_Assembly = mono_class_from_name (
688                         mono_defaults.corlib, "System.Reflection", "Assembly");
689
690         /* 
691          * Make a copy of the list of assemblies because we can't hold the assemblies
692          * lock while creating objects etc.
693          */
694         assemblies = g_ptr_array_new ();
695         /* Need to skip internal assembly builders created by remoting */
696         mono_domain_assemblies_lock (domain);
697         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
698                 ass = tmp->data;
699                 if (refonly && !ass->ref_only)
700                         continue;
701                 if (ass->corlib_internal)
702                         continue;
703                 g_ptr_array_add (assemblies, ass);
704         }
705         mono_domain_assemblies_unlock (domain);
706
707         res = mono_array_new (domain, System_Reflection_Assembly, assemblies->len);
708         for (i = 0; i < assemblies->len; ++i) {
709                 ass = g_ptr_array_index (assemblies, i);
710                 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
711         }
712
713         g_ptr_array_free (assemblies, TRUE);
714
715         return res;
716 }
717
718 MonoReflectionAssembly *
719 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refonly)
720 {
721         MonoClass *klass;
722         MonoMethod *method;
723         MonoBoolean isrefonly;
724         gpointer params [2];
725
726         g_assert (domain != NULL && fname != NULL);
727
728         klass = domain->domain->mbr.obj.vtable->klass;
729         g_assert (klass);
730         
731         method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
732         if (method == NULL) {
733                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
734                 return NULL;
735         }
736
737         isrefonly = refonly ? 1 : 0;
738         params [0] = fname;
739         params [1] = &isrefonly;
740         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
741 }
742
743 static MonoAssembly *
744 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
745                                                                           gpointer user_data)
746 {
747         gboolean refonly = GPOINTER_TO_UINT (user_data);
748         MonoReflectionAssembly *assembly;
749         MonoDomain *domain = mono_domain_get ();
750         char *aname_str;
751
752         aname_str = mono_stringify_assembly_name (aname);
753
754         /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
755         assembly = mono_try_assembly_resolve (domain, mono_string_new (domain, aname_str), refonly);
756
757         g_free (aname_str);
758
759         if (assembly)
760                 return assembly->assembly;
761         else
762                 return NULL;
763 }
764         
765 /*
766  * LOCKING: assumes assemblies_lock in the domain is already locked.
767  */
768 static void
769 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
770 {
771         gint i;
772         GSList *tmp;
773         gboolean destroy_ht = FALSE;
774
775         if (!ass->aname.name)
776                 return;
777
778         if (!ht) {
779                 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
780                 destroy_ht = TRUE;
781         }
782
783         /* FIXME: handle lazy loaded assemblies */
784         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
785                 g_hash_table_insert (ht, tmp->data, tmp->data);
786         }
787         if (!g_hash_table_lookup (ht, ass)) {
788                 mono_assembly_addref (ass);
789                 g_hash_table_insert (ht, ass, ass);
790                 domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
791                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s %p added to domain %s, ref_count=%d\n", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
792         }
793
794         if (ass->image->references) {
795                 for (i = 0; ass->image->references [i] != NULL; i++) {
796                         if (ass->image->references [i] != REFERENCE_MISSING)
797                                 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
798                                         add_assemblies_to_domain (domain, ass->image->references [i], ht);
799                                 }
800                 }
801         }
802         if (destroy_ht)
803                 g_hash_table_destroy (ht);
804 }
805
806 static void
807 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
808 {
809         static MonoClassField *assembly_load_field;
810         static MonoMethod *assembly_load_method;
811         MonoDomain *domain = mono_domain_get ();
812         MonoReflectionAssembly *ref_assembly;
813         MonoClass *klass;
814         gpointer load_value;
815         void *params [1];
816
817         if (!domain->domain)
818                 /* This can happen during startup */
819                 return;
820 #ifdef ASSEMBLY_LOAD_DEBUG
821         fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
822 #endif
823         klass = domain->domain->mbr.obj.vtable->klass;
824
825         mono_domain_assemblies_lock (domain);
826         add_assemblies_to_domain (domain, assembly, NULL);
827         mono_domain_assemblies_unlock (domain);
828
829         if (assembly_load_field == NULL) {
830                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
831                 g_assert (assembly_load_field);
832         }
833
834         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
835         if (load_value == NULL) {
836                 /* No events waiting to be triggered */
837                 return;
838         }
839
840         ref_assembly = mono_assembly_get_object (domain, assembly);
841         g_assert (ref_assembly);
842
843         if (assembly_load_method == NULL) {
844                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
845                 g_assert (assembly_load_method);
846         }
847
848         *params = ref_assembly;
849         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
850 }
851
852 /*
853  * LOCKING: Acquires the domain assemblies lock.
854  */
855 static void
856 set_domain_search_path (MonoDomain *domain)
857 {
858         MonoAppDomainSetup *setup;
859         gchar **tmp;
860         gchar *search_path = NULL;
861         gint i;
862         gint npaths = 0;
863         gchar **pvt_split = NULL;
864         GError *error = NULL;
865         gint appbaselen = -1;
866
867         /* 
868          * We use the low-level domain assemblies lock, since this is called from
869          * assembly loads hooks, which means this thread might hold the loader lock.
870          */
871         mono_domain_assemblies_lock (domain);
872
873         if ((domain->search_path != NULL) && !domain->setup->path_changed) {
874                 mono_domain_assemblies_unlock (domain);
875                 return;
876         }
877         if (!domain->setup) {
878                 mono_domain_assemblies_unlock (domain);
879                 return;
880         }
881
882         setup = domain->setup;
883         if (!setup->application_base) {
884                 mono_domain_assemblies_unlock (domain);
885                 return; /* Must set application base to get private path working */
886         }
887
888         npaths++;
889         
890         if (setup->private_bin_path)
891                 search_path = mono_string_to_utf8 (setup->private_bin_path);
892         
893         if (domain->private_bin_path) {
894                 if (search_path == NULL)
895                         search_path = domain->private_bin_path;
896                 else {
897                         gchar *tmp2 = search_path;
898                         search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
899                         g_free (tmp2);
900                 }
901         }
902         
903         if (search_path) {
904                 /*
905                  * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
906                  * directories relative to ApplicationBase separated by semicolons (see
907                  * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
908                  * The loop below copes with the fact that some Unix applications may use ':' (or
909                  * System.IO.Path.PathSeparator) as the path search separator. We replace it with
910                  * ';' for the subsequent split.
911                  *
912                  * The issue was reported in bug #81446
913                  */
914
915 #ifndef PLATFORM_WIN32
916                 gint slen;
917
918                 slen = strlen (search_path);
919                 for (i = 0; i < slen; i++)
920                         if (search_path [i] == ':')
921                                 search_path [i] = ';';
922 #endif
923                 
924                 pvt_split = g_strsplit (search_path, ";", 1000);
925                 g_free (search_path);
926                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
927         }
928
929         if (!npaths) {
930                 if (pvt_split)
931                         g_strfreev (pvt_split);
932                 /*
933                  * Don't do this because the first time is called, the domain
934                  * setup is not finished.
935                  *
936                  * domain->search_path = g_malloc (sizeof (char *));
937                  * domain->search_path [0] = NULL;
938                 */
939                 mono_domain_assemblies_unlock (domain);
940                 return;
941         }
942
943         if (domain->search_path)
944                 g_strfreev (domain->search_path);
945
946         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
947         tmp [npaths] = NULL;
948
949         *tmp = mono_string_to_utf8 (setup->application_base);
950
951         /* FIXME: is this needed? */
952         if (strncmp (*tmp, "file://", 7) == 0) {
953                 gchar *file = *tmp;
954                 gchar *uri = *tmp;
955                 gchar *tmpuri;
956
957                 if (uri [7] != '/')
958                         uri = g_strdup_printf ("file:///%s", uri + 7);
959
960                 tmpuri = uri;
961                 uri = mono_escape_uri_string (tmpuri);
962                 *tmp = g_filename_from_uri (uri, NULL, &error);
963                 g_free (uri);
964
965                 if (tmpuri != file)
966                         g_free (tmpuri);
967
968                 if (error != NULL) {
969                         g_warning ("%s\n", error->message);
970                         g_error_free (error);
971                         *tmp = file;
972                 } else {
973                         g_free (file);
974                 }
975         }
976
977         for (i = 1; pvt_split && i < npaths; i++) {
978                 if (g_path_is_absolute (pvt_split [i - 1])) {
979                         tmp [i] = g_strdup (pvt_split [i - 1]);
980                 } else {
981                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
982                 }
983
984                 if (strchr (tmp [i], '.')) {
985                         gchar *reduced;
986                         gchar *freeme;
987
988                         reduced = mono_path_canonicalize (tmp [i]);
989                         if (appbaselen == -1)
990                                 appbaselen = strlen (tmp [0]);
991
992                         if (strncmp (tmp [0], reduced, appbaselen)) {
993                                 g_free (reduced);
994                                 g_free (tmp [i]);
995                                 tmp [i] = g_strdup ("");
996                                 continue;
997                         }
998
999                         freeme = tmp [i];
1000                         tmp [i] = reduced;
1001                         g_free (freeme);
1002                 }
1003         }
1004         
1005         if (setup->private_bin_path_probe != NULL) {
1006                 g_free (tmp [0]);
1007                 tmp [0] = g_strdup ("");
1008         }
1009                 
1010         domain->setup->path_changed = FALSE;
1011
1012         g_strfreev (pvt_split);
1013
1014         mono_domain_assemblies_unlock (domain);
1015 }
1016
1017 static gboolean
1018 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
1019 {
1020         guint16 *orig, *dest;
1021         gboolean copy_result;
1022         
1023         strcpy (src + srclen - tail_len, extension);
1024         if (!g_file_test (src, G_FILE_TEST_IS_REGULAR))
1025                 return TRUE;
1026         orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
1027
1028         strcpy (target + targetlen - tail_len, extension);
1029         dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
1030         
1031         DeleteFile (dest);
1032         copy_result = CopyFile (orig, dest, FALSE);
1033         g_free (orig);
1034         g_free (dest);
1035         
1036         return copy_result;
1037 }
1038
1039 static gint32 
1040 get_cstring_hash (const char *str)
1041 {
1042         int len, i;
1043         const char *p;
1044         gint32 h = 0;
1045         
1046         if (!str || !str [0])
1047                 return 0;
1048                 
1049         len = strlen (str);
1050         p = str;
1051         for (i = 0; i < len; i++) {
1052                 h = (h << 5) - h + *p;
1053                 p++;
1054         }
1055         
1056         return h;
1057 }
1058
1059 static char *
1060 get_shadow_assembly_location (const char *filename)
1061 {
1062         gint32 hash = 0, hash2 = 0;
1063         char name_hash [9];
1064         char path_hash [30];
1065         char *bname = g_path_get_basename (filename);
1066         char *dirname = g_path_get_dirname (filename);
1067         char *location, *dyn_base;
1068         MonoDomain *domain = mono_domain_get ();
1069         
1070         hash = get_cstring_hash (bname);
1071         hash2 = get_cstring_hash (dirname);
1072         g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1073         g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1074         dyn_base = mono_string_to_utf8 (domain->setup->dynamic_base);
1075         location = g_build_filename (dyn_base, "assembly", "shadow", name_hash, path_hash, bname, NULL);
1076         g_free (dyn_base);
1077         g_free (bname);
1078         g_free (dirname);
1079         return location;
1080 }
1081
1082 static gboolean
1083 ensure_directory_exists (const char *filename)
1084 {
1085 #ifdef PLATFORM_WIN32
1086         gchar *dir_utf8 = g_path_get_dirname (filename);
1087         gunichar2 *p;
1088         gunichar2 *dir_utf16 = NULL;
1089         int retval;
1090         
1091         if (!dir_utf8 || !dir_utf8 [0])
1092                 return FALSE;
1093
1094         dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1095         g_free (dir_utf8);
1096
1097         if (!dir_utf16)
1098                 return FALSE;
1099
1100         p = dir_utf16;
1101
1102         /* make life easy and only use one directory seperator */
1103         while (*p != '\0')
1104         {
1105                 if (*p == '/')
1106                         *p = '\\';
1107                 p++;
1108         }
1109
1110         p = dir_utf16;
1111
1112         /* get past C:\ )*/
1113         while (*p++ != '\\')    
1114         {
1115         }
1116
1117         while (1) {
1118                 BOOL bRet = FALSE;
1119                 p = wcschr (p, '\\');
1120                 if (p)
1121                         *p = '\0';
1122                 retval = _wmkdir (dir_utf16);
1123                 if (retval != 0 && errno != EEXIST) {
1124                         g_free (dir_utf16);
1125                         return FALSE;
1126                 }
1127                 if (!p)
1128                         break;
1129                 *p++ = '\\';
1130         }
1131         
1132         g_free (dir_utf16);
1133         return TRUE;
1134 #else
1135         char *p;
1136         gchar *dir = g_path_get_dirname (filename);
1137         int retval;
1138         struct stat sbuf;
1139         
1140         if (!dir || !dir [0]) {
1141                 g_free (dir);
1142                 return FALSE;
1143         }
1144         
1145         if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
1146                 g_free (dir);
1147                 return TRUE;
1148         }
1149         
1150         p = dir;
1151         while (*p == '/')
1152                 p++;
1153
1154         while (1) {
1155                 p = strchr (p, '/');
1156                 if (p)
1157                         *p = '\0';
1158                 retval = mkdir (dir, 0777);
1159                 if (retval != 0 && errno != EEXIST) {
1160                         g_free (dir);
1161                         return FALSE;
1162                 }
1163                 if (!p)
1164                         break;
1165                 *p++ = '/';
1166         }
1167         
1168         g_free (dir);
1169         return TRUE;
1170 #endif
1171 }
1172
1173 static gboolean
1174 private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest)
1175 {
1176         struct stat sbuf_dest;
1177         
1178         if (stat (src, sbuf_src) == -1 || stat (dest, &sbuf_dest) == -1)
1179                 return TRUE;
1180
1181         if (sbuf_src->st_mode == sbuf_dest.st_mode &&
1182             sbuf_src->st_size == sbuf_dest.st_size &&
1183             sbuf_src->st_mtime == sbuf_dest.st_mtime)
1184                 return FALSE;
1185
1186         return TRUE;
1187 }
1188
1189 char *
1190 mono_make_shadow_copy (const char *filename)
1191 {
1192         gchar *sibling_source, *sibling_target;
1193         gint sibling_source_len, sibling_target_len;
1194         guint16 *orig, *dest;
1195         char *shadow;
1196         gboolean copy_result;
1197         gboolean is_private = FALSE;
1198         gboolean do_copy = FALSE;
1199         MonoException *exc;
1200         gchar **path;
1201         struct stat src_sbuf;
1202         struct utimbuf utbuf;
1203         char *dir_name = g_path_get_dirname (filename);
1204         MonoDomain *domain = mono_domain_get ();
1205         set_domain_search_path (domain);
1206
1207         if (!domain->search_path) {
1208                 g_free (dir_name);
1209                 return (char*) filename;
1210         }
1211         
1212         for (path = domain->search_path; *path; path++) {
1213                 if (**path == '\0') {
1214                         is_private = TRUE;
1215                         continue;
1216                 }
1217                 
1218                 if (!is_private)
1219                         continue;
1220
1221                 if (strstr (dir_name, *path) == dir_name) {
1222                         do_copy = TRUE;
1223                         break;
1224                 }
1225         }
1226         g_free (dir_name);
1227
1228         if (!do_copy)
1229                 return (char*) filename;
1230         
1231         shadow = get_shadow_assembly_location (filename);
1232         if (ensure_directory_exists (shadow) == FALSE) {
1233                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
1234                 mono_raise_exception (exc);
1235         }       
1236
1237         if (!private_file_needs_copying (filename, &src_sbuf, shadow))
1238                 return (char*) shadow;
1239
1240         orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1241         dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1242         DeleteFile (dest);
1243         copy_result = CopyFile (orig, dest, FALSE);
1244         g_free (dest);
1245         g_free (orig);
1246
1247         if (copy_result == FALSE) {
1248                 g_free (shadow);
1249                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
1250                 mono_raise_exception (exc);
1251         }
1252
1253         /* attempt to copy .mdb, .config if they exist */
1254         sibling_source = g_strconcat (filename, ".config", NULL);
1255         sibling_source_len = strlen (sibling_source);
1256         sibling_target = g_strconcat (shadow, ".config", NULL);
1257         sibling_target_len = strlen (sibling_target);
1258         
1259         copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1260         if (copy_result == TRUE)
1261                 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1262         
1263         g_free (sibling_source);
1264         g_free (sibling_target);
1265         
1266         if (copy_result == FALSE)  {
1267                 g_free (shadow);
1268                 exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
1269                 mono_raise_exception (exc);
1270         }
1271
1272         utbuf.actime = src_sbuf.st_atime;
1273         utbuf.modtime = src_sbuf.st_mtime;
1274         utime (shadow, &utbuf);
1275         
1276         return shadow;
1277 }
1278
1279
1280 MonoDomain *
1281 mono_domain_from_appdomain (MonoAppDomain *appdomain)
1282 {
1283         if (appdomain == NULL)
1284                 return NULL;
1285         
1286         return appdomain->data;
1287 }
1288
1289 static gboolean
1290 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1291                                         const gchar *path3, const gchar *path4,
1292                                         gboolean refonly, gboolean is_private)
1293 {
1294         gchar *fullpath;
1295         gboolean found = FALSE;
1296         
1297         *assembly = NULL;
1298         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1299
1300         if (IS_PORTABILITY_SET) {
1301                 gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE);
1302                 if (new_fullpath) {
1303                         g_free (fullpath);
1304                         fullpath = new_fullpath;
1305                         found = TRUE;
1306                 }
1307         } else
1308                 found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
1309         
1310         if (found)
1311                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1312
1313         g_free (fullpath);
1314         return (*assembly != NULL);
1315 }
1316
1317 static MonoAssembly *
1318 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1319 {
1320         MonoAssembly *result = NULL;
1321         gchar **path;
1322         gchar *filename;
1323         const gchar *local_culture;
1324         gint len;
1325         gboolean is_private = FALSE;
1326
1327         if (!culture || *culture == '\0') {
1328                 local_culture = "";
1329         } else {
1330                 local_culture = culture;
1331         }
1332
1333         filename =  g_strconcat (name, ".dll", NULL);
1334         len = strlen (filename);
1335
1336         for (path = search_path; *path; path++) {
1337                 if (**path == '\0') {
1338                         is_private = TRUE;
1339                         continue; /* Ignore empty ApplicationBase */
1340                 }
1341
1342                 /* See test cases in bug #58992 and bug #57710 */
1343                 /* 1st try: [culture]/[name].dll (culture may be empty) */
1344                 strcpy (filename + len - 4, ".dll");
1345                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1346                         break;
1347
1348                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1349                 strcpy (filename + len - 4, ".exe");
1350                 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1351                         break;
1352
1353                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1354                 strcpy (filename + len - 4, ".dll");
1355                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1356                         break;
1357
1358                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1359                 strcpy (filename + len - 4, ".exe");
1360                 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1361                         break;
1362         }
1363
1364         g_free (filename);
1365         return result;
1366 }
1367
1368 /*
1369  * Try loading the assembly from ApplicationBase and PrivateBinPath 
1370  * and then from assemblies_path if any.
1371  * LOCKING: This is called from the assembly loading code, which means the caller
1372  * might hold the loader lock. Thus, this function must not acquire the domain lock.
1373  */
1374 static MonoAssembly *
1375 mono_domain_assembly_preload (MonoAssemblyName *aname,
1376                               gchar **assemblies_path,
1377                               gpointer user_data)
1378 {
1379         MonoDomain *domain = mono_domain_get ();
1380         MonoAssembly *result = NULL;
1381         gboolean refonly = GPOINTER_TO_UINT (user_data);
1382
1383         set_domain_search_path (domain);
1384
1385         if (domain->search_path && domain->search_path [0] != NULL) {
1386                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1387         }
1388
1389         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1390                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1391         }
1392
1393         return result;
1394 }
1395
1396 /*
1397  * Check whenever a given assembly was already loaded in the current appdomain.
1398  */
1399 static MonoAssembly *
1400 mono_domain_assembly_search (MonoAssemblyName *aname,
1401                                                          gpointer user_data)
1402 {
1403         MonoDomain *domain = mono_domain_get ();
1404         GSList *tmp;
1405         MonoAssembly *ass;
1406         gboolean refonly = GPOINTER_TO_UINT (user_data);
1407
1408         mono_domain_assemblies_lock (domain);
1409         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1410                 ass = tmp->data;
1411                 /* Dynamic assemblies can't match here in MS.NET */
1412                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
1413                         continue;
1414
1415                 mono_domain_assemblies_unlock (domain);
1416                 return ass;
1417         }
1418         mono_domain_assemblies_unlock (domain);
1419
1420         return NULL;
1421 }
1422
1423 MonoReflectionAssembly *
1424 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
1425 {
1426         MonoDomain *domain = mono_domain_get ();
1427         char *name, *filename;
1428         MonoImageOpenStatus status = MONO_IMAGE_OK;
1429         MonoAssembly *ass;
1430
1431         MONO_ARCH_SAVE_REGS;
1432
1433         if (fname == NULL) {
1434                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
1435                 mono_raise_exception (exc);
1436         }
1437                 
1438         name = filename = mono_string_to_utf8 (fname);
1439         
1440         ass = mono_assembly_open_full (filename, &status, refOnly);
1441         
1442         if (!ass){
1443                 MonoException *exc;
1444
1445                 if (status == MONO_IMAGE_IMAGE_INVALID)
1446                         exc = mono_get_exception_bad_image_format2 (NULL, fname);
1447                 else
1448                         exc = mono_get_exception_file_not_found2 (NULL, fname);
1449                 g_free (name);
1450                 mono_raise_exception (exc);
1451         }
1452
1453         g_free (name);
1454
1455         return mono_assembly_get_object (domain, ass);
1456 }
1457
1458 MonoReflectionAssembly *
1459 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
1460                                             MonoArray *raw_assembly,
1461                                             MonoArray *raw_symbol_store, MonoObject *evidence,
1462                                             MonoBoolean refonly)
1463 {
1464         MonoAssembly *ass;
1465         MonoReflectionAssembly *refass = NULL;
1466         MonoDomain *domain = ad->data;
1467         MonoImageOpenStatus status;
1468         guint32 raw_assembly_len = mono_array_length (raw_assembly);
1469         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1470
1471         if (!image) {
1472                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1473                 return NULL;
1474         }
1475
1476         if (raw_symbol_store != NULL)
1477                 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
1478
1479         ass = mono_assembly_load_from_full (image, "", &status, refonly);
1480
1481
1482         if (!ass) {
1483                 mono_image_close (image);
1484                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1485                 return NULL; 
1486         }
1487
1488         refass = mono_assembly_get_object (domain, ass);
1489         MONO_OBJECT_SETREF (refass, evidence, evidence);
1490         return refass;
1491 }
1492
1493 MonoReflectionAssembly *
1494 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1495 {
1496         MonoDomain *domain = ad->data; 
1497         MonoImageOpenStatus status = MONO_IMAGE_OK;
1498         MonoAssembly *ass;
1499         MonoAssemblyName aname;
1500         MonoReflectionAssembly *refass = NULL;
1501         gchar *name;
1502         gboolean parsed;
1503
1504         MONO_ARCH_SAVE_REGS;
1505
1506         g_assert (assRef != NULL);
1507
1508         name = mono_string_to_utf8 (assRef);
1509         parsed = mono_assembly_name_parse (name, &aname);
1510         g_free (name);
1511
1512         if (!parsed) {
1513                 MonoException *exc;
1514
1515                 /* This is a parse error... */
1516                 exc = mono_get_exception_file_not_found2 (NULL, assRef);
1517                 mono_raise_exception (exc);
1518         }
1519
1520         ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
1521         mono_assembly_name_free (&aname);
1522
1523         if (!ass) {
1524                 /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
1525                 if (!refOnly)
1526                         refass = mono_try_assembly_resolve (domain, assRef, refOnly);
1527                 else
1528                         refass = NULL;
1529                 if (!refass) {
1530                         /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1531                         MonoException *exc = mono_get_exception_file_not_found2 (NULL, assRef);
1532                         mono_raise_exception (exc);
1533                 }
1534         }
1535
1536         if (refass == NULL)
1537                 refass = mono_assembly_get_object (domain, ass);
1538
1539         MONO_OBJECT_SETREF (refass, evidence, evidence);
1540         return refass;
1541 }
1542
1543 void
1544 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
1545 {
1546         MonoDomain * domain = mono_domain_get_by_id (domain_id);
1547
1548         MONO_ARCH_SAVE_REGS;
1549
1550         if (NULL == domain) {
1551                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1552                 mono_raise_exception (exc);
1553         }
1554         
1555         if (domain == mono_get_root_domain ()) {
1556                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1557                 return;
1558         }
1559
1560         /* 
1561          * Unloading seems to cause problems when running NUnit/NAnt, hence
1562          * this workaround.
1563          */
1564         if (g_getenv ("MONO_NO_UNLOAD"))
1565                 return;
1566
1567         mono_domain_unload (domain);
1568 }
1569
1570 gboolean
1571 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1572 {
1573         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1574
1575         if (!domain)
1576                 return TRUE;
1577
1578         return mono_domain_is_unloading (domain);
1579 }
1580
1581 gint32
1582 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, 
1583                                                                                         MonoReflectionAssembly *refass, MonoArray *args)
1584 {
1585         MonoImage *image;
1586         MonoMethod *method;
1587
1588         MONO_ARCH_SAVE_REGS;
1589
1590         g_assert (refass);
1591         image = refass->assembly->image;
1592         g_assert (image);
1593
1594         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1595
1596         if (!method)
1597                 g_error ("No entry point method found in %s", image->name);
1598
1599         if (!args)
1600                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1601
1602         return mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1603 }
1604
1605 gint32 
1606 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1607 {
1608         MONO_ARCH_SAVE_REGS;
1609
1610         return ad->data->domain_id;
1611 }
1612
1613 MonoAppDomain * 
1614 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1615 {
1616         MonoDomain *old_domain = mono_domain_get();
1617
1618         MONO_ARCH_SAVE_REGS;
1619
1620         if (!mono_domain_set (ad->data, FALSE))
1621                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1622
1623         return old_domain->domain;
1624 }
1625
1626 MonoAppDomain * 
1627 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1628 {
1629         MonoDomain *current_domain = mono_domain_get ();
1630         MonoDomain *domain = mono_domain_get_by_id (domainid);
1631
1632         MONO_ARCH_SAVE_REGS;
1633
1634         if (!domain || !mono_domain_set (domain, FALSE))        
1635                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1636
1637         return current_domain->domain;
1638 }
1639
1640 void
1641 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1642 {
1643         MONO_ARCH_SAVE_REGS;
1644
1645         mono_thread_push_appdomain_ref (ad->data);
1646 }
1647
1648 void
1649 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1650 {
1651         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1652
1653         MONO_ARCH_SAVE_REGS;
1654
1655         if (!domain)
1656                 /* 
1657                  * Raise an exception to prevent the managed code from executing a pop
1658                  * later.
1659                  */
1660                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1661
1662         mono_thread_push_appdomain_ref (domain);
1663 }
1664
1665 void
1666 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1667 {
1668         MONO_ARCH_SAVE_REGS;
1669
1670         mono_thread_pop_appdomain_ref ();
1671 }
1672
1673 MonoAppContext * 
1674 ves_icall_System_AppDomain_InternalGetContext ()
1675 {
1676         MONO_ARCH_SAVE_REGS;
1677
1678         return mono_context_get ();
1679 }
1680
1681 MonoAppContext * 
1682 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1683 {
1684         MONO_ARCH_SAVE_REGS;
1685
1686         return mono_domain_get ()->default_context;
1687 }
1688
1689 MonoAppContext * 
1690 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1691 {
1692         MonoAppContext *old_context = mono_context_get ();
1693
1694         MONO_ARCH_SAVE_REGS;
1695
1696         mono_context_set (mc);
1697
1698         return old_context;
1699 }
1700
1701 MonoString *
1702 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1703 {
1704         MonoDomain* mono_root_domain = mono_get_root_domain ();
1705         mono_domain_lock (mono_root_domain);
1706         if (process_guid_set) {
1707                 mono_domain_unlock (mono_root_domain);
1708                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1709         }
1710         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1711         process_guid_set = TRUE;
1712         mono_domain_unlock (mono_root_domain);
1713         return newguid;
1714 }
1715
1716 gboolean
1717 mono_domain_is_unloading (MonoDomain *domain)
1718 {
1719         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1720                 return TRUE;
1721         else
1722                 return FALSE;
1723 }
1724
1725 static void
1726 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1727 {
1728         MonoClass *klass = (MonoClass*)key;
1729         MonoDomain *domain = (MonoDomain*)user_data;
1730         MonoClassRuntimeInfo *runtime_info;
1731
1732         runtime_info = klass->runtime_info;
1733         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1734                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1735 }
1736
1737 typedef struct unload_data {
1738         MonoDomain *domain;
1739         char *failure_reason;
1740 } unload_data;
1741
1742 static guint32 WINAPI
1743 unload_thread_main (void *arg)
1744 {
1745         unload_data *data = (unload_data*)arg;
1746         MonoDomain *domain = data->domain;
1747
1748         /* Have to attach to the runtime so shutdown can wait for this thread */
1749         mono_thread_attach (mono_get_root_domain ());
1750
1751         /* 
1752          * FIXME: Abort our parent thread last, so we can return a failure 
1753          * indication if aborting times out.
1754          */
1755         if (!mono_threads_abort_appdomain_threads (domain, -1)) {
1756                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1757                 return 1;
1758         }
1759
1760         /* Finalize all finalizable objects in the doomed appdomain */
1761         if (!mono_domain_finalize (domain, -1)) {
1762                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1763                 return 1;
1764         }
1765
1766         /* Clear references to our vtables in class->runtime_info.
1767          * We also hold the loader lock because we're going to change
1768          * class->runtime_info.
1769          */
1770         mono_domain_lock (domain);
1771         mono_loader_lock ();
1772         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1773         mono_loader_unlock ();
1774         mono_domain_unlock (domain);
1775
1776         mono_threads_clear_cached_culture (domain);
1777
1778         domain->state = MONO_APPDOMAIN_UNLOADED;
1779
1780         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1781
1782         /* remove from the handle table the items related to this domain */
1783         mono_gchandle_free_domain (domain);
1784
1785         mono_domain_free (domain, FALSE);
1786
1787         mono_gc_collect (mono_gc_max_generation ());
1788
1789         return 0;
1790 }
1791
1792 /*
1793  * mono_domain_unload:
1794  * @domain: The domain to unload
1795  *
1796  *  Unloads an appdomain. Follows the process outlined in:
1797  *  http://blogs.gotdotnet.com/cbrumme
1798  *
1799  *  If doing things the 'right' way is too hard or complex, we do it the 
1800  *  'simple' way, which means do everything needed to avoid crashes and
1801  *  memory leaks, but not much else.
1802  */
1803 static void
1804 mono_domain_unload (MonoDomain *domain)
1805 {
1806         HANDLE thread_handle;
1807         gsize tid;
1808         guint32 res;
1809         MonoAppDomainState prev_state;
1810         MonoMethod *method;
1811         MonoObject *exc;
1812         unload_data thread_data;
1813         MonoDomain *caller_domain = mono_domain_get ();
1814
1815         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1816
1817         /* Atomically change our state to UNLOADING */
1818         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1819                                                                                          MONO_APPDOMAIN_UNLOADING,
1820                                                                                          MONO_APPDOMAIN_CREATED);
1821         if (prev_state != MONO_APPDOMAIN_CREATED) {
1822                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1823                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1824                 else
1825                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1826                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1827                 else
1828                         g_assert_not_reached ();
1829         }
1830
1831         mono_domain_set (domain, FALSE);
1832         /* Notify OnDomainUnload listeners */
1833         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1834         g_assert (method);
1835
1836         exc = NULL;
1837         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1838         if (exc) {
1839                 /* Roll back the state change */
1840                 domain->state = MONO_APPDOMAIN_CREATED;
1841                 mono_domain_set (caller_domain, FALSE);
1842                 mono_raise_exception ((MonoException*)exc);
1843         }
1844
1845         thread_data.domain = domain;
1846         thread_data.failure_reason = NULL;
1847
1848         /* 
1849          * First we create a separate thread for unloading, since
1850          * we might have to abort some threads, including the current one.
1851          */
1852         /*
1853          * If we create a non-suspended thread, the runtime will hang.
1854          * See:
1855          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1856          */ 
1857 #if 0
1858         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1859 #else
1860         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1861         if (thread_handle == NULL) {
1862                 return;
1863         }
1864         ResumeThread (thread_handle);
1865 #endif
1866
1867         /* Wait for the thread */       
1868         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1869                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1870                         /* The unload thread tries to abort us */
1871                         /* The icall wrapper will execute the abort */
1872                         CloseHandle (thread_handle);
1873                         return;
1874         }
1875         CloseHandle (thread_handle);
1876
1877         mono_domain_set (caller_domain, FALSE);
1878
1879         if (thread_data.failure_reason) {
1880                 MonoException *ex;
1881
1882                 /* Roll back the state change */
1883                 domain->state = MONO_APPDOMAIN_CREATED;
1884
1885                 g_warning (thread_data.failure_reason);
1886
1887                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1888
1889                 g_free (thread_data.failure_reason);
1890                 thread_data.failure_reason = NULL;
1891
1892                 mono_raise_exception (ex);
1893         }
1894 }