diff --git a/mono/metadata/marshal-ilgen.c b/mono/metadata/marshal-ilgen.c index 9882ccabfda1..8e7408cc5523 100644 --- a/mono/metadata/marshal-ilgen.c +++ b/mono/metadata/marshal-ilgen.c @@ -5815,9 +5815,7 @@ static void emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, uint32_t target_handle) { MonoMethodSignature *sig, *csig; - MonoExceptionClause *clauses, *clause_finally, *clause_catch; int i, *tmp_locals, ex_local, e_local, attach_cookie_local, attach_dummy_local; - int leave_try_pos, leave_catch_pos, ex_m1_pos; gboolean closed = FALSE; sig = m->sig; @@ -5858,35 +5856,17 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s /* * guint32 ex = -1; - * try { - * // does (STARTING|RUNNING|BLOCKING) -> RUNNING + set/switch domain - * mono_threads_attach_coop (); - * - * - * - * ret = method (...); - * } catch (Exception e) { - * ex = mono_gchandle_new (e, false); - * } finally { - * // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain - * mono_threads_detach_coop (); + * // does (STARTING|RUNNING|BLOCKING) -> RUNNING + set/switch domain + * mono_threads_attach_coop (); + * * - * if (ex != -1) - * mono_marshal_ftnptr_eh_callback (ex); - * } + * ret = method (...); + * // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain + * mono_threads_detach_coop (); * * return ret; */ - clauses = g_new0 (MonoExceptionClause, 2); - - clause_catch = &clauses [0]; - clause_catch->flags = MONO_EXCEPTION_CLAUSE_NONE; - clause_catch->data.catch_class = mono_defaults.exception_class; - - clause_finally = &clauses [1]; - clause_finally->flags = MONO_EXCEPTION_CLAUSE_FINALLY; - mono_mb_emit_icon (mb, 0); mono_mb_emit_stloc (mb, 2); @@ -5894,9 +5874,6 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s mono_mb_emit_byte (mb, CEE_CONV_U4); mono_mb_emit_stloc (mb, ex_local); - /* try { */ - clause_catch->try_offset = clause_finally->try_offset = mono_mb_get_label (mb); - if (!mono_threads_is_blocking_transition_enabled ()) { mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); mono_mb_emit_byte (mb, CEE_MONO_JIT_ATTACH); @@ -6054,31 +6031,6 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s } } - leave_try_pos = mono_mb_emit_branch (mb, CEE_LEAVE); - - /* } [endtry] */ - - /* catch (Exception e) { */ - clause_catch->try_len = mono_mb_get_label (mb) - clause_catch->try_offset; - clause_catch->handler_offset = mono_mb_get_label (mb); - - mono_mb_emit_stloc (mb, e_local); - - /* ex = mono_gchandle_new (e, false); */ - mono_mb_emit_ldloc (mb, e_local); - mono_mb_emit_icon (mb, 0); - mono_mb_emit_icall (mb, mono_gchandle_new); - mono_mb_emit_stloc (mb, ex_local); - - leave_catch_pos = mono_mb_emit_branch (mb, CEE_LEAVE); - - /* } [endcatch] */ - clause_catch->handler_len = mono_mb_get_pos (mb) - clause_catch->handler_offset; - - /* finally { */ - clause_finally->try_len = mono_mb_get_label (mb) - clause_finally->try_offset; - clause_finally->handler_offset = mono_mb_get_label (mb); - if (!mono_threads_is_blocking_transition_enabled ()) { mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); mono_mb_emit_byte (mb, CEE_MONO_JIT_DETACH); @@ -6089,27 +6041,6 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s mono_mb_emit_icall (mb, mono_threads_detach_coop); } - /* if (ex != -1) */ - mono_mb_emit_ldloc (mb, ex_local); - mono_mb_emit_icon (mb, -1); - mono_mb_emit_byte (mb, CEE_CONV_U4); - ex_m1_pos = mono_mb_emit_branch (mb, CEE_BEQ); - - /* mono_marshal_ftnptr_eh_callback (ex) */ - mono_mb_emit_ldloc (mb, ex_local); - mono_mb_emit_icall (mb, mono_marshal_ftnptr_eh_callback); - - /* [ex == -1] */ - mono_mb_patch_branch (mb, ex_m1_pos); - - mono_mb_emit_byte (mb, CEE_ENDFINALLY); - - /* } [endfinally] */ - clause_finally->handler_len = mono_mb_get_pos (mb) - clause_finally->handler_offset; - - mono_mb_patch_branch (mb, leave_try_pos); - mono_mb_patch_branch (mb, leave_catch_pos); - /* return ret; */ if (m->retobj_var) { mono_mb_emit_ldloc (mb, m->retobj_var); @@ -6117,13 +6048,11 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s mono_mb_emit_op (mb, CEE_MONO_RETOBJ, m->retobj_class); } else { - if (!MONO_TYPE_IS_VOID(sig->ret)) + if (!MONO_TYPE_IS_VOID (sig->ret)) mono_mb_emit_ldloc (mb, 3); mono_mb_emit_byte (mb, CEE_RET); } - mono_mb_set_clauses (mb, 2, clauses); - if (closed) g_free (sig); } diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 2c374c30e80d..a5f2c5be6b97 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -88,10 +88,6 @@ static gboolean use_aot_wrappers; static int class_marshal_info_count; -static void ftnptr_eh_callback_default (guint32 gchandle); - -static MonoFtnPtrEHCallback ftnptr_eh_callback = ftnptr_eh_callback_default; - static MonoMarshalCallbacks * get_marshal_cb (void); @@ -267,9 +263,7 @@ mono_marshal_init (void) register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE); register_icall (mono_gc_wbarrier_generic_nostore, "wb_generic", "void ptr", FALSE); register_icall (mono_gchandle_get_target, "mono_gchandle_get_target", "object int32", TRUE); - register_icall (mono_gchandle_new, "mono_gchandle_new", "uint32 object bool", TRUE); register_icall (mono_marshal_isinst_with_cache, "mono_marshal_isinst_with_cache", "object object ptr ptr", FALSE); - register_icall (mono_marshal_ftnptr_eh_callback, "mono_marshal_ftnptr_eh_callback", "void uint32", TRUE); register_icall (mono_threads_enter_gc_safe_region_unbalanced, "mono_threads_enter_gc_safe_region_unbalanced", "ptr ptr", TRUE); register_icall (mono_threads_exit_gc_safe_region_unbalanced, "mono_threads_exit_gc_safe_region_unbalanced", "void ptr ptr", TRUE); register_icall (mono_threads_enter_gc_unsafe_region_unbalanced, "mono_threads_enter_gc_unsafe_region_unbalanced", "ptr ptr", TRUE); @@ -6194,43 +6188,6 @@ mono_marshal_free_dynamic_wrappers (MonoMethod *method) mono_marshal_unlock (); } -void -mono_marshal_ftnptr_eh_callback (guint32 gchandle) -{ - g_assert (ftnptr_eh_callback); - ftnptr_eh_callback (gchandle); -} - -static void -ftnptr_eh_callback_default (guint32 gchandle) -{ - MonoException *exc; - gpointer stackdata; - - mono_threads_enter_gc_unsafe_region_unbalanced (&stackdata); - - exc = (MonoException*) mono_gchandle_get_target (gchandle); - - mono_gchandle_free (gchandle); - - mono_reraise_exception_deprecated (exc); -} - -/* - * mono_install_ftnptr_eh_callback: - * - * Install a callback that should be called when there is a managed exception - * in a native-to-managed wrapper. This is mainly used by iOS to convert a - * managed exception to a native exception, to properly unwind the native - * stack; this native exception will then be converted back to a managed - * exception in their managed-to-native wrapper. - */ -void -mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback) -{ - ftnptr_eh_callback = callback; -} - MonoThreadInfo* mono_icall_start (HandleStackMark *stackmark, MonoError *error) { diff --git a/mono/metadata/marshal.h b/mono/metadata/marshal.h index 7c30bc61d92a..6abe4b47641c 100644 --- a/mono/metadata/marshal.h +++ b/mono/metadata/marshal.h @@ -843,11 +843,6 @@ mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, MonoMethodBuilder *mb, MonoMethodSignature *sig, int max_stack, WrapperInfo *info, gboolean *out_found); -typedef void (*MonoFtnPtrEHCallback) (guint32 gchandle); - -MONO_API void -mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback); - G_END_DECLS #endif /* __MONO_MARSHAL_H__ */ diff --git a/mono/mini/mini-exceptions.c b/mono/mini/mini-exceptions.c index 45c64c435713..a7795f1d376c 100644 --- a/mono/mini/mini-exceptions.c +++ b/mono/mini/mini-exceptions.c @@ -112,6 +112,8 @@ static gpointer restore_context_func, call_filter_func; static gpointer throw_exception_func, rethrow_exception_func; static gpointer throw_corlib_exception_func; +static MonoFtnPtrEHCallback ftnptr_eh_callback; + static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data); static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx); static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data); @@ -1602,14 +1604,24 @@ setup_stack_trace (MonoException *mono_ex, GSList *dynamic_methods, GList **trac *trace_ips = NULL; } +typedef enum { + MONO_FIRST_PASS_UNHANDLED, + MONO_FIRST_PASS_CALLBACK_TO_NATIVE, + MONO_FIRST_PASS_HANDLED, +} MonoFirstPassResult; + /* * handle_exception_first_pass: * - * The first pass of exception handling. Unwind the stack until a catch clause which can catch - * OBJ is found. Store the index of the filter clause which caught the exception into - * OUT_FILTER_IDX. Return TRUE if the exception is caught, FALSE otherwise. + * The first pass of exception handling. Unwind the stack until a catch + * clause which can catch OBJ is found. Store the index of the filter clause + * which caught the exception into OUT_FILTER_IDX. Return + * \c MONO_FIRST_PASS_HANDLED if the exception is caught, + * \c MONO_FIRST_PASS_UNHANDLED otherwise, unless there is a native-to-managed + * wrapper and an exception handling callback is installed (in which case + * return \c MONO_FIRST_PASS_CALLBACK_TO_NATIVE). */ -static gboolean +static MonoFirstPassResult handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filter_idx, MonoJitInfo **out_ji, MonoJitInfo **out_prev_ji, MonoObject *non_exception, StackFrameInfo *catch_frame) { ERROR_DECL (error); @@ -1631,6 +1643,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt Unwinder unwinder; gboolean in_interp; + MonoFirstPassResult result = MONO_FIRST_PASS_UNHANDLED; + g_assert (ctx != NULL); if (obj == (MonoObject *)domain->stack_overflow_ex) @@ -1686,7 +1700,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt if (!unwind_res) { setup_stack_trace (mono_ex, dynamic_methods, &trace_ips); g_slist_free (dynamic_methods); - return FALSE; + return result; } switch (frame.type) { @@ -1740,6 +1754,11 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt } else { free_stack = 0xffffff; } + + if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) { + result = MONO_FIRST_PASS_CALLBACK_TO_NATIVE; + } + for (i = clause_index_start; i < ji->num_clauses; i++) { MonoJitExceptionInfo *ei = &ji->clauses [i]; @@ -1818,7 +1837,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt MONO_CONTEXT_SET_IP (ctx, ei->handler_start); frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start; *catch_frame = frame; - return TRUE; + result = MONO_FIRST_PASS_HANDLED; + return result; } } @@ -1836,7 +1856,8 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt MONO_CONTEXT_SET_IP (ctx, ei->handler_start); frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start; *catch_frame = frame; - return TRUE; + result = MONO_FIRST_PASS_HANDLED; + return result; } mono_error_cleanup (&isinst_error); } @@ -1962,8 +1983,6 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu memcpy (&jit_tls->orig_ex_ctx, ctx, sizeof (MonoContext)); if (!resume) { - gboolean res; - MonoContext ctx_cp = *ctx; if (mono_trace_is_enabled ()) { MonoMethod *system_exception_get_message = mono_class_get_method_from_name (mono_defaults.exception_class, "get_Message", 0); @@ -2003,9 +2022,10 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu jit_tls->orig_ex_ctx_set = FALSE; StackFrameInfo catch_frame; + MonoFirstPassResult res; res = handle_exception_first_pass (&ctx_cp, obj, &first_filter_idx, &ji, &prev_ji, non_exception, &catch_frame); - if (!res) { + if (res == MONO_FIRST_PASS_UNHANDLED) { if (mini_get_debug_options ()->break_on_exc) G_BREAKPOINT (); mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, NULL, NULL); @@ -2037,7 +2057,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu if (unhandled) mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, NULL, NULL); - else + else if (res != MONO_FIRST_PASS_CALLBACK_TO_NATIVE) mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame); } } @@ -2108,6 +2128,15 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu free_stack = 0xffffff; } + if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) { + guint32 handle = mono_gchandle_new (obj, FALSE); + gpointer stackptr; + + mono_threads_enter_gc_safe_region_unbalanced (&stackptr); + ftnptr_eh_callback (handle); + g_error ("Did not expect ftnptr_eh_callback to return."); + } + for (i = clause_index_start; i < ji->num_clauses; i++) { MonoJitExceptionInfo *ei = &ji->clauses [i]; gboolean filtered = FALSE; @@ -3189,6 +3218,21 @@ mono_jinfo_get_epilog_size (MonoJitInfo *ji) return info->epilog_size; } +/* + * mono_install_ftnptr_eh_callback: + * + * Install a callback that should be called when there is a managed exception + * in a native-to-managed wrapper. This is mainly used by iOS to convert a + * managed exception to a native exception, to properly unwind the native + * stack; this native exception will then be converted back to a managed + * exception in their managed-to-native wrapper. + */ +void +mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback) +{ + ftnptr_eh_callback = callback; +} + /* * LLVM/Bitcode exception handling. */ diff --git a/mono/mini/mini-runtime.h b/mono/mini/mini-runtime.h index dcf189d8c96a..9519e373136d 100644 --- a/mono/mini/mini-runtime.h +++ b/mono/mini/mini-runtime.h @@ -156,6 +156,8 @@ typedef struct { gpointer interp_exit_data; } MonoLMFExt; +typedef void (*MonoFtnPtrEHCallback) (guint32 gchandle); + typedef struct { gboolean handle_sigint; gboolean keep_delegates; @@ -355,6 +357,9 @@ void mini_cleanup (MonoDomain *domain); MONO_API MonoDebugOptions *mini_get_debug_options (void); MONO_API gboolean mini_parse_debug_option (const char *option); +MONO_API void +mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback); + void mini_jit_init (void); void mini_jit_cleanup (void); void mono_disable_optimizations (guint32 opts); diff --git a/mono/tests/Makefile.am b/mono/tests/Makefile.am index a4dbbf17573e..a4d21d19f673 100755 --- a/mono/tests/Makefile.am +++ b/mono/tests/Makefile.am @@ -326,6 +326,7 @@ TESTS_CS_SRC= \ desweak.cs \ exists.cs \ handleref.cs \ + install_eh_callback.cs \ dbnull-missing.cs \ test-type-ctor.cs \ soft-float-tests.cs \ @@ -970,12 +971,6 @@ CI_PR_DISABLED_TESTS = \ process-stress-3.exe \ bug-80307.exe -if ENABLE_COOP -CI_PR_DISABLED_TESTS += \ - bug-58782-plain-throw.exe \ - bug-58782-capture-and-throw.exe -endif - # appdomain-threadpool-unload.exe creates 100 appdomains, takes too long with llvm LLVM_DISABLED_TESTS = \ finally_block_ending_in_dead_bb.exe \ @@ -1773,7 +1768,7 @@ test-unhandled-exception: unhandled-exception-test-runner.2.exe safehandle.2.exe winx64structs.exe thunks.exe pinvoke3.exe pinvoke2.exe pinvoke-2.2.exe pinvoke17.exe pinvoke13.exe \ pinvoke11.exe pinvoke_ppcs.exe pinvoke_ppci.exe pinvoke_ppcf.exe pinvoke_ppcd.exe pinvoke_ppcc.exe pinvoke.exe \ marshalbool.exe marshal9.exe marshal5.exe marshal.exe handleref.exe cominterop.exe bug-Xamarin-5278.exe \ - bug-58782-plain-throw.exe bug-58782-capture-and-throw.exe: libtest.la + bug-58782-plain-throw.exe bug-58782-capture-and-throw.exe install_eh_callback.exe: libtest.la event-get.2.exe$(PLATFORM_AOT_SUFFIX): event-il.exe$(PLATFORM_AOT_SUFFIX) event-get.2.exe: event-il.exe diff --git a/mono/tests/install_eh_callback.cs b/mono/tests/install_eh_callback.cs new file mode 100644 index 000000000000..26fd88dea8a0 --- /dev/null +++ b/mono/tests/install_eh_callback.cs @@ -0,0 +1,93 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +public class MonoPInvokeCallbackAttribute : Attribute { + public MonoPInvokeCallbackAttribute (Type delegateType) { } +} + +public class Tests { + + [DllImport ("libtest")] + public static extern void mono_test_setjmp_and_call (VoidVoidDelegate del, out IntPtr handle); + + public delegate void VoidVoidDelegate (); + + public class SpecialExn : Exception { + } + + public class SomeOtherExn : Exception { + } + + [MethodImpl (MethodImplOptions.NoInlining)] + private static void callee (ref bool called) { + called = true; + throw new SpecialExn (); + } + + public class Caller { + public static bool called; + public static bool finally_called; + + public static void Setup () { + called = false; + finally_called = false; + } + + [MonoPInvokeCallback (typeof (VoidVoidDelegate))] + public static void M () { + try { + callee (ref called); + throw new Exception ("unexpected return from callee"); + } catch (SomeOtherExn) { + } finally { + finally_called = true; + } + } + } + + public static int test_0_setjmp_exn_handler () + { + IntPtr res; + Caller.Setup (); + VoidVoidDelegate f = new VoidVoidDelegate (Caller.M); + + try { + mono_test_setjmp_and_call (f, out res); + } catch (SpecialExn) { + Console.Error.WriteLine ("should not have caught a SpecialExn"); + return 1; + } + if (!Caller.called) { + Console.Error.WriteLine ("delegate not even called"); + return 2; + } + if (!Caller.finally_called) { + Console.Error.WriteLine ("finally not reached"); + return 3; + } + if (res == IntPtr.Zero) { + Console.Error.WriteLine ("res should be a GCHandle, was 0"); + return 4; + } + GCHandle h = GCHandle.FromIntPtr (res); + object o = h.Target; + h.Free (); + if (o == null) { + Console.Error.WriteLine ("GCHandle target was null"); + return 5; + } + else if (o is SpecialExn) + return 0; + else { + Console.Error.WriteLine ("o was not a SpecialExn, it is {0}", o); + return 6; + } + } + + + static int Main () + { + return TestDriver.RunTests (typeof (Tests)); + } +} diff --git a/mono/tests/libtest.c b/mono/tests/libtest.c index e1c68d5ca8b2..2ce095b35891 100644 --- a/mono/tests/libtest.c +++ b/mono/tests/libtest.c @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -7537,3 +7538,33 @@ mono_test_native_to_managed_exception_rethrow (NativeToManagedExceptionRethrowFu pthread_join (t, NULL); } #endif + +typedef void (*VoidVoidCallback) (void); +typedef void (*MonoFtnPtrEHCallback) (guint32 gchandle); + +static jmp_buf test_jmp_buf; +static guint32 test_gchandle; + +static void +mono_test_longjmp_callback (guint32 gchandle) +{ + test_gchandle = gchandle; + longjmp (test_jmp_buf, 1); +} + +LIBTEST_API void STDCALL +mono_test_setjmp_and_call (VoidVoidCallback managedCallback, intptr_t *out_handle) +{ + void (*mono_install_ftnptr_eh_callback) (MonoFtnPtrEHCallback) = + (void (*) (MonoFtnPtrEHCallback)) (lookup_mono_symbol ("mono_install_ftnptr_eh_callback")); + if (setjmp (test_jmp_buf) == 0) { + *out_handle = 0; + mono_install_ftnptr_eh_callback (mono_test_longjmp_callback); + managedCallback (); + *out_handle = 0; /* Do not expect to return here */ + } else { + mono_install_ftnptr_eh_callback (NULL); + *out_handle = test_gchandle; + } +} +