2006-06-08 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / assembly.c
index dbae3e620219b4092d34ca2ad784216ab1384a18..f52ffc393c4e5429ab90657aecabd442a6a674a2 100644 (file)
 #include <mono/metadata/mono-config.h>
 #include <mono/utils/mono-digest.h>
 #include <mono/utils/mono-logger.h>
-#include <mono/os/util.h>
+#include <mono/metadata/reflection.h>
+
+#ifndef PLATFORM_WIN32
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
 
 /* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
 typedef struct  {
@@ -350,7 +356,7 @@ mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
        if (!l->public_key_token [0] || !r->public_key_token [0])
                return TRUE;
 
-       if (strcmp (l->public_key_token, r->public_key_token))
+       if (strcmp ((char*)l->public_key_token, (char*)r->public_key_token))
                return FALSE;
 
        return TRUE;
@@ -371,7 +377,7 @@ search_loaded (MonoAssemblyName* aname, gboolean refonly)
         * The assembly might be under load by this thread. In this case, it is
         * safe to return an incomplete instance to prevent loops.
         */
-       loading = g_hash_table_lookup (refonly ? assemblies_refonly_loading : assemblies_loading, GetCurrentThread ());
+       loading = g_hash_table_lookup (refonly ? assemblies_refonly_loading : assemblies_loading, GetCurrentThreadId ());
        for (tmp = loading; tmp; tmp = tmp->next) {
                ass = tmp->data;
                if (!mono_assembly_names_equal (aname, &ass->aname))
@@ -457,14 +463,151 @@ mono_assembly_getrootdir (void)
 void
 mono_set_dirs (const char *assembly_dir, const char *config_dir)
 {
+#if defined (MONO_ASSEMBLIES)
        if (assembly_dir == NULL)
                assembly_dir = MONO_ASSEMBLIES;
+#endif
+#if defined (MONO_CFG_DIR)
        if (config_dir == NULL)
                config_dir = MONO_CFG_DIR;
+#endif
        mono_assembly_setrootdir (assembly_dir);
        mono_set_config_dir (config_dir);
 }
 
+#ifndef PLATFORM_WIN32
+
+static char *
+compute_base (char *path)
+{
+       char *p = rindex (path, '/');
+       if (p == NULL)
+               return NULL;
+
+       /* Not a well known Mono executable, we are embedded, cant guess the base  */
+       if (strcmp (p, "/mono") && strcmp (p, "/monodis") && strcmp (p, "/mint") && strcmp (p, "/monodiet"))
+               return NULL;
+           
+       *p = 0;
+       p = rindex (path, '/');
+       if (p == NULL)
+               return NULL;
+       
+       if (strcmp (p, "/bin") != 0)
+               return NULL;
+       *p = 0;
+       return path;
+}
+
+static void
+fallback (void)
+{
+       mono_set_dirs (MONO_ASSEMBLIES, MONO_CFG_DIR);
+}
+
+static void
+set_dirs (char *exe)
+{
+       char *base;
+       char *config, *lib, *mono;
+       struct stat buf;
+       
+       /*
+        * Only /usr prefix is treated specially
+        */
+       if (strncmp (exe, MONO_BINDIR, strlen (MONO_BINDIR)) == 0 || (base = compute_base (exe)) == NULL){
+               fallback ();
+               return;
+       }
+
+       config = g_build_filename (base, "etc", NULL);
+       lib = g_build_filename (base, "lib", NULL);
+       mono = g_build_filename (lib, "mono/1.0", NULL);
+       if (stat (mono, &buf) == -1)
+               fallback ();
+       else {
+               mono_set_dirs (lib, config);
+       }
+       
+       g_free (config);
+       g_free (lib);
+       g_free (mono);
+}
+
+#endif /* PLATFORM_WIN32 */
+
+#ifdef UNDER_CE
+#undef GetModuleFileName
+#define GetModuleFileName ceGetModuleFileNameA
+
+DWORD ceGetModuleFileNameA(HMODULE hModule, char* lpFilename, DWORD nSize)
+{
+       DWORD res = 0;
+       wchar_t* wbuff = (wchar_t*)LocalAlloc(LPTR, nSize*2);
+       res = GetModuleFileNameW(hModule, wbuff, nSize);
+       if (res) {
+               int len = wcslen(wbuff);
+               WideCharToMultiByte(CP_ACP, 0, wbuff, len, lpFilename, len, NULL, NULL);
+       }
+       LocalFree(wbuff);
+       return res;
+}
+#endif
+
+/**
+ * mono_set_rootdir:
+ *
+ * Registers the root directory for the Mono runtime, for Linux and Solaris 10,
+ * this auto-detects the prefix where Mono was installed. 
+ */
+void
+mono_set_rootdir (void)
+{
+#ifdef PLATFORM_WIN32
+       gunichar2 moddir [MAXPATHLEN];
+       gchar *bindir, *installdir, *root, *utf8name, *config;
+
+       GetModuleFileNameW (NULL, moddir, MAXPATHLEN);
+       utf8name = g_utf16_to_utf8 (moddir, -1, NULL, NULL, NULL);
+       bindir = g_path_get_dirname (utf8name);
+       installdir = g_path_get_dirname (bindir);
+       root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
+
+       config = g_build_filename (root, "..", "etc", NULL);
+       mono_set_dirs (root, config);
+
+       g_free (config);
+       g_free (root);
+       g_free (installdir);
+       g_free (bindir);
+       g_free (utf8name);
+#else
+       char buf [4096];
+       int  s;
+       char *str;
+
+       /* Linux style */
+       s = readlink ("/proc/self/exe", buf, sizeof (buf)-1);
+
+       if (s != -1){
+               buf [s] = 0;
+               set_dirs (buf);
+               return;
+       }
+
+       /* Solaris 10 style */
+       str = g_strdup_printf ("/proc/%d/path/a.out", getpid ());
+       s = readlink (str, buf, sizeof (buf)-1);
+       g_free (str);
+       if (s != -1){
+               buf [s] = 0;
+               set_dirs (buf);
+               return;
+       } 
+       fallback ();
+#endif
+}
+
 /**
  * mono_assemblies_init:
  *
@@ -704,7 +847,7 @@ mono_assembly_load_reference (MonoImage *image, int index)
                 * a non loaded reference using the ReflectionOnly api
                */
                if (!reference)
-                       reference = (gpointer)-1;
+                       reference = REFERENCE_MISSING;
        } else
                reference = mono_assembly_load (&aname, image->assembly->basedir, &status);
 
