From e6009c9a798cca5c940f080961f54166bc7ea3a9 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:10:10 -0400 Subject: [PATCH 01/27] [marshal] Add mono_method_has_unmanaged_callers_only_attribute --- src/mono/mono/metadata/class-internals.h | 3 +++ src/mono/mono/metadata/marshal.c | 32 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 05963326b1b5f..e0deb0b738655 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -1516,6 +1516,9 @@ mono_method_is_constructor (MonoMethod *method); 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. diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index bec2c3095b4f8..976c9104acbeb 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -113,6 +113,7 @@ static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, #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.InteropServicea", "UnmanagedCallersOnlyAttribute") #endif static MonoImage* @@ -6755,3 +6756,34 @@ get_marshal_cb (void) } 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) +{ + /* TODO: if this is slow and called repeatedly for the same method, add a bit to MonoMethod that is true if the + * method doesn't have the UnmanagedCallersOnlyAttribute, and false if we haven't checked yet, or if the + * attribute is present. */ + ERROR_DECL (attr_error); + MonoClass *attr_klass = NULL; +#ifdef ENABLE_NETCORE + attr_klass = = mono_class_try_get_unmanaged_callers_only_attribute_class (); +#endif + 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; +} From 4156eff15349aaa3709ed171112011bc9ccc24aa Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:10:47 -0400 Subject: [PATCH 02/27] [marshal] Allow calls to mono_marshal_get_managed_wrapped without a delegate class In that case, create a wrapper based on the signature of the method itself. TODO: - check that the method is static - check that the method does not have any marshal info (currently it's assumed to not be present and ignored) --- src/mono/mono/metadata/marshal.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 976c9104acbeb..13e511a7656c9 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3945,6 +3945,9 @@ emit_managed_wrapper_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke * 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) @@ -3976,11 +3979,21 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, 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); + 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); @@ -4009,7 +4022,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, 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; From a8d4d57231c8f17e52a0eb757629b6b3aee01d1b Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:13:09 -0400 Subject: [PATCH 03/27] [aot] Allow decode_method_ref to decode NATIVE_TO_MANAGED wrappers without a delegate class Bump the AOT file format --- src/mono/mono/mini/aot-runtime.c | 2 ++ src/mono/mono/mini/aot-runtime.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 209279d894d69..ed750b11bcc95 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -1313,8 +1313,10 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod if (!m) return FALSE; klass = decode_klass_ref (module, p, &p, error); +#if 0 if (!klass) return FALSE; +#endif ref->method = mono_marshal_get_managed_wrapper (m, klass, 0, error); if (!is_ok (error)) return FALSE; diff --git a/src/mono/mono/mini/aot-runtime.h b/src/mono/mono/mini/aot-runtime.h index c4171fab12a3a..79b9b6d8356b6 100644 --- a/src/mono/mono/mini/aot-runtime.h +++ b/src/mono/mono/mini/aot-runtime.h @@ -11,7 +11,7 @@ #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 From 40662c4fee5abf57b97613c76189c514d80d68b3 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:14:16 -0400 Subject: [PATCH 04/27] [mini] return ptr to wrapper in mono_ldftn for UnmanagedCallersOnly methods --- src/mono/mono/mini/jit-icalls.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 0cdc7a544c26f..a756f52ad48b9 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -39,6 +39,18 @@ mono_ldftn (MonoMethod *method) gpointer addr; ERROR_DECL (error); + if (G_UNLIKELY ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && + method->wrapper_type == MONO_WRAPPER_NONE && + mono_method_has_unmanaged_callers_only_attribute (method))) { + MonoClass *delegate_klass = NULL; + MonoGCHandle target_handle = 0; + method = mono_marshal_get_managed_wrapper (method, delegate_klass, target_handle, error); + if (!is_ok (error)) { + mono_error_set_pending_exception (error); + return NULL; + } + } + if (mono_llvm_only) { // FIXME: No error handling From 92ef04cc966caed055f556ec2a4da3ea03a83a6d Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:34:15 -0400 Subject: [PATCH 05/27] [interp] ldftn will return a native-to-managed wrapper to UnmanagedCallersOnly methods --- src/mono/mono/mini/interp/transform.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 126e0705ff4fa..58f311a5ab4fc 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -6208,6 +6208,16 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, 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 && + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && + method->wrapper_type == MONO_WRAPPER_NONE && + mono_method_has_unmanaged_callers_only_attribute (method))) { + MonoClass *delegate_klass = NULL; + MonoGCHandle target_handle = 0; + m = mono_marshal_get_managed_wrapper (m, delegate_klass, target_handle, error); + goto_if_nok (error, exit); + } + 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); From 36ebfaa6bd28aa88b5fa453c1f34821b0831a68a Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 17:51:25 -0400 Subject: [PATCH 06/27] [tests] Temporarily set UnmanagedCallersOnly tests to Pri0 --- .../UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj index a8c1862cc6f6c..6bffefc981a93 100644 --- a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj +++ b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj @@ -1,7 +1,7 @@ Exe - 1 + 0 From 376db974196c7e33303ee46e49662ff0b246713a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 2 Jul 2020 19:17:15 -0400 Subject: [PATCH 07/27] fixup build --- src/mono/mono/metadata/marshal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 13e511a7656c9..1608fe98ac918 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -6784,7 +6784,7 @@ mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method) ERROR_DECL (attr_error); MonoClass *attr_klass = NULL; #ifdef ENABLE_NETCORE - attr_klass = = mono_class_try_get_unmanaged_callers_only_attribute_class (); + attr_klass = mono_class_try_get_unmanaged_callers_only_attribute_class (); #endif if (!attr_klass) return FALSE; From ff121956127749a6c673c13c712dc2d000425608 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 21:25:49 -0400 Subject: [PATCH 08/27] fixup null --- src/mono/mono/metadata/marshal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 1608fe98ac918..51eab268f6dda 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -4111,7 +4111,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, } 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); From 04b6da5caf302f0e819e32b8793cc58f77fb60c8 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 2 Jul 2020 21:32:07 -0400 Subject: [PATCH 09/27] aot: emit byte when we don't expect a class --- src/mono/mono/mini/aot-compiler.c | 8 +++++++- src/mono/mono/mini/aot-runtime.c | 12 +++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index adb9cab48538c..7c76e4ce154b1 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -3761,7 +3761,13 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 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: diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index ed750b11bcc95..b42c76efba75b 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -1312,11 +1312,13 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod m = decode_resolve_method_ref (module, p, &p, error); if (!m) return FALSE; - klass = decode_klass_ref (module, p, &p, error); -#if 0 - if (!klass) - return FALSE; -#endif + 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; From 3ef43631ebc7376b27ea20c3bbc0c68d5b81573b Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 12:22:46 -0400 Subject: [PATCH 10/27] jit: move wrapper creation to method-to-ir, not mono_ldftn Do it at IR generation of the caller, not every time the ldftn is executed --- src/mono/mono/mini/jit-icalls.c | 12 ------------ src/mono/mono/mini/method-to-ir.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index a756f52ad48b9..0cdc7a544c26f 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -39,18 +39,6 @@ mono_ldftn (MonoMethod *method) gpointer addr; ERROR_DECL (error); - if (G_UNLIKELY ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && - method->wrapper_type == MONO_WRAPPER_NONE && - mono_method_has_unmanaged_callers_only_attribute (method))) { - MonoClass *delegate_klass = NULL; - MonoGCHandle target_handle = 0; - method = mono_marshal_get_managed_wrapper (method, delegate_klass, target_handle, error); - if (!is_ok (error)) { - mono_error_set_pending_exception (error); - return NULL; - } - } - if (mono_llvm_only) { // FIXME: No error handling diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 032d5195b4773..a5fbba1abf577 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -11106,6 +11106,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } + /* UnmanagedCallersOnlyAttribute means ldftn should return a method callable from native */ + if (G_UNLIKELY ((cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && + cmethod->wrapper_type == MONO_WRAPPER_NONE && + mono_method_has_unmanaged_callers_only_attribute (cmethod))) { + MonoClass *delegate_klass = NULL; + MonoGCHandle target_handle = 0; + cmethod = mono_marshal_get_managed_wrapper (cmethod, delegate_klass, target_handle, cfg->error); + CHECK_CFG_ERROR; + } + + argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD); ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst); *sp++ = ins; From 1f6d260d027974b40573c3869afec04b981d6a68 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 16:04:22 -0400 Subject: [PATCH 11/27] fix typo --- src/mono/mono/metadata/marshal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 51eab268f6dda..e826fef180bef 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -113,7 +113,7 @@ static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, #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.InteropServicea", "UnmanagedCallersOnlyAttribute") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callers_only_attribute, "System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute") #endif static MonoImage* From 2579230c508cb514665bf221b9587737228f6387 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 16:04:32 -0400 Subject: [PATCH 12/27] don't get callconv from Invoke if there's no invoke --- src/mono/mono/metadata/marshal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index e826fef180bef..2864c78da7b3a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -4019,7 +4019,8 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, 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 (delegate_klass && mono_class_try_get_unmanaged_function_pointer_attribute_class ()) { From 00ed38d335decd5ee790481c9ccaf8500d085186 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 17:45:30 -0400 Subject: [PATCH 13/27] jit: don't create a jump trampoline for ldftn of a native-to-managed wrapper The wrapper might be called from a thread that's not attached to the runtime, and the jump trampoline will look at TLS vars that are not initialized --- src/mono/mono/mini/jit-icalls.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 0cdc7a544c26f..6ad4801ceca80 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -54,7 +54,12 @@ mono_ldftn (MonoMethod *method) return addr; } - addr = mono_create_jump_trampoline (mono_domain_get (), method, FALSE, error); + /* if we're 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; From b100d40e9d4c2c218fad9c83d441002e3b82a935 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 17:46:32 -0400 Subject: [PATCH 14/27] interp: transform LDFTN into LDC of a create_method_pointer for UnmanagedCallersOnly method --- src/mono/mono/mini/interp/transform.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 58f311a5ab4fc..19f2aba5b380b 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -6209,13 +6209,25 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, m = mono_marshal_get_synchronized_wrapper (m); if (G_UNLIKELY (*td->ip == CEE_LDFTN && - (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && - method->wrapper_type == MONO_WRAPPER_NONE && - mono_method_has_unmanaged_callers_only_attribute (method))) { + (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && + m->wrapper_type == MONO_WRAPPER_NONE && + mono_method_has_unmanaged_callers_only_attribute (m))) { MonoClass *delegate_klass = NULL; MonoGCHandle target_handle = 0; m = mono_marshal_get_managed_wrapper (m, delegate_klass, target_handle, error); goto_if_nok (error, exit); + /* 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_I8); + break; } interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN); From 7b00b071a65b8e8e89080d3c7eaa304c6ea3bfed Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 17:51:25 -0400 Subject: [PATCH 15/27] remove obsolete comment --- src/mono/mono/metadata/marshal.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 2864c78da7b3a..2b07707772458 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -6779,9 +6779,6 @@ get_marshal_cb (void) gboolean mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method) { - /* TODO: if this is slow and called repeatedly for the same method, add a bit to MonoMethod that is true if the - * method doesn't have the UnmanagedCallersOnlyAttribute, and false if we haven't checked yet, or if the - * attribute is present. */ ERROR_DECL (attr_error); MonoClass *attr_klass = NULL; #ifdef ENABLE_NETCORE From 6340c36fb2f0c6e609052817a5df9bde162dd7ad Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 6 Jul 2020 21:52:41 -0400 Subject: [PATCH 16/27] marshal: throw invalid program exception for instance and generic methods --- src/mono/mono/metadata/marshal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 2b07707772458..e0c257179d783 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3987,6 +3987,14 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, } 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; + } mspecs = g_new0 (MonoMarshalSpec*, invoke_sig->param_count + 1); } else { invoke = mono_get_delegate_invoke_internal (delegate_klass); From a7199ad382c26caaefd72b26601eae0d404a645e Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 7 Jul 2020 14:53:07 -0400 Subject: [PATCH 17/27] Emit IPE throw instead of aborting JIT or interp compilation for bad UnmanagedCallersOnly methods Instead of throwing while JITing (or transforming), throw when the LDFTN is executed. --- src/mono/mono/metadata/jit-icall-reg.h | 1 + src/mono/mono/mini/interp/transform.c | 44 ++++++++++++++++++++------ src/mono/mono/mini/jit-icalls.c | 8 +++++ src/mono/mono/mini/jit-icalls.h | 2 ++ src/mono/mono/mini/method-to-ir.c | 34 ++++++++++++++++++-- src/mono/mono/mini/mini-runtime.c | 1 + 6 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index 5b07e4c9e1536..966ec484bdd67 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -308,6 +308,7 @@ MONO_JIT_ICALL (mono_threads_state_poll) \ 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_invalid_program) \ MONO_JIT_ICALL (mono_trace_enter_method) \ MONO_JIT_ICALL (mono_trace_leave_method) \ MONO_JIT_ICALL (mono_trace_tail_method) \ diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 19f2aba5b380b..1cd92bacfe9ec 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -841,6 +841,24 @@ interp_generate_bie_throw (TransformData *td) 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; + + /* FIXME: is this the right mempool? */ + char *msg = mono_mempool_strdup (m_class_get_image (td->method->klass)->mempool, 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 @@ -6214,19 +6232,27 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, mono_method_has_unmanaged_callers_only_attribute (m))) { MonoClass *delegate_klass = NULL; MonoGCHandle target_handle = 0; - m = mono_marshal_get_managed_wrapper (m, delegate_klass, target_handle, error); - goto_if_nok (error, exit); - /* push a pointer to a trampoline that calls m */ - gpointer entry = mini_get_interp_callbacks ()->create_method_pointer (m, TRUE, error); + 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); + 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); + interp_add_ins (td, MINT_LDC_I4); + WRITE32_INS (td->last_ins, 0, &entry); #endif + } td->ip += 5; - PUSH_SIMPLE_TYPE (td, STACK_TYPE_I8); + PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP); break; } diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 6ad4801ceca80..bf791bb1a63f4 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -1595,6 +1595,14 @@ mono_throw_bad_image () 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) { diff --git a/src/mono/mono/mini/jit-icalls.h b/src/mono/mono/mini/jit-icalls.h index 41ebae89ceee5..22611b448886d 100644 --- a/src/mono/mono/mini/jit-icalls.h +++ b/src/mono/mono/mini/jit-icalls.h @@ -223,6 +223,8 @@ ICALL_EXTERN_C void mono_throw_method_access (MonoMethod *caller, MonoMethod *ca ICALL_EXTERN_C void mono_throw_bad_image (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__ */ diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index a5fbba1abf577..929763ec6bcdf 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2307,6 +2307,20 @@ emit_bad_image_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee mono_emit_jit_icall (cfg, mono_throw_bad_image, NULL); } +static void +emit_invalid_program_with_msg (MonoCompile *cfg, MonoError *error_msg, MonoMethod *caller, MonoMethod *callee) +{ + g_assert (!is_ok (error_msg)); + /* FIXME: is this the right mempool? */ + char *str = mono_mempool_strdup (m_class_get_image (cfg->method->klass)->mempool, 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) @@ -11112,10 +11126,24 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_method_has_unmanaged_callers_only_attribute (cmethod))) { MonoClass *delegate_klass = NULL; MonoGCHandle target_handle = 0; - cmethod = mono_marshal_get_managed_wrapper (cmethod, delegate_klass, target_handle, cfg->error); - CHECK_CFG_ERROR; - } + 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); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 37ef936aa13d1..1987ea5916089 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4887,6 +4887,7 @@ register_icalls (void) 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_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); From d6b9c561e5a1389a00779941c8aca0ed70656d86 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 7 Jul 2020 15:26:56 -0400 Subject: [PATCH 18/27] Use domain mempool for IPE error message --- src/mono/mono/mini/interp/transform.c | 3 +-- src/mono/mono/mini/method-to-ir.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 1cd92bacfe9ec..5a213790e24e7 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -846,8 +846,7 @@ interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg) { MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_invalid_program; - /* FIXME: is this the right mempool? */ - char *msg = mono_mempool_strdup (m_class_get_image (td->method->klass)->mempool, mono_error_get_message (error_msg)); + 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); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 929763ec6bcdf..fbc7166961d00 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2311,8 +2311,7 @@ static void emit_invalid_program_with_msg (MonoCompile *cfg, MonoError *error_msg, MonoMethod *caller, MonoMethod *callee) { g_assert (!is_ok (error_msg)); - /* FIXME: is this the right mempool? */ - char *str = mono_mempool_strdup (m_class_get_image (cfg->method->klass)->mempool, mono_error_get_message (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); From 1b5422fefe278c6a2cd5809d253353df25d6c959 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 7 Jul 2020 17:17:58 -0400 Subject: [PATCH 19/27] disallow delegate constructor calls on UnmanagedCallersOnly methods --- src/mono/mono/mini/interp/transform.c | 18 ++++++++++++++++++ src/mono/mono/mini/method-to-ir.c | 13 ++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 5a213790e24e7..f43bcc0833af4 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -6229,6 +6229,24 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && m->wrapper_type == MONO_WRAPPER_NONE && mono_method_has_unmanaged_callers_only_attribute (m))) { + + 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); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index fbc7166961d00..3f3f201b7565c 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -11072,6 +11072,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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 */ @@ -11082,6 +11086,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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; @@ -11120,9 +11129,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } /* UnmanagedCallersOnlyAttribute means ldftn should return a method callable from native */ - if (G_UNLIKELY ((cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && - cmethod->wrapper_type == MONO_WRAPPER_NONE && - mono_method_has_unmanaged_callers_only_attribute (cmethod))) { + if (G_UNLIKELY (has_unmanaged_callers_only)) { MonoClass *delegate_klass = NULL; MonoGCHandle target_handle = 0; ERROR_DECL (wrapper_error); From 6e857429d64da06a87cd67d4057846036211a931 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 09:29:25 -0400 Subject: [PATCH 20/27] throw IPE if UnmanagedCallersOnly method has non-blittable args --- src/mono/mono/metadata/marshal.c | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index e0c257179d783..fa0ba79f7cd02 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3941,6 +3941,48 @@ emit_managed_wrapper_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke } #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_CHAR: + 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 @@ -3995,6 +4037,10 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_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", 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); From 9d502c5a029ad9339a93155ddbe36e88e0b49a10 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 14:48:53 -0400 Subject: [PATCH 21/27] disallow DllImport and UnmanagedCallersOnly together throw NotSupportedException --- src/mono/mono/metadata/jit-icall-reg.h | 1 + src/mono/mono/metadata/marshal.c | 19 +++++++++++++++++++ src/mono/mono/mini/interp/transform.c | 18 +++++++++++++++++- src/mono/mono/mini/jit-icalls.c | 8 ++++++++ src/mono/mono/mini/jit-icalls.h | 2 ++ src/mono/mono/mini/method-to-ir.c | 15 +++++++++++++++ src/mono/mono/mini/mini-runtime.c | 1 + 7 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index 966ec484bdd67..3abbdf45da4d4 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -308,6 +308,7 @@ MONO_JIT_ICALL (mono_threads_state_poll) \ 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) \ diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index fa0ba79f7cd02..bd94a07732e99 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3662,6 +3662,25 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, mb->method->save_lmf = 1; + gboolean unmanaged_callers_only = FALSE; + + 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. diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index f43bcc0833af4..3665021b1aaa1 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -841,6 +841,15 @@ interp_generate_bie_throw (TransformData *td) 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) { @@ -6226,10 +6235,17 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, m = mono_marshal_get_synchronized_wrapper (m); if (G_UNLIKELY (*td->ip == CEE_LDFTN && - (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && 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; diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index bf791bb1a63f4..3edf7a8c2f822 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -1595,6 +1595,14 @@ mono_throw_bad_image () mono_error_set_pending_exception (error); } +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) { diff --git a/src/mono/mono/mini/jit-icalls.h b/src/mono/mono/mini/jit-icalls.h index 22611b448886d..7de1133c0e471 100644 --- a/src/mono/mono/mini/jit-icalls.h +++ b/src/mono/mono/mini/jit-icalls.h @@ -223,6 +223,8 @@ ICALL_EXTERN_C void mono_throw_method_access (MonoMethod *caller, MonoMethod *ca 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); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 3f3f201b7565c..efcecb831ab9f 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2307,6 +2307,12 @@ emit_bad_image_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee 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) { @@ -11130,6 +11136,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* 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); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 1987ea5916089..ce148259d4f91 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -4887,6 +4887,7 @@ register_icalls (void) 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); From 70075a93833387ecbfb9036844868d09ec43f178 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 16:09:01 -0400 Subject: [PATCH 22/27] Change UnamangedCallersOnlyTest.csproj back to Pri1 --- .../UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj index 6bffefc981a93..a8c1862cc6f6c 100644 --- a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj +++ b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.csproj @@ -1,7 +1,7 @@ Exe - 0 + 1 From 025dd73b99787482a2cfc5649a62154e6b82a957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 8 Jul 2020 16:38:21 -0400 Subject: [PATCH 23/27] Apply suggestions from code review Co-authored-by: Ryan Lucia --- src/mono/mono/metadata/marshal.c | 2 +- src/mono/mono/mini/jit-icalls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index bd94a07732e99..4909ae47f5f74 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3664,7 +3664,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, gboolean unmanaged_callers_only = FALSE; - if (G_UNLIKELY (pinvoke && (mono_method_has_unmanaged_callers_only_attribute (method)))) { + 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"); diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index 3edf7a8c2f822..d4116aafa8a9a 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -54,7 +54,7 @@ mono_ldftn (MonoMethod *method) return addr; } - /* if we're need the address of a native-to-managed wrapper, just compile it now, trampoline needs thread local + /* 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); From 0e6f06766588ff0c3aa7d1db7b574ac998d8e1e1 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 16:38:50 -0400 Subject: [PATCH 24/27] System.Char isn't blittable --- src/mono/mono/metadata/marshal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 4909ae47f5f74..00e36a846cebe 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3979,7 +3979,6 @@ type_is_blittable (MonoType *type) case MONO_TYPE_R8: case MONO_TYPE_I: case MONO_TYPE_U: - case MONO_TYPE_CHAR: case MONO_TYPE_PTR: case MONO_TYPE_FNPTR: return TRUE; From a2f0d94a044fe1a8c6534ee900fe10f982c03aaa Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 16:39:21 -0400 Subject: [PATCH 25/27] fixup non-blittable exception message --- src/mono/mono/metadata/marshal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 00e36a846cebe..142eb9311f3df 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -4056,7 +4056,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, return NULL; } if (!method_signature_is_blittable (invoke_sig)) { - mono_error_set_invalid_program (error, "method %s with UnmanagedCallersOnlyAttribute has non-blittable parameters", mono_method_full_name (method, TRUE)); + 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); From 89c8c28ce856f6607adca27a95d59a522f28cb83 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 16:40:45 -0400 Subject: [PATCH 26/27] non-netcore doesn't have UnmanagedCallersOnlyAttribute --- src/mono/mono/metadata/marshal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 142eb9311f3df..c93cb6c139c1b 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -6851,11 +6851,12 @@ get_marshal_cb (void) gboolean mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method) { +#ifndef ENABLE_NETCORE + return FALSE; +#else ERROR_DECL (attr_error); MonoClass *attr_klass = NULL; -#ifdef ENABLE_NETCORE attr_klass = mono_class_try_get_unmanaged_callers_only_attribute_class (); -#endif if (!attr_klass) return FALSE; MonoCustomAttrInfo *cinfo; @@ -6869,4 +6870,5 @@ mono_method_has_unmanaged_callers_only_attribute (MonoMethod *method) if (!cinfo->cached) mono_custom_attrs_free (cinfo); return result; +#endif } From ee50f3e9dc4dbc09cfd27ae0e58403aa79d29feb Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 8 Jul 2020 16:42:42 -0400 Subject: [PATCH 27/27] remove dead code --- src/mono/mono/metadata/marshal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index c93cb6c139c1b..fe74f17444e9c 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3662,8 +3662,6 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, mb->method->save_lmf = 1; - gboolean unmanaged_callers_only = FALSE; - 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");