2010-07-12 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / assembly.c
index 58d2dca33ec7bbc3353a3cacf4b87eb4bd6f92da..29ef8580add944c3bf2f4eab668bda3b01a1623a 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>
 #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},
@@ -129,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)
@@ -157,7 +169,7 @@ encode_public_tok (const guchar *token, gint32 len)
 gboolean
 mono_public_tokens_are_equal (const unsigned char *pubt1, const unsigned char *pubt2)
 {
-       return g_strcasecmp ((char*)pubt1, (char*)pubt2) == 0;
+       return memcmp (pubt1, pubt2, 16) == 0;
 }
 
 static void
@@ -184,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);
@@ -217,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++;
        }
@@ -226,16 +239,19 @@ 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 (!mono_public_tokens_are_equal (info->public_key_token, aname->public_key_token))
@@ -247,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);
 }
@@ -477,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;
 
@@ -491,7 +510,7 @@ compute_base (char *path)
                return NULL;
            
        *p = 0;
-       p = rindex (path, '/');
+       p = strrchr (path, '/');
        if (p == NULL)
                return NULL;
        
@@ -536,7 +555,7 @@ set_dirs (char *exe)
        g_free (mono);
 }
 
-#endif /* PLATFORM_WIN32 */
+#endif /* HOST_WIN32 */
 
 /**
  * mono_set_rootdir:
@@ -547,22 +566,54 @@ set_dirs (char *exe)
 void
 mono_set_rootdir (void)
 {
-#ifdef PLATFORM_WIN32
+#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
+
        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 (name);
+#elif defined(DISABLE_MONO_AUTODETECTION)
+       fallback ();
 #else
        char buf [4096];
        int  s;
@@ -847,7 +898,7 @@ 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 != NULL ? image->assembly->basedir : "" );
@@ -857,9 +908,11 @@ mono_assembly_load_reference (MonoImage *image, int index)
                        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"
+               mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "The following assembly referenced from %s could not be loaded:\n"
                                   "     Assembly:   %s    (assemblyref_index=%d)\n"
                                   "     Version:    %d.%d.%d.%d\n"
                                   "     Public Key: %s\n%s",
@@ -1205,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;
                }
        }
@@ -1264,7 +1317,10 @@ mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboo
 
        mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
                        "Assembly Loader probing location: '%s'.", fname);
-       new_fname = mono_make_shadow_copy (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;
@@ -1433,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;
@@ -1513,7 +1569,7 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
        mono_assemblies_unlock ();
 
-#ifdef PLATFORM_WIN32
+#ifdef HOST_WIN32
        if (image->is_module_handle)
                mono_image_fixup_vtable (image);
 #endif
@@ -1602,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;
@@ -1618,8 +1678,8 @@ 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;
 }
@@ -1845,6 +1905,57 @@ mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
        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*
 probe_for_partial_name (const char *basepath, const char *fullname, MonoAssemblyName *aname, MonoImageOpenStatus *status)
 {
@@ -1988,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)
 {
@@ -2072,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))
@@ -2092,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 */
@@ -2381,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);
 
@@ -2413,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;
@@ -2422,13 +2733,39 @@ 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*
@@ -2521,10 +2858,12 @@ 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")
@@ -2567,6 +2906,16 @@ 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;