@@ -734,13 +877,21 @@ mono_assembly_load_reference (MonoImage *image, int index)
        mono_assemblies_lock ();
        if (reference == NULL) {
                /* Flag as not found */
-               reference = (gpointer)-1;
-       } else {
-               mono_assembly_addref (reference);
+               reference = REFERENCE_MISSING;
        }       
 
-       if (!image->references [index])
+       if (!image->references [index]) {
+               if (reference != REFERENCE_MISSING){
+                       mono_assembly_addref (reference);
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly Ref addref %s %p -> %s %p: %d\n",
+                                   image->assembly->aname.name, image->assembly, reference->aname.name, reference, reference->ref_count);
+               } else {
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Failed to load assembly %s %p\n",
+                                   image->assembly->aname.name, image->assembly);
+               }
+               
                image->references [index] = reference;
+       }
        mono_assemblies_unlock ();
 
        if (image->references [index] != reference) {
@@ -803,6 +954,17 @@ mono_install_assembly_load_hook (MonoAssemblyLoadFunc func, gpointer user_data)
        assembly_load_hook = hook;
 }
 
+static void
+free_assembly_load_hooks (void)
+{
+       AssemblyLoadHook *hook, *next;
+
+       for (hook = assembly_load_hook; hook; hook = next) {
+               next = hook->next;
+               g_free (hook);
+       }
+}
+
 typedef struct AssemblySearchHook AssemblySearchHook;
 struct AssemblySearchHook {
        AssemblySearchHook *next;
@@ -858,6 +1020,17 @@ mono_install_assembly_search_hook (MonoAssemblySearchFunc func, gpointer user_da
        mono_install_assembly_search_hook_internal (func, user_data, FALSE, FALSE);
 }      
 
+static void
+free_assembly_search_hooks (void)
+{
+       AssemblySearchHook *hook, *next;
+
+       for (hook = assembly_search_hook; hook; hook = next) {
+               next = hook->next;
+               g_free (hook);
+       }
+}
+
 void
 mono_install_assembly_refonly_search_hook (MonoAssemblySearchFunc func, gpointer user_data)
 {
@@ -944,6 +1117,22 @@ mono_install_assembly_refonly_preload_hook (MonoAssemblyPreLoadFunc func, gpoint
        assembly_refonly_preload_hook = hook;
 }
 
+static void
+free_assembly_preload_hooks (void)
+{
+       AssemblyPreLoadHook *hook, *next;
+
+       for (hook = assembly_preload_hook; hook; hook = next) {
+               next = hook->next;
+               g_free (hook);
+       }
+
+       for (hook = assembly_refonly_preload_hook; hook; hook = next) {
+               next = hook->next;
+               g_free (hook);
+       }
+}
+
 static gchar *
 absolute_dir (const gchar *filename)
 {
@@ -1035,24 +1224,6 @@ mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *statu
        return NULL;
 }
 
-static MonoImage*
-do_mono_assembly_open (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
-{
-       MonoImage *image = NULL;
-
-       if (bundles != NULL){
-               image = mono_assembly_open_from_bundle (filename, status, refonly);
-
-               if (image != NULL)
-                       return image;
-       }
-       mono_assemblies_lock ();
-       image = mono_image_open_full (filename, status, refonly);
-       mono_assemblies_unlock ();
-
-       return image;
-}
-
 MonoAssembly *
 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
 {
@@ -1098,14 +1269,32 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
 
        mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
                        "Assembly Loader probing location: '%s'.", filename);
-       image = do_mono_assembly_open (fname, status, refonly);
+       image = NULL;
+
+       if (bundles != NULL)
+               image = mono_assembly_open_from_bundle (fname, status, refonly);
+
+       if (!image) {
+               mono_assemblies_lock ();
+               image = mono_image_open_full (fname, status, refonly);
+               mono_assemblies_unlock ();
+       }
 
        if (!image){
-               *status = MONO_IMAGE_ERROR_ERRNO;
+               if (*status == MONO_IMAGE_OK)
+                       *status = MONO_IMAGE_ERROR_ERRNO;
                g_free (fname);
                return NULL;
        }
 
+       if (image->assembly) {
+               /* Already loaded by another appdomain */
+               mono_assembly_invoke_load_hook (image->assembly);
+               mono_image_close (image);
+               g_free (fname);
+               return image->assembly;
+       }
+
        ass = mono_assembly_load_from_full (image, fname, status, refonly);
 
        if (ass) {
@@ -1115,11 +1304,55 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
                        mono_config_for_assembly (ass->image);
        }
 
+       /* Clear the reference added by mono_image_open */
+       mono_image_close (image);
+       
        g_free (fname);
 
        return ass;
 }
 
+/*
+ * load_friend_assemblies:
+ * @ass: an assembly
+ *
+ * Load the list of friend assemblies that are allowed to access
+ * the assembly's internal types and members. They are stored as assembly
+ * names in custom attributes.
+ */
+static void
+load_friend_assemblies (MonoAssembly* ass)
+{
+       int i;
+       MonoCustomAttrInfo* attrs = mono_custom_attrs_from_assembly (ass);
+       if (!attrs)
+               return;
+       for (i = 0; i < attrs->num_attrs; ++i) {
+               MonoCustomAttrEntry *attr = &attrs->attrs [i];
+               MonoAssemblyName *aname;
+               const guchar *data;
+               guint slen;
+               /* Do some sanity checking */
+               if (!attr->ctor || attr->ctor->klass != mono_defaults.internals_visible_class)
+                       continue;
+               if (attr->data_size < 4)
+                       continue;
+               data = attr->data;
+               /* 0xFF means null string, see custom attr format */
+               if (data [0] != 1 || data [1] != 0 || data [2] == 0xFF)
+                       continue;
+               slen = mono_metadata_decode_value (data + 2, &data);
+               aname = g_new0 (MonoAssemblyName, 1);
+               /*g_print ("friend ass: %s\n", data);*/
+               if (mono_assembly_name_parse_full (data, aname, TRUE, NULL)) {
+                       ass->friend_assembly_names = g_slist_prepend (ass->friend_assembly_names, aname);
+               } else {
+                       g_free (aname);
+               }
+       }
+       mono_custom_attrs_free (attrs);
+}
+
 /**
  * mono_assembly_open:
  * @filename: Opens the assembly pointed out by this name
@@ -1128,8 +1361,9 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
  * mono_assembly_open opens the PE-image pointed by @filename, and
  * loads any external assemblies referenced by it.
  *
- * NOTE: we could do lazy loading of the assemblies.  Or maybe not worth
- * it. 
+ * Return: a pointer to the MonoAssembly if @filename contains a valid
+ * assembly or NULL on error.  Details about the error are stored in the
+ * @status variable.
  */
 MonoAssembly *
 mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
@@ -1139,7 +1373,7 @@ mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
 
 MonoAssembly *
 mono_assembly_load_from_full (MonoImage *image, const char*fname, 
-                        MonoImageOpenStatus *status, gboolean refonly)
+                             MonoImageOpenStatus *status, gboolean refonly)
 {
        MonoAssembly *ass, *ass2;
        char *base_dir;
@@ -1179,10 +1413,14 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        ass->basedir = base_dir;
        ass->ref_only = refonly;
        ass->image = image;
-       ass->ref_count = 1;
+
+       /* Add a non-temporary reference because of ass->image */
+       mono_image_addref (image);
 
        mono_assembly_fill_assembly_name (image, &ass->aname);
 
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image addref %s %p -> %s %p: %d\n", ass->aname.name, ass, image->name, image, image->ref_count);
+
        /* 
         * Atomically search the loaded list and add ourselves to it if necessary.
         */
@@ -1200,24 +1438,25 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
                }
        }
        ass_loading = refonly ? assemblies_refonly_loading : assemblies_loading;
