gboolean
mono_class_has_default_constructor (MonoClass *klass, gboolean public_only);
+gboolean
+mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method);
+
// There are many ways to do on-demand initialization.
// Some allow multiple concurrent initializations. Some do not.
// Some allow multiple concurrent writes to the global. Some do not.
MONO_JIT_ICALL (mono_throw_exception) \
MONO_JIT_ICALL (mono_throw_method_access) \
MONO_JIT_ICALL (mono_throw_bad_image) \
+MONO_JIT_ICALL (mono_throw_not_supported) \
+MONO_JIT_ICALL (mono_throw_invalid_program) \
MONO_JIT_ICALL (mono_trace_enter_method) \
MONO_JIT_ICALL (mono_trace_leave_method) \
MONO_JIT_ICALL (mono_trace_tail_method) \
#ifdef ENABLE_NETCORE
static GENERATE_TRY_GET_CLASS_WITH_CACHE (suppress_gc_transition_attribute, "System.Runtime.InteropServices", "SuppressGCTransitionAttribute")
+static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callers_only_attribute, "System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute")
#endif
static MonoImage*
mb->method->save_lmf = 1;
+ if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) {
+ /* emit a wrapper that throws a NotSupportedException */
+ get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute");
+
+ info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
+ info->d.managed_to_native.method = method;
+
+ csig = mono_metadata_signature_dup_full (get_method_image (method), sig);
+ csig->pinvoke = 0;
+ res = mono_mb_create_and_cache_full (cache, method, mb, csig,
+ csig->param_count + 16, info, NULL);
+ mono_mb_free (mb);
+
+ return res;
+ }
+
+
/*
* In AOT mode and embedding scenarios, it is possible that the icall is not
* registered in the runtime doing the AOT compilation.
}
#endif
+static gboolean
+type_is_blittable (MonoType *type)
+{
+ if (type->byref)
+ return FALSE;
+ type = mono_type_get_underlying_type (type);
+ switch (type->type) {
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
+ return TRUE;
+ default:
+ return m_class_is_blittable (mono_class_from_mono_type_internal (type));
+ }
+}
+
+static gboolean
+method_signature_is_blittable (MonoMethodSignature *sig)
+{
+ if (!type_is_blittable (sig->ret))
+ return FALSE;
+
+ for (int i = 0; i < sig->param_count; ++i) {
+ MonoType *type = sig->params [i];
+ if (!type_is_blittable (type))
+ return FALSE;
+ }
+ return TRUE;
+}
+
/**
* mono_marshal_get_managed_wrapper:
* Generates IL code to call managed methods from unmanaged code
* If \p target_handle is \c 0, the wrapper info will be a \c WrapperInfo structure.
+ *
+ * If \p delegate_klass is \c NULL, we're creating a wrapper for a function pointer to a method marked with
+ * UnamangedCallersOnlyAttribute.
*/
MonoMethod *
mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle target_handle, MonoError *error)
if (!target_handle && (res = mono_marshal_find_in_cache (cache, method)))
return res;
- invoke = mono_get_delegate_invoke_internal (delegate_klass);
- invoke_sig = mono_method_signature_internal (invoke);
-
- mspecs = g_new0 (MonoMarshalSpec*, mono_method_signature_internal (invoke)->param_count + 1);
- mono_method_get_marshal_info (invoke, mspecs);
+ if (G_UNLIKELY (!delegate_klass)) {
+ /* creating a wrapper for a function pointer with UnmanagedCallersOnlyAttribute */
+ if (mono_method_has_marshal_info (method)) {
+ mono_error_set_invalid_program (error, "method %s with UnmanadedCallersOnlyAttribute has marshal specs", mono_method_full_name (method, TRUE));
+ return NULL;
+ }
+ invoke = NULL;
+ invoke_sig = mono_method_signature_internal (method);
+ if (invoke_sig->hasthis) {
+ mono_error_set_invalid_program (error, "method %s with UnamanagedCallersOnlyAttribute is an instance method", mono_method_full_name (method, TRUE));
+ return NULL;
+ }
+ if (method->is_generic || method->is_inflated || mono_class_is_ginst (method->klass)) {
+ mono_error_set_invalid_program (error, "method %s with UnamangedCallersOnlyAttribute is generic", mono_method_full_name (method, TRUE));
+ return NULL;
+ }
+ if (!method_signature_is_blittable (invoke_sig)) {
+ mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has non-blittable parameters or return type", mono_method_full_name (method, TRUE));
+ return NULL;
+ }
+ mspecs = g_new0 (MonoMarshalSpec*, invoke_sig->param_count + 1);
+ } else {
+ invoke = mono_get_delegate_invoke_internal (delegate_klass);
+ invoke_sig = mono_method_signature_internal (invoke);
+ mspecs = g_new0 (MonoMarshalSpec*, invoke_sig->param_count + 1);
+ mono_method_get_marshal_info (invoke, mspecs);
+ }
sig = mono_method_signature_internal (method);
m.csig = csig;
m.image = get_method_image (method);
- mono_marshal_set_callconv_from_modopt (invoke, csig, TRUE);
+ if (invoke)
+ mono_marshal_set_callconv_from_modopt (invoke, csig, TRUE);
/* The attribute is only available in Net 2.0 */
- if (mono_class_try_get_unmanaged_function_pointer_attribute_class ()) {
+ if (delegate_klass && mono_class_try_get_unmanaged_function_pointer_attribute_class ()) {
MonoCustomAttrInfo *cinfo;
MonoCustomAttrEntry *attr;
}
mono_mb_free (mb);
- for (i = mono_method_signature_internal (invoke)->param_count; i >= 0; i--)
+ for (i = invoke_sig->param_count; i >= 0; i--)
if (mspecs [i])
mono_metadata_free_marshal_spec (mspecs [i]);
g_free (mspecs);
}
return &marshal_cb;
}
+
+/**
+ * mono_method_has_unmanaged_callers_only_attribute:
+ *
+ * Returns \c TRUE if \p method has the \c UnmanagedCallersOnlyAttribute
+ */
+gboolean
+mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method)
+{
+#ifndef ENABLE_NETCORE
+ return FALSE;
+#else
+ ERROR_DECL (attr_error);
+ MonoClass *attr_klass = NULL;
+ attr_klass = mono_class_try_get_unmanaged_callers_only_attribute_class ();
+ if (!attr_klass)
+ return FALSE;
+ MonoCustomAttrInfo *cinfo;
+ cinfo = mono_custom_attrs_from_method_checked (method, attr_error);
+ if (!is_ok (attr_error) || !cinfo) {
+ mono_error_cleanup (attr_error);
+ return FALSE;
+ }
+ gboolean result;
+ result = mono_custom_attrs_has_attr (cinfo, attr_klass);
+ if (!cinfo->cached)
+ mono_custom_attrs_free (cinfo);
+ return result;
+#endif
+}
case MONO_WRAPPER_NATIVE_TO_MANAGED: {
g_assert (info);
encode_method_ref (acfg, info->d.native_to_managed.method, p, &p);
- encode_klass_ref (acfg, info->d.native_to_managed.klass, p, &p);
+ MonoClass *klass = info->d.native_to_managed.klass;
+ if (!klass) {
+ encode_value (0, p, &p);
+ } else {
+ encode_value (1, p, &p);
+ encode_klass_ref (acfg, klass, p, &p);
+ }
break;
}
default:
m = decode_resolve_method_ref (module, p, &p, error);
if (!m)
return FALSE;
- klass = decode_klass_ref (module, p, &p, error);
- if (!klass)
- return FALSE;
+ gboolean has_class = decode_value (p, &p);
+ if (has_class) {
+ klass = decode_klass_ref (module, p, &p, error);
+ if (!klass)
+ return FALSE;
+ } else
+ klass = NULL;
ref->method = mono_marshal_get_managed_wrapper (m, klass, 0, error);
if (!is_ok (error))
return FALSE;
#include "mini.h"
/* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION 178
+#define MONO_AOT_FILE_VERSION 179
#define MONO_AOT_TRAMP_PAGE_SIZE 16384
td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
}
+static void
+interp_generate_not_supported_throw (TransformData *td)
+{
+ MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_not_supported;
+
+ interp_add_ins (td, MINT_ICALL_V_V);
+ td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
+}
+
+static void
+interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg)
+{
+ MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_invalid_program;
+
+ char *msg = mono_mempool_strdup (td->rtm->domain->mp, mono_error_get_message (error_msg));
+
+ interp_add_ins (td, MINT_MONO_LDPTR);
+ td->last_ins->data [0] = get_data_item_index (td, msg);
+ PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+
+ interp_add_ins (td, MINT_ICALL_P_V);
+ td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
+
+ td->sp -= 1;
+}
+
/*
* These are additional locals that can be allocated as we transform the code.
* They are allocated past the method locals so they are accessed in the same
if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
m = mono_marshal_get_synchronized_wrapper (m);
+ if (G_UNLIKELY (*td->ip == CEE_LDFTN &&
+ m->wrapper_type == MONO_WRAPPER_NONE &&
+ mono_method_has_unmanaged_callers_only_attribute (m))) {
+
+ if (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
+ interp_generate_not_supported_throw (td);
+ interp_add_ins (td, MINT_LDNULL);
+ td->ip += 5;
+ PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ break;
+ }
+
+ MonoMethod *ctor_method;
+
+ const unsigned char *next_ip = td->ip + 5;
+ /* check for
+ * ldftn method_sig
+ * newobj Delegate::.ctor
+ */
+ if (next_ip < end &&
+ *next_ip == CEE_NEWOBJ &&
+ ((ctor_method = interp_get_method (method, read32 (next_ip + 1), image, generic_context, error))) &&
+ is_ok (error) &&
+ m_class_get_parent (ctor_method->klass) == mono_defaults.multicastdelegate_class &&
+ !strcmp (ctor_method->name, ".ctor")) {
+ mono_error_set_not_supported (error, "Cannot create delegate from method with UnmanagedCallersOnlyAttribute");
+ goto exit;
+ }
+
+ MonoClass *delegate_klass = NULL;
+ MonoGCHandle target_handle = 0;
+ ERROR_DECL (wrapper_error);
+ m = mono_marshal_get_managed_wrapper (m, delegate_klass, target_handle, wrapper_error);
+ if (!is_ok (wrapper_error)) {
+ /* Generate a call that will throw an exception if the
+ * UnmanagedCallersOnly attribute is used incorrectly */
+ interp_generate_ipe_throw_with_msg (td, wrapper_error);
+ mono_error_cleanup (wrapper_error);
+ interp_add_ins (td, MINT_LDNULL);
+ } else {
+ /* push a pointer to a trampoline that calls m */
+ gpointer entry = mini_get_interp_callbacks ()->create_method_pointer (m, TRUE, error);
+#if SIZEOF_VOID_P == 8
+ interp_add_ins (td, MINT_LDC_I8);
+ WRITE64_INS (td->last_ins, 0, &entry);
+#else
+ interp_add_ins (td, MINT_LDC_I4);
+ WRITE32_INS (td->last_ins, 0, &entry);
+#endif
+ }
+ td->ip += 5;
+ PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ break;
+ }
+
interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
goto_if_nok (error, exit);
return addr;
}
- addr = mono_create_jump_trampoline (mono_domain_get (), method, FALSE, error);
+ /* if we need the address of a native-to-managed wrapper, just compile it now, trampoline needs thread local
+ * variables that won't be there if we run on a thread that's not attached yet. */
+ if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
+ addr = mono_compile_method_checked (method, error);
+ else
+ addr = mono_create_jump_trampoline (mono_domain_get (), method, FALSE, error);
if (!is_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
void
+mono_throw_not_supported ()
+{
+ ERROR_DECL (error);
+ mono_error_set_generic_error (error, "System", "NotSupportedException", "");
+ mono_error_set_pending_exception (error);
+}
+
+void
+mono_throw_invalid_program (const char *msg)
+{
+ ERROR_DECL (error);
+ mono_error_set_invalid_program (error, "Invalid IL due to: %s", msg);
+ mono_error_set_pending_exception (error);
+}
+
+void
mono_dummy_jit_icall (void)
{
}
ICALL_EXTERN_C void mono_throw_bad_image (void);
+ICALL_EXTERN_C void mono_throw_not_supported (void);
+
+ICALL_EXTERN_C void mono_throw_invalid_program (const char *msg);
+
ICALL_EXTERN_C void mono_dummy_jit_icall (void);
#endif /* __MONO_JIT_ICALLS_H__ */
mono_emit_jit_icall (cfg, mono_throw_bad_image, NULL);
}
+static void
+emit_not_supported_failure (MonoCompile *cfg)
+{
+ mono_emit_jit_icall (cfg, mono_throw_not_supported, NULL);
+}
+
+static void
+emit_invalid_program_with_msg (MonoCompile *cfg, MonoError *error_msg, MonoMethod *caller, MonoMethod *callee)
+{
+ g_assert (!is_ok (error_msg));
+ char *str = mono_mempool_strdup (cfg->domain->mp, mono_error_get_message (error_msg));
+ MonoInst *iargs[1];
+ if (cfg->compile_aot)
+ EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
+ else
+ EMIT_NEW_PCONST (cfg, iargs [0], str);
+ mono_emit_jit_icall (cfg, mono_throw_invalid_program, iargs);
+}
+
// FIXME Consolidate the multiple functions named get_method_nofail.
static MonoMethod*
get_method_nofail (MonoClass *klass, const char *method_name, int num_params, int flags)
if (mono_security_core_clr_enabled ())
ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
+ const gboolean has_unmanaged_callers_only =
+ cmethod->wrapper_type == MONO_WRAPPER_NONE &&
+ mono_method_has_unmanaged_callers_only_attribute (cmethod);
+
/*
* Optimize the common case of ldftn+delegate creation
*/
MonoMethod *invoke;
int invoke_context_used;
+ if (G_UNLIKELY (has_unmanaged_callers_only)) {
+ mono_error_set_not_supported (cfg->error, "Cannot create delegate from method with UnmanagedCallersOnlyAttribute");
+ CHECK_CFG_ERROR;
+ }
+
invoke = mono_get_delegate_invoke_internal (ctor_method->klass);
if (!invoke || !mono_method_signature_internal (invoke))
LOAD_ERROR;
}
}
+ /* UnmanagedCallersOnlyAttribute means ldftn should return a method callable from native */
+ if (G_UNLIKELY (has_unmanaged_callers_only)) {
+ if (G_UNLIKELY (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+ // Follow CoreCLR, disallow [UnmanagedCallersOnly] and [DllImport] to be used
+ // together
+ emit_not_supported_failure (cfg);
+ EMIT_NEW_PCONST (cfg, ins, NULL);
+ *sp++ = ins;
+ inline_costs += CALL_COST * MIN(10, num_calls++);
+ break;
+ }
+ MonoClass *delegate_klass = NULL;
+ MonoGCHandle target_handle = 0;
+ ERROR_DECL (wrapper_error);
+ MonoMethod *wrapped_cmethod;
+ wrapped_cmethod = mono_marshal_get_managed_wrapper (cmethod, delegate_klass, target_handle, wrapper_error);
+ if (!is_ok (wrapper_error)) {
+ /* if we couldn't create a wrapper because cmethod isn't supposed to have an
+ UnmanagedCallersOnly attribute, follow CoreCLR behavior and throw when the
+ method with the ldftn is executing, not when it is being compiled. */
+ emit_invalid_program_with_msg (cfg, wrapper_error, method, cmethod);
+ mono_error_cleanup (wrapper_error);
+ EMIT_NEW_PCONST (cfg, ins, NULL);
+ *sp++ = ins;
+
+ inline_costs += CALL_COST * MIN(10, num_calls++);
+ break;
+ } else {
+ cmethod = wrapped_cmethod;
+ }
+ }
+
argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
*sp++ = ins;
register_icall (mono_get_method_object, mono_icall_sig_object_ptr, TRUE);
register_icall (mono_throw_method_access, mono_icall_sig_void_ptr_ptr, FALSE);
register_icall (mono_throw_bad_image, mono_icall_sig_void, FALSE);
+ register_icall (mono_throw_not_supported, mono_icall_sig_void, FALSE);
+ register_icall (mono_throw_invalid_program, mono_icall_sig_void_ptr, FALSE);
register_icall_no_wrapper (mono_dummy_jit_icall, mono_icall_sig_void);
register_icall_with_wrapper (mono_monitor_enter_internal, mono_icall_sig_int32_obj);