diff --git a/src/mono/mono/metadata/jit-icall-reg.h b/src/mono/mono/metadata/jit-icall-reg.h index a3aac59f4d458..984c26721eec5 100644 --- a/src/mono/mono/metadata/jit-icall-reg.h +++ b/src/mono/mono/metadata/jit-icall-reg.h @@ -339,6 +339,7 @@ MONO_JIT_ICALL (ves_icall_string_new_wrapper) \ MONO_JIT_ICALL (ves_icall_thread_finish_async_abort) \ MONO_JIT_ICALL (mono_marshal_lookup_pinvoke) \ MONO_JIT_ICALL (mono_gsharedvt_constrained_call_fast) \ +MONO_JIT_ICALL (mono_dummy_runtime_init_callback) \ \ MONO_JIT_ICALL (count) \ diff --git a/src/mono/mono/metadata/marshal-lightweight.c b/src/mono/mono/metadata/marshal-lightweight.c index 9d43d0b124d52..54e37d2036d99 100644 --- a/src/mono/mono/metadata/marshal-lightweight.c +++ b/src/mono/mono/metadata/marshal-lightweight.c @@ -2491,7 +2491,7 @@ emit_managed_wrapper_validate_signature (MonoMethodSignature* sig, MonoMarshalSp } static void -emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, MonoError *error) +emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, gboolean runtime_init_callback, MonoError *error) { MonoMethodSignature *sig, *csig; int i, *tmp_locals; @@ -2550,9 +2550,18 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s * return ret; */ + /* delete_old = FALSE */ mono_mb_emit_icon (mb, 0); mono_mb_emit_stloc (mb, 2); + /* + * Transformed into a direct icall when runtime init callback is enabled for a native-to-managed wrapper. + * This icall is special cased in the JIT so it can be called in native-to-managed wrapper before + * runtime has been initialized. On return, runtime must be fully initialized. + */ + if (runtime_init_callback) + mono_mb_emit_icall (mb, mono_dummy_runtime_init_callback); + gc_unsafe_transition_builder_emit_enter(&gc_unsafe_builder); /* we first do all conversions */ diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 5ab899e032d0e..e7aff60aee5e2 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3910,7 +3910,7 @@ mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMeth /* * mono_marshal_emit_managed_wrapper: * - * Emit the body of a native-to-managed wrapper. INVOKE_SIG is the signature of + * Emit the body of a native-to-managed wrapper. INVOKE_SIG is the signature of * the delegate which wraps the managed method to be called. For closed delegates, * it could have fewer parameters than the method it wraps. * THIS_LOC is the memory location where the target of the delegate is stored. @@ -3918,7 +3918,7 @@ mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMeth void mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, MonoError *error) { - get_marshal_cb ()->emit_managed_wrapper (mb, invoke_sig, mspecs, m, method, target_handle, error); + get_marshal_cb ()->emit_managed_wrapper (mb, invoke_sig, mspecs, m, method, target_handle, FALSE, error); } static gboolean @@ -4017,16 +4017,8 @@ method_signature_is_usable_when_marshalling_disabled (MonoMethodSignature *sig) return check_all_types_in_method_signature (sig, &type_is_usable_when_marshalling_disabled); } -/** - * 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) +static MonoMethod * +marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle target_handle, gboolean runtime_init_callback, MonoError *error) { MonoMethodSignature *sig, *csig, *invoke_sig; MonoMethodBuilder *mb; @@ -4192,7 +4184,7 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, mono_custom_attrs_free (cinfo); } - mono_marshal_emit_managed_wrapper (mb, invoke_sig, mspecs, &m, method, target_handle, error); + get_marshal_cb ()->emit_managed_wrapper (mb, invoke_sig, mspecs, &m, method, target_handle, runtime_init_callback, error); res = NULL; if (is_ok (error)) { @@ -4225,6 +4217,34 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, return res; } +/** + * 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) +{ + return marshal_get_managed_wrapper (method, delegate_klass, target_handle, FALSE, error); +} + +/** + * mono_marshal_get_runtime_init_managed_wrapper: + * Generates IL code to call managed methods from unmanaged code with lazy runtime init support + * 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_runtime_init_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle target_handle, MonoError *error) +{ + return marshal_get_managed_wrapper (method, delegate_klass, target_handle, TRUE, error); +} + gpointer mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) { diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index 89b306d7def0c..6cd33778301f9 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -323,7 +323,7 @@ typedef struct { void (*emit_stelemref) (MonoMethodBuilder *mb); void (*emit_array_address) (MonoMethodBuilder *mb, int rank, int elem_size); void (*emit_native_wrapper) (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags); - void (*emit_managed_wrapper) (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, MonoError *error); + void (*emit_managed_wrapper) (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, gboolean runtime_init_callback, MonoError *error); void (*emit_runtime_invoke_body) (MonoMethodBuilder *mb, const char **param_names, MonoImage *image, MonoMethod *method, MonoMethodSignature *sig, MonoMethodSignature *callsig, gboolean virtual_, gboolean need_direct_wrapper); void (*emit_runtime_invoke_dynamic) (MonoMethodBuilder *mb); void (*emit_delegate_begin_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig); @@ -506,6 +506,9 @@ mono_marshal_get_string_ctor_signature (MonoMethod *method); MonoMethod * mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle this_loc, MonoError *exernal_error); +MonoMethod * +mono_marshal_get_runtime_init_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle this_loc, MonoError *exernal_error); + gpointer mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type); diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index fb7ed565a7c37..4481c694d36c8 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -255,6 +255,7 @@ typedef struct MonoAotOptions { gboolean no_opt; char *clangxx; char *depfile; + char *runtime_init_callback; } MonoAotOptions; typedef enum { @@ -5303,7 +5304,11 @@ MONO_RESTORE_WARNING } mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); - wrapper = mono_marshal_get_managed_wrapper (method, NULL, 0, error); + if (acfg->aot_opts.runtime_init_callback != NULL && export_name != NULL) + wrapper = mono_marshal_get_runtime_init_managed_wrapper (method, NULL, 0, error); + else + wrapper = mono_marshal_get_managed_wrapper (method, NULL, 0, error); + if (!is_ok (error)) { report_loader_error (acfg, error, FALSE, "Unable to generate native entry point '%s' due to '%s'.", mono_method_get_full_name (method), mono_error_get_message (error)); continue; @@ -6625,13 +6630,24 @@ emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, gui } } else if (patch_info->type == MONO_PATCH_INFO_JIT_ICALL_ID) { MonoJitICallInfo * const info = mono_find_jit_icall_info (patch_info->data.jit_icall_id); - const char * const sym = info->c_symbol; - if (!got_only && sym && acfg->aot_opts.direct_icalls && info->func == info->wrapper) { - /* Call to a jit icall without a wrapper */ - direct_call = TRUE; - external_call = TRUE; - g_assert (strlen (sym) < 1000); - direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym); + if (!got_only && info->func == info->wrapper) { + const char * sym = NULL; + if (patch_info->data.jit_icall_id == MONO_JIT_ICALL_mono_dummy_runtime_init_callback) { + g_assert (acfg->aot_opts.static_link && acfg->aot_opts.runtime_init_callback != NULL); + sym = acfg->aot_opts.runtime_init_callback; + direct_call = TRUE; + external_call = TRUE; + } else if (info->c_symbol && acfg->aot_opts.direct_icalls) { + sym = info->c_symbol; + direct_call = TRUE; + external_call = TRUE; + } + + if (sym) { + /* Call to a jit icall without a wrapper */ + g_assert (strlen (sym) < 1000); + direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym); + } } } if (direct_call) { @@ -8749,6 +8765,14 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) } } else if (str_begins_with (arg, "depfile=")) { opts->depfile = g_strdup (arg + strlen ("depfile=")); + } else if (str_begins_with (arg, "runtime-init-callback=")) { + opts->runtime_init_callback = g_strdup (arg + strlen ("runtime-init-callback=")); + if (opts->runtime_init_callback [0] == '\0') { + printf ("Missing runtime-init-callback symbol.\n"); + exit (0); + } + } else if (str_begins_with (arg, "runtime-init-callback")) { + opts->runtime_init_callback = g_strdup ("mono_invoke_runtime_init_callback"); } else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) { printf ("Supported options for --aot:\n"); printf (" asmonly - \n"); @@ -8786,6 +8810,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) printf (" mibc-profile= - \n"); printf (" print-skipped-methods - \n"); printf (" readonly-value= - \n"); + printf (" runtime-init-callback - Enable default runtime init callback support for UnmanagedCallersOnly+EntryPoint native-to-managed wrappers. Requires 'static' option.\n"); + printf (" runtime-init-callback= - Enable custom runtime init callback support for UnmanagedCallersOnly+EntryPoint native-to-managed wrappers. Requires 'static' option. Wrapper makes a direct call to symbol, initializing runtime.\n"); printf (" save-temps - \n"); printf (" soft-debug - \n"); printf (" static - \n"); @@ -10290,28 +10316,30 @@ mono_aot_mark_unused_llvm_plt_entry (MonoJumpInfo *patch_info) char* mono_aot_get_direct_call_symbol (MonoJumpInfoType type, gconstpointer data) { + gboolean direct_calls = llvm_acfg->aot_opts.direct_icalls; const char *sym = NULL; - if (llvm_acfg->aot_opts.direct_icalls) { - if (type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { - /* Call to a C function implementing a jit icall */ - sym = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data)->c_symbol; - } else if (type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { - MonoMethod *method = (MonoMethod *)data; - if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) - sym = lookup_icall_symbol_name_aot (method); - else if (is_direct_pinvoke_specified_for_method (llvm_acfg, method)) - get_pinvoke_import (llvm_acfg, method, NULL, &sym); - } else if (type == MONO_PATCH_INFO_JIT_ICALL_ID) { - MonoJitICallInfo const * const info = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data); - char const * const name = info->c_symbol; - if (name && info->func == info->wrapper) - sym = name; + if (direct_calls && type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + /* Call to a C function implementing a jit icall */ + sym = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data)->c_symbol; + } else if (direct_calls && type == MONO_PATCH_INFO_ICALL_ADDR_CALL) { + MonoMethod *method = (MonoMethod *)data; + if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) + sym = lookup_icall_symbol_name_aot (method); + else if (is_direct_pinvoke_specified_for_method (llvm_acfg, method)) + get_pinvoke_import (llvm_acfg, method, NULL, &sym); + } else if (type == MONO_PATCH_INFO_JIT_ICALL_ID && (direct_calls || (MonoJitICallId)(gsize)data == MONO_JIT_ICALL_mono_dummy_runtime_init_callback)) { + MonoJitICallInfo const * const info = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data); + if (info->func == info->wrapper) { + if ((MonoJitICallId)(gsize)data == MONO_JIT_ICALL_mono_dummy_runtime_init_callback) { + sym = llvm_acfg->aot_opts.runtime_init_callback; + } else { + sym = info->c_symbol ? info->c_symbol : NULL; + } } - if (sym) - return g_strdup (sym); } - return NULL; + + return sym ? g_strdup (sym) : NULL; } char* @@ -13890,6 +13918,7 @@ aot_opts_free (MonoAotOptions *aot_opts) g_free (aot_opts->llvm_cpu_attr); g_free (aot_opts->clangxx); g_free (aot_opts->depfile); + g_free (aot_opts->runtime_init_callback); } static void @@ -15190,6 +15219,12 @@ mono_aot_assemblies (MonoAssembly **assemblies, int nassemblies, guint32 jit_opt goto early_exit; } + if (aot_opts.runtime_init_callback != NULL && !aot_opts.static_link) { + fprintf (stderr, "The 'runtime-init-callback' option requires the 'static' option.\n"); + res = 1; + goto early_exit; + } + if (aot_opts.dedup_include) { /* Find the assembly which will contain the dedup-ed code */ int dedup_aindex = -1; diff --git a/src/mono/mono/mini/jit-icalls.c b/src/mono/mono/mini/jit-icalls.c index a22a36ea98f5b..c26e104a09d92 100644 --- a/src/mono/mono/mini/jit-icalls.c +++ b/src/mono/mono/mini/jit-icalls.c @@ -1708,6 +1708,16 @@ mono_dummy_jit_icall_val (gpointer val) { } +/* Dummy icall place holder function representing runtime init call. */ +/* When used, function will be replaced with a direct icall to a custom */ +/* runtime init function called from start of native-to-managed wrapper. */ +/* This function should never end up being called. */ +void +mono_dummy_runtime_init_callback (void) +{ + g_assert (!"Runtime incorrectly configured to support runtime init callback from native-to-managed wrapper."); +} + void mini_init_method_rgctx (MonoMethodRuntimeGenericContext *mrgctx, MonoGSharedMethodInfo *info) { diff --git a/src/mono/mono/mini/jit-icalls.h b/src/mono/mono/mini/jit-icalls.h index afd10935c5dc4..db59a7608c8da 100644 --- a/src/mono/mono/mini/jit-icalls.h +++ b/src/mono/mono/mini/jit-icalls.h @@ -238,6 +238,8 @@ ICALL_EXPORT void mono_dummy_jit_icall (void); ICALL_EXPORT void mono_dummy_jit_icall_val (gpointer ptr); +ICALL_EXPORT void mono_dummy_runtime_init_callback (void); + ICALL_EXPORT void mini_init_method_rgctx (MonoMethodRuntimeGenericContext *mrgctx, MonoGSharedMethodInfo *info); #endif /* __MONO_JIT_ICALLS_H__ */ diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 235774701cdd9..cd7117dde4143 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -157,6 +157,9 @@ static GPtrArray *profile_options; static GSList *tramp_infos; GSList *mono_interp_only_classes; +static MonoRuntimeInitCallback runtime_init_callback; +static guint64 runtime_init_thread_id = G_MAXUINT64; + static void register_icalls (void); static void runtime_cleanup (MonoDomain *domain, gpointer user_data); static void mini_invalidate_transformed_interp_methods (MonoAssemblyLoadContext *alc, uint32_t generation); @@ -5036,6 +5039,9 @@ register_icalls (void) register_icall_no_wrapper (mono_interp_entry_from_trampoline, mono_icall_sig_void_ptr_ptr); register_icall_no_wrapper (mono_interp_to_native_trampoline, mono_icall_sig_void_ptr_ptr); + /* Register dummy icall placeholder for runtime init callback support in native-to-managed wrapper */ + register_icall_no_wrapper (mono_dummy_runtime_init_callback, mono_icall_sig_void); + #ifdef MONO_ARCH_HAS_REGISTER_ICALL mono_arch_register_icall (); #endif @@ -5395,3 +5401,36 @@ mini_alloc_jinfo (MonoJitMemoryManager *jit_mm, int size) return (MonoJitInfo*)mono_mem_manager_alloc0 (jit_mm->mem_manager, size); } } + +void +mono_set_runtime_init_callback (MonoRuntimeInitCallback callback) +{ + runtime_init_callback = callback; +} + +/* +* Default implementation invoking runtime init callback in native-to-managed wrapper. +*/ +void +mono_invoke_runtime_init_callback (void) +{ + MonoRuntimeInitCallback callback = NULL; + mono_atomic_load_acquire (callback, MonoRuntimeInitCallback, &runtime_init_callback); + if (G_UNLIKELY (callback)) { + guint64 thread_id = mono_native_thread_os_id_get (); + if (callback && (mono_atomic_load_i64 ((volatile gint64 *)&runtime_init_thread_id) == thread_id)) + return; + + while (mono_atomic_cas_i64 ((volatile gint64 *)&runtime_init_thread_id, (gint64)thread_id, (gint64)G_MAXUINT64) != (gint64)G_MAXUINT64) + g_usleep (1000); + + mono_atomic_load_acquire (callback, MonoRuntimeInitCallback, &runtime_init_callback); + if (callback) { + if (!mono_thread_info_current_unchecked ()) + callback (); + mono_atomic_store_release (&runtime_init_callback, NULL); + } + + mono_atomic_xchg_i64 ((volatile gint64 *)&runtime_init_thread_id, (gint64)G_MAXUINT64); + } +} diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index 0abd8e4332334..16299ff402052 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -718,4 +718,12 @@ void mini_register_sigterm_handler (void); MONO_RESTORE_WARNING \ } while (0) +typedef void (*MonoRuntimeInitCallback) (void); + +MONO_COMPONENT_API void +mono_set_runtime_init_callback (MonoRuntimeInitCallback callback); + +MONO_COMPONENT_API void +mono_invoke_runtime_init_callback (void); + #endif /* __MONO_MINI_RUNTIME_H__ */ diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 098712ff609b8..c6dd66921ccf5 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3178,8 +3178,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts cfg->jit_mm = jit_mm_for_method (cfg->method); cfg->mem_manager = m_method_get_mem_manager (cfg->method); - if (cfg->method->wrapper_type == MONO_WRAPPER_ALLOC) { - /* We can't have seq points inside gc critical regions */ + if (cfg->method->wrapper_type == MONO_WRAPPER_ALLOC || cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) { + /* We can't have seq points inside gc critical regions or native-to-managed wrapper */ cfg->gen_seq_points = FALSE; cfg->gen_sdb_seq_points = FALSE; }