Tue May 13 16:41:49 CEST 2003 Paolo Molaro <lupus@ximian.com>
[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
23 /* the default search path is just MONO_ASSEMBLIES */
24 static const char*
25 default_path [] = {
26         MONO_ASSEMBLIES,
27         NULL
28 };
29
30 static char **assemblies_path = NULL;
31 static int env_checked = 0;
32
33 #ifdef PLATFORM_WIN32
34 static gboolean path_inited = FALSE;
35
36 static void
37 init_default_path (void)
38 {
39         int i;
40
41         if (path_inited)
42                 return;
43         
44         path_inited = TRUE;
45         default_path [0] = g_strdup (MONO_ASSEMBLIES);
46         for (i = strlen (MONO_ASSEMBLIES) - 1; i >= 0; i--) {
47                 if (default_path [0][i] == '/')
48                         ((char*) default_path [0])[i] = '\\';
49         }
50 }
51 #endif
52
53 static void
54 check_env (void) {
55         const char *path;
56         char **splitted;
57         
58         if (env_checked)
59                 return;
60         env_checked = 1;
61
62         path = getenv ("MONO_PATH");
63         if (!path)
64                 return;
65         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
66         if (assemblies_path)
67                 g_strfreev (assemblies_path);
68         assemblies_path = splitted;
69 }
70
71 /*
72  * keeps track of loaded assemblies
73  */
74 static GList *loaded_assemblies = NULL;
75 static MonoAssembly *corlib;
76
77 static MonoAssembly*
78 search_loaded (MonoAssemblyName* aname)
79 {
80         GList *tmp;
81         MonoAssembly *ass;
82         
83         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
84                 ass = tmp->data;
85                 if (!ass->aname.name)
86                         continue;
87                 /* we just compare the name, but later we'll do all the checks */
88                 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
89                 if (strcmp (aname->name, ass->aname.name))
90                         continue;
91                 /* g_print ("success\n"); */
92                 return ass;
93         }
94         return NULL;
95 }
96
97 static MonoAssembly *
98 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
99 {
100         int i;
101         char *fullpath;
102         MonoAssembly *result;
103
104         for (i = 0; search_path [i]; ++i) {
105                 fullpath = g_build_filename (search_path [i], basename, NULL);
106                 result = mono_assembly_open (fullpath, status);
107                 g_free (fullpath);
108                 if (result)
109                         return result;
110         }
111         return NULL;
112 }
113
114 /**
115  * mono_assembly_setrootdir:
116  * @root_dir: The pathname of the root directory where we will locate assemblies
117  *
118  * This routine sets the internal default root directory for looking up
119  * assemblies.  This is used by Windows installations to compute dynamically
120  * the place where the Mono assemblies are located.
121  *
122  */
123 void
124 mono_assembly_setrootdir (const char *root_dir)
125 {
126         /*
127          * Override the MONO_ASSEMBLIES directory configured at compile time.
128          */
129         default_path [0] = g_strdup (root_dir);
130 #ifdef PLATFORM_WIN32
131         path_inited = TRUE;
132 #endif
133 }
134
135 void
136 mono_image_load_references (MonoImage *image, MonoImageOpenStatus *status) {
137         MonoTableInfo *t;
138         guint32 cols [MONO_ASSEMBLYREF_SIZE];
139         const char *hash;
140         int i;
141
142         if (image->references)
143                 return;
144
145         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
146
147         image->references = g_new0 (MonoAssembly *, t->rows + 1);
148
149         /*
150          * Load any assemblies this image references
151          */
152         for (i = 0; i < t->rows; i++) {
153                 MonoAssemblyName aname;
154
155                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
156                 
157                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
158                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
159                 aname.hash_value = hash;
160                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
161                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
162                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
163                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
164                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
165                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
166                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
167
168                 image->references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
169
170                 if (image->references [i] == NULL){
171                         int j;
172                         
173                         for (j = 0; j < i; j++)
174                                 mono_assembly_close (image->references [j]);
175                         g_free (image->references);
176                         image->references = NULL;
177
178                         g_warning ("Could not find assembly %s", aname.name);
179                         *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
180                         return;
181                 }
182         }
183         image->references [i] = NULL;
184
185 }
186
187 typedef struct AssemblyLoadHook AssemblyLoadHook;
188 struct AssemblyLoadHook {
189         AssemblyLoadHook *next;
190         MonoAssemblyLoadFunc func;
191         gpointer user_data;
192 };
193
194 AssemblyLoadHook *assembly_load_hook = NULL;
195
196 void
197 mono_assembly_invoke_load_hook (MonoAssembly *ass)
198 {
199         AssemblyLoadHook *hook;
200
201         for (hook = assembly_load_hook; hook; hook = hook->next) {
202                 hook->func (ass, hook->user_data);
203         }
204 }
205
206 void
207 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
208 {
209         AssemblyLoadHook *hook;
210         
211         g_return_if_fail (func != NULL);
212
213         hook = g_new0 (AssemblyLoadHook, 1);
214         hook->func = func;
215         hook->user_data = user_data;
216         hook->next = assembly_load_hook;
217         assembly_load_hook = hook;
218 }
219
220 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
221 struct AssemblyPreLoadHook {
222         AssemblyPreLoadHook *next;
223         MonoAssemblyPreLoadFunc func;
224         gpointer user_data;
225 };
226
227 AssemblyPreLoadHook *assembly_preload_hook = NULL;
228
229 static MonoAssembly *
230 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
231 {
232         AssemblyPreLoadHook *hook;
233         MonoAssembly *assembly;
234
235         for (hook = assembly_preload_hook; hook; hook = hook->next) {
236                 assembly = hook->func (aname, assemblies_path, hook->user_data);
237                 if (assembly != NULL)
238                         return assembly;
239         }
240
241         return NULL;
242 }
243
244 void
245 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
246 {
247         AssemblyPreLoadHook *hook;
248         
249         g_return_if_fail (func != NULL);
250
251         hook = g_new0 (AssemblyPreLoadHook, 1);
252         hook->func = func;
253         hook->user_data = user_data;
254         hook->next = assembly_preload_hook;
255         assembly_preload_hook = hook;
256 }
257
258 static gchar *
259 absolute_dir (const gchar *filename)
260 {
261         gchar *cwd;
262         gchar *mixed;
263         gchar **parts;
264         gchar *part;
265         GSList *list, *tmp;
266         GString *result;
267         gchar *res;
268         gint i;
269
270         if (g_path_is_absolute (filename))
271                 return g_path_get_dirname (filename);
272
273         cwd = g_get_current_dir ();
274         mixed = g_build_filename (cwd, filename, NULL);
275         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
276         g_free (mixed);
277         g_free (cwd);
278
279         list = NULL;
280         for (i = 0; (part = parts [i]) != NULL; i++) {
281                 if (!strcmp (part, "."))
282                         continue;
283
284                 if (!strcmp (part, "..")) {
285                         if (list && list->next) /* Don't remove root */
286                                 list = g_slist_delete_link (list, list);
287                 } else {
288                         list = g_slist_prepend (list, part);
289                 }
290         }
291
292         result = g_string_new ("");
293         list = g_slist_reverse (list);
294
295         /* Ignores last data pointer, which should be the filename */
296         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
297                 if (tmp->data)
298                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
299                                                                 G_DIR_SEPARATOR);
300         
301         res = result->str;
302         g_string_free (result, FALSE);
303         g_slist_free (list);
304         g_strfreev (parts);
305         if (*res == '\0') {
306                 g_free (res);
307                 return g_strdup (".");
308         }
309
310         return res;
311 }
312
313 /**
314  * mono_assembly_open:
315  * @filename: Opens the assembly pointed out by this name
316  * @status: where a status code can be returned
317  *
318  * mono_assembly_open opens the PE-image pointed by @filename, and
319  * loads any external assemblies referenced by it.
320  *
321  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
322  * it. 
323  */
324 MonoAssembly *
325 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
326 {
327         MonoAssembly *ass, *ass2;
328         MonoImage *image;
329         MonoTableInfo *t;
330         guint32 cols [MONO_ASSEMBLY_SIZE];
331         int i;
332         char *base_dir, *aot_name;
333         MonoImageOpenStatus def_status;
334         gchar *fname;
335         
336         g_return_val_if_fail (filename != NULL, NULL);
337
338         if (!status)
339                 status = &def_status;
340         *status = MONO_IMAGE_OK;
341
342         if (strncmp (filename, "file://", 7) == 0) {
343                 GError *error = NULL;
344                 gchar *uri = (gchar *) filename;
345
346                 /*
347                  * MS allows file://c:/... and fails on file://localhost/c:/... 
348                  * They also throw an IndexOutOfRangeException if "file://"
349                  */
350                 if (uri [7] != '/')
351                         uri = g_strdup_printf ("file:///%s", uri + 7);
352                 
353                 fname = g_filename_from_uri (uri, NULL, &error);
354                 if (uri != filename)
355                         g_free (uri);
356
357                 if (error != NULL) {
358                         g_warning ("%s\n", error->message);
359                         g_error_free (error);
360                         fname = g_strdup (filename);
361                 }
362         } else {
363                 fname = g_strdup (filename);
364         }
365
366         /* g_print ("file loading %s\n", fname); */
367         image = mono_image_open (fname, status);
368
369         if (!image){
370                 *status = MONO_IMAGE_ERROR_ERRNO;
371                 g_free (fname);
372                 return NULL;
373         }
374
375 #if defined (PLATFORM_WIN32)
376         {
377                 gchar *tmp_fn;
378                 tmp_fn = g_strdup (fname);
379                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
380                         if (tmp_fn [i] == '/')
381                                 tmp_fn [i] = '\\';
382                 }
383
384                 base_dir = absolute_dir (tmp_fn);
385                 g_free (tmp_fn);
386         }
387 #else
388         base_dir = absolute_dir (fname);
389 #endif
390
391         /*
392          * Create assembly struct, and enter it into the assembly cache
393          */
394         ass = g_new0 (MonoAssembly, 1);
395         ass->basedir = base_dir;
396         ass->image = image;
397
398         /* load aot compiled module */
399         aot_name = g_strdup_printf ("%s.so", fname);
400         g_free (fname);
401         ass->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
402         g_free (aot_name);
403
404         if (ass->aot_module) {
405                 char *saved_guid = NULL;
406                 g_module_symbol (ass->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
407
408                 if (!saved_guid || strcmp (image->guid, saved_guid)) {
409                         g_module_close (ass->aot_module);
410                         ass->aot_module = NULL;
411                 }
412         }
413
414         t = &image->tables [MONO_TABLE_ASSEMBLY];
415         if (t->rows) {
416                 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
417                 
418                 ass->aname.hash_len = 0;
419                 ass->aname.hash_value = NULL;
420                 ass->aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
421                 ass->aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
422                 ass->aname.flags = cols [MONO_ASSEMBLY_FLAGS];
423                 ass->aname.major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
424                 ass->aname.minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
425                 ass->aname.build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
426                 ass->aname.revision = cols [MONO_ASSEMBLY_REV_NUMBER];
427
428                 /* avoid loading the same assembly twice for now... */
429                 if ((ass2 = search_loaded (&ass->aname))) {
430                         g_free (ass);
431                         g_free (base_dir);
432                         *status = MONO_IMAGE_OK;
433                         return ass2;
434                 }
435         }
436
437         image->assembly = ass;
438
439         /* register right away to prevent loops */
440         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
441
442         mono_image_load_references (image, status);
443         if (*status != MONO_IMAGE_OK) {
444                 mono_assembly_close (ass);
445                 return NULL;
446         }
447
448         /* resolve assembly references for modules */
449         t = &image->tables [MONO_TABLE_MODULEREF];
450         for (i = 0; i < t->rows; i++){
451                 if (image->modules [i]) {
452                         image->modules [i]->assembly = ass;
453                         mono_image_load_references (image->modules [i], status);
454                 }
455                 /* 
456                  * FIXME: what do we do here? it could be a native dll...
457                  * We should probably do lazy-loading of modules.
458                  */
459                 *status = MONO_IMAGE_OK;
460         }
461
462         mono_assembly_invoke_load_hook (ass);
463
464         return ass;
465 }
466
467 MonoAssembly*
468 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
469 {
470         MonoAssembly *result;
471         char *fullpath, *filename;
472
473 #ifdef PLATFORM_WIN32
474         init_default_path ();
475 #endif
476         check_env ();
477
478         result = invoke_assembly_preload_hook (aname, assemblies_path);
479         if (result)
480                 return result;
481
482         /* g_print ("loading %s\n", aname->name); */
483         /* special case corlib */
484         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
485                 if (corlib) {
486                         /* g_print ("corlib already loaded\n"); */
487                         return corlib;
488                 }
489                 /* g_print ("corlib load\n"); */
490                 if (assemblies_path) {
491                         corlib = load_in_path (CORLIB_NAME, (const char**)assemblies_path, status);
492                         if (corlib)
493                                 return corlib;
494                 }
495                 corlib = load_in_path (CORLIB_NAME, default_path, status);
496                 return corlib;
497         }
498         result = search_loaded (aname);
499         if (result)
500                 return result;
501         /* g_print ("%s not found in cache\n", aname->name); */
502         if (strstr (aname->name, ".dll"))
503                 filename = g_strdup (aname->name);
504         else
505                 filename = g_strconcat (aname->name, ".dll", NULL);
506         if (basedir) {
507                 fullpath = g_build_filename (basedir, filename, NULL);
508                 result = mono_assembly_open (fullpath, status);
509                 g_free (fullpath);
510                 if (result) {
511                         g_free (filename);
512                         return result;
513                 }
514         }
515         if (assemblies_path) {
516                 result = load_in_path (filename, (const char**)assemblies_path, status);
517                 if (result) {
518                         g_free (filename);
519                         return result;
520                 }
521         }
522         result = load_in_path (filename, default_path, status);
523         g_free (filename);
524         return result;
525 }
526
527 void
528 mono_assembly_close (MonoAssembly *assembly)
529 {
530         MonoImage *image;
531         int i;
532         
533         g_return_if_fail (assembly != NULL);
534
535         if (--assembly->ref_count != 0)
536                 return;
537         
538         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
539         image = assembly->image;
540         if (image->references) {
541                 for (i = 0; image->references [i] != NULL; i++)
542                         mono_image_close (image->references [i]->image);
543                 g_free (image->references);
544         }
545              
546         mono_image_close (assembly->image);
547
548         g_free (assembly->basedir);
549         g_free (assembly);
550 }
551
552 void
553 mono_assembly_foreach (GFunc func, gpointer user_data)
554 {
555         /* In the future this can do locking of loaded_assemblies */
556
557         g_list_foreach (loaded_assemblies, func, user_data);
558 }
559
560 /* Holds the assembly of the application, for
561  * System.Diagnostics.Process::MainModule
562  */
563 static MonoAssembly *main_assembly=NULL;
564
565 void
566 mono_assembly_set_main (MonoAssembly *assembly)
567 {
568         main_assembly=assembly;
569 }
570
571 MonoAssembly *
572 mono_assembly_get_main (void)
573 {
574         return(main_assembly);
575 }