Fri Jul 26 11:22:52 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 /**
187  * mono_assembly_open:
188  * @filename: Opens the assembly pointed out by this name
189  * @status: where a status code can be returned
190  *
191  * mono_assembly_open opens the PE-image pointed by @filename, and
192  * loads any external assemblies referenced by it.
193  *
194  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
195  * it. 
196  */
197 MonoAssembly *
198 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
199 {
200         MonoAssembly *ass, *ass2;
201         MonoImage *image;
202         MonoTableInfo *t;
203         guint32 cols [MONO_ASSEMBLY_SIZE];
204         int i;
205         char *base_dir;
206         MonoImageOpenStatus def_status;
207         
208         g_return_val_if_fail (filename != NULL, NULL);
209
210         if (!status)
211                 status = &def_status;
212         *status = MONO_IMAGE_OK;
213
214         /* g_print ("file loading %s\n", filename); */
215         image = mono_image_open (filename, status);
216
217         if (!image){
218                 *status = MONO_IMAGE_ERROR_ERRNO;
219                 return NULL;
220         }
221
222         base_dir = g_path_get_dirname (filename);
223         
224         /*
225          * Create assembly struct, and enter it into the assembly cache
226          */
227         ass = g_new0 (MonoAssembly, 1);
228         ass->basedir = base_dir;
229         ass->image = image;
230
231         t = &image->tables [MONO_TABLE_ASSEMBLY];
232         if (t->rows) {
233                 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
234                 
235                 ass->aname.hash_len = 0;
236                 ass->aname.hash_value = NULL;
237                 ass->aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
238                 ass->aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
239                 ass->aname.flags = cols [MONO_ASSEMBLY_FLAGS];
240                 ass->aname.major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
241                 ass->aname.minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
242                 ass->aname.build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
243                 ass->aname.revision = cols [MONO_ASSEMBLY_REV_NUMBER];
244
245                 /* avoid loading the same assembly twixe for now... */
246                 if ((ass2 = search_loaded (&ass->aname))) {
247                         g_free (ass);
248                         g_free (base_dir);
249                         *status = MONO_IMAGE_OK;
250                         return ass2;
251                 }
252         }
253
254         image->assembly = ass;
255
256         /* register right away to prevent loops */
257         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
258
259         load_references (image, status);
260         if (*status != MONO_IMAGE_OK) {
261                 mono_assembly_close (ass);
262                 return NULL;
263         }
264         
265         t = &image->tables [MONO_TABLE_MODULEREF];
266         ass->modules = g_new0 (MonoImage *, t->rows);
267         for (i = 0; i < t->rows; i++){
268                 char *module_ref;
269                 const char *name;
270                 guint32 cols [MONO_MODULEREF_SIZE];
271
272                 mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE);
273                 name = mono_metadata_string_heap (image, cols [MONO_MODULEREF_NAME]);
274                 module_ref = g_concat_dir_and_file (base_dir, name);
275                 ass->modules [i] = mono_image_open (module_ref, status);
276                 if (ass->modules [i]) {
277                         ass->modules [i]->assembly = ass;
278                         load_references (ass->modules [i], status);
279                 }
280                 /* 
281                  * FIXME: what do we do here? it could be a native dll...
282                  * We should probably do lazy-loading of modules.
283                  */
284                 *status = MONO_IMAGE_OK;
285                 g_free (module_ref);
286         }
287
288         g_free (base_dir);
289         return ass;
290 }
291
292 MonoAssembly*
293 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
294 {
295         MonoAssembly *result;
296         char *fullpath, *filename;
297
298         check_env ();
299
300         /* g_print ("loading %s\n", aname->name); */
301         /* special case corlib */
302         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
303                 if (corlib) {
304                         /* g_print ("corlib already loaded\n"); */
305                         return corlib;
306                 }
307                 /* g_print ("corlib load\n"); */
308                 if (assemblies_path) {
309                         corlib = load_in_path (CORLIB_NAME, (const char**)assemblies_path, status);
310                         if (corlib)
311                                 return corlib;
312                 }
313                 corlib = load_in_path (CORLIB_NAME, default_path, status);
314                 return corlib;
315         }
316         result = search_loaded (aname);
317         if (result)
318                 return result;
319         /* g_print ("%s not found in cache\n", aname->name); */
320         if (strstr (aname->name, ".dll"))
321                 filename = g_strdup (aname->name);
322         else
323                 filename = g_strconcat (aname->name, ".dll", NULL);
324         if (basedir) {
325                 fullpath = g_concat_dir_and_file (basedir, filename);
326                 result = mono_assembly_open (fullpath, status);
327                 g_free (fullpath);
328                 if (result) {
329                         g_free (filename);
330                         return result;
331                 }
332         }
333         if (assemblies_path) {
334                 result = load_in_path (filename, (const char**)assemblies_path, status);
335                 if (result) {
336                         g_free (filename);
337                         return result;
338                 }
339         }
340         result = load_in_path (filename, default_path, status);
341         g_free (filename);
342         return result;
343 }
344
345 void
346 mono_assembly_close (MonoAssembly *assembly)
347 {
348         MonoImage *image;
349         int i;
350         
351         g_return_if_fail (assembly != NULL);
352
353         if (--assembly->ref_count != 0)
354                 return;
355         
356         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
357         image = assembly->image;
358         if (image->references) {
359                 for (i = 0; image->references [i] != NULL; i++)
360                         mono_image_close (image->references [i]->image);
361                 g_free (image->references);
362         }
363              
364         mono_image_close (assembly->image);
365
366         g_free (assembly->basedir);
367         g_free (assembly);
368 }
369
370 void
371 mono_assembly_foreach (GFunc func, gpointer user_data)
372 {
373         /* In the future this can do locking of loaded_assemblies */
374
375         g_list_foreach (loaded_assemblies, func, user_data);
376 }
377
378 /* Holds the assembly of the application, for
379  * System.Diagnostics.Process::MainModule
380  */
381 static MonoAssembly *main_assembly=NULL;
382
383 void
384 mono_assembly_set_main (MonoAssembly *assembly)
385 {
386         main_assembly=assembly;
387 }
388
389 MonoAssembly *
390 mono_assembly_get_main (void)
391 {
392         return(main_assembly);
393 }