Merge pull request #3024 from lambdageek/dev/monoerror-endinvoke
[mono.git] / mono / metadata / marshal.c
index 3d4e8146decc3263d8a87bbba9a445e391fcfb57..91fc1d62ac0bc0c1b17df7835eaec559d6f44f31 100644 (file)
@@ -79,9 +79,16 @@ static MonoNativeTlsKey load_type_info_tls_id;
 
 static gboolean use_aot_wrappers;
 
+static void ftnptr_eh_callback_default (guint32 gchandle);
+
+static MonoFtnPtrEHCallback ftnptr_eh_callback = ftnptr_eh_callback_default;
+
 static void
 delegate_hash_table_add (MonoDelegate *d);
 
+static void
+delegate_hash_table_remove (MonoDelegate *d);
+
 static void
 emit_struct_conv (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_object);
 
@@ -192,6 +199,9 @@ mono_array_to_lparray (MonoArray *array);
 void
 mono_free_lparray (MonoArray *array, gpointer* nativeArray);
 
+static void
+mono_marshal_ftnptr_eh_callback (guint32 gchandle);
+
 /* Lazy class loading functions */
 static GENERATE_GET_CLASS_WITH_CACHE (string_builder, System.Text, StringBuilder)
 static GENERATE_GET_CLASS_WITH_CACHE (date_time, System, DateTime)
@@ -247,6 +257,15 @@ mono_object_isinst_icall (MonoObject *obj, MonoClass *klass)
        return result;
 }
 
+static MonoString*
+ves_icall_mono_string_from_utf16 (gunichar2 *data)
+{
+       MonoError error;
+       MonoString *result = mono_string_from_utf16_checked (data, &error);
+       mono_error_set_pending_exception (&error);
+       return result;
+}
+
 void
 mono_marshal_init (void)
 {
@@ -261,7 +280,7 @@ mono_marshal_init (void)
                register_icall (mono_marshal_string_to_utf16, "mono_marshal_string_to_utf16", "ptr obj", FALSE);
                register_icall (mono_marshal_string_to_utf16_copy, "mono_marshal_string_to_utf16_copy", "ptr obj", FALSE);
                register_icall (mono_string_to_utf16, "mono_string_to_utf16", "ptr obj", FALSE);
-               register_icall (mono_string_from_utf16, "mono_string_from_utf16", "obj ptr", FALSE);
+               register_icall (ves_icall_mono_string_from_utf16, "ves_icall_mono_string_from_utf16", "obj ptr", FALSE);
                register_icall (mono_string_from_byvalstr, "mono_string_from_byvalstr", "obj ptr int", FALSE);
                register_icall (mono_string_from_byvalwstr, "mono_string_from_byvalwstr", "obj ptr int", FALSE);
                register_icall (mono_string_new_wrapper, "mono_string_new_wrapper", "obj ptr", FALSE);
@@ -298,22 +317,20 @@ mono_marshal_init (void)
                register_icall (mono_struct_delete_old, "mono_struct_delete_old", "void ptr ptr", FALSE);
                register_icall (mono_delegate_begin_invoke, "mono_delegate_begin_invoke", "object object ptr", FALSE);
                register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE);
-               register_icall (mono_compile_method, "mono_compile_method", "ptr ptr", FALSE);
                register_icall (mono_context_get, "mono_context_get", "object", FALSE);
                register_icall (mono_context_set, "mono_context_set", "void object", 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_attach_coop, "mono_threads_attach_coop", "ptr ptr ptr", TRUE);
+               register_icall (mono_threads_detach_coop, "mono_threads_detach_coop", "void ptr ptr", TRUE);
 
                mono_cominterop_init ();
                mono_remoting_init ();
-
-               if (mono_threads_is_coop_enabled ()) {
-                       register_icall (mono_threads_prepare_blocking, "mono_threads_prepare_blocking", "ptr ptr", FALSE);
-                       register_icall (mono_threads_finish_blocking, "mono_threads_finish_blocking", "void ptr ptr", FALSE);
-                       register_icall (mono_threads_reset_blocking_start, "mono_threads_reset_blocking_start","ptr ptr", TRUE);
-                       register_icall (mono_threads_reset_blocking_end, "mono_threads_reset_blocking_end","void ptr ptr", TRUE);
-               }
        }
 }
 
@@ -382,19 +399,22 @@ mono_delegate_to_ftnptr (MonoDelegate *delegate)
 
        wrapper = mono_marshal_get_managed_wrapper (method, klass, target_handle);
 
-       delegate->delegate_trampoline = mono_compile_method (wrapper);
+       delegate->delegate_trampoline = mono_compile_method_checked (wrapper, &error);
+       if (!is_ok (&error))
+               goto fail;
 
        // Add the delegate to the delegate hash table
        delegate_hash_table_add (delegate);
 
        /* when the object is collected, collect the dynamic method, too */
-       mono_object_register_finalizer ((MonoObject*)delegate, &error);
-       if (!mono_error_ok (&error)) {
-               mono_error_set_pending_exception (&error);
-               return NULL;
-       }
+       mono_object_register_finalizer ((MonoObject*)delegate);
 
        return delegate->delegate_trampoline;
