2009-01-22 Mark Probst <mark.probst@gmail.com>
[mono-project.git] / mono / mini / mini-exceptions.c
blobc1a47d83878555b7f4e3ccf3577a4d2ffd2f50b0
1 /*
2 * mini-exceptions.c: generic exception support
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
12 #include <signal.h>
13 #include <string.h>
15 #ifdef HAVE_EXECINFO_H
16 #include <execinfo.h>
17 #endif
19 #ifndef PLATFORM_WIN32
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #endif
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
29 #ifdef HAVE_SYS_SYSCALL_H
30 #include <sys/syscall.h>
31 #endif
33 #include <mono/metadata/appdomain.h>
34 #include <mono/metadata/tabledefs.h>
35 #include <mono/metadata/threads.h>
36 #include <mono/metadata/threads-types.h>
37 #include <mono/metadata/debug-helpers.h>
38 #include <mono/metadata/exception.h>
39 #include <mono/metadata/gc-internal.h>
40 #include <mono/metadata/mono-debug.h>
41 #include <mono/metadata/profiler.h>
42 #include <mono/utils/mono-mmap.h>
44 #include "mini.h"
45 #include "trace.h"
47 #ifndef MONO_ARCH_CONTEXT_DEF
48 #define MONO_ARCH_CONTEXT_DEF
49 #endif
51 static gpointer restore_context_func, call_filter_func;
52 static gpointer throw_exception_func, rethrow_exception_func;
53 static gpointer throw_exception_by_name_func, throw_corlib_exception_func;
55 static gpointer try_more_restore_tramp = NULL;
56 static gpointer restore_stack_protection_tramp = NULL;
58 static void try_more_restore (void);
59 static void restore_stack_protection (void);
61 void
62 mono_exceptions_init (void)
64 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
65 guint32 code_size;
66 MonoJumpInfo *ji;
68 if (mono_aot_only) {
69 restore_context_func = mono_aot_get_named_code ("restore_context");
70 call_filter_func = mono_aot_get_named_code ("call_filter");
71 throw_exception_func = mono_aot_get_named_code ("throw_exception");
72 rethrow_exception_func = mono_aot_get_named_code ("rethrow_exception");
73 } else {
74 restore_context_func = mono_arch_get_restore_context_full (&code_size, &ji, FALSE);
75 call_filter_func = mono_arch_get_call_filter_full (&code_size, &ji, FALSE);
76 throw_exception_func = mono_arch_get_throw_exception_full (&code_size, &ji, FALSE);
77 rethrow_exception_func = mono_arch_get_rethrow_exception_full (&code_size, &ji, FALSE);
79 #else
80 restore_context_func = mono_arch_get_restore_context ();
81 call_filter_func = mono_arch_get_call_filter ();
82 throw_exception_func = mono_arch_get_throw_exception ();
83 rethrow_exception_func = mono_arch_get_rethrow_exception ();
84 #endif
85 #ifdef MONO_ARCH_HAVE_RESTORE_STACK_SUPPORT
86 try_more_restore_tramp = mono_create_specific_trampoline (try_more_restore, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
87 restore_stack_protection_tramp = mono_create_specific_trampoline (restore_stack_protection, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL);
88 #endif
91 gpointer
92 mono_get_throw_exception (void)
94 g_assert (throw_exception_func);
95 return throw_exception_func;
98 gpointer
99 mono_get_rethrow_exception (void)
101 g_assert (rethrow_exception_func);
102 return rethrow_exception_func;
105 gpointer
106 mono_get_call_filter (void)
108 g_assert (call_filter_func);
109 return call_filter_func;
112 gpointer
113 mono_get_restore_context (void)
115 g_assert (restore_context_func);
116 return restore_context_func;
119 gpointer
120 mono_get_throw_exception_by_name (void)
122 gpointer code = NULL;
123 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
124 guint32 code_size;
125 MonoJumpInfo *ji;
126 #endif
128 /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
129 if (throw_exception_by_name_func)
130 return throw_exception_by_name_func;
132 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
133 if (mono_aot_only)
134 code = mono_aot_get_named_code ("throw_exception_by_name");
135 else
136 code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, FALSE);
137 #else
138 code = mono_arch_get_throw_exception_by_name ();
139 #endif
141 mono_memory_barrier ();
143 throw_exception_by_name_func = code;
145 return throw_exception_by_name_func;
148 gpointer
149 mono_get_throw_corlib_exception (void)
151 gpointer code = NULL;
152 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
153 guint32 code_size;
154 MonoJumpInfo *ji;
155 #endif
157 /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
158 if (throw_corlib_exception_func)
159 return throw_corlib_exception_func;
161 #if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION
162 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
163 if (mono_aot_only)
164 code = mono_aot_get_named_code ("throw_corlib_exception");
165 else
166 code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, FALSE);
167 #else
168 code = mono_arch_get_throw_corlib_exception ();
169 #endif
170 #else
171 g_assert_not_reached ();
172 #endif
174 mono_memory_barrier ();
176 throw_corlib_exception_func = code;
178 return throw_corlib_exception_func;
181 /* mono_find_jit_info:
183 * This function is used to gather information from @ctx. It return the
184 * MonoJitInfo of the corresponding function, unwinds one stack frame and
185 * stores the resulting context into @new_ctx. It also stores a string
186 * describing the stack location into @trace (if not NULL), and modifies
187 * the @lmf if necessary. @native_offset return the IP offset from the
188 * start of the function or -1 if that info is not available.
190 MonoJitInfo *
191 mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
192 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
193 gboolean *managed)
195 gboolean managed2;
196 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
197 MonoJitInfo *ji;
199 if (trace)
200 *trace = NULL;
202 if (native_offset)
203 *native_offset = -1;
205 if (managed)
206 *managed = FALSE;
208 ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
210 if (ji == (gpointer)-1)
211 return ji;
213 if (managed2 || ji->method->wrapper_type) {
214 const char *real_ip, *start;
215 gint32 offset;
217 start = (const char *)ji->code_start;
218 if (!managed2)
219 /* ctx->ip points into native code */
220 real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx);
221 else
222 real_ip = (const char*)ip;
224 if ((real_ip >= start) && (real_ip <= start + ji->code_size))
225 offset = real_ip - start;
226 else
227 offset = -1;
229 if (native_offset)
230 *native_offset = offset;
232 if (managed)
233 if (!ji->method->wrapper_type)
234 *managed = TRUE;
236 if (trace)
237 *trace = mono_debug_print_stack_frame (ji->method, offset, domain);
238 } else {
239 if (trace) {
240 char *fname = mono_method_full_name (res->method, TRUE);
241 *trace = g_strdup_printf ("in (unmanaged) %s", fname);
242 g_free (fname);
246 return ji;
249 static gpointer
250 get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
252 MonoGenericJitInfo *gi;
254 if (!ji->has_generic_jit_info)
255 return NULL;
256 gi = mono_jit_info_get_generic_jit_info (ji);
257 if (!gi->has_this)
258 return NULL;
260 if (gi->this_in_reg)
261 return mono_arch_context_get_int_reg (ctx, gi->this_reg);
262 else
263 return *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
264 gi->this_offset);
267 static MonoGenericContext
268 get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
270 MonoGenericContext context = { NULL, NULL };
271 MonoClass *class, *method_container_class;
273 g_assert (generic_info);
275 g_assert (ji->method->is_inflated);
276 if (mono_method_get_context (ji->method)->method_inst) {
277 MonoMethodRuntimeGenericContext *mrgctx = generic_info;
279 class = mrgctx->class_vtable->klass;
280 context.method_inst = mrgctx->method_inst;
281 g_assert (context.method_inst);
282 } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) {
283 MonoVTable *vtable = generic_info;
285 class = vtable->klass;
286 } else {
287 MonoObject *this = generic_info;
289 class = this->vtable->klass;
292 if (class->generic_class || class->generic_container)
293 context.class_inst = mini_class_get_context (class)->class_inst;
295 g_assert (!ji->method->klass->generic_container);
296 if (ji->method->klass->generic_class)
297 method_container_class = ji->method->klass->generic_class->container_class;
298 else
299 method_container_class = ji->method->klass;
301 if (class->generic_class)
302 g_assert (mono_class_has_parent_and_ignore_generics (class->generic_class->container_class, method_container_class));
303 else
304 g_assert (mono_class_has_parent_and_ignore_generics (class, method_container_class));
306 return context;
309 static MonoMethod*
310 get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
312 MonoGenericContext context;
313 MonoMethod *method;
315 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
316 return ji->method;
317 context = get_generic_context_from_stack_frame (ji, generic_info);
319 method = mono_method_get_declaring_generic_method (ji->method);
320 method = mono_class_inflate_generic_method (method, &context);
322 return method;
325 MonoString *
326 ves_icall_System_Exception_get_trace (MonoException *ex)
328 MonoDomain *domain = mono_domain_get ();
329 MonoString *res;
330 MonoArray *ta = ex->trace_ips;
331 int i, len;
332 GString *trace_str;
334 if (ta == NULL)
335 /* Exception is not thrown yet */
336 return NULL;
338 len = mono_array_length (ta) >> 1;
339 trace_str = g_string_new ("");
340 for (i = 0; i < len; i++) {
341 MonoJitInfo *ji;
342 gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
343 gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
345 ji = mono_jit_info_table_find (domain, ip);
346 if (ji == NULL) {
347 /* Unmanaged frame */
348 g_string_append_printf (trace_str, "in (unmanaged) %p\n", ip);
349 } else {
350 gchar *location;
351 gint32 address;
352 MonoMethod *method = get_method_from_stack_frame (ji, generic_info);
354 address = (char *)ip - (char *)ji->code_start;
355 location = mono_debug_print_stack_frame (
356 method, address, ex->object.vtable->domain);
358 g_string_append_printf (trace_str, "%s\n", location);
359 g_free (location);
363 res = mono_string_new (ex->object.vtable->domain, trace_str->str);
364 g_string_free (trace_str, TRUE);
366 return res;
369 MonoArray *
370 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
372 MonoDomain *domain = mono_domain_get ();
373 MonoArray *res;
374 MonoArray *ta = exc->trace_ips;
375 MonoDebugSourceLocation *location;
376 int i, len;
378 if (ta == NULL) {
379 /* Exception is not thrown yet */
380 return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
383 len = mono_array_length (ta) >> 1;
385 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
387 for (i = skip; i < len; i++) {
388 MonoJitInfo *ji;
389 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
390 gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
391 gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
392 MonoMethod *method;
394 ji = mono_jit_info_table_find (domain, ip);
395 if (ji == NULL) {
396 /* Unmanaged frame */
397 mono_array_setref (res, i, sf);
398 continue;
401 g_assert (ji != NULL);
403 method = get_method_from_stack_frame (ji, generic_info);
404 if (ji->method->wrapper_type) {
405 char *s;
407 sf->method = NULL;
408 s = mono_method_full_name (method, TRUE);
409 MONO_OBJECT_SETREF (sf, internal_method_name, mono_string_new (domain, s));
410 g_free (s);
412 else
413 MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL));
414 sf->native_offset = (char *)ip - (char *)ji->code_start;
417 * mono_debug_lookup_source_location() returns both the file / line number information
418 * and the IL offset. Note that computing the IL offset is already an expensive
419 * operation, so we shouldn't call this method twice.
421 location = mono_debug_lookup_source_location (ji->method, sf->native_offset, domain);
422 if (location)
423 sf->il_offset = location->il_offset;
424 else
425 sf->il_offset = 0;
427 if (need_file_info) {
428 if (location && location->source_file) {
429 MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
430 sf->line = location->row;
431 sf->column = location->column;
432 } else {
433 sf->line = sf->column = 0;
434 sf->filename = NULL;
438 mono_debug_free_source_location (location);
439 mono_array_setref (res, i, sf);
442 return res;
446 * mono_walk_stack:
447 * @domain: starting appdomain
448 * @jit_tls: JIT data for the thread
449 * @start_ctx: starting state of the stack frame
450 * @func: callback to call for each stack frame
451 * @user_data: data passed to the callback
453 * This function walks the stack of a thread, starting from the state
454 * represented by jit_tls and start_ctx. For each frame the callback
455 * function is called with the relevant info. The walk ends when no more
456 * managed stack frames are found or when the callback returns a TRUE value.
457 * Note that the function can be used to walk the stack of a thread
458 * different from the current.
460 void
461 mono_walk_stack (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gpointer user_data)
463 MonoLMF *lmf = mono_get_lmf ();
464 MonoJitInfo *ji, rji;
465 gint native_offset;
466 gboolean managed;
467 MonoContext ctx, new_ctx;
469 ctx = *start_ctx;
471 while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
473 * FIXME: mono_find_jit_info () will need to be able to return a different
474 * MonoDomain when apddomain transitions are found on the stack.
476 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
477 if (!ji || ji == (gpointer)-1)
478 return;
480 if (func (domain, &new_ctx, ji, user_data))
481 return;
483 ctx = new_ctx;
487 void
488 mono_jit_walk_stack_from_ctx (MonoStackWalk func, MonoContext *start_ctx, gboolean do_il_offset, gpointer user_data)
490 MonoDomain *domain = mono_domain_get ();
491 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
492 MonoLMF *lmf = mono_get_lmf ();
493 MonoJitInfo *ji, rji;
494 gint native_offset, il_offset;
495 gboolean managed;
496 MonoContext ctx, new_ctx;
498 MONO_ARCH_CONTEXT_DEF
500 mono_arch_flush_register_windows ();
502 if (start_ctx) {
503 memcpy (&ctx, start_ctx, sizeof (MonoContext));
504 } else {
505 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
506 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
507 #else
508 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_jit_walk_stack_from_ctx);
509 #endif
512 while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
513 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
514 g_assert (ji);
516 if (ji == (gpointer)-1)
517 return;
519 if (do_il_offset) {
520 MonoDebugSourceLocation *source;
522 source = mono_debug_lookup_source_location (ji->method, native_offset, domain);
523 il_offset = source ? source->il_offset : -1;
524 mono_debug_free_source_location (source);
525 } else
526 il_offset = -1;
528 if (func (ji->method, native_offset, il_offset, managed, user_data))
529 return;
531 ctx = new_ctx;
535 void
536 mono_jit_walk_stack (MonoStackWalk func, gboolean do_il_offset, gpointer user_data)
538 mono_jit_walk_stack_from_ctx (func, NULL, do_il_offset, user_data);
541 MonoBoolean
542 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
543 MonoReflectionMethod **method,
544 gint32 *iloffset, gint32 *native_offset,
545 MonoString **file, gint32 *line, gint32 *column)
547 MonoDomain *domain = mono_domain_get ();
548 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
549 MonoLMF *lmf = mono_get_lmf ();
550 MonoJitInfo *ji, rji;
551 MonoContext ctx, new_ctx, old_ctx;
552 MonoDebugSourceLocation *location;
553 MonoMethod *last_method = NULL, *actual_method;
555 MONO_ARCH_CONTEXT_DEF;
557 mono_arch_flush_register_windows ();
559 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
560 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
561 #else
562 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
563 #endif
565 do {
566 old_ctx = ctx;
567 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
568 ctx = new_ctx;
570 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_SP (&ctx) >= jit_tls->end_of_stack)
571 return FALSE;
573 /* skip all wrappers ??*/
574 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
575 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
576 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
577 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
578 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE ||
579 ji->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
580 continue;
582 if (ji->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && ji->method == last_method) {
584 * FIXME: Native-to-managed wrappers sometimes show up twice.
585 * Probably the whole mono_find_jit_info () stuff needs to be fixed so this
586 * isn't needed.
588 continue;
591 last_method = ji->method;
593 skip--;
595 } while (skip >= 0);
597 actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &old_ctx));
599 *method = mono_method_get_object (domain, actual_method, NULL);
601 location = mono_debug_lookup_source_location (ji->method, *native_offset, domain);
602 if (location)
603 *iloffset = location->il_offset;
604 else
605 *iloffset = 0;
607 if (need_file_info) {
608 if (location) {
609 *file = mono_string_new (domain, location->source_file);
610 *line = location->row;
611 *column = location->column;
612 } else {
613 *file = NULL;
614 *line = *column = 0;
618 mono_debug_free_source_location (location);
620 return TRUE;
623 typedef struct {
624 guint32 skips;
625 MonoSecurityFrame *frame;
626 } MonoFrameSecurityInfo;
628 static gboolean
629 callback_get_first_frame_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
631 MonoFrameSecurityInfo *si = (MonoFrameSecurityInfo*) data;
633 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
634 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
635 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
636 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
637 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
638 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
639 return FALSE;
642 if (si->skips > 0) {
643 si->skips--;
644 return FALSE;
647 si->frame = mono_declsec_create_frame (domain, ji);
649 /* Stop - we only want the first frame (e.g. LinkDemand and InheritanceDemand) */
650 return TRUE;
654 * ves_icall_System_Security_SecurityFrame_GetSecurityFrame:
655 * @skip: the number of stack frames to skip
657 * This function returns a the security informations of a single stack frame
658 * (after the skipped ones). This is required for [NonCas]LinkDemand[Choice]
659 * and [NonCas]InheritanceDemand[Choice] as only the caller security is
660 * evaluated.
662 MonoSecurityFrame*
663 ves_icall_System_Security_SecurityFrame_GetSecurityFrame (gint32 skip)
665 MonoDomain *domain = mono_domain_get ();
666 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
667 MonoFrameSecurityInfo si;
668 MonoContext ctx;
670 MONO_ARCH_CONTEXT_DEF
672 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
673 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
674 #else
675 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityFrame);
676 #endif
678 #if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
679 skip--;
680 #endif
682 si.skips = skip;
683 si.frame = NULL;
684 mono_walk_stack (domain, jit_tls, &ctx, callback_get_first_frame_security_info, (gpointer)&si);
686 return (si.skips == 0) ? si.frame : NULL;
690 typedef struct {
691 guint32 skips;
692 MonoArray *stack;
693 guint32 count;
694 guint32 maximum;
695 } MonoSecurityStack;
697 static void
698 grow_array (MonoSecurityStack *stack)
700 MonoDomain *domain = mono_domain_get ();
701 guint32 newsize = (stack->maximum << 1);
702 MonoArray *newstack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, newsize);
703 int i;
704 for (i=0; i < stack->maximum; i++) {
705 gpointer frame = mono_array_get (stack->stack, gpointer, i);
706 mono_array_setref (newstack, i, frame);
708 stack->maximum = newsize;
709 stack->stack = newstack;
712 static gboolean
713 callback_get_stack_frames_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
715 MonoSecurityStack *ss = (MonoSecurityStack*) data;
717 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
718 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
719 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
720 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
721 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
722 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
723 return FALSE;
726 if (ss->skips > 0) {
727 ss->skips--;
728 return FALSE;
731 if (ss->count == ss->maximum)
732 grow_array (ss);
734 mono_array_setref (ss->stack, ss->count++, mono_declsec_create_frame (domain, ji));
736 /* continue down the stack */
737 return FALSE;
740 static MonoArray *
741 glist_to_array (GList *list, MonoClass *eclass)
743 MonoDomain *domain = mono_domain_get ();
744 MonoArray *res;
745 int len, i;
747 if (!list)
748 return NULL;
750 len = g_list_length (list);
751 res = mono_array_new (domain, eclass, len);
753 for (i = 0; list; list = list->next, i++)
754 mono_array_set (res, gpointer, i, list->data);
756 return res;
760 * ves_icall_System_Security_SecurityFrame_GetSecurityStack:
761 * @skip: the number of stack frames to skip
763 * This function returns an managed array of containing the security
764 * informations for each frame (after the skipped ones). This is used for
765 * [NonCas]Demand[Choice] where the complete evaluation of the stack is
766 * required.
768 MonoArray*
769 ves_icall_System_Security_SecurityFrame_GetSecurityStack (gint32 skip)
771 MonoDomain *domain = mono_domain_get ();
772 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
773 MonoSecurityStack ss;
774 MonoContext ctx;
776 MONO_ARCH_CONTEXT_DEF
778 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
779 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
780 #else
781 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityStack);
782 #endif
784 #if defined(__ia64__) || defined(__s390__) || defined(__s390x__)
785 skip--;
786 #endif
788 ss.skips = skip;
789 ss.count = 0;
790 ss.maximum = MONO_CAS_INITIAL_STACK_SIZE;
791 ss.stack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, ss.maximum);
792 mono_walk_stack (domain, jit_tls, &ctx, callback_get_stack_frames_security_info, (gpointer)&ss);
793 /* g_warning ("STACK RESULT: %d out of %d", ss.count, ss.maximum); */
794 return ss.stack;
797 static MonoClass*
798 get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContext *ctx)
800 MonoClass *catch_class = ei->data.catch_class;
801 MonoType *inflated_type;
802 MonoGenericContext context;
804 if (!catch_class)
805 return NULL;
807 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
808 return catch_class;
809 context = get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx));
811 /* FIXME: we shouldn't inflate but instead put the
812 type in the rgctx and fetch it from there. It
813 might be a good idea to do this lazily, i.e. only
814 when the exception is actually thrown, so as not to
815 waste space for exception clauses which might never
816 be encountered. */
817 inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg, &context);
818 catch_class = mono_class_from_mono_type (inflated_type);
819 mono_metadata_free_type (inflated_type);
821 return catch_class;
825 * mono_handle_exception_internal:
826 * @ctx: saved processor state
827 * @obj: the exception object
828 * @test_only: only test if the exception is caught, but dont call handlers
829 * @out_filter_idx: out parameter. if test_only is true, set to the index of
830 * the first filter clause which caught the exception.
832 static gboolean
833 mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gint32 *out_filter_idx)
835 MonoDomain *domain = mono_domain_get ();
836 MonoJitInfo *ji, rji;
837 static int (*call_filter) (MonoContext *, gpointer) = NULL;
838 static void (*restore_context) (void *);
839 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
840 MonoLMF *lmf = mono_get_lmf ();
841 MonoArray *initial_trace_ips = NULL;
842 GList *trace_ips = NULL;
843 MonoException *mono_ex;
844 gboolean stack_overflow = FALSE;
845 MonoContext initial_ctx;
846 int frame_count = 0;
847 gboolean has_dynamic_methods = FALSE;
848 gint32 filter_idx, first_filter_idx;
850 g_assert (ctx != NULL);
851 if (!obj) {
852 MonoException *ex = mono_get_exception_null_reference ();
853 MONO_OBJECT_SETREF (ex, message, mono_string_new (domain, "Object reference not set to an instance of an object"));
854 obj = (MonoObject *)ex;
858 * Allocate a new exception object instead of the preconstructed ones.
860 if (obj == domain->stack_overflow_ex) {
862 * It is not a good idea to try and put even more pressure on the little stack available.
863 * obj = mono_get_exception_stack_overflow ();
865 stack_overflow = TRUE;
867 else if (obj == domain->null_reference_ex) {
868 obj = mono_get_exception_null_reference ();
871 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
872 mono_ex = (MonoException*)obj;
873 initial_trace_ips = mono_ex->trace_ips;
874 } else {
875 mono_ex = NULL;
878 if (mono_ex && jit_tls->class_cast_from && !strcmp (mono_ex->object.vtable->klass->name, "InvalidCastException")) {
879 char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
880 char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
881 char *msg = g_strdup_printf ("Unable to cast object of type '%s' to type '%s'.", from_name, to_name);
882 mono_ex->message = mono_string_new (domain, msg);
883 g_free (from_name);
884 g_free (to_name);
885 g_free (msg);
888 if (!call_filter)
889 call_filter = mono_get_call_filter ();
891 if (!restore_context)
892 restore_context = mono_get_restore_context ();
894 g_assert (jit_tls->end_of_stack);
895 g_assert (jit_tls->abort_func);
897 if (!test_only) {
898 MonoContext ctx_cp = *ctx;
899 if (mono_trace_is_enabled ())
900 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
901 mono_profiler_exception_thrown (obj);
902 if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, &first_filter_idx)) {
903 if (mono_break_on_exc)
904 G_BREAKPOINT ();
905 // FIXME: This runs managed code so it might cause another stack overflow when
906 // we are handling a stack overflow
907 mono_unhandled_exception (obj);
909 if (mono_debugger_unhandled_exception (original_ip, MONO_CONTEXT_GET_SP (ctx), obj)) {
911 * If this returns true, then we're running inside the
912 * Mono Debugger and the debugger wants us to restore the
913 * context and continue (normally, the debugger inserts
914 * a breakpoint on the `original_ip', so it regains control
915 * immediately after restoring the context).
917 MONO_CONTEXT_SET_IP (ctx, original_ip);
918 restore_context (ctx);
919 g_assert_not_reached ();
924 if (out_filter_idx)
925 *out_filter_idx = -1;
926 filter_idx = 0;
927 initial_ctx = *ctx;
928 memset (&rji, 0, sizeof (rji));
930 while (1) {
931 MonoContext new_ctx;
932 guint32 free_stack;
934 ji = mono_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx,
935 NULL, &lmf, NULL, NULL);
936 if (!ji) {
937 g_warning ("Exception inside function without unwind info");
938 g_assert_not_reached ();
941 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))))) {
943 * The exception was raised in native code and we got back to managed code
944 * using the LMF.
946 *ctx = new_ctx;
947 continue;
950 if (ji != (gpointer)-1) {
951 frame_count ++;
952 //printf ("M: %s %d %d.\n", mono_method_full_name (ji->method, TRUE), frame_count, test_only);
954 if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
956 * Avoid overwriting the stack trace if the exception is
957 * rethrown. Also avoid giant stack traces during a stack
958 * overflow.
960 if (!initial_trace_ips && (frame_count < 1000)) {
961 trace_ips = g_list_prepend (trace_ips, MONO_CONTEXT_GET_IP (ctx));
962 trace_ips = g_list_prepend (trace_ips,
963 get_generic_info_from_stack_frame (ji, ctx));
967 if (ji->method->dynamic)
968 has_dynamic_methods = TRUE;
970 if (stack_overflow)
971 #ifndef MONO_ARCH_STACK_GROWS_UP
972 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
973 #else
974 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
975 #endif
976 else
977 free_stack = 0xffffff;
980 * During stack overflow, wait till the unwinding frees some stack
981 * space before running handlers/finalizers.
983 if ((free_stack > (64 * 1024)) && ji->num_clauses) {
984 int i;
986 for (i = 0; i < ji->num_clauses; i++) {
987 MonoJitExceptionInfo *ei = &ji->clauses [i];
988 gboolean filtered = FALSE;
990 #if defined(__s390__)
992 * This is required in cases where a try block starts immediately after
993 * a call which causes an exception. Testcase: tests/exception8.cs.
994 * FIXME: Clean this up.
996 if (ei->try_start < MONO_CONTEXT_GET_IP (ctx) &&
997 #else
998 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
999 #endif
1000 MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
1001 /* catch block */
1002 MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
1004 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
1005 /* store the exception object in bp + ei->exvar_offset */
1006 *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = obj;
1009 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
1010 // mono_debugger_handle_exception (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
1011 if (test_only) {
1012 mono_perfcounters->exceptions_filters++;
1013 filtered = call_filter (ctx, ei->data.filter);
1014 if (filtered && out_filter_idx)
1015 *out_filter_idx = filter_idx;
1017 else {
1019 * Filter clauses should only be run in the
1020 * first pass of exception handling.
1022 filtered = (filter_idx == first_filter_idx);
1024 filter_idx ++;
1027 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
1028 mono_object_isinst (obj, catch_class)) || filtered) {
1029 if (test_only) {
1030 if (mono_ex && !initial_trace_ips) {
1031 trace_ips = g_list_reverse (trace_ips);
1032 MONO_OBJECT_SETREF (mono_ex, trace_ips, glist_to_array (trace_ips, mono_defaults.int_class));
1033 if (has_dynamic_methods)
1034 /* These methods could go away anytime, so compute the stack trace now */
1035 MONO_OBJECT_SETREF (mono_ex, stack_trace, ves_icall_System_Exception_get_trace (mono_ex));
1037 g_list_free (trace_ips);
1039 return TRUE;
1041 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1042 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1043 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1044 mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1045 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
1046 *(mono_get_lmf_addr ()) = lmf;
1047 mono_perfcounters->exceptions_depth += frame_count;
1048 if (obj == domain->stack_overflow_ex)
1049 jit_tls->handling_stack_ovf = FALSE;
1051 return 0;
1053 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1054 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1055 (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT)) {
1056 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1057 g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1058 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1059 mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1060 call_filter (ctx, ei->handler_start);
1062 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1063 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1064 (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
1065 if (mono_trace_is_enabled () && mono_trace_eval (ji->method))
1066 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1067 mono_profiler_exception_clause_handler (ji->method, ei->flags, i);
1068 mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
1069 mono_perfcounters->exceptions_finallys++;
1070 call_filter (ctx, ei->handler_start);
1076 if (!test_only)
1077 mono_profiler_exception_method_leave (ji->method);
1080 *ctx = new_ctx;
1082 if (ji == (gpointer)-1) {
1084 if (!test_only) {
1085 *(mono_get_lmf_addr ()) = lmf;
1087 jit_tls->abort_func (obj);
1088 g_assert_not_reached ();
1089 } else {
1090 if (mono_ex && !initial_trace_ips) {
1091 trace_ips = g_list_reverse (trace_ips);
1092 MONO_OBJECT_SETREF (mono_ex, trace_ips, glist_to_array (trace_ips, mono_defaults.int_class));
1093 if (has_dynamic_methods)
1094 /* These methods could go away anytime, so compute the stack trace now */
1095 MONO_OBJECT_SETREF (mono_ex, stack_trace, ves_icall_System_Exception_get_trace (mono_ex));
1097 g_list_free (trace_ips);
1098 return FALSE;
1103 g_assert_not_reached ();
1107 * mono_debugger_run_finally:
1108 * @start_ctx: saved processor state
1110 * This method is called by the Mono Debugger to call all `finally' clauses of the
1111 * current stack frame. It's used when the user issues a `return' command to make
1112 * the current stack frame return. After returning from this method, the debugger
1113 * unwinds the stack one frame and gives control back to the user.
1115 * NOTE: This method is only used when running inside the Mono Debugger.
1117 void
1118 mono_debugger_run_finally (MonoContext *start_ctx)
1120 static int (*call_filter) (MonoContext *, gpointer) = NULL;
1121 MonoDomain *domain = mono_domain_get ();
1122 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1123 MonoLMF *lmf = mono_get_lmf ();
1124 MonoContext ctx, new_ctx;
1125 MonoJitInfo *ji, rji;
1126 int i;
1128 ctx = *start_ctx;
1130 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, NULL);
1131 if (!ji || ji == (gpointer)-1)
1132 return;
1134 if (!call_filter)
1135 call_filter = mono_get_call_filter ();
1137 for (i = 0; i < ji->num_clauses; i++) {
1138 MonoJitExceptionInfo *ei = &ji->clauses [i];
1140 if ((ei->try_start <= MONO_CONTEXT_GET_IP (&ctx)) &&
1141 (MONO_CONTEXT_GET_IP (&ctx) < ei->try_end) &&
1142 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
1143 call_filter (&ctx, ei->handler_start);
1149 * mono_handle_exception:
1150 * @ctx: saved processor state
1151 * @obj: the exception object
1152 * @test_only: only test if the exception is caught, but dont call handlers
1154 gboolean
1155 mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only)
1157 if (!test_only)
1158 mono_perfcounters->exceptions_thrown++;
1159 return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL);
1162 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
1164 #ifndef MONO_ARCH_USE_SIGACTION
1165 #error "Can't use sigaltstack without sigaction"
1166 #endif
1168 void
1169 mono_setup_altstack (MonoJitTlsData *tls)
1171 size_t stsize = 0;
1172 struct sigaltstack sa;
1173 guint8 *staddr = NULL;
1175 if (mono_running_on_valgrind ())
1176 return;
1178 mono_thread_get_stack_bounds (&staddr, &stsize);
1180 g_assert (staddr);
1182 tls->end_of_stack = staddr + stsize;
1184 /*g_print ("thread %p, stack_base: %p, stack_size: %d\n", (gpointer)pthread_self (), staddr, stsize);*/
1186 tls->stack_ovf_guard_base = staddr + mono_pagesize ();
1187 tls->stack_ovf_guard_size = mono_pagesize () * 8;
1189 if (mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE)) {
1190 /* mprotect can fail for the main thread stack */
1191 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);
1192 g_assert (gaddr == tls->stack_ovf_guard_base);
1196 * threads created by nptl does not seem to have a guard page, and
1197 * since the main thread is not created by us, we can't even set one.
1198 * Increasing stsize fools the SIGSEGV signal handler into thinking this
1199 * is a stack overflow exception.
1201 tls->stack_size = stsize + mono_pagesize ();
1203 /* Setup an alternate signal stack */
1204 tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON);
1205 tls->signal_stack_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1207 g_assert (tls->signal_stack);
1209 sa.ss_sp = tls->signal_stack;
1210 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1211 sa.ss_flags = SS_ONSTACK;
1212 sigaltstack (&sa, NULL);
1215 void
1216 mono_free_altstack (MonoJitTlsData *tls)
1218 struct sigaltstack sa;
1219 int err;
1221 sa.ss_sp = tls->signal_stack;
1222 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
1223 sa.ss_flags = SS_DISABLE;
1224 err = sigaltstack (&sa, NULL);
1225 g_assert (err == 0);
1227 if (tls->signal_stack)
1228 mono_vfree (tls->signal_stack, MONO_ARCH_SIGNAL_STACK_SIZE);
1231 #else /* !MONO_ARCH_SIGSEGV_ON_ALTSTACK */
1233 void
1234 mono_setup_altstack (MonoJitTlsData *tls)
1238 void
1239 mono_free_altstack (MonoJitTlsData *tls)
1243 #endif /* MONO_ARCH_SIGSEGV_ON_ALTSTACK */
1245 static gboolean
1246 try_restore_stack_protection (MonoJitTlsData *jit_tls, int extra_bytes)
1248 gint32 unprotect_size = jit_tls->stack_ovf_guard_size;
1249 /* we need to leave some room for throwing the exception */
1250 while (unprotect_size >= 0 && (char*)jit_tls->stack_ovf_guard_base + unprotect_size > ((char*)&unprotect_size - extra_bytes))
1251 unprotect_size -= mono_pagesize ();
1252 /* at this point we could try and build a new domain->stack_overflow_ex, but only if there
1253 * is sufficient stack
1255 //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);
1256 if (unprotect_size)
1257 mono_mprotect (jit_tls->stack_ovf_guard_base, unprotect_size, MONO_MMAP_NONE);
1258 return unprotect_size == jit_tls->stack_ovf_guard_size;
1261 static void
1262 try_more_restore (void)
1264 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1265 if (try_restore_stack_protection (jit_tls, 500))
1266 jit_tls->restore_stack_prot = NULL;
1269 static void
1270 restore_stack_protection (void)
1272 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1273 MonoException *ex = mono_domain_get ()->stack_overflow_ex;
1274 /* if we can't restore the stack protection, keep a callback installed so
1275 * we'll try to restore as much stack as we can at each return from unmanaged
1276 * code.
1278 if (try_restore_stack_protection (jit_tls, 4096))
1279 jit_tls->restore_stack_prot = NULL;
1280 else
1281 jit_tls->restore_stack_prot = try_more_restore_tramp;
1282 /* here we also throw a stack overflow exception */
1283 ex->trace_ips = NULL;
1284 ex->stack_trace = NULL;
1285 mono_raise_exception (ex);
1288 gpointer
1289 mono_altstack_restore_prot (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
1291 void (*func)(void) = (gpointer)tramp_data;
1292 func ();
1293 return NULL;
1296 gboolean
1297 mono_handle_soft_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, void *ctx, guint8* fault_addr)
1299 /* we got a stack overflow in the soft-guard pages
1300 * There are two cases:
1301 * 1) managed code caused the overflow: we unprotect the soft-guard page
1302 * and let the arch-specific code trigger the exception handling mechanism
1303 * in the thread stack. The soft-guard pages will be protected again as the stack is unwound.
1304 * 2) unmanaged code caused the overflow: we unprotect the soft-guard page
1305 * and hope we can continue with those enabled, at least until the hard-guard page
1306 * is hit. The alternative to continuing here is to just print a message and abort.
1307 * We may add in the future the code to protect the pages again in the codepath
1308 * when we return from unmanaged to managed code.
1310 if (jit_tls->stack_ovf_guard_size && fault_addr >= (guint8*)jit_tls->stack_ovf_guard_base &&
1311 fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) {
1312 /* we unprotect the minimum amount we can */
1313 guint32 guard_size;
1314 gboolean handled = FALSE;
1316 guard_size = jit_tls->stack_ovf_guard_size - (mono_pagesize () * SIZEOF_VOID_P / 4);
1317 while (guard_size && fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + guard_size) {
1318 guard_size -= mono_pagesize ();
1320 guard_size = jit_tls->stack_ovf_guard_size - guard_size;
1321 /*fprintf (stderr, "unprotecting: %d\n", guard_size);*/
1322 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);
1323 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
1324 if (ji) {
1325 mono_arch_handle_altstack_exception (ctx, fault_addr, TRUE);
1326 handled = TRUE;
1328 #endif
1329 if (!handled) {
1330 /* We print a message: after this even managed stack overflows
1331 * may crash the runtime
1333 fprintf (stderr, "Stack overflow in unmanaged: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), fault_addr);
1334 if (!jit_tls->handling_stack_ovf) {
1335 jit_tls->restore_stack_prot = restore_stack_protection_tramp;
1336 jit_tls->handling_stack_ovf = 1;
1337 } else {
1338 /*fprintf (stderr, "Already handling stack overflow\n");*/
1341 return TRUE;
1343 return FALSE;
1346 static gboolean
1347 print_stack_frame (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
1349 FILE *stream = (FILE*)data;
1351 if (method) {
1352 gchar *location = mono_debug_print_stack_frame (method, native_offset, mono_domain_get ());
1353 fprintf (stream, " %s\n", location);
1354 g_free (location);
1355 } else
1356 fprintf (stream, " at <unknown> <0x%05x>\n", native_offset);
1358 return FALSE;
1361 static G_GNUC_UNUSED gboolean
1362 print_stack_frame_to_string (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed,
1363 gpointer data)
1365 GString *p = (GString*)data;
1367 if (method) {
1368 gchar *location = mono_debug_print_stack_frame (method, native_offset, mono_domain_get ());
1369 g_string_append_printf (p, " %s\n", location);
1370 g_free (location);
1371 } else
1372 g_string_append_printf (p, " at <unknown> <0x%05x>\n", native_offset);
1374 return FALSE;
1377 static gboolean handling_sigsegv = FALSE;
1380 * mono_handle_native_sigsegv:
1382 * Handle a SIGSEGV received while in native code by printing diagnostic
1383 * information and aborting.
1385 void
1386 mono_handle_native_sigsegv (int signal, void *ctx)
1388 #ifndef PLATFORM_WIN32
1389 struct sigaction sa;
1390 #endif
1391 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1393 if (handling_sigsegv)
1394 return;
1396 /* To prevent infinite loops when the stack walk causes a crash */
1397 handling_sigsegv = TRUE;
1399 /* !jit_tls means the thread was not registered with the runtime */
1400 if (jit_tls) {
1401 fprintf (stderr, "Stacktrace:\n\n");
1403 mono_jit_walk_stack (print_stack_frame, TRUE, stderr);
1405 fflush (stderr);
1408 #ifdef HAVE_BACKTRACE_SYMBOLS
1410 void *array [256];
1411 char **names;
1412 int i, size;
1413 const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT";
1415 fprintf (stderr, "\nNative stacktrace:\n\n");
1417 size = backtrace (array, 256);
1418 names = backtrace_symbols (array, size);
1419 for (i =0; i < size; ++i) {
1420 fprintf (stderr, "\t%s\n", names [i]);
1422 free (names);
1424 fflush (stderr);
1426 /* Try to get more meaningful information using gdb */
1428 #if !defined(PLATFORM_WIN32) && defined(HAVE_SYS_SYSCALL_H) && defined(SYS_fork)
1429 if (!mini_get_debug_options ()->no_gdb_backtrace && !mono_debug_using_mono_debugger ()) {
1430 /* From g_spawn_command_line_sync () in eglib */
1431 int res;
1432 int stdout_pipe [2] = { -1, -1 };
1433 pid_t pid;
1434 const char *argv [16];
1435 char buf1 [128];
1436 int status;
1437 char buffer [1024];
1439 res = pipe (stdout_pipe);
1440 g_assert (res != -1);
1442 //pid = fork ();
1444 * glibc fork acquires some locks, so if the crash happened inside malloc/free,
1445 * it will deadlock. Call the syscall directly instead.
1447 pid = syscall (SYS_fork);
1448 if (pid == 0) {
1449 close (stdout_pipe [0]);
1450 dup2 (stdout_pipe [1], STDOUT_FILENO);
1452 for (i = getdtablesize () - 1; i >= 3; i--)
1453 close (i);
1455 argv [0] = g_find_program_in_path ("gdb");
1456 if (argv [0] == NULL) {
1457 close (STDOUT_FILENO);
1458 exit (1);
1461 argv [1] = "-ex";
1462 sprintf (buf1, "attach %ld", (long)getpid ());
1463 argv [2] = buf1;
1464 argv [3] = "--ex";
1465 argv [4] = "info threads";
1466 argv [5] = "--ex";
1467 argv [6] = "thread apply all bt";
1468 argv [7] = "--batch";
1469 argv [8] = 0;
1471 execv (argv [0], (char**)argv);
1472 exit (1);
1475 close (stdout_pipe [1]);
1477 fprintf (stderr, "\nDebug info from gdb:\n\n");
1479 while (1) {
1480 int nread = read (stdout_pipe [0], buffer, 1024);
1482 if (nread <= 0)
1483 break;
1484 write (STDERR_FILENO, buffer, nread);
1487 waitpid (pid, &status, WNOHANG);
1489 #endif
1491 * A SIGSEGV indicates something went very wrong so we can no longer depend
1492 * on anything working. So try to print out lots of diagnostics, starting
1493 * with ones which have a greater chance of working.
1495 fprintf (stderr,
1496 "\n"
1497 "=================================================================\n"
1498 "Got a %s while executing native code. This usually indicates\n"
1499 "a fatal error in the mono runtime or one of the native libraries \n"
1500 "used by your application.\n"
1501 "=================================================================\n"
1502 "\n", signal_str);
1505 #endif
1507 #ifndef PLATFORM_WIN32
1509 /* Remove our SIGABRT handler */
1510 sa.sa_handler = SIG_DFL;
1511 sigemptyset (&sa.sa_mask);
1512 sa.sa_flags = 0;
1514 g_assert (sigaction (SIGABRT, &sa, NULL) != -1);
1516 #endif
1518 abort ();
1522 * mono_print_thread_dump:
1524 * Print information about the current thread to stdout.
1525 * SIGCTX can be NULL, allowing this to be called from gdb.
1527 void
1528 mono_print_thread_dump (void *sigctx)
1530 MonoThread *thread = mono_thread_current ();
1531 #if defined(__i386__) || defined(__x86_64__)
1532 MonoContext ctx;
1533 #endif
1534 GString* text = g_string_new (0);
1535 char *name, *wapi_desc;
1536 GError *error = NULL;
1538 if (thread->name) {
1539 name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, &error);
1540 g_assert (!error);
1541 g_string_append_printf (text, "\n\"%s\"", name);
1542 g_free (name);
1544 else if (thread->threadpool_thread)
1545 g_string_append (text, "\n\"<threadpool thread>\"");
1546 else
1547 g_string_append (text, "\n\"<unnamed thread>\"");
1549 #ifndef PLATFORM_WIN32
1550 wapi_desc = wapi_current_thread_desc ();
1551 g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc);
1552 free (wapi_desc);
1553 #endif
1555 /* FIXME: */
1556 #if defined(__i386__) || defined(__x86_64__)
1557 if (!sigctx)
1558 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_print_thread_dump);
1559 else
1560 mono_arch_sigctx_to_monoctx (sigctx, &ctx);
1562 mono_jit_walk_stack_from_ctx (print_stack_frame_to_string, &ctx, TRUE, text);
1563 #else
1564 printf ("\t<Stack traces in thread dumps not supported on this platform>\n");
1565 #endif
1567 fprintf (stdout, text->str);
1568 g_string_free (text, TRUE);
1569 fflush (stdout);