2010-06-30 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / assembly.c
index b7bd17d114f969be4531a2b87066dc71e6a9397f..6a6959dd5ab2d08d4bfbb58be47f86ea70258c31 100644 (file)
@@ -4,8 +4,8 @@
  * Author:
  *   Miguel de Icaza (miguel@ximian.com)
  *
- * (C) 2001 Ximian, Inc.  http://www.ximian.com
- *
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  */
 #include <config.h>
 #include <stdio.h>
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include "assembly.h"
 #include "image.h"
-#include "rawbuffer.h"
 #include "object-internals.h"
 #include <mono/metadata/loader.h>
 #include <mono/metadata/tabledefs.h>
 #include <mono/utils/mono-uri.h>
 #include <mono/metadata/mono-config.h>
 #include <mono/utils/mono-digest.h>
-#include <mono/utils/mono-logger.h>
+#include <mono/utils/mono-logger-internal.h>
 #include <mono/metadata/reflection.h>
+#include <mono/metadata/coree.h>
 
-#ifndef PLATFORM_WIN32
+#ifndef HOST_WIN32
 #include <sys/types.h>
 #include <unistd.h>
 #include <sys/stat.h>
 #endif
 
+#ifdef PLATFORM_MACOSX
+#include <mach-o/dyld.h>
+#endif
+
 /* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
 typedef struct  {
        const char* assembly_name;
@@ -87,8 +91,11 @@ static const AssemblyVersionMap framework_assemblies [] = {
        {"Npgsql", 0},
        {"PEAPI", 0},
        {"System", 0},
+       {"System.ComponentModel.DataAnnotations", 2},
        {"System.Configuration.Install", 0},
+       {"System.Core", 2},
        {"System.Data", 0},
+       {"System.Data.Linq", 2},
        {"System.Data.OracleClient", 0},
        {"System.Data.SqlXml", 0},
        {"System.Design", 0},
@@ -103,7 +110,10 @@ static const AssemblyVersionMap framework_assemblies [] = {
        {"System.Security", 0},
        {"System.ServiceProcess", 0},
        {"System.Web", 0},
+       {"System.Web.Abstractions", 2},
+       {"System.Web.Extensions", 2},
        {"System.Web.Mobile", 0},
+       {"System.Web.Routing", 2},
        {"System.Web.Services", 0},
        {"System.Windows.Forms", 0},
        {"System.Xml", 0},
@@ -121,12 +131,6 @@ static MonoAssembly *corlib;
 #define mono_assemblies_unlock() LeaveCriticalSection (&assemblies_mutex)
 static CRITICAL_SECTION assemblies_mutex;
 
-/* A hastable of thread->assembly list mappings */
-static GHashTable *assemblies_loading;
-
-/* A hashtable of reflection only load thread->assemblies mappings */
-static GHashTable *assemblies_refonly_loading;
-
 /* If defined, points to the bundled assembly information */
 const MonoBundledAssembly **bundles;
 
@@ -135,6 +139,8 @@ static GSList *loaded_assembly_bindings = NULL;
 
 static MonoAssembly*
 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, gboolean refonly, gboolean postload);
+static MonoBoolean
+mono_assembly_is_in_gac (const gchar *filanem);
 
 static gchar*
 encode_public_tok (const guchar *token, gint32 len)
@@ -152,6 +158,20 @@ encode_public_tok (const guchar *token, gint32 len)
        return res;
 }
 
