2 * assembly.c: Routines for loading assemblies.
5 * Miguel de Icaza (miguel@ximian.com)
7 * (C) 2001 Ximian, Inc. http://www.ximian.com
10 * Implement big-endian versions of the reading routines.
21 #include "rawbuffer.h"
23 #include "mono-bundle.h"
25 #include <mono/metadata/loader.h>
26 #include <mono/metadata/tabledefs.h>
27 #include <mono/io-layer/io-layer.h>
28 #include <mono/utils/mono-uri.h>
29 #include <mono/metadata/mono-config.h>
30 #include <mono/utils/mono-digest.h>
32 #include <mono/os/util.h>
35 /* the default search path is just MONO_ASSEMBLIES */
42 static char **assemblies_path = NULL;
45 * keeps track of loaded assemblies
47 static GList *loaded_assemblies = NULL;
48 static MonoAssembly *corlib;
50 /* This protects loaded_assemblies and image->references */
51 static CRITICAL_SECTION assemblies_mutex;
53 /* A hastable of thread->assembly list mappings */
54 static GHashTable *assemblies_loading;
56 static char **extra_gac_paths = NULL;
59 encode_public_tok (const guchar *token, gint32 len)
61 static gchar allowed [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
65 res = g_malloc (len * 2 + 1);
66 for (i = 0; i < len; i++) {
67 res [i * 2] = allowed [token [i] >> 4];
68 res [i * 2 + 1] = allowed [token [i] & 0xF];
75 check_path_env (void) {
79 path = g_getenv ("MONO_PATH");
83 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
85 g_strfreev (assemblies_path);
86 assemblies_path = splitted;
87 if (g_getenv ("MONO_DEBUG") == NULL)
91 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
92 g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
99 check_extra_gac_path_env (void) {
103 path = g_getenv ("MONO_GAC_PATH");
107 splitted = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 1000);
109 g_strfreev (extra_gac_paths);
110 extra_gac_paths = splitted;
111 if (g_getenv ("MONO_DEBUG") == NULL)
115 if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
116 g_warning ("'%s' in MONO_GAC_PATH doesn't exist or has wrong permissions.", *splitted);
123 mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
125 if (!l->name || !r->name)
128 if (strcmp (l->name, r->name))
132 * simply compare names until some other issues are resolved
133 * (version info not getting set correctly for custom
137 if (l->major != r->major || l->minor != r->minor ||
138 l->build != r->build || l->revision != r->revision)
141 if (!l->public_tok_value && !r->public_tok_value)
144 if ((l->public_tok_value && !r->public_tok_value) || (!l->public_tok_value && r->public_tok_value))
147 if (strcmp (l->public_tok_value, r->public_tok_value))
153 /* assemblies_mutex must be held by the caller */
155 search_loaded (MonoAssemblyName* aname)
161 for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
163 /* g_print ("compare %s %s\n", aname->name, ass->aname.name); */
164 if (!mono_assembly_names_equal (aname, &ass->aname))
166 /* g_print ("success\n"); */
172 * The assembly might be under load by this thread. In this case, it is
173 * safe to return an incomplete instance to prevent loops.
175 loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
176 for (tmp = loading; tmp; tmp = tmp->next) {
178 if (!mono_assembly_names_equal (aname, &ass->aname))
187 static MonoAssembly *
188 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status)
192 MonoAssembly *result;
194 for (i = 0; search_path [i]; ++i) {
195 fullpath = g_build_filename (search_path [i], basename, NULL);
196 result = mono_assembly_open (fullpath, status);
205 * mono_assembly_setrootdir:
206 * @root_dir: The pathname of the root directory where we will locate assemblies
208 * This routine sets the internal default root directory for looking up
209 * assemblies. This is used by Windows installations to compute dynamically
210 * the place where the Mono assemblies are located.
214 mono_assembly_setrootdir (const char *root_dir)
217 * Override the MONO_ASSEMBLIES directory configured at compile time.
219 /* Leak if called more than once */
220 default_path [0] = g_strdup (root_dir);
223 G_CONST_RETURN gchar *
224 mono_assembly_getrootdir (void)
226 return default_path [0];
230 * mono_assemblies_init:
232 * Initialize global variables used by this module.
235 mono_assemblies_init (void)
237 #ifdef PLATFORM_WIN32
242 check_extra_gac_path_env ();
244 InitializeCriticalSection (&assemblies_mutex);
246 assemblies_loading = g_hash_table_new (NULL, NULL);
250 mono_assembly_fill_assembly_name (MonoImage *image, MonoAssemblyName *aname)
252 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
253 guint32 cols [MONO_ASSEMBLY_SIZE];
258 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
261 aname->hash_value = NULL;
262 aname->name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_NAME]);
263 aname->culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
264 aname->flags = cols [MONO_ASSEMBLY_FLAGS];
265 aname->major = cols [MONO_ASSEMBLY_MAJOR_VERSION];
266 aname->minor = cols [MONO_ASSEMBLY_MINOR_VERSION];
267 aname->build = cols [MONO_ASSEMBLY_BUILD_NUMBER];
268 aname->revision = cols [MONO_ASSEMBLY_REV_NUMBER];
269 aname->hash_alg = cols [MONO_ASSEMBLY_HASH_ALG];
270 if (cols [MONO_ASSEMBLY_PUBLIC_KEY])
271 aname->public_key = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLY_PUBLIC_KEY]);
273 aname->public_key = 0;
279 assemblyref_public_tok (MonoImage *image, guint32 key_index, guint32 flags)
281 const gchar *public_tok;
284 public_tok = mono_metadata_blob_heap (image, key_index);
285 len = mono_metadata_decode_blob_size (public_tok, &public_tok);
287 if (flags & ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG) {
289 mono_digest_get_public_token (token, public_tok, len);
290 return encode_public_tok (token, 8);
293 return encode_public_tok (public_tok, len);
297 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
300 guint32 cols [MONO_ASSEMBLYREF_SIZE];
303 MonoAssembly **references = NULL;
305 *status = MONO_IMAGE_OK;
308 * image->references is shared between threads, so we need to access
309 * it inside a critical section.
311 EnterCriticalSection (&assemblies_mutex);
312 references = image->references;
313 LeaveCriticalSection (&assemblies_mutex);
317 t = &image->tables [MONO_TABLE_ASSEMBLYREF];
319 references = g_new0 (MonoAssembly *, t->rows + 1);
322 * Load any assemblies this image references
324 for (i = 0; i < t->rows; i++) {
325 MonoAssemblyName aname;
327 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
329 hash = mono_metadata_blob_heap (image, cols [MONO_ASSEMBLYREF_HASH_VALUE]);
330 aname.hash_len = mono_metadata_decode_blob_size (hash, &hash);
331 aname.hash_value = hash;
332 aname.name = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_NAME]);
333 aname.culture = mono_metadata_string_heap (image, cols [MONO_ASSEMBLYREF_CULTURE]);
334 aname.flags = cols [MONO_ASSEMBLYREF_FLAGS];
335 aname.major = cols [MONO_ASSEMBLYREF_MAJOR_VERSION];
336 aname.minor = cols [MONO_ASSEMBLYREF_MINOR_VERSION];
337 aname.build = cols [MONO_ASSEMBLYREF_BUILD_NUMBER];
338 aname.revision = cols [MONO_ASSEMBLYREF_REV_NUMBER];
340 if (cols [MONO_ASSEMBLYREF_PUBLIC_KEY]) {
341 aname.public_tok_value = assemblyref_public_tok (image,
342 cols [MONO_ASSEMBLYREF_PUBLIC_KEY], aname.flags);
344 aname.public_tok_value = NULL;
347 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
349 if (references [i] == NULL){
353 ** Temporary work around: any System.* which are 3300 build, will get
354 ** remapped, this is to keep old applications running that might have
355 ** been linked against our 5000 API, before we were strongnamed, and
356 ** hence were labeled as 3300 builds by reflection.c
358 if (aname.build == 3300 && strncmp (aname.name, "System", 6) == 0){
361 references [i] = mono_assembly_load (&aname, image->assembly->basedir, status);
363 if (references [i] != NULL){
364 if (g_getenv ("MONO_SILENT_WARNING") == NULL)
365 g_printerr ("Compat mode: the request from %s to load %s was remapped (http://www.go-mono.com/remap.html)\n",
366 image->name, aname.name);
370 for (j = 0; j < i; j++)
371 mono_assembly_close (references [j]);
374 g_warning ("Could not find assembly %s, references from %s (assemblyref_index=%d)\n"
375 " Major/Minor: %d,%d\n"
378 aname.name, image->name, i,
379 aname.major, aname.minor, aname.build, aname.revision,
380 aname.public_tok_value);
381 *status = MONO_IMAGE_MISSING_ASSEMBLYREF;
387 * This check is disabled since lots of people seem to have an older
388 * corlib which triggers this.
391 if (image->references [i]->image == image)
392 g_error ("Error: Assembly %s references itself", image->name);
395 references [i] = NULL;
397 /* resolve assembly references for modules */
398 t = &image->tables [MONO_TABLE_MODULEREF];
399 for (i = 0; i < t->rows; i++){
400 if (image->modules [i]) {
401 image->modules [i]->assembly = image->assembly;
402 mono_assembly_load_references (image->modules [i], status);
406 EnterCriticalSection (&assemblies_mutex);
407 if (!image->references)
408 image->references = references;
409 LeaveCriticalSection (&assemblies_mutex);
411 if (image->references != references) {
412 /* Somebody loaded it before us */
413 for (i = 0; i < t->rows; i++)
414 mono_assembly_close (references [i]);
419 typedef struct AssemblyLoadHook AssemblyLoadHook;
420 struct AssemblyLoadHook {
421 AssemblyLoadHook *next;
422 MonoAssemblyLoadFunc func;
426 AssemblyLoadHook *assembly_load_hook = NULL;
429 mono_assembly_invoke_load_hook (MonoAssembly *ass)
431 AssemblyLoadHook *hook;
433 for (hook = assembly_load_hook; hook; hook = hook->next) {
434 hook->func (ass, hook->user_data);
439 mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
441 AssemblyLoadHook *hook;
443 g_return_if_fail (func != NULL);
445 hook = g_new0 (AssemblyLoadHook, 1);
447 hook->user_data = user_data;
448 hook->next = assembly_load_hook;
449 assembly_load_hook = hook;
452 typedef struct AssemblyPreLoadHook AssemblyPreLoadHook;
453 struct AssemblyPreLoadHook {
454 AssemblyPreLoadHook *next;
455 MonoAssemblyPreLoadFunc func;
459 AssemblyPreLoadHook *assembly_preload_hook = NULL;
461 static MonoAssembly *
462 invoke_assembly_preload_hook (MonoAssemblyName *aname, gchar **assemblies_path)
464 AssemblyPreLoadHook *hook;
465 MonoAssembly *assembly;
467 for (hook = assembly_preload_hook; hook; hook = hook->next) {
468 assembly = hook->func (aname, assemblies_path, hook->user_data);
469 if (assembly != NULL)
477 mono_install_assembly_preload_hook (MonoAssemblyPreLoadFunc func, gpointer user_data)
479 AssemblyPreLoadHook *hook;
481 g_return_if_fail (func != NULL);
483 hook = g_new0 (AssemblyPreLoadHook, 1);
485 hook->user_data = user_data;
486 hook->next = assembly_preload_hook;
487 assembly_preload_hook = hook;
491 absolute_dir (const gchar *filename)
502 if (g_path_is_absolute (filename))
503 return g_path_get_dirname (filename);
505 cwd = g_get_current_dir ();
506 mixed = g_build_filename (cwd, filename, NULL);
507 parts = g_strsplit (mixed, G_DIR_SEPARATOR_S, 0);
512 for (i = 0; (part = parts [i]) != NULL; i++) {
513 if (!strcmp (part, "."))
516 if (!strcmp (part, "..")) {
517 if (list && list->next) /* Don't remove root */
518 list = g_list_delete_link (list, list);
520 list = g_list_prepend (list, part);
524 result = g_string_new ("");
525 list = g_list_reverse (list);
527 /* Ignores last data pointer, which should be the filename */
528 for (tmp = list; tmp && tmp->next != NULL; tmp = tmp->next)
530 g_string_append_printf (result, "%s%c", (char *) tmp->data,
534 g_string_free (result, FALSE);
539 return g_strdup (".");
546 do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
548 MonoImage *image = NULL;
551 char *name = g_path_get_basename (filename);
552 char *dot = strrchr (name, '.');
558 /* we do a very simple search for bundled assemblies: it's not a general
559 * purpose assembly loading mechanism.
561 EnterCriticalSection (&assemblies_mutex);
562 for (tmp = loaded_assemblies; tmp; tmp = tmp->next) {
564 if (!ass->aname.name)
566 if (strcmp (name, ass->aname.name))
572 for (i = 0; !image && bundled_assemblies [i]; ++i) {
573 if (strcmp (bundled_assemblies [i]->name, name) == 0) {
574 image = mono_image_open_from_data ((char*)bundled_assemblies [i]->data, bundled_assemblies [i]->size, FALSE, status);
578 LeaveCriticalSection (&assemblies_mutex);
581 mono_image_addref (image);
585 EnterCriticalSection (&assemblies_mutex);
586 image = mono_image_open (filename, status);
587 LeaveCriticalSection (&assemblies_mutex);
593 * mono_assembly_open:
594 * @filename: Opens the assembly pointed out by this name
595 * @status: where a status code can be returned
597 * mono_assembly_open opens the PE-image pointed by @filename, and
598 * loads any external assemblies referenced by it.
600 * NOTE: we could do lazy loading of the assemblies. Or maybe not worth
604 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
608 MonoImageOpenStatus def_status;
611 g_return_val_if_fail (filename != NULL, NULL);
614 status = &def_status;
615 *status = MONO_IMAGE_OK;
617 if (strncmp (filename, "file://", 7) == 0) {
618 GError *error = NULL;
619 gchar *uri = (gchar *) filename;
623 * MS allows file://c:/... and fails on file://localhost/c:/...
624 * They also throw an IndexOutOfRangeException if "file://"
627 uri = g_strdup_printf ("file:///%s", uri + 7);
630 uri = mono_escape_uri_string (tmpuri);
631 fname = g_filename_from_uri (uri, NULL, &error);
634 if (tmpuri != filename)
638 g_warning ("%s\n", error->message);
639 g_error_free (error);
640 fname = g_strdup (filename);
643 fname = g_strdup (filename);
646 /* g_print ("file loading %s\n", fname); */
647 image = do_mono_assembly_open (fname, status);
650 *status = MONO_IMAGE_ERROR_ERRNO;
655 ass = mono_assembly_load_from (image, fname, status);
657 mono_config_for_assembly (ass->image);
665 mono_assembly_load_from (MonoImage *image, const char*fname,
666 MonoImageOpenStatus *status)
668 MonoAssembly *ass, *ass2;
672 #if defined (PLATFORM_WIN32)
677 tmp_fn = g_strdup (fname);
678 for (i = strlen (tmp_fn) - 1; i >= 0; i--) {
679 if (tmp_fn [i] == '/')
683 base_dir = absolute_dir (tmp_fn);
687 base_dir = absolute_dir (fname);
691 * To avoid deadlocks and scalability problems, we load assemblies outside
692 * the assembly lock. This means that multiple threads might try to load
693 * the same assembly at the same time. The first one to load it completely
694 * "wins", the other threads free their copy and use the one loaded by
695 * the winning thread.
699 * Create assembly struct, and enter it into the assembly cache
701 ass = g_new0 (MonoAssembly, 1);
702 ass->basedir = base_dir;
705 mono_assembly_fill_assembly_name (image, &ass->aname);
708 * Atomically search the loaded list and add ourselves to it if necessary.
710 EnterCriticalSection (&assemblies_mutex);
712 /* avoid loading the same assembly twice for now... */
713 if ((ass2 = search_loaded (&ass->aname))) {
716 mono_image_close (image);
717 *status = MONO_IMAGE_OK;
718 LeaveCriticalSection (&assemblies_mutex);
721 loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
722 loading = g_list_prepend (loading, ass);
723 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
724 LeaveCriticalSection (&assemblies_mutex);
726 image->assembly = ass;
729 * We load referenced assemblies outside the lock to prevent deadlocks
730 * with regards to preload hooks.
732 mono_assembly_load_references (image, status);
734 EnterCriticalSection (&assemblies_mutex);
736 loading = g_hash_table_lookup (assemblies_loading, GetCurrentThread ());
737 loading = g_list_remove (loading, ass);
739 /* Prevent memory leaks */
740 g_hash_table_remove (assemblies_loading, GetCurrentThread ());
742 g_hash_table_insert (assemblies_loading, GetCurrentThread (), loading);
743 if (*status != MONO_IMAGE_OK) {
744 LeaveCriticalSection (&assemblies_mutex);
745 mono_assembly_close (ass);
749 if (ass->aname.name) {
750 ass2 = search_loaded (&ass->aname);
752 /* Somebody else has loaded the assembly before us */
753 LeaveCriticalSection (&assemblies_mutex);
754 mono_assembly_close (ass);
759 loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
760 LeaveCriticalSection (&assemblies_mutex);
762 mono_assembly_invoke_load_hook (ass);
768 probe_for_partial_name (const char *basepath, const char *fullname, MonoImageOpenStatus *status)
770 MonoAssembly *res = NULL;
773 const char* direntry;
775 dirhandle = g_dir_open (basepath, 0, NULL);
779 while ((direntry = g_dir_read_name (dirhandle))) {
780 fullpath = g_build_path (G_DIR_SEPARATOR_S, basepath, direntry, fullname, NULL);
781 res = mono_assembly_open (fullpath, status);
786 g_dir_close (dirhandle);
792 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
795 MonoAssemblyName aname;
796 gchar *fullname, *gacpath;
799 memset (&aname, 0, sizeof (MonoAssemblyName));
802 res = invoke_assembly_preload_hook (&aname, assemblies_path);
808 res = mono_assembly_loaded (&aname);
812 fullname = g_strdup_printf ("%s.dll", name);
814 if (extra_gac_paths) {
815 paths = extra_gac_paths;
816 while (!res && *paths) {
817 gacpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "lib", "mono", "gac", name, NULL);
818 res = probe_for_partial_name (gacpath, fullname, status);
831 gacpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (), "mono", "gac", name, NULL);
832 res = probe_for_partial_name (gacpath, fullname, status);
844 * mono_assembly_load_from_gac
846 * @aname: The assembly name object
849 mono_assembly_load_from_gac (MonoAssemblyName *aname, gchar *filename, MonoImageOpenStatus *status)
851 MonoAssembly *result = NULL;
852 gchar *name, *version, *fullpath, *subpath;
856 if (!aname->public_tok_value) {
860 if (strstr (aname->name, ".dll")) {
861 len = strlen (filename) - 4;
862 name = g_malloc (len);
863 strncpy (name, aname->name, len);
865 name = g_strdup (aname->name);
868 version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
869 aname->minor, aname->build, aname->revision,
870 aname->culture == NULL ? "" : aname->culture,
871 aname->public_tok_value);
873 subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
877 if (extra_gac_paths) {
878 paths = extra_gac_paths;
879 while (!result && *paths) {
880 fullpath = g_build_path (G_DIR_SEPARATOR_S, *paths, "gac", subpath, NULL);
881 result = mono_assembly_open (fullpath, status);
888 result->in_gac = TRUE;
893 fullpath = g_build_path (G_DIR_SEPARATOR_S, mono_assembly_getrootdir (),
894 "mono", "gac", subpath, NULL);
895 result = mono_assembly_open (fullpath, status);
899 result->in_gac = TRUE;
908 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
910 MonoAssembly *result;
911 char *fullpath, *filename;
913 result = invoke_assembly_preload_hook (aname, assemblies_path);
915 result->in_gac = FALSE;
919 result = mono_assembly_loaded (aname);
923 /* g_print ("loading %s\n", aname->name); */
924 /* special case corlib */
925 if (strcmp (aname->name, "mscorlib") == 0) {
927 /* g_print ("corlib already loaded\n"); */
930 /* g_print ("corlib load\n"); */
931 if (assemblies_path) {
932 corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status);
936 corlib = load_in_path ("mscorlib.dll", default_path, status);
940 if (strstr (aname->name, ".dll"))
941 filename = g_strdup (aname->name);
943 filename = g_strconcat (aname->name, ".dll", NULL);
945 result = mono_assembly_load_from_gac (aname, filename, status);
952 fullpath = g_build_filename (basedir, filename, NULL);
953 result = mono_assembly_open (fullpath, status);
956 result->in_gac = FALSE;
962 result = load_in_path (filename, default_path, status);
964 result->in_gac = FALSE;
970 mono_assembly_loaded (MonoAssemblyName *aname)
974 EnterCriticalSection (&assemblies_mutex);
975 res = search_loaded (aname);
976 LeaveCriticalSection (&assemblies_mutex);
982 mono_assembly_close (MonoAssembly *assembly)
987 g_return_if_fail (assembly != NULL);
989 EnterCriticalSection (&assemblies_mutex);
990 if (--assembly->ref_count != 0) {
991 LeaveCriticalSection (&assemblies_mutex);
994 loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
995 LeaveCriticalSection (&assemblies_mutex);
996 image = assembly->image;
997 if (image->references) {
998 for (i = 0; image->references [i] != NULL; i++)
999 mono_image_close (image->references [i]->image);
1000 g_free (image->references);
1003 mono_image_close (assembly->image);
1005 g_free (assembly->basedir);
1010 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
1012 MonoImageOpenStatus status;
1015 module = mono_image_load_file_for_image (assembly->image, idx);
1017 mono_assembly_load_references (module, &status);
1023 mono_assembly_foreach (GFunc func, gpointer user_data)
1028 * We make a copy of the list to avoid calling the callback inside the
1029 * lock, which could lead to deadlocks.
1031 EnterCriticalSection (&assemblies_mutex);
1032 copy = g_list_copy (loaded_assemblies);
1033 LeaveCriticalSection (&assemblies_mutex);
1035 g_list_foreach (loaded_assemblies, func, user_data);
1040 /* Holds the assembly of the application, for
1041 * System.Diagnostics.Process::MainModule
1043 static MonoAssembly *main_assembly=NULL;
1046 mono_assembly_set_main (MonoAssembly *assembly)
1048 main_assembly=assembly;
1052 mono_assembly_get_main (void)
1054 return(main_assembly);