2 * assembly.c: Routines for loading assemblies.
5 * Miguel de Icaza (miguel@ximian.com)
7 * (C) 2001 Ximian, Inc. http://www.ximian.com
10 * Implement big-endian versions of the reading routines.
21 #include "rawbuffer.h"
23 /* the default search path is just MONO_ASSEMBLIES */
30 static char **assemblies_path = NULL;
31 static int env_checked = 0;
34 static gboolean path_inited = FALSE;
37 init_default_path (void)
45 default_path [0] = g_strdup (MONO_ASSEMBLIES);
46 for (i = strlen (MONO_ASSEMBLIES) - 1; i >= 0; i--) {
47 if (default_path [0][i] == '/')
48 default_path [0][i] = '\\';
62 path = getenv ("MONO_PATH");
65 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
67 g_strfreev (assemblies_path);
68 assemblies_path = splitted;
72 * keeps track of loaded assemblies
74 static GList *loaded_assemblies = NULL;
75 static MonoAssembly *corlib;
78 search_loaded (MonoAssemblyName* aname)
83 for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
87 /* we just compare the name, but later we'll do all the checks */
88 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
89 if (strcmp (aname->name, ass->aname.name))
91 /* g_print ("success\n"); */
98 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
102 MonoAssembly *result;
104 for (i = 0; search_path [i]; ++i) {
105 fullpath = g_build_filename (search_path [i], basename, NULL);
106 result = mono_assembly_open (fullpath, status);
115 * mono_assembly_setrootdir:
116 * @root_dir: The pathname of the root directory where we will locate assemblies
118 * This routine sets the internal default root directory for looking up
119 * assemblies. This is used by Windows installations to compute dynamically
120 * the place where the Mono assemblies are located.
124 mono_assembly_setrootdir (const char *root_dir)
127 * Override the MONO_ASSEMBLIES directory configured at compile time.
129 default_path [0] = g_strdup (root_dir);
130 #ifdef PLATFORM_WIN32
136 mono_image_load_references (MonoImage *image, MonoImageOpenStatus *status) {
138 guint32 cols [MONO_ASSEMBLYREF_SIZE];
142 if (image->references)
145 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
147 image->references = g_new0 (MonoAssembly *, t->rows + 1);
150 * Load any assemblies this image references
152 for (i = 0; i < t->rows; i++) {
153 MonoAssemblyName aname;
155 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
157 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
158 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
159 aname.hash_value = hash;
160 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
161 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
162 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
163 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
164 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
165 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
166 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
168 image->references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
170 if (image->references [i] == NULL){
173 for (j = 0; j < i; j++)
174 mono_assembly_close (image->references [j]);
175 g_free (image->references);
176 image->references = NULL;
178 g_warning ("Could not find assembly %s", aname.name);
179 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
183 image->references [i] = NULL;
187 typedef struct AssemblyLoadHook AssemblyLoadHook;
188 struct AssemblyLoadHook {
189 AssemblyLoadHook *next;
190 MonoAssemblyLoadFunc func;
194 AssemblyLoadHook *assembly_load_hook = NULL;
197 mono_assembly_invoke_load_hook (MonoAssembly *ass)
199 AssemblyLoadHook *hook;
201 for (hook = assembly_load_hook; hook; hook = hook->next) {
202 hook->func (ass, hook->user_data);
207 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
209 AssemblyLoadHook *hook;
211 g_return_if_fail (func != NULL);
213 hook = g_new0 (AssemblyLoadHook, 1);
215 hook->user_data = user_data;
216 hook->next = assembly_load_hook;
217 assembly_load_hook = hook;
220 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
221 struct AssemblyPreLoadHook {
222 AssemblyPreLoadHook *next;
223 MonoAssemblyPreLoadFunc func;
227 AssemblyPreLoadHook *assembly_preload_hook = NULL;
229 static MonoAssembly *
230 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
232 AssemblyPreLoadHook *hook;
233 MonoAssembly *assembly;
235 for (hook = assembly_preload_hook; hook; hook = hook->next) {
236 assembly = hook->func (aname, assemblies_path, hook->user_data);
237 if (assembly != NULL)
245 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
247 AssemblyPreLoadHook *hook;
249 g_return_if_fail (func != NULL);
251 hook = g_new0 (AssemblyPreLoadHook, 1);
253 hook->user_data = user_data;
254 hook->next = assembly_preload_hook;
255 assembly_preload_hook = hook;
259 absolute_dir (const gchar *filename)
270 if (g_path_is_absolute (filename))
271 return g_path_get_dirname (filename);
273 cwd = g_get_current_dir ();
274 mixed = g_build_filename (cwd, filename, NULL);
275 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
280 for (i = 0; (part = parts [i]) != NULL; i++) {
281 if (!strcmp (part, "."))
284 if (!strcmp (part, "..")) {
285 if (list && list->next) /* Don't remove root */
286 list = g_slist_delete_link (list, list);
288 list = g_slist_prepend (list, part);
292 result = g_string_new ("");
293 list = g_slist_reverse (list);
295 /* Ignores last data pointer, which should be the filename */
296 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
298 g_string_append_printf (result, "%s%c", (char *) tmp->data,
302 g_string_free (result, FALSE);
307 return g_strdup (".");
314 * mono_assembly_open:
315 * @filename: Opens the assembly pointed out by this name
316 * @status: where a status code can be returned
318 * mono_assembly_open opens the PE-image pointed by @filename, and
319 * loads any external assemblies referenced by it.
321 * NOTE: we could do lazy loading of the assemblies. Or maybe not worth
325 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
327 MonoAssembly *ass, *ass2;
330 guint32 cols [MONO_ASSEMBLY_SIZE];
332 char *base_dir, *aot_name;
333 MonoImageOpenStatus def_status;
336 g_return_val_if_fail (filename != NULL, NULL);
339 status = &def_status;
340 *status = MONO_IMAGE_OK;
342 if (strncmp (filename, "file://", 7) == 0) {
343 GError *error = NULL;
344 gchar *uri = (gchar *) filename;
347 * MS allows file://c:/... and fails on file://localhost/c:/...
348 * They also throw an IndexOutOfRangeException if "file://"
351 uri = g_strdup_printf ("file:///%s", uri + 7);
353 fname = g_filename_from_uri (uri, NULL, &error);
358 g_warning ("%s\n", error->message);
359 g_error_free (error);
360 fname = g_strdup (filename);
363 fname = g_strdup (filename);
366 /* g_print ("file loading %s\n", fname); */
367 image = mono_image_open (fname, status);
370 *status = MONO_IMAGE_ERROR_ERRNO;
375 #if defined (PLATFORM_WIN32)
378 tmp_fn = g_strdup (fname);
379 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
380 if (tmp_fn [i] == '/')
384 base_dir = absolute_dir (tmp_fn);
388 base_dir = absolute_dir (fname);
392 * Create assembly struct, and enter it into the assembly cache
394 ass = g_new0 (MonoAssembly, 1);
395 ass->basedir = base_dir;
398 /* load aot compiled module */
399 aot_name = g_strdup_printf ("%s.so", fname);
401 ass->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
404 if (ass->aot_module) {
405 char *saved_guid = NULL;
406 g_module_symbol (ass->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
408 if (!saved_guid || strcmp (image->guid, saved_guid)) {
409 g_module_close (ass->aot_module);
410 ass->aot_module = NULL;
414 t = &image->tables [MONO_TABLE_ASSEMBLY];
416 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
418 ass->aname.hash_len = 0;
419 ass->aname.hash_value = NULL;
420 ass->aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
421 ass->aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
422 ass->aname.flags = cols [MONO_ASSEMBLY_FLAGS];
423 ass->aname.major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
424 ass->aname.minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
425 ass->aname.build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
426 ass->aname.revision = cols [MONO_ASSEMBLY_REV_NUMBER];
428 /* avoid loading the same assembly twice for now... */
429 if ((ass2 = search_loaded (&ass->aname))) {
432 *status = MONO_IMAGE_OK;
437 image->assembly = ass;
439 /* register right away to prevent loops */
440 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
442 mono_image_load_references (image, status);
443 if (*status != MONO_IMAGE_OK) {
444 mono_assembly_close (ass);
448 /* resolve assembly references for modules */
449 t = &image->tables [MONO_TABLE_MODULEREF];
450 for (i = 0; i < t->rows; i++){
451 if (image->modules [i]) {
452 image->modules [i]->assembly = ass;
453 mono_image_load_references (image->modules [i], status);
456 * FIXME: what do we do here? it could be a native dll...
457 * We should probably do lazy-loading of modules.
459 *status = MONO_IMAGE_OK;
462 mono_assembly_invoke_load_hook (ass);
468 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
470 MonoAssembly *result;
471 char *fullpath, *filename;
473 #ifdef PLATFORM_WIN32
474 init_default_path ();
478 result = invoke_assembly_preload_hook (aname, assemblies_path);
482 /* g_print ("loading %s\n", aname->name); */
483 /* special case corlib */
484 if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
486 /* g_print ("corlib already loaded\n"); */
489 /* g_print ("corlib load\n"); */
490 if (assemblies_path) {
491 corlib = load_in_path (CORLIB_NAME, (const char**)assemblies_path, status);
495 corlib = load_in_path (CORLIB_NAME, default_path, status);
498 result = search_loaded (aname);
501 /* g_print ("%s not found in cache\n", aname->name); */
502 if (strstr (aname->name, ".dll"))
503 filename = g_strdup (aname->name);
505 filename = g_strconcat (aname->name, ".dll", NULL);
507 fullpath = g_build_filename (basedir, filename, NULL);
508 result = mono_assembly_open (fullpath, status);
515 if (assemblies_path) {
516 result = load_in_path (filename, (const char**)assemblies_path, status);
522 result = load_in_path (filename, default_path, status);
528 mono_assembly_close (MonoAssembly *assembly)
533 g_return_if_fail (assembly != NULL);
535 if (--assembly->ref_count != 0)
538 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
539 image = assembly->image;
540 if (image->references) {
541 for (i = 0; image->references [i] != NULL; i++)
542 mono_image_close (image->references [i]->image);
543 g_free (image->references);
546 mono_image_close (assembly->image);
548 g_free (assembly->basedir);
553 mono_assembly_foreach (GFunc func, gpointer user_data)
555 /* In the future this can do locking of loaded_assemblies */
557 g_list_foreach (loaded_assemblies, func, user_data);
560 /* Holds the assembly of the application, for
561 * System.Diagnostics.Process::MainModule
563 static MonoAssembly *main_assembly=NULL;
566 mono_assembly_set_main (MonoAssembly *assembly)
568 main_assembly=assembly;
572 mono_assembly_get_main (void)
574 return(main_assembly);