2002-03-14 Duncan Mak <duncan@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 *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         if (g_file_test (name, G_FILE_TEST_EXISTS))
65                 return g_strdup (name);
66         path = g_concat_dir_and_file (MONO_ASSEMBLIES, name);
67         if (g_file_test (path, G_FILE_TEST_EXISTS))
68                 return g_strdup (path);
69         g_free (path);
70         file = g_strconcat (name, ".dll", NULL);
71         path = g_concat_dir_and_file (MONO_ASSEMBLIES, file);
72         g_free (file);
73
74         return path;
75 }
76
77 /**
78  * mono_assembly_open:
79  * @filename: Opens the assembly pointed out by this name
80  * @resolver: A user provided function to resolve assembly references
81  * @status: where a status code can be returned
82  *
83  * mono_assembly_open opens the PE-image pointed by @filename, and
84  * loads any external assemblies referenced by it.
85  *
86  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
87  * it. 
88  */
89 MonoAssembly *
90 mono_assembly_open (const char *filename, MonoAssemblyResolverFn resolver,
91                     enum MonoImageOpenStatus *status)
92 {
93         MonoAssembly *ass;
94         MonoImage *image;
95         MonoTableInfo *t;
96         int i;
97         char *fullname;
98         const char *basename = strrchr (filename, G_DIR_SEPARATOR);
99         static MonoAssembly *corlib;
100         
101         g_return_val_if_fail (filename != NULL, NULL);
102
103         if (assemblies == NULL)
104                 assemblies = g_hash_table_new (g_str_hash, g_str_equal);
105
106         if ((ass = g_hash_table_lookup (assemblies, filename)) != NULL){
107                 ass->ref_count++;
108                 return ass;
109         }
110         
111         if (basename == NULL)
112                 basename = filename;
113         else
114                 basename++;
115
116         if (resolver == NULL)
117                 resolver = default_assembly_name_resolver;
118
119
120         fullname = resolver (filename);
121         image = mono_image_open (fullname, status);
122
123         if (!image){
124                 if (status)
125                         *status = MONO_IMAGE_ERROR_ERRNO;
126                 g_free (fullname);
127                 return NULL;
128         }
129
130         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
131
132         image->references = g_new0 (MonoAssembly *, t->rows + 1);
133
134         /*
135          * Create assembly struct, and enter it into the assembly cache
136          */
137         ass = g_new (MonoAssembly, 1);
138         ass->name = fullname;
139         ass->image = image;
140
141         g_hash_table_insert (assemblies, image->name, ass);
142         g_hash_table_insert (assemblies, ass->name, ass);
143         
144         /*
145          * Load any assemblies this image references
146          */
147         for (i = 0; i < t->rows; i++){
148                 char *assembly_ref;
149                 const char *name;
150                 guint32 cols [MONO_ASSEMBLYREF_SIZE];
151
152                 mono_metadata_decode_row (t, i, cols, CSIZE (cols));
153                 name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
154
155                 /*
156                  * Special case until we have a passable corlib:
157                  *
158                  * ie, references to mscorlib from corlib.dll are ignored 
159                  * and we do not load corlib twice.
160                  */
161                 if (strcmp (basename, CORLIB_NAME) == 0){
162                         if (corlib == NULL)
163                                 corlib = ass;
164                         
165                         if (strcmp (name, "mscorlib") == 0)
166                                 continue;
167                 }
168                 
169                 assembly_ref = (*resolver) (name);
170
171                 image->references [i] = mono_assembly_open (assembly_ref, resolver, status);
172
173                 if (image->references [i] == NULL){
174                         int j;
175                         
176                         for (j = 0; j < i; j++)
177                                 mono_assembly_close (image->references [j]);
178                         g_free (image->references);
179                         mono_image_close (image);
180
181                         g_warning ("Could not find assembly %s %s", name, assembly_ref);
182                         g_free (assembly_ref);
183                         if (status)
184                                 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
185                         g_free (ass);
186                         return NULL;
187                 }
188                 g_free (assembly_ref);
189         }
190         image->references [i] = NULL;
191
192         return ass;
193 }
194
195 void
196 mono_assembly_close (MonoAssembly *assembly)
197 {
198         MonoImage *image;
199         int i;
200         
201         g_return_if_fail (assembly != NULL);
202
203         if (--assembly->ref_count != 0)
204                 return;
205         
206         image = assembly->image;
207         for (i = 0; image->references [i] != NULL; i++)
208                 mono_image_close (image->references [i]->image);
209         g_free (image->references);
210              
211         mono_image_close (assembly->image);
212
213         g_hash_table_remove (assemblies, assembly->name);
214                              
215         g_free (assembly->name);
216         g_free (assembly);
217 }
218
219 /*
220  * Temporary hack until we get AppDomains
221  */
222 GHashTable *
223 mono_get_assemblies ()
224 {
225         return assemblies;
226 }