-       loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
+       loading = g_hash_table_lookup (ass_loading, (gpointer)GetCurrentThreadId ());
        loading = g_list_prepend (loading, ass);
-       g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
+       g_hash_table_insert (ass_loading, (gpointer)GetCurrentThreadId (), loading);
        mono_assemblies_unlock ();
 
+       g_assert (image->assembly == NULL);
        image->assembly = ass;
 
        mono_assembly_load_references (image, status);
 
        mono_assemblies_lock ();
 
-       loading = g_hash_table_lookup (ass_loading, GetCurrentThread ());
+       loading = g_hash_table_lookup (ass_loading, (gpointer)GetCurrentThreadId ());
        loading = g_list_remove (loading, ass);
        if (loading == NULL)
                /* Prevent memory leaks */
-               g_hash_table_remove (ass_loading, GetCurrentThread ());
+               g_hash_table_remove (ass_loading, (gpointer)GetCurrentThreadId ());
        else
-               g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
+               g_hash_table_insert (ass_loading, (gpointer)GetCurrentThreadId (), loading);
        if (*status != MONO_IMAGE_OK) {
                mono_assemblies_unlock ();
                mono_assembly_close (ass);
@@ -1235,6 +1474,8 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        }
 
        loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
+       if (mono_defaults.internals_visible_class)
+               load_friend_assemblies (ass);
        mono_assemblies_unlock ();
 
        mono_assembly_invoke_load_hook (ass);