+
+fail:
+       mono_gchandle_free (target_handle);
+       mono_error_set_pending_exception (&error);
+       return NULL;
 }
 
 /* 
@@ -559,7 +579,12 @@ mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn)
                        mono_error_set_pending_exception (&error);
                        return NULL;
                }
-               mono_delegate_ctor_with_method ((MonoObject*)d, this_obj, mono_compile_method (wrapper), wrapper);
+               gpointer compiled_ptr = mono_compile_method_checked (wrapper, &error);
+               if (mono_error_set_pending_exception (&error))
+                       return NULL;
+               mono_delegate_ctor_with_method ((MonoObject*)d, this_obj, compiled_ptr, wrapper, &error);
+               if (mono_error_set_pending_exception (&error))
+                       return NULL;
        }
 
        if (d->object.vtable->domain != mono_domain_get ()) {
@@ -606,9 +631,11 @@ mono_delegate_free_ftnptr (MonoDelegate *delegate)
        }
 }
 
+/* This is a JIT icall, it sets the pending exception and returns NULL on error */
 static MonoString *
 mono_string_from_byvalstr (const char *data, int max_len)
 {
+       MonoError error;
        MonoDomain *domain = mono_domain_get ();
        int len = 0;
 
@@ -618,7 +645,9 @@ mono_string_from_byvalstr (const char *data, int max_len)
        while (len < max_len - 1 && data [len])
                len++;
 
-       return mono_string_new_len (domain, data, len);
+       MonoString *result = mono_string_new_len_checked (domain, data, len, &error);
+       mono_error_set_pending_exception (&error);
+       return result;
 }
 
 /* This is a JIT icall, it sets the pending exception and return NULL on error */
@@ -1126,10 +1155,14 @@ mono_string_to_byvalwstr (gpointer dst, MonoString *src, int size)
        *((gunichar2 *) dst + len) = 0;
 }
 
+/* this is an icall, it sets the pending exception and returns NULL on error */
 static MonoString*
 mono_string_new_len_wrapper (const char *text, guint length)
 {
-       return mono_string_new_len (mono_domain_get (), text, length);
+       MonoError error;
+       MonoString *result = mono_string_new_len_checked (mono_domain_get (), text, length, &error);
+       mono_error_set_pending_exception (&error);
+       return result;
 }
 
 #ifndef DISABLE_JIT
@@ -1216,7 +1249,8 @@ guint
 mono_type_to_stind (MonoType *type)
 {
        if (type->byref)
-               return CEE_STIND_I;
+               return MONO_TYPE_IS_REFERENCE (type) ? CEE_STIND_REF : CEE_STIND_I;
+
 
 handle_enum:
        switch (type->type) {
@@ -1312,7 +1346,7 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 1);
                mono_mb_emit_icon (mb, mspec->data.array_data.num_elem);
                mono_mb_emit_op (mb, CEE_NEWARR, eklass);       
-               mono_mb_emit_byte (mb, CEE_STIND_I);
+               mono_mb_emit_byte (mb, CEE_STIND_REF);
 
                if (eklass->blittable) {
                        /* copy the elements */
@@ -1422,7 +1456,7 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                } else {
                        mono_mb_emit_ldloc (mb, 1);
                        mono_mb_emit_ldloc (mb, 0);
-                       mono_mb_emit_icall (mb, mono_string_from_utf16);
+                       mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16);
                }
                mono_mb_emit_byte (mb, CEE_STIND_REF);          
                break;          
@@ -1431,7 +1465,7 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 0);
                mono_mb_emit_byte (mb, CEE_LDIND_I);
 #ifdef TARGET_WIN32
-               mono_mb_emit_icall (mb, mono_string_from_utf16);
+               mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16);
 #else
                mono_mb_emit_icall (mb, mono_string_new_wrapper);
 #endif
@@ -1448,7 +1482,7 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 1);
                mono_mb_emit_ldloc (mb, 0);
                mono_mb_emit_byte (mb, CEE_LDIND_I);
-               mono_mb_emit_icall (mb, mono_string_from_utf16);
+               mono_mb_emit_icall (mb, ves_icall_mono_string_from_utf16);
                mono_mb_emit_byte (mb, CEE_STIND_REF);
                break;
        case MONO_MARSHAL_CONV_OBJECT_STRUCT: {
@@ -1550,16 +1584,23 @@ emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
 }
 
 static gpointer
