2004-04-09 Zoltan Varga <vargaz@freemail.hu>
[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 <stdlib.h>
18 #include "assembly.h"
19 #include "image.h"
20 #include "cil-coff.h"
21 #include "rawbuffer.h"
22 #ifdef WITH_BUNDLE
23 #include "mono-bundle.h"
24 #endif
25 #include <mono/io-layer/io-layer.h>
26 #include <mono/utils/mono-uri.h>
27
28 /* the default search path is just MONO_ASSEMBLIES */
29 static const char*
30 default_path [] = {
31         MONO_ASSEMBLIES,
32         NULL
33 };
34
35 static char **assemblies_path = NULL;
36
37 /*
38  * keeps track of loaded assemblies
39  */
40 static GList *loaded_assemblies = NULL;
41 static MonoAssembly *corlib;
42
43 /* This protects loaded_assemblies and image->references */
44 static CRITICAL_SECTION assemblies_mutex;
45
46 /* A hastable of thread->assembly list mappings */
47 static GHashTable *assemblies_loading;
48
49 #ifdef PLATFORM_WIN32
50
51 static void
52 init_default_path (void)
53 {
54         int i;
55
56         default_path [0] = g_strdup (MONO_ASSEMBLIES);
57         for (i = strlen (MONO_ASSEMBLIES) - 1; i >= 0; i--) {
58                 if (default_path [0][i] == '/')
59                         ((char*) default_path [0])[i] = '\\';
60         }
61 }
62 #endif
63
64 static void
65 check_env (void) {
66         const char *path;
67         char **splitted;
68         
69         path = getenv ("MONO_PATH");
70         if (!path)
71                 return;
72         splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
73         if (assemblies_path)
74                 g_strfreev (assemblies_path);
75         assemblies_path = splitted;
76 }
77
78 /* assemblies_mutex must be held by the caller */
79 static MonoAssembly*
80 search_loaded (MonoAssemblyName* aname)
81 {
82         GList *tmp;
83         MonoAssembly *ass;
84         GList *loading;
85         
86         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
87                 ass = tmp->data;
88                 if (!ass->aname.name)
89                         continue;
90                 /* we just compare the name, but later we'll do all the checks */
91                 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
92                 if (strcmp (aname->name, ass->aname.name))
93                         continue;
94                 /* g_print ("success\n"); */
95
96                 return ass;
97         }
98
99         /*
100          * The assembly might be under load by this thread. In this case, it is
101          * safe to return an incomplete instance to prevent loops.
102          */
103         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
104         for (tmp = loading; tmp; tmp = tmp->next) {
105                 ass = tmp->data;
106                 if (!ass->aname.name)
107                         continue;
108                 if (strcmp (aname->name, ass->aname.name))
109                         continue;
110
111                 return ass;
112         }
113
114         return NULL;
115 }
116
117 static MonoAssembly *
118 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
119 {
120         int i;
121         char *fullpath;
122         MonoAssembly *result;
123
124         for (i = 0; search_path [i]; ++i) {
125                 fullpath = g_build_filename (search_path [i], basename, NULL);
126                 result = mono_assembly_open (fullpath, status);
127                 g_free (fullpath);
128                 if (result)
129                         return result;
130         }
131         return NULL;
132 }
133
134 /**
135  * mono_assembly_setrootdir:
136  * @root_dir: The pathname of the root directory where we will locate assemblies
137  *
138  * This routine sets the internal default root directory for looking up
139  * assemblies.  This is used by Windows installations to compute dynamically
140  * the place where the Mono assemblies are located.
141  *
142  */
143 void
144 mono_assembly_setrootdir (const char *root_dir)
145 {
146         /*
147          * Override the MONO_ASSEMBLIES directory configured at compile time.
148          */
149         /* Leak if called more than once */
150         default_path [0] = g_strdup (root_dir);
151 }
152
153 /**
154  * mono_assemblies_init:
155  *
156  *  Initialize global variables used by this module.
157  */
158 void
159 mono_assemblies_init (void)
160 {
161 #ifdef PLATFORM_WIN32
162         init_default_path ();
163 #endif
164
165         check_env ();
166
167         InitializeCriticalSection (&assemblies_mutex);
168
169         assemblies_loading = g_hash_table_new (NULL, NULL);
170 }
171
172 gboolean
173 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
174 {
175         MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
176         guint32 cols [MONO_ASSEMBLY_SIZE];
177
178         if (!t->rows)
179                 return FALSE;
180
181         mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
182
183         aname->hash_len = 0;
184         aname->hash_value = NULL;
185         aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
186         aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
187         aname->flags = cols [MONO_ASSEMBLY_FLAGS];
188         aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
189         aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
190         aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
191         aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
192         aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
193         if (cols [MONO_ASSEMBLY_PUBLIC_KEY])
194                 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
195         else
196                 aname->public_key = 0;
197
198         return TRUE;
199 }
200
201 void
202 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
203 {
204         MonoTableInfo *t;
205         guint32 cols [MONO_ASSEMBLYREF_SIZE];
206         const char *hash;
207         int i;
208         MonoAssembly **references = NULL;
209
210         *status = MONO_IMAGE_OK;
211         
212         /*
213          * image->references is shared between threads, so we need to access
214          * it inside a critical section.
215          */
216         EnterCriticalSection (&assemblies_mutex);
217         references = image->references;
218         LeaveCriticalSection (&assemblies_mutex);
219         if (references)
220                 return;
221
222         t = &image->tables [MONO_TABLE_ASSEMBLYREF];
223
224         references = g_new0 (MonoAssembly *, t->rows + 1);
225
226         /*
227          * Load any assemblies this image references
228          */
229         for (i = 0; i < t->rows; i++) {
230                 MonoAssemblyName aname;
231
232                 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
233                 
234                 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
235                 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
236                 aname.hash_value = hash;
237                 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
238                 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
239                 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
240                 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
241                 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
242                 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
243                 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
244
245                 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
246
247                 if (references [i] == NULL){
248                         int j;
249                         
250                         for (j = 0; j < i; j++)
251                                 mono_assembly_close (references [j]);
252                         g_free (references);
253
254                         g_warning ("Could not find assembly %s", aname.name);
255                         *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
256                         return;
257                 }
258
259                 /* 
260                  * This check is disabled since lots of people seem to have an older
261                  * corlib which triggers this.
262                  */
263                 /* 
264                 if (image->references [i]->image == image)
265                         g_error ("Error: Assembly %s references itself", image->name);
266                 */
267         }
268         references [i] = NULL;
269
270         /* resolve assembly references for modules */
271         t = &image->tables [MONO_TABLE_MODULEREF];
272         for (i = 0; i < t->rows; i++){
273                 if (image->modules [i]) {
274                         image->modules [i]->assembly = image->assembly;
275                         mono_assembly_load_references (image->modules [i], status);
276                 }
277         }
278
279         EnterCriticalSection (&assemblies_mutex);
280         if (!image->references)
281                 image->references = references;
282         LeaveCriticalSection (&assemblies_mutex);
283
284         if (image->references != references) {
285                 /* Somebody loaded it before us */
286                 for (i = 0; i < t->rows; i++)
287                         mono_assembly_close (references [i]);
288                 g_free (references);
289         }
290 }
291
292 typedef struct AssemblyLoadHook AssemblyLoadHook;
293 struct AssemblyLoadHook {
294         AssemblyLoadHook *next;
295         MonoAssemblyLoadFunc func;
296         gpointer user_data;
297 };
298
299 AssemblyLoadHook *assembly_load_hook = NULL;
300
301 void
302 mono_assembly_invoke_load_hook (MonoAssembly *ass)
303 {
304         AssemblyLoadHook *hook;
305
306         for (hook = assembly_load_hook; hook; hook = hook->next) {
307                 hook->func (ass, hook->user_data);
308         }
309 }
310
311 void
312 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
313 {
314         AssemblyLoadHook *hook;
315         
316         g_return_if_fail (func != NULL);
317
318         hook = g_new0 (AssemblyLoadHook, 1);
319         hook->func = func;
320         hook->user_data = user_data;
321         hook->next = assembly_load_hook;
322         assembly_load_hook = hook;
323 }
324
325 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
326 struct AssemblyPreLoadHook {
327         AssemblyPreLoadHook *next;
328         MonoAssemblyPreLoadFunc func;
329         gpointer user_data;
330 };
331
332 AssemblyPreLoadHook *assembly_preload_hook = NULL;
333
334 static MonoAssembly *
335 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
336 {
337         AssemblyPreLoadHook *hook;
338         MonoAssembly *assembly;
339
340         for (hook = assembly_preload_hook; hook; hook = hook->next) {
341                 assembly = hook->func (aname, assemblies_path, hook->user_data);
342                 if (assembly != NULL)
343                         return assembly;
344         }
345
346         return NULL;
347 }
348
349 void
350 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
351 {
352         AssemblyPreLoadHook *hook;
353         
354         g_return_if_fail (func != NULL);
355
356         hook = g_new0 (AssemblyPreLoadHook, 1);
357         hook->func = func;
358         hook->user_data = user_data;
359         hook->next = assembly_preload_hook;
360         assembly_preload_hook = hook;
361 }
362
363 static gchar *
364 absolute_dir (const gchar *filename)
365 {
366         gchar *cwd;
367         gchar *mixed;
368         gchar **parts;
369         gchar *part;
370         GList *list, *tmp;
371         GString *result;
372         gchar *res;
373         gint i;
374
375         if (g_path_is_absolute (filename))
376                 return g_path_get_dirname (filename);
377
378         cwd = g_get_current_dir ();
379         mixed = g_build_filename (cwd, filename, NULL);
380         parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
381         g_free (mixed);
382         g_free (cwd);
383
384         list = NULL;
385         for (i = 0; (part = parts [i]) != NULL; i++) {
386                 if (!strcmp (part, "."))
387                         continue;
388
389                 if (!strcmp (part, "..")) {
390                         if (list && list->next) /* Don't remove root */
391                                 list = g_list_delete_link (list, list);
392                 } else {
393                         list = g_list_prepend (list, part);
394                 }
395         }
396
397         result = g_string_new ("");
398         list = g_list_reverse (list);
399
400         /* Ignores last data pointer, which should be the filename */
401         for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
402                 if (tmp->data)
403                         g_string_append_printf (result, "%s%c", (char *) tmp->data,
404                                                                 G_DIR_SEPARATOR);
405         
406         res = result->str;
407         g_string_free (result, FALSE);
408         g_list_free (list);
409         g_strfreev (parts);
410         if (*res == '\0') {
411                 g_free (res);
412                 return g_strdup (".");
413         }
414
415         return res;
416 }
417
418 static MonoImage*
419 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
420 {
421         MonoImage *image = NULL;
422 #ifdef WITH_BUNDLE
423         int i;
424         char *name = g_path_get_basename (filename);
425         char *dot = strrchr (name, '.');
426         GList *tmp;
427         MonoAssembly *ass;
428         
429         if (dot)
430                 *dot = 0;
431         if (strcmp (name, "corlib") == 0) {
432                 g_free (name);
433                 name = g_strdup ("mscorlib");
434         }
435         /* we do a very simple search for bundled assemblies: it's not a general 
436          * purpose assembly loading mechanism.
437          */
438         EnterCriticalSection (&assemblies_mutex);
439         for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
440                 ass = tmp->data;
441                 if (!ass->aname.name)
442                         continue;
443                 if (strcmp (name, ass->aname.name))
444                         continue;
445                 image = ass->image;
446                 break;
447         }
448
449         for (i = 0; !image && bundled_assemblies [i]; ++i) {
450                 if (strcmp (bundled_assemblies [i]->name, name) == 0) {
451                         image = mono_image_open_from_data ((char*)bundled_assemblies [i]->data, bundled_assemblies [i]->size, FALSE, status);
452                         break;
453                 }
454         }
455         LeaveCriticalSection (&assemblies_mutex);
456         g_free (name);
457         if (image) {
458                 mono_image_addref (image);
459                 return image;
460         }
461 #endif
462         EnterCriticalSection (&assemblies_mutex);
463         image = mono_image_open (filename, status);
464         LeaveCriticalSection (&assemblies_mutex);
465         
466         return image;
467 }
468
469 /**
470  * mono_assembly_open:
471  * @filename: Opens the assembly pointed out by this name
472  * @status: where a status code can be returned
473  *
474  * mono_assembly_open opens the PE-image pointed by @filename, and
475  * loads any external assemblies referenced by it.
476  *
477  * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
478  * it. 
479  */
480 MonoAssembly *
481 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
482 {
483         MonoImage *image;
484         MonoAssembly *ass;
485         MonoImageOpenStatus def_status;
486         gchar *fname;
487         
488         g_return_val_if_fail (filename != NULL, NULL);
489
490         if (!status)
491                 status = &def_status;
492         *status = MONO_IMAGE_OK;
493
494         if (strncmp (filename, "file://", 7) == 0) {
495                 GError *error = NULL;
496                 gchar *uri = (gchar *) filename;
497                 gchar *tmpuri;
498
499                 /*
500                  * MS allows file://c:/... and fails on file://localhost/c:/... 
501                  * They also throw an IndexOutOfRangeException if "file://"
502                  */
503                 if (uri [7] != '/')
504                         uri = g_strdup_printf ("file:///%s", uri + 7);
505         
506                 tmpuri = uri;
507                 uri = mono_escape_uri_string (tmpuri);
508                 fname = g_filename_from_uri (uri, NULL, &error);
509                 g_free (uri);
510
511                 if (tmpuri != filename)
512                         g_free (tmpuri);
513
514                 if (error != NULL) {
515                         g_warning ("%s\n", error->message);
516                         g_error_free (error);
517                         fname = g_strdup (filename);
518                 }
519         } else {
520                 fname = g_strdup (filename);
521         }
522
523         /* g_print ("file loading %s\n", fname); */
524         image = do_mono_assembly_open (fname, status);
525
526         if (!image){
527                 *status = MONO_IMAGE_ERROR_ERRNO;
528                 g_free (fname);
529                 return NULL;
530         }
531
532         ass = mono_assembly_load_from (image, fname, status);
533
534         g_free (fname);
535
536         return ass;
537 }
538
539 MonoAssembly *
540 mono_assembly_load_from (MonoImage *image, const char*fname, 
541                          MonoImageOpenStatus *status)
542 {
543         MonoAssembly *ass, *ass2;
544         char *base_dir;
545         GList *loading;
546
547 #if defined (PLATFORM_WIN32)
548         {
549                 gchar *tmp_fn;
550                 int i;
551
552                 tmp_fn = g_strdup (fname);
553                 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
554                         if (tmp_fn [i] == '/')
555                                 tmp_fn [i] = '\\';
556                 }
557
558                 base_dir = absolute_dir (tmp_fn);
559                 g_free (tmp_fn);
560         }
561 #else
562         base_dir = absolute_dir (fname);
563 #endif
564
565         /*
566          * To avoid deadlocks and scalability problems, we load assemblies outside
567          * the assembly lock. This means that multiple threads might try to load
568          * the same assembly at the same time. The first one to load it completely
569          * "wins", the other threads free their copy and use the one loaded by
570          * the winning thread.
571          */
572
573         /*
574          * Create assembly struct, and enter it into the assembly cache
575          */
576         ass = g_new0 (MonoAssembly, 1);
577         ass->basedir = base_dir;
578         ass->image = image;
579
580         mono_assembly_fill_assembly_name (image, &ass->aname);
581
582         /* 
583          * Atomically search the loaded list and add ourselves to it if necessary.
584          */
585         EnterCriticalSection (&assemblies_mutex);
586         if (ass->aname.name)
587                 /* avoid loading the same assembly twice for now... */
588                 if ((ass2 = search_loaded (&ass->aname))) {
589                         g_free (ass);
590                         g_free (base_dir);
591                         mono_image_close (image);
592                         *status = MONO_IMAGE_OK;
593                         LeaveCriticalSection (&assemblies_mutex);
594                         return ass2;
595                 }
596         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
597         loading = g_list_prepend (loading, ass);
598         g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
599         LeaveCriticalSection (&assemblies_mutex);
600
601         image->assembly = ass;
602
603         /*
604          * We load referenced assemblies outside the lock to prevent deadlocks
605          * with regards to preload hooks.
606          */
607         mono_assembly_load_references (image, status);
608
609         EnterCriticalSection (&assemblies_mutex);
610
611         loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
612         loading = g_list_remove (loading, ass);
613         if (loading == NULL)
614                 /* Prevent memory leaks */
615                 g_hash_table_remove (assemblies_loading, GetCurrentThread ());
616         else
617                 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
618         if (*status != MONO_IMAGE_OK) {
619                 LeaveCriticalSection (&assemblies_mutex);
620                 mono_assembly_close (ass);
621                 return NULL;
622         }
623
624         if (ass->aname.name) {
625                 ass2 = search_loaded (&ass->aname);
626                 if (ass2) {
627                         /* Somebody else has loaded the assembly before us */
628                         LeaveCriticalSection (&assemblies_mutex);
629                         mono_assembly_close (ass);
630                         return ass2;
631                 }
632         }
633
634         loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
635         LeaveCriticalSection (&assemblies_mutex);
636
637         mono_assembly_invoke_load_hook (ass);
638
639         return ass;
640 }
641
642 MonoAssembly*
643 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
644 {
645         MonoAssembly *result;
646         char *fullpath, *filename;
647
648         result = invoke_assembly_preload_hook (aname, assemblies_path);
649         if (result)
650                 return result;
651
652         /* g_print ("loading %s\n", aname->name); */
653         /* special case corlib */
654         if ((strcmp (aname->name, "mscorlib") == 0) || (strcmp (aname->name, "corlib") == 0)) {
655                 if (corlib) {
656                         /* g_print ("corlib already loaded\n"); */
657                         return corlib;
658                 }
659                 /* g_print ("corlib load\n"); */
660                 if (assemblies_path) {
661                         corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status);
662                         if (corlib)
663                                 return corlib;
664                         corlib = load_in_path ("corlib.dll", (const char**)assemblies_path, status);
665                         if (corlib)
666                                 return corlib;
667                 }
668                 corlib = load_in_path ("mscorlib.dll", default_path, status);
669                 if (!corlib)
670                         corlib = load_in_path ("corlib.dll", default_path, status);
671                 return corlib;
672         }
673         result = search_loaded (aname);
674         if (result)
675                 return result;
676         /* g_print ("%s not found in cache\n", aname->name); */
677         if (strstr (aname->name, ".dll"))
678                 filename = g_strdup (aname->name);
679         else
680                 filename = g_strconcat (aname->name, ".dll", NULL);
681         if (basedir) {
682                 fullpath = g_build_filename (basedir, filename, NULL);
683                 result = mono_assembly_open (fullpath, status);
684                 g_free (fullpath);
685                 if (result) {
686                         g_free (filename);
687                         return result;
688                 }
689         }
690         if (assemblies_path) {
691                 result = load_in_path (filename, (const char**)assemblies_path, status);
692                 if (result) {
693                         g_free (filename);
694                         return result;
695                 }
696         }
697         result = load_in_path (filename, default_path, status);
698         g_free (filename);
699         return result;
700 }
701
702 MonoAssembly*
703 mono_assembly_loaded (MonoAssemblyName *aname)
704 {
705         MonoAssembly *res;
706
707         EnterCriticalSection (&assemblies_mutex);
708         res = search_loaded (aname);
709         LeaveCriticalSection (&assemblies_mutex);
710
711         return res;
712 }
713
714 void
715 mono_assembly_close (MonoAssembly *assembly)
716 {
717         MonoImage *image;
718         int i;
719         
720         g_return_if_fail (assembly != NULL);
721
722         EnterCriticalSection (&assemblies_mutex);
723         if (--assembly->ref_count != 0) {
724                 LeaveCriticalSection (&assemblies_mutex);
725                 return;
726         }
727         loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
728         LeaveCriticalSection (&assemblies_mutex);
729         image = assembly->image;
730         if (image->references) {
731                 for (i = 0; image->references [i] != NULL; i++)
732                         mono_image_close (image->references [i]->image);
733                 g_free (image->references);
734         }
735
736         mono_image_close (assembly->image);
737
738         g_free (assembly->basedir);
739         g_free (assembly);
740 }
741
742 MonoImage*
743 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
744 {
745         MonoImageOpenStatus status;
746         MonoImage *module;
747
748         module = mono_image_load_file_for_image (assembly->image, idx);
749         if (module)
750                 mono_assembly_load_references (module, &status);
751
752         return module;
753 }
754
755 void
756 mono_assembly_foreach (GFunc func, gpointer user_data)
757 {
758         GList *copy;
759
760         /*
761      * We make a copy of the list to avoid calling the callback inside the 
762          * lock, which could lead to deadlocks.
763          */
764         EnterCriticalSection (&assemblies_mutex);
765         copy = g_list_copy (loaded_assemblies);
766         LeaveCriticalSection (&assemblies_mutex);
767
768         g_list_foreach (loaded_assemblies, func, user_data);
769
770         g_list_free (copy);
771 }
772
773 /* Holds the assembly of the application, for
774  * System.Diagnostics.Process::MainModule
775  */
776 static MonoAssembly *main_assembly=NULL;
777
778 void
779 mono_assembly_set_main (MonoAssembly *assembly)
780 {
781         main_assembly=assembly;
782 }
783
784 MonoAssembly *
785 mono_assembly_get_main (void)
786 {
787         return(main_assembly);
788 }