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