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