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