Sat May 4 14:03:21 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, 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_open:
118  * @filename: Opens the assembly pointed out by this name
119  * @status: where a status code can be returned
120  *
121  * mono_assembly_open opens the PE-image pointed by @filename, and
122  * loads any external assemblies referenced by it.
123  *
124  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
125  * it. 
126  */
127 MonoAssembly *
128 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
129 {
130         MonoAssembly *ass, *ass2;
131         MonoImage *image;
132         MonoTableInfo *t;
133         guint32 cols [MONO_ASSEMBLY_SIZE];
134         int i;
135         char *base_dir;
136         const char *hash;
137         
138         g_return_val_if_fail (filename != NULL, NULL);
139
140         /* g_print ("file loading %s\n", filename); */
141         image = mono_image_open (filename, status);
142
143         if (!image){
144                 if (status)
145                         *status = MONO_IMAGE_ERROR_ERRNO;
146                 return NULL;
147         }
148
149         base_dir = g_path_get_dirname (filename);
150         
151         /*
152          * Create assembly struct, and enter it into the assembly cache
153          */
154         ass = g_new0 (MonoAssembly, 1);
155         ass->basedir = base_dir;
156         ass->image = image;
157
158         image->assembly = ass;
159
160         t = &image->tables [MONO_TABLE_ASSEMBLY];
161         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
162                 
163         ass->aname.hash_len = 0;
164         ass->aname.hash_value = NULL;
165         ass->aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
166         ass->aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
167         ass->aname.flags = cols [MONO_ASSEMBLY_FLAGS];
168         ass->aname.major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
169         ass->aname.minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
170         ass->aname.build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
171         ass->aname.revision = cols [MONO_ASSEMBLY_REV_NUMBER];
172
173         /* avoid loading the same assembly twixe for now... */
174         if ((ass2 = search_loaded (&ass->aname))) {
175                 g_free (ass);
176                 if (status)
177                         *status = MONO_IMAGE_OK;
178                 return ass2;
179         }
180
181         /* register right away to prevent loops */
182         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
183
184         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
185
186         image->references = g_new0 (MonoAssembly *, t->rows + 1);
187
188         /*
189          * Load any assemblies this image references
190          */
191         for (i = 0; i < t->rows; i++){
192                 MonoAssemblyName aname;
193
194                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
195                 
196                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
197                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
198                 aname.hash_value = hash;
199                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
200                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
201                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
202                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
203                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
204                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
205                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
206
207                 image->references [i] = mono_assembly_load (&aname, base_dir, status);
208
209                 if (image->references [i] == NULL){
210                         int j;
211                         
212                         for (j = 0; j < i; j++)
213                                 mono_assembly_close (image->references [j]);
214                         g_free (image->references);
215                         mono_image_close (image);
216
217                         g_warning ("Could not find assembly %s", aname.name);
218                         if (status)
219                                 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
220                         g_free (ass);
221                         loaded_assemblies = g_list_remove (loaded_assemblies, ass);
222                         g_free (base_dir);
223                         return NULL;
224                 }
225         }
226         image->references [i] = NULL;
227
228         t = &image->tables [MONO_TABLE_MODULEREF];
229         ass->modules = g_new0 (MonoImage *, t->rows);
230         for (i = 0; i < t->rows; i++){
231                 char *module_ref;
232                 const char *name;
233                 guint32 cols [MONO_MODULEREF_SIZE];
234
235                 mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE);
236                 name = mono_metadata_string_heap (image, cols [MONO_MODULEREF_NAME]);
237                 module_ref = g_concat_dir_and_file (base_dir, name);
238                 ass->modules [i] = mono_image_open (module_ref, status);
239                 g_free (module_ref);
240         }
241
242         g_free (base_dir);
243         return ass;
244 }
245
246 MonoAssembly*
247 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
248 {
249         MonoAssembly *result;
250         char *fullpath, *filename;
251
252         check_env ();
253
254         /* g_print ("loading %s\n", aname->name); */
255         /* special case corlib */
256         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
257                 if (corlib) {
258                         /* g_print ("corlib already loaded\n"); */
259                         return corlib;
260                 }
261                 /* g_print ("corlib load\n"); */
262                 if (assemblies_path) {
263                         corlib = load_in_path (CORLIB_NAME, assemblies_path, status);
264                         if (corlib)
265                                 return corlib;
266                 }
267                 fullpath = g_concat_dir_and_file (MONO_ASSEMBLIES, CORLIB_NAME);
268                 corlib = mono_assembly_open (fullpath, status);
269                 g_free (fullpath);
270                 return corlib;
271         }
272         result = search_loaded (aname);
273         if (result)
274                 return result;
275         /* g_print ("%s not found in cache\n", aname->name); */
276         if (strstr (aname->name, ".dll"))
277                 filename = g_strdup (aname->name);
278         else
279                 filename = g_strconcat (aname->name, ".dll", NULL);
280         if (basedir) {
281                 fullpath = g_concat_dir_and_file (basedir, filename);
282                 result = mono_assembly_open (fullpath, status);
283                 g_free (fullpath);
284                 if (result) {
285                         g_free (filename);
286                         return result;
287                 }
288         }
289         if (assemblies_path) {
290                 result = load_in_path (filename, assemblies_path, status);
291                 if (result) {
292                         g_free (filename);
293                         return result;
294                 }
295         }
296         result = load_in_path (filename, default_path, status);
297         g_free (filename);
298         return result;
299 }
300
301 void
302 mono_assembly_close (MonoAssembly *assembly)
303 {
304         MonoImage *image;
305         int i;
306         
307         g_return_if_fail (assembly != NULL);
308
309         if (--assembly->ref_count != 0)
310                 return;
311         
312         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
313         image = assembly->image;
314         for (i = 0; image->references [i] != NULL; i++)
315                 mono_image_close (image->references [i]->image);
316         g_free (image->references);
317              
318         mono_image_close (assembly->image);
319
320         g_free (assembly->basedir);
321         g_free (assembly);
322 }
323