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