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