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