+/**
+ * mono_public_tokens_are_equal:
+ * @pubt1: first public key token
+ * @pubt2: second public key token
+ *
+ * Compare two public key tokens and return #TRUE is they are equal and #FALSE
+ * otherwise.
+ */
+gboolean
+mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
+{
+       return memcmp (pubt1, pubt2, 16) == 0;
+}
+
 static void
 check_path_env (void)
 {
@@ -176,6 +196,7 @@ check_path_env (void)
        if (g_getenv ("MONO_DEBUG") == NULL)
                return;
 
+       splitted = assemblies_path;
        while (*splitted) {
                if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
                        g_warning ("'%s' in MONO_PATH doesn't exist or has wrong permissions.", *splitted);
@@ -209,7 +230,7 @@ check_extra_gac_path_env (void) {
 
        while (*splitted) {
                if (**splitted && !g_file_test (*splitted, G_FILE_TEST_IS_DIR))
-                       g_warning ("'%s' in MONO_GAC_PATH doesn't exist or has wrong permissions.", *splitted);
+                       g_warning ("'%s' in MONO_GAC_PREFIX doesn't exist or has wrong permissions.", *splitted);
 
                splitted++;
        }
@@ -218,19 +239,22 @@ check_extra_gac_path_env (void) {
 static gboolean
 assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
 {
+       if (!info || !info->name)
+               return FALSE;
+
        if (strcmp (info->name, aname->name))
                return FALSE;
 
        if (info->major != aname->major || info->minor != aname->minor)
                return FALSE;
 
-       if ((info->culture != NULL) != (aname->culture != NULL))
+       if ((info->culture != NULL && info->culture [0]) != (aname->culture != NULL && aname->culture [0])) 
                return FALSE;
        
-       if (info->culture && strcmp (info->culture, aname->culture))
+       if (info->culture && aname->culture && strcmp (info->culture, aname->culture))
                return FALSE;
        
-       if (strcmp ((const char *)info->public_key_token, (const char *)aname->public_key_token))
+       if (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
                return FALSE;
 
        return TRUE;
@@ -239,6 +263,9 @@ assembly_binding_maps_name (MonoAssemblyBindingInfo *info, MonoAssemblyName *ana
 static void
 mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
 {
+       if (!info)
+               return;
+
        g_free (info->name);
        g_free (info->culture);
 }
@@ -372,39 +399,12 @@ mono_assembly_names_equal (MonoAssemblyName *l, MonoAssemblyName *r)
        if (!l->public_key_token [0] || !r->public_key_token [0])
                return TRUE;
 
-       if (strcmp ((char*)l->public_key_token, (char*)r->public_key_token))
+       if (!mono_public_tokens_are_equal (l->public_key_token, r->public_key_token))
                return FALSE;
 
        return TRUE;
 }
 
-static MonoAssembly*
-search_loaded (MonoAssemblyName* aname, gboolean refonly)
-{
-       GList *tmp;
-       MonoAssembly *ass;
-       GList *loading;
-
-       ass = mono_assembly_invoke_search_hook_internal (aname, refonly, FALSE);
-       if (ass)
-               return ass;
-       
-       /*
-        * 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, (gpointer)GetCurrentThreadId ());
-       for (tmp = loading; tmp; tmp = tmp->next) {
-               ass = tmp->data;
-               if (!mono_assembly_names_equal (aname, &ass->aname))
-                       continue;
-
-               return ass;
-       }
-
-       return NULL;
-}
-
 static MonoAssembly *
 load_in_path (const char *basename, const char** search_path, MonoImageOpenStatus *status, MonoBoolean refonly)
 {
@@ -496,12 +496,12 @@ mono_set_dirs (const char *assembly_dir, const char *config_dir)
        mono_set_config_dir (config_dir);
 }
 
-#ifndef PLATFORM_WIN32
+#ifndef HOST_WIN32
 
 static char *
 compute_base (char *path)
 {
-       char *p = rindex (path, '/');
+       char *p = strrchr (path, '/');
        if (p == NULL)
                return NULL;
 
@@ -510,7 +510,7 @@ compute_base (char *path)
                return NULL;
            
        *p = 0;
-       p = rindex (path, '/');
+       p = strrchr (path, '/');
        if (p == NULL)
                return NULL;
        
@@ -555,25 +555,7 @@ set_dirs (char *exe)
        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
+#endif /* HOST_WIN32 */
 
 /**
  * mono_set_rootdir:
@@ -584,24 +566,54 @@ DWORD ceGetModuleFileNameA(HMODULE hModule, char* lpFilename, DWORD nSize)
 void
 mono_set_rootdir (void)
 {
-#ifdef PLATFORM_WIN32
-       gunichar2 moddir [MAXPATHLEN];
-       gchar *bindir, *installdir, *root, *utf8name, *config;
+#if defined(HOST_WIN32) || (defined(PLATFORM_MACOSX) && !defined(TARGET_ARM))
+       gchar *bindir, *installdir, *root, *name, *config;
+
+#ifdef HOST_WIN32
+       name = mono_get_module_file_name ((HMODULE) &__ImageBase);
+#else
+       {
+               /* 
+                * _NSGetExecutablePath may return -1 to indicate buf is not large
+                *  enough, but we ignore that case to avoid having to do extra dynamic
+                *  allocation for the path and hope that 4096 is enough - this is 
+                *  ok in the Linux/Solaris case below at least...
+                */
+               
+               gchar buf[4096];
+               guint buf_size = sizeof (buf);
+               if (_NSGetExecutablePath (buf, &buf_size) == 0)
+                       name = g_strdup (buf);
+               if (name == NULL) {
+                       fallback ();
+                       return;
+               }
+       }
+#endif
 
-       GetModuleFileNameW (NULL, moddir, MAXPATHLEN);
-       utf8name = g_utf16_to_utf8 (moddir, -1, NULL, NULL, NULL);
-       bindir = g_path_get_dirname (utf8name);
+       bindir = g_path_get_dirname (name);
        installdir = g_path_get_dirname (bindir);
        root = g_build_path (G_DIR_SEPARATOR_S, installdir, "lib", NULL);
 
        config = g_build_filename (root, "..", "etc", NULL);
+#ifdef HOST_WIN32
        mono_set_dirs (root, config);
+#else
+       if (g_file_test (root, G_FILE_TEST_EXISTS) && g_file_test (config, G_FILE_TEST_EXISTS))
+               mono_set_dirs (root, config);
+       else
+               fallback ();
+#endif
 
        g_free (config);
        g_free (root);
        g_free (installdir);
        g_free (bindir);
-       g_free (utf8name);
+       g_free (name);
+#elif defined(DISABLE_MONO_AUTODETECTION)
+       fallback ();
 #else
        char buf [4096];
        int  s;
@@ -648,9 +660,6 @@ mono_assemblies_init (void)
        check_extra_gac_path_env ();
 
        InitializeCriticalSection (&assemblies_mutex);
-
-       assemblies_loading = g_hash_table_new (NULL, NULL);
-       assemblies_refonly_loading = g_hash_table_new (NULL, NULL);
 }
 
 gboolean
@@ -719,12 +728,12 @@ char*
 mono_stringify_assembly_name (MonoAssemblyName *aname)
 {
        return g_strdup_printf (
-               "%s, Version=%d.%d.%d.%d, Culture=%s%s%s",
+               "%s, Version=%d.%d.%d.%d, Culture=%s, PublicKeyToken=%s%s",
                aname->name,
                aname->major, aname->minor, aname->build, aname->revision,
                aname->culture && *aname->culture? aname->culture: "neutral",
-               aname->public_key_token [0] ? ", PublicKeyToken=" : "",
-               aname->public_key_token [0] ? (char *)aname->public_key_token : "");
+               aname->public_key_token [0] ? (char *)aname->public_key_token : "null",
+               (aname->flags & ASSEMBLYREF_RETARGETABLE_FLAG) ? ", Retargetable=Yes" : "");
 }
 
 static gchar*
@@ -856,8 +865,11 @@ mono_assembly_load_reference (MonoImage *image, int index)
         * it inside a critical section.
         */
        mono_assemblies_lock ();
-       if (!image->references)
-               mono_assembly_load_references (image, &status);
+       if (!image->references) {
+               MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
+       
+               image->references = g_new0 (MonoAssembly *, t->rows + 1);
+       }
        reference = image->references [index];
        mono_assemblies_unlock ();
        if (reference)
@@ -869,8 +881,13 @@ mono_assembly_load_reference (MonoImage *image, int index)
                /* We use the loaded corlib */
                if (!strcmp (aname.name, "mscorlib"))
                        reference = mono_assembly_load_full (&aname, image->assembly->basedir, &status, FALSE);
-               else
+               else {
                        reference = mono_assembly_loaded_full (&aname, TRUE);
+                       if (!reference)
+                               /* Try a postload search hook */
+                               reference = mono_assembly_invoke_search_hook_internal (&aname, TRUE, TRUE);
+               }
+
                /*
                 * Here we must advice that the error was due to
                 * a non loaded reference using the ReflectionOnly api
@@ -881,16 +898,18 @@ mono_assembly_load_reference (MonoImage *image, int index)
                reference = mono_assembly_load (&aname, image->assembly? image->assembly->basedir: NULL, &status);
 
        if (reference == NULL){
-               char *extra_msg = g_strdup ("");
+               char *extra_msg;
 
                if (status == MONO_IMAGE_ERROR_ERRNO && errno == ENOENT) {
-                       extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly->basedir);
+                       extra_msg = g_strdup_printf ("The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (%s).\n", image->assembly != NULL ? image->assembly->basedir : "" );
                } else if (status == MONO_IMAGE_ERROR_ERRNO) {
                        extra_msg = g_strdup_printf ("System error: %s\n", strerror (errno));
                } else if (status == MONO_IMAGE_MISSING_ASSEMBLYREF) {
                        extra_msg = g_strdup ("Cannot find an assembly referenced from this one.\n");
                } else if (status == MONO_IMAGE_IMAGE_INVALID) {
                        extra_msg = g_strdup ("The file exists but is not a valid assembly.\n");
+               } else {
+                       extra_msg = g_strdup ("");
                }
                
                g_warning ("The following assembly referenced from %s could not be loaded:\n"
@@ -934,13 +953,8 @@ mono_assembly_load_reference (MonoImage *image, int index)
 void
 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
 {
-       MonoTableInfo *t;
-
+       /* This is a no-op now but it is part of the embedding API so we can't remove it */
        *status = MONO_IMAGE_OK;
-
-       t = &image->tables [MONO_TABLE_ASSEMBLYREF];
-       
-       image->references = g_new0 (MonoAssembly *, t->rows + 1);
 }
 
 typedef struct AssemblyLoadHook AssemblyLoadHook;
@@ -1167,8 +1181,12 @@ absolute_dir (const gchar *filename)
        gchar *res;
        gint i;
 
-       if (g_path_is_absolute (filename))
-               return g_path_get_dirname (filename);
+       if (g_path_is_absolute (filename)) {
+               part = g_path_get_dirname (filename);
+               res = g_strconcat (part, G_DIR_SEPARATOR_S, NULL);
+               g_free (part);
+               return res;
+       }
 
        cwd = g_get_current_dir ();
        mixed = g_build_filename (cwd, filename, NULL);
@@ -1240,7 +1258,7 @@ mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *statu
        mono_assemblies_lock ();
        for (i = 0; !image && bundles [i]; ++i) {
                if (strcmp (bundles [i]->name, name) == 0) {
-                       image = mono_image_open_from_data_full ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly);
+                       image = mono_image_open_from_data_with_name ((char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, name);
                        break;
                }
        }
@@ -1260,6 +1278,7 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
        MonoAssembly *ass;
        MonoImageOpenStatus def_status;
        gchar *fname;
+       gchar *new_fname;
        
        g_return_val_if_fail (filename != NULL, NULL);
 
@@ -1297,17 +1316,25 @@ 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);
+                       "Assembly Loader probing location: '%s'.", fname);
+
+       new_fname = NULL;
+       if (!mono_assembly_is_in_gac (fname))
+               new_fname = mono_make_shadow_copy (fname);
+       if (new_fname && new_fname != fname) {
+               g_free (fname);
+               fname = new_fname;
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
+                           "Assembly Loader shadow-copied assembly to: '%s'.", fname);
+       }
+       
        image = NULL;
 
        if (bundles != NULL)
                image = mono_assembly_open_from_bundle (fname, status, refonly);
 
