66e8547d3a0a5583865503b7f0a04f6f58ac50e1
[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  */
10 #include <config.h>
11 #include <stdio.h>
12 #include <glib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include "assembly.h"
17 #include "image.h"
18 #include "cil-coff.h"
19 #include "rawbuffer.h"
20 #include <mono/metadata/loader.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/io-layer/io-layer.h>
25 #include <mono/utils/mono-uri.h>
26 #include <mono/metadata/mono-config.h>
27 #include <mono/utils/mono-digest.h>
28 #include <mono/utils/mono-logger.h>
29 #ifdef PLATFORM_WIN32
30 #include <mono/os/util.h>
31 #ifdef _MSC_VER
32         /* not used on Windows - see mono_set_rootdir () */
33         #define MONO_ASSEMBLIES         NULL
34 #endif
35 #endif
36
37 /* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
38 typedef struct  {
39         const char* assembly_name;
40         guint8 version_set_index;
41 } AssemblyVersionMap;
42
43 /* the default search path is just MONO_ASSEMBLIES */
44 static const char*
45 default_path [] = {
46         MONO_ASSEMBLIES,
47         NULL
48 };
49
50 /* Contains the list of directories to be searched for assemblies (MONO_PATH) */
51 static char **assemblies_path = NULL;
52
53 /* Contains the list of directories that point to auxiliary GACs */
54 static char **extra_gac_paths = NULL;
55
56 /* The list of system assemblies what will be remapped to the running
57  * runtime version. WARNING: this list must be sorted.
58  */
59 static const AssemblyVersionMap framework_assemblies [] = {
60         {"Accessibility", 0},
61         {"Commons.Xml.Relaxng", 0},
62         {"I18N", 0},
63         {"I18N.CJK", 0},
64         {"I18N.MidEast", 0},
65         {"I18N.Other", 0},
66         {"I18N.Rare", 0},
67         {"I18N.West", 0},
68         {"Microsoft.VisualBasic", 1},
69         {"Microsoft.VisualC", 1},
70         {"Mono.Cairo", 0},
71         {"Mono.CompilerServices.SymbolWriter", 0},
72         {"Mono.Data", 0},
73         {"Mono.Data.SqliteClient", 0},
74         {"Mono.Data.SybaseClient", 0},
75         {"Mono.Data.Tds", 0},
76         {"Mono.Data.TdsClient", 0},
77         {"Mono.GetOptions", 0},
78         {"Mono.Http", 0},
79         {"Mono.Posix", 0},
80         {"Mono.Security", 0},
81         {"Mono.Security.Win32", 0},
82         {"Mono.Xml.Ext", 0},
83         {"Novell.Directory.Ldap", 0},
84         {"Npgsql", 0},
85         {"PEAPI", 0},
86         {"System", 0},
87         {"System.Configuration.Install", 0},
88         {"System.Data", 0},
89         {"System.Data.OracleClient", 0},
90         {"System.Data.SqlXml", 0},
91         {"System.Design", 0},
92         {"System.DirectoryServices", 0},
93         {"System.Drawing", 0},
94         {"System.Drawing.Design", 0},
95         {"System.EnterpriseServices", 0},
96         {"System.Management", 0},
97         {"System.Messaging", 0},
98         {"System.Runtime.Remoting", 0},
99         {"System.Runtime.Serialization.Formatters.Soap", 0},
100         {"System.Security", 0},
101         {"System.ServiceProcess", 0},
102         {"System.Web", 0},
103         {"System.Web.Mobile", 0},
104         {"System.Web.Services", 0},
105         {"System.Windows.Forms", 0},
106         {"System.Xml", 0},
107         {"mscorlib", 0}
108 };
109
110 /*
111  * keeps track of loaded assemblies
112  */
113 static GList *loaded_assemblies = NULL;
114 static MonoAssembly *corlib;
115
116 /* This protects loaded_assemblies and image->references */
117 static CRITICAL_SECTION assemblies_mutex;
118
119 /* A hastable of thread->assembly list mappings */
120 static GHashTable *assemblies_loading;
121
122 /* A hashtable of reflection only load thread->assemblies mappings */
123 static GHashTable *assemblies_refonly_loading;
124
125 /* If defined, points to the bundled assembly information */
126 const MonoBundledAssembly **bundles;
127
128 /* Reflection only private hook functions */
129 static MonoAssembly* mono_assembly_refonly_invoke_search_hook (MonoAssemblyName *aname);
130
131 static gchar*
132 encode_public_tok (const guchar *token, gint32 len)
133 {
134         const static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
135         gchar *res;
136         int i;
137
138         res = g_malloc (len * 2 + 1);
139         for (i = 0; i < len; i++) {
140                 res [i * 2] = allowed [token [i] >> 4];
141                 res [i * 2 + 1] = allowed [token [i] & 0xF];
142         }
143         res [len * 2] = 0;
144         return res;
145 }
146
147 static void
148 check_path_env (void)
149 {
150         const char *path;
151         char **splitted;
152         
153         path = g_getenv ("MONO_PATH");
154         if (!path)
155                 return;
156
157         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
158         if (assemblies_path)
159                 g_strfreev (assemblies_path);
160         assemblies_path = splitted;
161         if (g_getenv ("MONO_DEBUG") == NULL)
162                 return;
163
164         while (*splitted) {
165                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
166                         g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
167
168                 splitted++;
169         }
170 }
171
172 static void
173 check_extra_gac_path_env (void) {
174         const char *path;
175         char **splitted;
176         
177         path = g_getenv ("MONO_GAC_PREFIX");
178         if (!path)
179                 return;
180
181         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
182         if (extra_gac_paths)
183                 g_strfreev (extra_gac_paths);
184         extra_gac_paths = splitted;
185         if (g_getenv ("MONO_DEBUG") == NULL)
186                 return;
187
188         while (*splitted) {
189                 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
190                         g_warning ("'%s' in MONO_GAC_PATH doesn't exist or has wrong permissions.", *splitted);
191
192                 splitted++;
193         }
194 }
195
196 gboolean
197 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
198 {
199         if (!l->name || !r->name)
200                 return FALSE;
201
202         if (strcmp (l->name, r->name))
203                 return FALSE;
204
205         if (l->culture && r->culture && strcmp (l->culture, r->culture))
206                 return FALSE;
207
208         if (l->major != r->major || l->minor != r->minor ||
209                         l->build != r->build || l->revision != r->revision)
210                 if (! ((l->major == 0 && l->minor == 0 && l->build == 0 && l->revision == 0) || (r->major == 0 && r->minor == 0 && r->build == 0 && r->revision == 0)))
211                         return FALSE;
212
213         if (!l->public_key_token [0] || !r->public_key_token [0])
214                 return TRUE;
215
216         if (strcmp (l->public_key_token, r->public_key_token))
217                 return FALSE;
218
219         return TRUE;
220 }
221
222 static MonoAssembly*
223 search_loaded (MonoAssemblyName* aname, gboolean refonly)
224 {
225         GList *tmp;
226         MonoAssembly *ass;
227         GList *loading;
228
229         ass = refonly ? mono_assembly_refonly_invoke_search_hook (aname) : mono_assembly_invoke_search_hook (aname);
230         if (ass)
231                 return ass;
232         
233         /*
234          * The assembly might be under load by this thread. In this case, it is
235          * safe to return an incomplete instance to prevent loops.
236          */
237         loading = g_hash_table_lookup (refonly ? assemblies_refonly_loading : assemblies_loading, GetCurrentThread ());
238         for (tmp = loading; tmp; tmp = tmp->next) {
239                 ass = tmp->data;
240                 if (!mono_assembly_names_equal (aname, &ass->aname))
241                         continue;
242
243                 return ass;
244         }
245
246         return NULL;
247 }
248
249 static MonoAssembly *
250 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly)
251 {
252         int i;
253         char *fullpath;
254         MonoAssembly *result;
255
256         for (i = 0; search_path [i]; ++i) {
257                 fullpath = g_build_filename (search_path [i], basename, NULL);
258                 result = mono_assembly_open_full (fullpath, status, refonly);
259                 g_free (fullpath);
260                 if (result)
261                         return result;
262         }
263         return NULL;
264 }
265
266 /**
267  * mono_assembly_setrootdir:
268  * @root_dir: The pathname of the root directory where we will locate assemblies
269  *
270  * This routine sets the internal default root directory for looking up
271  * assemblies.  This is used by Windows installations to compute dynamically
272  * the place where the Mono assemblies are located.
273  *
274  */
275 void
276 mono_assembly_setrootdir (const char *root_dir)
277 {
278         /*
279          * Override the MONO_ASSEMBLIES directory configured at compile time.
280          */
281         /* Leak if called more than once */
282         default_path [0] = g_strdup (root_dir);
283 }
284
285 G_CONST_RETURN gchar *
286 mono_assembly_getrootdir (void)
287 {
288         return default_path [0];
289 }
290
291 /**
292  * mono_assemblies_init:
293  *
294  *  Initialize global variables used by this module.
295  */
296 void
297 mono_assemblies_init (void)
298 {
299 #ifdef PLATFORM_WIN32
300         mono_set_rootdir ();
301 #endif
302
303         check_path_env ();
304         check_extra_gac_path_env ();
305
306         InitializeCriticalSection (&assemblies_mutex);
307
308         assemblies_loading = g_hash_table_new (NULL, NULL);
309         assemblies_refonly_loading = g_hash_table_new (NULL, NULL);
310 }
311
312 gboolean
313 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
314 {
315         MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
316         guint32 cols [MONO_ASSEMBLY_SIZE];
317
318         if (!t->rows)
319                 return FALSE;
320
321         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
322
323         aname->hash_len = 0;
324         aname->hash_value = NULL;
325         aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
326         aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
327         aname->flags = cols [MONO_ASSEMBLY_FLAGS];
328         aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
329         aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
330         aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
331         aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
332         aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
333         if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
334                 gchar* token = g_malloc (8);
335                 gchar* encoded;
336                 int len;
337
338                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
339                 len = mono_metadata_decode_blob_size (aname->public_key, (const char**)&aname->public_key);
340
341                 mono_digest_get_public_token (token, aname->public_key, len);
342                 encoded = encode_public_tok (token, 8);
343                 g_strlcpy (aname->public_key_token, encoded, MONO_PUBLIC_KEY_TOKEN_LENGTH);
344
345                 g_free (encoded);
346                 g_free (token);
347         }
348         else {
349                 aname->public_key = NULL;
350                 memset (aname->public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
351         }
352
353         if (cols [MONO_ASSEMBLY_PUBLIC_KEY]) {
354                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
355         }
356         else
357                 aname->public_key = 0;
358
359         return TRUE;
360 }
361
362 static gchar*
363 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
364 {
365         const gchar *public_tok;
366         int len;
367
368         public_tok = mono_metadata_blob_heap (image, key_index);
369         len = mono_metadata_decode_blob_size (public_tok, &public_tok);
370
371         if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
372                 gchar token [8];
373                 mono_digest_get_public_token (token, public_tok, len);
374                 return encode_public_tok (token, 8);
375         }
376
377         return encode_public_tok (public_tok, len);
378 }
379
380 void
381 mono_assembly_addref (MonoAssembly *assembly)
382 {
383         EnterCriticalSection (&assemblies_mutex);
384         /*g_print ("adding ref from %d to %s (%p)\n", assembly->ref_count, assembly->aname.name, assembly);*/
385         assembly->ref_count++;
386         LeaveCriticalSection (&assemblies_mutex);
387 }
388
389 static MonoAssemblyName *
390 mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_aname)
391 {
392         const MonoRuntimeInfo *current_runtime;
393         int pos, first, last;
394
395         if (aname->name == NULL) return aname;
396         current_runtime = mono_get_runtime_info ();
397
398         first = 0;
399         last = G_N_ELEMENTS (framework_assemblies) - 1;
400         
401         while (first <= last) {
402                 int res;
403                 pos = first + (last - first) / 2;
404                 res = strcmp (aname->name, framework_assemblies[pos].assembly_name);
405                 if (res == 0) {
406                         const AssemblyVersionSet* vset;
407                         int index = framework_assemblies[pos].version_set_index;
408                         g_assert (index < G_N_ELEMENTS (current_runtime->version_sets));
409                         vset = &current_runtime->version_sets [index];
410
411                         if (aname->major == vset->major && aname->minor == vset->minor &&
412                                 aname->build == vset->build && aname->revision == vset->revision)
413                                 return aname;
414                 
415                         if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
416                                 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
417                                         "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
418                                                         aname->name,
419                                                         aname->major, aname->minor, aname->build, aname->revision,
420                                                         vset->major, vset->minor, vset->build, vset->revision
421                                                         );
422                         
423                         memcpy (dest_aname, aname, sizeof(MonoAssemblyName));
424                         dest_aname->major = vset->major;
425                         dest_aname->minor = vset->minor;
426                         dest_aname->build = vset->build;
427                         dest_aname->revision = vset->revision;
428                         return dest_aname;
429                 } else if (res < 0) {
430                         last = pos - 1;
431                 } else {
432                         first = pos + 1;
433                 }
434         }
435         return aname;
436 }
437
438 void
439 mono_assembly_load_reference (MonoImage *image, int index)
440 {
441         MonoTableInfo *t;
442         guint32 cols [MONO_ASSEMBLYREF_SIZE];
443         const char *hash;
444         MonoAssembly *reference;
445         MonoAssemblyName aname;
446         MonoImageOpenStatus status;
447
448         /*
449          * image->references is shared between threads, so we need to access
450          * it inside a critical section.
451          */
452         EnterCriticalSection (&assemblies_mutex);
453         reference = image->references [index];
454         LeaveCriticalSection (&assemblies_mutex);
455         if (reference)
456                 return;
457
458         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
459
460         mono_metadata_decode_row (t, index, cols, MONO_ASSEMBLYREF_SIZE);
461                 
462         hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
463         aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
464         aname.hash_value = hash;
465         aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
466         aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
467         aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
468         aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
469         aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
470         aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
471         aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
472
473         if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
474                 gchar *token = assemblyref_public_tok (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname.flags);
475                 g_strlcpy (aname.public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
476                 g_free (token);
477         } else {
478                 memset (aname.public_key_token, 0, MONO_PUBLIC_KEY_TOKEN_LENGTH);
479         }
480
481         if (image->assembly->ref_only) {
482                 /* We use the loaded corlib */
483                 if (!strcmp (aname.name, "mscorlib"))
484                         reference = mono_assembly_load_full (&aname, image->assembly->basedir, &status, FALSE);
485                 else
486                         reference = mono_assembly_loaded_full (&aname, TRUE);
487                 /*
488                  * Here we must advice that the error was due to
489                  * a non loaded reference using the ReflectionOnly api
490                 */
491                 if (!reference)
492                         reference = (gpointer)-1;
493         } else
494                 reference = mono_assembly_load (&aname, image->assembly->basedir, &status);
495
496         if (reference == NULL){
497                 char *extra_msg = g_strdup ("");
498
499                 if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
500                         extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly->basedir);
501                 } else if (status == MONO_IMAGE_ERROR_ERRNO) {
502                         extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
503                 } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
504                         extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
505                 } else if (status == MONO_IMAGE_IMAGE_INVALID) {
506                         extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
507                 }
508                 
509                 g_warning ("The following assembly referenced from %s could not be loaded:\n"
510                                    "     Assembly:   %s    (assemblyref_index=%d)\n"
511                                    "     Version:    %d.%d.%d.%d\n"
512                                    "     Public Key: %s\n%s",
513                                    image->name, aname.name, index,
514                                    aname.major, aname.minor, aname.build, aname.revision,
515                                    strlen(aname.public_key_token) == 0 ? "(none)" : aname.public_key_token, extra_msg);
516                 g_free (extra_msg);
517         }
518
519         EnterCriticalSection (&assemblies_mutex);
520         if (reference == NULL) {
521                 /* Flag as not found */
522                 reference = (gpointer)-1;
523         } else {
524                 reference->ref_count++;
525         }       
526
527         if (!image->references [index])
528                 image->references [index] = reference;
529         LeaveCriticalSection (&assemblies_mutex);
530
531         if (image->references [index] != reference) {
532                 /* Somebody loaded it before us */
533                 mono_assembly_close (reference);
534         }
535 }
536
537 void
538 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
539 {
540         MonoTableInfo *t;
541         int i;
542
543         *status = MONO_IMAGE_OK;
544
545         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
546         
547         image->references = g_new0 (MonoAssembly *, t->rows + 1);
548
549         /* resolve assembly references for modules */
550         for (i = 0; i < image->module_count; i++){
551                 if (image->modules [i]) {
552                         image->modules [i]->assembly = image->assembly;
553                         mono_assembly_load_references (image->modules [i], status);
554                 }
555         }
556 }
557
558 typedef struct AssemblyLoadHook AssemblyLoadHook;
559 struct AssemblyLoadHook {
560         AssemblyLoadHook *next;
561         MonoAssemblyLoadFunc func;
562         gpointer user_data;
563 };
564
565 AssemblyLoadHook *assembly_load_hook = NULL;
566
567 void
568 mono_assembly_invoke_load_hook (MonoAssembly *ass)
569 {
570         AssemblyLoadHook *hook;
571
572         for (hook = assembly_load_hook; hook; hook = hook->next) {
573                 hook->func (ass, hook->user_data);
574         }
575 }
576
577 void
578 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
579 {
580         AssemblyLoadHook *hook;
581         
582         g_return_if_fail (func != NULL);
583
584         hook = g_new0 (AssemblyLoadHook, 1);
585         hook->func = func;
586         hook->user_data = user_data;
587         hook->next = assembly_load_hook;
588         assembly_load_hook = hook;
589 }
590
591 typedef struct AssemblySearchHook AssemblySearchHook;
592 struct AssemblySearchHook {
593         AssemblySearchHook *next;
594         MonoAssemblySearchFunc func;
595         gpointer user_data;
596 };
597
598 AssemblySearchHook *assembly_search_hook = NULL;
599 static AssemblySearchHook *assembly_refonly_search_hook = NULL;
600
601 MonoAssembly*
602 mono_assembly_invoke_search_hook (MonoAssemblyName *aname)
603 {
604         AssemblySearchHook *hook;
605
606         for (hook = assembly_search_hook; hook; hook = hook->next) {
607                 MonoAssembly *ass = hook->func (aname, hook->user_data);
608                 if (ass)
609                         return ass;
610         }
611
612         return NULL;
613 }
614
615 static MonoAssembly*
616 mono_assembly_refonly_invoke_search_hook (MonoAssemblyName *aname)
617 {
618         AssemblySearchHook *hook;
619
620         for (hook = assembly_refonly_search_hook; hook; hook = hook->next) {
621                 MonoAssembly *ass = hook->func (aname, hook->user_data);
622                 if (ass)
623                         return ass;
624         }
625
626         return NULL;
627 }
628
629 void          
630 mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
631 {
632         AssemblySearchHook *hook;
633         
634         g_return_if_fail (func != NULL);
635
636         hook = g_new0 (AssemblySearchHook, 1);
637         hook->func = func;
638         hook->user_data = user_data;
639         hook->next = assembly_search_hook;
640         assembly_search_hook = hook;
641 }       
642
643 void
644 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
645 {
646         AssemblySearchHook *hook;
647
648         g_return_if_fail (func != NULL);
649
650         hook = g_new0 (AssemblySearchHook, 1);
651         hook->func = func;
652         hook->user_data = user_data;
653         hook->next = assembly_refonly_search_hook;
654         assembly_refonly_search_hook = hook;
655 }
656
657 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
658 struct AssemblyPreLoadHook {
659         AssemblyPreLoadHook *next;
660         MonoAssemblyPreLoadFunc func;
661         gpointer user_data;
662 };
663
664 static AssemblyPreLoadHook *assembly_preload_hook = NULL;
665 static AssemblyPreLoadHook *assembly_refonly_preload_hook = NULL;
666
667 static MonoAssembly *
668 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
669 {
670         AssemblyPreLoadHook *hook;
671         MonoAssembly *assembly;
672
673         for (hook = assembly_preload_hook; hook; hook = hook->next) {
674                 assembly = hook->func (aname, assemblies_path, hook->user_data);
675                 if (assembly != NULL)
676                         return assembly;
677         }
678
679         return NULL;
680 }
681
682 static MonoAssembly *
683 invoke_assembly_refonly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
684 {
685         AssemblyPreLoadHook *hook;
686         MonoAssembly *assembly;
687
688         for (hook = assembly_refonly_preload_hook; hook; hook = hook->next) {
689                 assembly = hook->func (aname, assemblies_path, hook->user_data);
690                 if (assembly != NULL)
691                         return assembly;
692         }
693
694         return NULL;
695 }
696
697 void
698 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
699 {
700         AssemblyPreLoadHook *hook;
701         
702         g_return_if_fail (func != NULL);
703
704         hook = g_new0 (AssemblyPreLoadHook, 1);
705         hook->func = func;
706         hook->user_data = user_data;
707         hook->next = assembly_preload_hook;
708         assembly_preload_hook = hook;
709 }
710
711 void
712 mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
713 {
714         AssemblyPreLoadHook *hook;
715         
716         g_return_if_fail (func != NULL);
717
718         hook = g_new0 (AssemblyPreLoadHook, 1);
719         hook->func = func;
720         hook->user_data = user_data;
721         hook->next = assembly_refonly_preload_hook;
722         assembly_refonly_preload_hook = hook;
723 }
724
725 static gchar *
726 absolute_dir (const gchar *filename)
727 {
728         gchar *cwd;
729         gchar *mixed;
730         gchar **parts;
731         gchar *part;
732         GList *list, *tmp;
733         GString *result;
734         gchar *res;
735         gint i;
736
737         if (g_path_is_absolute (filename))
738                 return g_path_get_dirname (filename);
739
740         cwd = g_get_current_dir ();
741         mixed = g_build_filename (cwd, filename, NULL);
742         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
743         g_free (mixed);
744         g_free (cwd);
745
746         list = NULL;
747         for (i = 0; (part = parts [i]) != NULL; i++) {
748                 if (!strcmp (part, "."))
749                         continue;
750
751                 if (!strcmp (part, "..")) {
752                         if (list && list->next) /* Don't remove root */
753                                 list = g_list_delete_link (list, list);
754                 } else {
755                         list = g_list_prepend (list, part);
756                 }
757         }
758
759         result = g_string_new ("");
760         list = g_list_reverse (list);
761
762         /* Ignores last data pointer, which should be the filename */
763         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
764                 if (tmp->data)
765                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
766                                                                 G_DIR_SEPARATOR);
767         
768         res = result->str;
769         g_string_free (result, FALSE);
770         g_list_free (list);
771         g_strfreev (parts);
772         if (*res == '\0') {
773                 g_free (res);
774                 return g_strdup (".");
775         }
776
777         return res;
778 }
779
780 /** 
781  * mono_assembly_open_from_bundle:
782  * @filename: Filename requested
783  * @status: return value
784  *
785  * This routine tries to open the assembly specified by `filename' from the
786  * defined bundles, if found, returns the MonoImage for it, if not found
787  * returns NULL
788  */
789 static MonoImage *
790 mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *status)
791 {
792         int i;
793         char *name = g_path_get_basename (filename);
794         MonoImage *image = NULL;
795
796         /*
797          * we do a very simple search for bundled assemblies: it's not a general 
798          * purpose assembly loading mechanism.
799          */
800         EnterCriticalSection (&assemblies_mutex);
801         for (i = 0; !image && bundles [i]; ++i) {
802                 if (strcmp (bundles [i]->name, name) == 0) {
803                         image = mono_image_open_from_data ((char*)bundles [i]->data, bundles [i]->size, FALSE, status);
804                         break;
805                 }
806         }
807         LeaveCriticalSection (&assemblies_mutex);
808         g_free (name);
809         if (image) {
810                 mono_image_addref (image);
811                 return image;
812         }
813         return NULL;
814 }
815
816 static MonoImage*
817 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
818 {
819         MonoImage *image = NULL;
820
821         if (bundles != NULL){
822                 image = mono_assembly_open_from_bundle (filename, status);
823
824                 if (image != NULL)
825                         return image;
826         }
827         EnterCriticalSection (&assemblies_mutex);
828         image = mono_image_open (filename, status);
829         LeaveCriticalSection (&assemblies_mutex);
830
831         return image;
832 }
833
834 MonoAssembly *
835 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
836 {
837         MonoImage *image;
838         MonoAssembly *ass;
839         MonoImageOpenStatus def_status;
840         gchar *fname;
841         
842         g_return_val_if_fail (filename != NULL, NULL);
843
844         if (!status)
845                 status = &def_status;
846         *status = MONO_IMAGE_OK;
847
848         if (strncmp (filename, "file://", 7) == 0) {
849                 GError *error = NULL;
850                 gchar *uri = (gchar *) filename;
851                 gchar *tmpuri;
852
853                 /*
854                  * MS allows file://c:/... and fails on file://localhost/c:/... 
855                  * They also throw an IndexOutOfRangeException if "file://"
856                  */
857                 if (uri [7] != '/')
858                         uri = g_strdup_printf ("file:///%s", uri + 7);
859         
860                 tmpuri = uri;
861                 uri = mono_escape_uri_string (tmpuri);
862                 fname = g_filename_from_uri (uri, NULL, &error);
863                 g_free (uri);
864
865                 if (tmpuri != filename)
866                         g_free (tmpuri);
867
868                 if (error != NULL) {
869                         g_warning ("%s\n", error->message);
870                         g_error_free (error);
871                         fname = g_strdup (filename);
872                 }
873         } else {
874                 fname = g_strdup (filename);
875         }
876
877         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
878                         "Assembly Loader probing location: '%s'.", filename);
879         image = do_mono_assembly_open (fname, status);
880
881         if (!image){
882                 *status = MONO_IMAGE_ERROR_ERRNO;
883                 g_free (fname);
884                 return NULL;
885         }
886
887         ass = mono_assembly_load_from_full (image, fname, status, refonly);
888
889         if (ass) {
890                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
891                                 "Assembly Loader loaded assembly from location: '%s'.", filename);
892                 if (!refonly)
893                         mono_config_for_assembly (ass->image);
894         }
895
896         g_free (fname);
897
898         return ass;
899 }
900
901 /**
902  * mono_assembly_open:
903  * @filename: Opens the assembly pointed out by this name
904  * @status: where a status code can be returned
905  *
906  * mono_assembly_open opens the PE-image pointed by @filename, and
907  * loads any external assemblies referenced by it.
908  *
909  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
910  * it. 
911  */
912 MonoAssembly *
913 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
914 {
915         return mono_assembly_open_full (filename, status, FALSE);
916 }
917
918 MonoAssembly *
919 mono_assembly_load_from_full (MonoImage *image, const char*fname, 
920                          MonoImageOpenStatus *status, gboolean refonly)
921 {
922         MonoAssembly *ass, *ass2;
923         char *base_dir;
924         GList *loading;
925         GHashTable *ass_loading;
926
927 #if defined (PLATFORM_WIN32)
928         {
929                 gchar *tmp_fn;
930                 int i;
931
932                 tmp_fn = g_strdup (fname);
933                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
934                         if (tmp_fn [i] == '/')
935                                 tmp_fn [i] = '\\';
936                 }
937
938                 base_dir = absolute_dir (tmp_fn);
939                 g_free (tmp_fn);
940         }
941 #else
942         base_dir = absolute_dir (fname);
943 #endif
944
945         /*
946          * To avoid deadlocks and scalability problems, we load assemblies outside
947          * the assembly lock. This means that multiple threads might try to load
948          * the same assembly at the same time. The first one to load it completely
949          * "wins", the other threads free their copy and use the one loaded by
950          * the winning thread.
951          */
952
953         /*
954          * Create assembly struct, and enter it into the assembly cache
955          */
956         ass = g_new0 (MonoAssembly, 1);
957         ass->basedir = base_dir;
958         ass->ref_only = refonly;
959         ass->image = image;
960         ass->ref_count = 1;
961
962         mono_assembly_fill_assembly_name (image, &ass->aname);
963
964         /* 
965          * Atomically search the loaded list and add ourselves to it if necessary.
966          */
967         EnterCriticalSection (&assemblies_mutex);
968         if (ass->aname.name) {
969                 /* avoid loading the same assembly twice for now... */
970                 ass2 = search_loaded (&ass->aname, refonly);
971                 if (ass2) {
972                         g_free (ass);
973                         g_free (base_dir);
974                         mono_image_close (image);
975                         *status = MONO_IMAGE_OK;
976                         LeaveCriticalSection (&assemblies_mutex);
977                         return ass2;
978                 }
979         }
980         ass_loading = refonly ? assemblies_refonly_loading : assemblies_loading;
981         loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
982         loading = g_list_prepend (loading, ass);
983         g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
984         LeaveCriticalSection (&assemblies_mutex);
985
986         image->assembly = ass;
987
988         mono_assembly_load_references (image, status);
989
990         EnterCriticalSection (&assemblies_mutex);
991
992         loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
993         loading = g_list_remove (loading, ass);
994         if (loading == NULL)
995                 /* Prevent memory leaks */
996                 g_hash_table_remove (ass_loading, GetCurrentThread ());
997         else
998                 g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
999         if (*status != MONO_IMAGE_OK) {
1000                 LeaveCriticalSection (&assemblies_mutex);
1001                 mono_assembly_close (ass);
1002                 return NULL;
1003         }
1004
1005         if (ass->aname.name) {
1006                 ass2 = search_loaded (&ass->aname, refonly);
1007                 if (ass2) {
1008                         /* Somebody else has loaded the assembly before us */
1009                         LeaveCriticalSection (&assemblies_mutex);
1010                         mono_assembly_close (ass);
1011                         return ass2;
1012                 }
1013         }
1014
1015         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
1016         LeaveCriticalSection (&assemblies_mutex);
1017
1018         mono_assembly_invoke_load_hook (ass);
1019
1020         return ass;
1021 }
1022
1023 MonoAssembly *
1024 mono_assembly_load_from (MonoImage *image, const char *fname,
1025                          MonoImageOpenStatus *status)
1026 {
1027         return mono_assembly_load_from_full (image, fname, status, FALSE);
1028 }
1029
1030 static MonoAssembly*
1031 probe_for_partial_name (const char *basepath, const char *fullname, MonoImageOpenStatus *status)
1032 {
1033         MonoAssembly *res = NULL;
1034         gchar *fullpath;
1035         GDir *dirhandle;
1036         const char* direntry;
1037
1038         dirhandle = g_dir_open (basepath, 0, NULL);
1039         if (!dirhandle)
1040                 return NULL;
1041
1042         while ((direntry = g_dir_read_name (dirhandle))) {
1043                 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
1044                 res = mono_assembly_open (fullpath, status);
1045                 g_free (fullpath);
1046                 if (res)
1047                         break;
1048         }
1049         g_dir_close (dirhandle);
1050
1051         return res;
1052 }
1053
1054 MonoAssembly*
1055 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
1056 {
1057         MonoAssembly *res;
1058         MonoAssemblyName aname;
1059         gchar *fullname, *gacpath;
1060         gchar **paths;
1061
1062         memset (&aname, 0, sizeof (MonoAssemblyName));
1063         aname.name = name;
1064
1065         res = mono_assembly_loaded (&aname);
1066         if (res)
1067                 return res;
1068
1069         res = invoke_assembly_preload_hook (&aname, assemblies_path);
1070         if (res) {
1071                 res->in_gac = FALSE;
1072                 return res;
1073         }
1074
1075         fullname = g_strdup_printf ("%s.dll", name);
1076
1077         if (extra_gac_paths) {
1078                 paths = extra_gac_paths;
1079                 while (!res && *paths) {
1080                         gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", name, NULL);
1081                         res = probe_for_partial_name (gacpath, fullname, status);
1082                         g_free (gacpath);
1083                         paths++;
1084                 }
1085         }
1086
1087         if (res) {
1088                 res->in_gac = TRUE;
1089                 g_free (fullname);
1090                 return res;
1091                 
1092         }
1093
1094         gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", name, NULL);
1095         res = probe_for_partial_name (gacpath, fullname, status);
1096         g_free (gacpath);
1097
1098         if (res)
1099                 res->in_gac = TRUE;
1100
1101         g_free (fullname);
1102
1103         return res;
1104 }
1105
1106 /**
1107  * mono_assembly_load_from_gac
1108  *
1109  * @aname: The assembly name object
1110  */
1111 static MonoAssembly*
1112 mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImageOpenStatus *status, MonoBoolean refonly)
1113 {
1114         MonoAssembly *result = NULL;
1115         gchar *name, *version, *culture, *fullpath, *subpath;
1116         gint32 len;
1117         gchar **paths;
1118
1119         if (aname->public_key_token [0] == 0) {
1120                 return NULL;
1121         }
1122
1123         if (strstr (aname->name, ".dll")) {
1124                 len = strlen (filename) - 4;
1125                 name = g_malloc (len);
1126                 strncpy (name, aname->name, len);
1127         } else {
1128                 name = g_strdup (aname->name);
1129         }
1130
1131         if (aname->culture) {
1132                 culture = g_strdup (aname->culture);
1133                 g_strdown (culture);
1134         } else {
1135                 culture = g_strdup ("");
1136         }
1137
1138         version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
1139                         aname->minor, aname->build, aname->revision,
1140                         culture, aname->public_key_token);
1141         
1142         subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
1143         g_free (name);
1144         g_free (version);
1145         g_free (culture);
1146
1147         if (extra_gac_paths) {
1148                 paths = extra_gac_paths;
1149                 while (!result && *paths) {
1150                         fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", subpath, NULL);
1151                         result = mono_assembly_open_full (fullpath, status, refonly);
1152                         g_free (fullpath);
1153                         paths++;
1154                 }
1155         }
1156
1157         if (result) {
1158                 result->in_gac = TRUE;
1159                 g_free (subpath);
1160                 return result;
1161         }
1162
1163         fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
1164                         "mono", "gac", subpath, NULL);
1165         result = mono_assembly_open_full (fullpath, status, refonly);
1166         g_free (fullpath);
1167
1168         if (result)
1169                 result->in_gac = TRUE;
1170         
1171         g_free (subpath);
1172
1173         return result;
1174 }
1175
1176
1177 MonoAssembly*
1178 mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *status)
1179 {
1180         char *corlib_file;
1181
1182         if (corlib) {
1183                 /* g_print ("corlib already loaded\n"); */
1184                 return corlib;
1185         }
1186         
1187         if (assemblies_path) {
1188                 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE);
1189                 if (corlib)
1190                         return corlib;
1191         }
1192
1193         /* Load corlib from mono/<version> */
1194         
1195         corlib_file = g_build_filename ("mono", runtime->framework_version, "mscorlib.dll", NULL);
1196         if (assemblies_path) {
1197                 corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE);
1198                 if (corlib) {
1199                         g_free (corlib_file);
1200                         return corlib;
1201                 }
1202         }
1203         corlib = load_in_path (corlib_file, default_path, status, FALSE);
1204         g_free (corlib_file);
1205
1206         return corlib;
1207 }
1208
1209
1210 MonoAssembly*
1211 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
1212 {
1213         MonoAssembly *result;
1214         char *fullpath, *filename;
1215         MonoAssemblyName maped_aname;
1216
1217         aname = mono_assembly_remap_version (aname, &maped_aname);
1218         
1219         result = mono_assembly_loaded_full (aname, refonly);
1220         if (result)
1221                 return result;
1222
1223         result = refonly ? invoke_assembly_refonly_preload_hook (aname, assemblies_path) : invoke_assembly_preload_hook (aname, assemblies_path);
1224         if (result) {
1225                 result->in_gac = FALSE;
1226                 return result;
1227         }
1228
1229         /* Currently we retrieve the loaded corlib for reflection 
1230          * only requests, like a common reflection only assembly 
1231          */
1232         if (strcmp (aname->name, "mscorlib") == 0 || strcmp (aname->name, "mscorlib.dll") == 0) {
1233                 return mono_assembly_load_corlib (mono_get_runtime_info (), status);
1234         }
1235
1236         if (strstr (aname->name, ".dll"))
1237                 filename = g_strdup (aname->name);
1238         else
1239                 filename = g_strconcat (aname->name, ".dll", NULL);
1240
1241         result = mono_assembly_load_from_gac (aname, filename, status, refonly);
1242         if (result) {
1243                 g_free (filename);
1244                 return result;
1245         }
1246
1247         if (basedir) {
1248                 fullpath = g_build_filename (basedir, filename, NULL);
1249                 result = mono_assembly_open_full (fullpath, status, refonly);
1250                 g_free (fullpath);
1251                 if (result) {
1252                         result->in_gac = FALSE;
1253                         g_free (filename);
1254                         return result;
1255                 }
1256         }
1257
1258         result = load_in_path (filename, default_path, status, refonly);
1259         if (result)
1260                 result->in_gac = FALSE;
1261         g_free (filename);
1262         return result;
1263 }
1264
1265 MonoAssembly*
1266 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
1267 {
1268         return mono_assembly_load_full (aname, basedir, status, FALSE);
1269 }
1270         
1271 MonoAssembly*
1272 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
1273 {
1274         MonoAssembly *res;
1275         MonoAssemblyName maped_aname;
1276
1277         aname = mono_assembly_remap_version (aname, &maped_aname);
1278
1279         EnterCriticalSection (&assemblies_mutex);
1280         res = search_loaded (aname, refonly);
1281         LeaveCriticalSection (&assemblies_mutex);
1282
1283         return res;
1284 }
1285
1286 MonoAssembly*
1287 mono_assembly_loaded (MonoAssemblyName *aname)
1288 {
1289         return mono_assembly_loaded_full (aname, FALSE);
1290 }
1291
1292 void
1293 mono_assembly_close (MonoAssembly *assembly)
1294 {
1295         MonoImage *image;
1296         
1297         g_return_if_fail (assembly != NULL);
1298
1299         EnterCriticalSection (&assemblies_mutex);
1300         /*g_print ("destroy assembly %p %d (%s)\n", assembly, assembly->ref_count, assembly->image->name);*/
1301         g_assert (assembly->ref_count > 0);
1302         if (--assembly->ref_count != 0) {
1303                 LeaveCriticalSection (&assemblies_mutex);
1304                 return;
1305         }
1306         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
1307         LeaveCriticalSection (&assemblies_mutex);
1308         image = assembly->image;
1309         /* assemblies belong to domains, so the domain code takes care of unloading the
1310          * referenced assemblies
1311          */
1312
1313         mono_image_close (assembly->image);
1314
1315         g_free (assembly->basedir);
1316         if (!assembly->dynamic)
1317                 g_free (assembly);
1318 }
1319
1320 MonoImage*
1321 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
1322 {
1323         MonoImageOpenStatus status;
1324         MonoImage *module;
1325
1326         module = mono_image_load_file_for_image (assembly->image, idx);
1327         if (module)
1328                 mono_assembly_load_references (module, &status);
1329
1330         return module;
1331 }
1332
1333 void
1334 mono_assembly_foreach (GFunc func, gpointer user_data)
1335 {
1336         GList *copy;
1337
1338         /*
1339          * We make a copy of the list to avoid calling the callback inside the 
1340          * lock, which could lead to deadlocks.
1341          */
1342         EnterCriticalSection (&assemblies_mutex);
1343         copy = g_list_copy (loaded_assemblies);
1344         LeaveCriticalSection (&assemblies_mutex);
1345
1346         g_list_foreach (loaded_assemblies, func, user_data);
1347
1348         g_list_free (copy);
1349 }
1350
1351 /*
1352  * Holds the assembly of the application, for
1353  * System.Diagnostics.Process::MainModule
1354  */
1355 static MonoAssembly *main_assembly=NULL;
1356
1357 void
1358 mono_assembly_set_main (MonoAssembly *assembly)
1359 {
1360         main_assembly=assembly;
1361 }
1362
1363 MonoAssembly *
1364 mono_assembly_get_main (void)
1365 {
1366         return(main_assembly);
1367 }
1368
1369 MonoImage*
1370 mono_assembly_get_image (MonoAssembly *assembly)
1371 {
1372         return assembly->image;
1373 }
1374
1375 void
1376 mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
1377 {
1378         bundles = assemblies;
1379 }