2003-12-20 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  * TODO:
10  *   Implement big-endian versions of the reading routines.
11  */
12 #include <config.h>
13 #include <stdio.h>
14 #include <glib.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include "assembly.h"
19 #include "image.h"
20 #include "cil-coff.h"
21 #include "rawbuffer.h"
22 #ifdef WITH_BUNDLE
23 #include "mono-bundle.h"
24 #endif
25 #include <mono/io-layer/io-layer.h>
26
27 /* the default search path is just MONO_ASSEMBLIES */
28 static const char*
29 default_path [] = {
30         MONO_ASSEMBLIES,
31         NULL
32 };
33
34 static char **assemblies_path = NULL;
35
36 /*
37  * keeps track of loaded assemblies
38  */
39 static GList *loaded_assemblies = NULL;
40 static MonoAssembly *corlib;
41
42 /* This protects loaded_assemblies and image->references */
43 static CRITICAL_SECTION assemblies_mutex;
44
45 /* A hastable of thread->assembly list mappings */
46 static GHashTable *assemblies_loading;
47
48 #ifdef PLATFORM_WIN32
49
50 static void
51 init_default_path (void)
52 {
53         int i;
54
55         default_path [0] = g_strdup (MONO_ASSEMBLIES);
56         for (i = strlen (MONO_ASSEMBLIES) - 1; i >= 0; i--) {
57                 if (default_path [0][i] == '/')
58                         ((char*) default_path [0])[i] = '\\';
59         }
60 }
61 #endif
62
63 static void
64 check_env (void) {
65         const char *path;
66         char **splitted;
67         
68         path = getenv ("MONO_PATH");
69         if (!path)
70                 return;
71         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
72         if (assemblies_path)
73                 g_strfreev (assemblies_path);
74         assemblies_path = splitted;
75 }
76
77 /* assemblies_mutex must be held by the caller */
78 static MonoAssembly*
79 search_loaded (MonoAssemblyName* aname)
80 {
81         GList *tmp;
82         MonoAssembly *ass;
83         GList *loading;
84         
85         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
86                 ass = tmp->data;
87                 if (!ass->aname.name)
88                         continue;
89                 /* we just compare the name, but later we'll do all the checks */
90                 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
91                 if (strcmp (aname->name, ass->aname.name))
92                         continue;
93                 /* g_print ("success\n"); */
94
95                 return ass;
96         }
97
98         /*
99          * The assembly might be under load by this thread. In this case, it is
100          * safe to return an incomplete instance to prevent loops.
101          */
102         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
103         for (tmp = loading; tmp; tmp = tmp->next) {
104                 ass = tmp->data;
105                 if (!ass->aname.name)
106                         continue;
107                 if (strcmp (aname->name, ass->aname.name))
108                         continue;
109
110                 return ass;
111         }
112
113         return NULL;
114 }
115
116 static MonoAssembly *
117 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
118 {
119         int i;
120         char *fullpath;
121         MonoAssembly *result;
122
123         for (i = 0; search_path [i]; ++i) {
124                 fullpath = g_build_filename (search_path [i], basename, NULL);
125                 result = mono_assembly_open (fullpath, status);
126                 g_free (fullpath);
127                 if (result)
128                         return result;
129         }
130         return NULL;
131 }
132
133 /**
134  * mono_assembly_setrootdir:
135  * @root_dir: The pathname of the root directory where we will locate assemblies
136  *
137  * This routine sets the internal default root directory for looking up
138  * assemblies.  This is used by Windows installations to compute dynamically
139  * the place where the Mono assemblies are located.
140  *
141  */
142 void
143 mono_assembly_setrootdir (const char *root_dir)
144 {
145         /*
146          * Override the MONO_ASSEMBLIES directory configured at compile time.
147          */
148         /* Leak if called more than once */
149         default_path [0] = g_strdup (root_dir);
150 }
151
152 /**
153  * mono_assemblies_init:
154  *
155  *  Initialize global variables used by this module.
156  */
157 void
158 mono_assemblies_init (void)
159 {
160 #ifdef PLATFORM_WIN32
161         init_default_path ();
162 #endif
163
164         check_env ();
165
166         InitializeCriticalSection (&assemblies_mutex);
167
168         assemblies_loading = g_hash_table_new (NULL, NULL);
169 }
170
171 gboolean
172 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
173 {
174         MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
175         guint32 cols [MONO_ASSEMBLY_SIZE];
176
177         if (!t->rows)
178                 return FALSE;
179
180         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
181
182         aname->hash_len = 0;
183         aname->hash_value = NULL;
184         aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
185         aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
186         aname->flags = cols [MONO_ASSEMBLY_FLAGS];
187         aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
188         aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
189         aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
190         aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
191         aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
192         if (cols [MONO_ASSEMBLY_PUBLIC_KEY])
193                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
194         else
195                 aname->public_key = 0;
196
197         return TRUE;
198 }
199
200 void
201 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
202 {
203         MonoTableInfo *t;
204         guint32 cols [MONO_ASSEMBLYREF_SIZE];
205         const char *hash;
206         int i;
207
208         *status = MONO_IMAGE_OK;
209
210         if (image->references)
211                 return;
212
213         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
214
215         image->references = g_new0 (MonoAssembly *, t->rows + 1);
216
217         /*
218          * Load any assemblies this image references
219          */
220         for (i = 0; i < t->rows; i++) {
221                 MonoAssemblyName aname;
222
223                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
224                 
225                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
226                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
227                 aname.hash_value = hash;
228                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
229                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
230                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
231                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
232                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
233                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
234                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
235
236                 image->references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
237
238                 if (image->references [i] == NULL){
239                         int j;
240                         
241                         for (j = 0; j < i; j++)
242                                 mono_assembly_close (image->references [j]);
243                         g_free (image->references);
244
245                         g_warning ("Could not find assembly %s", aname.name);
246                         *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
247                         return;
248                 }
249
250                 /* 
251                  * This check is disabled since lots of people seem to have an older
252                  * corlib which triggers this.
253                  */
254                 /* 
255                 if (image->references [i]->image == image)
256                         g_error ("Error: Assembly %s references itself", image->name);
257                 */
258         }
259         image->references [i] = NULL;
260
261         /* resolve assembly references for modules */
262         t = &image->tables [MONO_TABLE_MODULEREF];
263         for (i = 0; i < t->rows; i++){
264                 if (image->modules [i]) {
265                         image->modules [i]->assembly = image->assembly;
266                         mono_assembly_load_references (image->modules [i], status);
267                 }
268         }
269 }
270
271 typedef struct AssemblyLoadHook AssemblyLoadHook;
272 struct AssemblyLoadHook {
273         AssemblyLoadHook *next;
274         MonoAssemblyLoadFunc func;
275         gpointer user_data;
276 };
277
278 AssemblyLoadHook *assembly_load_hook = NULL;
279
280 void
281 mono_assembly_invoke_load_hook (MonoAssembly *ass)
282 {
283         AssemblyLoadHook *hook;
284
285         for (hook = assembly_load_hook; hook; hook = hook->next) {
286                 hook->func (ass, hook->user_data);
287         }
288 }
289
290 void
291 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
292 {
293         AssemblyLoadHook *hook;
294         
295         g_return_if_fail (func != NULL);
296
297         hook = g_new0 (AssemblyLoadHook, 1);
298         hook->func = func;
299         hook->user_data = user_data;
300         hook->next = assembly_load_hook;
301         assembly_load_hook = hook;
302 }
303
304 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
305 struct AssemblyPreLoadHook {
306         AssemblyPreLoadHook *next;
307         MonoAssemblyPreLoadFunc func;
308         gpointer user_data;
309 };
310
311 AssemblyPreLoadHook *assembly_preload_hook = NULL;
312
313 static MonoAssembly *
314 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
315 {
316         AssemblyPreLoadHook *hook;
317         MonoAssembly *assembly;
318
319         for (hook = assembly_preload_hook; hook; hook = hook->next) {
320                 assembly = hook->func (aname, assemblies_path, hook->user_data);
321                 if (assembly != NULL)
322                         return assembly;
323         }
324
325         return NULL;
326 }
327
328 void
329 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
330 {
331         AssemblyPreLoadHook *hook;
332         
333         g_return_if_fail (func != NULL);
334
335         hook = g_new0 (AssemblyPreLoadHook, 1);
336         hook->func = func;
337         hook->user_data = user_data;
338         hook->next = assembly_preload_hook;
339         assembly_preload_hook = hook;
340 }
341
342 static gchar *
343 absolute_dir (const gchar *filename)
344 {
345         gchar *cwd;
346         gchar *mixed;
347         gchar **parts;
348         gchar *part;
349         GList *list, *tmp;
350         GString *result;
351         gchar *res;
352         gint i;
353
354         if (g_path_is_absolute (filename))
355                 return g_path_get_dirname (filename);
356
357         cwd = g_get_current_dir ();
358         mixed = g_build_filename (cwd, filename, NULL);
359         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
360         g_free (mixed);
361         g_free (cwd);
362
363         list = NULL;
364         for (i = 0; (part = parts [i]) != NULL; i++) {
365                 if (!strcmp (part, "."))
366                         continue;
367
368                 if (!strcmp (part, "..")) {
369                         if (list && list->next) /* Don't remove root */
370                                 list = g_list_delete_link (list, list);
371                 } else {
372                         list = g_list_prepend (list, part);
373                 }
374         }
375
376         result = g_string_new ("");
377         list = g_list_reverse (list);
378
379         /* Ignores last data pointer, which should be the filename */
380         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
381                 if (tmp->data)
382                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
383                                                                 G_DIR_SEPARATOR);
384         
385         res = result->str;
386         g_string_free (result, FALSE);
387         g_list_free (list);
388         g_strfreev (parts);
389         if (*res == '\0') {
390                 g_free (res);
391                 return g_strdup (".");
392         }
393
394         return res;
395 }
396
397 static MonoImage*
398 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
399 {
400         MonoImage *image = NULL;
401 #ifdef WITH_BUNDLE
402         int i;
403         char *name = g_path_get_basename (filename);
404         char *dot = strrchr (name, '.');
405         GList *tmp;
406         MonoAssembly *ass;
407         
408         if (dot)
409                 *dot = 0;
410         if (strcmp (name, "corlib") == 0) {
411                 g_free (name);
412                 name = g_strdup ("mscorlib");
413         }
414         /* we do a very simple search for bundled assemblies: it's not a general 
415          * purpose assembly loading mechanism.
416          */
417         EnterCriticalSection (&assemblies_mutex);
418         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
419                 ass = tmp->data;
420                 if (!ass->aname.name)
421                         continue;
422                 if (strcmp (name, ass->aname.name))
423                         continue;
424                 image = ass->image;
425                 break;
426         }
427         LeaveCriticalSection (&assemblies_mutex);
428         for (i = 0; !image && bundled_assemblies [i]; ++i) {
429                 if (strcmp (bundled_assemblies [i]->name, name) == 0) {
430                         image = mono_image_open_from_data ((char*)bundled_assemblies [i]->data, bundled_assemblies [i]->size, FALSE, status);
431                         break;
432                 }
433         }
434         g_free (name);
435         if (image) {
436                 InterlockedIncrement (&image->ref_count);
437                 return image;
438         }
439 #endif
440         image = mono_image_open (filename, status);
441         return image;
442 }
443
444 /**
445  * mono_assembly_open:
446  * @filename: Opens the assembly pointed out by this name
447  * @status: where a status code can be returned
448  *
449  * mono_assembly_open opens the PE-image pointed by @filename, and
450  * loads any external assemblies referenced by it.
451  *
452  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
453  * it. 
454  */
455 MonoAssembly *
456 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
457 {
458         MonoImage *image;
459         MonoAssembly *ass;
460         MonoImageOpenStatus def_status;
461         gchar *fname;
462         
463         g_return_val_if_fail (filename != NULL, NULL);
464
465         if (!status)
466                 status = &def_status;
467         *status = MONO_IMAGE_OK;
468
469         if (strncmp (filename, "file://", 7) == 0) {
470                 GError *error = NULL;
471                 gchar *uri = (gchar *) filename;
472
473                 /*
474                  * MS allows file://c:/... and fails on file://localhost/c:/... 
475                  * They also throw an IndexOutOfRangeException if "file://"
476                  */
477                 if (uri [7] != '/')
478                         uri = g_strdup_printf ("file:///%s", uri + 7);
479         
480                 fname = g_filename_from_uri (uri, NULL, &error);
481                 if (uri != filename)
482                         g_free (uri);
483
484                 if (error != NULL) {
485                         g_warning ("%s\n", error->message);
486                         g_error_free (error);
487                         fname = g_strdup (filename);
488                 }
489         } else {
490                 fname = g_strdup (filename);
491         }
492
493         /* g_print ("file loading %s\n", fname); */
494         image = do_mono_assembly_open (fname, status);
495
496         if (!image){
497                 *status = MONO_IMAGE_ERROR_ERRNO;
498                 g_free (fname);
499                 return NULL;
500         }
501
502         ass = mono_assembly_load_from (image, fname, status);
503
504         g_free (fname);
505
506         return ass;
507 }
508
509 MonoAssembly *
510 mono_assembly_load_from (MonoImage *image, const char*fname, 
511                                                  MonoImageOpenStatus *status)
512 {
513         MonoAssembly *ass, *ass2;
514         char *base_dir;
515         GList *loading;
516
517 #if defined (PLATFORM_WIN32)
518         {
519                 gchar *tmp_fn;
520                 int i;
521
522                 tmp_fn = g_strdup (fname);
523                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
524                         if (tmp_fn [i] == '/')
525                                 tmp_fn [i] = '\\';
526                 }
527
528                 base_dir = absolute_dir (tmp_fn);
529                 g_free (tmp_fn);
530         }
531 #else
532         base_dir = absolute_dir (fname);
533 #endif
534
535         /*
536          * Create assembly struct, and enter it into the assembly cache
537          */
538         ass = g_new0 (MonoAssembly, 1);
539         ass->basedir = base_dir;
540         ass->image = image;
541
542         mono_assembly_fill_assembly_name (image, &ass->aname);
543
544         /* 
545          * Atomically search the loaded list and add ourselves to it if necessary.
546          */
547         EnterCriticalSection (&assemblies_mutex);
548         if (ass->aname.name)
549                 /* avoid loading the same assembly twice for now... */
550                 if ((ass2 = search_loaded (&ass->aname))) {
551                         g_free (ass);
552                         g_free (base_dir);
553                         mono_image_close (image);
554                         *status = MONO_IMAGE_OK;
555                         LeaveCriticalSection (&assemblies_mutex);
556                         return ass2;
557                 }
558         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
559         loading = g_list_prepend (loading, ass);
560         g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
561         LeaveCriticalSection (&assemblies_mutex);
562
563         image->assembly = ass;
564
565         /*
566          * We load referenced assemblies outside the lock to prevent deadlocks
567          * with regards to preload hooks.
568          */
569         mono_assembly_load_references (image, status);
570
571         EnterCriticalSection (&assemblies_mutex);
572
573         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
574         loading = g_list_remove (loading, ass);
575         if (loading == NULL)
576                 /* Prevent memory leaks */
577                 g_hash_table_remove (assemblies_loading, GetCurrentThread ());
578         else
579                 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
580         if (*status != MONO_IMAGE_OK) {
581                 LeaveCriticalSection (&assemblies_mutex);
582                 mono_assembly_close (ass);
583                 return NULL;
584         }
585
586         if (ass->aname.name) {
587                 ass2 = search_loaded (&ass->aname);
588                 if (ass2) {
589                         /* Somebody else has loaded the assembly before us */
590                         LeaveCriticalSection (&assemblies_mutex);
591                         mono_assembly_close (ass);
592                         return ass2;
593                 }
594         }
595
596         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
597         LeaveCriticalSection (&assemblies_mutex);
598
599         mono_assembly_invoke_load_hook (ass);
600
601         return ass;
602 }
603
604 MonoAssembly*
605 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
606 {
607         MonoAssembly *result;
608         char *fullpath, *filename;
609
610         result = invoke_assembly_preload_hook (aname, assemblies_path);
611         if (result)
612                 return result;
613
614         /* g_print ("loading %s\n", aname->name); */
615         /* special case corlib */
616         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
617                 if (corlib) {
618                         /* g_print ("corlib already loaded\n"); */
619                         return corlib;
620                 }
621                 /* g_print ("corlib load\n"); */
622                 if (assemblies_path) {
623                         corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status);
624                         if (corlib)
625                                 return corlib;
626                         corlib = load_in_path ("corlib.dll", (const char**)assemblies_path, status);
627                         if (corlib)
628                                 return corlib;
629                 }
630                 corlib = load_in_path ("mscorlib.dll", default_path, status);
631                 if (!corlib)
632                         corlib = load_in_path ("corlib.dll", default_path, status);
633                 return corlib;
634         }
635         result = search_loaded (aname);
636         if (result)
637                 return result;
638         /* g_print ("%s not found in cache\n", aname->name); */
639         if (strstr (aname->name, ".dll"))
640                 filename = g_strdup (aname->name);
641         else
642                 filename = g_strconcat (aname->name, ".dll", NULL);
643         if (basedir) {
644                 fullpath = g_build_filename (basedir, filename, NULL);
645                 result = mono_assembly_open (fullpath, status);
646                 g_free (fullpath);
647                 if (result) {
648                         g_free (filename);
649                         return result;
650                 }
651         }
652         if (assemblies_path) {
653                 result = load_in_path (filename, (const char**)assemblies_path, status);
654                 if (result) {
655                         g_free (filename);
656                         return result;
657                 }
658         }
659         result = load_in_path (filename, default_path, status);
660         g_free (filename);
661         return result;
662 }
663
664 void
665 mono_assembly_close (MonoAssembly *assembly)
666 {
667         MonoImage *image;
668         int i;
669         
670         g_return_if_fail (assembly != NULL);
671
672         EnterCriticalSection (&assemblies_mutex);
673         if (--assembly->ref_count != 0) {
674                 LeaveCriticalSection (&assemblies_mutex);
675                 return;
676         }
677         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
678         LeaveCriticalSection (&assemblies_mutex);
679         image = assembly->image;
680         if (image->references) {
681                 for (i = 0; image->references [i] != NULL; i++)
682                         mono_image_close (image->references [i]->image);
683                 g_free (image->references);
684         }
685
686         mono_image_close (assembly->image);
687
688         g_free (assembly->basedir);
689         g_free (assembly);
690 }
691
692 MonoImage*
693 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
694 {
695         MonoImageOpenStatus status;
696         MonoImage *module;
697
698         module = mono_image_load_file_for_image (assembly->image, idx);
699         if (module)
700                 mono_assembly_load_references (module, &status);
701
702         return module;
703 }
704
705 void
706 mono_assembly_foreach (GFunc func, gpointer user_data)
707 {
708         GList *copy;
709
710         /*
711      * We make a copy of the list to avoid calling the callback inside the 
712          * lock, which could lead to deadlocks.
713          */
714         EnterCriticalSection (&assemblies_mutex);
715         copy = g_list_copy (loaded_assemblies);
716         LeaveCriticalSection (&assemblies_mutex);
717
718         g_list_foreach (loaded_assemblies, func, user_data);
719
720         g_list_free (copy);
721 }
722
723 /* Holds the assembly of the application, for
724  * System.Diagnostics.Process::MainModule
725  */
726 static MonoAssembly *main_assembly=NULL;
727
728 void
729 mono_assembly_set_main (MonoAssembly *assembly)
730 {
731         main_assembly=assembly;
732 }
733
734 MonoAssembly *
735 mono_assembly_get_main (void)
736 {
737         return(main_assembly);
738 }