-       if (!image) {
-               mono_assemblies_lock ();
+       if (!image)
                image = mono_image_open_full (fname, status, refonly);
-               mono_assemblies_unlock ();
-       }
 
        if (!image){
                if (*status == MONO_IMAGE_OK)
@@ -1341,8 +1368,14 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
        return ass;
 }
 
+static void
+free_item (gpointer val, gpointer user_data)
+{
+       g_free (val);
+}
+
 /*
- * mono_load_friend_assemblies:
+ * mono_assembly_load_friends:
  * @ass: an assembly
  *
  * Load the list of friend assemblies that are allowed to access
@@ -1352,14 +1385,39 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
  * This is an internal method, we need this because when we load mscorlib
  * we do not have the mono_defaults.internals_visible_class loaded yet,
  * so we need to load these after we initialize the runtime. 
+ *
+ * LOCKING: Acquires the assemblies lock plus the loader lock.
  */
 void
 mono_assembly_load_friends (MonoAssembly* ass)
 {
        int i;
-       MonoCustomAttrInfo* attrs = mono_custom_attrs_from_assembly (ass);
-       if (!attrs)
+       MonoCustomAttrInfo* attrs;
+       GSList *list;
+
+       if (ass->friend_assembly_names_inited)
+               return;
+
+       attrs = mono_custom_attrs_from_assembly (ass);
+       if (!attrs) {
+               mono_assemblies_lock ();
+               ass->friend_assembly_names_inited = TRUE;
+               mono_assemblies_unlock ();
                return;
+       }
+
+       mono_assemblies_lock ();
+       if (ass->friend_assembly_names_inited) {
+               mono_assemblies_unlock ();
+               return;
+       }
+       mono_assemblies_unlock ();
+
+       list = NULL;
+       /* 
+        * We build the list outside the assemblies lock, the worse that can happen
+        * is that we'll need to free the allocated list.
+        */
        for (i = 0; i < attrs->num_attrs; ++i) {
                MonoCustomAttrEntry *attr = &attrs->attrs [i];
                MonoAssemblyName *aname;
@@ -1377,13 +1435,27 @@ mono_assembly_load_friends (MonoAssembly* ass)
                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);
+               if (mono_assembly_name_parse_full (data, aname, TRUE, NULL, NULL)) {
+                       list = g_slist_prepend (list, aname);
                } else {
                        g_free (aname);
                }
        }
        mono_custom_attrs_free (attrs);
+
+       mono_assemblies_lock ();
+       if (ass->friend_assembly_names_inited) {
+               mono_assemblies_unlock ();
+               g_slist_foreach (list, free_item, NULL);
+               g_slist_free (list);
+               return;
+       }
+       ass->friend_assembly_names = list;
+
+       /* Because of the double checked locking pattern above */
+       mono_memory_barrier ();
+       ass->friend_assembly_names_inited = TRUE;
+       mono_assemblies_unlock ();
 }
 
 /**
@@ -1410,8 +1482,6 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
 {
        MonoAssembly *ass, *ass2;
        char *base_dir;
-       GList *loading;
-       GHashTable *ass_loading;
 
        if (!image->tables [MONO_TABLE_ASSEMBLY].rows) {
                /* 'image' doesn't have a manifest -- maybe someone is trying to Assembly.Load a .netmodule */
