* appdomain.c: Increment version number.
[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  * TODO:
10  *   Implement big-endian versions of the reading routines.
11  */
12 #include <config.h>
13 #include <stdio.h>
14 #include <glib.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include "assembly.h"
19 #include "image.h"
20 #include "cil-coff.h"
21 #include "rawbuffer.h"
22 #ifdef WITH_BUNDLE
23 #include "mono-bundle.h"
24 #endif
25 #include <mono/metadata/loader.h>
26 #include <mono/metadata/tabledefs.h>
27 #include <mono/io-layer/io-layer.h>
28 #include <mono/utils/mono-uri.h>
29 #include <mono/metadata/mono-config.h>
30 #include <mono/utils/mono-digest.h>
31 #ifdef PLATFORM_WIN32
32 #include <mono/os/util.h>
33 #endif
34
35 /* the default search path is just MONO_ASSEMBLIES */
36 static const char*
37 default_path [] = {
38         MONO_ASSEMBLIES,
39         NULL
40 };
41
42 static char **assemblies_path = NULL;
43
44 /*
45  * keeps track of loaded assemblies
46  */
47 static GList *loaded_assemblies = NULL;
48 static MonoAssembly *corlib;
49
50 /* This protects loaded_assemblies and image->references */
51 static CRITICAL_SECTION assemblies_mutex;
52
53 /* A hastable of thread->assembly list mappings */
54 static GHashTable *assemblies_loading;
55
56 static char **extra_gac_paths = NULL;
57
58 static gchar*
59 encode_public_tok (const guchar *token, gint32 len)
60 {
61         static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
62         gchar *res;
63         int i;
64
65         res = g_malloc (len * 2 + 1);
66         for (i = 0; i < len; i++) {
67                 res [i * 2] = allowed [token [i] >> 4];
68                 res [i * 2 + 1] = allowed [token [i] & 0xF];
69         }
70         res [len * 2] = 0;
71         return res;
72 }
73
74 static void
75 check_path_env (void) {
76         const char *path;
77         char **splitted;
78         
79         path = g_getenv ("MONO_PATH");
80         if (!path)
81                 return;
82
83         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
84         if (assemblies_path)
85                 g_strfreev (assemblies_path);
86         assemblies_path = splitted;
87         if (g_getenv ("MONO_DEBUG") == NULL)
88                 return;
89
90         while (*splitted) {
91                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
92                         g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
93
94                 splitted++;
95         }
96 }
97
98 static void
99 check_extra_gac_path_env (void) {
100         const char *path;
101         char **splitted;
102         
103         path = g_getenv ("MONO_GAC_PATH");
104         if (!path)
105                 return;
106
107         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
108         if (extra_gac_paths)
109                 g_strfreev (extra_gac_paths);
110         extra_gac_paths = splitted;
111         if (g_getenv ("MONO_DEBUG") == NULL)
112                 return;
113
114         while (*splitted) {
115                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
116                         g_warning ("'%s' in MONO_GAC_PATH doesn't exist or has wrong permissions.", *splitted);
117
118                 splitted++;
119         }
120 }
121
122 static gboolean
123 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
124 {
125         if (!l->name || !r->name)
126                 return FALSE;
127
128         if (strcmp (l->name, r->name))
129                 return FALSE;
130
131         /*
132          * simply compare names until some other issues are resolved
133          * (version info not getting set correctly for custom
134          * attributes).
135          */
136         /*
137         if (l->major != r->major || l->minor != r->minor ||
138                         l->build != r->build || l->revision != r->revision)
139                 return FALSE;
140
141         if (!l->public_tok_value && !r->public_tok_value)
142                 return TRUE;
143
144         if ((l->public_tok_value && !r->public_tok_value) || (!l->public_tok_value && r->public_tok_value))
145                 return FALSE;
146
147         if (strcmp (l->public_tok_value, r->public_tok_value))
148                 return FALSE;
149         */
150         return TRUE;
151 }
152
153 /* assemblies_mutex must be held by the caller */
154 static MonoAssembly*
155 search_loaded (MonoAssemblyName* aname)
156 {
157         GList *tmp;
158         MonoAssembly *ass;
159         GList *loading;
160         
161         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
162                 ass = tmp->data;
163                 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
164                 if (!mono_assembly_names_equal (aname, &ass->aname))
165                         continue;
166                 /* g_print ("success\n"); */
167
168                 return ass;
169         }
170
171         /*
172          * The assembly might be under load by this thread. In this case, it is
173          * safe to return an incomplete instance to prevent loops.
174          */
175         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
176         for (tmp = loading; tmp; tmp = tmp->next) {
177                 ass = tmp->data;
178                 if (!mono_assembly_names_equal (aname, &ass->aname))
179                         continue;
180
181                 return ass;
182         }
183
184         return NULL;
185 }
186
187 static MonoAssembly *
188 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
189 {
190         int i;
191         char *fullpath;
192         MonoAssembly *result;
193
194         for (i = 0; search_path [i]; ++i) {
195                 fullpath = g_build_filename (search_path [i], basename, NULL);
196                 result = mono_assembly_open (fullpath, status);
197                 g_free (fullpath);
198                 if (result)
199                         return result;
200         }
201         return NULL;
202 }
203
204 /**
205  * mono_assembly_setrootdir:
206  * @root_dir: The pathname of the root directory where we will locate assemblies
207  *
208  * This routine sets the internal default root directory for looking up
209  * assemblies.  This is used by Windows installations to compute dynamically
210  * the place where the Mono assemblies are located.
211  *
212  */
213 void
214 mono_assembly_setrootdir (const char *root_dir)
215 {
216         /*
217          * Override the MONO_ASSEMBLIES directory configured at compile time.
218          */
219         /* Leak if called more than once */
220         default_path [0] = g_strdup (root_dir);
221 }
222
223 G_CONST_RETURN gchar *
224 mono_assembly_getrootdir (void)
225 {
226         return default_path [0];
227 }
228
229 /**
230  * mono_assemblies_init:
231  *
232  *  Initialize global variables used by this module.
233  */
234 void
235 mono_assemblies_init (void)
236 {
237 #ifdef PLATFORM_WIN32
238         mono_set_rootdir ();
239 #endif
240
241         check_path_env ();
242         check_extra_gac_path_env ();
243
244         InitializeCriticalSection (&assemblies_mutex);
245
246         assemblies_loading = g_hash_table_new (NULL, NULL);
247 }
248
249 gboolean
250 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
251 {
252         MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
253         guint32 cols [MONO_ASSEMBLY_SIZE];
254
255         if (!t->rows)
256                 return FALSE;
257
258         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
259
260         aname->hash_len = 0;
261         aname->hash_value = NULL;
262         aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
263         aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
264         aname->flags = cols [MONO_ASSEMBLY_FLAGS];
265         aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
266         aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
267         aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
268         aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
269         aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
270         if (cols [MONO_ASSEMBLY_PUBLIC_KEY])
271                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
272         else
273                 aname->public_key = 0;
274
275         return TRUE;
276 }
277
278 static gchar*
279 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
280 {
281         const gchar *public_tok;
282         int len;
283
284         public_tok = mono_metadata_blob_heap (image, key_index);
285         len = mono_metadata_decode_blob_size (public_tok, &public_tok);
286
287         if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
288                 gchar token [8];
289                 mono_digest_get_public_token (token, public_tok, len);
290                 return encode_public_tok (token, 8);
291         }
292
293         return encode_public_tok (public_tok, len);
294 }
295
296 void
297 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
298 {
299         MonoTableInfo *t;
300         guint32 cols [MONO_ASSEMBLYREF_SIZE];
301         const char *hash;
302         int i;
303         MonoAssembly **references = NULL;
304
305         *status = MONO_IMAGE_OK;
306         
307         /*
308          * image->references is shared between threads, so we need to access
309          * it inside a critical section.
310          */
311         EnterCriticalSection (&assemblies_mutex);
312         references = image->references;
313         LeaveCriticalSection (&assemblies_mutex);
314         if (references)
315                 return;
316
317         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
318
319         references = g_new0 (MonoAssembly *, t->rows + 1);
320
321         /*
322          * Load any assemblies this image references
323          */
324         for (i = 0; i < t->rows; i++) {
325                 MonoAssemblyName aname;
326
327                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
328                 
329                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
330                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
331                 aname.hash_value = hash;
332                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
333                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
334                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
335                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
336                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
337                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
338                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
339
340                 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
341                         aname.public_tok_value = assemblyref_public_tok (image,
342                                         cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname.flags);
343                 } else {
344                         aname.public_tok_value = NULL;
345                 } 
346
347                 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
348
349                 if (references [i] == NULL){
350                         int j;
351
352                         /*
353                         ** Temporary work around: any System.* which are 3300 build, will get
354                         ** remapped, this is to keep old applications running that might have
355                         ** been linked against our 5000 API, before we were strongnamed, and
356                         ** hence were labeled as 3300 builds by reflection.c
357                         */
358                         if (aname.build == 3300 && strncmp (aname.name, "System", 6) == 0){
359                                 aname.build = 5000;
360                                 
361                                 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
362                         }
363                         if (references [i] != NULL){
364                                 if (g_getenv ("MONO_SILENT_WARNING") == NULL)
365                                         g_printerr ("Compat mode: the request from %s to load %s was remapped (http://www.go-mono.com/remap.html)\n",
366                                                  image->name, aname.name);
367                                 
368                         } else {
369                         
370                                 for (j = 0; j < i; j++)
371                                         mono_assembly_close (references [j]);
372                                 g_free (references);
373                                 
374                                 g_warning ("Could not find assembly %s, references from %s (assemblyref_index=%d)\n"
375                                            "     Major/Minor: %d,%d\n"
376                                            "     Build:       %d,%d\n"
377                                            "     Token:       %s\n",
378                                            aname.name, image->name, i,
379                                            aname.major, aname.minor, aname.build, aname.revision,
380                                            aname.public_tok_value);
381                                 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
382                                 return;
383                         }
384                 }
385
386                 /* 
387                  * This check is disabled since lots of people seem to have an older
388                  * corlib which triggers this.
389                  */
390                 /* 
391                 if (image->references [i]->image == image)
392                         g_error ("Error: Assembly %s references itself", image->name);
393                 */
394         }
395         references [i] = NULL;
396
397         /* resolve assembly references for modules */
398         t = &image->tables [MONO_TABLE_MODULEREF];
399         for (i = 0; i < t->rows; i++){
400                 if (image->modules [i]) {
401                         image->modules [i]->assembly = image->assembly;
402                         mono_assembly_load_references (image->modules [i], status);
403                 }
404         }
405
406         EnterCriticalSection (&assemblies_mutex);
407         if (!image->references)
408                 image->references = references;
409         LeaveCriticalSection (&assemblies_mutex);
410
411         if (image->references != references) {
412                 /* Somebody loaded it before us */
413                 for (i = 0; i < t->rows; i++)
414                         mono_assembly_close (references [i]);
415                 g_free (references);
416         }
417 }
418
419 typedef struct AssemblyLoadHook AssemblyLoadHook;
420 struct AssemblyLoadHook {
421         AssemblyLoadHook *next;
422         MonoAssemblyLoadFunc func;
423         gpointer user_data;
424 };
425
426 AssemblyLoadHook *assembly_load_hook = NULL;
427
428 void
429 mono_assembly_invoke_load_hook (MonoAssembly *ass)
430 {
431         AssemblyLoadHook *hook;
432
433         for (hook = assembly_load_hook; hook; hook = hook->next) {
434                 hook->func (ass, hook->user_data);
435         }
436 }
437
438 void
439 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
440 {
441         AssemblyLoadHook *hook;
442         
443         g_return_if_fail (func != NULL);
444
445         hook = g_new0 (AssemblyLoadHook, 1);
446         hook->func = func;
447         hook->user_data = user_data;
448         hook->next = assembly_load_hook;
449         assembly_load_hook = hook;
450 }
451
452 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
453 struct AssemblyPreLoadHook {
454         AssemblyPreLoadHook *next;
455         MonoAssemblyPreLoadFunc func;
456         gpointer user_data;
457 };
458
459 AssemblyPreLoadHook *assembly_preload_hook = NULL;
460
461 static MonoAssembly *
462 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
463 {
464         AssemblyPreLoadHook *hook;
465         MonoAssembly *assembly;
466
467         for (hook = assembly_preload_hook; hook; hook = hook->next) {
468                 assembly = hook->func (aname, assemblies_path, hook->user_data);
469                 if (assembly != NULL)
470                         return assembly;
471         }
472
473         return NULL;
474 }
475
476 void
477 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
478 {
479         AssemblyPreLoadHook *hook;
480         
481         g_return_if_fail (func != NULL);
482
483         hook = g_new0 (AssemblyPreLoadHook, 1);
484         hook->func = func;
485         hook->user_data = user_data;
486         hook->next = assembly_preload_hook;
487         assembly_preload_hook = hook;
488 }
489
490 static gchar *
491 absolute_dir (const gchar *filename)
492 {
493         gchar *cwd;
494         gchar *mixed;
495         gchar **parts;
496         gchar *part;
497         GList *list, *tmp;
498         GString *result;
499         gchar *res;
500         gint i;
501
502         if (g_path_is_absolute (filename))
503                 return g_path_get_dirname (filename);
504
505         cwd = g_get_current_dir ();
506         mixed = g_build_filename (cwd, filename, NULL);
507         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
508         g_free (mixed);
509         g_free (cwd);
510
511         list = NULL;
512         for (i = 0; (part = parts [i]) != NULL; i++) {
513                 if (!strcmp (part, "."))
514                         continue;
515
516                 if (!strcmp (part, "..")) {
517                         if (list && list->next) /* Don't remove root */
518                                 list = g_list_delete_link (list, list);
519                 } else {
520                         list = g_list_prepend (list, part);
521                 }
522         }
523
524         result = g_string_new ("");
525         list = g_list_reverse (list);
526
527         /* Ignores last data pointer, which should be the filename */
528         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
529                 if (tmp->data)
530                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
531                                                                 G_DIR_SEPARATOR);
532         
533         res = result->str;
534         g_string_free (result, FALSE);
535         g_list_free (list);
536         g_strfreev (parts);
537         if (*res == '\0') {
538                 g_free (res);
539                 return g_strdup (".");
540         }
541
542         return res;
543 }
544
545 static MonoImage*
546 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
547 {
548         MonoImage *image = NULL;
549 #ifdef WITH_BUNDLE
550         int i;
551         char *name = g_path_get_basename (filename);
552         char *dot = strrchr (name, '.');
553         GList *tmp;
554         MonoAssembly *ass;
555         
556         if (dot)
557                 *dot = 0;
558         /* we do a very simple search for bundled assemblies: it's not a general 
559          * purpose assembly loading mechanism.
560          */
561         EnterCriticalSection (&assemblies_mutex);
562         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
563                 ass = tmp->data;
564                 if (!ass->aname.name)
565                         continue;
566                 if (strcmp (name, ass->aname.name))
567                         continue;
568                 image = ass->image;
569                 break;
570         }
571
572         for (i = 0; !image && bundled_assemblies [i]; ++i) {
573                 if (strcmp (bundled_assemblies [i]->name, name) == 0) {
574                         image = mono_image_open_from_data ((char*)bundled_assemblies [i]->data, bundled_assemblies [i]->size, FALSE, status);
575                         break;
576                 }
577         }
578         LeaveCriticalSection (&assemblies_mutex);
579         g_free (name);
580         if (image) {
581                 mono_image_addref (image);
582                 return image;
583         }
584 #endif
585         EnterCriticalSection (&assemblies_mutex);
586         image = mono_image_open (filename, status);
587         LeaveCriticalSection (&assemblies_mutex);
588         
589         return image;
590 }
591
592 /**
593  * mono_assembly_open:
594  * @filename: Opens the assembly pointed out by this name
595  * @status: where a status code can be returned
596  *
597  * mono_assembly_open opens the PE-image pointed by @filename, and
598  * loads any external assemblies referenced by it.
599  *
600  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
601  * it. 
602  */
603 MonoAssembly *
604 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
605 {
606         MonoImage *image;
607         MonoAssembly *ass;
608         MonoImageOpenStatus def_status;
609         gchar *fname;
610         
611         g_return_val_if_fail (filename != NULL, NULL);
612
613         if (!status)
614                 status = &def_status;
615         *status = MONO_IMAGE_OK;
616
617         if (strncmp (filename, "file://", 7) == 0) {
618                 GError *error = NULL;
619                 gchar *uri = (gchar *) filename;
620                 gchar *tmpuri;
621
622                 /*
623                  * MS allows file://c:/... and fails on file://localhost/c:/... 
624                  * They also throw an IndexOutOfRangeException if "file://"
625                  */
626                 if (uri [7] != '/')
627                         uri = g_strdup_printf ("file:///%s", uri + 7);
628         
629                 tmpuri = uri;
630                 uri = mono_escape_uri_string (tmpuri);
631                 fname = g_filename_from_uri (uri, NULL, &error);
632                 g_free (uri);
633
634                 if (tmpuri != filename)
635                         g_free (tmpuri);
636
637                 if (error != NULL) {
638                         g_warning ("%s\n", error->message);
639                         g_error_free (error);
640                         fname = g_strdup (filename);
641                 }
642         } else {
643                 fname = g_strdup (filename);
644         }
645
646         /* g_print ("file loading %s\n", fname); */
647         image = do_mono_assembly_open (fname, status);
648
649         if (!image){
650                 *status = MONO_IMAGE_ERROR_ERRNO;
651                 g_free (fname);
652                 return NULL;
653         }
654
655         ass = mono_assembly_load_from (image, fname, status);
656
657         mono_config_for_assembly (ass->image);
658
659         g_free (fname);
660
661         return ass;
662 }
663
664 MonoAssembly *
665 mono_assembly_load_from (MonoImage *image, const char*fname, 
666                          MonoImageOpenStatus *status)
667 {
668         MonoAssembly *ass, *ass2;
669         char *base_dir;
670         GList *loading;
671
672 #if defined (PLATFORM_WIN32)
673         {
674                 gchar *tmp_fn;
675                 int i;
676
677                 tmp_fn = g_strdup (fname);
678                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
679                         if (tmp_fn [i] == '/')
680                                 tmp_fn [i] = '\\';
681                 }
682
683                 base_dir = absolute_dir (tmp_fn);
684                 g_free (tmp_fn);
685         }
686 #else
687         base_dir = absolute_dir (fname);
688 #endif
689
690         /*
691          * To avoid deadlocks and scalability problems, we load assemblies outside
692          * the assembly lock. This means that multiple threads might try to load
693          * the same assembly at the same time. The first one to load it completely
694          * "wins", the other threads free their copy and use the one loaded by
695          * the winning thread.
696          */
697
698         /*
699          * Create assembly struct, and enter it into the assembly cache
700          */
701         ass = g_new0 (MonoAssembly, 1);
702         ass->basedir = base_dir;
703         ass->image = image;
704
705         mono_assembly_fill_assembly_name (image, &ass->aname);
706
707         /* 
708          * Atomically search the loaded list and add ourselves to it if necessary.
709          */
710         EnterCriticalSection (&assemblies_mutex);
711         if (ass->aname.name)
712                 /* avoid loading the same assembly twice for now... */
713                 if ((ass2 = search_loaded (&ass->aname))) {
714                         g_free (ass);
715                         g_free (base_dir);
716                         mono_image_close (image);
717                         *status = MONO_IMAGE_OK;
718                         LeaveCriticalSection (&assemblies_mutex);
719                         return ass2;
720                 }
721         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
722         loading = g_list_prepend (loading, ass);
723         g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
724         LeaveCriticalSection (&assemblies_mutex);
725
726         image->assembly = ass;
727
728         /*
729          * We load referenced assemblies outside the lock to prevent deadlocks
730          * with regards to preload hooks.
731          */
732         mono_assembly_load_references (image, status);
733
734         EnterCriticalSection (&assemblies_mutex);
735
736         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
737         loading = g_list_remove (loading, ass);
738         if (loading == NULL)
739                 /* Prevent memory leaks */
740                 g_hash_table_remove (assemblies_loading, GetCurrentThread ());
741         else
742                 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
743         if (*status != MONO_IMAGE_OK) {
744                 LeaveCriticalSection (&assemblies_mutex);
745                 mono_assembly_close (ass);
746                 return NULL;
747         }
748
749         if (ass->aname.name) {
750                 ass2 = search_loaded (&ass->aname);
751                 if (ass2) {
752                         /* Somebody else has loaded the assembly before us */
753                         LeaveCriticalSection (&assemblies_mutex);
754                         mono_assembly_close (ass);
755                         return ass2;
756                 }
757         }
758
759         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
760         LeaveCriticalSection (&assemblies_mutex);
761
762         mono_assembly_invoke_load_hook (ass);
763
764         return ass;
765 }
766
767 static MonoAssembly*
768 probe_for_partial_name (const char *basepath, const char *fullname, MonoImageOpenStatus *status)
769 {
770         MonoAssembly *res = NULL;
771         gchar *fullpath;
772         GDir *dirhandle;
773         const char* direntry;
774
775         dirhandle = g_dir_open (basepath, 0, NULL);
776         if (!dirhandle)
777                 return NULL;
778
779         while ((direntry = g_dir_read_name (dirhandle))) {
780                 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
781                 res = mono_assembly_open (fullpath, status);
782                 g_free (fullpath);
783                 if (res)
784                         break;
785         }
786         g_dir_close (dirhandle);
787
788         return res;
789 }
790
791 MonoAssembly*
792 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
793 {
794         MonoAssembly *res;
795         MonoAssemblyName aname;
796         gchar *fullname, *gacpath;
797         gchar **paths;
798
799         memset (&aname, 0, sizeof (MonoAssemblyName));
800         aname.name = name;
801
802         res = mono_assembly_loaded (&aname);
803
804         if (res)
805                 return res;
806
807         fullname = g_strdup_printf ("%s.dll", name);
808
809         if (extra_gac_paths) {
810                 paths = extra_gac_paths;
811                 while (!res && *paths) {
812                         gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "gac", name, NULL);
813                         res = probe_for_partial_name (gacpath, fullname, status);
814                         g_free (gacpath);
815                         paths++;
816                 }
817         }
818
819         if (res) {
820                 res->in_gac = TRUE;
821                 g_free (fullname);
822                 return res;
823                 
824         }
825
826         gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", name, NULL);
827         res = probe_for_partial_name (gacpath, fullname, status);
828         g_free (gacpath);
829
830         if (res)
831                 res->in_gac = TRUE;
832
833         g_free (fullname);
834
835         return res;
836 }
837
838 /**
839  * mono_assembly_load_from_gac
840  *
841  * @aname: The assembly name object
842  */
843 static MonoAssembly*
844 mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImageOpenStatus *status)
845 {
846         MonoAssembly *result = NULL;
847         gchar *name, *version, *fullpath, *subpath;
848         gint32 len;
849         gchar **paths;
850
851         if (!aname->public_tok_value) {
852                 return NULL;
853         }
854
855         if (strstr (aname->name, ".dll")) {
856                 len = strlen (filename) - 4;
857                 name = g_malloc (len);
858                 strncpy (name, aname->name, len);
859         } else {
860                 name = g_strdup (aname->name);
861         }
862
863         version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
864                         aname->minor, aname->build, aname->revision,
865                         aname->culture == NULL ? "" : aname->culture,
866                         aname->public_tok_value);
867         
868         subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
869         g_free (name);
870         g_free (version);
871
872         if (extra_gac_paths) {
873                 paths = extra_gac_paths;
874                 while (!result && *paths) {
875                         fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "gac", subpath, NULL);
876                         result = mono_assembly_open (fullpath, status);
877                         g_free (fullpath);
878                         paths++;
879                 }
880         }
881
882         if (result) {
883                 result->in_gac = TRUE;
884                 g_free (subpath);
885                 return result;
886         }
887
888         fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
889                         "mono", "gac", subpath, NULL);
890         result = mono_assembly_open (fullpath, status);
891         g_free (fullpath);
892
893         if (result)
894                 result->in_gac = TRUE;
895         
896         g_free (subpath);
897
898         return result;
899 }
900
901         
902 MonoAssembly*
903 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
904 {
905         MonoAssembly *result;
906         char *fullpath, *filename;
907
908         result = invoke_assembly_preload_hook (aname, assemblies_path);
909         if (result)
910                 return result;
911         /* g_print ("loading %s\n", aname->name); */
912         /* special case corlib */
913         if (strcmp (aname->name, "mscorlib") == 0) {
914                 if (corlib) {
915                         /* g_print ("corlib already loaded\n"); */
916                         return corlib;
917                 }
918                 /* g_print ("corlib load\n"); */
919                 if (assemblies_path) {
920                         corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status);
921                         if (corlib)
922                                 return corlib;
923                 }
924                 corlib = load_in_path ("mscorlib.dll", default_path, status);
925                 return corlib;
926         }
927         result = search_loaded (aname);
928
929         if (result)
930                 return result;
931         /* g_print ("%s not found in cache\n", aname->name); */
932         if (strstr (aname->name, ".dll"))
933                 filename = g_strdup (aname->name);
934         else
935                 filename = g_strconcat (aname->name, ".dll", NULL);
936
937         result = mono_assembly_load_from_gac (aname, filename, status);
938         if (result) {
939                 g_free (filename);
940                 return result;
941         }
942
943         if (basedir) {
944                 fullpath = g_build_filename (basedir, filename, NULL);
945                 result = mono_assembly_open (fullpath, status);
946                 g_free (fullpath);
947                 if (result) {
948                         result->in_gac = FALSE;
949                         g_free (filename);
950                         return result;
951                 }
952         }
953         if (assemblies_path) {
954                 result = load_in_path (filename, (const char**)assemblies_path, status);
955                 if (result) {
956                         result->in_gac = FALSE;
957                         g_free (filename);
958                         return result;
959                 }
960         }
961         result = load_in_path (filename, default_path, status);
962         
963         if (result)
964                 result->in_gac = FALSE;
965         g_free (filename);
966         return result;
967 }
968
969 MonoAssembly*
970 mono_assembly_loaded (MonoAssemblyName *aname)
971 {
972         MonoAssembly *res;
973
974         EnterCriticalSection (&assemblies_mutex);
975         res = search_loaded (aname);
976         LeaveCriticalSection (&assemblies_mutex);
977
978         return res;
979 }
980
981 void
982 mono_assembly_close (MonoAssembly *assembly)
983 {
984         MonoImage *image;
985         int i;
986         
987         g_return_if_fail (assembly != NULL);
988
989         EnterCriticalSection (&assemblies_mutex);
990         if (--assembly->ref_count != 0) {
991                 LeaveCriticalSection (&assemblies_mutex);
992                 return;
993         }
994         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
995         LeaveCriticalSection (&assemblies_mutex);
996         image = assembly->image;
997         if (image->references) {
998                 for (i = 0; image->references [i] != NULL; i++)
999                         mono_image_close (image->references [i]->image);
1000                 g_free (image->references);
1001         }
1002
1003         mono_image_close (assembly->image);
1004
1005         g_free (assembly->basedir);
1006         g_free (assembly);
1007 }
1008
1009 MonoImage*
1010 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
1011 {
1012         MonoImageOpenStatus status;
1013         MonoImage *module;
1014
1015         module = mono_image_load_file_for_image (assembly->image, idx);
1016         if (module)
1017                 mono_assembly_load_references (module, &status);
1018
1019         return module;
1020 }
1021
1022 void
1023 mono_assembly_foreach (GFunc func, gpointer user_data)
1024 {
1025         GList *copy;
1026
1027         /*
1028      * We make a copy of the list to avoid calling the callback inside the 
1029          * lock, which could lead to deadlocks.
1030          */
1031         EnterCriticalSection (&assemblies_mutex);
1032         copy = g_list_copy (loaded_assemblies);
1033         LeaveCriticalSection (&assemblies_mutex);
1034
1035         g_list_foreach (loaded_assemblies, func, user_data);
1036
1037         g_list_free (copy);
1038 }
1039
1040 /* Holds the assembly of the application, for
1041  * System.Diagnostics.Process::MainModule
1042  */
1043 static MonoAssembly *main_assembly=NULL;
1044
1045 void
1046 mono_assembly_set_main (MonoAssembly *assembly)
1047 {
1048         main_assembly=assembly;
1049 }
1050
1051 MonoAssembly *
1052 mono_assembly_get_main (void)
1053 {
1054         return(main_assembly);
1055 }
1056
1057