-conv_to_icall (MonoMarshalConv conv)
+conv_to_icall (MonoMarshalConv conv, int *ind_store_type)
 {
+       int dummy;
+       if (!ind_store_type)
+               ind_store_type = &dummy;
+       *ind_store_type = CEE_STIND_I;
        switch (conv) {
        case MONO_MARSHAL_CONV_STR_LPWSTR:
                return mono_marshal_string_to_utf16;            
        case MONO_MARSHAL_CONV_LPWSTR_STR:
-               return mono_string_from_utf16;
+               *ind_store_type = CEE_STIND_REF;
+               return ves_icall_mono_string_from_utf16;
        case MONO_MARSHAL_CONV_LPTSTR_STR:
+               *ind_store_type = CEE_STIND_REF;
                return mono_string_new_wrapper;
        case MONO_MARSHAL_CONV_LPSTR_STR:
+               *ind_store_type = CEE_STIND_REF;
                return mono_string_new_wrapper;
        case MONO_MARSHAL_CONV_STR_LPTSTR:
 #ifdef TARGET_WIN32
@@ -1572,7 +1613,8 @@ conv_to_icall (MonoMarshalConv conv)
        case MONO_MARSHAL_CONV_STR_BSTR:
                return mono_string_to_bstr;
        case MONO_MARSHAL_CONV_BSTR_STR:
-               return mono_string_from_bstr;
+               *ind_store_type = CEE_STIND_REF;
+               return mono_string_from_bstr_icall;
        case MONO_MARSHAL_CONV_STR_TBSTR:
        case MONO_MARSHAL_CONV_STR_ANSIBSTR:
                return mono_string_to_ansibstr;
@@ -1595,16 +1637,20 @@ conv_to_icall (MonoMarshalConv conv)
        case MONO_MARSHAL_CONV_DEL_FTN:
                return mono_delegate_to_ftnptr;
        case MONO_MARSHAL_CONV_FTN_DEL:
+               *ind_store_type = CEE_STIND_REF;
                return mono_ftnptr_to_delegate;
        case MONO_MARSHAL_CONV_LPSTR_SB:
+               *ind_store_type = CEE_STIND_REF;
                return mono_string_utf8_to_builder;
        case MONO_MARSHAL_CONV_LPTSTR_SB:
+               *ind_store_type = CEE_STIND_REF;
 #ifdef TARGET_WIN32
                return mono_string_utf16_to_builder;
 #else
                return mono_string_utf8_to_builder;
 #endif
        case MONO_MARSHAL_CONV_LPWSTR_SB:
+               *ind_store_type = CEE_STIND_REF;
                return mono_string_utf16_to_builder;
        case MONO_MARSHAL_FREE_ARRAY:
                return mono_marshal_free_array;
@@ -1623,6 +1669,7 @@ static void
 emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec)
 {
        int pos;
+       int stind_op;
 
        switch (conv) {
        case MONO_MARSHAL_CONV_BOOL_I4:
@@ -1657,8 +1704,8 @@ emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 1);
                mono_mb_emit_ldloc (mb, 0);
                mono_mb_emit_byte (mb, CEE_LDIND_REF);
-               mono_mb_emit_icall (mb, conv_to_icall (conv));
-               mono_mb_emit_byte (mb, CEE_STIND_I);    
+               mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+               mono_mb_emit_byte (mb, stind_op);
                break;
        }
        case MONO_MARSHAL_CONV_ARRAY_SAVEARRAY:
@@ -1667,8 +1714,8 @@ emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 1);
                mono_mb_emit_ldloc (mb, 0);
                mono_mb_emit_byte (mb, CEE_LDIND_REF);
-               mono_mb_emit_icall (mb, conv_to_icall (conv));
-               mono_mb_emit_byte (mb, CEE_STIND_I);    
+               mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+               mono_mb_emit_byte (mb, stind_op);
                break;
        case MONO_MARSHAL_CONV_STR_BYVALSTR: 
        case MONO_MARSHAL_CONV_STR_BYVALWSTR: {
@@ -1678,7 +1725,7 @@ emit_object_to_ptr_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv
                mono_mb_emit_ldloc (mb, 0);     
                mono_mb_emit_byte (mb, CEE_LDIND_REF); /* src String */
                mono_mb_emit_icon (mb, mspec->data.array_data.num_elem);
-               mono_mb_emit_icall (mb, conv_to_icall (conv));
+               mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                break;
        }
        case MONO_MARSHAL_CONV_ARRAY_BYVALARRAY: {
@@ -1940,8 +1987,9 @@ emit_struct_conv_full (MonoMethodBuilder *mb, MonoClass *klass, gboolean to_obje
                case MONO_MARSHAL_CONV_NONE: {
                        int t;
 
-                       if (ftype->byref || ftype->type == MONO_TYPE_I ||
-                           ftype->type == MONO_TYPE_U) {
+                       //XXX a byref field!?!? that's not allowed! and worse, it might miss a WB
+                       g_assert (!ftype->byref);
+                       if (ftype->type == MONO_TYPE_I || ftype->type == MONO_TYPE_U) {
                                mono_mb_emit_ldloc (mb, 1);
                                mono_mb_emit_ldloc (mb, 0);
                                mono_mb_emit_byte (mb, CEE_LDIND_I);
@@ -2188,8 +2236,12 @@ mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params)
                        MonoArray *out_args;
                        method = delegate->method;
 
-                       msg = mono_method_call_message_new (mono_marshal_method_from_wrapper (method), params, NULL, &async_callback, &state);
-                       ares = mono_async_result_new (mono_domain_get (), NULL, state, NULL, NULL);
+                       msg = mono_method_call_message_new (mono_marshal_method_from_wrapper (method), params, NULL, &async_callback, &state, &error);
+                       if (mono_error_set_pending_exception (&error))
+                               return NULL;
+                       ares = mono_async_result_new (mono_domain_get (), NULL, state, NULL, NULL, &error);
+                       if (mono_error_set_pending_exception (&error))
+                               return NULL;
                        MONO_OBJECT_SETREF (ares, async_delegate, (MonoObject *)delegate);
                        MONO_OBJECT_SETREF (ares, async_callback, (MonoObject *)async_callback);
                        MONO_OBJECT_SETREF (msg, async_result, ares);
@@ -2916,7 +2968,9 @@ mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params)
 
        sig = mono_signature_no_pinvoke (method);
 
-       msg = mono_method_call_message_new (method, params, NULL, NULL, NULL);
+       msg = mono_method_call_message_new (method, params, NULL, NULL, NULL, &error);
+       if (mono_error_set_pending_exception (&error))
+               return NULL;
 
        ares = (MonoAsyncResult *)mono_array_get (msg->args, gpointer, sig->param_count - 1);
        if (ares == NULL) {
@@ -2938,7 +2992,9 @@ mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params)
                        mono_error_set_pending_exception (&error);
                        return NULL;
                }
-               mono_message_init (domain, msg, delegate->method_info, NULL);
+               mono_message_init (domain, msg, delegate->method_info, NULL, &error);
+               if (mono_error_set_pending_exception (&error))
+                       return NULL;
                msg->call_type = CallType_EndInvoke;
                MONO_OBJECT_SETREF (msg, async_result, ares);
                res = mono_remoting_invoke ((MonoObject *)tp->rp, msg, &exc, &out_args, &error);
@@ -2949,7 +3005,9 @@ mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params)
        } else
 #endif
        {
-               res = mono_threadpool_ms_end_invoke (ares, &out_args, &exc);
+               res = mono_threadpool_ms_end_invoke (ares, &out_args, &exc, &error);
+               if (mono_error_set_pending_exception (&error))
+                       return NULL;
        }
 
        if (exc) {
@@ -2964,7 +3022,8 @@ mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params)
                mono_set_pending_exception ((MonoException*)exc);
        }
 
