Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 7 additions & 78 deletions mono/metadata/marshal-ilgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -5858,45 +5856,24 @@ 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 ();
*
* <interrupt check>
*
* 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 ();
* <interrupt check>
*
* 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);

mono_mb_emit_icon (mb, -1);
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);
Expand Down Expand Up @@ -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);
Expand All @@ -6089,41 +6041,18 @@ 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);
mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
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);
}
Expand Down
43 changes: 0 additions & 43 deletions mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down
5 changes: 0 additions & 5 deletions mono/metadata/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__ */
Expand Down
66 changes: 55 additions & 11 deletions mono/mini/mini-exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know the code enough but shouldn't we return here? It seems to me that, since we found a native-to-managed frame, we don't want to continue looking for a clause that could handle the exception. We don't want to call the callback just yet either because we want to run the finalizers, but the first pass should be finished here IIUC.

@vargaz @alexanderkyte @kumpera could you please confirm? Thank you.

}


for (i = clause_index_start; i < ji->num_clauses; i++) {
MonoJitExceptionInfo *ei = &ji->clauses [i];
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we got here, res is MONO_FIRST_PASS_UNHANDLED so this check seems redundant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - this is all in the else branch of if (res == MONO_FIRST_PASS_UNHANDLED), so it could be handled or passed to native.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, misread the code.

mono_debugger_agent_handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame);
}
}
Expand Down Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MONO_FIRST_PASS_CALLBACK_TO_NATIVE doesn't seem necessary since returning TRUE from handle_exception_first_pass and checking for the wrapper_type again has the same effect.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the difference might be in how/when we signal the debugger... we don't want MONO_FIRST_PASS_CALLBACK_TO_NATIVE to tell the debugger that we're handling the exception since the callback is kind of internal to the embedder.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But obviously I haven't written a test for that part, so I'm just guessing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I based this comment on https://github.com/mono/mono/pull/5837/files#r176922871 which was wrong, so please ignore this review.

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;
Expand Down Expand Up @@ -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.
*/
Expand Down
5 changes: 5 additions & 0 deletions mono/mini/mini-runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ typedef struct {
gpointer interp_exit_data;
} MonoLMFExt;

typedef void (*MonoFtnPtrEHCallback) (guint32 gchandle);

typedef struct {
gboolean handle_sigint;
gboolean keep_delegates;
Expand Down Expand Up @@ -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);
Expand Down
Loading