Hide some more methods
[mono.git] / mono / metadata / assembly.c
index cdd680983dd429c79b988cd76978df069b86de4c..b72ce7fb3fc38e3fc62f93269442ca7d5b258595 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  {
@@ -430,6 +436,178 @@ mono_assembly_getrootdir (void)
        return default_path [0];
 }
 
+/**
+ * mono_set_dirs:
+ * @assembly_dir: the base directory for assemblies
+ * @config_dir: the base directory for configuration files
+ *
+ * This routine is used internally and by developers embedding
+ * the runtime into their own applications.  There are a number
+ * of cases to consider: Mono as a system-installed package that
+ * is available on the location preconfigured or Mono in a relocated
+ * location.
+ *
+ * If you are using a system-installed Mono, you can pass NULL
+ * to both parameters.  If you are not, you should compute both
+ * directory values and call this routine.
+ *
+ * The values for a given PREFIX are:
+ *
+ *    assembly_dir: PREFIX/lib
+ *    config_dir:   PREFIX/etc
+ *
+ * Notice that embedders that use Mono in a relocated way must
+ * compute the location at runtime, as they will be in control
+ * of where Mono is installed.
+ */
+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:
  *
@@ -700,12 +878,13 @@ mono_assembly_load_reference (MonoImage *image, int index)
        if (reference == NULL) {
                /* Flag as not found */
                reference = (gpointer)-1;
-       } else {
-               mono_assembly_addref (reference);
        }       
 
-       if (!image->references [index])
+       if (!image->references [index]) {
+               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);
                image->references [index] = reference;
+       }
        mono_assemblies_unlock ();
 
        if (image->references [index] != reference) {
@@ -1071,6 +1250,12 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
                return NULL;
        }
 
+       if (image->assembly) {
+               /* Already loaded by another appdomain */
+               mono_assembly_invoke_load_hook (image->assembly);
+               return image->assembly;
+       }
+
        ass = mono_assembly_load_from_full (image, fname, status, refonly);
 
        if (ass) {
@@ -1085,6 +1270,47 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
        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
@@ -1144,10 +1370,13 @@ 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;
+
+       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.
         */
@@ -1170,6 +1399,7 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        g_hash_table_insert (ass_loading, GetCurrentThread (), loading);
        mono_assemblies_unlock ();
 
+       g_assert (image->assembly == NULL);
        image->assembly = ass;
 
        mono_assembly_load_references (image, status);
@@ -1200,6 +1430,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);
@@ -1328,14 +1560,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)) {
@@ -1945,20 +2180,38 @@ mono_assembly_loaded (MonoAssemblyName *aname)
 void
 mono_assembly_close (MonoAssembly *assembly)
 {
+       GSList *tmp;
        g_return_if_fail (assembly != NULL);
 
-       if (InterlockedDecrement (&assembly->ref_count))
+       /* 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]);
+               }
+       }
+
+       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)
                g_free (assembly);