* 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 <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;
{"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},
{"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},
#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;
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)
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)
{
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);
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++;
}
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;
static void
mono_assembly_binding_info_free (MonoAssemblyBindingInfo *info)
{
+ if (!info)
+ return;
+
g_free (info->name);
g_free (info->culture);
}
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)
{
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;
return NULL;
*p = 0;
- p = rindex (path, '/');
+ p = strrchr (path, '/');
if (p == NULL)
return NULL;
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:
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;
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
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*
* 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)
/* 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
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"
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;
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);
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;
}
}
MonoAssembly *ass;
MonoImageOpenStatus def_status;
gchar *fname;
+ gchar *new_fname;
g_return_val_if_fail (filename != NULL, NULL);
}
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)
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
* 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;
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 ();
}
/**
{
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 */
return NULL;
}
-#if defined (PLATFORM_WIN32)
+#if defined (HOST_WIN32)
{
gchar *tmp_fn;
int i;
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
*/
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);
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);
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;
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;
aname->revision = 0;
}
+ aname->flags = flags;
aname->name = g_strdup (name);
if (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;
}
else
g_free (pkey);
}
-
+
return TRUE;
}
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;
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;
}
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;
}
/**
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*
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) {
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)
{
} 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);
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))
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 */
gchar *name, *version, *culture, *fullpath, *subpath;
gint32 len;
gchar **paths;
+ char *pubtok;
if (aname->public_key_token [0] == 0) {
return NULL;
}
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);
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;
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;
}
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);
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;
}
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
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;
{
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;
+}