@@ -1363,14 +1604,17 @@ build_assembly_name (const char *name, const char *version, const char *culture,
        aname->name = g_strdup (name);
        
        if (culture) {
-               if (g_strcasecmp (culture, "neutral") == 0)
+               if (g_ascii_strcasecmp (culture, "neutral") == 0)
                        aname->culture = g_strdup ("");
                else
                        aname->culture = g_strdup (culture);
        }
        
-       if (token && strncmp (token, "null", 4) != 0)
-               g_strlcpy ((char*)aname->public_key_token, token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
+       if (token && strncmp (token, "null", 4) != 0) {
+               char *lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
+               g_strlcpy ((char*)aname->public_key_token, lower, MONO_PUBLIC_KEY_TOKEN_LENGTH);
+               g_free (lower);
+       }
 
        if (key && strncmp (key, "null", 4) != 0) {
                if (!parse_public_key (key, &pkey)) {
@@ -1479,10 +1723,11 @@ mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboole
 * mono_assembly_name_parse:
 * @name: name to parse
 * @aname: the destination assembly name
-* Returns: true if the name could be parsed.
 * 
 * Parses an assembly qualified type name and assigns the name,
 * version, culture and token to the provided assembly name object.
+*
+* Returns: true if the name could be parsed.
 */
 gboolean
 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
@@ -1872,7 +2117,22 @@ mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *
        return corlib;
 }
 
-
+/**
+ * mono_assembly_load_full:
+ * @aname: A MonoAssemblyName with the assembly name to load.
+ * @basedir: A directory to look up the assembly at.
+ * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation
+ * @refonly: Whether this assembly is being opened in "reflection-only" mode.
+ *
+ * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it
+ * attempts to load the assembly from that directory before probing the standard locations.
+ *
+ * If the assembly is being opened in reflection-only mode (@refonly set to TRUE) then no 
+ * assembly binding takes place.
+ *
+ * Returns: the assembly referenced by @aname loaded or NULL on error.   On error the
+ * value pointed by status is updated with an error code.
+ */
 MonoAssembly*
 mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status, gboolean refonly)
 {
@@ -1943,6 +2203,18 @@ mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImage
        return result;
 }
 
+/**
+ * mono_assembly_load:
+ * @aname: A MonoAssemblyName with the assembly name to load.
+ * @basedir: A directory to look up the assembly at.
+ * @status: a pointer to a MonoImageOpenStatus to return the status of the load operation
+ *
+ * Loads the assembly referenced by @aname, if the value of @basedir is not NULL, it
+ * attempts to load the assembly from that directory before probing the standard locations.
+ *
+ * Returns: the assembly referenced by @aname loaded or NULL on error.   On error the
+ * value pointed by status is updated with an error code.
+ */
 MonoAssembly*
 mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status)
 {
@@ -1964,6 +2236,13 @@ mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
        return res;
 }
 
+/**
+ * mono_assembly_loaded:
+ * @aname: an assembly to look for.
+ *
+ * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to
+ * a MonoAssembly that matches the MonoAssemblyName specified.
+ */
 MonoAssembly*
 mono_assembly_loaded (MonoAssemblyName *aname)
 {
@@ -1980,23 +2259,50 @@ mono_assembly_loaded (MonoAssemblyName *aname)
 void
 mono_assembly_close (MonoAssembly *assembly)
 {
+       GSList *tmp;
        g_return_if_fail (assembly != NULL);
 
-       if (InterlockedDecrement (&assembly->ref_count))
+       if (assembly == REFERENCE_MISSING)
                return;
        
+       /* Might be 0 already */
+       if (InterlockedDecrement (&assembly->ref_count) > 0)
+               return;
+
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Unloading assembly %s [%p].", assembly->aname.name, assembly);
+
        mono_assemblies_lock ();
        loaded_assemblies = g_list_remove (loaded_assemblies, assembly);
        mono_assemblies_unlock ();
-       /* assemblies belong to domains, so the domain code takes care of unloading the
-        * referenced assemblies
-        */
+
+       if (assembly->image->references) {
+               int i;
+
+               for (i = 0; assembly->image->references [i]; i++) {
+                       if (assembly->image->references [i])
+                               mono_assembly_close (assembly->image->references [i]);
+               }
+
+               g_free (assembly->image->references);
+               assembly->image->references = NULL;
+       }
+
+       assembly->image->assembly = NULL;
 
        mono_image_close (assembly->image);
 
+       for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
+               MonoAssemblyName *fname = tmp->data;
+               mono_assembly_name_free (fname);
+               g_free (fname);
+       }
+       g_slist_free (assembly->friend_assembly_names);
        g_free (assembly->basedir);
-       if (!assembly->dynamic)
+       if (assembly->dynamic) {
+               g_free ((char*)assembly->aname.culture);
+       } else {
                g_free (assembly);
+       }
 }
 
 MonoImage*
@@ -2030,6 +2336,34 @@ mono_assembly_foreach (GFunc func, gpointer user_data)
        g_list_free (copy);
 }
 
