2004-06-22 Sebastien Pouliot <sebastien@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 #ifdef WITH_BUNDLE
21 #include "mono-bundle.h"
22 #endif
23 #include <mono/metadata/loader.h>
24 #include <mono/metadata/tabledefs.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/io-layer/io-layer.h>
27 #include <mono/utils/mono-uri.h>
28 #include <mono/metadata/mono-config.h>
29 #include <mono/utils/mono-digest.h>
30 #include <mono/utils/mono-logger.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         const 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_PREFIX");
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                 gchar* token = g_malloc (8);
272                 gchar* encoded;
273
274                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
275                 int len = mono_metadata_decode_blob_size (aname->public_key, &aname->public_key);
276
277                 mono_digest_get_public_token (token, aname->public_key, len);
278                 encoded = encode_public_tok (token, 8);
279                 g_strlcpy (aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
280
281                 g_free (encoded);
282                 g_free (token);
283         }
284         else {
285                 aname->public_key = NULL;
286                 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
287         }
288
289         if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
290                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
291         }
292         else
293                 aname->public_key = 0;
294
295         return TRUE;
296 }
297
298 static gchar*
299 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
300 {
301         const gchar *public_tok;
302         int len;
303
304         public_tok = mono_metadata_blob_heap (image, key_index);
305         len = mono_metadata_decode_blob_size (public_tok, &public_tok);
306
307         if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
308                 gchar token [8];
309                 mono_digest_get_public_token (token, public_tok, len);
310                 return encode_public_tok (token, 8);
311         }
312
313         return encode_public_tok (public_tok, len);
314 }
315
316 void
317 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
318 {
319         MonoTableInfo *t;
320         guint32 cols [MONO_ASSEMBLYREF_SIZE];
321         const char *hash;
322         int i;
323         MonoAssembly **references = NULL;
324
325         *status = MONO_IMAGE_OK;
326         
327         /*
328          * image->references is shared between threads, so we need to access
329          * it inside a critical section.
330          */
331         EnterCriticalSection (&assemblies_mutex);
332         references = image->references;
333         LeaveCriticalSection (&assemblies_mutex);
334         if (references)
335                 return;
336
337         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
338
339         references = g_new0 (MonoAssembly *, t->rows + 1);
340
341         /*
342          * Load any assemblies this image references
343          */
344         for (i = 0; i < t->rows; i++) {
345                 MonoAssemblyName aname;
346
347                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
348                 
349                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
350                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
351                 aname.hash_value = hash;
352                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
353                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
354                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
355                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
356                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
357                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
358                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
359
360                 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
361                         gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname.flags);
362                         g_strlcpy (aname.public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
363                         g_free (token);
364                 } else {
365                         memset (aname.public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
366                 } 
367
368                 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
369
370                 if (references [i] == NULL){
371                         int j;
372
373                         /*
374                         ** Temporary work around: any System.* which are 3300 build, will get
375                         ** remapped, this is to keep old applications running that might have
376                         ** been linked against our 5000 API, before we were strongnamed, and
377                         ** hence were labeled as 3300 builds by reflection.c
378                         */
379                         if (aname.build == 3300 && strncmp (aname.name, "System", 6) == 0){
380                                 aname.build = 5000;
381                                 
382                                 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
383                         }
384                         if (references [i] != NULL){
385                                 if (g_getenv ("MONO_SILENT_WARNING") == NULL)
386                                         g_printerr ("Compat mode: the request from %s to load %s was remapped (http://www.go-mono.com/remap.html)\n",
387                                                  image->name, aname.name);
388                                 
389                         } else {
390                         
391                                 for (j = 0; j < i; j++)
392                                         mono_assembly_close (references [j]);
393                                 g_free (references);
394                                 
395                                 g_warning ("Could not find assembly %s, references from %s (assemblyref_index=%d)\n"
396                                            "     Major/Minor: %d,%d\n"
397                                            "     Build:       %d,%d\n"
398                                            "     Token:       %s\n",
399                                            aname.name, image->name, i,
400                                            aname.major, aname.minor, aname.build, aname.revision,
401                                            aname.public_key_token);
402                                 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
403                                 return;
404                         }
405                 }
406
407                 /* 
408                  * This check is disabled since lots of people seem to have an older
409                  * corlib which triggers this.
410                  */
411                 /* 
412                 if (image->references [i]->image == image)
413                         g_error ("Error: Assembly %s references itself", image->name);
414                 */
415         }
416         references [i] = NULL;
417
418         /* resolve assembly references for modules */
419         t = &image->tables [MONO_TABLE_MODULEREF];
420         for (i = 0; i < t->rows; i++){
421                 if (image->modules [i]) {
422                         image->modules [i]->assembly = image->assembly;
423                         mono_assembly_load_references (image->modules [i], status);
424                 }
425         }
426
427         EnterCriticalSection (&assemblies_mutex);
428         if (!image->references)
429                 image->references = references;
430         LeaveCriticalSection (&assemblies_mutex);
431
432         if (image->references != references) {
433                 /* Somebody loaded it before us */
434                 for (i = 0; i < t->rows; i++)
435                         mono_assembly_close (references [i]);
436                 g_free (references);
437         }
438 }
439
440 typedef struct AssemblyLoadHook AssemblyLoadHook;
441 struct AssemblyLoadHook {
442         AssemblyLoadHook *next;
443         MonoAssemblyLoadFunc func;
444         gpointer user_data;
445 };
446
447 AssemblyLoadHook *assembly_load_hook = NULL;
448
449 void
450 mono_assembly_invoke_load_hook (MonoAssembly *ass)
451 {
452         AssemblyLoadHook *hook;
453
454         for (hook = assembly_load_hook; hook; hook = hook->next) {
455                 hook->func (ass, hook->user_data);
456         }
457 }
458
459 void
460 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
461 {
462         AssemblyLoadHook *hook;
463         
464         g_return_if_fail (func != NULL);
465
466         hook = g_new0 (AssemblyLoadHook, 1);
467         hook->func = func;
468         hook->user_data = user_data;
469         hook->next = assembly_load_hook;
470         assembly_load_hook = hook;
471 }
472
473 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
474 struct AssemblyPreLoadHook {
475         AssemblyPreLoadHook *next;
476         MonoAssemblyPreLoadFunc func;
477         gpointer user_data;
478 };
479
480 AssemblyPreLoadHook *assembly_preload_hook = NULL;
481
482 static MonoAssembly *
483 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
484 {
485         AssemblyPreLoadHook *hook;
486         MonoAssembly *assembly;
487
488         for (hook = assembly_preload_hook; hook; hook = hook->next) {
489                 assembly = hook->func (aname, assemblies_path, hook->user_data);
490                 if (assembly != NULL)
491                         return assembly;
492         }
493
494         return NULL;
495 }
496
497 void
498 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
499 {
500         AssemblyPreLoadHook *hook;
501         
502         g_return_if_fail (func != NULL);
503
504         hook = g_new0 (AssemblyPreLoadHook, 1);
505         hook->func = func;
506         hook->user_data = user_data;
507         hook->next = assembly_preload_hook;
508         assembly_preload_hook = hook;
509 }
510
511 static gchar *
512 absolute_dir (const gchar *filename)
513 {
514         gchar *cwd;
515         gchar *mixed;
516         gchar **parts;
517         gchar *part;
518         GList *list, *tmp;
519         GString *result;
520         gchar *res;
521         gint i;
522
523         if (g_path_is_absolute (filename))
524                 return g_path_get_dirname (filename);
525
526         cwd = g_get_current_dir ();
527         mixed = g_build_filename (cwd, filename, NULL);
528         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
529         g_free (mixed);
530         g_free (cwd);
531
532         list = NULL;
533         for (i = 0; (part = parts [i]) != NULL; i++) {
534                 if (!strcmp (part, "."))
535                         continue;
536
537                 if (!strcmp (part, "..")) {
538                         if (list && list->next) /* Don't remove root */
539                                 list = g_list_delete_link (list, list);
540                 } else {
541                         list = g_list_prepend (list, part);
542                 }
543         }
544
545         result = g_string_new ("");
546         list = g_list_reverse (list);
547
548         /* Ignores last data pointer, which should be the filename */
549         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
550                 if (tmp->data)
551                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
552                                                                 G_DIR_SEPARATOR);
553         
554         res = result->str;
555         g_string_free (result, FALSE);
556         g_list_free (list);
557         g_strfreev (parts);
558         if (*res == '\0') {
559                 g_free (res);
560                 return g_strdup (".");
561         }
562
563         return res;
564 }
565
566 static MonoImage*
567 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
568 {
569         MonoImage *image = NULL;
570 #ifdef WITH_BUNDLE
571         int i;
572         char *name = g_path_get_basename (filename);
573         char *dot = strrchr (name, '.');
574         GList *tmp;
575         MonoAssembly *ass;
576         
577         if (dot)
578                 *dot = 0;
579         /* we do a very simple search for bundled assemblies: it's not a general 
580          * purpose assembly loading mechanism.
581          */
582         EnterCriticalSection (&assemblies_mutex);
583         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
584                 ass = tmp->data;
585                 if (!ass->aname.name)
586                         continue;
587                 if (strcmp (name, ass->aname.name))
588                         continue;
589                 image = ass->image;
590                 break;
591         }
592
593         for (i = 0; !image && bundled_assemblies [i]; ++i) {
594                 if (strcmp (bundled_assemblies [i]->name, name) == 0) {
595                         image = mono_image_open_from_data ((char*)bundled_assemblies [i]->data, bundled_assemblies [i]->size, FALSE, status);
596                         break;
597                 }
598         }
599         LeaveCriticalSection (&assemblies_mutex);
600         g_free (name);
601         if (image) {
602                 mono_image_addref (image);
603                 return image;
604         }
605 #endif
606         EnterCriticalSection (&assemblies_mutex);
607         image = mono_image_open (filename, status);
608         LeaveCriticalSection (&assemblies_mutex);
609
610         return image;
611 }
612
613 /**
614  * mono_assembly_open:
615  * @filename: Opens the assembly pointed out by this name
616  * @status: where a status code can be returned
617  *
618  * mono_assembly_open opens the PE-image pointed by @filename, and
619  * loads any external assemblies referenced by it.
620  *
621  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
622  * it. 
623  */
624 MonoAssembly *
625 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
626 {
627         MonoImage *image;
628         MonoAssembly *ass;
629         MonoImageOpenStatus def_status;
630         gchar *fname;
631         
632         g_return_val_if_fail (filename != NULL, NULL);
633
634         if (!status)
635                 status = &def_status;
636         *status = MONO_IMAGE_OK;
637
638         if (strncmp (filename, "file://", 7) == 0) {
639                 GError *error = NULL;
640                 gchar *uri = (gchar *) filename;
641                 gchar *tmpuri;
642
643                 /*
644                  * MS allows file://c:/... and fails on file://localhost/c:/... 
645                  * They also throw an IndexOutOfRangeException if "file://"
646                  */
647                 if (uri [7] != '/')
648                         uri = g_strdup_printf ("file:///%s", uri + 7);
649         
650                 tmpuri = uri;
651                 uri = mono_escape_uri_string (tmpuri);
652                 fname = g_filename_from_uri (uri, NULL, &error);
653                 g_free (uri);
654
655                 if (tmpuri != filename)
656                         g_free (tmpuri);
657
658                 if (error != NULL) {
659                         g_warning ("%s\n", error->message);
660                         g_error_free (error);
661                         fname = g_strdup (filename);
662                 }
663         } else {
664                 fname = g_strdup (filename);
665         }
666
667         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
668                         "Assembly Loader probing location: '%s'.", filename);
669         image = do_mono_assembly_open (fname, status);
670
671         if (!image){
672                 *status = MONO_IMAGE_ERROR_ERRNO;
673                 g_free (fname);
674                 return NULL;
675         }
676
677         ass = mono_assembly_load_from (image, fname, status);
678
679         if (ass) {
680                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
681                                 "Assembly Loader loaded assembly from location: '%s'.", filename);
682                 mono_config_for_assembly (ass->image);
683         }
684
685         g_free (fname);
686
687         return ass;
688 }
689
690 MonoAssembly *
691 mono_assembly_load_from (MonoImage *image, const char*fname, 
692                          MonoImageOpenStatus *status)
693 {
694         MonoAssembly *ass, *ass2;
695         char *base_dir;
696         GList *loading;
697
698 #if defined (PLATFORM_WIN32)
699         {
700                 gchar *tmp_fn;
701                 int i;
702
703                 tmp_fn = g_strdup (fname);
704                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
705                         if (tmp_fn [i] == '/')
706                                 tmp_fn [i] = '\\';
707                 }
708
709                 base_dir = absolute_dir (tmp_fn);
710                 g_free (tmp_fn);
711         }
712 #else
713         base_dir = absolute_dir (fname);
714 #endif
715
716         /*
717          * To avoid deadlocks and scalability problems, we load assemblies outside
718          * the assembly lock. This means that multiple threads might try to load
719          * the same assembly at the same time. The first one to load it completely
720          * "wins", the other threads free their copy and use the one loaded by
721          * the winning thread.
722          */
723
724         /*
725          * Create assembly struct, and enter it into the assembly cache
726          */
727         ass = g_new0 (MonoAssembly, 1);
728         ass->basedir = base_dir;
729         ass->image = image;
730
731         mono_assembly_fill_assembly_name (image, &ass->aname);
732
733         /* 
734          * Atomically search the loaded list and add ourselves to it if necessary.
735          */
736         EnterCriticalSection (&assemblies_mutex);
737         if (ass->aname.name)
738                 /* avoid loading the same assembly twice for now... */
739                 if ((ass2 = search_loaded (&ass->aname))) {
740                         g_free (ass);
741                         g_free (base_dir);
742                         mono_image_close (image);
743                         *status = MONO_IMAGE_OK;
744                         LeaveCriticalSection (&assemblies_mutex);
745                         return ass2;
746                 }
747         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
748         loading = g_list_prepend (loading, ass);
749         g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
750         LeaveCriticalSection (&assemblies_mutex);
751
752         image->assembly = ass;
753
754         /*
755          * We load referenced assemblies outside the lock to prevent deadlocks
756          * with regards to preload hooks.
757          */
758         mono_assembly_load_references (image, status);
759
760         EnterCriticalSection (&assemblies_mutex);
761
762         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
763         loading = g_list_remove (loading, ass);
764         if (loading == NULL)
765                 /* Prevent memory leaks */
766                 g_hash_table_remove (assemblies_loading, GetCurrentThread ());
767         else
768                 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
769         if (*status != MONO_IMAGE_OK) {
770                 LeaveCriticalSection (&assemblies_mutex);
771                 mono_assembly_close (ass);
772                 return NULL;
773         }
774
775         if (ass->aname.name) {
776                 ass2 = search_loaded (&ass->aname);
777                 if (ass2) {
778                         /* Somebody else has loaded the assembly before us */
779                         LeaveCriticalSection (&assemblies_mutex);
780                         mono_assembly_close (ass);
781                         return ass2;
782                 }
783         }
784
785         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
786         LeaveCriticalSection (&assemblies_mutex);
787
788         mono_assembly_invoke_load_hook (ass);
789
790         return ass;
791 }
792
793 static MonoAssembly*
794 probe_for_partial_name (const char *basepath, const char *fullname, MonoImageOpenStatus *status)
795 {
796         MonoAssembly *res = NULL;
797         gchar *fullpath;
798         GDir *dirhandle;
799         const char* direntry;
800
801         dirhandle = g_dir_open (basepath, 0, NULL);
802         if (!dirhandle)
803                 return NULL;
804
805         while ((direntry = g_dir_read_name (dirhandle))) {
806                 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
807                 res = mono_assembly_open (fullpath, status);
808                 g_free (fullpath);
809                 if (res)
810                         break;
811         }
812         g_dir_close (dirhandle);
813
814         return res;
815 }
816
817 MonoAssembly*
818 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
819 {
820         MonoAssembly *res;
821         MonoAssemblyName aname;
822         gchar *fullname, *gacpath;
823         gchar **paths;
824
825         memset (&aname, 0, sizeof (MonoAssemblyName));
826         aname.name = name;
827
828         res = invoke_assembly_preload_hook (&aname, assemblies_path);
829         if (res) {
830                 res->in_gac = FALSE;
831                 return res;
832         }
833
834         res = mono_assembly_loaded (&aname);
835         if (res)
836                 return res;
837
838         fullname = g_strdup_printf ("%s.dll", name);
839
840         if (extra_gac_paths) {
841                 paths = extra_gac_paths;
842                 while (!res && *paths) {
843                         gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", name, NULL);
844                         res = probe_for_partial_name (gacpath, fullname, status);
845                         g_free (gacpath);
846                         paths++;
847                 }
848         }
849
850         if (res) {
851                 res->in_gac = TRUE;
852                 g_free (fullname);
853                 return res;
854                 
855         }
856
857         gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", name, NULL);
858         res = probe_for_partial_name (gacpath, fullname, status);
859         g_free (gacpath);
860
861         if (res)
862                 res->in_gac = TRUE;
863
864         g_free (fullname);
865
866         return res;
867 }
868
869 /**
870  * mono_assembly_load_from_gac
871  *
872  * @aname: The assembly name object
873  */
874 static MonoAssembly*
875 mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImageOpenStatus *status)
876 {
877         MonoAssembly *result = NULL;
878         gchar *name, *version, *culture, *fullpath, *subpath;
879         gint32 len;
880         gchar **paths;
881
882         if (aname->public_key_token [0] == NULL) {
883                 return NULL;
884         }
885
886         if (strstr (aname->name, ".dll")) {
887                 len = strlen (filename) - 4;
888                 name = g_malloc (len);
889                 strncpy (name, aname->name, len);
890         } else {
891                 name = g_strdup (aname->name);
892         }
893
894         if (aname->culture) {
895                 culture = g_strdup (aname->culture);
896                 g_strdown (culture);
897         } else {
898                 culture = g_strdup ("");
899         }
900
901         version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
902                         aname->minor, aname->build, aname->revision,
903                         culture, aname->public_key_token);
904         
905         subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
906         g_free (name);
907         g_free (version);
908         g_free (culture);
909
910         if (extra_gac_paths) {
911                 paths = extra_gac_paths;
912                 while (!result && *paths) {
913                         fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
914                         result = mono_assembly_open (fullpath, status);
915                         g_free (fullpath);
916                         paths++;
917                 }
918         }
919
920         if (result) {
921                 result->in_gac = TRUE;
922                 g_free (subpath);
923                 return result;
924         }
925
926         fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
927                         "mono", "gac", subpath, NULL);
928         result = mono_assembly_open (fullpath, status);
929         g_free (fullpath);
930
931         if (result)
932                 result->in_gac = TRUE;
933         
934         g_free (subpath);
935
936         return result;
937 }
938
939         
940 MonoAssembly*
941 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
942 {
943         MonoAssembly *result;
944         char *fullpath, *filename;
945
946         result = invoke_assembly_preload_hook (aname, assemblies_path);
947         if (result) {
948                 result->in_gac = FALSE;
949                 return result;
950         }
951
952         result = mono_assembly_loaded (aname);
953         if (result)
954                 return result;
955
956         /* g_print ("loading %s\n", aname->name); */
957         /* special case corlib */
958         if (strcmp (aname->name, "mscorlib") == 0) {
959                 if (corlib) {
960                         /* g_print ("corlib already loaded\n"); */
961                         return corlib;
962                 }
963                 /* g_print ("corlib load\n"); */
964                 if (assemblies_path) {
965                         corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status);
966                         if (corlib)
967                                 return corlib;
968                 }
969                 corlib = load_in_path ("mscorlib.dll", default_path, status);
970                 return corlib;
971         }
972
973         if (strstr (aname->name, ".dll"))
974                 filename = g_strdup (aname->name);
975         else
976                 filename = g_strconcat (aname->name, ".dll", NULL);
977
978         result = mono_assembly_load_from_gac (aname, filename, status);
979         if (result) {
980                 g_free (filename);
981                 return result;
982         }
983
984         if (basedir) {
985                 fullpath = g_build_filename (basedir, filename, NULL);
986                 result = mono_assembly_open (fullpath, status);
987                 g_free (fullpath);
988                 if (result) {
989                         result->in_gac = FALSE;
990                         g_free (filename);
991                         return result;
992                 }
993         }
994
995         result = load_in_path (filename, default_path, status);
996         if (result)
997                 result->in_gac = FALSE;
998         g_free (filename);
999         return result;
1000 }
1001
1002 MonoAssembly*
1003 mono_assembly_loaded (MonoAssemblyName *aname)
1004 {
1005         MonoAssembly *res;
1006
1007         EnterCriticalSection (&assemblies_mutex);
1008         res = search_loaded (aname);
1009         LeaveCriticalSection (&assemblies_mutex);
1010
1011         return res;
1012 }
1013
1014 void
1015 mono_assembly_close (MonoAssembly *assembly)
1016 {
1017         MonoImage *image;
1018         int i;
1019         
1020         g_return_if_fail (assembly != NULL);
1021
1022         EnterCriticalSection (&assemblies_mutex);
1023         if (--assembly->ref_count != 0) {
1024                 LeaveCriticalSection (&assemblies_mutex);
1025                 return;
1026         }
1027         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
1028         LeaveCriticalSection (&assemblies_mutex);
1029         image = assembly->image;
1030         if (image->references) {
1031                 for (i = 0; image->references [i] != NULL; i++)
1032                         mono_image_close (image->references [i]->image);
1033                 g_free (image->references);
1034         }
1035
1036         mono_image_close (assembly->image);
1037
1038         g_free (assembly->basedir);
1039         g_free (assembly);
1040 }
1041
1042 MonoImage*
1043 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
1044 {
1045         MonoImageOpenStatus status;
1046         MonoImage *module;
1047
1048         module = mono_image_load_file_for_image (assembly->image, idx);
1049         if (module)
1050                 mono_assembly_load_references (module, &status);
1051
1052         return module;
1053 }
1054
1055 void
1056 mono_assembly_foreach (GFunc func, gpointer user_data)
1057 {
1058         GList *copy;
1059
1060         /*
1061      * We make a copy of the list to avoid calling the callback inside the 
1062          * lock, which could lead to deadlocks.
1063          */
1064         EnterCriticalSection (&assemblies_mutex);
1065         copy = g_list_copy (loaded_assemblies);
1066         LeaveCriticalSection (&assemblies_mutex);
1067
1068         g_list_foreach (loaded_assemblies, func, user_data);
1069
1070         g_list_free (copy);
1071 }
1072
1073 /* Holds the assembly of the application, for
1074  * System.Diagnostics.Process::MainModule
1075  */
1076 static MonoAssembly *main_assembly=NULL;
1077
1078 void
1079 mono_assembly_set_main (MonoAssembly *assembly)
1080 {
1081         main_assembly=assembly;
1082 }
1083
1084 MonoAssembly *
1085 mono_assembly_get_main (void)
1086 {
1087         return(main_assembly);
1088 }
1089
1090 MonoImage*
1091 mono_assembly_get_image (MonoAssembly *assembly)
1092 {
1093         return assembly->image;
1094 }
1095