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