@@ -1419,7 +1489,7 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
                return NULL;
        }
 
-#if defined (PLATFORM_WIN32)
+#if defined (HOST_WIN32)
        {
                gchar *tmp_fn;
                int i;
@@ -1437,14 +1507,6 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        base_dir = absolute_dir (fname);
 #endif
 
-       /*
-        * To avoid deadlocks and scalability problems, we load assemblies outside
-        * the assembly lock. This means that multiple threads might try to load
-        * the same assembly at the same time. The first one to load it completely
-        * "wins", the other threads free their copy and use the one loaded by
-        * the winning thread.
-        */
-
        /*
         * Create assembly struct, and enter it into the assembly cache
         */
@@ -1455,22 +1517,29 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
 
        mono_profiler_assembly_event (ass, MONO_PROFILE_START_LOAD);
 
+       mono_assembly_fill_assembly_name (image, &ass->aname);
+
+       if (mono_defaults.corlib && strcmp (ass->aname.name, "mscorlib") == 0) {
+               // MS.NET doesn't support loading other mscorlibs
+               g_free (ass);
+               g_free (base_dir);
+               mono_image_addref (mono_defaults.corlib);
+               *status = MONO_IMAGE_OK;
+               return mono_defaults.corlib->assembly;
+       }
+
        /* 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.
+        * The load hooks might take locks so we can't call them while holding the
+        * assemblies lock.
         */
-       mono_assemblies_lock ();
        if (ass->aname.name) {
-               /* avoid loading the same assembly twice for now... */
-               ass2 = search_loaded (&ass->aname, refonly);
+               ass2 = mono_assembly_invoke_search_hook_internal (&ass->aname, refonly, FALSE);
                if (ass2) {
-                       mono_assemblies_unlock ();
                        g_free (ass);
                        g_free (base_dir);
                        mono_image_close (image);
@@ -1478,53 +1547,33 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
                        return ass2;
                }
        }
-       ass_loading = refonly ? assemblies_refonly_loading : assemblies_loading;
-       loading = g_hash_table_lookup (ass_loading, (gpointer)GetCurrentThreadId ());
-       loading = g_list_prepend (loading, ass);
-       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, (gpointer)GetCurrentThreadId ());
-       loading = g_list_remove (loading, ass);
-       if (loading == NULL)
-               /* Prevent memory leaks */
-               g_hash_table_remove (ass_loading, (gpointer)GetCurrentThreadId ());
-       else
-               g_hash_table_insert (ass_loading, (gpointer)GetCurrentThreadId (), loading);
-       if (*status != MONO_IMAGE_OK) {
+       if (image->assembly) {
+               /* 
+                * This means another thread has already loaded the assembly, but not yet
+                * called the load hooks so the search hook can't find the assembly.
+                */
                mono_assemblies_unlock ();
-               
-               mono_profiler_assembly_loaded (ass, MONO_PROFILE_FAILED);
-               
-               mono_assembly_close (ass);
-               return NULL;
+               ass2 = image->assembly;
+               g_free (ass);
+               g_free (base_dir);
+               mono_image_close (image);
+               *status = MONO_IMAGE_OK;
+               return ass2;
        }
 
-       if (ass->aname.name) {
-               ass2 = search_loaded (&ass->aname, refonly);
-               if (ass2) {
-                       /* Somebody else has loaded the assembly before us */
-                       mono_assemblies_unlock ();
-                       
-                       mono_profiler_assembly_loaded (ass, MONO_PROFILE_FAILED);
-                       
-                       mono_assembly_close (ass);
-                       return ass2;
-               }
-       }
+       image->assembly = ass;
 
        loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
-       if (mono_defaults.internals_visible_class)
-               mono_assembly_load_friends (ass);
        mono_assemblies_unlock ();
 
+#ifdef HOST_WIN32
+       if (image->is_module_handle)
+               mono_image_fixup_vtable (image);
+#endif
+
        mono_assembly_invoke_load_hook (ass);
 
        mono_profiler_assembly_loaded (ass, MONO_PROFILE_OK);
@@ -1609,6 +1658,10 @@ parse_public_key (const gchar *key, gchar** pubkey)
        bitlen = read32 (header + 12) >> 3;
        if ((bitlen + 16 + 4) != pkeylen)
                return FALSE;
+
+       /* parsing is OK and the public key itself is not requested back */
+       if (!pubkey)
+               return TRUE;
                
        /* Encode the size of the blob */
        offset = 0;
@@ -1625,14 +1678,14 @@ parse_public_key (const gchar *key, gchar** pubkey)
                arr [i] = g_ascii_xdigit_value (key [j++]) << 4;
                arr [i] |= g_ascii_xdigit_value (key [j++]);
        }
-       if (pubkey)
-               *pubkey = arr;
+
+       *pubkey = arr;
 
        return TRUE;
 }
 
 static gboolean