-       mono_method_return_message_restore (method, params, out_args);
+       mono_method_return_message_restore (method, params, out_args, &error);
+       mono_error_set_pending_exception (&error);
        return res;
 }
 
@@ -5109,7 +5168,7 @@ emit_marshal_string (EmitMarshalContext *m, int argnum, MonoType *t,
                        char *msg = g_strdup_printf ("string marshalling conversion %d not implemented", encoding);
                        mono_mb_emit_exception_marshal_directive (mb, msg);
                } else {
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
 
                        mono_mb_emit_stloc (mb, conv_arg);
                }
@@ -5150,10 +5209,11 @@ emit_marshal_string (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_icall (mb, mono_string_new_len_wrapper);
                        mono_mb_emit_byte (mb, CEE_STIND_REF);
                } else if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
+                       int stind_op;
                        mono_mb_emit_ldarg (mb, argnum);
                        mono_mb_emit_ldloc (mb, conv_arg);
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
-                       mono_mb_emit_byte (mb, CEE_STIND_REF);
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+                       mono_mb_emit_byte (mb, stind_op);
                        need_free = TRUE;
                }
 
@@ -5184,7 +5244,7 @@ emit_marshal_string (EmitMarshalContext *m, int argnum, MonoType *t,
                }
 
                mono_mb_emit_ldloc (mb, 0);
-               mono_mb_emit_icall (mb, conv_to_icall (conv));
+               mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                mono_mb_emit_stloc (mb, 3);
 
                /* free the string */
@@ -5215,27 +5275,28 @@ emit_marshal_string (EmitMarshalContext *m, int argnum, MonoType *t,
                mono_mb_emit_ldarg (mb, argnum);
                if (t->byref)
                        mono_mb_emit_byte (mb, CEE_LDIND_I);
-               mono_mb_emit_icall (mb, conv_to_icall (conv));
+               mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                mono_mb_emit_stloc (mb, conv_arg);
                break;
 
        case MARSHAL_ACTION_MANAGED_CONV_OUT:
                if (t->byref) {
                        if (conv_arg) {
+                               int stind_op;
                                mono_mb_emit_ldarg (mb, argnum);
                                mono_mb_emit_ldloc (mb, conv_arg);
-                               mono_mb_emit_icall (mb, conv_to_icall (conv));
-                               mono_mb_emit_byte (mb, CEE_STIND_I);
+                               mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+                               mono_mb_emit_byte (mb, stind_op);
                        }
                }
                break;
 
        case MARSHAL_ACTION_MANAGED_CONV_RESULT:
-               if (conv_to_icall (conv) == mono_marshal_string_to_utf16)
+               if (conv_to_icall (conv, NULL) == mono_marshal_string_to_utf16)
                        /* We need to make a copy so the caller is able to free it */
                        mono_mb_emit_icall (mb, mono_marshal_string_to_utf16_copy);
                else
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                mono_mb_emit_stloc (mb, 3);
                break;
 
@@ -5514,7 +5575,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                                mono_mb_emit_stloc (mb, conv_arg);
                        } else {
                                mono_mb_emit_ldarg (mb, argnum);
-                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN));
+                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL));
                                mono_mb_emit_stloc (mb, conv_arg);
                        }
                } else if (klass == mono_defaults.stringbuilder_class) {
@@ -5544,7 +5605,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                        if (t->byref)
                                mono_mb_emit_byte (mb, CEE_LDIND_I);
 
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                        mono_mb_emit_stloc (mb, conv_arg);
                } else if (klass->blittable) {
                        mono_mb_emit_byte (mb, CEE_LDNULL);
@@ -5646,7 +5707,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                                mono_mb_emit_ldarg (mb, argnum);
                                mono_mb_emit_ldloc (mb, conv_arg);
 
-                               mono_mb_emit_icall (mb, conv_to_icall (conv));
+                               mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                        }
 
                        if (need_free) {
@@ -5662,7 +5723,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                                mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
                                mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass);
                                mono_mb_emit_ldloc (mb, conv_arg);
