Make sure x86 ATOMIC_CAS doesn't overwrite its own operands.
[mono-debugger.git] / mono / mini / mini-exceptions.c
blob3b5eacc141c70e63441ae930f23424ed57ed54ba
1 /*
2 * mini-exceptions.c: generic exception support
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Mono Team (mono-list@lists.ximian.com)
8 * Copyright 2001-2003 Ximian, Inc.
9 * Copyright 2003-2008 Ximian, Inc.
12 #include <config.h>
13 #include <glib.h>
14 #include <signal.h>
15 #include <string.h>
17 #ifdef HAVE_EXECINFO_H
18 #include <execinfo.h>
19 #endif
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
25 #ifdef HAVE_SYS_WAIT_H
26 #include <sys/wait.h>
27 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
33 #ifdef HAVE_SYS_SYSCALL_H
34 #include <sys/syscall.h>
35 #endif
37 #include <mono/metadata/appdomain.h>
38 #include <mono/metadata/tabledefs.h>
39 #include <mono/metadata/threads.h>
40 #include <mono/metadata/threads-types.h>
41 #include <mono/metadata/debug-helpers.h>
42 #include <mono/metadata/exception.h>
43 #include <mono/metadata/gc-internal.h>
44 #include <mono/metadata/mono-debug.h>
45 #include <mono/metadata/profiler.h>
46 #include <mono/utils/mono-mmap.h>
48 #include "mini.h"
49 #include "debug-mini.h"
50 #include "trace.h"
52 #ifndef MONO_ARCH_CONTEXT_DEF
53 #define MONO_ARCH_CONTEXT_DEF
54 #endif
56 static gpointer restore_context_func, call_filter_func;
57 static gpointer throw_exception_func, rethrow_exception_func;
58 static gpointer throw_exception_by_name_func, throw_corlib_exception_func;
60 static gpointer try_more_restore_tramp = NULL;
61 static gpointer restore_stack_protection_tramp = NULL;
63 static void try_more_restore (void);
64 static void restore_stack_protection (void);
66 void
67 mono_exceptions_init (void)
69 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
70 guint32 code_size;
71 MonoJumpInfo *ji;
73 if (mono_aot_only) {
74 restore_context_func = mono_aot_get_named_code ("restore_context");
75 call_filter_func = mono_aot_get_named_code ("call_filter");
76 throw_exception_func = mono_aot_get_named_code ("throw_exception");
77 rethrow_exception_func = mono_aot_get_named_code ("rethrow_exception");
78 } else {
79 restore_context_func = mono_arch_get_restore_context_full (&code_size, &ji, FALSE);
80 call_filter_func = mono_arch_get_call_filter_full (&code_size, &ji, FALSE);
81 throw_exception_func = mono_arch_get_throw_exception_full (&code_size, &ji, FALSE);
82 rethrow_exception_func = mono_arch_get_rethrow_exception_full (&code_size, &ji, FALSE);
84 #else
85 restore_context_func = mono_arch_get_restore_context ();
86 call_filter_func = mono_arch_get_call_filter ();
87 throw_exception_func = mono_arch_get_throw_exception ();
88 rethrow_exception_func = mono_arch_get_rethrow_exception ();
89 #endif
90 #ifdef MONO_ARCH_HAVE_RESTORE_STACK_SUPPORT
91 try_more_restore_tramp = mono_create_specific_trampoline (try_more_restore, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
92 restore_stack_protection_tramp = mono_create_specific_trampoline (restore_stack_protection, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
93 #endif
95 #ifdef MONO_ARCH_HAVE_EXCEPTIONS_INIT
96 mono_arch_exceptions_init ();
97 #endif
100 gpointer
101 mono_get_throw_exception (void)
103 g_assert (throw_exception_func);
104 return throw_exception_func;
107 gpointer
108 mono_get_rethrow_exception (void)
110 g_assert (rethrow_exception_func);
111 return rethrow_exception_func;
114 gpointer
115 mono_get_call_filter (void)
117 g_assert (call_filter_func);
118 return call_filter_func;
121 gpointer
122 mono_get_restore_context (void)
124 g_assert (restore_context_func);
125 return restore_context_func;
128 gpointer
129 mono_get_throw_exception_by_name (void)
131 gpointer code = NULL;
132 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
133 guint32 code_size;
134 MonoJumpInfo *ji;
135 #endif
137 /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
138 if (throw_exception_by_name_func)
139 return throw_exception_by_name_func;
141 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
142 if (mono_aot_only)
143 code = mono_aot_get_named_code ("throw_exception_by_name");
144 else
145 code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, FALSE);
146 #else
147 code = mono_arch_get_throw_exception_by_name ();
148 #endif
150 mono_memory_barrier ();
152 throw_exception_by_name_func = code;
154 return throw_exception_by_name_func;
157 gpointer
158 mono_get_throw_corlib_exception (void)
160 gpointer code = NULL;
161 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
162 guint32 code_size;
163 MonoJumpInfo *ji;
164 #endif
166 /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
167 if (throw_corlib_exception_func)
168 return throw_corlib_exception_func;
170 #if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION
171 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
172 if (mono_aot_only)
173 code = mono_aot_get_named_code ("throw_corlib_exception");
174 else
175 code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, FALSE);
176 #else
177 code = mono_arch_get_throw_corlib_exception ();
178 #endif
179 #else
180 g_assert_not_reached ();
181 #endif
183 mono_memory_barrier ();
185 throw_corlib_exception_func = code;
187 return throw_corlib_exception_func;
190 /* mono_find_jit_info:
192 * This function is used to gather information from @ctx. It return the
193 * MonoJitInfo of the corresponding function, unwinds one stack frame and
194 * stores the resulting context into @new_ctx. It also stores a string
195 * describing the stack location into @trace (if not NULL), and modifies
196 * the @lmf if necessary. @native_offset return the IP offset from the
197 * start of the function or -1 if that info is not available.
199 MonoJitInfo *
200 mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
201 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
202 gboolean *managed)
204 gboolean managed2;
205 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
206 MonoJitInfo *ji;
208 if (trace)
209 *trace = NULL;
211 if (native_offset)
212 *native_offset = -1;
214 if (managed)
215 *managed = FALSE;
217 ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
219 if (ji == (gpointer)-1)
220 return ji;
222 if (managed2 || ji->method->wrapper_type) {
223 const char *real_ip, *start;
224 gint32 offset;
226 start = (const char *)ji->code_start;
227 if (!managed2)
228 /* ctx->ip points into native code */
229 real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx);
230 else
231 real_ip = (const char*)ip;
233 if ((real_ip >= start) && (real_ip <= start + ji->code_size))
234 offset = real_ip - start;
235 else
236 offset = -1;
238 if (native_offset)
239 *native_offset = offset;
241 if (managed)
242 if (!ji->method->wrapper_type)
243 *managed = TRUE;
245 if (trace)
246 *trace = mono_debug_print_stack_frame (ji->method, offset, domain);
247 } else {
248 if (trace) {
249 char *fname = mono_method_full_name (res->method, TRUE);
250 *trace = g_strdup_printf ("in (unmanaged) %s", fname);
251 g_free (fname);
255 return ji;
258 static gpointer
259 get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
261 MonoGenericJitInfo *gi;
263 if (!ji->has_generic_jit_info)
264 return NULL;
265 gi = mono_jit_info_get_generic_jit_info (ji);
266 if (!gi->has_this)
267 return NULL;
269 if (gi->this_in_reg)
270 return mono_arch_context_get_int_reg (ctx, gi->this_reg);
271 else
272 return *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
273 gi->this_offset);
276 static MonoGenericContext
277 get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
279 MonoGenericContext context = { NULL, NULL };
280 MonoClass *class, *method_container_class;
282 g_assert (generic_info);
284 g_assert (ji->method->is_inflated);
285 if (mono_method_get_context (ji->method)->method_inst) {
286 MonoMethodRuntimeGenericContext *mrgctx = generic_info;
288 class = mrgctx->class_vtable->klass;
289 context.method_inst = mrgctx->method_inst;
290 g_assert (context.method_inst);
291 } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) {
292 MonoVTable *vtable = generic_info;
294 class = vtable->klass;
295 } else {
296 MonoObject *this = generic_info;
298 class = this->vtable->klass;
301 if (class->generic_class || class->generic_container)
302 context.class_inst = mini_class_get_context (class)->class_inst;
304 g_assert (!ji->method->klass->generic_container);
305 if (ji->method->klass->generic_class)
306 method_container_class = ji->method->klass->generic_class->container_class;
307 else
308 method_container_class = ji->method->klass;
310 if (class->generic_class)
311 g_assert (mono_class_has_parent_and_ignore_generics (class->generic_class->container_class, method_container_class));
312 else
313 g_assert (mono_class_has_parent_and_ignore_generics (class, method_container_class));
315 return context;
318 static MonoMethod*
319 get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
321 MonoGenericContext context;
322 MonoMethod *method;
324 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
325 return ji->method;
326 context = get_generic_context_from_stack_frame (ji, generic_info);
328 method = mono_method_get_declaring_generic_method (ji->method);
329 method = mono_class_inflate_generic_method (method, &context);
331 return method;
334 MonoString *
335 ves_icall_System_Exception_get_trace (MonoException *ex)
337 MonoDomain *domain = mono_domain_get ();
338 MonoString *res;
339 MonoArray *ta = ex->trace_ips;
340 int i, len;
341 GString *trace_str;
343 if (ta == NULL)
344 /* Exception is not thrown yet */
345 return NULL;
347 len = mono_array_length (ta) >> 1;
348 trace_str = g_string_new ("");
349 for (i = 0; i < len; i++) {
350 MonoJitInfo *ji;
351 gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
352 gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
354 ji = mono_jit_info_table_find (domain, ip);
355 if (ji == NULL) {
356 /* Unmanaged frame */
357 g_string_append_printf (trace_str, "in (unmanaged) %p\n", ip);
358 } else {
359 gchar *location;
360 gint32 address;
361 MonoMethod *method = get_method_from_stack_frame (ji, generic_info);
363 address = (char *)ip - (char *)ji->code_start;
364 location = mono_debug_print_stack_frame (
365 method, address, ex->object.vtable->domain);
367 g_string_append_printf (trace_str, "%s\n", location);
368 g_free (location);
372 res = mono_string_new (ex->object.vtable->domain, trace_str->str);
373 g_string_free (trace_str, TRUE);
375 return res;
378 MonoArray *
379 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
381 MonoDomain *domain = mono_domain_get ();
382 MonoArray *res;
383 MonoArray *ta = exc->trace_ips;
384 MonoDebugSourceLocation *location;
385 int i, len;
387 if (ta == NULL) {
388 /* Exception is not thrown yet */
389 return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
392 len = mono_array_length (ta) >> 1;
394 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
396 for (i = skip; i < len; i++) {
397 MonoJitInfo *ji;
398 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
399 gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
400 gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
401 MonoMethod *method;
403 ji = mono_jit_info_table_find (domain, ip);
404 if (ji == NULL) {
405 /* Unmanaged frame */
406 mono_array_setref (res, i, sf);
407 continue;
410 g_assert (ji != NULL);
412 method = get_method_from_stack_frame (ji, generic_info);
413 if (ji->method->wrapper_type) {
414 char *s;
416 sf->method = NULL;
417 s = mono_method_full_name (method, TRUE);
418 MONO_OBJECT_SETREF (sf, internal_method_name, mono_string_new (domain, s));
419 g_free (s);
421 else
422 MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL));
423 sf->native_offset = (char *)ip - (char *)ji->code_start;
426 * mono_debug_lookup_source_location() returns both the file / line number information
427 * and the IL offset. Note that computing the IL offset is already an expensive
428 * operation, so we shouldn't call this method twice.
430 location = mono_debug_lookup_source_location (ji->method, sf->native_offset, domain);
431 if (location)
432 sf->il_offset = location->il_offset;
433 else
434 sf->il_offset = 0;
436 if (need_file_info) {
437 if (location && location->source_file) {
438 MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
439 sf->line = location->row;
440 sf->column = location->column;
441 } else {
442 sf->line = sf->column = 0;
443 sf->filename = NULL;
447 mono_debug_free_source_location (location);
448 mono_array_setref (res, i, sf);
451 return res;
455 * mono_walk_stack:
456 * @domain: starting appdomain
457 * @jit_tls: JIT data for the thread
458 * @start_ctx: starting state of the stack frame
459 * @func: callback to call for each stack frame
460 * @user_data: data passed to the callback
462 * This function walks the stack of a thread, starting from the state
463 * represented by jit_tls and start_ctx. For each frame the callback
464 * function is called with the relevant info. The walk ends when no more
465 * managed stack frames are found or when the callback returns a TRUE value.
466 * Note that the function can be used to walk the stack of a thread
467 * different from the current.
469 void
470 mono_walk_stack (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gpointer user_data)
472 MonoLMF *lmf = mono_get_lmf ();
473 MonoJitInfo *ji, rji;
474 gint native_offset;
475 gboolean managed;
476 MonoContext ctx, new_ctx;
478 ctx = *start_ctx;
480 while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
482 * FIXME: mono_find_jit_info () will need to be able to return a different
483 * MonoDomain when apddomain transitions are found on the stack.
485 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
486 if (!ji || ji == (gpointer)-1)
487 return;
489 if (func (domain, &new_ctx, ji, user_data))
490 return;
492 ctx = new_ctx;
496 void
497 mono_jit_walk_stack_from_ctx (MonoStackWalk func, MonoContext *start_ctx, gboolean do_il_offset, gpointer user_data)
499 MonoDomain *domain = mono_domain_get ();
500 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
501 MonoLMF *lmf = mono_get_lmf ();
502 MonoJitInfo *ji, rji;
503 gint native_offset, il_offset;
504 gboolean managed;
505 MonoContext ctx, new_ctx;
507 MONO_ARCH_CONTEXT_DEF
509 mono_arch_flush_register_windows ();
511 if (start_ctx) {
512 memcpy (&ctx, start_ctx, sizeof (MonoContext));
513 } else {
514 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
515 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
516 #else
517 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_jit_walk_stack_from_ctx);
518 #endif
521 while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
522 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
523 g_assert (ji);
525 if (ji == (gpointer)-1)
526 return;
528 if (do_il_offset) {
529 MonoDebugSourceLocation *source;
531 source = mono_debug_lookup_source_location (ji->method, native_offset, domain);
532 il_offset = source ? source->il_offset : -1;
533 mono_debug_free_source_location (source);
534 } else
535 il_offset = -1;
537 if (func (ji->method, native_offset, il_offset, managed, user_data))
538 return;
540 ctx = new_ctx;
544 void
545 mono_jit_walk_stack (MonoStackWalk func, gboolean do_il_offset, gpointer user_data)
547 mono_jit_walk_stack_from_ctx (func, NULL, do_il_offset, user_data);
550 MonoBoolean
551 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
552 MonoReflectionMethod **method,
553 gint32 *iloffset, gint32 *native_offset,
554 MonoString **file, gint32 *line, gint32 *column)
556 MonoDomain *domain = mono_domain_get ();
557 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
558 MonoLMF *lmf = mono_get_lmf ();
559 MonoJitInfo *ji, rji;
560 MonoContext ctx, new_ctx, ji_ctx;
561 MonoDebugSourceLocation *location;
562 MonoMethod *last_method = NULL, *actual_method;
564 MONO_ARCH_CONTEXT_DEF;
566 mono_arch_flush_register_windows ();
568 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
569 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
570 #else
571 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
572 #endif
574 do {
575 ji_ctx = ctx;
576 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, (int*) native_offset, NULL);
577 ctx = new_ctx;
579 if (ji && ji != (gpointer)-1 &&
580 MONO_CONTEXT_GET_IP (&ctx) >= ji->code_start &&
581 (guint8*)MONO_CONTEXT_GET_IP (&ctx) < (guint8*)ji->code_start + ji->code_size) {
582 ji_ctx = ctx;
585 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_SP (&ctx) >= jit_tls->end_of_stack)
586 return FALSE;
588 /* skip all wrappers ??*/
589 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
590 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
591 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
592 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
593 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE ||
594 ji->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
595 continue;
597 if (ji->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && ji->method == last_method) {
599 * FIXME: Native-to-managed wrappers sometimes show up twice.
600 * Probably the whole mono_find_jit_info () stuff needs to be fixed so this
601 * isn't needed.
603 continue;
606 last_method = ji->method;
608 skip--;
610 } while (skip >= 0);
612 actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ji_ctx));
614 *method = mono_method_get_object (domain, actual_method, NULL);
616 location = mono_debug_lookup_source_location (ji->method, *native_offset, domain);
617 if (location)
618 *iloffset = location->il_offset;
619 else
620 *iloffset = 0;
622 if (need_file_info) {
623 if (location) {
624 *file = mono_string_new (domain, location->source_file);
625 *line = location->row;
626 *column = location->column;
627 } else {
628 *file = NULL;
629 *line = *column = 0;
633 mono_debug_free_source_location (location);
635 return TRUE;
638 typedef struct {
639 guint32 skips;
640 MonoSecurityFrame *frame;
641 } MonoFrameSecurityInfo;
643 static gboolean
644 callback_get_first_frame_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
646 MonoFrameSecurityInfo *si = (MonoFrameSecurityInfo*) data;
648 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
649 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
650 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
651 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
652 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
653 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
654 return FALSE;
657 if (si->skips > 0) {
658 si->skips--;
659 return FALSE;
662 si->frame = mono_declsec_create_frame (domain, ji);
664 /* Stop - we only want the first frame (e.g. LinkDemand and InheritanceDemand) */
665 return TRUE;
669 * ves_icall_System_Security_SecurityFrame_GetSecurityFrame:
670 * @skip: the number of stack frames to skip
672 * This function returns a the security informations of a single stack frame
673 * (after the skipped ones). This is required for [NonCas]LinkDemand[Choice]
674 * and [NonCas]InheritanceDemand[Choice] as only the caller security is
675 * evaluated.
677 MonoSecurityFrame*
678 ves_icall_System_Security_SecurityFrame_GetSecurityFrame (gint32 skip)
680 MonoDomain *domain = mono_domain_get ();
681 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
682 MonoFrameSecurityInfo si;
683 MonoContext ctx;
685 MONO_ARCH_CONTEXT_DEF
687 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
688 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
689 #else
690 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityFrame);
691 #endif
693 #if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
694 skip--;
695 #endif
697 si.skips = skip;
698 si.frame = NULL;
699 mono_walk_stack (domain, jit_tls, &ctx, callback_get_first_frame_security_info, (gpointer)&si);
701 return (si.skips == 0) ? si.frame : NULL;
705 typedef struct {
706 guint32 skips;
707 MonoArray *stack;
708 guint32 count;
709 guint32 maximum;
710 } MonoSecurityStack;
712 static void
713 grow_array (MonoSecurityStack *stack)
715 MonoDomain *domain = mono_domain_get ();
716 guint32 newsize = (stack->maximum << 1);
717 MonoArray *newstack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, newsize);
718 int i;
719 for (i=0; i < stack->maximum; i++) {
720 gpointer frame = mono_array_get (stack->stack, gpointer, i);
721 mono_array_setref (newstack, i, frame);
723 stack->maximum = newsize;
724 stack->stack = newstack;
727 static gboolean
728 callback_get_stack_frames_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
730 MonoSecurityStack *ss = (MonoSecurityStack*) data;
732 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
733 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
734 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
735 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
736 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
737 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
738 return FALSE;
741 if (ss->skips > 0) {
742 ss->skips--;
743 return FALSE;
746 if (ss->count == ss->maximum)
747 grow_array (ss);
749 mono_array_setref (ss->stack, ss->count++, mono_declsec_create_frame (domain, ji));
751 /* continue down the stack */
752 return FALSE;
755 static MonoArray *
756 glist_to_array (GList *list, MonoClass *eclass)
758 MonoDomain *domain = mono_domain_get ();
759 MonoArray *res;
760 int len, i;
762 if (!list)
763 return NULL;
765 len = g_list_length (list);
766 res = mono_array_new (domain, eclass, len);
768 for (i = 0; list; list = list->next, i++)
769 mono_array_set (res, gpointer, i, list->data);
771 return res;
775 * ves_icall_System_Security_SecurityFrame_GetSecurityStack:
776 * @skip: the number of stack frames to skip
778 * This function returns an managed array of containing the security
779 * informations for each frame (after the skipped ones). This is used for
780 * [NonCas]Demand[Choice] where the complete evaluation of the stack is
781 * required.
783 MonoArray*
784 ves_icall_System_Security_SecurityFrame_GetSecurityStack (gint32 skip)
786 MonoDomain *domain = mono_domain_get ();
787 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
788 MonoSecurityStack ss;
789 MonoContext ctx;
791 MONO_ARCH_CONTEXT_DEF
793 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
794 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
795 #else
796 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityStack);
797 #endif
799 #if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
800 skip--;
801 #endif
803 ss.skips = skip;
804 ss.count = 0;
805 ss.maximum = MONO_CAS_INITIAL_STACK_SIZE;
806 ss.stack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, ss.maximum);
807 mono_walk_stack (domain, jit_tls, &ctx, callback_get_stack_frames_security_info, (gpointer)&ss);
808 /* g_warning ("STACK RESULT: %d out of %d", ss.count, ss.maximum); */
809 return ss.stack;
812 static MonoClass*
813 get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContext *ctx)
815 MonoClass *catch_class = ei->data.catch_class;
816 MonoType *inflated_type;
817 MonoGenericContext context;
819 if (!catch_class)
820 return NULL;
822 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
823 return catch_class;
824 context = get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx));
826 /* FIXME: we shouldn't inflate but instead put the
827 type in the rgctx and fetch it from there. It
828 might be a good idea to do this lazily, i.e. only
829 when the exception is actually thrown, so as not to
830 waste space for exception clauses which might never
831 be encountered. */
832 inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg, &context);
833 catch_class = mono_class_from_mono_type (inflated_type);
834 mono_metadata_free_type (inflated_type);
836 return catch_class;
840 * mono_handle_exception_internal:
841 * @ctx: saved processor state
842 * @obj: the exception object
843 * @test_only: only test if the exception is caught, but dont call handlers
844 * @out_filter_idx: out parameter. if test_only is true, set to the index of
845 * the first filter clause which caught the exception.
847 static gboolean
848 mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gint32 *out_filter_idx)
850 MonoDomain *domain = mono_domain_get ();
851 MonoJitInfo *ji, rji;
852 static int (*call_filter) (MonoContext *, gpointer) = NULL;
853 static void (*restore_context) (void *);
854 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
855 MonoLMF *lmf = mono_get_lmf ();
856 MonoArray *initial_trace_ips = NULL;
857 GList *trace_ips = NULL;
858 MonoException *mono_ex;
859 gboolean stack_overflow = FALSE;
860 MonoContext initial_ctx;
861 int frame_count = 0;
862 gboolean has_dynamic_methods = FALSE;
863 gint32 filter_idx, first_filter_idx;
865 g_assert (ctx != NULL);
866 if (!obj) {
867 MonoException *ex = mono_get_exception_null_reference ();
868 MONO_OBJECT_SETREF (ex, message, mono_string_new (domain, "Object reference not set to an instance of an object"));
869 obj = (MonoObject *)ex;
873 * Allocate a new exception object instead of the preconstructed ones.
875 if (obj == domain->stack_overflow_ex) {
877 * It is not a good idea to try and put even more pressure on the little stack available.
878 * obj = mono_get_exception_stack_overflow ();
880 stack_overflow = TRUE;
882 else if (obj == domain->null_reference_ex) {
883 obj = mono_get_exception_null_reference ();
886 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
887 mono_ex = (MonoException*)obj;
888 initial_trace_ips = mono_ex->trace_ips;
889 } else {
890 mono_ex = NULL;
893 if (mono_ex && jit_tls->class_cast_from && !strcmp (mono_ex->object.vtable->klass->name, "InvalidCastException")) {
894 char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
895 char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
896 char *msg = g_strdup_printf ("Unable to cast object of type '%s' to type '%s'.", from_name, to_name);
897 mono_ex->message = mono_string_new (domain, msg);
898 g_free (from_name);
899 g_free (to_name);
900 g_free (msg);
903 if (!call_filter)
904 call_filter = mono_get_call_filter ();
906 if (!restore_context)
907 restore_context = mono_get_restore_context ();
909 g_assert (jit_tls->end_of_stack);
910 g_assert (jit_tls->abort_func);
912 if (!test_only) {
913 MonoContext ctx_cp = *ctx;
914 if (mono_trace_is_enabled ())
915 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
916 mono_profiler_exception_thrown (obj);
917 if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, &first_filter_idx)) {
918 if (mono_break_on_exc)
919 G_BREAKPOINT ();
920 // FIXME: This runs managed code so it might cause another stack overflow when
921 // we are handling a stack overflow
922 mono_unhandled_exception (obj);
926 if (out_filter_idx)
927 *out_filter_idx = -1;
928 filter_idx = 0;
929 initial_ctx = *ctx;
930 memset (&rji, 0, sizeof (rji));
932 while (1) {
933 MonoContext new_ctx;
934 guint32 free_stack;
936 ji = mono_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx,
937 NULL, &lmf, NULL, NULL);
938 if (!ji) {
939 g_warning ("Exception inside function without unwind info");
940 g_assert_not_reached ();
943 if (ji != (gpointer)-1 && !(ji->code_start <= MONO_CONTEXT_GET_IP (ctx) && (((guint8*)ji->code_start + ji->code_size >= (guint8*)MONO_CONTEXT_GET_IP (ctx))))) {
945 * The exception was raised in native code and we got back to managed code
946 * using the LMF.
948 *ctx = new_ctx;
949 continue;
952 if (ji != (gpointer)-1) {
953 frame_count ++;
954 //printf ("M: %s %d %d.\n", mono_method_full_name (ji->method, TRUE), frame_count, test_only);
956 if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
958 * Avoid overwriting the stack trace if the exception is
959 * rethrown. Also avoid giant stack traces during a stack
960 * overflow.
962 if (!initial_trace_ips && (frame_count < 1000)) {
963 trace_ips = g_list_prepend (trace_ips, MONO_CONTEXT_GET_IP (ctx));
964 trace_ips = g_list_prepend (trace_ips,
965 get_generic_info_from_stack_frame (ji, ctx));
969 if (ji->method->dynamic)
970 has_dynamic_methods = TRUE;
972 if (stack_overflow)
973 #ifndef MONO_ARCH_STACK_GROWS_UP
974 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
975 #else
976 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
977 #endif
978 else
979 free_stack = 0xffffff;
982 * During stack overflow, wait till the unwinding frees some stack
983 * space before running handlers/finalizers.
985 if ((free_stack > (64 * 1024)) && ji->num_clauses) {
986 int i;
988 for (i = 0; i < ji->num_clauses; i++) {
989 MonoJitExceptionInfo *ei = &ji->clauses [i];
990 gboolean filtered = FALSE;
992 #if defined(__s390__)
994 * This is required in cases where a try block starts immediately after
995 * a call which causes an exception. Testcase: tests/exception8.cs.
996 * FIXME: Clean this up.
998 if (ei->try_start < MONO_CONTEXT_GET_IP (ctx) &&
999 #else
1000 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1001 #endif
1002 MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
1003 /* catch block */
1004 MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
1006 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
1007 /* store the exception object in bp + ei->exvar_offset */
1008 *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = obj;
1011 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
1012 // mono_debugger_call_exception_handler (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
1013 if (test_only) {
1014 mono_perfcounters->exceptions_filters++;
1015 filtered = call_filter (ctx, ei->data.filter);
1016 if (filtered && out_filter_idx)
1017 *out_filter_idx = filter_idx;
1019 else {
1021 * Filter clauses should only be run in the
1022 * first pass of exception handling.
1024 filtered = (filter_idx == first_filter_idx);
1026 filter_idx ++;
1029 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
1030 mono_object_isinst (obj, catch_class)) || filtered) {
1031 if (test_only) {
1032 if (mono_ex && !initial_trace_ips) {
1033 trace_ips = g_list_reverse (trace_ips);
1034 MONO_OBJECT_SETREF (mono_ex, trace_ips, glist_to_array (trace_ips, mono_defaults.int_class));
1035 if (has_dynamic_methods)
1036 /* These methods could go away anytime, so compute the stack trace now */
1037 MONO_OBJECT_SETREF (mono_ex, stack_trace, ves_icall_System_Exception_get_trace (mono_ex));
1039 g_list_free (trace_ips);
1041 return TRUE;
1043 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1044 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1045 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1046 mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1047 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
1048 *(mono_get_lmf_addr ()) = lmf;
1049 mono_perfcounters->exceptions_depth += frame_count;
1050 if (obj == domain->stack_overflow_ex)
1051 jit_tls->handling_stack_ovf = FALSE;
1053 return 0;
1055 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1056 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1057 (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT)) {
1058 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1059 g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1060 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1061 mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1062 call_filter (ctx, ei->handler_start);
1064 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1065 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1066 (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
1067 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1068 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1069 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1070 mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1071 mono_perfcounters->exceptions_finallys++;
1072 call_filter (ctx, ei->handler_start);
1078 if (!test_only)
1079 mono_profiler_exception_method_leave (ji->method);
1082 *ctx = new_ctx;
1084 if (ji == (gpointer)-1) {
1086 if (!test_only) {
1087 *(mono_get_lmf_addr ()) = lmf;
1089 jit_tls->abort_func (obj);
1090 g_assert_not_reached ();
1091 } else {
1092 if (mono_ex && !initial_trace_ips) {
1093 trace_ips = g_list_reverse (trace_ips);
1094 MONO_OBJECT_SETREF (mono_ex, trace_ips, glist_to_array (trace_ips, mono_defaults.int_class));
1095 if (has_dynamic_methods)
1096 /* These methods could go away anytime, so compute the stack trace now */
1097 MONO_OBJECT_SETREF (mono_ex, stack_trace, ves_icall_System_Exception_get_trace (mono_ex));
1099 g_list_free (trace_ips);
1100 return FALSE;
1105 g_assert_not_reached ();
1109 * mono_debugger_run_finally:
1110 * @start_ctx: saved processor state
1112 * This method is called by the Mono Debugger to call all `finally' clauses of the
1113 * current stack frame. It's used when the user issues a `return' command to make
1114 * the current stack frame return. After returning from this method, the debugger
1115 * unwinds the stack one frame and gives control back to the user.
1117 * NOTE: This method is only used when running inside the Mono Debugger.
1119 void
1120 mono_debugger_run_finally (MonoContext *start_ctx)
1122 static int (*call_filter) (MonoContext *, gpointer) = NULL;
1123 MonoDomain *domain = mono_domain_get ();
1124 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1125 MonoLMF *lmf = mono_get_lmf ();
1126 MonoContext ctx, new_ctx;
1127 MonoJitInfo *ji, rji;
1128 int i;
1130 ctx = *start_ctx;
1132 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, NULL);
1133 if (!ji || ji == (gpointer)-1)
1134 return;
1136 if (!call_filter)
1137 call_filter = mono_get_call_filter ();
1139 for (i = 0; i < ji->num_clauses; i++) {
1140 MonoJitExceptionInfo *ei = &ji->clauses [i];
1142 if ((ei->try_start <= MONO_CONTEXT_GET_IP (&ctx)) &&
1143 (MONO_CONTEXT_GET_IP (&ctx) < ei->try_end) &&
1144 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
1145 call_filter (&ctx, ei->handler_start);
1151 * mono_handle_exception:
1152 * @ctx: saved processor state
1153 * @obj: the exception object
1154 * @test_only: only test if the exception is caught, but dont call handlers
1156 gboolean
1157 mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only)
1159 if (!test_only)
1160 mono_perfcounters->exceptions_thrown++;
1161 return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL);
1164 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
1166 #ifndef MONO_ARCH_USE_SIGACTION
1167 #error "Can't use sigaltstack without sigaction"
1168 #endif
1170 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
1172 void
1173 mono_setup_altstack (MonoJitTlsData *tls)
1175 size_t stsize = 0;
1176 struct sigaltstack sa;
1177 guint8 *staddr = NULL;
1179 if (mono_running_on_valgrind ())
1180 return;
1182 mono_thread_get_stack_bounds (&staddr, &stsize);
1184 g_assert (staddr);
1186 tls->end_of_stack = staddr + stsize;
1188 /*g_print ("thread %p, stack_base: %p, stack_size: %d\n", (gpointer)pthread_self (), staddr, stsize);*/
1190 tls->stack_ovf_guard_base = staddr + mono_pagesize ();
1191 tls->stack_ovf_guard_size = ALIGN_TO (8 * 4096, mono_pagesize ());
1193 if (mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE)) {
1194 /* mprotect can fail for the main thread stack */
1195 gpointer gaddr = mono_valloc (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON|MONO_MMAP_FIXED);
1196 g_assert (gaddr == tls->stack_ovf_guard_base);
1200 * threads created by nptl does not seem to have a guard page, and
1201 * since the main thread is not created by us, we can't even set one.
1202 * Increasing stsize fools the SIGSEGV signal handler into thinking this
1203 * is a stack overflow exception.
1205 tls->stack_size = stsize + mono_pagesize ();
1207 /* Setup an alternate signal stack */
1208 tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON);
1209 tls->signal_stack_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1211 g_assert (tls->signal_stack);
1213 sa.ss_sp = tls->signal_stack;
1214 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1215 sa.ss_flags = SS_ONSTACK;
1216 sigaltstack (&sa, NULL);
1219 void
1220 mono_free_altstack (MonoJitTlsData *tls)
1222 struct sigaltstack sa;
1223 int err;
1225 sa.ss_sp = tls->signal_stack;
1226 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1227 sa.ss_flags = SS_DISABLE;
1228 err = sigaltstack (&sa, NULL);
1229 g_assert (err == 0);
1231 if (tls->signal_stack)
1232 mono_vfree (tls->signal_stack, MONO_ARCH_SIGNAL_STACK_SIZE);
1235 #else /* !MONO_ARCH_SIGSEGV_ON_ALTSTACK */
1237 void
1238 mono_setup_altstack (MonoJitTlsData *tls)
1242 void
1243 mono_free_altstack (MonoJitTlsData *tls)
1247 #endif /* MONO_ARCH_SIGSEGV_ON_ALTSTACK */
1249 static gboolean
1250 try_restore_stack_protection (MonoJitTlsData *jit_tls, int extra_bytes)
1252 gint32 unprotect_size = jit_tls->stack_ovf_guard_size;
1253 /* we need to leave some room for throwing the exception */
1254 while (unprotect_size >= 0 && (char*)jit_tls->stack_ovf_guard_base + unprotect_size > ((char*)&unprotect_size - extra_bytes))
1255 unprotect_size -= mono_pagesize ();
1256 /* at this point we could try and build a new domain->stack_overflow_ex, but only if there
1257 * is sufficient stack
1259 //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);
1260 if (unprotect_size)
1261 mono_mprotect (jit_tls->stack_ovf_guard_base, unprotect_size, MONO_MMAP_NONE);
1262 return unprotect_size == jit_tls->stack_ovf_guard_size;
1265 static void
1266 try_more_restore (void)
1268 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1269 if (try_restore_stack_protection (jit_tls, 500))
1270 jit_tls->restore_stack_prot = NULL;
1273 static void
1274 restore_stack_protection (void)
1276 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1277 MonoException *ex = mono_domain_get ()->stack_overflow_ex;
1278 /* if we can't restore the stack protection, keep a callback installed so
1279 * we'll try to restore as much stack as we can at each return from unmanaged
1280 * code.
1282 if (try_restore_stack_protection (jit_tls, 4096))
1283 jit_tls->restore_stack_prot = NULL;
1284 else
1285 jit_tls->restore_stack_prot = try_more_restore_tramp;
1286 /* here we also throw a stack overflow exception */
1287 ex->trace_ips = NULL;
1288 ex->stack_trace = NULL;
1289 mono_raise_exception (ex);
1292 gpointer
1293 mono_altstack_restore_prot (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
1295 void (*func)(void) = (gpointer)tramp_data;
1296 func ();
1297 return NULL;
1300 gboolean
1301 mono_handle_soft_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, void *ctx, guint8* fault_addr)
1303 /* we got a stack overflow in the soft-guard pages
1304 * There are two cases:
1305 * 1) managed code caused the overflow: we unprotect the soft-guard page
1306 * and let the arch-specific code trigger the exception handling mechanism
1307 * in the thread stack. The soft-guard pages will be protected again as the stack is unwound.
1308 * 2) unmanaged code caused the overflow: we unprotect the soft-guard page
1309 * and hope we can continue with those enabled, at least until the hard-guard page
1310 * is hit. The alternative to continuing here is to just print a message and abort.
1311 * We may add in the future the code to protect the pages again in the codepath
1312 * when we return from unmanaged to managed code.
1314 if (jit_tls->stack_ovf_guard_size && fault_addr >= (guint8*)jit_tls->stack_ovf_guard_base &&
1315 fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) {
1316 /* we unprotect the minimum amount we can */
1317 guint32 guard_size;
1318 gboolean handled = FALSE;
1320 guard_size = jit_tls->stack_ovf_guard_size - (mono_pagesize () * SIZEOF_VOID_P / 4);
1321 while (guard_size && fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + guard_size) {
1322 guard_size -= mono_pagesize ();
1324 guard_size = jit_tls->stack_ovf_guard_size - guard_size;
1325 /*fprintf (stderr, "unprotecting: %d\n", guard_size);*/
1326 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);
1327 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
1328 if (ji) {
1329 mono_arch_handle_altstack_exception (ctx, fault_addr, TRUE);
1330 handled = TRUE;
1332 #endif
1333 if (!handled) {
1334 /* We print a message: after this even managed stack overflows
1335 * may crash the runtime
1337 fprintf (stderr, "Stack overflow in unmanaged: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), fault_addr);
1338 if (!jit_tls->handling_stack_ovf) {
1339 jit_tls->restore_stack_prot = restore_stack_protection_tramp;
1340 jit_tls->handling_stack_ovf = 1;
1341 } else {
1342 /*fprintf (stderr, "Already handling stack overflow\n");*/
1345 return TRUE;
1347 return FALSE;
1350 static gboolean
1351 print_stack_frame (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
1353 FILE *stream = (FILE*)data;
1355 if (method) {
1356 gchar *location = mono_debug_print_stack_frame (method, native_offset, mono_domain_get ());
1357 fprintf (stream, " %s\n", location);
1358 g_free (location);
1359 } else
1360 fprintf (stream, " at <unknown> <0x%05x>\n", native_offset);
1362 return FALSE;
1365 static G_GNUC_UNUSED gboolean
1366 print_stack_frame_to_string (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed,
1367 gpointer data)
1369 GString *p = (GString*)data;
1371 if (method) {
1372 gchar *location = mono_debug_print_stack_frame (method, native_offset, mono_domain_get ());
1373 g_string_append_printf (p, " %s\n", location);
1374 g_free (location);
1375 } else
1376 g_string_append_printf (p, " at <unknown> <0x%05x>\n", native_offset);
1378 return FALSE;
1381 static gboolean handling_sigsegv = FALSE;
1384 * mono_handle_native_sigsegv:
1386 * Handle a SIGSEGV received while in native code by printing diagnostic
1387 * information and aborting.
1389 void
1390 mono_handle_native_sigsegv (int signal, void *ctx)
1392 #ifdef MONO_ARCH_USE_SIGACTION
1393 struct sigaction sa;
1394 #endif
1395 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1397 if (handling_sigsegv)
1398 return;
1400 if (mini_get_debug_options ()->suspend_on_sigsegv) {
1401 fprintf (stderr, "Received SIGSEGV, suspending...");
1402 while (1)
1406 /* To prevent infinite loops when the stack walk causes a crash */
1407 handling_sigsegv = TRUE;
1409 /* !jit_tls means the thread was not registered with the runtime */
1410 if (jit_tls) {
1411 fprintf (stderr, "Stacktrace:\n\n");
1413 mono_jit_walk_stack (print_stack_frame, TRUE, stderr);
1415 fflush (stderr);
1418 #ifdef HAVE_BACKTRACE_SYMBOLS
1420 void *array [256];
1421 char **names;
1422 int i, size;
1423 const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT";
1425 fprintf (stderr, "\nNative stacktrace:\n\n");
1427 size = backtrace (array, 256);
1428 names = backtrace_symbols (array, size);
1429 for (i =0; i < size; ++i) {
1430 fprintf (stderr, "\t%s\n", names [i]);
1432 free (names);
1434 fflush (stderr);
1436 /* Try to get more meaningful information using gdb */
1438 #if !defined(PLATFORM_WIN32) && defined(HAVE_SYS_SYSCALL_H) && defined(SYS_fork)
1439 if (!mini_get_debug_options ()->no_gdb_backtrace && !mono_debug_using_mono_debugger ()) {
1440 /* From g_spawn_command_line_sync () in eglib */
1441 int res;
1442 int stdout_pipe [2] = { -1, -1 };
1443 pid_t pid;
1444 const char *argv [16];
1445 char buf1 [128];
1446 int status;
1447 char buffer [1024];
1449 res = pipe (stdout_pipe);
1450 g_assert (res != -1);
1452 //pid = fork ();
1454 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
1455 * it will deadlock. Call the syscall directly instead.
1457 pid = syscall (SYS_fork);
1458 if (pid == 0) {
1459 close (stdout_pipe [0]);
1460 dup2 (stdout_pipe [1], STDOUT_FILENO);
1462 for (i = getdtablesize () - 1; i >= 3; i--)
1463 close (i);
1465 argv [0] = g_find_program_in_path ("gdb");
1466 if (argv [0] == NULL) {
1467 close (STDOUT_FILENO);
1468 exit (1);
1471 argv [1] = "-ex";
1472 sprintf (buf1, "attach %ld", (long)getpid ());
1473 argv [2] = buf1;
1474 argv [3] = "--ex";
1475 argv [4] = "info threads";
1476 argv [5] = "--ex";
1477 argv [6] = "thread apply all bt";
1478 argv [7] = "--batch";
1479 argv [8] = 0;
1481 execv (argv [0], (char**)argv);
1482 exit (1);
1485 close (stdout_pipe [1]);
1487 fprintf (stderr, "\nDebug info from gdb:\n\n");
1489 while (1) {
1490 int nread = read (stdout_pipe [0], buffer, 1024);
1492 if (nread <= 0)
1493 break;
1494 write (STDERR_FILENO, buffer, nread);
1497 waitpid (pid, &status, WNOHANG);
1499 #endif
1501 * A SIGSEGV indicates something went very wrong so we can no longer depend
1502 * on anything working. So try to print out lots of diagnostics, starting
1503 * with ones which have a greater chance of working.
1505 fprintf (stderr,
1506 "\n"
1507 "=================================================================\n"
1508 "Got a %s while executing native code. This usually indicates\n"
1509 "a fatal error in the mono runtime or one of the native libraries \n"
1510 "used by your application.\n"
1511 "=================================================================\n"
1512 "\n", signal_str);
1515 #endif
1517 #ifdef MONO_ARCH_USE_SIGACTION
1519 /* Remove our SIGABRT handler */
1520 sa.sa_handler = SIG_DFL;
1521 sigemptyset (&sa.sa_mask);
1522 sa.sa_flags = 0;
1524 g_assert (sigaction (SIGABRT, &sa, NULL) != -1);
1526 #endif
1528 abort ();
1532 * mono_print_thread_dump:
1534 * Print information about the current thread to stdout.
1535 * SIGCTX can be NULL, allowing this to be called from gdb.
1537 void
1538 mono_print_thread_dump (void *sigctx)
1540 MonoThread *thread = mono_thread_current ();
1541 #if defined(__i386__) || defined(__x86_64__)
1542 MonoContext ctx;
1543 #endif
1544 GString* text = g_string_new (0);
1545 char *name, *wapi_desc;
1546 GError *error = NULL;
1548 if (thread->name) {
1549 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
1550 g_assert (!error);
1551 g_string_append_printf (text, "\n\"%s\"", name);
1552 g_free (name);
1554 else if (thread->threadpool_thread)
1555 g_string_append (text, "\n\"<threadpool thread>\"");
1556 else
1557 g_string_append (text, "\n\"<unnamed thread>\"");
1559 #ifndef PLATFORM_WIN32
1560 wapi_desc = wapi_current_thread_desc ();
1561 g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc);
1562 free (wapi_desc);
1563 #endif
1565 #ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX
1566 if (!sigctx)
1567 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_print_thread_dump);
1568 else
1569 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
1571 mono_jit_walk_stack_from_ctx (print_stack_frame_to_string, &ctx, TRUE, text);
1572 #else
1573 printf ("\t<Stack traces in thread dumps not supported on this platform>\n");
1574 #endif
1576 fprintf (stdout, text->str);
1577 g_string_free (text, TRUE);
1578 fflush (stdout);