2005-06-29 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mono / metadata / assembly.c
1 /*
2  * assembly.c: Routines for loading assemblies.
3  * 
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.  http://www.ximian.com
8  *
9  */
10 #include <config.h>
11 #include <stdio.h>
12 #include <glib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include "assembly.h"
17 #include "image.h"
18 #include "cil-coff.h"
19 #include "rawbuffer.h"
20 #include <mono/metadata/loader.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/io-layer/io-layer.h>
25 #include <mono/utils/mono-uri.h>
26 #include <mono/metadata/mono-config.h>
27 #include <mono/utils/mono-digest.h>
28 #include <mono/utils/mono-logger.h>
29 #ifdef PLATFORM_WIN32
30 #include <mono/os/util.h>
31 #ifdef _MSC_VER
32         /* not used on Windows - see mono_set_rootdir () */
33         #define MONO_ASSEMBLIES         NULL
34 #endif
35 #endif
36
37 /* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
38 typedef struct  {
39         const char* assembly_name;
40         guint8 version_set_index;
41 } AssemblyVersionMap;
42
43 /* the default search path is just MONO_ASSEMBLIES */
44 static const char*
45 default_path [] = {
46         MONO_ASSEMBLIES,
47         NULL
48 };
49
50 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
51 static char **assemblies_path = NULL;
52
53 /* Contains the list of directories that point to auxiliary GACs */
54 static char **extra_gac_paths = NULL;
55
56 /* The list of system assemblies what will be remapped to the running
57  * runtime version. WARNING: this list must be sorted.
58  */
59 static const AssemblyVersionMap framework_assemblies [] = {
60         {"Accessibility", 0},
61         {"Commons.Xml.Relaxng", 0},
62         {"I18N", 0},
63         {"I18N.CJK", 0},
64         {"I18N.MidEast", 0},
65         {"I18N.Other", 0},
66         {"I18N.Rare", 0},
67         {"I18N.West", 0},
68         {"Microsoft.VisualBasic", 1},
69         {"Microsoft.VisualC", 1},
70         {"Mono.Cairo", 0},
71         {"Mono.CompilerServices.SymbolWriter", 0},
72         {"Mono.Data", 0},
73         {"Mono.Data.SqliteClient", 0},
74         {"Mono.Data.SybaseClient", 0},
75         {"Mono.Data.Tds", 0},
76         {"Mono.Data.TdsClient", 0},
77         {"Mono.GetOptions", 0},
78         {"Mono.Http", 0},
79         {"Mono.Posix", 0},
80         {"Mono.Security", 0},
81         {"Mono.Security.Win32", 0},
82         {"Mono.Xml.Ext", 0},
83         {"Novell.Directory.Ldap", 0},
84         {"Npgsql", 0},
85         {"PEAPI", 0},
86         {"System", 0},
87         {"System.Configuration.Install", 0},
88         {"System.Data", 0},
89         {"System.Data.OracleClient", 0},
90         {"System.Data.SqlXml", 0},
91         {"System.Design", 0},
92         {"System.DirectoryServices", 0},
93         {"System.Drawing", 0},
94         {"System.Drawing.Design", 0},
95         {"System.EnterpriseServices", 0},
96         {"System.Management", 0},
97         {"System.Messaging", 0},
98         {"System.Runtime.Remoting", 0},
99         {"System.Runtime.Serialization.Formatters.Soap", 0},
100         {"System.Security", 0},
101         {"System.ServiceProcess", 0},
102         {"System.Web", 0},
103         {"System.Web.Mobile", 0},
104         {"System.Web.Services", 0},
105         {"System.Windows.Forms", 0},
106         {"System.Xml", 0},
107         {"mscorlib", 0}
108 };
109
110 /*
111  * keeps track of loaded assemblies
112  */
113 static GList *loaded_assemblies = NULL;
114 static MonoAssembly *corlib;
115
116 /* This protects loaded_assemblies and image->references */
117 static CRITICAL_SECTION assemblies_mutex;
118
119 /* A hastable of thread->assembly list mappings */
120 static GHashTable *assemblies_loading;
121
122 /* A hashtable of reflection only load thread->assemblies mappings */
123 static GHashTable *assemblies_refonly_loading;
124
125 /* If defined, points to the bundled assembly information */
126 const MonoBundledAssembly **bundles;
127
128 /* Reflection only private hook functions */
129 static MonoAssembly* mono_assembly_refonly_invoke_search_hook (MonoAssemblyName *aname);
130
131 static gchar*
132 encode_public_tok (const guchar *token, gint32 len)
133 {
134         const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
135         gchar *res;
136         int i;
137
138         res = g_malloc (len * 2 + 1);
139         for (i = 0; i < len; i++) {
140                 res [i * 2] = allowed [token [i] >> 4];
141                 res [i * 2 + 1] = allowed [token [i] & 0xF];
142         }
143         res [len * 2] = 0;
144         return res;
145 }
146
147 static void
148 check_path_env (void)
149 {
150         const char *path;
151         char **splitted;
152         
153         path = g_getenv ("MONO_PATH");
154         if (!path)
155                 return;
156
157         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
158         if (assemblies_path)
159                 g_strfreev (assemblies_path);
160         assemblies_path = splitted;
161         if (g_getenv ("MONO_DEBUG") == NULL)
162                 return;
163
164         while (*splitted) {
165                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
166                         g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
167
168                 splitted++;
169         }
170 }
171
172 static void
173 check_extra_gac_path_env (void) {
174         const char *path;
175         char **splitted;
176         
177         path = g_getenv ("MONO_GAC_PREFIX");
178         if (!path)
179                 return;
180
181         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
182         if (extra_gac_paths)
183                 g_strfreev (extra_gac_paths);
184         extra_gac_paths = splitted;
185         if (g_getenv ("MONO_DEBUG") == NULL)
186                 return;
187
188         while (*splitted) {
189                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
190                         g_warning ("'%s' in MONO_GAC_PATH doesn't exist or has wrong permissions.", *splitted);
191
192                 splitted++;
193         }
194 }
195
196 gboolean
197 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
198 {
199         if (!l->name || !r->name)
200                 return FALSE;
201
202         if (strcmp (l->name, r->name))
203                 return FALSE;
204
205         if (l->culture && r->culture && strcmp (l->culture, r->culture))
206                 return FALSE;
207
208         if (l->major != r->major || l->minor != r->minor ||
209                         l->build != r->build || l->revision != r->revision)
210                 if (! ((l->major == 0 && l->minor == 0 && l->build == 0 && l->revision == 0) || (r->major == 0 && r->minor == 0 && r->build == 0 && r->revision == 0)))
211                         return FALSE;
212
213         if (!l->public_key_token [0] || !r->public_key_token [0])
214                 return TRUE;
215
216         if (strcmp (l->public_key_token, r->public_key_token))
217                 return FALSE;
218
219         return TRUE;
220 }
221
222 static MonoAssembly*
223 search_loaded (MonoAssemblyName* aname, gboolean refonly)
224 {
225         GList *tmp;
226         MonoAssembly *ass;
227         GList *loading;
228
229         ass = refonly ? mono_assembly_refonly_invoke_search_hook (aname) : mono_assembly_invoke_search_hook (aname);
230         if (ass)
231                 return ass;
232         
233         /*
234          * The assembly might be under load by this thread. In this case, it is
235          * safe to return an incomplete instance to prevent loops.
236          */
237         loading = g_hash_table_lookup (refonly ? assemblies_refonly_loading : assemblies_loading, GetCurrentThread ());
238         for (tmp = loading; tmp; tmp = tmp->next) {
239                 ass = tmp->data;
240                 if (!mono_assembly_names_equal (aname, &ass->aname))
241                         continue;
242
243                 return ass;
244         }
245
246         return NULL;
247 }
248
249 static MonoAssembly *
250 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly)
251 {
252         int i;
253         char *fullpath;
254         MonoAssembly *result;
255
256         for (i = 0; search_path [i]; ++i) {
257                 fullpath = g_build_filename (search_path [i], basename, NULL);
258                 result = mono_assembly_open_full (fullpath, status, refonly);
259                 g_free (fullpath);
260                 if (result)
261                         return result;
262         }
263         return NULL;
264 }
265
266 /**
267  * mono_assembly_setrootdir:
268  * @root_dir: The pathname of the root directory where we will locate assemblies
269  *
270  * This routine sets the internal default root directory for looking up
271  * assemblies.  This is used by Windows installations to compute dynamically
272  * the place where the Mono assemblies are located.
273  *
274  */
275 void
276 mono_assembly_setrootdir (const char *root_dir)
277 {
278         /*
279          * Override the MONO_ASSEMBLIES directory configured at compile time.
280          */
281         /* Leak if called more than once */
282         default_path [0] = g_strdup (root_dir);
283 }
284
285 G_CONST_RETURN gchar *
286 mono_assembly_getrootdir (void)
287 {
288         return default_path [0];
289 }
290
291 /**
292  * mono_assemblies_init:
293  *
294  *  Initialize global variables used by this module.
295  */
296 void
297 mono_assemblies_init (void)
298 {
299 #ifdef PLATFORM_WIN32
300         mono_set_rootdir ();
301 #endif
302
303         check_path_env ();
304         check_extra_gac_path_env ();
305
306         InitializeCriticalSection (&assemblies_mutex);
307
308         assemblies_loading = g_hash_table_new (NULL, NULL);
309         assemblies_refonly_loading = g_hash_table_new (NULL, NULL);
310 }
311
312 gboolean
313 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
314 {
315         MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
316         guint32 cols [MONO_ASSEMBLY_SIZE];
317
318         if (!t->rows)
319                 return FALSE;
320
321         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
322
323         aname->hash_len = 0;
324         aname->hash_value = NULL;
325         aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
326         aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
327         aname->flags = cols [MONO_ASSEMBLY_FLAGS];
328         aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
329         aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
330         aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
331         aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
332         aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
333         if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
334                 gchar* token = g_malloc (8);
335                 gchar* encoded;
336                 int len;
337
338                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
339                 len = mono_metadata_decode_blob_size (aname->public_key, (const char**)&aname->public_key);
340
341                 mono_digest_get_public_token (token, aname->public_key, len);
342                 encoded = encode_public_tok (token, 8);
343                 g_strlcpy (aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
344
345                 g_free (encoded);
346                 g_free (token);
347         }
348         else {
349                 aname->public_key = NULL;
350                 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
351         }
352
353         if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
354                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
355         }
356         else
357                 aname->public_key = 0;
358
359         return TRUE;
360 }
361
362 static gchar*
363 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
364 {
365         const gchar *public_tok;
366         int len;
367
368         public_tok = mono_metadata_blob_heap (image, key_index);
369         len = mono_metadata_decode_blob_size (public_tok, &public_tok);
370
371         if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
372                 gchar token [8];
373                 mono_digest_get_public_token (token, public_tok, len);
374                 return encode_public_tok (token, 8);
375         }
376
377         return encode_public_tok (public_tok, len);
378 }
379
380 void
381 mono_assembly_addref (MonoAssembly *assembly)
382 {
383         EnterCriticalSection (&assemblies_mutex);
384         /*g_print ("adding ref from %d to %s (%p)\n", assembly->ref_count, assembly->aname.name, assembly);*/
385         assembly->ref_count++;
386         LeaveCriticalSection (&assemblies_mutex);
387 }
388
389 static MonoAssemblyName *
390 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
391 {
392         const MonoRuntimeInfo *current_runtime;
393         int pos, first, last;
394
395         if (aname->name == NULL) return aname;
396         current_runtime = mono_get_runtime_info ();
397
398         first = 0;
399         last = G_N_ELEMENTS (framework_assemblies) - 1;
400         
401         while (first <= last) {
402                 int res;
403                 pos = first + (last - first) / 2;
404                 res = strcmp (aname->name, framework_assemblies[pos].assembly_name);
405                 if (res == 0) {
406                         const AssemblyVersionSet* vset;
407                         int index = framework_assemblies[pos].version_set_index;
408                         g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
409                         vset = &current_runtime->version_sets [index];
410
411                         if (aname->major == vset->major && aname->minor == vset->minor &&
412                                 aname->build == vset->build && aname->revision == vset->revision)
413                                 return aname;
414                 
415                         if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
416                                 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
417                                         "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
418                                                         aname->name,
419                                                         aname->major, aname->minor, aname->build, aname->revision,
420                                                         vset->major, vset->minor, vset->build, vset->revision
421                                                         );
422                         
423                         memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
424                         dest_aname->major = vset->major;
425                         dest_aname->minor = vset->minor;
426                         dest_aname->build = vset->build;
427                         dest_aname->revision = vset->revision;
428                         return dest_aname;
429                 } else if (res < 0) {
430                         last = pos - 1;
431                 } else {
432                         first = pos + 1;
433                 }
434         }
435         return aname;
436 }
437
438 void
439 mono_assembly_load_reference (MonoImage *image, int index)
440 {
441         MonoTableInfo *t;
442         guint32 cols [MONO_ASSEMBLYREF_SIZE];
443         const char *hash;
444         MonoAssembly *reference;
445         MonoAssemblyName aname;
446         MonoImageOpenStatus status;
447
448         /*
449          * image->references is shared between threads, so we need to access
450          * it inside a critical section.
451          */
452         EnterCriticalSection (&assemblies_mutex);
453         reference = image->references [index];
454         LeaveCriticalSection (&assemblies_mutex);
455         if (reference)
456                 return;
457
458         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
459
460         mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
461                 
462         hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
463         aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
464         aname.hash_value = hash;
465         aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
466         aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
467         aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
468         aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
469         aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
470         aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
471         aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
472
473         if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
474                 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname.flags);
475                 g_strlcpy (aname.public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
476                 g_free (token);
477         } else {
478                 memset (aname.public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
479         }
480
481         if (image->assembly->ref_only) {
482                 /* We use the loaded corlib */
483                 if (!strcmp (aname.name, "mscorlib"))
484                         reference = mono_assembly_load_full (&aname, image->assembly->basedir, &status, FALSE);
485                 else
486                         reference = mono_assembly_loaded_full (&aname, TRUE);
487                 /*
488                  * Here we must advice that the error was due to
489                  * a non loaded reference using the ReflectionOnly api
490                 */
491                 if (!reference)
492                         reference = (gpointer)-1;
493         } else
494                 reference = mono_assembly_load (&aname, image->assembly->basedir, &status);
495
496         if (reference == NULL){
497                 char *extra_msg = g_strdup ("");
498
499                 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
500                         extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly->basedir);
501                 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
502                         extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
503                 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
504                         extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
505                 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
506                         extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
507                 }
508                 
509                 g_warning ("The following assembly referenced from %s could not be loaded:\n"
510                                    "     Assembly:   %s    (assemblyref_index=%d)\n"
511                                    "     Version:    %d.%d.%d.%d\n"
512                                    "     Public Key: %s\n%s",
513                                    image->name, aname.name, index,
514                                    aname.major, aname.minor, aname.build, aname.revision,
515                                    strlen(aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
516                 g_free (extra_msg);
517         }
518
519         EnterCriticalSection (&assemblies_mutex);
520         if (reference == NULL) {
521                 /* Flag as not found */
522                 reference = (gpointer)-1;
523         } else {
524                 reference->ref_count++;
525         }       
526
527         if (!image->references [index])
528                 image->references [index] = reference;
529         LeaveCriticalSection (&assemblies_mutex);
530
531         if (image->references [index] != reference) {
532                 /* Somebody loaded it before us */
533                 mono_assembly_close (reference);
534         }
535 }
536
537 void
538 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
539 {
540         MonoTableInfo *t;
541         int i;
542
543         *status = MONO_IMAGE_OK;
544
545         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
546         
547         image->references = g_new0 (MonoAssembly *, t->rows + 1);
548
549         /* resolve assembly references for modules */
550         for (i = 0; i < image->module_count; i++){
551                 if (image->modules [i]) {
552                         image->modules [i]->assembly = image->assembly;
553                         mono_assembly_load_references (image->modules [i], status);
554                 }
555         }
556 }
557
558 typedef struct AssemblyLoadHook AssemblyLoadHook;
559 struct AssemblyLoadHook {
560         AssemblyLoadHook *next;
561         MonoAssemblyLoadFunc func;
562         gpointer user_data;
563 };
564
565 AssemblyLoadHook *assembly_load_hook = NULL;
566
567 void
568 mono_assembly_invoke_load_hook (MonoAssembly *ass)
569 {
570         AssemblyLoadHook *hook;
571
572         for (hook = assembly_load_hook; hook; hook = hook->next) {
573                 hook->func (ass, hook->user_data);
574         }
575 }
576
577 void
578 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
579 {
580         AssemblyLoadHook *hook;
581         
582         g_return_if_fail (func != NULL);
583
584         hook = g_new0 (AssemblyLoadHook, 1);
585         hook->func = func;
586         hook->user_data = user_data;
587         hook->next = assembly_load_hook;
588         assembly_load_hook = hook;
589 }
590
591 typedef struct AssemblySearchHook AssemblySearchHook;
592 struct AssemblySearchHook {
593         AssemblySearchHook *next;
594         MonoAssemblySearchFunc func;
595         gpointer user_data;
596 };
597
598 AssemblySearchHook *assembly_search_hook = NULL;
599 static AssemblySearchHook *assembly_refonly_search_hook = NULL;
600
601 MonoAssembly*
602 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
603 {
604         AssemblySearchHook *hook;
605
606         for (hook = assembly_search_hook; hook; hook = hook->next) {
607                 MonoAssembly *ass = hook->func (aname, hook->user_data);
608                 if (ass)
609                         return ass;
610         }
611
612         return NULL;
613 }
614
615 static MonoAssembly*
616 mono_assembly_refonly_invoke_search_hook (MonoAssemblyName *aname)
617 {
618         AssemblySearchHook *hook;
619
620         for (hook = assembly_refonly_search_hook; hook; hook = hook->next) {
621                 MonoAssembly *ass = hook->func (aname, hook->user_data);
622                 if (ass)
623                         return ass;
624         }
625
626         return NULL;
627 }
628
629 void          
630 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
631 {
632         AssemblySearchHook *hook;
633         
634         g_return_if_fail (func != NULL);
635
636         hook = g_new0 (AssemblySearchHook, 1);
637         hook->func = func;
638         hook->user_data = user_data;
639         hook->next = assembly_search_hook;
640         assembly_search_hook = hook;
641 }       
642
643 void
644 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
645 {
646         AssemblySearchHook *hook;
647
648         g_return_if_fail (func != NULL);
649
650         hook = g_new0 (AssemblySearchHook, 1);
651         hook->func = func;
652         hook->user_data = user_data;
653         hook->next = assembly_refonly_search_hook;
654         assembly_refonly_search_hook = hook;
655 }
656
657 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
658 struct AssemblyPreLoadHook {
659         AssemblyPreLoadHook *next;
660         MonoAssemblyPreLoadFunc func;
661         gpointer user_data;
662 };
663
664 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
665 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
666
667 static MonoAssembly *
668 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
669 {
670         AssemblyPreLoadHook *hook;
671         MonoAssembly *assembly;
672
673         for (hook = assembly_preload_hook; hook; hook = hook->next) {
674                 assembly = hook->func (aname, assemblies_path, hook->user_data);
675                 if (assembly != NULL)
676                         return assembly;
677         }
678
679         return NULL;
680 }
681
682 static MonoAssembly *
683 invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
684 {
685         AssemblyPreLoadHook *hook;
686         MonoAssembly *assembly;
687
688         for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
689                 assembly = hook->func (aname, assemblies_path, hook->user_data);
690                 if (assembly != NULL)
691                         return assembly;
692         }
693
694         return NULL;
695 }
696
697 void
698 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
699 {
700         AssemblyPreLoadHook *hook;
701         
702         g_return_if_fail (func != NULL);
703
704         hook = g_new0 (AssemblyPreLoadHook, 1);
705         hook->func = func;
706         hook->user_data = user_data;
707         hook->next = assembly_preload_hook;
708         assembly_preload_hook = hook;
709 }
710
711 void
712 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
713 {
714         AssemblyPreLoadHook *hook;
715         
716         g_return_if_fail (func != NULL);
717
718         hook = g_new0 (AssemblyPreLoadHook, 1);
719         hook->func = func;
720         hook->user_data = user_data;
721         hook->next = assembly_refonly_preload_hook;
722         assembly_refonly_preload_hook = hook;
723 }
724
725 static gchar *
726 absolute_dir (const gchar *filename)
727 {
728         gchar *cwd;
729         gchar *mixed;
730         gchar **parts;
731         gchar *part;
732         GList *list, *tmp;
733         GString *result;
734         gchar *res;
735         gint i;
736
737         if (g_path_is_absolute (filename))
738                 return g_path_get_dirname (filename);
739
740         cwd = g_get_current_dir ();
741         mixed = g_build_filename (cwd, filename, NULL);
742         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
743         g_free (mixed);
744         g_free (cwd);
745
746         list = NULL;
747         for (i = 0; (part = parts [i]) != NULL; i++) {
748                 if (!strcmp (part, "."))
749                         continue;
750
751                 if (!strcmp (part, "..")) {
752                         if (list && list->next) /* Don't remove root */
753                                 list = g_list_delete_link (list, list);
754                 } else {
755                         list = g_list_prepend (list, part);
756                 }
757         }
758
759         result = g_string_new ("");
760         list = g_list_reverse (list);
761
762         /* Ignores last data pointer, which should be the filename */
763         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
764                 if (tmp->data)
765                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
766                                                                 G_DIR_SEPARATOR);
767         
768         res = result->str;
769         g_string_free (result, FALSE);
770         g_list_free (list);
771         g_strfreev (parts);
772         if (*res == '\0') {
773                 g_free (res);
774                 return g_strdup (".");
775         }
776
777         return res;
778 }
779
780 /** 
781  * mono_assembly_open_from_bundle:
782  * @filename: Filename requested
783  * @status: return value
784  *
785  * This routine tries to open the assembly specified by `filename' from the
786  * defined bundles, if found, returns the MonoImage for it, if not found
787  * returns NULL
788  */
789 static MonoImage *
790 mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status)
791 {
792         int i;
793         char *name = g_path_get_basename (filename);
794         MonoImage *image = NULL;
795
796         /*
797          * we do a very simple search for bundled assemblies: it's not a general 
798          * purpose assembly loading mechanism.
799          */
800         EnterCriticalSection (&assemblies_mutex);
801         for (i = 0; !image && bundles [i]; ++i) {
802                 if (strcmp (bundles [i]->name, name) == 0) {
803                         image = mono_image_open_from_data ((char*)bundles [i]->data, bundles [i]->size, FALSE, status);
804                         break;
805                 }
806         }
807         LeaveCriticalSection (&assemblies_mutex);
808         g_free (name);
809         if (image) {
810                 mono_image_addref (image);
811                 return image;
812         }
813         return NULL;
814 }
815
816 static MonoImage*
817 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
818 {
819         MonoImage *image = NULL;
820
821         if (bundles != NULL){
822                 image = mono_assembly_open_from_bundle (filename, status);
823
824                 if (image != NULL)
825                         return image;
826         }
827         EnterCriticalSection (&assemblies_mutex);
828         image = mono_image_open (filename, status);
829         LeaveCriticalSection (&assemblies_mutex);
830
831         return image;
832 }
833
834 MonoAssembly *
835 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
836 {
837         MonoImage *image;
838         MonoAssembly *ass;
839         MonoImageOpenStatus def_status;
840         gchar *fname;
841         
842         g_return_val_if_fail (filename != NULL, NULL);
843
844         if (!status)
845                 status = &def_status;
846         *status = MONO_IMAGE_OK;
847
848         if (strncmp (filename, "file://", 7) == 0) {
849                 GError *error = NULL;
850                 gchar *uri = (gchar *) filename;
851                 gchar *tmpuri;
852
853                 /*
854                  * MS allows file://c:/... and fails on file://localhost/c:/... 
855                  * They also throw an IndexOutOfRangeException if "file://"
856                  */
857                 if (uri [7] != '/')
858                         uri = g_strdup_printf ("file:///%s", uri + 7);
859         
860                 tmpuri = uri;
861                 uri = mono_escape_uri_string (tmpuri);
862                 fname = g_filename_from_uri (uri, NULL, &error);
863                 g_free (uri);
864
865                 if (tmpuri != filename)
866                         g_free (tmpuri);
867
868                 if (error != NULL) {
869                         g_warning ("%s\n", error->message);
870                         g_error_free (error);
871                         fname = g_strdup (filename);
872                 }
873         } else {
874                 fname = g_strdup (filename);
875         }
876
877         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
878                         "Assembly Loader probing location: '%s'.", filename);
879         image = do_mono_assembly_open (fname, status);
880
881         if (!image){
882                 *status = MONO_IMAGE_ERROR_ERRNO;
883                 g_free (fname);
884                 return NULL;
885         }
886
887         ass = mono_assembly_load_from_full (image, fname, status, refonly);
888
889         if (ass) {
890                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
891                                 "Assembly Loader loaded assembly from location: '%s'.", filename);
892                 if (!refonly)
893                         mono_config_for_assembly (ass->image);
894         }
895
896         g_free (fname);
897
898         return ass;
899 }
900
901 /**
902  * mono_assembly_open:
903  * @filename: Opens the assembly pointed out by this name
904  * @status: where a status code can be returned
905  *
906  * mono_assembly_open opens the PE-image pointed by @filename, and
907  * loads any external assemblies referenced by it.
908  *
909  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
910  * it. 
911  */
912 MonoAssembly *
913 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
914 {
915         return mono_assembly_open_full (filename, status, FALSE);
916 }
917
918 MonoAssembly *
919 mono_assembly_load_from_full (MonoImage *image, const char*fname, 
920                          MonoImageOpenStatus *status, gboolean refonly)
921 {
922         MonoAssembly *ass, *ass2;
923         char *base_dir;
924         GList *loading;
925         GHashTable *ass_loading;
926
927 #if defined (PLATFORM_WIN32)
928         {
929                 gchar *tmp_fn;
930                 int i;
931
932                 tmp_fn = g_strdup (fname);
933                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
934                         if (tmp_fn [i] == '/')
935                                 tmp_fn [i] = '\\';
936                 }
937
938                 base_dir = absolute_dir (tmp_fn);
939                 g_free (tmp_fn);
940         }
941 #else
942         base_dir = absolute_dir (fname);
943 #endif
944
945         /*
946          * To avoid deadlocks and scalability problems, we load assemblies outside
947          * the assembly lock. This means that multiple threads might try to load
948          * the same assembly at the same time. The first one to load it completely
949          * "wins", the other threads free their copy and use the one loaded by
950          * the winning thread.
951          */
952
953         /*
954          * Create assembly struct, and enter it into the assembly cache
955          */
956         ass = g_new0 (MonoAssembly, 1);
957         ass->basedir = base_dir;
958         ass->ref_only = refonly;
959         ass->image = image;
960         ass->ref_count = 1;
961
962         mono_assembly_fill_assembly_name (image, &ass->aname);
963
964         /* 
965          * Atomically search the loaded list and add ourselves to it if necessary.
966          */
967         EnterCriticalSection (&assemblies_mutex);
968         if (ass->aname.name) {
969                 /* avoid loading the same assembly twice for now... */
970                 ass2 = search_loaded (&ass->aname, refonly);
971                 if (ass2) {
972                         g_free (ass);
973                         g_free (base_dir);
974                         mono_image_close (image);
975                         *status = MONO_IMAGE_OK;
976                         LeaveCriticalSection (&assemblies_mutex);
977                         return ass2;
978                 }
979         }
980         ass_loading = refonly ? assemblies_refonly_loading : assemblies_loading;
981         loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
982         loading = g_list_prepend (loading, ass);
983         g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
984         LeaveCriticalSection (&assemblies_mutex);
985
986         image->assembly = ass;
987
988         mono_assembly_load_references (image, status);
989
990         EnterCriticalSection (&assemblies_mutex);
991
992         loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
993         loading = g_list_remove (loading, ass);
994         if (loading == NULL)
995                 /* Prevent memory leaks */
996                 g_hash_table_remove (ass_loading, GetCurrentThread ());
997         else
998                 g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
999         if (*status != MONO_IMAGE_OK) {
1000                 LeaveCriticalSection (&assemblies_mutex);
1001                 mono_assembly_close (ass);
1002                 return NULL;
1003         }
1004
1005         if (ass->aname.name) {
1006                 ass2 = search_loaded (&ass->aname, refonly);
1007                 if (ass2) {
1008                         /* Somebody else has loaded the assembly before us */
1009                         LeaveCriticalSection (&assemblies_mutex);
1010                         mono_assembly_close (ass);
1011                         return ass2;
1012                 }
1013         }
1014
1015         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
1016         LeaveCriticalSection (&assemblies_mutex);
1017
1018         mono_assembly_invoke_load_hook (ass);
1019
1020         return ass;
1021 }
1022
1023 MonoAssembly *
1024 mono_assembly_load_from (MonoImage *image, const char *fname,
1025                          MonoImageOpenStatus *status)
1026 {
1027         return mono_assembly_load_from_full (image, fname, status, FALSE);
1028 }
1029
1030 /**
1031 * mono_assembly_name_free:
1032 * @aname: assembly name to free
1033
1034 * Frees the provided assembly name object.
1035 * (it does not frees the object itself, only the name members).
1036 */
1037 void
1038 mono_assembly_name_free (MonoAssemblyName *aname)
1039 {
1040         if (aname == NULL)
1041                 return;
1042
1043         g_free ((void *) aname->name);
1044         g_free ((void *) aname->culture);
1045         g_free ((void *) aname->hash_value);
1046 }
1047
1048 static gboolean
1049 build_assembly_name (const char *name, const char *version, const char *culture, const char *token, MonoAssemblyName *aname)
1050 {
1051         gint major, minor, build, revision;
1052
1053         memset (aname, 0, sizeof (MonoAssemblyName));
1054
1055         if (version) {
1056                 if (sscanf (version, "%u.%u.%u.%u", &major, &minor, &build, &revision) != 4)
1057                         return FALSE;
1058
1059                 aname->major = major;
1060                 aname->minor = minor;
1061                 aname->build = build;
1062                 aname->revision = revision;
1063         }
1064         
1065         aname->name = g_strdup (name);
1066         
1067         if (culture) {
1068                 if (g_strcasecmp (culture, "neutral") == 0)
1069                         aname->culture = g_strdup ("");
1070                 else
1071                         aname->culture = g_strdup (culture);
1072         }
1073         
1074         if (token && strncmp (token, "null", 4) != 0)
1075                 g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
1076         
1077         return TRUE;
1078 }
1079
1080 static gboolean
1081 parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemblyName *aname)
1082 {
1083         gchar **parts;
1084         gboolean res;
1085         
1086         parts = g_strsplit (dirname, "_", 3);
1087         if (!parts || !parts[0] || !parts[1] || !parts[2]) {
1088                 g_strfreev (parts);
1089                 return FALSE;
1090         }
1091         
1092         res = build_assembly_name (name, parts[0], parts[1], parts[2], aname);
1093         g_strfreev (parts);
1094         return res;
1095 }
1096
1097 /**
1098 * mono_assembly_name_parse:
1099 * @name: name to parse
1100 * @aname: the destination assembly name
1101 * Returns: true if the name could be parsed.
1102
1103 * Parses an assembly qualified type name and assigns the name,
1104 * version, culture and token to the provided assembly name object.
1105 */
1106 gboolean
1107 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
1108 {
1109         gchar *dllname;
1110         gchar *version = NULL;
1111         gchar *culture = NULL;
1112         gchar *token = NULL;
1113         gboolean res;
1114         gchar *value;
1115         gchar **parts;
1116         gchar **tmp;
1117
1118         parts = tmp = g_strsplit (name, ",", 4);
1119         if (!tmp || !*tmp) {
1120                 g_strfreev (tmp);
1121                 return FALSE;
1122         }
1123
1124         dllname = g_strstrip (*tmp);
1125         
1126         tmp++;
1127
1128         while (*tmp) {
1129                 value = g_strstrip (*tmp);
1130                 if (!g_ascii_strncasecmp (value, "Version=", 8)) {
1131                         version = g_strstrip (value + 8);
1132                         tmp++;
1133                         continue;
1134                 }
1135
1136                 if (!g_ascii_strncasecmp (value, "Culture=", 8)) {
1137                         culture = g_strstrip (value + 8);
1138                         tmp++;
1139                         continue;
1140                 }
1141
1142                 if (!g_ascii_strncasecmp (value, "PublicKeyToken=", 15)) {
1143                         token = g_strstrip (value + 15);
1144                         tmp++;
1145                         continue;
1146                 }
1147                 
1148                 g_strfreev (parts);
1149                 return FALSE;
1150         }
1151
1152         res = build_assembly_name (dllname, version, culture, token, aname);
1153         g_strfreev (parts);
1154         return res;
1155 }
1156
1157 static MonoAssembly*
1158 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
1159 {
1160         gchar *fullpath = NULL;
1161         GDir *dirhandle;
1162         const char* direntry;
1163         MonoAssemblyName gac_aname;
1164         gint major=-1, minor=0, build=0, revision=0;
1165         gboolean exact_version;
1166         
1167         dirhandle = g_dir_open (basepath, 0, NULL);
1168         if (!dirhandle)
1169                 return NULL;
1170                 
1171         exact_version = (aname->major | aname->minor | aname->build | aname->revision) != 0;
1172
1173         while ((direntry = g_dir_read_name (dirhandle))) {
1174                 gboolean match = TRUE;
1175                 
1176                 parse_assembly_directory_name (aname->name, direntry, &gac_aname);
1177                 
1178                 if (aname->culture != NULL && strcmp (aname->culture, gac_aname.culture) != 0)
1179                         match = FALSE;
1180                         
1181                 if (match && strlen ((char*)aname->public_key_token) > 0 && 
1182                                 strcmp ((char*)aname->public_key_token, (char*)gac_aname.public_key_token) != 0)
1183                         match = FALSE;
1184                 
1185                 if (match) {
1186                         if (exact_version) {
1187                                 match = (aname->major == gac_aname.major && aname->minor == gac_aname.minor &&
1188                                                  aname->build == gac_aname.build && aname->revision == gac_aname.revision); 
1189                         }
1190                         else if (gac_aname.major < major)
1191                                 match = FALSE;
1192                         else if (gac_aname.major == major) {
1193                                 if (gac_aname.minor < minor)
1194                                         match = FALSE;
1195                                 else if (gac_aname.minor == minor) {
1196                                         if (gac_aname.build < build)
1197                                                 match = FALSE;
1198                                         else if (gac_aname.build == build && gac_aname.revision <= revision)
1199                                                 match = FALSE; 
1200                                 }
1201                         }
1202                 }
1203                 
1204                 if (match) {
1205                         major = gac_aname.major;
1206                         minor = gac_aname.minor;
1207                         build = gac_aname.build;
1208                         revision = gac_aname.revision;
1209                         g_free (fullpath);
1210                         fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
1211                 }
1212
1213                 mono_assembly_name_free (&gac_aname);
1214         }
1215         
1216         g_dir_close (dirhandle);
1217         
1218         if (fullpath == NULL)
1219                 return NULL;
1220         else {
1221                 MonoAssembly *res = mono_assembly_open (fullpath, status);
1222                 g_free (fullpath);
1223                 return res;
1224         }
1225 }
1226
1227 MonoAssembly*
1228 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
1229 {
1230         MonoAssembly *res;
1231         MonoAssemblyName *aname, base_name, maped_aname;
1232         gchar *fullname, *gacpath;
1233         gchar **paths;
1234
1235         memset (&base_name, 0, sizeof (MonoAssemblyName));
1236         aname = &base_name;
1237
1238         if (!mono_assembly_name_parse (name, aname))
1239                 return NULL;
1240
1241         /* 
1242          * If no specific version has been requested, make sure we load the
1243          * correct version for system assemblies.
1244          */ 
1245         if ((aname->major | aname->minor | aname->build | aname->revision) == 0)
1246                 aname = mono_assembly_remap_version (aname, &maped_aname);
1247         
1248         res = mono_assembly_loaded (aname);
1249         if (res) {
1250                 mono_assembly_name_free (aname);
1251                 return res;
1252         }
1253
1254         res = invoke_assembly_preload_hook (aname, assemblies_path);
1255         if (res) {
1256                 res->in_gac = FALSE;
1257                 mono_assembly_name_free (aname);
1258                 return res;
1259         }
1260
1261         fullname = g_strdup_printf ("%s.dll", aname->name);
1262
1263         if (extra_gac_paths) {
1264                 paths = extra_gac_paths;
1265                 while (!res && *paths) {
1266                         gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", aname->name, NULL);
1267                         res = probe_for_partial_name (gacpath, fullname, aname, status);
1268                         g_free (gacpath);
1269                         paths++;
1270                 }
1271         }
1272
1273         if (res) {
1274                 res->in_gac = TRUE;
1275                 g_free (fullname);
1276                 mono_assembly_name_free (aname);
1277                 return res;
1278         }
1279
1280         gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", aname->name, NULL);
1281         res = probe_for_partial_name (gacpath, fullname, aname, status);
1282         g_free (gacpath);
1283
1284         if (res)
1285                 res->in_gac = TRUE;
1286
1287         g_free (fullname);
1288         mono_assembly_name_free (aname);
1289
1290         return res;
1291 }
1292
1293 /**
1294  * mono_assembly_load_from_gac
1295  *
1296  * @aname: The assembly name object
1297  */
1298 static MonoAssembly*
1299 mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
1300 {
1301         MonoAssembly *result = NULL;
1302         gchar *name, *version, *culture, *fullpath, *subpath;
1303         gint32 len;
1304         gchar **paths;
1305
1306         if (aname->public_key_token [0] == 0) {
1307                 return NULL;
1308         }
1309
1310         if (strstr (aname->name, ".dll")) {
1311                 len = strlen (filename) - 4;
1312                 name = g_malloc (len);
1313                 strncpy (name, aname->name, len);
1314         } else {
1315                 name = g_strdup (aname->name);
1316         }
1317
1318         if (aname->culture) {
1319                 culture = g_strdup (aname->culture);
1320                 g_strdown (culture);
1321         } else {
1322                 culture = g_strdup ("");
1323         }
1324
1325         version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
1326                         aname->minor, aname->build, aname->revision,
1327                         culture, aname->public_key_token);
1328         
1329         subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
1330         g_free (name);
1331         g_free (version);
1332         g_free (culture);
1333
1334         if (extra_gac_paths) {
1335                 paths = extra_gac_paths;
1336                 while (!result && *paths) {
1337                         fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
1338                         result = mono_assembly_open_full (fullpath, status, refonly);
1339                         g_free (fullpath);
1340                         paths++;
1341                 }
1342         }
1343
1344         if (result) {
1345                 result->in_gac = TRUE;
1346                 g_free (subpath);
1347                 return result;
1348         }
1349
1350         fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
1351                         "mono", "gac", subpath, NULL);
1352         result = mono_assembly_open_full (fullpath, status, refonly);
1353         g_free (fullpath);
1354
1355         if (result)
1356                 result->in_gac = TRUE;
1357         
1358         g_free (subpath);
1359
1360         return result;
1361 }
1362
1363
1364 MonoAssembly*
1365 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
1366 {
1367         char *corlib_file;
1368
1369         if (corlib) {
1370                 /* g_print ("corlib already loaded\n"); */
1371                 return corlib;
1372         }
1373         
1374         if (assemblies_path) {
1375                 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE);
1376                 if (corlib)
1377                         return corlib;
1378         }
1379
1380         /* Load corlib from mono/<version> */
1381         
1382         corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
1383         if (assemblies_path) {
1384                 corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE);
1385                 if (corlib) {
1386                         g_free (corlib_file);
1387                         return corlib;
1388                 }
1389         }
1390         corlib = load_in_path (corlib_file, default_path, status, FALSE);
1391         g_free (corlib_file);
1392
1393         return corlib;
1394 }
1395
1396
1397 MonoAssembly*
1398 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
1399 {
1400         MonoAssembly *result;
1401         char *fullpath, *filename;
1402         MonoAssemblyName maped_aname;
1403
1404         aname = mono_assembly_remap_version (aname, &maped_aname);
1405         
1406         result = mono_assembly_loaded_full (aname, refonly);
1407         if (result)
1408                 return result;
1409
1410         result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path);
1411         if (result) {
1412                 result->in_gac = FALSE;
1413                 return result;
1414         }
1415
1416         /* Currently we retrieve the loaded corlib for reflection 
1417          * only requests, like a common reflection only assembly 
1418          */
1419         if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) {
1420                 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
1421         }
1422
1423         if (strstr (aname->name, ".dll"))
1424                 filename = g_strdup (aname->name);
1425         else
1426                 filename = g_strconcat (aname->name, ".dll", NULL);
1427
1428         result = mono_assembly_load_from_gac (aname, filename, status, refonly);
1429         if (result) {
1430                 g_free (filename);
1431                 return result;
1432         }
1433
1434         if (basedir) {
1435                 fullpath = g_build_filename (basedir, filename, NULL);
1436                 result = mono_assembly_open_full (fullpath, status, refonly);
1437                 g_free (fullpath);
1438                 if (result) {
1439                         result->in_gac = FALSE;
1440                         g_free (filename);
1441                         return result;
1442                 }
1443         }
1444
1445         result = load_in_path (filename, default_path, status, refonly);
1446         if (result)
1447                 result->in_gac = FALSE;
1448         g_free (filename);
1449         return result;
1450 }
1451
1452 MonoAssembly*
1453 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
1454 {
1455         return mono_assembly_load_full (aname, basedir, status, FALSE);
1456 }
1457         
1458 MonoAssembly*
1459 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
1460 {
1461         MonoAssembly *res;
1462         MonoAssemblyName maped_aname;
1463
1464         aname = mono_assembly_remap_version (aname, &maped_aname);
1465
1466         EnterCriticalSection (&assemblies_mutex);
1467         res = search_loaded (aname, refonly);
1468         LeaveCriticalSection (&assemblies_mutex);
1469
1470         return res;
1471 }
1472
1473 MonoAssembly*
1474 mono_assembly_loaded (MonoAssemblyName *aname)
1475 {
1476         return mono_assembly_loaded_full (aname, FALSE);
1477 }
1478
1479 void
1480 mono_assembly_close (MonoAssembly *assembly)
1481 {
1482         MonoImage *image;
1483         
1484         g_return_if_fail (assembly != NULL);
1485
1486         EnterCriticalSection (&assemblies_mutex);
1487         /*g_print ("destroy assembly %p %d (%s)\n", assembly, assembly->ref_count, assembly->image->name);*/
1488         g_assert (assembly->ref_count > 0);
1489         if (--assembly->ref_count != 0) {
1490                 LeaveCriticalSection (&assemblies_mutex);
1491                 return;
1492         }
1493         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
1494         LeaveCriticalSection (&assemblies_mutex);
1495         image = assembly->image;
1496         /* assemblies belong to domains, so the domain code takes care of unloading the
1497          * referenced assemblies
1498          */
1499
1500         mono_image_close (assembly->image);
1501
1502         g_free (assembly->basedir);
1503         if (!assembly->dynamic)
1504                 g_free (assembly);
1505 }
1506
1507 MonoImage*
1508 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
1509 {
1510         MonoImageOpenStatus status;
1511         MonoImage *module;
1512
1513         module = mono_image_load_file_for_image (assembly->image, idx);
1514         if (module)
1515                 mono_assembly_load_references (module, &status);
1516
1517         return module;
1518 }
1519
1520 void
1521 mono_assembly_foreach (GFunc func, gpointer user_data)
1522 {
1523         GList *copy;
1524
1525         /*
1526          * We make a copy of the list to avoid calling the callback inside the 
1527          * lock, which could lead to deadlocks.
1528          */
1529         EnterCriticalSection (&assemblies_mutex);
1530         copy = g_list_copy (loaded_assemblies);
1531         LeaveCriticalSection (&assemblies_mutex);
1532
1533         g_list_foreach (loaded_assemblies, func, user_data);
1534
1535         g_list_free (copy);
1536 }
1537
1538 /*
1539  * Holds the assembly of the application, for
1540  * System.Diagnostics.Process::MainModule
1541  */
1542 static MonoAssembly *main_assembly=NULL;
1543
1544 void
1545 mono_assembly_set_main (MonoAssembly *assembly)
1546 {
1547         main_assembly=assembly;
1548 }
1549
1550 MonoAssembly *
1551 mono_assembly_get_main (void)
1552 {
1553         return(main_assembly);
1554 }
1555
1556 MonoImage*
1557 mono_assembly_get_image (MonoAssembly *assembly)
1558 {
1559         return assembly->image;
1560 }
1561
1562 void
1563 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
1564 {
1565         bundles = assemblies;
1566 }