-                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL));
+                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL));
                                mono_mb_emit_byte (mb, CEE_STIND_REF);
                        }
                        break;
@@ -5745,7 +5806,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
                        mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass);
                        mono_mb_emit_ldloc (mb, 0);
-                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL));
+                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL));
                        mono_mb_emit_stloc (mb, 3);
                } else {
                        /* set src */
@@ -5795,7 +5856,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_ldarg (mb, argnum);
                        if (t->byref)
                                mono_mb_emit_byte (mb, CEE_LDIND_I);
-                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL));
+                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL));
                        mono_mb_emit_stloc (mb, conv_arg);
                        break;
                }
@@ -5869,10 +5930,11 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
        case MARSHAL_ACTION_MANAGED_CONV_OUT:
                if (klass->delegate) {
                        if (t->byref) {
+                               int stind_op;
                                mono_mb_emit_ldarg (mb, argnum);
                                mono_mb_emit_ldloc (mb, conv_arg);
-                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN));
-                               mono_mb_emit_byte (mb, CEE_STIND_I);
+                               mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, &stind_op));
+                               mono_mb_emit_byte (mb, stind_op);
                                break;
                        }
                }
@@ -5931,7 +5993,7 @@ emit_marshal_object (EmitMarshalContext *m, int argnum, MonoType *t,
 
        case MARSHAL_ACTION_MANAGED_CONV_RESULT:
                if (klass->delegate) {
-                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN));
+                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL));
                        mono_mb_emit_stloc (mb, 3);
                        break;
                }
@@ -6148,7 +6210,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_ldarg (mb, argnum);
                        if (t->byref)
                                mono_mb_emit_byte (mb, CEE_LDIND_I);
-                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_ARRAY_LPARRAY));
+                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_ARRAY_LPARRAY, NULL));
                        mono_mb_emit_stloc (mb, conv_arg);
                } else {
                        MonoClass *eklass;
@@ -6228,12 +6290,13 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                        /* Emit marshalling code */
 
                        if (is_string) {
+                               int stind_op;
                                mono_mb_emit_ldloc (mb, dest_ptr);
                                mono_mb_emit_ldloc (mb, src_var);
                                mono_mb_emit_ldloc (mb, index_var);
                                mono_mb_emit_byte (mb, CEE_LDELEM_REF);
-                               mono_mb_emit_icall (mb, conv_to_icall (conv));
-                               mono_mb_emit_byte (mb, CEE_STIND_I);
+                               mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+                               mono_mb_emit_byte (mb, stind_op);
                        } else {
                                /* set the src_ptr */
                                mono_mb_emit_ldloc (mb, src_var);
@@ -6297,7 +6360,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                                mono_mb_emit_byte (mb, CEE_CONV_OVF_I);
                                mono_mb_emit_op (mb, CEE_NEWARR, klass->element_class);
                                /* Store into argument */
-                               mono_mb_emit_byte (mb, CEE_STIND_I);
+                               mono_mb_emit_byte (mb, CEE_STIND_REF);
                        }
                }
 
@@ -6357,7 +6420,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                                mono_mb_emit_ldloc (mb, src_ptr);
                                mono_mb_emit_byte (mb, CEE_LDIND_I);
 
-                               mono_mb_emit_icall (mb, conv_to_icall (conv));
+                               mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
 
                                if (need_free) {
                                        /* src */
@@ -6419,7 +6482,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                        if (t->byref)
                                mono_mb_emit_byte (mb, CEE_LDIND_REF);
                        mono_mb_emit_ldloc (mb, conv_arg);
-                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_FREE_LPARRAY));
+                       mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_FREE_LPARRAY, NULL));
                }
 
                break;