-build_assembly_name (const char *name, const char *version, const char *culture, const char *token, const char *key, MonoAssemblyName *aname, gboolean save_public_key)
+build_assembly_name (const char *name, const char *version, const char *culture, const char *token, const char *key, guint32 flags, MonoAssemblyName *aname, gboolean save_public_key)
 {
        gint major, minor, build, revision;
        gint len;
@@ -1661,6 +1714,7 @@ build_assembly_name (const char *name, const char *version, const char *culture,
                        aname->revision = 0;
        }
        
+       aname->flags = flags;
        aname->name = g_strdup (name);
        
        if (culture) {
@@ -1671,13 +1725,20 @@ build_assembly_name (const char *name, const char *version, const char *culture,
        }
        
        if (token && strncmp (token, "null", 4) != 0) {
-               char *lower = g_ascii_strdown (token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
+               char *lower;
+
+               /* the constant includes the ending NULL, hence the -1 */
+               if (strlen (token) != (MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)) {
+                       mono_assembly_name_free (aname);
+                       return FALSE;
+               }
+               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)) {
+       if (key) {
+               if (strcmp (key, "null") == 0 || !parse_public_key (key, &pkey)) {
                        mono_assembly_name_free (aname);
                        return FALSE;
                }
@@ -1694,7 +1755,7 @@ build_assembly_name (const char *name, const char *version, const char *culture,
                else
                        g_free (pkey);
        }
-       
+
        return TRUE;
 }
 
@@ -1710,30 +1771,36 @@ parse_assembly_directory_name (const char *name, const char *dirname, MonoAssemb
                return FALSE;
        }
        
-       res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, aname, FALSE);
+       res = build_assembly_name (name, parts[0], parts[1], parts[2], NULL, 0, aname, FALSE);
        g_strfreev (parts);
        return res;
 }
 
 gboolean
-mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined)
+mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboolean save_public_key, gboolean *is_version_defined, gboolean *is_token_defined)
 {
        gchar *dllname;
        gchar *version = NULL;
        gchar *culture = NULL;
        gchar *token = NULL;
        gchar *key = NULL;
+       gchar *retargetable = NULL;
        gboolean res;
        gchar *value;
        gchar **parts;
        gchar **tmp;
        gboolean version_defined;
+       gboolean token_defined;
+       guint32 flags = 0;
 
        if (!is_version_defined)
                is_version_defined = &version_defined;
        *is_version_defined = FALSE;
+       if (!is_token_defined)
+               is_token_defined = &token_defined;
+       *is_token_defined = FALSE;
        
-       parts = tmp = g_strsplit (name, ",", 4);
+       parts = tmp = g_strsplit (name, ",", 6);
        if (!tmp || !*tmp) {
                g_strfreev (tmp);
                return FALSE;
@@ -1748,24 +1815,51 @@ mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboole
                if (!g_ascii_strncasecmp (value, "Version=", 8)) {
                        *is_version_defined = TRUE;
                        version = g_strstrip (value + 8);
+                       if (strlen (version) == 0) {
+                               goto cleanup_and_fail;
+                       }
                        tmp++;
                        continue;
                }
 
                if (!g_ascii_strncasecmp (value, "Culture=", 8)) {
                        culture = g_strstrip (value + 8);
+                       if (strlen (culture) == 0) {
+                               goto cleanup_and_fail;
+                       }
                        tmp++;
                        continue;
                }
 
                if (!g_ascii_strncasecmp (value, "PublicKeyToken=", 15)) {
+                       *is_token_defined = TRUE;
                        token = g_strstrip (value + 15);
+                       if (strlen (token) == 0) {
+                               goto cleanup_and_fail;
+                       }
                        tmp++;
                        continue;
                }
 
                if (!g_ascii_strncasecmp (value, "PublicKey=", 10)) {
                        key = g_strstrip (value + 10);
+                       if (strlen (key) == 0) {
+                               goto cleanup_and_fail;
+                       }
+                       tmp++;
+                       continue;
+               }
+
+               if (!g_ascii_strncasecmp (value, "Retargetable=", 13)) {
+                       retargetable = g_strstrip (value + 13);
+                       if (strlen (retargetable) == 0) {
+                               goto cleanup_and_fail;
+                       }
+                       if (!g_ascii_strcasecmp (retargetable, "yes")) {
+                               flags |= ASSEMBLYREF_RETARGETABLE_FLAG;
+                       } else if (g_ascii_strcasecmp (retargetable, "no")) {
+                               goto cleanup_and_fail;
+                       }
                        tmp++;
                        continue;
                }
@@ -1780,9 +1874,19 @@ mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboole
                return FALSE;
        }
 
-       res = build_assembly_name (dllname, version, culture, token, key, aname, save_public_key);
+       /* if retargetable flag is set, then we must have a fully qualified name */
+       if (retargetable != NULL && (version == NULL || culture == NULL || (key == NULL && token == NULL))) {
+               goto cleanup_and_fail;
+       }
+
+       res = build_assembly_name (dllname, version, culture, token, key, flags,
+               aname, save_public_key);
        g_strfreev (parts);
        return res;
+
+cleanup_and_fail:
+       g_strfreev (parts);
+       return FALSE;
 }
 
 /**
@@ -1798,7 +1902,58 @@ mono_assembly_name_parse_full (const char *name, MonoAssemblyName *aname, gboole
 gboolean
 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
 {
-       return mono_assembly_name_parse_full (name, aname, FALSE, NULL);
+       return mono_assembly_name_parse_full (name, aname, FALSE, NULL, NULL);
+}
+
+/**
+ * mono_assembly_name_new:
+ * @name: name to parse
+ *
+ * Allocate a new MonoAssemblyName and fill its values from the
+ * passed @name.
+ *
+ * Returns: a newly allocated structure or NULL if there was any failure.
+ */
+MonoAssemblyName*
+mono_assembly_name_new (const char *name)
+{
+       MonoAssemblyName *aname = g_new0 (MonoAssemblyName, 1);
+       if (mono_assembly_name_parse (name, aname))
+               return aname;
+       g_free (aname);
+       return NULL;
+}
+
+const char*
+mono_assembly_name_get_name (MonoAssemblyName *aname)
+{
+       return aname->name;
+}
+
+const char*
+mono_assembly_name_get_culture (MonoAssemblyName *aname)
+{
+       return aname->culture;
+}
+
+mono_byte*
+mono_assembly_name_get_pubkeytoken (MonoAssemblyName *aname)
+{
+       if (aname->public_key_token [0])
+               return aname->public_key_token;
+       return NULL;
+}
+
+uint16_t
+mono_assembly_name_get_version (MonoAssemblyName *aname, uint16_t *minor, uint16_t *build, uint16_t *revision)
+{
+       if (minor)
+               *minor = aname->minor;
+       if (build)
+               *build = aname->build;
+       if (revision)
+               *revision = aname->revision;
+       return aname->major;
 }
 
 static MonoAssembly*
@@ -1827,7 +1982,7 @@ probe_for_partial_name (const char *basepath, const char *fullname, MonoAssembly
                        match = FALSE;
                        
                if (match && strlen ((char*)aname->public_key_token) > 0 && 
-                               strcmp ((char*)aname->public_key_token, (char*)gac_aname.public_key_token) != 0)
+                               !mono_public_tokens_are_equal (aname->public_key_token, gac_aname.public_key_token))
                        match = FALSE;
                
                if (match) {
@@ -1944,6 +2099,67 @@ mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *sta
        return res;
 }
 
