#define MONO_ARCH_CONTEXT_DEF
#endif
+static gpointer restore_context_func, call_filter_func;
+static gpointer throw_exception_func, rethrow_exception_func;
+static gpointer throw_exception_by_name_func, throw_corlib_exception_func;
+
+static gpointer try_more_restore_tramp = NULL;
+static gpointer restore_stack_protection_tramp = NULL;
+
+static void try_more_restore (void);
+static void restore_stack_protection (void);
+
void
mono_exceptions_init (void)
{
-#ifndef CUSTOM_EXCEPTION_HANDLING
- mono_arch_get_restore_context ();
- mono_arch_get_call_filter ();
- mono_arch_get_throw_exception ();
- mono_arch_get_rethrow_exception ();
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ guint32 code_size;
+ MonoJumpInfo *ji;
+
+ if (mono_aot_only) {
+ restore_context_func = mono_aot_get_named_code ("restore_context");
+ call_filter_func = mono_aot_get_named_code ("call_filter");
+ throw_exception_func = mono_aot_get_named_code ("throw_exception");
+ rethrow_exception_func = mono_aot_get_named_code ("rethrow_exception");
+ } else {
+ restore_context_func = mono_arch_get_restore_context_full (&code_size, &ji, FALSE);
+ call_filter_func = mono_arch_get_call_filter_full (&code_size, &ji, FALSE);
+ throw_exception_func = mono_arch_get_throw_exception_full (&code_size, &ji, FALSE);
+ rethrow_exception_func = mono_arch_get_rethrow_exception_full (&code_size, &ji, FALSE);
+ }
+#else
+ restore_context_func = mono_arch_get_restore_context ();
+ call_filter_func = mono_arch_get_call_filter ();
+ throw_exception_func = mono_arch_get_throw_exception ();
+ rethrow_exception_func = mono_arch_get_rethrow_exception ();
+#endif
+#ifdef MONO_ARCH_HAVE_RESTORE_STACK_SUPPORT
+ try_more_restore_tramp = mono_create_specific_trampoline (try_more_restore, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
+ restore_stack_protection_tramp = mono_create_specific_trampoline (restore_stack_protection, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
+#endif
+}
+
+gpointer
+mono_get_throw_exception (void)
+{
+ g_assert (throw_exception_func);
+ return throw_exception_func;
+}
+
+gpointer
+mono_get_rethrow_exception (void)
+{
+ g_assert (rethrow_exception_func);
+ return rethrow_exception_func;
+}
+
+gpointer
+mono_get_call_filter (void)
+{
+ g_assert (call_filter_func);
+ return call_filter_func;
+}
+
+gpointer
+mono_get_restore_context (void)
+{
+ g_assert (restore_context_func);
+ return restore_context_func;
+}
+
+gpointer
+mono_get_throw_exception_by_name (void)
+{
+ gpointer code = NULL;
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ guint32 code_size;
+ MonoJumpInfo *ji;
+#endif
+
+ /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
+ if (throw_exception_by_name_func)
+ return throw_exception_by_name_func;
+
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ if (mono_aot_only)
+ code = mono_aot_get_named_code ("throw_exception_by_name");
+ else
+ code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, FALSE);
+#else
+ code = mono_arch_get_throw_exception_by_name ();
#endif
+
+ mono_memory_barrier ();
+
+ throw_exception_by_name_func = code;
+
+ return throw_exception_by_name_func;
}
-#ifndef mono_find_jit_info
+gpointer
+mono_get_throw_corlib_exception (void)
+{
+ gpointer code = NULL;
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ guint32 code_size;
+ MonoJumpInfo *ji;
+#endif
+
+ /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
+ if (throw_corlib_exception_func)
+ return throw_corlib_exception_func;
+
+#if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ if (mono_aot_only)
+ code = mono_aot_get_named_code ("throw_corlib_exception");
+ else
+ code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, FALSE);
+#else
+ code = mono_arch_get_throw_corlib_exception ();
+#endif
+#else
+ g_assert_not_reached ();
+#endif
+
+ mono_memory_barrier ();
+
+ throw_corlib_exception_func = code;
+
+ return throw_corlib_exception_func;
+}
/* mono_find_jit_info:
*
* the @lmf if necessary. @native_offset return the IP offset from the
* start of the function or -1 if that info is not available.
*/
-static MonoJitInfo *
+MonoJitInfo *
mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
gboolean *managed)
if (managed)
*managed = FALSE;
- ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, NULL, lmf, NULL, &managed2);
+ ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
if (ji == (gpointer)-1)
return ji;
return ji;
}
-#endif /* mono_find_jit_info */
-
MonoString *
ves_icall_System_Exception_get_trace (MonoException *ex)
{
sf->il_offset = 0;
if (need_file_info) {
- if (location) {
+ if (location && location->source_file) {
MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
sf->line = location->row;
sf->column = location->column;
}
}
-#ifndef CUSTOM_STACK_WALK
-
void
mono_jit_walk_stack_from_ctx (MonoStackWalk func, MonoContext *start_ctx, gboolean do_il_offset, gpointer user_data)
{
return TRUE;
}
-#endif /* CUSTOM_STACK_WALK */
-
typedef struct {
guint32 skips;
MonoSecurityFrame *frame;
return ss.stack;
}
-#ifndef CUSTOM_EXCEPTION_HANDLING
-
static MonoClass*
get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContext *ctx)
{
MonoClass *catch_class = ei->data.catch_class;
+ MonoGenericJitInfo *gi;
+ gpointer info;
+ MonoClass *class, *method_container_class;
+ MonoType *inflated_type;
+ MonoGenericContext context = { NULL, NULL };
- if (ji->has_generic_jit_info) {
- MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
- gpointer info;
- MonoClass *class;
- MonoType *inflated_type;
+ if (!catch_class)
+ return NULL;
- if (gi->this_in_reg)
- info = mono_arch_context_get_int_reg (ctx, gi->this_reg);
- else
- info = *(gpointer*)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
- gi->this_offset);
+ if (!ji->has_generic_jit_info)
+ return catch_class;
- if (ji->method->flags & METHOD_ATTRIBUTE_STATIC) {
- MonoVTable *vtable = info;
+ gi = mono_jit_info_get_generic_jit_info (ji);
+ if (!gi->has_this)
+ return catch_class;
- class = vtable->klass;
- } else {
- MonoObject *this = info;
+ if (gi->this_in_reg)
+ info = mono_arch_context_get_int_reg (ctx, gi->this_reg);
+ else
+ info = *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
+ gi->this_offset);
- class = this->vtable->klass;
- }
+ g_assert (ji->method->is_inflated);
- /* FIXME: we shouldn't inflate but instead put the
- type in the rgctx and fetch it from there. It
- might be a good idea to do this lazily, i.e. only
- when the exception is actually thrown, so as not to
- waste space for exception clauses which might never
- be encountered. */
- inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg,
- mini_class_get_context (class));
- catch_class = mono_class_from_mono_type (inflated_type);
- g_free (inflated_type);
+ if (mono_method_get_context (ji->method)->method_inst) {
+ MonoMethodRuntimeGenericContext *mrgctx = info;
+
+ class = mrgctx->class_vtable->klass;
+ context.method_inst = mrgctx->method_inst;
+ g_assert (context.method_inst);
+ } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) {
+ MonoVTable *vtable = info;
+
+ class = vtable->klass;
+ } else {
+ MonoObject *this = info;
+
+ class = this->vtable->klass;
}
+ if (class->generic_class || class->generic_container)
+ context.class_inst = mini_class_get_context (class)->class_inst;
+
+ g_assert (!ji->method->klass->generic_container);
+ if (ji->method->klass->generic_class)
+ method_container_class = ji->method->klass->generic_class->container_class;
+ else
+ method_container_class = ji->method->klass;
+
+ if (class->generic_class)
+ g_assert (class->generic_class->container_class == method_container_class);
+ else
+ g_assert (!class->generic_container && class == method_container_class);
+
+ /* FIXME: we shouldn't inflate but instead put the
+ type in the rgctx and fetch it from there. It
+ might be a good idea to do this lazily, i.e. only
+ when the exception is actually thrown, so as not to
+ waste space for exception clauses which might never
+ be encountered. */
+ inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg, &context);
+ catch_class = mono_class_from_mono_type (inflated_type);
+ mono_metadata_free_type (inflated_type);
+
return catch_class;
}
* Allocate a new exception object instead of the preconstructed ones.
*/
if (obj == domain->stack_overflow_ex) {
- obj = mono_get_exception_stack_overflow ();
+ /*
+ * It is not a good idea to try and put even more pressure on the little stack available.
+ * obj = mono_get_exception_stack_overflow ();
+ */
stack_overflow = TRUE;
}
else if (obj == domain->null_reference_ex) {
}
if (!call_filter)
- call_filter = mono_arch_get_call_filter ();
+ call_filter = mono_get_call_filter ();
if (!restore_context)
- restore_context = mono_arch_get_restore_context ();
+ restore_context = mono_get_restore_context ();
g_assert (jit_tls->end_of_stack);
g_assert (jit_tls->abort_func);
MonoJitExceptionInfo *ei = &ji->clauses [i];
gboolean filtered = FALSE;
-#ifdef __s390__
+#if defined(__s390__)
+ /*
+ * This is required in cases where a try block starts immediately after
+ * a call which causes an exception. Testcase: tests/exception8.cs.
+ * FIXME: Clean this up.
+ */
if (ei->try_start < MONO_CONTEXT_GET_IP (ctx) &&
#else
if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
// mono_debugger_handle_exception (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
if (test_only) {
+ mono_perfcounters->exceptions_filters++;
filtered = call_filter (ctx, ei->data.filter);
if (filtered && out_filter_idx)
*out_filter_idx = filter_idx;
mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
*(mono_get_lmf_addr ()) = lmf;
+ mono_perfcounters->exceptions_depth += frame_count;
+ if (obj == domain->stack_overflow_ex)
+ jit_tls->handling_stack_ovf = FALSE;
return 0;
}
g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
+ mono_perfcounters->exceptions_finallys++;
call_filter (ctx, ei->handler_start);
}
return;
if (!call_filter)
- call_filter = mono_arch_get_call_filter ();
+ call_filter = mono_get_call_filter ();
for (i = 0; i < ji->num_clauses; i++) {
MonoJitExceptionInfo *ei = &ji->clauses [i];
gboolean
mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only)
{
+ if (!test_only)
+ mono_perfcounters->exceptions_thrown++;
return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL);
}
-#endif /* CUSTOM_EXCEPTION_HANDLING */
-
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
#ifndef MONO_ARCH_USE_SIGACTION
tls->stack_size = stsize + mono_pagesize ();
/* Setup an alternate signal stack */
- tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC|MONO_MMAP_PRIVATE|MONO_MMAP_ANON);
+ tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON);
tls->signal_stack_size = MONO_ARCH_SIGNAL_STACK_SIZE;
g_assert (tls->signal_stack);
#endif /* MONO_ARCH_SIGSEGV_ON_ALTSTACK */
+static gboolean
+try_restore_stack_protection (MonoJitTlsData *jit_tls, int extra_bytes)
+{
+ gint32 unprotect_size = jit_tls->stack_ovf_guard_size;
+ /* we need to leave some room for throwing the exception */
+ while (unprotect_size >= 0 && (char*)jit_tls->stack_ovf_guard_base + unprotect_size > ((char*)&unprotect_size - extra_bytes))
+ unprotect_size -= mono_pagesize ();
+ /* at this point we could try and build a new domain->stack_overflow_ex, but only if there
+ * is sufficient stack
+ */
+ //fprintf (stderr, "restoring stack protection: %p-%p (%d)\n", jit_tls->stack_ovf_guard_base, (char*)jit_tls->stack_ovf_guard_base + unprotect_size, unprotect_size);
+ if (unprotect_size)
+ mono_mprotect (jit_tls->stack_ovf_guard_base, unprotect_size, MONO_MMAP_NONE);
+ return unprotect_size == jit_tls->stack_ovf_guard_size;
+}
+
+static void
+try_more_restore (void)
+{
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ if (try_restore_stack_protection (jit_tls, 500))
+ jit_tls->restore_stack_prot = NULL;
+}
+
+static void
+restore_stack_protection (void)
+{
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ MonoException *ex = mono_domain_get ()->stack_overflow_ex;
+ /* if we can't restore the stack protection, keep a callback installed so
+ * we'll try to restore as much stack as we can at each return from unmanaged
+ * code.
+ */
+ if (try_restore_stack_protection (jit_tls, 4096))
+ jit_tls->restore_stack_prot = NULL;
+ else
+ jit_tls->restore_stack_prot = try_more_restore_tramp;
+ /* here we also throw a stack overflow exception */
+ ex->trace_ips = NULL;
+ ex->stack_trace = NULL;
+ mono_raise_exception (ex);
+}
+
+gpointer
+mono_altstack_restore_prot (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
+{
+ void (*func)(void) = (gpointer)tramp_data;
+ func ();
+ return NULL;
+}
+
+gboolean
+mono_handle_soft_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, void *ctx, guint8* fault_addr)
+{
+ /* we got a stack overflow in the soft-guard pages
+ * There are two cases:
+ * 1) managed code caused the overflow: we unprotect the soft-guard page
+ * and let the arch-specific code trigger the exception handling mechanism
+ * in the thread stack. The soft-guard pages will be protected again as the stack is unwound.
+ * 2) unmanaged code caused the overflow: we unprotect the soft-guard page
+ * and hope we can continue with those enabled, at least until the hard-guard page
+ * is hit. The alternative to continuing here is to just print a message and abort.
+ * We may add in the future the code to protect the pages again in the codepath
+ * when we return from unmanaged to managed code.
+ */
+ if (jit_tls->stack_ovf_guard_size && fault_addr >= (guint8*)jit_tls->stack_ovf_guard_base &&
+ fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) {
+ /* we unprotect the minimum amount we can */
+ guint32 guard_size;
+ gboolean handled = FALSE;
+
+ guard_size = jit_tls->stack_ovf_guard_size - (mono_pagesize () * SIZEOF_VOID_P / 4);
+ while (guard_size && fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + guard_size) {
+ guard_size -= mono_pagesize ();
+ }
+ guard_size = jit_tls->stack_ovf_guard_size - guard_size;
+ /*fprintf (stderr, "unprotecting: %d\n", guard_size);*/
+ mono_mprotect ((char*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size - guard_size, guard_size, MONO_MMAP_READ|MONO_MMAP_WRITE);
+#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
+ if (ji) {
+ mono_arch_handle_altstack_exception (ctx, fault_addr, TRUE);
+ handled = TRUE;
+ }
+#endif
+ if (!handled) {
+ /* We print a message: after this even managed stack overflows
+ * may crash the runtime
+ */
+ fprintf (stderr, "Stack overflow in unmanaged: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), fault_addr);
+ if (!jit_tls->handling_stack_ovf) {
+ jit_tls->restore_stack_prot = restore_stack_protection_tramp;
+ jit_tls->handling_stack_ovf = 1;
+ } else {
+ /*fprintf (stderr, "Already handling stack overflow\n");*/
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
static gboolean
print_stack_frame (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
{
return FALSE;
}
-static gboolean
+static G_GNUC_UNUSED gboolean
print_stack_frame_to_string (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed,
gpointer data)
{
#ifndef PLATFORM_WIN32
struct sigaction sa;
#endif
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+
if (handling_sigsegv)
return;
/* To prevent infinite loops when the stack walk causes a crash */
handling_sigsegv = TRUE;
- fprintf (stderr, "Stacktrace:\n\n");
+ /* !jit_tls means the thread was not registered with the runtime */
+ if (jit_tls) {
+ fprintf (stderr, "Stacktrace:\n\n");
- mono_jit_walk_stack (print_stack_frame, TRUE, stderr);
+ mono_jit_walk_stack (print_stack_frame, TRUE, stderr);
- fflush (stderr);
+ fflush (stderr);
+ }
#ifdef HAVE_BACKTRACE_SYMBOLS
{
* mono_print_thread_dump:
*
* Print information about the current thread to stdout.
+ * SIGCTX can be NULL, allowing this to be called from gdb.
*/
void
mono_print_thread_dump (void *sigctx)
/* FIXME: */
#if defined(__i386__) || defined(__x86_64__)
- mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+ if (!sigctx)
+ MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_print_thread_dump);
+ else
+ mono_arch_sigctx_to_monoctx (sigctx, &ctx);
mono_jit_walk_stack_from_ctx (print_stack_frame_to_string, &ctx, TRUE, text);
#else