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