+static MonoBoolean
+mono_assembly_is_in_gac (const gchar *filename)
+{
+       const gchar *rootdir;
+       gchar *gp;
+       gchar **paths;
+
+       if (filename == NULL)
+               return FALSE;
+
+       for (paths = extra_gac_paths; paths && *paths; paths++) {
+               if (strstr (*paths, filename) != *paths)
+                       continue;
+
+               gp = (gchar *) (filename + strlen (*paths));
+               if (*gp != G_DIR_SEPARATOR)
+                       continue;
+               gp++;
+               if (strncmp (gp, "lib", 3))
+                       continue;
+               gp += 3;
+               if (*gp != G_DIR_SEPARATOR)
+                       continue;
+               gp++;
+               if (strncmp (gp, "mono", 4))
+                       continue;
+               gp += 4;
+               if (*gp != G_DIR_SEPARATOR)
+                       continue;
+               gp++;
+               if (strncmp (gp, "gac", 3))
+                       continue;
+               gp += 3;
+               if (*gp != G_DIR_SEPARATOR)
+                       continue;
+
+               return TRUE;
+       }
+
+       rootdir = mono_assembly_getrootdir ();
+       if (strstr (filename, rootdir) != filename)
+               return FALSE;
+
+       gp = (gchar *) (filename + strlen (rootdir));
+       if (*gp != G_DIR_SEPARATOR)
+               return FALSE;
+       gp++;
+       if (strncmp (gp, "mono", 4))
+               return FALSE;
+       gp += 4;
+       if (*gp != G_DIR_SEPARATOR)
+               return FALSE;
+       gp++;
+       if (strncmp (gp, "gac", 3))
+               return FALSE;
+       gp += 3;
+       if (*gp != G_DIR_SEPARATOR)
+               return FALSE;
+       return TRUE;
+}
+
 static MonoImage*
 mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
 {
@@ -1959,10 +2175,9 @@ mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
        } else
                name = g_strdup (aname->name);
        
-       if (aname->culture) {
-               culture = g_strdup (aname->culture);
-               g_strdown (culture);
-       } else
+       if (aname->culture)
+               culture = g_utf8_strdown (aname->culture, -1);
+       else
                culture = g_strdup ("");
        
        pname = g_strdup_printf ("policy.%d.%d.%s", aname->major, aname->minor, name);
@@ -2029,17 +2244,135 @@ search_binding_loaded (MonoAssemblyName *aname)
        return NULL;
 }
 
+static inline gboolean
+info_compare_versions (AssemblyVersionSet *left, AssemblyVersionSet *right)
+{
+       if (left->major != right->major || left->minor != right->minor ||
+           left->build != right->build || left->revision != right->revision)
+               return FALSE;
+
+       return TRUE;
+}
+
+static inline gboolean
+info_versions_equal (MonoAssemblyBindingInfo *left, MonoAssemblyBindingInfo *right)
+{
+       if (left->has_old_version_bottom != right->has_old_version_bottom)
+               return FALSE;
+
+       if (left->has_old_version_top != right->has_old_version_top)
+               return FALSE;
+
+       if (left->has_new_version != right->has_new_version)
+               return FALSE;
+
+       if (left->has_old_version_bottom && !info_compare_versions (&left->old_version_bottom, &right->old_version_bottom))
+               return FALSE;
+
+       if (left->has_old_version_top && !info_compare_versions (&left->old_version_top, &right->old_version_top))
+               return FALSE;
+
+       if (left->has_new_version && !info_compare_versions (&left->new_version, &right->new_version))
+               return FALSE;
+
+       return TRUE;
+}
+
+/* LOCKING: assumes all the necessary locks are held */
+static void
+assembly_binding_info_parsed (MonoAssemblyBindingInfo *info, void *user_data)
+{
+       MonoAssemblyBindingInfo *info_copy;
+       GSList *tmp;
+       MonoAssemblyBindingInfo *info_tmp;
+       MonoDomain *domain = (MonoDomain*)user_data;
+
+       if (!domain)
+               return;
+
+       for (tmp = domain->assembly_bindings; tmp; tmp = tmp->next) {
+               info_tmp = tmp->data;
+               if (strcmp (info->name, info_tmp->name) == 0 && info_versions_equal (info, info_tmp))
+                       return;
+       }
+
+       info_copy = mono_mempool_alloc0 (domain->mp, sizeof (MonoAssemblyBindingInfo));
+       memcpy (info_copy, info, sizeof (MonoAssemblyBindingInfo));
+       if (info->name)
+               info_copy->name = mono_mempool_strdup (domain->mp, info->name);
+       if (info->culture)
+               info_copy->culture = mono_mempool_strdup (domain->mp, info->culture);
+
+       domain->assembly_bindings = g_slist_append_mempool (domain->mp, domain->assembly_bindings, info_copy);
+}
+
+static inline gboolean
+info_major_minor_in_range (MonoAssemblyBindingInfo *info, MonoAssemblyName *aname)
+{
+       if (!info->has_old_version_bottom)
+               return FALSE;
+
+       if (info->old_version_bottom.major > aname->major || info->old_version_bottom.minor > aname->minor)
+               return FALSE;
+
+       if (info->has_old_version_top && (info->old_version_top.major < aname->major || info->old_version_top.minor < aname->minor))
+               return FALSE;
+
+       /* This is not the nicest way to do it, but it's a by-product of the way parsing is done */
+       info->major = aname->major;
+       info->minor = aname->minor;
+
+       return TRUE;
+}
+
+/* LOCKING: Assumes that we are already locked - both loader and domain locks */
+static MonoAssemblyBindingInfo*
+get_per_domain_assembly_binding_info (MonoDomain *domain, MonoAssemblyName *aname)
+{
+       MonoAssemblyBindingInfo *info;
+       GSList *list;
+
+       if (!domain->assembly_bindings)
+               return NULL;
+
+       info = NULL;
+       for (list = domain->assembly_bindings; list; list = list->next) {
+               info = list->data;
+               if (info && !strcmp (aname->name, info->name) && info_major_minor_in_range (info, aname))
+                       break;
+               info = NULL;
+       }
+
+       if (info) {
+               if (info->name && info->public_key_token [0] && info->has_old_version_bottom &&
+                   info->has_new_version && assembly_binding_maps_name (info, aname))
+                       info->is_valid = TRUE;
+               else
+                       info->is_valid = FALSE;
+       }
+
+       return info;
+}
+
 static MonoAssemblyName*
 mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_name)
 {
        MonoAssemblyBindingInfo *info, *info2;
        MonoImage *ppimage;
+       MonoDomain *domain;
 
        if (aname->public_key_token [0] == 0)
                return aname;
 
+       domain = mono_domain_get ();
        mono_loader_lock ();
        info = search_binding_loaded (aname);
+       if (!info) {
+               mono_domain_lock (domain);
+               info = get_per_domain_assembly_binding_info (domain, aname);
+               mono_domain_unlock (domain);
+       }
+
        mono_loader_unlock ();
        if (info) {
                if (!check_policy_versions (info, aname))
@@ -2049,14 +2382,36 @@ mono_assembly_apply_binding (MonoAssemblyName *aname, MonoAssemblyName *dest_nam
                return dest_name;
        }
 
-       info = g_new0 (MonoAssemblyBindingInfo, 1);
-       info->major = aname->major;
-       info->minor = aname->minor;
-       
-       ppimage = mono_assembly_load_publisher_policy (aname);
-       if (ppimage) {
-               get_publisher_policy_info (ppimage, aname, info);
-               mono_image_close (ppimage);
+       if (domain && domain->setup && domain->setup->configuration_file) {
+               mono_domain_lock (domain);
+               if (!domain->assembly_bindings_parsed) {
+                       gchar *domain_config_file = mono_string_to_utf8 (domain->setup->configuration_file);
+
+                       mono_config_parse_assembly_bindings (domain_config_file, aname->major, aname->minor, domain, assembly_binding_info_parsed);
+                       domain->assembly_bindings_parsed = TRUE;
+                       g_free (domain_config_file);
+               }
+               mono_domain_unlock (domain);
+
+               mono_loader_lock ();
+               mono_domain_lock (domain);
+               info = get_per_domain_assembly_binding_info (domain, aname);
+               mono_domain_unlock (domain);
+               mono_loader_unlock ();
+       }
+
+       if (!info) {
+               info = g_new0 (MonoAssemblyBindingInfo, 1);
+               info->major = aname->major;
+               info->minor = aname->minor;
+       }
+
+       if (!info->is_valid) {
+               ppimage = mono_assembly_load_publisher_policy (aname);
+               if (ppimage) {
+                       get_publisher_policy_info (ppimage, aname, info);
+                       mono_image_close (ppimage);
+               }
        }
 
        /* Define default error value if needed */
@@ -2099,6 +2454,7 @@ mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImag
        gchar *name, *version, *culture, *fullpath, *subpath;
        gint32 len;
        gchar **paths;
+       char *pubtok;
 
        if (aname->public_key_token [0] == 0) {
                return NULL;
@@ -2113,15 +2469,16 @@ mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImag
        }
 
        if (aname->culture) {
-               culture = g_strdup (aname->culture);
-               g_strdown (culture);
+               culture = g_utf8_strdown (aname->culture, -1);
        } else {
                culture = g_strdup ("");
        }
 
+       pubtok = g_ascii_strdown ((char*)aname->public_key_token, MONO_PUBLIC_KEY_TOKEN_LENGTH);
        version = g_strdup_printf ("%d.%d.%d.%d_%s_%s", aname->major,
                        aname->minor, aname->build, aname->revision,
-                       culture, aname->public_key_token);
+                       culture, pubtok);
+       g_free (pubtok);
        
        subpath = g_build_path (G_DIR_SEPARATOR_S, name, version, filename, NULL);
        g_free (name);
@@ -2190,10 +2547,11 @@ mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *
        return corlib;
 }
 
-MonoAssembly* mono_assembly_load_full_nosearch (MonoAssemblyName *aname, 
-                                               const char       *basedir, 
-                                               MonoImageOpenStatus *status,
-                                               gboolean refonly)
+MonoAssembly*
+mono_assembly_load_full_nosearch (MonoAssemblyName *aname, 
+                                                                 const char       *basedir, 
+                                                                 MonoImageOpenStatus *status,
+                                                                 gboolean refonly)
 {
        MonoAssembly *result;
        char *fullpath, *filename;
@@ -2317,9 +2675,7 @@ mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
 
        aname = mono_assembly_remap_version (aname, &maped_aname);
 
-       mono_assemblies_lock ();
-       res = search_loaded (aname, refonly);
-       mono_assemblies_unlock ();
+       res = mono_assembly_invoke_search_hook_internal (aname, refonly, FALSE);
 
        return res;
 }
@@ -2337,25 +2693,23 @@ mono_assembly_loaded (MonoAssemblyName *aname)
        return mono_assembly_loaded_full (aname, FALSE);
 }
 