@@ -6594,7 +6657,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_ldloc (mb, src_ptr);
                        mono_mb_emit_byte (mb, CEE_LDIND_I);
 
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, NULL));
                        mono_mb_emit_byte (mb, CEE_STELEM_REF);
                }
                else {
@@ -6702,6 +6765,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
 
                /* Emit marshalling code */
                if (is_string) {
+                       int stind_op;
                        g_assert (conv != MONO_MARSHAL_CONV_INVALID);
 
                        /* dest */
@@ -6713,8 +6777,8 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
 
                        mono_mb_emit_byte (mb, CEE_LDELEM_REF);
 
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
-                       mono_mb_emit_byte (mb, CEE_STIND_I);
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+                       mono_mb_emit_byte (mb, stind_op);
                }
                else {
                        char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented.");
@@ -6799,6 +6863,7 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
 
                /* Emit marshalling code */
                if (is_string) {
+                       int stind_op;
                        g_assert (conv != MONO_MARSHAL_CONV_INVALID);
 
                        /* dest */
@@ -6810,8 +6875,8 @@ emit_marshal_array (EmitMarshalContext *m, int argnum, MonoType *t,
 
                        mono_mb_emit_byte (mb, CEE_LDELEM_REF);
 
-                       mono_mb_emit_icall (mb, conv_to_icall (conv));
-                       mono_mb_emit_byte (mb, CEE_STIND_I);
+                       mono_mb_emit_icall (mb, conv_to_icall (conv, &stind_op));
+                       mono_mb_emit_byte (mb, stind_op);
                }
                else {
                        char *msg = g_strdup ("Marshalling of non-string arrays to managed code is not implemented.");
@@ -7237,12 +7302,10 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
        EmitMarshalContext m;
        MonoMethodSignature *csig;
        MonoClass *klass;
-       MonoExceptionClause *clause;
        int i, argnum, *tmp_locals;
        int type, param_shift = 0;
        static MonoMethodSignature *get_last_error_sig = NULL;
-       int coop_gc_stack_dummy, coop_gc_var, coop_unblocked_var;
-       int leave_pos;
+       int coop_gc_stack_dummy, coop_gc_var;
 
        memset (&m, 0, sizeof (m));
        m.mb = mb;
@@ -7285,12 +7348,20 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                coop_gc_stack_dummy = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
                /* local 5, the local to be used when calling the suspend funcs */
                coop_gc_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-               coop_unblocked_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-
-               clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
-               clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY;
        }
 
+       /*
+        * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy);
+        *
+        * ret = method (...);
+        *
+        * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy);
+        *
+        * <interrupt check>
+        *
+        * return ret;
+        */
+
        if (MONO_TYPE_ISSTRUCT (sig->ret))
                m.vtaddr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
 
@@ -7322,10 +7393,8 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                        mono_mb_emit_byte (mb, CEE_POP); // Result not needed yet
                }
 
-               clause->try_offset = mono_mb_get_label (mb);
-
                mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy);
-               mono_mb_emit_icall (mb, mono_threads_prepare_blocking);
+               mono_mb_emit_icall (mb, mono_threads_enter_gc_safe_region_unbalanced);
                mono_mb_emit_stloc (mb, coop_gc_var);
        }
 
@@ -7350,8 +7419,7 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
 #else
                g_assert_not_reached ();
 #endif
-       }
-       else {
+       } else {
                if (aot) {
                        /* Reuse the ICALL_ADDR opcode for pinvokes too */
                        mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
@@ -7362,6 +7430,24 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                }
        }
 
+       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+               MonoClass *klass = mono_class_from_mono_type (sig->ret);
+               mono_class_init (klass);
+               if (!(((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) || klass->blittable)) {
+                       /* This is used by emit_marshal_vtype (), but it needs to go right before the call */
+                       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+                       mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
+                       mono_mb_emit_stloc (mb, m.vtaddr_var);
+               }
+       }
+
+       /* Unblock before converting the result, since that can involve calls into the runtime */
+       if (mono_threads_is_coop_enabled ()) {
+               mono_mb_emit_ldloc (mb, coop_gc_var);
+               mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy);
+               mono_mb_emit_icall (mb, mono_threads_exit_gc_safe_region_unbalanced);
+       }
+
        /* Set LastError if needed */
        if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
                if (!get_last_error_sig) {
@@ -7371,7 +7457,7 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                }
 
 #ifdef TARGET_WIN32
-               /* 
+               /*
                 * Have to call GetLastError () early and without a wrapper, since various runtime components could
                 * clobber its value.
                 */
@@ -7380,26 +7466,6 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
 #else
                mono_mb_emit_icall (mb, mono_marshal_set_last_error);
 #endif
-       }               
-
-       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
-               MonoClass *klass = mono_class_from_mono_type (sig->ret);
-               mono_class_init (klass);
-               if (!(((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) || klass->blittable)) {
-                       /* This is used by emit_marshal_vtype (), but it needs to go right before the call */
-                       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
-                       mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
-                       mono_mb_emit_stloc (mb, m.vtaddr_var);
-               }
-       }
-
-       /* Unblock before converting the result, since that can involve calls into the runtime */
-       if (mono_threads_is_coop_enabled ()) {
-               mono_mb_emit_ldloc (mb, coop_gc_var);
-               mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy);
-               mono_mb_emit_icall (mb, mono_threads_finish_blocking);
-               mono_mb_emit_icon (mb, 1);
-               mono_mb_emit_stloc (mb, coop_unblocked_var);
        }
 
        /* convert the result */
@@ -7456,31 +7522,6 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                mono_mb_emit_stloc (mb, 3);
        }
 
-       if (mono_threads_is_coop_enabled ()) {
-               int pos;
-
-               leave_pos = mono_mb_emit_branch (mb, CEE_LEAVE);
-
-               clause->try_len = mono_mb_get_label (mb) - clause->try_offset;
-               clause->handler_offset = mono_mb_get_label (mb);
-
-               mono_mb_emit_ldloc (mb, coop_unblocked_var);
-               mono_mb_emit_icon (mb, 1);
-               pos = mono_mb_emit_branch (mb, CEE_BEQ);
-
-               mono_mb_emit_ldloc (mb, coop_gc_var);
-               mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy);
-               mono_mb_emit_icall (mb, mono_threads_finish_blocking);
-
-               mono_mb_patch_branch (mb, pos);
-
-               mono_mb_emit_byte (mb, CEE_ENDFINALLY);
-
-               clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
-
-               mono_mb_patch_branch (mb, leave_pos);
-       }
-
        /* 
         * Need to call this after converting the result since MONO_VTADDR needs 
         * to be adjacent to the call instruction.
@@ -7518,10 +7559,6 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                mono_mb_emit_ldloc (mb, 3);
 
        mono_mb_emit_byte (mb, CEE_RET);
-
-       if (mono_threads_is_coop_enabled ()) {
-               mono_mb_set_clauses (mb, 1, clause);
-       }
 }
 #endif /* DISABLE_JIT */
 
