2002-03-30 Dietmar Maurer <dietmar@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 "assembly.h"
18 #include "image.h"
19 #include "cil-coff.h"
20 #include "rawbuffer.h"
21
22 #define CSIZE(x) (sizeof (x) / 4)
23
24 /*
25  * keeps track of loaded assemblies
26  */
27 static GHashTable *assemblies;
28
29 /**
30  * g_concat_dir_and_file:
31  * @dir:  directory name
32  * @file: filename.
33  *
34  * returns a new allocated string that is the concatenation of dir and file,
35  * takes care of the exact details for concatenating them.
36  */
37 static char *
38 g_concat_dir_and_file (const char *dir, const char *file)
39 {
40         g_return_val_if_fail (dir != NULL, NULL);
41         g_return_val_if_fail (file != NULL, NULL);
42
43         /*
44          * If the directory name doesn't have a / on the end, we need
45          * to add one so we get a proper path to the file
46          */
47         if (dir [strlen(dir) - 1] != G_DIR_SEPARATOR)
48                 return g_strconcat (dir, G_DIR_SEPARATOR_S, file, NULL);
49         else
50                 return g_strconcat (dir, file, NULL);
51 }
52
53 static char *
54 default_assembly_name_resolver (const char *base_dir, const char *name)
55 {
56         char *file, *path;
57         
58         if ((strcmp (name, "mscorlib") == 0) ||
59                         (strcmp (name, "mscorlib.dll") == 0) ||
60                         (strcmp (name, "corlib.dll") == 0) ||
61                         (strcmp (name, "corlib") == 0))
62                 return g_concat_dir_and_file (MONO_ASSEMBLIES, CORLIB_NAME);
63
64         /* Full name already supplied */
65         path = g_strdup (name);
66         if (g_file_test (name, G_FILE_TEST_EXISTS))
67                 return path;
68
69         g_free (path);
70         path = g_concat_dir_and_file (base_dir, name);
71         if (g_file_test (path, G_FILE_TEST_EXISTS))
72                 return path;
73
74         file = path;
75         path = g_strconcat (file, ".dll", NULL);
76         g_free (file);
77         if (g_file_test (path, G_FILE_TEST_EXISTS))
78                 return path;
79         g_free (path);
80         
81         path = g_concat_dir_and_file (MONO_ASSEMBLIES, name);
82         if (g_file_test (path, G_FILE_TEST_EXISTS))
83                 return path;
84         g_free (path);
85
86         file = g_strconcat (name, ".dll", NULL);
87         path = g_concat_dir_and_file (MONO_ASSEMBLIES, file);
88         g_free (file);
89
90         return path;
91 }
92
93 /**
94  * mono_assembly_open:
95  * @filename: Opens the assembly pointed out by this name
96  * @resolver: A user provided function to resolve assembly references
97  * @status: where a status code can be returned
98  *
99  * mono_assembly_open opens the PE-image pointed by @filename, and
100  * loads any external assemblies referenced by it.
101  *
102  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
103  * it. 
104  */
105 MonoAssembly *
106 mono_assembly_open (const char *filename, MonoAssemblyResolverFn resolver,
107                     enum MonoImageOpenStatus *status)
108 {
109         MonoAssembly *ass;
110         MonoImage *image;
111         MonoTableInfo *t;
112         int i;
113         char *fullname, *base_dir;
114         const char *base_name = strrchr (filename, G_DIR_SEPARATOR);
115         static MonoAssembly *corlib;
116         
117         g_return_val_if_fail (filename != NULL, NULL);
118
119         if (assemblies == NULL)
120                 assemblies = g_hash_table_new (g_str_hash, g_str_equal);
121
122         if ((ass = g_hash_table_lookup (assemblies, filename)) != NULL){
123                 ass->ref_count++;
124                 return ass;
125         }
126         
127         if (base_name == NULL)
128                 base_name = filename;
129         else
130                 base_name++;
131
132         if (resolver == NULL)
133                 resolver = default_assembly_name_resolver;
134
135         base_dir = g_path_get_dirname (filename);
136         
137         fullname = resolver (base_dir, filename);
138         image = mono_image_open (fullname, status);
139
140         if (!image){
141                 if (status)
142                         *status = MONO_IMAGE_ERROR_ERRNO;
143                 g_free (fullname);
144                 g_free (base_dir);
145                 return NULL;
146         }
147
148         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
149
150         image->references = g_new0 (MonoAssembly *, t->rows + 1);
151
152         /*
153          * Create assembly struct, and enter it into the assembly cache
154          */
155         ass = g_new (MonoAssembly, 1);
156         ass->name = fullname;
157         ass->image = image;
158
159         g_hash_table_insert (assemblies, image->name, ass);
160         g_hash_table_insert (assemblies, ass->name, ass);
161         
162         /*
163          * Load any assemblies this image references
164          */
165         for (i = 0; i < t->rows; i++){
166                 char *assembly_ref;
167                 const char *name;
168                 guint32 cols [MONO_ASSEMBLYREF_SIZE];
169
170                 mono_metadata_decode_row (t, i, cols, CSIZE (cols));
171                 name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
172
173                 /*
174                  * Special case until we have a passable corlib:
175                  *
176                  * ie, references to mscorlib from corlib.dll are ignored 
177                  * and we do not load corlib twice.
178                  */
179                 if (strcmp (base_name, CORLIB_NAME) == 0){
180                         if (corlib == NULL)
181                                 corlib = ass;
182                         
183                         if (strcmp (name, "mscorlib") == 0)
184                                 continue;
185                 }
186                 
187                 assembly_ref = (*resolver) (base_dir, name);
188
189                 image->references [i] = mono_assembly_open (assembly_ref, resolver, status);
190
191                 if (image->references [i] == NULL){
192                         int j;
193                         
194                         for (j = 0; j < i; j++)
195                                 mono_assembly_close (image->references [j]);
196                         g_free (image->references);
197                         mono_image_close (image);
198
199                         g_warning ("Could not find assembly %s %s", name, assembly_ref);
200                         g_free (assembly_ref);
201                         if (status)
202                                 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
203                         g_free (ass);
204                         g_free (base_dir);
205                         return NULL;
206                 }
207                 g_free (assembly_ref);
208         }
209         image->references [i] = NULL;
210
211         g_free (base_dir);
212         return ass;
213 }
214
215 void
216 mono_assembly_close (MonoAssembly *assembly)
217 {
218         MonoImage *image;
219         int i;
220         
221         g_return_if_fail (assembly != NULL);
222
223         if (--assembly->ref_count != 0)
224                 return;
225         
226         image = assembly->image;
227         for (i = 0; image->references [i] != NULL; i++)
228                 mono_image_close (image->references [i]->image);
229         g_free (image->references);
230              
231         mono_image_close (assembly->image);
232
233         g_hash_table_remove (assemblies, assembly->name);
234                              
235         g_free (assembly->name);
236         g_free (assembly);
237 }
238
239 /*
240  * Temporary hack until we get AppDomains
241  */
242 GHashTable *
243 mono_get_assemblies ()
244 {
245         return assemblies;
246 }