-/**
- * mono_assembly_close:
- * @assembly: the assembly to release.
- *
- * This method releases a reference to the @assembly.  The assembly is
- * only released when all the outstanding references to it are released.
+/*
+ * Returns whether mono_assembly_close_finish() must be called as
+ * well.  See comment for mono_image_close_except_pools() for why we
+ * unload in two steps.
  */
-void
-mono_assembly_close (MonoAssembly *assembly)
+gboolean
+mono_assembly_close_except_image_pools (MonoAssembly *assembly)
 {
        GSList *tmp;
-       g_return_if_fail (assembly != NULL);
+       g_return_val_if_fail (assembly != NULL, FALSE);
 
        if (assembly == REFERENCE_MISSING)
-               return;
-       
+               return FALSE;
+
        /* Might be 0 already */
        if (InterlockedDecrement (&assembly->ref_count) > 0)
-               return;
+               return FALSE;
 
        mono_profiler_assembly_event (assembly, MONO_PROFILE_START_UNLOAD);
 
@@ -2369,7 +2723,8 @@ mono_assembly_close (MonoAssembly *assembly)
 
        assembly->image->assembly = NULL;
 
-       mono_image_close (assembly->image);
+       if (!mono_image_close_except_pools (assembly->image))
+               assembly->image = NULL;
 
        for (tmp = assembly->friend_assembly_names; tmp; tmp = tmp->next) {
                MonoAssemblyName *fname = tmp->data;
@@ -2378,26 +2733,45 @@ mono_assembly_close (MonoAssembly *assembly)
        }
        g_slist_free (assembly->friend_assembly_names);
        g_free (assembly->basedir);
+
+       mono_profiler_assembly_event (assembly, MONO_PROFILE_END_UNLOAD);
+
+       return TRUE;
+}
+
+void
+mono_assembly_close_finish (MonoAssembly *assembly)
+{
+       g_assert (assembly && assembly != REFERENCE_MISSING);
+
+       if (assembly->image)
+               mono_image_close_finish (assembly->image);
+
        if (assembly->dynamic) {
                g_free ((char*)assembly->aname.culture);
        } else {
                g_free (assembly);
        }
+}
 