@@ -7927,9 +7964,9 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
        }
 #else
        MonoMethodSignature *sig, *csig;
-       MonoExceptionClause *clause;
-       int i, *tmp_locals;
-       int leave_pos;
+       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;
@@ -7960,43 +7997,64 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
        if (MONO_TYPE_ISSTRUCT (sig->ret))
                m->vtaddr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
 
+       ex_local = mono_mb_add_local (mb, &mono_defaults.uint32_class->byval_arg);
+       e_local = mono_mb_add_local (mb, &mono_defaults.exception_class->byval_arg);
+
+       attach_cookie_local = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+       attach_dummy_local = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+
        /*
+        * guint32 ex = -1;
         * try {
-        *   mono_jit_attach ();
+        *   // 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 {
-        *   mono_jit_detach ();
+        *   // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain
+        *   mono_threads_detach_coop ();
+        *
+        *   if (ex != -1)
+        *     mono_marshal_ftnptr_eh_callback (ex);
         * }
         *
         * return ret;
         */
 
-       if (mono_threads_is_coop_enabled ()) {
-               clause = g_new0 (MonoExceptionClause, 1);
-               clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY;
-       }
+       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);
 
-       if (mono_threads_is_coop_enabled ()) {
-               /* try { */
-               clause->try_offset = mono_mb_get_label (mb);
-       }
+       mono_mb_emit_icon (mb, -1);
+       mono_mb_emit_byte (mb, CEE_CONV_U4);
+       mono_mb_emit_stloc (mb, ex_local);
 
-       /*
-        * Might need to attach the thread to the JIT or change the
-        * domain for the callback.
-        *
-        * Also does the (STARTING|BLOCKING|RUNNING) -> RUNNING thread state transtion
-        *
-        * mono_jit_attach ();
-        */
-       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
-       mono_mb_emit_byte (mb, CEE_MONO_JIT_ATTACH);
+       /* try { */
+       clause_catch->try_offset = clause_finally->try_offset = mono_mb_get_label (mb);
+
+       if (!mono_threads_is_coop_enabled ()) {
+               mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+               mono_mb_emit_byte (mb, CEE_MONO_JIT_ATTACH);
+       } else {
+               /* mono_threads_attach_coop (); */
+               mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+               mono_mb_emit_byte (mb, CEE_MONO_LDDOMAIN);
+               mono_mb_emit_ldloc_addr (mb, attach_dummy_local);
+               mono_mb_emit_icall (mb, mono_threads_attach_coop);
+               mono_mb_emit_stloc (mb, attach_cookie_local);
+       }
 
        /* <interrupt check> */
        emit_thread_interrupt_checkpoint (mb);
@@ -8139,31 +8197,62 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
                }
        }
 
-       if (mono_threads_is_coop_enabled ()) {
-               leave_pos = mono_mb_emit_branch (mb, CEE_LEAVE);
+       leave_try_pos = mono_mb_emit_branch (mb, CEE_LEAVE);
 
-               /* } finally { */
-               clause->try_len = mono_mb_get_label (mb) - clause->try_offset;
-               clause->handler_offset = mono_mb_get_label (mb);
-       }
+       /* } [endtry] */
 
-       /*
-        * Also does the RUNNING -> (BLOCKING|RUNNING) thread state transition
-        *
-        * mono_jit_detach ();
-        */
-       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
-       mono_mb_emit_byte (mb, CEE_MONO_JIT_DETACH);
+       /* 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);
 
-       if (mono_threads_is_coop_enabled ()) {
-               mono_mb_emit_byte (mb, CEE_ENDFINALLY);
+       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);
 
-               /* } [endfinally] */
-               clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+       /* } [endcatch] */
+       clause_catch->handler_len = mono_mb_get_pos (mb) - clause_catch->handler_offset;
 
-               mono_mb_patch_branch (mb, leave_pos);
+       /* 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_coop_enabled ()) {
+               mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+               mono_mb_emit_byte (mb, CEE_MONO_JIT_DETACH);
+       } else {
+               /* mono_threads_detach_coop (); */
+               mono_mb_emit_ldloc (mb, attach_cookie_local);
+               mono_mb_emit_ldloc_addr (mb, attach_dummy_local);
+               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);
@@ -8176,9 +8265,7 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
                mono_mb_emit_byte (mb, CEE_RET);
        }
 
-       if (mono_threads_is_coop_enabled ()) {
-               mono_mb_set_clauses (mb, 1, clause);
-       }
+       mono_mb_set_clauses (mb, 2, clauses);
 
        if (closed)
                g_free (sig);
@@ -8444,7 +8531,9 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type)
                                mono_metadata_free_marshal_spec (mspecs [i]);
                g_free (mspecs);
 
-               return mono_compile_method (method);
+               gpointer compiled_ptr = mono_compile_method_checked (method, &error);
+               mono_error_assert_ok (&error);
+               return compiled_ptr;
        }
 
        sig = mono_method_signature (method);
@@ -8467,7 +8556,9 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type)
        method = mono_mb_create (mb, sig, param_count, NULL);
        mono_mb_free (mb);
 
