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