* TODO:
* This should keep track of the assembly versions that we are loading.
*
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <glib.h>
static guint32 methods_size;
static guint32 signatures_size;
-/*
- * This TLS variable contains the last type load error encountered by the loader.
- */
-MonoNativeTlsKey loader_error_thread_id;
-
/*
* This TLS variable holds how many times the current thread has acquired the loader
* lock.
mono_os_mutex_init_recursive (&global_loader_data_mutex);
loader_lock_inited = TRUE;
- mono_native_tls_alloc (&loader_error_thread_id, NULL);
mono_native_tls_alloc (&loader_lock_nest_id, NULL);
mono_counters_init ();
{
dllmap_cleanup ();
- mono_native_tls_free (loader_error_thread_id);
mono_native_tls_free (loader_lock_nest_id);
mono_coop_mutex_destroy (&loader_mutex);
loader_lock_inited = FALSE;
}
-/*
- * Handling of type load errors should be done as follows:
- *
- * If something could not be loaded, the loader should call one of the
- * mono_loader_set_error_XXX functions ()
- * with the appropriate arguments, then return NULL to report the failure. The error
- * should be propagated until it reaches code which can throw managed exceptions. At that
- * point, an exception should be thrown based on the information returned by
- * mono_loader_get_last_error (). Then the error should be cleared by calling
- * mono_loader_clear_error ().
- */
-
-static void
-set_loader_error (MonoLoaderError *error)
-{
- mono_loader_clear_error ();
- mono_native_tls_set_value (loader_error_thread_id, error);
-}
-
-/**
- * mono_loader_set_error_assembly_load:
- *
- * Set the loader error for this thread.
- */
-void
-mono_loader_set_error_assembly_load (const char *assembly_name, gboolean ref_only)
-{
- MonoLoaderError *error;
-
- if (mono_loader_get_last_error ())
- return;
-
- error = g_new0 (MonoLoaderError, 1);
- error->exception_type = MONO_EXCEPTION_FILE_NOT_FOUND;
- error->assembly_name = g_strdup (assembly_name);
- error->ref_only = ref_only;
-
- /*
- * This is not strictly needed, but some (most) of the loader code still
- * can't deal with load errors, and this message is more helpful than an
- * assert.
- */
- if (ref_only)
- mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "Cannot resolve dependency to assembly '%s' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.", assembly_name);
- else
- mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY, "Could not load file or assembly '%s' or one of its dependencies.", assembly_name);
-
- set_loader_error (error);
-}
-
-/**
- * mono_loader_set_error_type_load:
- *
- * Set the loader error for this thread.
- */
-void
-mono_loader_set_error_type_load (const char *class_name, const char *assembly_name)
-{
- MonoLoaderError *error;
-
- if (mono_loader_get_last_error ())
- return;
-
- error = g_new0 (MonoLoaderError, 1);
- error->exception_type = MONO_EXCEPTION_TYPE_LOAD;
- error->class_name = g_strdup (class_name);
- error->assembly_name = g_strdup (assembly_name);
-
- /*
- * This is not strictly needed, but some (most) of the loader code still
- * can't deal with load errors, and this message is more helpful than an
- * assert.
- */
- mono_trace_warning (MONO_TRACE_TYPE, "The class %s could not be loaded, used in %s", class_name, assembly_name);
-
- set_loader_error (error);
-}
-
-/*
- * mono_loader_set_error_method_load:
- *
- * Set the loader error for this thread. MEMBER_NAME should point to a string
- * inside metadata.
- */
-void
-mono_loader_set_error_method_load (const char *class_name, const char *member_name)
-{
- MonoLoaderError *error;
-
- /* FIXME: Store the signature as well */
- if (mono_loader_get_last_error ())
- return;
-
- error = g_new0 (MonoLoaderError, 1);
- error->exception_type = MONO_EXCEPTION_MISSING_METHOD;
- error->class_name = g_strdup (class_name);
- error->member_name = member_name;
-
- set_loader_error (error);
-}
-
-/*
- * mono_loader_set_error_field_load:
- *
- * Set the loader error for this thread. MEMBER_NAME should point to a string
- * inside metadata.
- */
-void
-mono_loader_set_error_field_load (MonoClass *klass, const char *member_name)
-{
- MonoLoaderError *error;
-
- /* FIXME: Store the signature as well */
- if (mono_loader_get_last_error ())
- return;
-
- error = g_new0 (MonoLoaderError, 1);
- error->exception_type = MONO_EXCEPTION_MISSING_FIELD;
- error->klass = klass;
- error->member_name = member_name;
-
- set_loader_error (error);
-}
-
-/*
- * mono_loader_set_error_bad_image:
- *
- * Set the loader error for this thread.
- */
-void
-mono_loader_set_error_bad_image (char *msg)
-{
- MonoLoaderError *error;
-
- if (mono_loader_get_last_error ())
- return;
-
- error = g_new0 (MonoLoaderError, 1);
- error->exception_type = MONO_EXCEPTION_BAD_IMAGE;
- error->msg = msg;
-
- set_loader_error (error);
-}
-
-
-/*
- * mono_loader_get_last_error:
- *
- * Returns information about the last type load exception encountered by the loader, or
- * NULL. After use, the exception should be cleared by calling mono_loader_clear_error.
- */
-MonoLoaderError*
-mono_loader_get_last_error (void)
-{
- return (MonoLoaderError*)mono_native_tls_get_value (loader_error_thread_id);
-}
-
-void
-mono_loader_assert_no_error (void)
-{
- MonoLoaderError *error = mono_loader_get_last_error ();
-
- if (error) {
- g_print ("Unhandled loader error: %x, %s %s %s\n", error->exception_type, error->msg, error->assembly_name, error->class_name);
- g_assert_not_reached ();
- }
-}
-
-/**
- * mono_loader_clear_error:
- *
- * Disposes any loader error messages on this thread
- */
-void
-mono_loader_clear_error (void)
-{
- MonoLoaderError *ex = (MonoLoaderError*)mono_native_tls_get_value (loader_error_thread_id);
-
- if (ex) {
- g_free (ex->class_name);
- g_free (ex->assembly_name);
- g_free (ex->msg);
- g_free (ex);
-
- mono_native_tls_set_value (loader_error_thread_id, NULL);
- }
-}
-
-/**
- * mono_loader_error_prepare_exception:
- * @error: The MonoLoaderError to turn into an exception
- *
- * This turns a MonoLoaderError into an exception that can be thrown
- * and resets the Mono Loader Error state during this process.
- *
- */
-MonoException *
-mono_loader_error_prepare_exception (MonoLoaderError *error)
-{
- MonoException *ex = NULL;
-
- switch (error->exception_type) {
- case MONO_EXCEPTION_TYPE_LOAD: {
- char *cname = g_strdup (error->class_name);
- char *aname = g_strdup (error->assembly_name);
- MonoString *class_name;
-
- mono_loader_clear_error ();
-
- class_name = mono_string_new (mono_domain_get (), cname);
-
- ex = mono_get_exception_type_load (class_name, aname);
- g_free (cname);
- g_free (aname);
- break;
- }
- case MONO_EXCEPTION_MISSING_METHOD: {
- char *cname = g_strdup (error->class_name);
- char *aname = g_strdup (error->member_name);
-
- mono_loader_clear_error ();
- ex = mono_get_exception_missing_method (cname, aname);
- g_free (cname);
- g_free (aname);
- break;
- }
-
- case MONO_EXCEPTION_MISSING_FIELD: {
- char *class_name;
- char *cmembername = g_strdup (error->member_name);
- if (error->klass)
- class_name = mono_type_get_full_name (error->klass);
- else
- class_name = g_strdup ("");
-
- mono_loader_clear_error ();
-
- ex = mono_get_exception_missing_field (class_name, cmembername);
- g_free (class_name);
- g_free (cmembername);
- break;
- }
-
- case MONO_EXCEPTION_FILE_NOT_FOUND: {
- char *msg;
- char *filename;
-
- if (error->ref_only)
- msg = g_strdup_printf ("Cannot resolve dependency to assembly '%s' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.", error->assembly_name);
- else
- msg = g_strdup_printf ("Could not load file or assembly '%s' or one of its dependencies.", error->assembly_name);
- filename = g_strdup (error->assembly_name);
- /* Has to call this before calling anything which might call mono_class_init () */
- mono_loader_clear_error ();
- ex = mono_get_exception_file_not_found2 (msg, mono_string_new (mono_domain_get (), filename));
- g_free (msg);
- g_free (filename);
- break;
- }
-
- case MONO_EXCEPTION_BAD_IMAGE: {
- char *msg = g_strdup (error->msg);
- mono_loader_clear_error ();
- ex = mono_get_exception_bad_image_format (msg);
- g_free (msg);
- break;
- }
-
- default:
- g_assert_not_reached ();
- }
-
- return ex;
-}
-
/*
* find_cached_memberref_sig:
*
field = mono_class_get_field_from_name_full (klass, fname, sig_type);
if (!field) {
- mono_loader_assert_no_error ();
mono_error_set_field_load (error, klass, fname, "Could not find field '%s'", fname);
}
MonoClass *handle_class;
*retklass = NULL;
- result = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context);
+ MonoError inner_error;
+ result = (MonoClassField *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, &inner_error);
+ mono_error_cleanup (&inner_error);
// This checks the memberref type as well
if (!result || handle_class != mono_defaults.fieldhandle_class) {
mono_error_set_bad_image (error, image, "Bad field token 0x%08x", token);
if (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF) {
field = field_from_memberref (image, token, retklass, context, error);
- mono_loader_assert_no_error ();
} else {
type = mono_metadata_typedef_from_field (image, mono_metadata_token_index (token));
if (!type) {
*retklass = k;
field = mono_class_get_field (k, token);
if (!field) {
- if (mono_loader_get_last_error ())
- mono_error_set_from_loader_error (error);
- else
- mono_error_set_bad_image (error, image, "Could not resolve field token 0x%08x", token);
+ mono_error_set_bad_image (error, image, "Could not resolve field token 0x%08x", token);
}
}
mono_image_unlock (image);
}
- mono_loader_assert_no_error ();
return field;
}
static MonoMethodHeader*
inflate_generic_header (MonoMethodHeader *header, MonoGenericContext *context, MonoError *error)
{
- MonoMethodHeader *res;
- int i;
- res = (MonoMethodHeader *)g_malloc0 (MONO_SIZEOF_METHOD_HEADER + sizeof (gpointer) * header->num_locals);
+ size_t locals_size = sizeof (gpointer) * header->num_locals;
+ size_t clauses_size = header->num_clauses * sizeof (MonoExceptionClause);
+ size_t header_size = MONO_SIZEOF_METHOD_HEADER + locals_size + clauses_size;
+ MonoMethodHeader *res = (MonoMethodHeader *)g_malloc0 (header_size);
+ res->num_locals = header->num_locals;
+ res->clauses = (MonoExceptionClause *) &res->locals [res->num_locals] ;
+ memcpy (res->clauses, header->clauses, clauses_size);
+
res->code = header->code;
res->code_size = header->code_size;
res->max_stack = header->max_stack;
res->num_clauses = header->num_clauses;
res->init_locals = header->init_locals;
- res->num_locals = header->num_locals;
- res->clauses = header->clauses;
+
+ res->is_transient = TRUE;
mono_error_init (error);
- for (i = 0; i < header->num_locals; ++i) {
+ for (int i = 0; i < header->num_locals; ++i) {
res->locals [i] = mono_class_inflate_generic_type_checked (header->locals [i], context, error);
if (!is_ok (error))
goto fail;
}
if (res->num_clauses) {
- res->clauses = (MonoExceptionClause *)g_memdup (header->clauses, sizeof (MonoExceptionClause) * res->num_clauses);
- for (i = 0; i < header->num_clauses; ++i) {
+ for (int i = 0; i < header->num_clauses; ++i) {
MonoExceptionClause *clause = &res->clauses [i];
if (clause->flags != MONO_EXCEPTION_CLAUSE_NONE)
continue;
{
MonoError error;
MonoMethodSignature *res = mono_method_get_signature_checked (method, image, token, context, &error);
- g_assert (mono_error_ok (&error));
+ mono_error_cleanup (&error);
return res;
}
{
MonoError error;
MonoMethodSignature *res = mono_method_get_signature_checked (method, image, token, NULL, &error);
- g_assert (mono_error_ok (&error));
+ mono_error_cleanup (&error);
return res;
}
g_free (msig);
msig = g_string_free (s, FALSE);
- if (mono_loader_get_last_error ()) /* FIXME find_method and mono_method_search_in_array_class can leak a loader error */
- mono_error_set_from_loader_error (error);
- else
- mono_error_set_method_load (error, klass, mname, "Could not find method %s", msig);
+ mono_error_set_method_load (error, klass, mname, "Could not find method %s", msig);
g_free (msig);
}
- mono_loader_assert_no_error ();
return method;
fail:
- mono_loader_assert_no_error ();
g_assert (!mono_error_ok (error));
return NULL;
}
new_context.method_inst = inst;
method = mono_class_inflate_generic_method_full_checked (method, klass, &new_context, error);
- mono_loader_assert_no_error ();
return method;
}
*/
}
if (dll_map->func && strcmp (dll_map->func, func) == 0) {
+ *rdll = dll_map->target;
*rfunc = dll_map->target_func;
break;
}
* @dll: The name of the external library, as it would be found in the DllImport declaration. If prefixed with 'i:' the matching of the library name is done without case sensitivity
* @func: if not null, the mapping will only applied to the named function (the value of EntryPoint)
* @tdll: The name of the library to map the specified @dll if it matches.
- * @tfunc: if func is not NULL, the name of the function that replaces the invocation
+ * @tfunc: The name of the function that replaces the invocation. If NULL, it is replaced with a copy of @func.
*
* LOCKING: Acquires the loader lock.
*
entry->dll = dll? g_strdup (dll): NULL;
entry->target = tdll? g_strdup (tdll): NULL;
entry->func = func? g_strdup (func): NULL;
- entry->target_func = tfunc? g_strdup (tfunc): NULL;
+ entry->target_func = tfunc? g_strdup (tfunc): (func? g_strdup (func): NULL);
global_loader_data_lock ();
entry->next = global_dll_map;
entry->dll = dll? mono_image_strdup (assembly, dll): NULL;
entry->target = tdll? mono_image_strdup (assembly, tdll): NULL;
entry->func = func? mono_image_strdup (assembly, func): NULL;
- entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): NULL;
+ entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): (func? mono_image_strdup (assembly, func): NULL);
mono_image_lock (assembly);
entry->next = assembly->dll_map;
return res;
}
+void
+mono_loader_register_module (const char *name, MonoDl *module)
+{
+ if (!global_module_map)
+ global_module_map = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (global_module_map, g_strdup (name), module);
+}
+
static MonoDl *internal_module;
static gboolean
if (image_is_dynamic (image)) {
MonoClass *handle_class;
- result = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context);
- mono_loader_assert_no_error ();
+ result = (MonoMethod *)mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context, error);
+ mono_error_assert_ok (error);
// This checks the memberref type as well
if (result && handle_class != mono_defaults.methodhandle_class) {
*/
if (*sig & 0x10) {
generic_container = mono_metadata_load_generic_params (image, token, container);
- mono_loader_assert_no_error (); /* FIXME don't swallow this error. */
}
if (generic_container) {
result->is_generic = TRUE;
if (generic_container)
mono_method_set_generic_container (result, generic_container);
- mono_loader_assert_no_error ();
return result;
}
g_assert (method != NULL);
g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
- if (method->is_inflated)
- method = ((MonoMethodInflated *) method)->declaring;
data = (void **)((MonoMethodWrapper *)method)->method_data;
g_assert (data != NULL);
g_assert (id <= GPOINTER_TO_UINT (*data));
AsyncStackWalkUserData ud = { func, user_data };
mono_sigctx_to_monoctx (initial_sig_context, &ctx);
- mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (async_stack_walk_adapter, NULL, MONO_UNWIND_SIGNAL_SAFE, &ud);
+ mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (async_stack_walk_adapter, &ctx, MONO_UNWIND_SIGNAL_SAFE, &ud);
}
static gboolean
}
MonoMethodHeader*
-mono_method_get_header (MonoMethod *method)
+mono_method_get_header_checked (MonoMethod *method, MonoError *error)
{
- MonoError error;
int idx;
guint32 rva;
MonoImage* img;
gpointer loc;
- MonoMethodHeader *header;
MonoGenericContainer *container;
- if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
- return NULL;
-
+ mono_error_init (error);
img = method->klass->image;
+ if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+ mono_error_set_bad_image (error, img, "Method has no body");
+ return NULL;
+ }
+
if (method->is_inflated) {
MonoMethodInflated *imethod = (MonoMethodInflated *) method;
MonoMethodHeader *header, *iheader;
- header = mono_method_get_header (imethod->declaring);
+ header = mono_method_get_header_checked (imethod->declaring, error);
if (!header)
return NULL;
- iheader = inflate_generic_header (header, mono_method_get_context (method), &error);
+ iheader = inflate_generic_header (header, mono_method_get_context (method), error);
mono_metadata_free_mh (header);
if (!iheader) {
- mono_loader_set_error_from_mono_error (&error);
return NULL;
}
- mono_image_lock (img);
-
- if (imethod->header) {
- mono_metadata_free_mh (iheader);
- mono_image_unlock (img);
- return imethod->header;
- }
-
- mono_memory_barrier ();
- imethod->header = iheader;
-
- mono_image_unlock (img);
-
- return imethod->header;
+ return iheader;
}
if (method->wrapper_type != MONO_WRAPPER_NONE || method->sre_method) {
idx = mono_metadata_token_index (method->token);
rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA);
- if (!mono_verifier_verify_method_header (img, rva, NULL))
+ if (!mono_verifier_verify_method_header (img, rva, NULL)) {
+ mono_error_set_bad_image (error, img, "Invalid method header, failed verification");
return NULL;
+ }
loc = mono_image_rva_map (img, rva);
- if (!loc)
+ if (!loc) {
+ mono_error_set_bad_image (error, img, "Method has zero rva");
return NULL;
+ }
/*
* When parsing the types of local variables, we must pass any container available
container = mono_method_get_generic_container (method);
if (!container)
container = method->klass->generic_container;
- header = mono_metadata_parse_mh_full (img, container, (const char *)loc, &error);
- if (!header)
- mono_loader_set_error_from_mono_error (&error);
+ return mono_metadata_parse_mh_full (img, container, (const char *)loc, error);
+}
+MonoMethodHeader*
+mono_method_get_header (MonoMethod *method)
+{
+ MonoError error;
+ MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
+ mono_error_cleanup (&error);
return header;
}
+
guint32
mono_method_get_flags (MonoMethod *method, guint32 *iflags)
{