+2010-03-30 Rodrigo Kumpera <rkumpera@novell.com>
+
+ * mini-exceptions.c: Introduce mono_walk_stack_full, which
+ allows to select if it's new or old context that is passed to
+ the callback.
+
+ * mini-exceptions.c (mono_handle_exception_internal): Handle the
+ case when executing a guarded handler from the EH machinery.
+
+ * mini-exceptions.c (mono_install_handler_block_guard): New function
+ responsible for checking for handler blocks, installing the guard and
+ clearing abort state.
+
+ * mini-posix.c (sigusr1_signal_handler): Call mono_install_handler_block_guard
+ to check for handler blocks and skip interruption logic if one was found.
+
+ * mini-trampolines.c (mono_handler_block_guard_trampoline): Function called
+ by the handler block guard trampoline. Resumes interruption by raising the
+ pending ThreadAbortException.
+
+ * mini.c (create_jit_info): Calculate the end address of a finally block.
+
+ * mini-x86.c (mono_arch_install_handler_block_guard): Patch the return address
+ of a finally block to a specified address and return the old one.
+
+ * tramp-x86.c (mono_arch_create_handler_block_trampoline): The handler block
+ trampoline patches the original return address and calls the trampoline function.
+
2010-03-30 Zoltan Varga <vargaz@gmail.com>
* mini-trampolines.c (mono_aot_trampoline): Remove some dead code.
encode_value (ei->flags, p, &p);
encode_value (ei->exvar_offset, p, &p);
- if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
encode_value ((gint)((guint8*)ei->data.filter - code), p, &p);
else {
if (ei->data.catch_class) {
ei->exvar_offset = decode_value (p, &p);
- if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
ei->data.filter = code + decode_value (p, &p);
else {
if (decode_value (p, &p))
static void try_more_restore (void);
static void restore_stack_protection (void);
+static void mono_walk_stack_full (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gboolean use_new_ctx, gpointer user_data);
void
mono_exceptions_init (void)
*/
void
mono_walk_stack (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gpointer user_data)
+{
+ mono_walk_stack_full (domain, jit_tls, start_ctx, func, TRUE, user_data);
+}
+
+static void
+mono_walk_stack_full (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gboolean use_new_ctx, gpointer user_data)
{
MonoLMF *lmf = mono_get_lmf ();
MonoJitInfo *ji, rji;
if (!ji || ji == (gpointer)-1)
return;
- if (func (domain, &new_ctx, ji, user_data))
+ if (func (domain, use_new_ctx ? &new_ctx : &ctx, ji, user_data))
return;
ctx = new_ctx;
gboolean has_dynamic_methods = FALSE;
gint32 filter_idx, first_filter_idx;
+
g_assert (ctx != NULL);
if (!obj) {
MonoException *ex = mono_get_exception_null_reference ();
return TRUE;
}
+ /*
+ * This guards against the situation that we abort a thread that is executing a finally clause
+ * that was called by the EH machinery. It won't have a guard trampoline installed, so we must
+ * check for this situation here and resume interruption if we are below the guarded block.
+ */
+ if (G_UNLIKELY (jit_tls->handler_block_return_address)) {
+ gboolean is_outside = FALSE;
+ gpointer prot_bp = MONO_CONTEXT_GET_BP (&jit_tls->ex_ctx);
+ gpointer catch_bp = MONO_CONTEXT_GET_BP (ctx);
+ //FIXME make this stack direction aware
+ if (catch_bp > prot_bp) {
+ is_outside = TRUE;
+ } else if (catch_bp == prot_bp) {
+ /* Can be either try { try { } catch {} } finally {} or try { try { } finally {} } catch {}
+ * So we check if the catch handler_start is protected by the guarded handler protected region
+ *
+ * Assumptions:
+ * If there is an outstanding guarded_block return address, it means the current thread must be aborted.
+ * This is the only way to reach out the guarded block as other cases are handled by the trampoline.
+ * There aren't any further finally/fault handler blocks down the stack over this exception.
+ * This must be ensured by the code that installs the guard trampoline.
+ */
+ g_assert (ji == mini_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (&jit_tls->ex_ctx), NULL));
+
+ if (!is_address_protected (ji, jit_tls->handler_block, ei->handler_start)) {
+ is_outside = TRUE;
+ }
+ }
+ if (is_outside) {
+ jit_tls->handler_block_return_address = NULL;
+ jit_tls->handler_block = NULL;
+ mono_thread_resume_interruption (); /*We ignore the exception here, it will be raised later*/
+ }
+ }
+
if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
restore_context (&ctx);
}
+
+#ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
+
+typedef struct {
+ MonoJitInfo *ji;
+ MonoContext ctx;
+ MonoJitExceptionInfo *ei;
+} FindHandlerBlockData;
+
+static gboolean
+find_last_handler_block (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
+{
+ int i;
+ gpointer ip;
+ FindHandlerBlockData *pdata = data;
+
+ if (ji->method->wrapper_type)
+ return FALSE;
+
+ ip = MONO_CONTEXT_GET_IP (ctx);
+
+ for (i = 0; i < ji->num_clauses; ++i) {
+ MonoJitExceptionInfo *ei = ji->clauses + i;
+ if (ei->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
+ continue;
+ /*If ip points to the first instruction it means the handler block didn't start
+ so we can leave its execution to the EH machinery*/
+ if (ei->handler_start < ip && ip < ei->data.handler_end) {
+ pdata->ji = ji;
+ pdata->ei = ei;
+ pdata->ctx = *ctx;
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+static gpointer
+install_handler_block_guard (MonoJitInfo *ji, MonoContext *ctx)
+{
+ int i;
+ MonoJitExceptionInfo *clause = NULL;
+ gpointer ip;
+
+ ip = MONO_CONTEXT_GET_IP (ctx);
+
+ for (i = 0; i < ji->num_clauses; ++i) {
+ clause = &ji->clauses [i];
+ if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
+ continue;
+ if (clause->handler_start < ip && clause->data.handler_end > ip)
+ break;
+ }
+
+ /*no matching finally */
+ if (i == ji->num_clauses)
+ return NULL;
+
+ /*If we stopped on the instruction right before the try, we haven't actually started executing it*/
+ if (ip == clause->handler_start)
+ return NULL;
+
+ return mono_arch_install_handler_block_guard (ji, clause, ctx, mono_create_handler_block_trampoline ());
+}
+
+/*
+ * Finds the bottom handler block running and install a block guard if needed.
+ */
+gboolean
+mono_install_handler_block_guard (MonoInternalThread *thread, MonoContext *ctx)
+{
+ FindHandlerBlockData data = { 0 };
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ gpointer resume_ip;
+
+ if (jit_tls->handler_block_return_address)
+ return FALSE;
+
+ mono_walk_stack_full (domain, jit_tls, ctx, find_last_handler_block, FALSE, &data);
+
+ if (!data.ji)
+ return FALSE;
+
+ memcpy (&jit_tls->ex_ctx, &data.ctx, sizeof (MonoContext));
+
+ resume_ip = install_handler_block_guard (data.ji, &data.ctx);
+ if (resume_ip == NULL)
+ return FALSE;
+
+ jit_tls->handler_block_return_address = resume_ip;
+ jit_tls->handler_block = data.ei;
+ /*Clear current thread from been wapi interrupted otherwise things can go south*/
+ wapi_clear_interruption ();
+
+ return TRUE;
+}
+
+#else
+gboolean
+mono_install_handler_block_guard (MonoInternalThread *thread, MonoContext *ctx)
+{
+ return FALSE;
+}
+
+#endif
+
static void
SIG_HANDLER_SIGNATURE (sigusr1_signal_handler)
{
+ MonoContext mctx;
gboolean running_managed;
MonoException *exc;
MonoInternalThread *thread = mono_thread_internal_current ();
} else {
running_managed = FALSE;
}
-
+
+ /* We can't do handler block checking from metadata since it requires doing
+ * a stack walk with context.
+ *
+ * FIXME add full-aot support.
+ */
+ if (!mono_aot_only) {
+ mono_arch_sigctx_to_monoctx (ctx, &mctx);
+ if (mono_install_handler_block_guard (thread, &mctx)) {
+ return;
+ }
+ }
+
exc = mono_thread_request_interruption (running_managed);
if (!exc)
return;
#endif
+static gpointer
+mono_handler_block_guard_trampoline (mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
+{
+ MonoContext ctx;
+ MonoException *exc;
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ gpointer resume_ip = jit_tls->handler_block_return_address;
+
+ memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
+ MONO_CONTEXT_SET_IP (&ctx, jit_tls->handler_block_return_address);
+
+ jit_tls->handler_block_return_address = NULL;
+ jit_tls->handler_block = NULL;
+
+ if (!resume_ip) /*this should not happen, but we should avoid crashing */
+ exc = mono_get_exception_execution_engine ("Invalid internal state, resuming abort after handler block but no resume ip found");
+ else
+ exc = mono_thread_resume_interruption ();
+
+ if (exc) {
+ static void (*restore_context) (MonoContext *);
+
+ if (!restore_context)
+ restore_context = mono_get_restore_context ();
+
+ mono_handle_exception (&ctx, exc, NULL, FALSE);
+ restore_context (&ctx);
+ }
+
+ return resume_ip;
+}
+
+gpointer
+mono_create_handler_block_trampoline (void)
+{
+ static gpointer code;
+
+ if (mono_aot_only) {
+ g_assert (0);
+ return code;
+ }
+
+ mono_trampolines_lock ();
+
+ if (!code)
+ code = mono_arch_create_handler_block_trampoline ();
+
+ mono_trampolines_unlock ();
+
+ return code;
+}
+
/*
* mono_get_trampoline_func:
*
#ifdef MONO_ARCH_LLVM_SUPPORTED
case MONO_TRAMPOLINE_LLVM_VCALL:
return mono_llvm_vcall_trampoline;
+#endif
+#ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
+ case MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD:
+ return mono_handler_block_guard_trampoline;
#endif
default:
g_assert_not_reached ();
#ifdef MONO_ARCH_LLVM_SUPPORTED
mono_trampoline_code [MONO_TRAMPOLINE_LLVM_VCALL] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_LLVM_VCALL);
#endif
+#ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
+ mono_trampoline_code [MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
+ mono_create_handler_block_trampoline ();
+#endif
mono_counters_register ("Calls to trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &trampoline_calls);
}
#endif /* MONO_ARCH_SIMD_INTRINSICS */
}
+/*MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD*/
+gpointer
+mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value)
+{
+ int offset;
+ gpointer *sp, old_value;
+ char *bp;
+ const unsigned char *handler;
+
+ /*Decode the first instruction to figure out where did we store the spvar*/
+ /*Our jit MUST generate the following:
+ mov %esp, -?(%ebp)
+ Which is encoded as: 0x89 mod_rm.
+ mod_rm (esp, ebp, imm) which can be: (imm will never be zero)
+ mod (reg + imm8): 01 reg(esp): 100 rm(ebp): 101 -> 01100101 (0x65)
+ mod (reg + imm32): 10 reg(esp): 100 rm(ebp): 101 -> 10100101 (0xA5)
+ */
+ handler = clause->handler_start;
+
+ if (*handler != 0x89)
+ return NULL;
+
+ ++handler;
+
+ if (*handler == 0x65)
+ offset = *(signed char*)(handler + 1);
+ else if (*handler == 0xA5)
+ offset = *(int*)(handler + 1);
+ else
+ return NULL;
+
+ /*Load the spvar*/
+ bp = MONO_CONTEXT_GET_BP (ctx);
+ sp = *(gpointer*)(bp + offset);
+
+ old_value = *sp;
+ if (old_value < ji->code_start || (char*)old_value > ((char*)ji->code_start + ji->code_size))
+ return old_value;
+
+ *sp = new_value;
+
+ return old_value;
+}
+
#if __APPLE__
#define DBG_SIGNAL SIGBUS
#else
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
#define MONO_ARCH_HAVE_FIND_JIT_INFO_EXT 1
#define MONO_ARCH_HAVE_EXCEPTIONS_INIT 1
+#define MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD 1
/* Used for optimization, not complete */
#define MONO_ARCH_IS_OP_MEMBASE(opcode) ((opcode) == OP_X86_PUSH_MEMBASE)
break;
}
}
+
+ if (ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
+ int end_offset;
+ if (ec->handler_offset + ec->handler_len < header->code_size) {
+ tblock = cfg->cil_offset_to_bb [ec->handler_offset + ec->handler_len];
+ g_assert (tblock);
+ end_offset = tblock->native_offset;
+ } else {
+ end_offset = cfg->epilog_begin;
+ }
+ ei->data.handler_end = cfg->native_code + end_offset;
+ }
}
if (G_UNLIKELY (cfg->verbose_level >= 4)) {
/* Used to implement --debug=casts */
MonoClass *class_cast_from, *class_cast_to;
- /* Stores state needed by mono_resume_unwind () */
+ /* Stores state needed by mono_resume_unwind () and the handler block with a guard */
MonoContext ex_ctx;
/* FIXME: GC */
gpointer ex_obj;
+ /* handle block return address */
+ gpointer handler_block_return_address;
+ /* handler block been guarded */
+ MonoJitExceptionInfo *handler_block;
} MonoJitTlsData;
typedef enum {
MONO_TRAMPOLINE_MONITOR_EXIT,
#ifdef MONO_ARCH_LLVM_SUPPORTED
MONO_TRAMPOLINE_LLVM_VCALL,
+#endif
+#ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
+ MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD,
#endif
MONO_TRAMPOLINE_NUM
} MonoTrampolineType;
gpointer mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) MONO_INTERNAL;
void mono_arch_notify_pending_exc (void) MONO_INTERNAL;
+/* Handle block guard */
+gpointer mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value) MONO_INTERNAL;
+gpointer mono_arch_create_handler_block_trampoline (void) MONO_INTERNAL;
+gpointer mono_create_handler_block_trampoline (void) MONO_INTERNAL;
+gboolean mono_install_handler_block_guard (MonoInternalThread *thread, MonoContext *ctx) MONO_INTERNAL;
+
/* Exception handling */
/* Same as MonoStackWalk, but pass the context/frame type as well */
x86_push_imm (code, func_arg);
x86_call_code (code, (guint8*)func);
}
+
+static void
+handler_block_trampoline_helper (gpointer *ptr)
+{
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ *ptr = jit_tls->handler_block_return_address;
+}
+
+gpointer
+mono_arch_create_handler_block_trampoline (void)
+{
+ guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
+ guint8 *code, *buf;
+ int tramp_size = 64;
+ code = buf = mono_global_codeman_reserve (tramp_size);
+
+ /*
+ This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
+ */
+
+ if (mono_get_jit_tls_offset () != -1) {
+ code = mono_x86_emit_tls_get (code, X86_EAX, mono_get_jit_tls_offset ());
+ x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, handler_block_return_address), 4);
+ /*simulate a call*/
+ x86_push_reg (code, X86_EAX);
+ x86_jump_code (code, tramp);
+ } else {
+ /*Slow path uses a c helper*/
+ x86_push_reg (code, X86_ESP);
+ x86_push_imm (code, tramp);
+ x86_jump_code (code, handler_block_trampoline_helper);
+ }
+
+ mono_arch_flush_icache (buf, code - buf);
+ g_assert (code - buf <= tramp_size);
+
+ return buf;
+}