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>
25 #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)))
27 #ifndef mono_find_jit_info
29 /* mono_find_jit_info:
31 * This function is used to gather information from @ctx. It return the
32 * MonoJitInfo of the corresponding function, unwinds one stack frame and
33 * stores the resulting context into @new_ctx. It also stores a string
34 * describing the stack location into @trace (if not NULL), and modifies
35 * the @lmf if necessary. @native_offset return the IP offset from the
36 * start of the function or -1 if that info is not available.
39 mono_find_jit_info (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
, MonoJitInfo
*res
, MonoJitInfo
*prev_ji
, MonoContext
*ctx
,
40 MonoContext
*new_ctx
, char **trace
, MonoLMF
**lmf
, int *native_offset
,
44 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
56 ji
= mono_arch_find_jit_info (domain
, jit_tls
, res
, prev_ji
, ctx
, new_ctx
, NULL
, lmf
, NULL
, &managed2
);
58 if (ji
== (gpointer
)-1)
61 if (managed2
|| ji
->method
->wrapper_type
) {
62 char *source_location
, *tmpaddr
, *fname
;
63 gint32 address
, iloffset
;
65 address
= (char *)ip
- (char *)ji
->code_start
;
68 *native_offset
= address
;
71 if (!ji
->method
->wrapper_type
)
75 source_location
= mono_debug_source_location_from_address (ji
->method
, address
, NULL
, domain
);
76 iloffset
= mono_debug_il_offset_from_address (ji
->method
, address
, domain
);
79 tmpaddr
= g_strdup_printf ("<0x%05x>", address
);
81 tmpaddr
= g_strdup_printf ("[0x%05x]", iloffset
);
83 fname
= mono_method_full_name (ji
->method
, TRUE
);
86 *trace
= g_strdup_printf ("in %s (at %s) %s", tmpaddr
, source_location
, fname
);
88 *trace
= g_strdup_printf ("in %s %s", tmpaddr
, fname
);
91 g_free (source_location
);
97 char *fname
= mono_method_full_name (res
->method
, TRUE
);
98 *trace
= g_strdup_printf ("in (unmanaged) %s", fname
);
106 #endif /* mono_find_jit_info */
109 ves_icall_get_trace (MonoException
*exc
, gint32 skip
, MonoBoolean need_file_info
)
111 MonoDomain
*domain
= mono_domain_get ();
113 MonoArray
*ta
= exc
->trace_ips
;
117 /* Exception is not thrown yet */
118 return mono_array_new (domain
, mono_defaults
.stack_frame_class
, 0);
121 len
= mono_array_length (ta
);
123 res
= mono_array_new (domain
, mono_defaults
.stack_frame_class
, len
> skip
? len
- skip
: 0);
125 for (i
= skip
; i
< len
; i
++) {
127 MonoStackFrame
*sf
= (MonoStackFrame
*)mono_object_new (domain
, mono_defaults
.stack_frame_class
);
128 gpointer ip
= mono_array_get (ta
, gpointer
, i
);
130 ji
= mono_jit_info_table_find (domain
, ip
);
132 /* Unmanaged frame */
133 mono_array_set (res
, gpointer
, i
, sf
);
137 g_assert (ji
!= NULL
);
139 sf
->method
= mono_method_get_object (domain
, ji
->method
, NULL
);
140 sf
->native_offset
= (char *)ip
- (char *)ji
->code_start
;
142 sf
->il_offset
= mono_debug_il_offset_from_address (ji
->method
, sf
->native_offset
, domain
);
144 if (need_file_info
) {
147 filename
= mono_debug_source_location_from_address (ji
->method
, sf
->native_offset
, &sf
->line
, domain
);
149 sf
->filename
= filename
? mono_string_new (domain
, filename
): NULL
;
155 mono_array_set (res
, gpointer
, i
, sf
);
164 * @domain: starting appdomain
165 * @jit_tls: JIT data for the thread
166 * @start_ctx: starting state of the stack frame
167 * @func: callback to call for each stack frame
168 * @user_data: data passed to the callback
170 * This function walks the stack of a thread, starting from the state
171 * represented by jit_tls and start_ctx. For each frame the callback
172 * function is called with the relevant info. The walk ends when no more
173 * managed stack frames are found or when the callback returns a TRUE value.
174 * Note that the function can be used to walk the stack of a thread
175 * different from the current.
178 mono_walk_stack (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
, MonoContext
*start_ctx
, MonoStackFrameWalk func
, gpointer user_data
)
180 MonoLMF
*lmf
= jit_tls
->lmf
;
181 MonoJitInfo
*ji
, rji
;
184 MonoContext ctx
, new_ctx
;
188 while (MONO_CONTEXT_GET_BP (&ctx
) < jit_tls
->end_of_stack
) {
190 * FIXME: mono_find_jit_info () will need to be able to return a different
191 * MonoDomain when apddomain transitions are found on the stack.
193 ji
= mono_find_jit_info (domain
, jit_tls
, &rji
, NULL
, &ctx
, &new_ctx
, NULL
, &lmf
, &native_offset
, &managed
);
194 if (!ji
|| ji
== (gpointer
)-1)
197 if (func (domain
, &new_ctx
, ji
, user_data
))
204 #ifndef CUSTOM_STACK_WALK
207 mono_jit_walk_stack (MonoStackWalk func
, gboolean do_il_offset
, gpointer user_data
) {
208 MonoDomain
*domain
= mono_domain_get ();
209 MonoJitTlsData
*jit_tls
= TlsGetValue (mono_jit_tls_id
);
210 MonoLMF
*lmf
= jit_tls
->lmf
;
211 MonoJitInfo
*ji
, rji
;
212 gint native_offset
, il_offset
;
215 MonoContext ctx
, new_ctx
;
217 mono_arch_flush_register_windows ();
219 MONO_CONTEXT_SET_IP (&ctx
, __builtin_return_address (0));
220 MONO_CONTEXT_SET_BP (&ctx
, __builtin_frame_address (1));
222 while (MONO_CONTEXT_GET_BP (&ctx
) < jit_tls
->end_of_stack
) {
224 ji
= mono_find_jit_info (domain
, jit_tls
, &rji
, NULL
, &ctx
, &new_ctx
, NULL
, &lmf
, &native_offset
, &managed
);
227 if (ji
== (gpointer
)-1)
230 il_offset
= do_il_offset
? mono_debug_il_offset_from_address (ji
->method
, native_offset
, domain
): -1;
232 if (func (ji
->method
, native_offset
, il_offset
, managed
, user_data
))
240 ves_icall_get_frame_info (gint32 skip
, MonoBoolean need_file_info
,
241 MonoReflectionMethod
**method
,
242 gint32
*iloffset
, gint32
*native_offset
,
243 MonoString
**file
, gint32
*line
, gint32
*column
)
245 MonoDomain
*domain
= mono_domain_get ();
246 MonoJitTlsData
*jit_tls
= TlsGetValue (mono_jit_tls_id
);
247 MonoLMF
*lmf
= jit_tls
->lmf
;
248 MonoJitInfo
*ji
, rji
;
249 MonoContext ctx
, new_ctx
;
251 mono_arch_flush_register_windows ();
253 MONO_CONTEXT_SET_IP (&ctx
, ves_icall_get_frame_info
);
254 MONO_CONTEXT_SET_BP (&ctx
, __builtin_frame_address (0));
259 ji
= mono_find_jit_info (domain
, jit_tls
, &rji
, NULL
, &ctx
, &new_ctx
, NULL
, &lmf
, native_offset
, NULL
);
263 if (!ji
|| ji
== (gpointer
)-1 || MONO_CONTEXT_GET_BP (&ctx
) >= jit_tls
->end_of_stack
)
266 /* skip all wrappers ??*/
267 if (ji
->method
->wrapper_type
== MONO_WRAPPER_RUNTIME_INVOKE
||
268 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_INVOKE
||
269 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_DISPATCH
||
270 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK
||
271 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE
)
278 *method
= mono_method_get_object (domain
, ji
->method
, NULL
);
279 *iloffset
= mono_debug_il_offset_from_address (ji
->method
, *native_offset
, domain
);
281 if (need_file_info
) {
284 filename
= mono_debug_source_location_from_address (ji
->method
, *native_offset
, line
, domain
);
286 *file
= filename
? mono_string_new (domain
, filename
): NULL
;
295 #endif /* CUSTOM_STACK_WALK */
299 MonoSecurityFrame
*frame
;
300 } MonoFrameSecurityInfo
;
303 callback_get_first_frame_security_info (MonoDomain
*domain
, MonoContext
*ctx
, MonoJitInfo
*ji
, gpointer data
)
305 MonoFrameSecurityInfo
*si
= (MonoFrameSecurityInfo
*) data
;
307 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
308 if (ji
->method
->wrapper_type
== MONO_WRAPPER_RUNTIME_INVOKE
||
309 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_INVOKE
||
310 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_DISPATCH
||
311 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK
||
312 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE
) {
321 si
->frame
= mono_declsec_create_frame (domain
, ji
);
323 /* Stop - we only want the first frame (e.g. LinkDemand and InheritanceDemand) */
328 * ves_icall_System_Security_SecurityFrame_GetSecurityFrame:
329 * @skip: the number of stack frames to skip
331 * This function returns a the security informations of a single stack frame
332 * (after the skipped ones). This is required for [NonCas]LinkDemand[Choice]
333 * and [NonCas]InheritanceDemand[Choice] as only the caller security is
337 ves_icall_System_Security_SecurityFrame_GetSecurityFrame (gint32 skip
)
339 MonoDomain
*domain
= mono_domain_get ();
340 MonoJitTlsData
*jit_tls
= TlsGetValue (mono_jit_tls_id
);
341 MonoFrameSecurityInfo si
;
344 MONO_INIT_CONTEXT_FROM_FUNC (&ctx
, ves_icall_System_Security_SecurityFrame_GetSecurityFrame
);
348 mono_walk_stack (domain
, jit_tls
, &ctx
, callback_get_first_frame_security_info
, (gpointer
)&si
);
350 return (si
.skips
== 0) ? si
.frame
: NULL
;
360 callback_get_stack_frames_security_info (MonoDomain
*domain
, MonoContext
*ctx
, MonoJitInfo
*ji
, gpointer data
)
362 MonoSecurityStack
*ss
= (MonoSecurityStack
*) data
;
364 /* FIXME: skip all wrappers ?? probably not - case by case testing is required */
365 if (ji
->method
->wrapper_type
== MONO_WRAPPER_RUNTIME_INVOKE
||
366 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_INVOKE
||
367 ji
->method
->wrapper_type
== MONO_WRAPPER_XDOMAIN_DISPATCH
||
368 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK
||
369 ji
->method
->wrapper_type
== MONO_WRAPPER_REMOTING_INVOKE
) {
378 ss
->stack
= g_list_prepend (ss
->stack
, mono_declsec_create_frame (domain
, ji
));
380 /* continue down the stack */
385 glist_to_array (GList
*list
, MonoClass
*eclass
)
387 MonoDomain
*domain
= mono_domain_get ();
394 len
= g_list_length (list
);
395 res
= mono_array_new (domain
, eclass
, len
);
397 for (i
= 0; list
; list
= list
->next
, i
++)
398 mono_array_set (res
, gpointer
, i
, list
->data
);
404 * ves_icall_System_Security_SecurityFrame_GetSecurityStack:
405 * @skip: the number of stack frames to skip
407 * This function returns an managed array of containing the security
408 * informations for each frame (after the skipped ones). This is used for
409 * [NonCas]Demand[Choice] where the complete evaluation of the stack is
413 ves_icall_System_Security_SecurityFrame_GetSecurityStack (gint32 skip
)
415 MonoDomain
*domain
= mono_domain_get ();
416 MonoJitTlsData
*jit_tls
= TlsGetValue (mono_jit_tls_id
);
417 MonoSecurityStack ss
;
421 MONO_INIT_CONTEXT_FROM_FUNC (&ctx
, ves_icall_System_Security_SecurityFrame_GetSecurityStack
);
425 mono_walk_stack (domain
, jit_tls
, &ctx
, callback_get_stack_frames_security_info
, (gpointer
)&ss
);
427 stack
= glist_to_array (ss
.stack
, mono_defaults
.runtimesecurityframe_class
);
429 g_list_free (ss
.stack
);
434 #ifndef CUSTOM_EXCEPTION_HANDLING
437 * mono_handle_exception:
438 * @ctx: saved processor state
439 * @obj: the exception object
440 * @test_only: only test if the exception is caught, but dont call handlers
444 mono_handle_exception (MonoContext
*ctx
, gpointer obj
, gpointer original_ip
, gboolean test_only
)
446 MonoDomain
*domain
= mono_domain_get ();
447 MonoJitInfo
*ji
, rji
;
448 static int (*call_filter
) (MonoContext
*, gpointer
) = NULL
;
449 static void (*restore_context
) (void *);
450 MonoJitTlsData
*jit_tls
= TlsGetValue (mono_jit_tls_id
);
451 MonoLMF
*lmf
= jit_tls
->lmf
;
452 GList
*trace_ips
= NULL
;
453 MonoException
*mono_ex
;
454 gboolean stack_overflow
= FALSE
;
455 MonoContext initial_ctx
;
457 gboolean gc_disabled
= FALSE
;
458 MonoString
*initial_stack_trace
= NULL
;
459 GString
*trace_str
= NULL
;
462 * This function might execute on an alternate signal stack, and Boehm GC
464 * Also, since the altstack is small, stack space intensive operations like
465 * JIT compilation should be avoided.
467 if (IS_ON_SIGALTSTACK (jit_tls
)) {
469 * FIXME: disabling/enabling GC while already on a signal stack might
470 * not be safe either.
472 /* Have to reenable it later */
477 g_assert (ctx
!= NULL
);
479 MonoException
*ex
= mono_get_exception_null_reference ();
480 ex
->message
= mono_string_new (domain
, "Object reference not set to an instance of an object");
481 obj
= (MonoObject
*)ex
;
485 * Allocate a new exception object instead of the preconstructed ones.
486 * We can't do this in sigsegv_signal_handler, since GC is not yet
489 if (obj
== domain
->stack_overflow_ex
) {
490 obj
= mono_get_exception_stack_overflow ();
492 else if (obj
== domain
->null_reference_ex
) {
493 obj
= mono_get_exception_null_reference ();
496 if (mono_object_isinst (obj
, mono_defaults
.exception_class
)) {
497 mono_ex
= (MonoException
*)obj
;
498 initial_stack_trace
= mono_ex
->stack_trace
;
503 if (obj
== domain
->stack_overflow_ex
)
504 stack_overflow
= TRUE
;
507 call_filter
= mono_arch_get_call_filter ();
509 if (!restore_context
)
510 restore_context
= mono_arch_get_restore_context ();
512 g_assert (jit_tls
->end_of_stack
);
513 g_assert (jit_tls
->abort_func
);
516 MonoContext ctx_cp
= *ctx
;
517 if (mono_jit_trace_calls
!= NULL
)
518 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj
)->name
);
519 if (!mono_handle_exception (&ctx_cp
, obj
, original_ip
, TRUE
)) {
520 if (mono_break_on_exc
)
522 mono_unhandled_exception (obj
);
524 if (mono_debugger_unhandled_exception (original_ip
, MONO_CONTEXT_GET_SP (ctx
), obj
)) {
526 * If this returns true, then we're running inside the
527 * Mono Debugger and the debugger wants us to restore the
528 * context and continue (normally, the debugger inserts
529 * a breakpoint on the `original_ip', so it regains control
530 * immediately after restoring the context).
532 MONO_CONTEXT_SET_IP (ctx
, original_ip
);
533 restore_context (ctx
);
534 g_assert_not_reached ();
540 memset (&rji
, 0, sizeof (rji
));
545 gboolean need_trace
= FALSE
;
548 if (test_only
&& (frame_count
< 1000)) {
551 trace_str
= g_string_new ("");
554 ji
= mono_find_jit_info (domain
, jit_tls
, &rji
, &rji
, ctx
, &new_ctx
,
555 need_trace
? &trace
: NULL
, &lmf
, NULL
, NULL
);
557 g_warning ("Exception inside function without unwind info");
558 g_assert_not_reached ();
561 if (ji
!= (gpointer
)-1) {
563 //printf ("M: %s %d %d.\n", mono_method_full_name (ji->method, TRUE), frame_count, test_only);
565 if (test_only
&& ji
->method
->wrapper_type
!= MONO_WRAPPER_RUNTIME_INVOKE
&& mono_ex
) {
567 * Avoid overwriting the stack trace if the exception is
568 * rethrown. Also avoid giant stack traces during a stack
571 if (!initial_stack_trace
&& (frame_count
< 1000)) {
572 trace_ips
= g_list_prepend (trace_ips
, MONO_CONTEXT_GET_IP (ctx
));
574 g_string_append (trace_str
, trace
);
575 g_string_append_c (trace_str
, '\n');
580 free_stack
= (guint8
*)(MONO_CONTEXT_GET_BP (ctx
)) - (guint8
*)(MONO_CONTEXT_GET_BP (&initial_ctx
));
582 free_stack
= 0xffffff;
585 * During stack overflow, wait till the unwinding frees some stack
586 * space before running handlers/finalizers.
588 if ((free_stack
> (64 * 1024)) && ji
->num_clauses
) {
591 g_assert (ji
->clauses
);
593 for (i
= 0; i
< ji
->num_clauses
; i
++) {
594 MonoJitExceptionInfo
*ei
= &ji
->clauses
[i
];
595 gboolean filtered
= FALSE
;
598 if (ei
->try_start
< MONO_CONTEXT_GET_IP (ctx
) &&
600 if (ei
->try_start
<= MONO_CONTEXT_GET_IP (ctx
) &&
602 MONO_CONTEXT_GET_IP (ctx
) <= ei
->try_end
) {
605 if ((ei
->flags
== MONO_EXCEPTION_CLAUSE_NONE
) || (ei
->flags
== MONO_EXCEPTION_CLAUSE_FILTER
)) {
606 /* store the exception object int cfg->excvar */
607 g_assert (ji
->exvar_offset
);
608 *((gpointer
*)((char *)MONO_CONTEXT_GET_BP (ctx
) + ji
->exvar_offset
)) = obj
;
609 if (!initial_stack_trace
&& trace_str
) {
610 mono_ex
->stack_trace
= mono_string_new (domain
, trace_str
->str
);
614 if (ei
->flags
== MONO_EXCEPTION_CLAUSE_FILTER
) {
615 mono_debugger_handle_exception (ei
->data
.filter
, MONO_CONTEXT_GET_SP (ctx
), obj
);
616 filtered
= call_filter (ctx
, ei
->data
.filter
);
619 if ((ei
->flags
== MONO_EXCEPTION_CLAUSE_NONE
&&
620 mono_object_isinst (obj
, ei
->data
.catch_class
)) || filtered
) {
623 trace_ips
= g_list_reverse (trace_ips
);
624 mono_ex
->trace_ips
= glist_to_array (trace_ips
, mono_defaults
.int_class
);
626 g_list_free (trace_ips
);
632 g_string_free (trace_str
, TRUE
);
635 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (ji
->method
))
636 g_print ("EXCEPTION: catch found at clause %d of %s\n", i
, mono_method_full_name (ji
->method
, TRUE
));
637 mono_debugger_handle_exception (ei
->handler_start
, MONO_CONTEXT_GET_SP (ctx
), obj
);
638 MONO_CONTEXT_SET_IP (ctx
, ei
->handler_start
);
645 g_string_free (trace_str
, TRUE
);
648 if (!test_only
&& ei
->try_start
<= MONO_CONTEXT_GET_IP (ctx
) &&
649 MONO_CONTEXT_GET_IP (ctx
) < ei
->try_end
&&
650 (ei
->flags
& MONO_EXCEPTION_CLAUSE_FINALLY
)) {
651 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (ji
->method
))
652 g_print ("EXCEPTION: finally clause %d of %s\n", i
, mono_method_full_name (ji
->method
, TRUE
));
653 mono_debugger_handle_exception (ei
->handler_start
, MONO_CONTEXT_GET_SP (ctx
), obj
);
654 call_filter (ctx
, ei
->handler_start
);
666 if ((ji
== (gpointer
)-1) || MONO_CONTEXT_GET_BP (ctx
) >= jit_tls
->end_of_stack
) {
673 if (IS_ON_SIGALTSTACK (jit_tls
)) {
674 /* Switch back to normal stack */
676 /* Free up some stack space */
677 MONO_CONTEXT_SET_SP (&initial_ctx
, (guint32
)(MONO_CONTEXT_GET_SP (&initial_ctx
)) + (64 * 1024));
678 MONO_CONTEXT_SET_IP (&initial_ctx
, (unsigned int)jit_tls
->abort_func
);
679 restore_context (&initial_ctx
);
682 jit_tls
->abort_func (obj
);
683 g_assert_not_reached ();
686 trace_ips
= g_list_reverse (trace_ips
);
687 mono_ex
->trace_ips
= glist_to_array (trace_ips
, mono_defaults
.int_class
);
689 g_list_free (trace_ips
);
691 g_string_free (trace_str
, TRUE
);
697 g_assert_not_reached ();
699 #endif /* CUSTOM_EXECPTION_HANDLING */