+/**
+ * mono_assemblies_cleanup:
+ *
+ * Free all resources used by this module.
+ */
+void
+mono_assemblies_cleanup (void)
+{
+       GSList *l;
+
+       DeleteCriticalSection (&assemblies_mutex);
+
+       g_hash_table_destroy (assemblies_loading);
+       g_hash_table_destroy (assemblies_refonly_loading);
+
+       for (l = loaded_assembly_bindings; l; l = l->next) {
+               MonoAssemblyBindingInfo *info = l->data;
+
+               mono_assembly_binding_info_free (info);
+               g_free (info);
+       }
+       g_slist_free (loaded_assembly_bindings);
+
+       free_assembly_load_hooks ();
+       free_assembly_search_hooks ();
+       free_assembly_preload_hooks ();
+}
+
 /*
  * Holds the assembly of the application, for
  * System.Diagnostics.Process::MainModule
@@ -2039,16 +2373,25 @@ static MonoAssembly *main_assembly=NULL;
 void
 mono_assembly_set_main (MonoAssembly *assembly)
 {
-       main_assembly=assembly;
+       main_assembly = assembly;
 }
 
+/**
+ * mono_assembly_get_main:
+ *
+ * Returns: the assembly for the application, the first assembly that is loaded by the VM
+ */
 MonoAssembly *
 mono_assembly_get_main (void)
 {
-       return(main_assembly);
+       return (main_assembly);
 }
 
-/*
+/**
+ * mono_assembly_get_image:
+ * @assembly: The assembly to retrieve the image from
+ *
+ * Returns: the MonoImage associated with this assembly.
  */
 MonoImage*
 mono_assembly_get_image (MonoAssembly *assembly)