-       mono_profiler_assembly_event (assembly, MONO_PROFILE_END_UNLOAD);
+/**
+ * mono_assembly_close:
+ * @assembly: the assembly to release.
+ *
+ * This method releases a reference to the @assembly.  The assembly is
+ * only released when all the outstanding references to it are released.
+ */
+void
+mono_assembly_close (MonoAssembly *assembly)
+{
+       if (mono_assembly_close_except_image_pools (assembly))
+               mono_assembly_close_finish (assembly);
 }
 
 MonoImage*
 mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
 {
-       MonoImageOpenStatus status;
-       MonoImage *module;
-
-       module = mono_image_load_file_for_image (assembly->image, idx);
-       if (module)
-               mono_assembly_load_references (module, &status);
-
-       return module;
+       return mono_image_load_file_for_image (assembly->image, idx);
 }
 
 void
@@ -2430,9 +2804,6 @@ mono_assemblies_cleanup (void)
 
        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;
 
@@ -2486,3 +2857,137 @@ mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
 {
        bundles = assemblies;
 }
+
+#define MONO_DECLSEC_FORMAT_10         0x3C
+#define MONO_DECLSEC_FORMAT_20         0x2E
+#define MONO_DECLSEC_FIELD             0x53
+#define MONO_DECLSEC_PROPERTY          0x54
+
+#define SKIP_VISIBILITY_XML_ATTRIBUTE ("\"SkipVerification\"")
+#define SKIP_VISIBILITY_ATTRIBUTE_NAME ("System.Security.Permissions.SecurityPermissionAttribute")
+#define SKIP_VISIBILITY_ATTRIBUTE_SIZE (sizeof (SKIP_VISIBILITY_ATTRIBUTE_NAME) - 1)
+#define SKIP_VISIBILITY_PROPERTY_NAME ("SkipVerification")
+#define SKIP_VISIBILITY_PROPERTY_SIZE (sizeof (SKIP_VISIBILITY_PROPERTY_NAME) - 1)
+
+static gboolean
+mono_assembly_try_decode_skip_verification_param (const char *p, const char **resp, gboolean *abort_decoding)
+{
+       int len;
+       switch (*p++) {
+       case MONO_DECLSEC_PROPERTY:
+               break;
+       case MONO_DECLSEC_FIELD:
+       default:
+               *abort_decoding = TRUE;
+               return FALSE;
+               break;
+       }
+
+       if (*p++ != MONO_TYPE_BOOLEAN) {
+               *abort_decoding = TRUE;
+               return FALSE;
+       }
+               
+       /* property name length */
+       len = mono_metadata_decode_value (p, &p);
+
+       if (len >= SKIP_VISIBILITY_PROPERTY_SIZE && !memcmp (p, SKIP_VISIBILITY_PROPERTY_NAME, SKIP_VISIBILITY_PROPERTY_SIZE)) {
+               p += len;
+               return *p;
+       }
+       p += len + 1;
+
+       *resp = p;
+       return FALSE;
+}
+
+static gboolean
+mono_assembly_try_decode_skip_verification (const char *p, const char *endn)
+{
+       int i, j, num, len, params_len;
+
+       if (*p == MONO_DECLSEC_FORMAT_10) {
+               gsize read, written;
+               char *res = g_convert (p, endn - p, "UTF-8", "UTF-16LE", &read, &written, NULL);
+               if (res) {
+                       gboolean found = strstr (res, SKIP_VISIBILITY_XML_ATTRIBUTE) != NULL;
+                       g_free (res);
+                       return found;
+               }
+               return FALSE;
+       }
+       if (*p++ != MONO_DECLSEC_FORMAT_20)
+               return FALSE;
+
+       /* number of encoded permission attributes */
+       num = mono_metadata_decode_value (p, &p);
+       for (i = 0; i < num; ++i) {
+               gboolean is_valid = FALSE;
+               gboolean abort_decoding = FALSE;
+
+               /* attribute name length */
+               len =  mono_metadata_decode_value (p, &p);
+
+               /* We don't really need to fully decode the type. Comparing the name is enough */
+               is_valid = len >= SKIP_VISIBILITY_ATTRIBUTE_SIZE && !memcmp (p, SKIP_VISIBILITY_ATTRIBUTE_NAME, SKIP_VISIBILITY_ATTRIBUTE_SIZE);
+
+               p += len;
+
+               /*size of the params table*/
+               params_len =  mono_metadata_decode_value (p, &p);
+               if (is_valid) {
+                       const char *params_end = p + params_len;
+                       
+                       /* number of parameters */
+                       len = mono_metadata_decode_value (p, &p);
+       
+                       for (j = 0; j < len; ++j) {
+                               if (mono_assembly_try_decode_skip_verification_param (p, &p, &abort_decoding))
+                                       return TRUE;
+                               if (abort_decoding)
+                                       break;
+                       }
+                       p = params_end;
+               } else {
+                       p += params_len;
+               }
+       }
+       
+       return FALSE;
+}
+
+
+gboolean
+mono_assembly_has_skip_verification (MonoAssembly *assembly)
+{
+       MonoTableInfo *t;       
+       guint32 cols [MONO_DECL_SECURITY_SIZE];
+       const char *blob;
+       int i, len;
+
+       if (MONO_SECMAN_FLAG_INIT (assembly->skipverification))
+               return MONO_SECMAN_FLAG_GET_VALUE (assembly->skipverification);
+
+       t = &assembly->image->tables [MONO_TABLE_DECLSECURITY];
+
+       for (i = 0; i < t->rows; ++i) {
+               mono_metadata_decode_row (t, i, cols, MONO_DECL_SECURITY_SIZE);
+               if ((cols [MONO_DECL_SECURITY_PARENT] & MONO_HAS_DECL_SECURITY_MASK) != MONO_HAS_DECL_SECURITY_ASSEMBLY)
+                       continue;
+               if (cols [MONO_DECL_SECURITY_ACTION] != SECURITY_ACTION_REQMIN)
+                       continue;
+
+               blob = mono_metadata_blob_heap (assembly->image, cols [MONO_DECL_SECURITY_PERMISSIONSET]);
+               len = mono_metadata_decode_blob_size (blob, &blob);
+               if (!len)
+                       continue;
+
+               if (mono_assembly_try_decode_skip_verification (blob, blob + len)) {
+                       MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, TRUE);
+                       return TRUE;
+               }
+       }
+
+       MONO_SECMAN_FLAG_SET_VALUE (assembly->skipverification, FALSE);
+       return FALSE;
+}