2 * mini-exceptions.c: generic exception support
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/debug-helpers.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/gc-internal.h>
21 #include <mono/metadata/mono-debug.h>
22 #include <mono/metadata/mono-debug-debugger.h>
26 #define IS_ON_SIGALTSTACK(jit_tls) ((jit_tls) && ((guint8*)&(jit_tls) > (guint8*)(jit_tls)->signal_stack) && ((guint8*)&(jit_tls) < ((guint8*)(jit_tls)->signal_stack + (jit_tls)->signal_stack_size)))
28 #ifndef MONO_ARCH_CONTEXT_DEF
29 #define MONO_ARCH_CONTEXT_DEF
32 #ifndef MONO_INIT_CONTEXT_FROM_CALLER
33 #define MONO_INIT_CONTEXT_FROM_CALLER(ctx) do { \
34 MONO_CONTEXT_SET_IP ((ctx), __builtin_return_address (0)); \
35 MONO_CONTEXT_SET_BP ((ctx), __builtin_frame_address (1)); \
39 #ifndef mono_find_jit_info
41 /* mono_find_jit_info:
43 * This function is used to gather information from @ctx. It return the
44 * MonoJitInfo of the corresponding function, unwinds one stack frame and
45 * stores the resulting context into @new_ctx. It also stores a string
46 * describing the stack location into @trace (if not NULL), and modifies
47 * the @lmf if necessary. @native_offset return the IP offset from the
48 * start of the function or -1 if that info is not available.
51 mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
52 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
56 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
68 ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, NULL, lmf, NULL, &managed2);
70 if (ji == (gpointer)-1)
73 if (managed2 || ji->method->wrapper_type) {
74 char *source_location, *tmpaddr, *fname;
75 gint32 offset, iloffset;
78 /* ctx->ip points into native code */
79 offset = (char*)MONO_CONTEXT_GET_IP (new_ctx) - (char*)ji->code_start;
81 offset = (char *)ip - (char *)ji->code_start;
84 *native_offset = offset;
87 if (!ji->method->wrapper_type)
91 source_location = mono_debug_source_location_from_address (ji->method, offset, NULL, domain);
92 iloffset = mono_debug_il_offset_from_address (ji->method, offset, domain);
95 tmpaddr = g_strdup_printf ("<0x%05x>", offset);
97 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
99 fname = mono_method_full_name (ji->method, TRUE);
102 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
104 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
107 g_free (source_location);
113 char *fname = mono_method_full_name (res->method, TRUE);
114 *trace = g_strdup_printf ("in (unmanaged) %s", fname);
122 #endif /* mono_find_jit_info */
125 ves_icall_System_Exception_get_trace (MonoException *ex)
127 MonoDomain *domain = mono_domain_get ();
129 MonoArray *ta = ex->trace_ips;
135 /* Exception is not thrown yet */
138 len = mono_array_length (ta);
139 trace_str = g_string_new ("");
140 for (i = 0; i < len; i++) {
142 gpointer ip = mono_array_get (ta, gpointer, i);
144 ji = mono_jit_info_table_find (domain, ip);
146 /* Unmanaged frame */
147 g_string_append_printf (trace_str, "in (unmanaged) %p\n", ip);
149 char *source_location, *fname;
150 gint32 address, iloffset;
152 address = (char *)ip - (char *)ji->code_start;
154 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, ex->object.vtable->domain);
155 iloffset = mono_debug_il_offset_from_address (ji->method, address, ex->object.vtable->domain);
158 sprintf (tmpaddr, "<0x%05x>", address);
160 sprintf (tmpaddr, "[0x%05x]", iloffset);
162 fname = mono_method_full_name (ji->method, TRUE);
165 g_string_append_printf (trace_str, "in %s (at %s) %s\n", tmpaddr, source_location, fname);
167 g_string_append_printf (trace_str, "in %s %s\n", tmpaddr, fname);
170 g_free (source_location);
174 res = mono_string_new (ex->object.vtable->domain, trace_str->str);
175 g_string_free (trace_str, TRUE);
181 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
183 MonoDomain *domain = mono_domain_get ();
185 MonoArray *ta = exc->trace_ips;
189 /* Exception is not thrown yet */
190 return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
193 len = mono_array_length (ta);
195 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
197 for (i = skip; i < len; i++) {
199 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
200 gpointer ip = mono_array_get (ta, gpointer, i);
202 ji = mono_jit_info_table_find (domain, ip);
204 /* Unmanaged frame */
205 mono_array_set (res, gpointer, i, sf);
209 g_assert (ji != NULL);
211 if (ji->method->wrapper_type) {
215 s = mono_method_full_name (ji->method, TRUE);
216 sf->internal_method_name = mono_string_new (domain, s);
220 sf->method = mono_method_get_object (domain, ji->method, NULL);
221 sf->native_offset = (char *)ip - (char *)ji->code_start;
223 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset, domain);
225 if (need_file_info) {
228 filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
230 sf->filename = filename? mono_string_new (domain, filename): NULL;
236 mono_array_set (res, gpointer, i, sf);
244 * @domain: starting appdomain
245 * @jit_tls: JIT data for the thread
246 * @start_ctx: starting state of the stack frame
247 * @func: callback to call for each stack frame
248 * @user_data: data passed to the callback
250 * This function walks the stack of a thread, starting from the state
251 * represented by jit_tls and start_ctx. For each frame the callback
252 * function is called with the relevant info. The walk ends when no more
253 * managed stack frames are found or when the callback returns a TRUE value.
254 * Note that the function can be used to walk the stack of a thread
255 * different from the current.
258 mono_walk_stack (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start_ctx, MonoStackFrameWalk func, gpointer user_data)
260 MonoLMF *lmf = jit_tls->lmf;
261 MonoJitInfo *ji, rji;
264 MonoContext ctx, new_ctx;
268 while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
270 * FIXME: mono_find_jit_info () will need to be able to return a different
271 * MonoDomain when apddomain transitions are found on the stack.
273 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
274 if (!ji || ji == (gpointer)-1)
277 if (func (domain, &new_ctx, ji, user_data))
284 #ifndef CUSTOM_STACK_WALK
287 mono_jit_walk_stack (MonoStackWalk func, gboolean do_il_offset, gpointer user_data) {
288 MonoDomain *domain = mono_domain_get ();
289 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
290 MonoLMF *lmf = jit_tls->lmf;
291 MonoJitInfo *ji, rji;
292 gint native_offset, il_offset;
294 MonoContext ctx, new_ctx;
296 MONO_ARCH_CONTEXT_DEF
298 mono_arch_flush_register_windows ();
300 #ifdef MONO_INIT_CONTEXT_FROM_CURRENT
301 MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
303 MONO_INIT_CONTEXT_FROM_CALLER (&ctx);
306 while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
308 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
311 if (ji == (gpointer)-1)
314 il_offset = do_il_offset ? mono_debug_il_offset_from_address (ji->method, native_offset, domain): -1;
316 if (func (ji->method, native_offset, il_offset, managed, user_data))
324 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
325 MonoReflectionMethod **method,
326 gint32 *iloffset, gint32 *native_offset,
327 MonoString **file, gint32 *line, gint32 *column)
329 MonoDomain *domain = mono_domain_get ();
330 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
331 MonoLMF *lmf = jit_tls->lmf;
332 MonoJitInfo *ji, rji;
333 MonoContext ctx, new_ctx;
335 MONO_ARCH_CONTEXT_DEF;
337 mono_arch_flush_register_windows ();
339 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
344 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
348 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
351 /* skip all wrappers ??*/
352 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
353 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
354 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
355 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
356 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE)
363 *method = mono_method_get_object (domain, ji->method, NULL);
364 *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset, domain);
366 if (need_file_info) {
369 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
371 *file = filename? mono_string_new (domain, filename): NULL;
380 #endif /* CUSTOM_STACK_WALK */
384 MonoSecurityFrame *frame;
385 } MonoFrameSecurityInfo;
388 callback_get_first_frame_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
390 MonoFrameSecurityInfo *si = (MonoFrameSecurityInfo*) data;
392 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
393 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
394 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
395 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
396 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
397 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
406 si->frame = mono_declsec_create_frame (domain, ji);
408 /* Stop - we only want the first frame (e.g. LinkDemand and InheritanceDemand) */
413 * ves_icall_System_Security_SecurityFrame_GetSecurityFrame:
414 * @skip: the number of stack frames to skip
416 * This function returns a the security informations of a single stack frame
417 * (after the skipped ones). This is required for [NonCas]LinkDemand[Choice]
418 * and [NonCas]InheritanceDemand[Choice] as only the caller security is
422 ves_icall_System_Security_SecurityFrame_GetSecurityFrame (gint32 skip)
424 MonoDomain *domain = mono_domain_get ();
425 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
426 MonoFrameSecurityInfo si;
429 MONO_ARCH_CONTEXT_DEF
431 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityFrame);
435 mono_walk_stack (domain, jit_tls, &ctx, callback_get_first_frame_security_info, (gpointer)&si);
437 return (si.skips == 0) ? si.frame : NULL;
449 grow_array (MonoSecurityStack *stack)
451 MonoDomain *domain = mono_domain_get ();
452 guint32 newsize = (stack->maximum << 1);
453 MonoArray *newstack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, newsize);
455 for (i=0; i < stack->maximum; i++) {
456 gpointer frame = mono_array_get (stack->stack, gpointer, i);
457 mono_array_set (newstack, gpointer, i, frame);
459 stack->maximum = newsize;
460 stack->stack = newstack;
464 callback_get_stack_frames_security_info (MonoDomain *domain, MonoContext *ctx, MonoJitInfo *ji, gpointer data)
466 MonoSecurityStack *ss = (MonoSecurityStack*) data;
468 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
469 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
470 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE ||
471 ji->method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH ||
472 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
473 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE) {
482 if (ss->count == ss->maximum)
485 mono_array_set (ss->stack, gpointer, ss->count++, mono_declsec_create_frame (domain, ji));
487 /* continue down the stack */
492 glist_to_array (GList *list, MonoClass *eclass)
494 MonoDomain *domain = mono_domain_get ();
501 len = g_list_length (list);
502 res = mono_array_new (domain, eclass, len);
504 for (i = 0; list; list = list->next, i++)
505 mono_array_set (res, gpointer, i, list->data);
511 * ves_icall_System_Security_SecurityFrame_GetSecurityStack:
512 * @skip: the number of stack frames to skip
514 * This function returns an managed array of containing the security
515 * informations for each frame (after the skipped ones). This is used for
516 * [NonCas]Demand[Choice] where the complete evaluation of the stack is
520 ves_icall_System_Security_SecurityFrame_GetSecurityStack (gint32 skip)
522 MonoDomain *domain = mono_domain_get ();
523 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
524 MonoSecurityStack ss;
527 MONO_ARCH_CONTEXT_DEF
529 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_System_Security_SecurityFrame_GetSecurityStack);
533 ss.maximum = MONO_CAS_INITIAL_STACK_SIZE;
534 ss.stack = mono_array_new (domain, mono_defaults.runtimesecurityframe_class, ss.maximum);
535 mono_walk_stack (domain, jit_tls, &ctx, callback_get_stack_frames_security_info, (gpointer)&ss);
536 /* g_warning ("STACK RESULT: %d out of %d", ss.count, ss.maximum); */
540 #ifndef CUSTOM_EXCEPTION_HANDLING
543 * mono_handle_exception:
544 * @ctx: saved processor state
545 * @obj: the exception object
546 * @test_only: only test if the exception is caught, but dont call handlers
550 mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only)
552 MonoDomain *domain = mono_domain_get ();
553 MonoJitInfo *ji, rji;
554 static int (*call_filter) (MonoContext *, gpointer) = NULL;
555 static void (*restore_context) (void *);
556 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
557 MonoLMF *lmf = jit_tls->lmf;
558 MonoArray *initial_trace_ips = NULL;
559 GList *trace_ips = NULL;
560 MonoException *mono_ex;
561 gboolean stack_overflow = FALSE;
562 MonoContext initial_ctx;
564 gboolean gc_disabled = FALSE;
565 gboolean has_dynamic_methods = FALSE;
568 * This function might execute on an alternate signal stack, and Boehm GC
570 * Also, since the altstack is small, stack space intensive operations like
571 * JIT compilation should be avoided.
573 if (IS_ON_SIGALTSTACK (jit_tls)) {
575 * FIXME: disabling/enabling GC while already on a signal stack might
576 * not be safe either.
578 /* Have to reenable it later */
583 g_assert (ctx != NULL);
585 MonoException *ex = mono_get_exception_null_reference ();
586 ex->message = mono_string_new (domain, "Object reference not set to an instance of an object");
587 obj = (MonoObject *)ex;
591 * Allocate a new exception object instead of the preconstructed ones.
592 * We can't do this in sigsegv_signal_handler, since GC is not yet
595 if (obj == domain->stack_overflow_ex) {
596 obj = mono_get_exception_stack_overflow ();
597 stack_overflow = TRUE;
599 else if (obj == domain->null_reference_ex) {
600 obj = mono_get_exception_null_reference ();
603 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
604 mono_ex = (MonoException*)obj;
605 initial_trace_ips = mono_ex->trace_ips;
611 call_filter = mono_arch_get_call_filter ();
613 if (!restore_context)
614 restore_context = mono_arch_get_restore_context ();
616 g_assert (jit_tls->end_of_stack);
617 g_assert (jit_tls->abort_func);
620 MonoContext ctx_cp = *ctx;
621 if (mono_jit_trace_calls != NULL)
622 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
623 if (!mono_handle_exception (&ctx_cp, obj, original_ip, TRUE)) {
624 if (mono_break_on_exc)
626 mono_unhandled_exception (obj);
628 if (mono_debugger_unhandled_exception (original_ip, MONO_CONTEXT_GET_SP (ctx), obj)) {
630 * If this returns true, then we're running inside the
631 * Mono Debugger and the debugger wants us to restore the
632 * context and continue (normally, the debugger inserts
633 * a breakpoint on the `original_ip', so it regains control
634 * immediately after restoring the context).
636 MONO_CONTEXT_SET_IP (ctx, original_ip);
637 restore_context (ctx);
638 g_assert_not_reached ();
644 memset (&rji, 0, sizeof (rji));
650 ji = mono_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx,
651 NULL, &lmf, NULL, NULL);
653 g_warning ("Exception inside function without unwind info");
654 g_assert_not_reached ();
657 if (ji != (gpointer)-1) {
659 //printf ("M: %s %d %d.\n", mono_method_full_name (ji->method, TRUE), frame_count, test_only);
661 if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
663 * Avoid overwriting the stack trace if the exception is
664 * rethrown. Also avoid giant stack traces during a stack
667 if (!initial_trace_ips && (frame_count < 1000)) {
668 trace_ips = g_list_prepend (trace_ips, MONO_CONTEXT_GET_IP (ctx));
672 if (ji->method->dynamic)
673 has_dynamic_methods = TRUE;
676 free_stack = (guint8*)(MONO_CONTEXT_GET_BP (ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
678 free_stack = 0xffffff;
681 * During stack overflow, wait till the unwinding frees some stack
682 * space before running handlers/finalizers.
684 if ((free_stack > (64 * 1024)) && ji->num_clauses) {
687 for (i = 0; i < ji->num_clauses; i++) {
688 MonoJitExceptionInfo *ei = &ji->clauses [i];
689 gboolean filtered = FALSE;
692 if (ei->try_start < MONO_CONTEXT_GET_IP (ctx) &&
694 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
696 MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
699 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
700 /* store the exception object in cfg->excvar */
701 g_assert (ei->exvar_offset);
702 *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = obj;
705 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
706 // mono_debugger_handle_exception (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj);
707 filtered = call_filter (ctx, ei->data.filter);
710 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
711 mono_object_isinst (obj, ei->data.catch_class)) || filtered) {
713 if (mono_ex && !initial_trace_ips) {
714 trace_ips = g_list_reverse (trace_ips);
715 mono_ex->trace_ips = glist_to_array (trace_ips, mono_defaults.int_class);
716 if (has_dynamic_methods)
717 /* These methods could go away anytime, so compute the stack trace now */
718 mono_ex->stack_trace = ves_icall_System_Exception_get_trace (mono_ex);
720 g_list_free (trace_ips);
726 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
727 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
728 mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
729 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
736 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
737 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
738 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
739 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
740 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
741 mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj);
742 call_filter (ctx, ei->handler_start);
752 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
759 if (IS_ON_SIGALTSTACK (jit_tls)) {
760 /* Switch back to normal stack */
762 /* Free up some stack space */
763 MONO_CONTEXT_SET_SP (&initial_ctx, (gssize)(MONO_CONTEXT_GET_SP (&initial_ctx)) + (64 * 1024));
764 MONO_CONTEXT_SET_IP (&initial_ctx, (gssize)jit_tls->abort_func);
765 restore_context (&initial_ctx);
768 jit_tls->abort_func (obj);
769 g_assert_not_reached ();
771 if (mono_ex && !initial_trace_ips) {
772 trace_ips = g_list_reverse (trace_ips);
773 mono_ex->trace_ips = glist_to_array (trace_ips, mono_defaults.int_class);
774 if (has_dynamic_methods)
775 /* These methods could go away anytime, so compute the stack trace now */
776 mono_ex->stack_trace = ves_icall_System_Exception_get_trace (mono_ex);
778 g_list_free (trace_ips);
784 g_assert_not_reached ();
786 #endif /* CUSTOM_EXECPTION_HANDLING */