Fri Sep 27 15:38:31 CEST 2002 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 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 /**
76  * g_concat_dir_and_file:
77  * @dir:  directory name
78  * @file: filename.
79  *
80  * returns a new allocated string that is the concatenation of dir and file,
81  * takes care of the exact details for concatenating them.
82  */
83 static char *
84 g_concat_dir_and_file (const char *dir, const char *file)
85 {
86         g_return_val_if_fail (dir != NULL, NULL);
87         g_return_val_if_fail (file != NULL, NULL);
88
89         /*
90          * If the directory name doesn't have a / on the end, we need
91          * to add one so we get a proper path to the file
92          */
93         if (dir [strlen(dir) - 1] != G_DIR_SEPARATOR)
94                 return g_strconcat (dir, G_DIR_SEPARATOR_S, file, NULL);
95         else
96                 return g_strconcat (dir, file, NULL);
97 }
98
99 static MonoAssembly *
100 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
101 {
102         int i;
103         char *fullpath;
104         MonoAssembly *result;
105
106         for (i = 0; search_path [i]; ++i) {
107                 fullpath = g_concat_dir_and_file (search_path [i], basename);
108                 result = mono_assembly_open (fullpath, status);
109                 g_free (fullpath);
110                 if (result)
111                         return result;
112         }
113         return NULL;
114 }
115
116 /**
117  * mono_assembly_setrootdir:
118  * @root_dir: The pathname of the root directory where we will locate assemblies
119  *
120  * This routine sets the internal default root directory for looking up
121  * assemblies.  This is used by Windows installations to compute dynamically
122  * the place where the Mono assemblies are located.
123  *
124  */
125 void
126 mono_assembly_setrootdir (const char *root_dir)
127 {
128         /*
129          * Override the MONO_ASSEMBLIES directory configured at compile time.
130          */
131         default_path [0] = g_strdup (root_dir);
132 }
133
134 static void
135 load_references (MonoImage *image, MonoImageOpenStatus *status) {
136         MonoTableInfo *t;
137         guint32 cols [MONO_ASSEMBLYREF_SIZE];
138         const char *hash;
139         int i;
140
141         if (image->references)
142                 return;
143
144         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
145
146         image->references = g_new0 (MonoAssembly *, t->rows + 1);
147
148         /*
149          * Load any assemblies this image references
150          */
151         for (i = 0; i < t->rows; i++) {
152                 MonoAssemblyName aname;
153
154                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
155                 
156                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
157                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
158                 aname.hash_value = hash;
159                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
160                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
161                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
162                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
163                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
164                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
165                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
166
167                 image->references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
168
169                 if (image->references [i] == NULL){
170                         int j;
171                         
172                         for (j = 0; j < i; j++)
173                                 mono_assembly_close (image->references [j]);
174                         g_free (image->references);
175                         image->references = NULL;
176
177                         g_warning ("Could not find assembly %s", aname.name);
178                         *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
179                         return;
180                 }
181         }
182         image->references [i] = NULL;
183
184 }
185
186 typedef struct AssemblyLoadHook AssemblyLoadHook;
187 struct AssemblyLoadHook {
188         AssemblyLoadHook *next;
189         MonoAssemblyLoadFunc func;
190         gpointer user_data;
191 };
192
193 AssemblyLoadHook *assembly_load_hook = NULL;
194
195 static void
196 invoke_assembly_hook (MonoAssembly *ass)
197 {
198         AssemblyLoadHook *hook;
199
200         for (hook = assembly_load_hook; hook; hook = hook->next) {
201                 hook->func (ass, hook->user_data);
202         }
203 }
204
205 void
206 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
207 {
208         AssemblyLoadHook *hook;
209         
210         g_return_if_fail (func != NULL);
211
212         hook = g_new0 (AssemblyLoadHook, 1);
213         hook->func = func;
214         hook->user_data = user_data;
215         hook->next = assembly_load_hook;
216         assembly_load_hook = hook;
217 }
218
219 /**
220  * mono_assembly_open:
221  * @filename: Opens the assembly pointed out by this name
222  * @status: where a status code can be returned
223  *
224  * mono_assembly_open opens the PE-image pointed by @filename, and
225  * loads any external assemblies referenced by it.
226  *
227  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
228  * it. 
229  */
230 MonoAssembly *
231 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
232 {
233         MonoAssembly *ass, *ass2;
234         MonoImage *image;
235         MonoTableInfo *t;
236         guint32 cols [MONO_ASSEMBLY_SIZE];
237         int i;
238         char *base_dir;
239         MonoImageOpenStatus def_status;
240         
241         g_return_val_if_fail (filename != NULL, NULL);
242
243         if (!status)
244                 status = &def_status;
245         *status = MONO_IMAGE_OK;
246
247         /* g_print ("file loading %s\n", filename); */
248         image = mono_image_open (filename, status);
249
250         if (!image){
251                 *status = MONO_IMAGE_ERROR_ERRNO;
252                 return NULL;
253         }
254
255         base_dir = g_path_get_dirname (filename);
256         
257         /*
258          * Create assembly struct, and enter it into the assembly cache
259          */
260         ass = g_new0 (MonoAssembly, 1);
261         ass->basedir = base_dir;
262         ass->image = image;
263
264         t = &image->tables [MONO_TABLE_ASSEMBLY];
265         if (t->rows) {
266                 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
267                 
268                 ass->aname.hash_len = 0;
269                 ass->aname.hash_value = NULL;
270                 ass->aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
271                 ass->aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
272                 ass->aname.flags = cols [MONO_ASSEMBLY_FLAGS];
273                 ass->aname.major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
274                 ass->aname.minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
275                 ass->aname.build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
276                 ass->aname.revision = cols [MONO_ASSEMBLY_REV_NUMBER];
277
278                 /* avoid loading the same assembly twixe for now... */
279                 if ((ass2 = search_loaded (&ass->aname))) {
280                         g_free (ass);
281                         g_free (base_dir);
282                         *status = MONO_IMAGE_OK;
283                         return ass2;
284                 }
285         }
286
287         image->assembly = ass;
288
289         /* register right away to prevent loops */
290         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
291
292         load_references (image, status);
293         if (*status != MONO_IMAGE_OK) {
294                 mono_assembly_close (ass);
295                 return NULL;
296         }
297         
298         t = &image->tables [MONO_TABLE_MODULEREF];
299         ass->modules = g_new0 (MonoImage *, t->rows);
300         for (i = 0; i < t->rows; i++){
301                 char *module_ref;
302                 const char *name;
303                 guint32 cols [MONO_MODULEREF_SIZE];
304
305                 mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE);
306                 name = mono_metadata_string_heap (image, cols [MONO_MODULEREF_NAME]);
307                 module_ref = g_concat_dir_and_file (base_dir, name);
308                 ass->modules [i] = mono_image_open (module_ref, status);
309                 if (ass->modules [i]) {
310                         ass->modules [i]->assembly = ass;
311                         load_references (ass->modules [i], status);
312                 }
313                 /* 
314                  * FIXME: what do we do here? it could be a native dll...
315                  * We should probably do lazy-loading of modules.
316                  */
317                 *status = MONO_IMAGE_OK;
318                 g_free (module_ref);
319         }
320
321         invoke_assembly_hook (ass);
322
323         return ass;
324 }
325
326 MonoAssembly*
327 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
328 {
329         MonoAssembly *result;
330         char *fullpath, *filename;
331
332         check_env ();
333
334         /* g_print ("loading %s\n", aname->name); */
335         /* special case corlib */
336         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
337                 if (corlib) {
338                         /* g_print ("corlib already loaded\n"); */
339                         return corlib;
340                 }
341                 /* g_print ("corlib load\n"); */
342                 if (assemblies_path) {
343                         corlib = load_in_path (CORLIB_NAME, (const char**)assemblies_path, status);
344                         if (corlib)
345                                 return corlib;
346                 }
347                 corlib = load_in_path (CORLIB_NAME, default_path, status);
348                 return corlib;
349         }
350         result = search_loaded (aname);
351         if (result)
352                 return result;
353         /* g_print ("%s not found in cache\n", aname->name); */
354         if (strstr (aname->name, ".dll"))
355                 filename = g_strdup (aname->name);
356         else
357                 filename = g_strconcat (aname->name, ".dll", NULL);
358         if (basedir) {
359                 fullpath = g_concat_dir_and_file (basedir, filename);
360                 result = mono_assembly_open (fullpath, status);
361                 g_free (fullpath);
362                 if (result) {
363                         g_free (filename);
364                         return result;
365                 }
366         }
367         if (assemblies_path) {
368                 result = load_in_path (filename, (const char**)assemblies_path, status);
369                 if (result) {
370                         g_free (filename);
371                         return result;
372                 }
373         }
374         result = load_in_path (filename, default_path, status);
375         g_free (filename);
376         return result;
377 }
378
379 void
380 mono_assembly_close (MonoAssembly *assembly)
381 {
382         MonoImage *image;
383         int i;
384         
385         g_return_if_fail (assembly != NULL);
386
387         if (--assembly->ref_count != 0)
388                 return;
389         
390         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
391         image = assembly->image;
392         if (image->references) {
393                 for (i = 0; image->references [i] != NULL; i++)
394                         mono_image_close (image->references [i]->image);
395                 g_free (image->references);
396         }
397              
398         mono_image_close (assembly->image);
399
400         g_free (assembly->basedir);
401         g_free (assembly);
402 }
403
404 void
405 mono_assembly_foreach (GFunc func, gpointer user_data)
406 {
407         /* In the future this can do locking of loaded_assemblies */
408
409         g_list_foreach (loaded_assemblies, func, user_data);
410 }
411
412 /* Holds the assembly of the application, for
413  * System.Diagnostics.Process::MainModule
414  */
415 static MonoAssembly *main_assembly=NULL;
416
417 void
418 mono_assembly_set_main (MonoAssembly *assembly)
419 {
420         main_assembly=assembly;
421 }
422
423 MonoAssembly *
424 mono_assembly_get_main (void)
425 {
426         return(main_assembly);
427 }