-       return mono_compile_method (method);
+       gpointer compiled_ptr = mono_compile_method_checked (method, &error);
+       mono_error_assert_ok (&error);
+       return compiled_ptr;
 }
 
 #ifndef DISABLE_JIT
@@ -8598,12 +8689,14 @@ mono_marshal_get_castclass_with_cache (void)
        return cached;
 }
 
+/* this is an icall */
 static MonoObject *
 mono_marshal_isinst_with_cache (MonoObject *obj, MonoClass *klass, uintptr_t *cache)
 {
        MonoError error;
        MonoObject *isinst = mono_object_isinst_checked (obj, klass, &error);
-       mono_error_raise_exception (&error); /* FIXME don't raise here */
+       if (mono_error_set_pending_exception (&error))
+               return NULL;
 
 #ifndef DISABLE_REMOTING
        if (obj->vtable->klass == mono_defaults.transparent_proxy_class)
@@ -8665,7 +8758,7 @@ mono_marshal_get_isinst_with_cache (void)
 
        // return obj
        mono_mb_patch_branch (mb, positive_cache_hit_pos);
-       mono_mb_emit_ldarg (mb, 0);
+       mono_mb_emit_ldarg (mb, obj_arg_position);
        mono_mb_emit_byte (mb, CEE_RET);
 #endif
 
@@ -9172,7 +9265,7 @@ mono_marshal_get_synchronized_wrapper (MonoMethod *method)
        if (!enter_method) {
                MonoMethodDesc *desc;
 
-               desc = mono_method_desc_new ("Monitor:enter_with_atomic_var(object,bool&)", FALSE);
+               desc = mono_method_desc_new ("Monitor:Enter(object,bool&)", FALSE);
                enter_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class);
                g_assert (enter_method);
                mono_method_desc_free (desc);
@@ -10485,12 +10578,15 @@ ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi (char *ptr)
 MonoString *
 ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi_len (char *ptr, gint32 len)
 {
-       if (ptr == NULL) {
-               mono_set_pending_exception (mono_get_exception_argument_null ("ptr"));
-               return NULL;
-       } else {
-               return mono_string_new_len (mono_domain_get (), ptr, len);
-       }
+       MonoError error;
+       MonoString *result = NULL;
+       mono_error_init (&error);
+       if (ptr == NULL)
+               mono_error_set_argument_null (&error, "ptr", "");
+       else
+               result = mono_string_new_len_checked (mono_domain_get (), ptr, len, &error);
+       mono_error_set_pending_exception (&error);
+       return result;
 }
 
 MonoString *
@@ -11596,10 +11692,12 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
        GHashTable *cache;
        MonoMethod *res;
        int i, param_count, sig_size, pos_leave;
-       int coop_gc_var, coop_gc_dummy_local;
 
        g_assert (method);
 
+       // FIXME: we need to store the exception into a MonoHandle
+       g_assert (!mono_threads_is_coop_enabled ());
+
        klass = method->klass;
        image = method->klass->image;
 
@@ -11649,26 +11747,11 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
        if (!MONO_TYPE_IS_VOID (sig->ret))
                mono_mb_add_local (mb, sig->ret);
 
-       if (mono_threads_is_coop_enabled ()) {
-               /* local 4, the local to be used when calling the reset_blocking funcs */
-               /* tons of code hardcode 3 to be the return var */
-               coop_gc_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-               /* local 5, the local used to get a stack address for suspend funcs */
-               coop_gc_dummy_local = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-       }
-
        /* clear exception arg */
        mono_mb_emit_ldarg (mb, param_count - 1);
        mono_mb_emit_byte (mb, CEE_LDNULL);
        mono_mb_emit_byte (mb, CEE_STIND_REF);
 
-       if (mono_threads_is_coop_enabled ()) {
-               /* FIXME this is technically wrong as the callback itself must be executed in gc unsafe context. */
-               mono_mb_emit_ldloc_addr (mb, coop_gc_dummy_local);
-               mono_mb_emit_icall (mb, mono_threads_reset_blocking_start);
-               mono_mb_emit_stloc (mb, coop_gc_var);
-       }
-
        /* try */
        clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
        clause->try_offset = mono_mb_get_label (mb);
@@ -11738,13 +11821,6 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
                        mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (sig->ret));
        }
 
-       if (mono_threads_is_coop_enabled ()) {
-               /* XXX merge reset_blocking_end with detach */
-               mono_mb_emit_ldloc (mb, coop_gc_var);
-               mono_mb_emit_ldloc_addr (mb, coop_gc_dummy_local);
-               mono_mb_emit_icall (mb, mono_threads_reset_blocking_end);
-       }
-
        mono_mb_emit_byte (mb, CEE_RET);
 #endif
 
@@ -11784,3 +11860,42 @@ mono_marshal_free_dynamic_wrappers (MonoMethod *method)
        if (marshal_mutex_initialized)
                mono_marshal_unlock ();
 }
+
+static 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;
+
+       g_assert (gchandle >= 0);
+
+       mono_threads_enter_gc_unsafe_region_unbalanced (&stackdata);
+
+       exc = (MonoException*) mono_gchandle_get_target (gchandle);
+
+       mono_gchandle_free (gchandle);
+
+       mono_raise_exception (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;
+}