Apply changes from https://github.com/dotnet/runtime/commit/eb1756e97d23df13bc6fe798e...
[mono-project.git] / mono / mini / mini-exceptions.c
blob19d8b2b43b0ca2e7e900903a8ac5403ba7632a6f
1 /**
2 * \file
3 * generic exception support
5 * Authors:
6 * Dietmar Maurer (dietmar@ximian.com)
7 * Mono Team (mono-list@lists.ximian.com)
9 * Copyright 2001-2003 Ximian, Inc.
10 * Copyright 2003-2008 Novell, Inc.
11 * Copyright 2011 Xamarin Inc (http://www.xamarin.com).
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18 #include <signal.h>
20 #ifdef HAVE_EXECINFO_H
21 #include <execinfo.h>
22 #endif
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
28 #ifdef HAVE_SYS_WAIT_H
29 #include <sys/wait.h>
30 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
36 #ifdef HAVE_SYS_SYSCALL_H
37 #include <sys/syscall.h>
38 #endif
40 #ifdef HAVE_SYS_PRCTL_H
41 #include <sys/prctl.h>
42 #endif
44 #ifdef HAVE_UNWIND_H
45 #include <unwind.h>
46 #endif
48 #include <mono/metadata/appdomain.h>
49 #include <mono/metadata/tabledefs.h>
50 #include <mono/metadata/threads.h>
51 #include <mono/metadata/threads-types.h>
52 #include <mono/metadata/debug-helpers.h>
53 #include <mono/metadata/exception.h>
54 #include <mono/metadata/exception-internals.h>
55 #include <mono/metadata/object-internals.h>
56 #include <mono/metadata/reflection-internals.h>
57 #include <mono/metadata/gc-internals.h>
58 #include <mono/metadata/debug-internals.h>
59 #include <mono/metadata/mono-debug.h>
60 #include <mono/metadata/profiler-private.h>
61 #include <mono/metadata/mono-endian.h>
62 #include <mono/metadata/environment.h>
63 #include <mono/metadata/mono-mlist.h>
64 #include <mono/metadata/handle.h>
65 #include <mono/utils/mono-merp.h>
66 #include <mono/utils/mono-mmap.h>
67 #include <mono/utils/mono-logger-internals.h>
68 #include <mono/utils/mono-error.h>
69 #include <mono/utils/mono-error-internals.h>
70 #include <mono/utils/mono-state.h>
71 #include <mono/utils/mono-threads-debug.h>
72 #include <mono/utils/w32subset.h>
74 #include "mini.h"
75 #include "trace.h"
76 #include "debugger-agent.h"
77 #include "debugger-engine.h"
78 #include "seq-points.h"
79 #include "llvm-runtime.h"
80 #include "mini-llvm.h"
81 #include "aot-runtime.h"
82 #include "mini-runtime.h"
83 #include "interp/interp.h"
85 #ifdef ENABLE_LLVM
86 #include "mini-llvm-cpp.h"
87 #endif
89 #ifdef TARGET_ARM
90 #include "mini-arm.h"
91 #endif
93 #ifndef MONO_ARCH_CONTEXT_DEF
94 #define MONO_ARCH_CONTEXT_DEF
95 #endif
97 #if !defined(DISABLE_CRASH_REPORTING)
98 #include <gmodule.h>
99 #endif
100 #include "mono/utils/mono-tls-inline.h"
103 * Raw frame information is stored in MonoException.trace_ips as an IntPtr[].
104 * This structure represents one entry.
105 * This should consists of pointers only.
107 typedef struct
109 gpointer ip;
110 gpointer generic_info;
111 /* Only for interpreter frames */
112 MonoJitInfo *ji;
113 } ExceptionTraceIp;
115 /* Number of words in trace_ips belonging to one entry */
116 #define TRACE_IP_ENTRY_SIZE (sizeof (ExceptionTraceIp) / sizeof (gpointer))
118 static gpointer restore_context_func, call_filter_func;
119 static gpointer throw_exception_func, rethrow_exception_func, rethrow_preserve_exception_func;
120 static gpointer throw_corlib_exception_func;
122 static MonoFtnPtrEHCallback ftnptr_eh_callback;
124 static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context);
125 static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
126 static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
127 static gboolean mono_current_thread_has_handle_block_guard (void);
128 static gboolean mono_install_handler_block_guard (MonoThreadUnwindState *ctx);
129 static void mono_uninstall_current_handler_block_guard (void);
130 static gboolean mono_exception_walk_trace_internal (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data);
131 static void throw_exception (MonoObject *ex, gboolean rethrow);
133 static void mono_summarize_managed_stack (MonoThreadSummary *out);
134 static void mono_summarize_unmanaged_stack (MonoThreadSummary *out);
135 static void mono_summarize_exception (MonoException *exc, MonoThreadSummary *out);
136 static void mono_crash_reporting_register_native_library (const char *module_path, const char *module_name);
137 static void mono_crash_reporting_allow_all_native_libraries (void);
139 static gboolean
140 first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
142 gpointer *data = (gpointer *)addr;
144 if (!frame->managed)
145 return FALSE;
147 if (!ctx) {
148 // FIXME: Happens with llvm_only
149 *data = NULL;
150 return TRUE;
153 *data = frame->frame_addr;
154 g_assert (*data);
155 return TRUE;
158 static gpointer
159 mono_thread_get_managed_sp (void)
161 gpointer addr = NULL;
162 mono_walk_stack (first_managed, MONO_UNWIND_SIGNAL_SAFE, &addr);
163 return addr;
166 static void
167 mini_clear_abort_threshold (void)
169 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
170 jit_tls->abort_exc_stack_threshold = NULL;
173 static void
174 mini_set_abort_threshold (StackFrameInfo *frame)
176 gpointer sp = frame->frame_addr;
177 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
178 // Only move it up, to avoid thrown/caught
179 // exceptions lower in the stack from triggering
180 // a rethrow
181 gboolean above_threshold = (gsize) sp >= (gsize) jit_tls->abort_exc_stack_threshold;
182 if (!jit_tls->abort_exc_stack_threshold || above_threshold) {
183 jit_tls->abort_exc_stack_threshold = sp;
187 // Note: In the case that the frame is above where the thread abort
188 // was set we bump the threshold so that functions called from the new,
189 // higher threshold don't trigger the thread abort exception
190 static gboolean
191 mini_above_abort_threshold (void)
193 gpointer sp = mono_thread_get_managed_sp ();
194 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
196 if (!sp)
197 return TRUE;
199 gboolean above_threshold = (gsize) sp >= (gsize) jit_tls->abort_exc_stack_threshold;
201 if (above_threshold)
202 jit_tls->abort_exc_stack_threshold = sp;
204 return above_threshold;
207 static int
208 mono_get_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
210 SeqPoint sp;
211 if (mono_find_prev_seq_point_for_native_offset (domain, method, native_offset, NULL, &sp))
212 return sp.il_offset;
213 return -1;
216 void
217 mono_exceptions_init (void)
219 MonoRuntimeExceptionHandlingCallbacks cbs;
220 if (mono_ee_features.use_aot_trampolines) {
221 restore_context_func = mono_aot_get_trampoline ("restore_context");
222 call_filter_func = mono_aot_get_trampoline ("call_filter");
223 throw_exception_func = mono_aot_get_trampoline ("throw_exception");
224 rethrow_exception_func = mono_aot_get_trampoline ("rethrow_exception");
225 rethrow_preserve_exception_func = mono_aot_get_trampoline ("rethrow_preserve_exception");
226 } else if (!mono_llvm_only) {
227 MonoTrampInfo *info;
229 restore_context_func = mono_arch_get_restore_context (&info, FALSE);
230 mono_tramp_info_register (info, NULL);
231 call_filter_func = mono_arch_get_call_filter (&info, FALSE);
232 mono_tramp_info_register (info, NULL);
233 throw_exception_func = mono_arch_get_throw_exception (&info, FALSE);
234 mono_tramp_info_register (info, NULL);
235 rethrow_exception_func = mono_arch_get_rethrow_exception (&info, FALSE);
236 mono_tramp_info_register (info, NULL);
237 rethrow_preserve_exception_func = mono_arch_get_rethrow_preserve_exception (&info, FALSE);
238 mono_tramp_info_register (info, NULL);
241 mono_arch_exceptions_init ();
243 cbs.mono_walk_stack_with_ctx = mono_runtime_walk_stack_with_ctx;
244 cbs.mono_walk_stack_with_state = mono_walk_stack_with_state;
245 cbs.mono_summarize_managed_stack = mono_summarize_managed_stack;
246 cbs.mono_summarize_unmanaged_stack = mono_summarize_unmanaged_stack;
247 cbs.mono_summarize_exception = mono_summarize_exception;
248 cbs.mono_register_native_library = mono_crash_reporting_register_native_library;
249 cbs.mono_allow_all_native_libraries = mono_crash_reporting_allow_all_native_libraries;
251 if (mono_llvm_only) {
252 cbs.mono_raise_exception = mono_llvm_raise_exception;
253 cbs.mono_reraise_exception = mono_llvm_reraise_exception;
254 } else {
255 cbs.mono_raise_exception = (void (*)(MonoException *))mono_get_throw_exception ();
256 cbs.mono_reraise_exception = (void (*)(MonoException *))mono_get_rethrow_exception ();
258 cbs.mono_raise_exception_with_ctx = mono_raise_exception_with_ctx;
259 cbs.mono_exception_walk_trace = mono_exception_walk_trace;
260 cbs.mono_install_handler_block_guard = mono_install_handler_block_guard;
261 cbs.mono_uninstall_current_handler_block_guard = mono_uninstall_current_handler_block_guard;
262 cbs.mono_current_thread_has_handle_block_guard = mono_current_thread_has_handle_block_guard;
263 cbs.mono_clear_abort_threshold = mini_clear_abort_threshold;
264 cbs.mono_above_abort_threshold = mini_above_abort_threshold;
265 mono_install_eh_callbacks (&cbs);
266 mono_install_get_seq_point (mono_get_seq_point_for_native_offset);
269 gpointer
270 mono_get_throw_exception (void)
272 g_assert (throw_exception_func);
273 return throw_exception_func;
276 gpointer
277 mono_get_rethrow_exception (void)
279 g_assert (rethrow_exception_func);
280 return rethrow_exception_func;
283 gpointer
284 mono_get_rethrow_preserve_exception (void)
286 g_assert (rethrow_preserve_exception_func);
287 return rethrow_preserve_exception_func;
290 static void
291 no_call_filter (void)
293 g_assert_not_reached ();
296 gpointer
297 mono_get_call_filter (void)
299 /* This is called even in llvmonly mode etc. */
300 if (!call_filter_func)
301 return (gpointer)no_call_filter;
302 return call_filter_func;
305 gpointer
306 mono_get_restore_context (void)
308 g_assert (restore_context_func);
309 return restore_context_func;
312 gpointer
313 mono_get_throw_corlib_exception (void)
315 gpointer code = NULL;
316 MonoTrampInfo *info;
318 /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */
319 if (throw_corlib_exception_func)
320 return throw_corlib_exception_func;
322 if (mono_ee_features.use_aot_trampolines)
323 code = mono_aot_get_trampoline ("throw_corlib_exception");
324 else {
325 code = mono_arch_get_throw_corlib_exception (&info, FALSE);
326 mono_tramp_info_register (info, NULL);
329 mono_memory_barrier ();
331 throw_corlib_exception_func = code;
333 return throw_corlib_exception_func;
337 * mono_get_throw_exception_addr:
339 * Return an address which stores the result of
340 * mono_get_throw_exception.
342 gpointer
343 mono_get_throw_exception_addr (void)
345 return &throw_exception_func;
348 gpointer
349 mono_get_rethrow_preserve_exception_addr (void)
351 return &rethrow_preserve_exception_func;
354 static gboolean
355 is_address_protected (MonoJitInfo *ji, MonoJitExceptionInfo *ei, gpointer ip)
357 MonoTryBlockHoleTableJitInfo *table;
358 int i;
359 guint32 offset;
360 guint16 clause;
362 if (ei->try_start > ip || ip >= ei->try_end)
363 return FALSE;
365 if (!ji->has_try_block_holes)
366 return TRUE;
368 table = mono_jit_info_get_try_block_hole_table_info (ji);
369 offset = (guint32)((char*)ip - (char*)ji->code_start);
370 clause = (guint16)(ei - ji->clauses);
371 g_assert (clause < ji->num_clauses);
373 for (i = 0; i < table->num_holes; ++i) {
374 MonoTryBlockHoleJitInfo *hole = &table->holes [i];
375 if (hole->clause == clause && hole->offset <= offset && hole->offset + hole->length > offset)
376 return FALSE;
378 return TRUE;
381 #ifdef MONO_ARCH_HAVE_UNWIND_BACKTRACE
383 #if 0
384 static gboolean show_native_addresses = TRUE;
385 #else
386 static gboolean show_native_addresses = FALSE;
387 #endif
389 static _Unwind_Reason_Code
390 build_stack_trace (struct _Unwind_Context *frame_ctx, void *state)
392 MonoDomain *domain = mono_domain_get ();
393 uintptr_t ip = _Unwind_GetIP (frame_ctx);
395 if (show_native_addresses || mono_jit_info_table_find (domain, (char*)ip)) {
396 GList **trace_ips = (GList **)state;
397 *trace_ips = g_list_prepend (*trace_ips, (gpointer)ip);
400 return _URC_NO_REASON;
403 static GSList*
404 get_unwind_backtrace (void)
406 GSList *ips = NULL;
408 _Unwind_Backtrace (build_stack_trace, &ips);
410 return g_slist_reverse (ips);
413 #else
415 static GSList*
416 get_unwind_backtrace (void)
418 return NULL;
421 #endif
423 static gboolean
424 arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls,
425 MonoJitInfo *ji, MonoContext *ctx,
426 MonoContext *new_ctx, MonoLMF **lmf,
427 host_mgreg_t **save_locations,
428 StackFrameInfo *frame)
430 if (!ji && *lmf) {
431 if (((gsize)(*lmf)->previous_lmf) & 2) {
432 MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
434 memset (frame, 0, sizeof (StackFrameInfo));
435 frame->ji = ji;
437 *new_ctx = *ctx;
439 if (ext->kind == MONO_LMFEXT_DEBUGGER_INVOKE) {
441 * This LMF entry is created by the soft debug code to mark transitions to
442 * managed code done during invokes.
444 frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
445 memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
446 } else if (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX) {
447 frame->type = FRAME_TYPE_INTERP_TO_MANAGED;
448 frame->interp_exit_data = ext->interp_exit_data;
449 if (ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX) {
450 frame->type = FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX;
451 memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
453 } else {
454 g_assert_not_reached ();
457 *lmf = (MonoLMF *)(((gsize)(*lmf)->previous_lmf) & ~3);
459 return TRUE;
463 return mono_arch_unwind_frame (domain, jit_tls, ji, ctx, new_ctx, lmf, save_locations, frame);
467 * find_jit_info:
469 * Translate between the mono_arch_unwind_frame function and the old API.
471 static MonoJitInfo *
472 find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
473 MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
475 StackFrameInfo frame;
476 MonoJitInfo *ji;
477 gboolean err;
478 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
480 /* Avoid costly table lookup during stack overflow */
481 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
482 ji = prev_ji;
483 else
484 ji = mini_jit_info_table_find (domain, ip, NULL);
486 if (managed)
487 *managed = FALSE;
489 err = arch_unwind_frame (domain, jit_tls, ji, ctx, new_ctx, lmf, NULL, &frame);
490 if (!err)
491 return (MonoJitInfo *)-1;
493 if (*lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
495 * Remove any unused lmf.
496 * Mask out the lower bits which might be used to hold additional information.
498 *lmf = (MonoLMF *)(((gsize)(*lmf)->previous_lmf) & ~(TARGET_SIZEOF_VOID_P -1));
501 /* Convert between the new and the old APIs */
502 switch (frame.type) {
503 case FRAME_TYPE_MANAGED:
504 if (managed)
505 *managed = TRUE;
506 return frame.ji;
507 case FRAME_TYPE_TRAMPOLINE:
508 return frame.ji;
509 case FRAME_TYPE_MANAGED_TO_NATIVE:
510 if (frame.ji)
511 return frame.ji;
512 else {
513 memset (res, 0, sizeof (MonoJitInfo));
514 res->d.method = frame.method;
515 return res;
517 case FRAME_TYPE_DEBUGGER_INVOKE: {
518 MonoContext tmp_ctx;
521 * The normal exception handling code can't handle this frame, so just
522 * skip it.
524 ji = find_jit_info (domain, jit_tls, res, NULL, new_ctx, &tmp_ctx, lmf, managed);
525 memcpy (new_ctx, &tmp_ctx, sizeof (MonoContext));
526 return ji;
528 default:
529 g_assert_not_reached ();
530 return NULL;
534 /* mono_find_jit_info:
536 * This function is used to gather information from @ctx. It return the
537 * MonoJitInfo of the corresponding function, unwinds one stack frame and
538 * stores the resulting context into @new_ctx. It also stores a string
539 * describing the stack location into @trace (if not NULL), and modifies
540 * the @lmf if necessary. @native_offset return the IP offset from the
541 * start of the function or -1 if that info is not available.
543 MonoJitInfo *
544 mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
545 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
546 gboolean *managed)
548 gboolean managed2;
549 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
550 MonoJitInfo *ji;
551 MonoMethod *method = NULL;
553 if (trace)
554 *trace = NULL;
556 if (native_offset)
557 *native_offset = -1;
559 if (managed)
560 *managed = FALSE;
562 ji = find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
564 if (ji == (gpointer)-1)
565 return ji;
567 if (ji && !ji->is_trampoline)
568 method = jinfo_get_method (ji);
570 if (managed2 || (method && method->wrapper_type)) {
571 const char *real_ip, *start;
572 gint32 offset;
574 start = (const char *)ji->code_start;
575 if (!managed2)
576 /* ctx->ip points into native code */
577 real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx);
578 else
579 real_ip = (const char*)ip;
581 if ((real_ip >= start) && (real_ip <= start + ji->code_size))
582 offset = real_ip - start;
583 else
584 offset = -1;
586 if (native_offset)
587 *native_offset = offset;
589 if (managed)
590 if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
591 *managed = TRUE;
593 if (trace)
594 *trace = mono_debug_print_stack_frame (method, offset, domain);
595 } else {
596 if (trace) {
597 char *fname = mono_method_full_name (jinfo_get_method (res), TRUE);
598 *trace = g_strdup_printf ("in (unmanaged) %s", fname);
599 g_free (fname);
603 return ji;
607 * mono_find_jit_info_ext:
609 * A version of mono_find_jit_info which returns all data in the StackFrameInfo
610 * structure.
611 * A note about frames of type FRAME_TYPE_MANAGED_TO_NATIVE:
612 * - These frames are used to mark managed-to-native transitions, so CTX will refer to native
613 * code, and new_ctx will refer to the last managed frame. The caller should unwind once more
614 * to obtain the last managed frame.
615 * If SAVE_LOCATIONS is not NULL, it should point to an array of size MONO_MAX_IREGS.
616 * On return, it will be filled with the locations where callee saved registers are saved
617 * by the current frame. This is returned outside of StackFrameInfo because it can be
618 * quite large on some platforms.
619 * If ASYNC true, this function will be async safe, but some fields of frame and frame->ji will
620 * not be set.
622 gboolean
623 mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls,
624 MonoJitInfo *prev_ji, MonoContext *ctx,
625 MonoContext *new_ctx, char **trace, MonoLMF **lmf,
626 host_mgreg_t **save_locations,
627 StackFrameInfo *frame)
629 gboolean err;
630 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
631 MonoJitInfo *ji;
632 MonoDomain *target_domain = domain;
633 MonoMethod *method = NULL;
634 gboolean async = mono_thread_info_is_async_context ();
636 if (trace)
637 *trace = NULL;
639 /* Avoid costly table lookup during stack overflow */
640 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
641 ji = prev_ji;
642 else
643 ji = mini_jit_info_table_find_ext (domain, ip, TRUE, &target_domain);
645 if (!target_domain)
646 target_domain = domain;
648 if (save_locations)
649 memset (save_locations, 0, MONO_MAX_IREGS * sizeof (host_mgreg_t*));
651 err = arch_unwind_frame (target_domain, jit_tls, ji, ctx, new_ctx, lmf, save_locations, frame);
652 if (!err)
653 return FALSE;
655 gboolean not_i2m = frame->type != FRAME_TYPE_INTERP_TO_MANAGED && frame->type != FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX;
657 if (not_i2m && *lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
659 * Remove any unused lmf.
660 * Mask out the lower bits which might be used to hold additional information.
662 *lmf = (MonoLMF *)(((gsize)(*lmf)->previous_lmf) & ~(TARGET_SIZEOF_VOID_P -1));
665 if (frame->ji && !frame->ji->is_trampoline && !frame->ji->async)
666 method = jinfo_get_method (frame->ji);
668 if (frame->type == FRAME_TYPE_MANAGED && method) {
669 if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
670 frame->managed = TRUE;
673 if (frame->type == FRAME_TYPE_MANAGED_TO_NATIVE) {
675 * This type of frame is just a marker, the caller should unwind once more to get the
676 * last managed frame.
678 frame->ji = NULL;
679 frame->method = NULL;
682 frame->native_offset = -1;
683 frame->domain = target_domain;
684 frame->async_context = async;
685 frame->frame_addr = MONO_CONTEXT_GET_SP (ctx);
687 ji = frame->ji;
689 if (frame->type == FRAME_TYPE_MANAGED)
690 frame->method = method;
692 if (ji && (frame->managed || (method && method->wrapper_type))) {
693 const char *real_ip, *start;
695 start = (const char *)ji->code_start;
696 if (frame->type == FRAME_TYPE_MANAGED)
697 real_ip = (const char*)ip;
698 else
699 /* ctx->ip points into native code */
700 real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx);
702 if ((real_ip >= start) && (real_ip <= start + ji->code_size))
703 frame->native_offset = real_ip - start;
704 else {
705 frame->native_offset = -1;
708 if (trace)
709 *trace = mono_debug_print_stack_frame (method, frame->native_offset, domain);
710 } else {
711 if (trace && frame->method) {
712 char *fname = mono_method_full_name (frame->method, TRUE);
713 *trace = g_strdup_printf ("in (unmanaged) %s", fname);
714 g_free (fname);
718 return TRUE;
721 typedef struct {
722 gboolean in_interp;
723 MonoInterpStackIter interp_iter;
724 gpointer last_frame_addr;
725 } Unwinder;
727 static void
728 unwinder_init (Unwinder *unwinder)
730 memset (unwinder, 0, sizeof (Unwinder));
733 #if defined(__GNUC__) && defined(TARGET_ARM64)
734 /* gcc 4.9.2 seems to miscompile this on arm64 */
735 static __attribute__((optimize("O0"))) gboolean
736 #else
737 static gboolean
738 #endif
739 unwinder_unwind_frame (Unwinder *unwinder,
740 MonoDomain *domain, MonoJitTlsData *jit_tls,
741 MonoJitInfo *prev_ji, MonoContext *ctx,
742 MonoContext *new_ctx, char **trace, MonoLMF **lmf,
743 host_mgreg_t **save_locations,
744 StackFrameInfo *frame)
746 if (unwinder->in_interp) {
747 memcpy (new_ctx, ctx, sizeof (MonoContext));
749 /* Process debugger invokes */
750 /* The DEBUGGER_INVOKE should be returned before the first interpreter frame for the invoke */
751 if (unwinder->last_frame_addr < (gpointer)(*lmf)) {
752 if (((gsize)(*lmf)->previous_lmf) & 2) {
753 MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
754 if (ext->kind == MONO_LMFEXT_DEBUGGER_INVOKE) {
755 *lmf = (MonoLMF *)(((gsize)(*lmf)->previous_lmf) & ~7);
756 frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
757 return TRUE;
762 unwinder->in_interp = mini_get_interp_callbacks ()->frame_iter_next (&unwinder->interp_iter, frame);
763 if (frame->type == FRAME_TYPE_INTERP) {
764 const gpointer parent = mini_get_interp_callbacks ()->frame_get_parent (frame->interp_frame);
765 unwinder->last_frame_addr = parent;
768 if (!unwinder->in_interp)
769 return unwinder_unwind_frame (unwinder, domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf, save_locations, frame);
770 return TRUE;
771 } else {
772 gboolean res = mono_find_jit_info_ext (domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf,
773 save_locations, frame);
774 if (!res)
775 return FALSE;
776 if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED || frame->type == FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX) {
777 unwinder->in_interp = TRUE;
778 mini_get_interp_callbacks ()->frame_iter_init (&unwinder->interp_iter, frame->interp_exit_data);
780 unwinder->last_frame_addr = frame->frame_addr;
781 return TRUE;
786 * This function is async-safe.
788 static gpointer
789 get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
791 MonoGenericJitInfo *gi;
792 MonoMethod *method;
793 gpointer info;
795 if (!ji->has_generic_jit_info)
796 return NULL;
797 gi = mono_jit_info_get_generic_jit_info (ji);
798 if (!gi->has_this)
799 return NULL;
801 info = NULL;
803 * Search location list if available, it contains the precise location of the
804 * argument for every pc offset, even if the method was interrupted while it was in
805 * its prolog.
807 if (gi->nlocs) {
808 int offset = (gsize)MONO_CONTEXT_GET_IP (ctx) - (gsize)ji->code_start;
809 int i;
811 for (i = 0; i < gi->nlocs; ++i) {
812 MonoDwarfLocListEntry *entry = &gi->locations [i];
814 if (offset >= entry->from && (offset < entry->to || entry->to == 0)) {
815 if (entry->is_reg)
816 info = (gpointer)mono_arch_context_get_int_reg (ctx, entry->reg);
817 else
818 info = *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, entry->reg) + entry->offset);
819 break;
822 g_assert (i < gi->nlocs);
823 } else {
824 if (gi->this_in_reg)
825 info = (gpointer)mono_arch_context_get_int_reg (ctx, gi->this_reg);
826 else
827 info = *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) +
828 gi->this_offset);
831 method = jinfo_get_method (ji);
832 if (mono_method_get_context (method)->method_inst) {
833 /* A MonoMethodRuntimeGenericContext* */
834 return info;
835 } else if ((method->flags & METHOD_ATTRIBUTE_STATIC) || m_class_is_valuetype (method->klass)) {
836 /* A MonoVTable* */
837 return info;
838 } else {
839 /* Avoid returning a managed object */
840 MonoObject *this_obj = (MonoObject *)info;
842 return this_obj->vtable;
847 * generic_info is either a MonoMethodRuntimeGenericContext or a MonoVTable.
849 MonoGenericContext
850 mono_get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
852 MonoGenericContext context = { NULL, NULL };
853 MonoClass *klass, *method_container_class;
854 MonoMethod *method;
856 g_assert (generic_info);
858 method = jinfo_get_method (ji);
859 g_assert (method->is_inflated);
860 if (mono_method_get_context (method)->method_inst) {
861 MonoMethodRuntimeGenericContext *mrgctx = (MonoMethodRuntimeGenericContext *)generic_info;
863 klass = mrgctx->class_vtable->klass;
864 context.method_inst = mrgctx->method_inst;
865 g_assert (context.method_inst);
866 } else {
867 MonoVTable *vtable = (MonoVTable *)generic_info;
869 klass = vtable->klass;
872 //g_assert (!mono_class_is_gtd (method->klass));
873 if (mono_class_is_ginst (method->klass))
874 method_container_class = mono_class_get_generic_class (method->klass)->container_class;
875 else
876 method_container_class = method->klass;
878 /* class might refer to a subclass of method's class */
879 while (!(klass == method->klass || (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->container_class == method_container_class))) {
880 klass = m_class_get_parent (klass);
881 g_assert (klass);
884 if (mono_class_is_ginst (klass) || mono_class_is_gtd (klass))
885 context.class_inst = mini_class_get_context (klass)->class_inst;
887 if (mono_class_is_ginst (klass))
888 g_assert (mono_class_has_parent_and_ignore_generics (mono_class_get_generic_class (klass)->container_class, method_container_class));
889 else
890 g_assert (mono_class_has_parent_and_ignore_generics (klass, method_container_class));
892 return context;
896 static MonoMethod*
897 get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info)
899 ERROR_DECL (error);
900 MonoGenericContext context;
901 MonoMethod *method;
903 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
904 return jinfo_get_method (ji);
905 context = mono_get_generic_context_from_stack_frame (ji, generic_info);
907 method = jinfo_get_method (ji);
908 method = mono_method_get_declaring_generic_method (method);
909 method = mono_class_inflate_generic_method_checked (method, &context, error);
910 g_assert (is_ok (error)); /* FIXME don't swallow the error */
912 return method;
916 * mono_exception_walk_native_trace:
917 * \param ex The exception object whose frames should be walked
918 * \param func callback to call for each stack frame
919 * \param user_data data passed to the callback
920 * This function walks the stacktrace of an exception. For
921 * each frame the callback function is called with the relevant info.
922 * The walk ends when no more stack frames are found or when the callback
923 * returns a TRUE value.
926 gboolean
927 mono_exception_walk_trace (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data)
929 gboolean res;
931 MONO_ENTER_GC_UNSAFE;
932 res = mono_exception_walk_trace_internal (ex, func, user_data);
933 MONO_EXIT_GC_UNSAFE;
934 return res;
937 static gboolean
938 mono_exception_stackframe_obj_walk (MonoStackFrame *captured_frame, MonoExceptionFrameWalk func, gpointer user_data)
940 if (!captured_frame)
941 return TRUE;
943 gpointer ip = (gpointer) (captured_frame->method_address + captured_frame->native_offset);
944 MonoJitInfo *ji = mono_jit_info_table_find_internal (mono_domain_get (), ip, TRUE, TRUE);
946 // Other domain maybe?
947 if (!ji)
948 return FALSE;
949 MonoMethod *method = jinfo_get_method (ji);
951 gboolean r = func (method, (gpointer) captured_frame->method_address, captured_frame->native_offset, TRUE, user_data);
952 if (r)
953 return TRUE;
955 return FALSE;
958 static gboolean
959 mono_exception_stacktrace_obj_walk (MonoStackTrace *st, MonoExceptionFrameWalk func, gpointer user_data)
961 int num_captured = st->captured_traces ? mono_array_length_internal (st->captured_traces) : 0;
962 for (int i=0; i < num_captured; i++) {
963 MonoStackTrace *curr_trace = mono_array_get_fast (st->captured_traces, MonoStackTrace *, i);
964 mono_exception_stacktrace_obj_walk (curr_trace, func, user_data);
967 int num_frames = st->frames ? mono_array_length_internal (st->frames) : 0;
968 for (int frame = 0; frame < num_frames; frame++) {
969 gboolean r = mono_exception_stackframe_obj_walk (mono_array_get_fast (st->frames, MonoStackFrame *, frame), func, user_data);
970 if (r)
971 return TRUE;
974 return TRUE;
977 gboolean
978 mono_exception_walk_trace_internal (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data)
980 MONO_REQ_GC_UNSAFE_MODE;
982 MonoDomain *domain = mono_domain_get ();
983 MonoArray *ta = ex->trace_ips;
985 /* Exception is not thrown yet */
986 if (ta == NULL)
987 return FALSE;
989 int len = mono_array_length_internal (ta) / TRACE_IP_ENTRY_SIZE;
990 gboolean otherwise_has_traces = len > 0;
992 for (int i = 0; i < len; i++) {
993 ExceptionTraceIp trace_ip;
995 memcpy (&trace_ip, mono_array_addr_fast (ta, ExceptionTraceIp, i), sizeof (ExceptionTraceIp));
996 gpointer ip = trace_ip.ip;
997 gpointer generic_info = trace_ip.generic_info;
999 MonoJitInfo *ji = NULL;
1000 if (trace_ip.ji) {
1001 ji = trace_ip.ji;
1002 } else {
1003 ji = mono_jit_info_table_find (domain, ip);
1006 if (ji == NULL) {
1007 gboolean r;
1008 MONO_ENTER_GC_SAFE;
1009 r = func (NULL, ip, 0, FALSE, user_data);
1010 MONO_EXIT_GC_SAFE;
1011 if (r)
1012 break;
1013 } else {
1014 MonoMethod *method = get_method_from_stack_frame (ji, generic_info);
1015 if (func (method, ji->code_start, (char *) ip - (char *) ji->code_start, TRUE, user_data))
1016 break;
1020 ta = (MonoArray *) ex->captured_traces;
1021 len = ta ? mono_array_length_internal (ta) : 0;
1022 gboolean captured_has_traces = len > 0;
1024 for (int i = 0; i < len; i++) {
1025 MonoStackTrace *captured_trace = mono_array_get_fast (ta, MonoStackTrace *, i);
1026 if (!captured_trace)
1027 break;
1029 mono_exception_stacktrace_obj_walk (captured_trace, func, user_data);
1032 return captured_has_traces || otherwise_has_traces;
1035 MonoArray *
1036 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
1038 ERROR_DECL (error);
1039 MonoDomain *domain = mono_domain_get ();
1040 MonoArray *res;
1041 MonoArray *ta = exc->trace_ips;
1042 MonoDebugSourceLocation *location;
1043 int i, len;
1045 if (ta == NULL) {
1046 /* Exception is not thrown yet */
1047 res = mono_array_new_checked (domain, mono_defaults.stack_frame_class, 0, error);
1048 mono_error_set_pending_exception (error);
1049 return res;
1052 HANDLE_FUNCTION_ENTER ();
1054 MONO_HANDLE_PIN (ta);
1056 len = mono_array_length_internal (ta) / TRACE_IP_ENTRY_SIZE;
1058 res = mono_array_new_checked (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0, error);
1059 if (!is_ok (error))
1060 goto fail;
1062 MONO_HANDLE_PIN (res);
1064 MonoObjectHandle sf_h;
1065 sf_h = MONO_HANDLE_NEW (MonoObject, NULL);
1067 for (i = skip; i < len; i++) {
1068 MonoJitInfo *ji;
1069 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new_checked (domain, mono_defaults.stack_frame_class, error);
1070 if (!is_ok (error))
1071 goto fail;
1072 MONO_HANDLE_ASSIGN_RAW (sf_h, sf);
1074 ExceptionTraceIp trace_ip;
1075 memcpy (&trace_ip, mono_array_addr_fast (ta, ExceptionTraceIp, i), sizeof (ExceptionTraceIp));
1076 gpointer ip = trace_ip.ip;
1077 gpointer generic_info = trace_ip.generic_info;
1078 MonoMethod *method;
1080 if (trace_ip.ji) {
1081 ji = trace_ip.ji;
1082 } else {
1083 ji = mono_jit_info_table_find (domain, ip);
1084 if (ji == NULL) {
1085 /* Unmanaged frame */
1086 mono_array_setref_internal (res, i, sf);
1087 continue;
1091 g_assert (ji != NULL);
1093 if (mono_llvm_only || !generic_info)
1094 /* Can't resolve actual method */
1095 method = jinfo_get_method (ji);
1096 else
1097 method = get_method_from_stack_frame (ji, generic_info);
1098 if (jinfo_get_method (ji)->wrapper_type) {
1099 char *s;
1101 sf->method = NULL;
1102 s = mono_method_get_name_full (method, TRUE, FALSE, MONO_TYPE_NAME_FORMAT_REFLECTION);
1103 MonoString *name = mono_string_new_checked (domain, s, error);
1104 g_free (s);
1105 if (!is_ok (error))
1106 goto fail;
1107 MONO_OBJECT_SETREF_INTERNAL (sf, internal_method_name, name);
1109 else {
1110 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, error);
1111 if (!is_ok (error))
1112 goto fail;
1113 MONO_OBJECT_SETREF_INTERNAL (sf, method, rm);
1116 sf->method_index = ji->from_aot ? mono_aot_find_method_index (method) : 0xffffff;
1117 sf->method_address = (gsize) ji->code_start;
1118 sf->native_offset = (char *)ip - (char *)ji->code_start;
1121 * mono_debug_lookup_source_location() returns both the file / line number information
1122 * and the IL offset. Note that computing the IL offset is already an expensive
1123 * operation, so we shouldn't call this method twice.
1125 location = mono_debug_lookup_source_location (jinfo_get_method (ji), sf->native_offset, domain);
1126 if (location) {
1127 sf->il_offset = location->il_offset;
1128 } else {
1129 SeqPoint sp;
1130 if (mono_find_prev_seq_point_for_native_offset (domain, jinfo_get_method (ji), sf->native_offset, NULL, &sp))
1131 sf->il_offset = sp.il_offset;
1132 else
1133 sf->il_offset = -1;
1136 if (need_file_info) {
1137 if (location && location->source_file) {
1138 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
1139 if (!is_ok (error))
1140 goto fail;
1141 MONO_OBJECT_SETREF_INTERNAL (sf, filename, filename);
1142 sf->line = location->row;
1143 sf->column = location->column;
1144 } else {
1145 sf->line = sf->column = 0;
1146 sf->filename = NULL;
1150 mono_debug_free_source_location (location);
1151 mono_array_setref_internal (res, i - skip, sf);
1153 goto exit;
1155 fail:
1156 mono_error_set_pending_exception (error);
1157 res = NULL;
1158 exit:
1159 HANDLE_FUNCTION_RETURN_VAL (res);
1162 static void
1163 mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data)
1165 if (!start_ctx) {
1166 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
1167 if (jit_tls && jit_tls->orig_ex_ctx_set)
1168 start_ctx = &jit_tls->orig_ex_ctx;
1170 mono_walk_stack_with_ctx (func, start_ctx, unwind_options, user_data);
1173 * mono_walk_stack_with_ctx:
1174 * Unwind the current thread starting at \p start_ctx.
1175 * If \p start_ctx is null, we capture the current context.
1177 void
1178 mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data)
1180 MonoContext extra_ctx;
1181 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
1182 MONO_ARCH_CONTEXT_DEF
1184 if (!thread || !thread->jit_data)
1185 return;
1187 if (!start_ctx) {
1188 mono_arch_flush_register_windows ();
1189 MONO_INIT_CONTEXT_FROM_FUNC (&extra_ctx, mono_walk_stack_with_ctx);
1190 start_ctx = &extra_ctx;
1193 mono_walk_stack_full (func, start_ctx, mono_domain_get (), thread->jit_data, mono_get_lmf (), unwind_options, user_data, FALSE);
1197 * mono_walk_stack_with_state:
1198 * Unwind a thread described by \p state.
1200 * State must be valid (state->valid == TRUE).
1202 * If you are using this function to unwind another thread, make sure it is suspended.
1204 * If \p state is null, we capture the current context.
1206 void
1207 mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions unwind_options, void *user_data)
1209 MonoThreadUnwindState extra_state;
1210 if (!state) {
1211 g_assert (!mono_thread_info_is_async_context ());
1212 if (!mono_thread_state_init_from_current (&extra_state))
1213 return;
1214 state = &extra_state;
1217 g_assert (state->valid);
1219 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
1220 /* Not attached */
1221 return;
1223 mono_walk_stack_full (func,
1224 &state->ctx,
1225 (MonoDomain *)state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
1226 (MonoJitTlsData *)state->unwind_data [MONO_UNWIND_DATA_JIT_TLS],
1227 (MonoLMF *)state->unwind_data [MONO_UNWIND_DATA_LMF],
1228 unwind_options, user_data, FALSE);
1231 void
1232 mono_walk_stack (MonoJitStackWalk func, MonoUnwindOptions options, void *user_data)
1234 MonoThreadUnwindState state;
1235 if (!mono_thread_state_init_from_current (&state))
1236 return;
1237 mono_walk_stack_with_state (func, &state, options, user_data);
1241 * mono_walk_stack_full:
1242 * \param func callback to call for each stack frame
1243 * \param domain starting appdomain, can be NULL to use the current domain
1244 * \param unwind_options what extra information the unwinder should gather
1245 * \param start_ctx starting state of the stack walk, can be NULL.
1246 * \param thread the thread whose stack to walk, can be NULL to use the current thread
1247 * \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
1248 * \param user_data data passed to the callback
1249 * \param crash_context tells us that we're in a context where it's not safe to lock or allocate
1250 * This function walks the stack of a thread, starting from the state
1251 * represented by \p start_ctx. For each frame the callback
1252 * function is called with the relevant info. The walk ends when no more
1253 * managed stack frames are found or when the callback returns a TRUE value.
1255 static void
1256 mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context)
1258 gint il_offset;
1259 MonoContext ctx, new_ctx;
1260 StackFrameInfo frame;
1261 gboolean res;
1262 host_mgreg_t *reg_locations [MONO_MAX_IREGS];
1263 host_mgreg_t *new_reg_locations [MONO_MAX_IREGS];
1264 gboolean get_reg_locations = unwind_options & MONO_UNWIND_REG_LOCATIONS;
1265 gboolean async = mono_thread_info_is_async_context ();
1266 Unwinder unwinder;
1268 memset (&frame, 0, sizeof (StackFrameInfo));
1270 #ifndef TARGET_WASM
1271 if (mono_llvm_only) {
1272 GSList *l, *ips;
1274 if (async)
1275 return;
1277 ips = get_unwind_backtrace ();
1278 for (l = ips; l; l = l->next) {
1279 guint8 *ip = (guint8*)l->data;
1280 memset (&frame, 0, sizeof (StackFrameInfo));
1281 frame.ji = mini_jit_info_table_find (domain, ip, &frame.domain);
1282 if (!frame.ji || frame.ji->is_trampoline)
1283 continue;
1284 frame.type = FRAME_TYPE_MANAGED;
1285 frame.method = jinfo_get_method (frame.ji);
1286 // FIXME: Cannot lookup the actual method
1287 frame.actual_method = frame.method;
1288 if (frame.type == FRAME_TYPE_MANAGED) {
1289 if (!frame.method->wrapper_type || frame.method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
1290 frame.managed = TRUE;
1292 frame.native_offset = ip - (guint8*)frame.ji->code_start;
1293 frame.il_offset = -1;
1295 if (func (&frame, NULL, user_data))
1296 break;
1298 g_slist_free (ips);
1299 return;
1301 #endif
1303 if (!start_ctx) {
1304 g_warning ("start_ctx required for stack walk");
1305 return;
1308 if (!domain) {
1309 g_warning ("domain required for stack walk");
1310 return;
1313 if (!jit_tls) {
1314 g_warning ("jit_tls required for stack walk");
1315 return;
1318 /*The LMF will be null if the target have no managed frames.*/
1319 /* g_assert (lmf); */
1320 if (async && (unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD)) {
1321 g_warning ("async && (unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD) not legal");
1322 return;
1325 memcpy (&ctx, start_ctx, sizeof (MonoContext));
1326 memset (reg_locations, 0, sizeof (reg_locations));
1328 unwinder_init (&unwinder);
1330 while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
1331 frame.lmf = lmf;
1332 res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame);
1333 if (!res)
1334 return;
1336 if (frame.type == FRAME_TYPE_TRAMPOLINE)
1337 goto next;
1339 if ((unwind_options & MONO_UNWIND_LOOKUP_IL_OFFSET) && frame.ji) {
1340 MonoDebugSourceLocation *source = NULL;
1342 // Don't do this when we can be in a signal handler
1343 if (!crash_context)
1344 source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, domain);
1345 if (source) {
1346 il_offset = source->il_offset;
1347 } else {
1348 MonoSeqPointInfo *seq_points = NULL;
1350 // It's more reliable to look into the global cache if possible
1351 if (crash_context)
1352 seq_points = (MonoSeqPointInfo *) frame.ji->seq_points;
1353 else
1354 seq_points = mono_get_seq_points (domain, jinfo_get_method (frame.ji));
1356 SeqPoint sp;
1357 if (seq_points && mono_seq_point_find_prev_by_native_offset (seq_points, frame.native_offset, &sp))
1358 il_offset = sp.il_offset;
1359 else
1360 il_offset = -1;
1362 mono_debug_free_source_location (source);
1363 } else
1364 il_offset = -1;
1366 frame.il_offset = il_offset;
1368 if ((unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD) && frame.ji) {
1369 frame.actual_method = get_method_from_stack_frame (frame.ji, get_generic_info_from_stack_frame (frame.ji, &ctx));
1370 } else {
1371 frame.actual_method = frame.method;
1374 if (get_reg_locations)
1375 frame.reg_locations = reg_locations;
1377 if (func (&frame, &ctx, user_data))
1378 return;
1380 next:
1381 if (get_reg_locations) {
1382 for (int i = 0; i < MONO_MAX_IREGS; ++i)
1383 if (new_reg_locations [i])
1384 reg_locations [i] = new_reg_locations [i];
1387 ctx = new_ctx;
1391 #ifdef DISABLE_CRASH_REPORTING
1393 static void
1394 mono_summarize_managed_stack (MonoThreadSummary *out)
1396 return;
1399 static void
1400 mono_summarize_unmanaged_stack (MonoThreadSummary *out)
1402 return;
1405 static void
1406 mono_summarize_exception (MonoException *exc, MonoThreadSummary *out)
1408 return;
1411 static void
1412 mono_crash_reporting_register_native_library (const char *module_path, const char *module_name)
1414 return;
1417 static void
1418 mono_crash_reporting_allow_all_native_libraries ()
1420 return;
1424 #else
1426 typedef struct {
1427 MonoFrameSummary *frames;
1428 int num_frames;
1429 int max_frames;
1430 MonoStackHash *hashes;
1431 const char *error;
1432 } MonoSummarizeUserData;
1434 static void
1435 copy_summary_string_safe (char *dest, const char *src)
1437 g_strlcpy (dest, src, MONO_MAX_SUMMARY_NAME_LEN);
1440 static void
1441 fill_frame_managed_info (MonoFrameSummary *frame, MonoMethod * method)
1443 MonoImage *image = mono_class_get_image (method->klass);
1444 // Used for hashing, more stable across rebuilds than using GUID
1445 copy_summary_string_safe (frame->str_descr, image->assembly_name);
1447 frame->managed_data.guid = image->guid;
1448 frame->managed_data.token = method->token;
1449 frame->managed_data.filename = image->module_name;
1451 MonoDotNetHeader *header = &image->image_info->cli_header;
1452 frame->managed_data.image_size = header->nt.pe_image_size;
1453 frame->managed_data.time_date_stamp = image->time_date_stamp;
1456 typedef struct {
1457 char *suffix;
1458 char *exported_name;
1459 } MonoLibAllowlistEntry;
1461 static GList *native_library_allowlist;
1462 static gboolean allow_all_native_libraries = FALSE;
1464 static void
1465 mono_crash_reporting_register_native_library (const char *module_path, const char *module_name)
1467 // Examples: libsystem_pthread.dylib -> "pthread"
1468 // Examples: libsystem_platform.dylib -> "platform"
1469 // Examples: mono-sgen -> "mono" from above line
1470 MonoLibAllowlistEntry*entry = g_new0 (MonoLibAllowlistEntry, 1);
1471 entry->suffix = g_strdup (module_path);
1472 entry->exported_name = g_strdup (module_name);
1473 native_library_allowlist = g_list_append (native_library_allowlist, entry);
1476 static void
1477 mono_crash_reporting_allow_all_native_libraries ()
1479 allow_all_native_libraries = TRUE;
1482 static gboolean
1483 check_allowlisted_module (const char *in_name, const char **out_module)
1485 #ifndef MONO_PRIVATE_CRASHES
1486 return TRUE;
1487 #else
1488 if (g_str_has_suffix (in_name, "mono-sgen")) {
1489 if (out_module)
1490 copy_summary_string_safe ((char *) *out_module, "mono");
1491 return TRUE;
1493 if (allow_all_native_libraries) {
1494 if (out_module) {
1495 /* for a module name, use the basename of the full path in in_name */
1496 char *basename = (char *) in_name, *p = (char *) in_name;
1497 while (*p != '\0') {
1498 if (*p == '/')
1499 basename = p + 1;
1500 p++;
1502 if (*basename)
1503 copy_summary_string_safe ((char *) *out_module, basename);
1504 else
1505 copy_summary_string_safe ((char *) *out_module, "unknown");
1508 return TRUE;
1511 for (GList *cursor = native_library_allowlist; cursor; cursor = cursor->next) {
1512 MonoLibAllowlistEntry*iter = (MonoLibAllowlistEntry*) cursor->data;
1513 if (!g_str_has_suffix (in_name, iter->suffix))
1514 continue;
1515 if (out_module)
1516 copy_summary_string_safe ((char *) *out_module, iter->exported_name);
1517 return TRUE;
1520 return FALSE;
1521 #endif
1524 static intptr_t
1525 mono_make_portable_ip (intptr_t in_ip, intptr_t module_base)
1527 // FIXME: Make generalize away from llvm tools?
1528 // So lldb starts the pointer base at 0x100000000
1529 // and expects to get pointers as (offset + constant)
1531 // Quirk shared by:
1532 // /usr/bin/symbols -- symbols version: @(#)PROGRAM:symbols PROJECT:SamplingTools-63501
1533 // *CoreSymbolicationDT.framework version: 63750*/
1534 intptr_t offset = in_ip - module_base;
1535 intptr_t magic_value = offset + 0x100000000;
1536 return magic_value;
1539 static gboolean
1540 mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, gint32 *out_offset, const char **out_module, char *out_name)
1542 // Note: it's not safe for us to be interrupted while inside of dl_addr, because if we
1543 // try to call dl_addr while interrupted while inside the lock, we will try to take a
1544 // non-recursive lock twice on this thread, and will deadlock.
1545 char sname [256], fname [256];
1546 void *saddr = NULL, *fbase = NULL;
1547 gboolean success = g_module_address ((void*)in_ip, fname, 256, &fbase, sname, 256, &saddr);
1548 if (!success)
1549 return FALSE;
1551 if (!check_allowlisted_module (fname, out_module))
1552 return FALSE;
1554 *out_ip = mono_make_portable_ip ((intptr_t) saddr, (intptr_t) fbase);
1555 *out_offset = in_ip - (intptr_t) saddr;
1557 if (saddr && out_name)
1558 copy_summary_string_safe (out_name, sname);
1559 return TRUE;
1562 static guint64
1563 summarize_offset_free_hash (guint64 accum, MonoFrameSummary *frame)
1565 if (!frame->is_managed)
1566 return accum;
1568 // See: mono_ptrarray_hash
1569 guint64 hash_accum = accum;
1571 // The assembly and the method token, no offsets
1572 hash_accum += mono_metadata_str_hash (frame->str_descr);
1573 hash_accum += frame->managed_data.token;
1575 return hash_accum;
1578 static guint64
1579 summarize_offset_rich_hash (guint64 accum, MonoFrameSummary *frame)
1581 // See: mono_ptrarray_hash
1582 guint64 hash_accum = accum;
1584 if (!frame->is_managed) {
1585 hash_accum += frame->unmanaged_data.ip;
1586 } else {
1587 hash_accum += mono_metadata_str_hash (frame->str_descr);
1588 hash_accum += frame->managed_data.token;
1589 hash_accum += frame->managed_data.il_offset;
1592 return hash_accum;
1595 static gboolean
1596 summarize_frame_internal (MonoMethod *method, gpointer ip, size_t native_offset, int il_offset, gboolean managed, gpointer user_data)
1598 MonoSummarizeUserData *ud = (MonoSummarizeUserData *) user_data;
1600 gboolean valid_state = ud->num_frames + 1 < ud->max_frames;
1601 if (!valid_state) {
1602 ud->error = "Exceeded the maximum number of frames";
1603 return TRUE;
1606 MonoFrameSummary *dest = &ud->frames [ud->num_frames];
1608 dest->unmanaged_data.ip = (intptr_t) ip;
1609 dest->is_managed = managed;
1610 dest->unmanaged_data.module [0] = '\0';
1612 if (!managed && method && method->wrapper_type != MONO_WRAPPER_NONE && method->wrapper_type < MONO_WRAPPER_NUM) {
1613 dest->is_managed = FALSE;
1614 dest->unmanaged_data.has_name = TRUE;
1615 copy_summary_string_safe (dest->str_descr, mono_wrapper_type_to_str (method->wrapper_type));
1618 #ifndef MONO_PRIVATE_CRASHES
1619 if (method)
1620 dest->managed_data.name = (char *) method->name;
1621 #endif
1623 if (managed) {
1624 if (!method) {
1625 ud->error = "Managed method frame, but no provided managed method";
1626 return TRUE;
1628 fill_frame_managed_info (dest, method);
1629 dest->managed_data.native_offset = native_offset;
1630 dest->managed_data.il_offset = il_offset;
1631 } else {
1632 dest->managed_data.token = -1;
1636 ud->hashes->offset_free_hash = summarize_offset_free_hash (ud->hashes->offset_free_hash, dest);
1637 ud->hashes->offset_rich_hash = summarize_offset_rich_hash (ud->hashes->offset_rich_hash, dest);
1639 // We return FALSE, so we're continuing walking
1640 // And we increment the pointer because we're done with this cell in the array
1641 ud->num_frames++;
1642 return FALSE;
1645 static gboolean
1646 summarize_frame_managed_walk (MonoMethod *method, gpointer ip, size_t frame_native_offset, gboolean managed, gpointer user_data)
1648 int il_offset = -1;
1650 if (managed && method) {
1651 MonoDebugSourceLocation *location = mono_debug_lookup_source_location (method, frame_native_offset, mono_domain_get ());
1652 if (location) {
1653 il_offset = location->il_offset;
1654 mono_debug_free_source_location (location);
1658 intptr_t portable_ip = 0;
1659 gint32 offset = 0;
1660 mono_get_portable_ip ((intptr_t) ip, &portable_ip, &offset, NULL, NULL);
1662 return summarize_frame_internal (method, (gpointer) portable_ip, frame_native_offset, il_offset, managed, user_data);
1666 static gboolean
1667 summarize_frame (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
1669 // Don't record trampolines between managed frames
1670 if (frame->ji && frame->ji->is_trampoline)
1671 return TRUE;
1673 if (frame->ji && (frame->ji->is_trampoline || frame->ji->async))
1674 return FALSE; // Keep unwinding
1676 intptr_t ip = 0;
1677 gint32 offset = 0;
1678 mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &ip, &offset, NULL, NULL);
1679 // Don't need to handle return status "success" because this ip is stored below only, NULL is okay
1681 gboolean is_managed = (frame->type == FRAME_TYPE_MANAGED || frame->type == FRAME_TYPE_INTERP);
1682 MonoMethod *method = NULL;
1683 if (frame && frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE)
1684 method = jinfo_get_method (frame->ji);
1686 if (is_managed)
1687 method = jinfo_get_method (frame->ji);
1689 return summarize_frame_internal (method, (gpointer) ip, offset, frame->il_offset, is_managed, data);
1692 static void
1693 mono_summarize_exception (MonoException *exc, MonoThreadSummary *out)
1695 memset (out, 0, sizeof (MonoThreadSummary));
1697 MonoException *inner_exc = exc;
1698 int exc_index = 0;
1700 for (exc_index = 0; exc_index < MONO_MAX_SUMMARY_EXCEPTIONS; exc_index++) {
1701 if (inner_exc == NULL)
1702 break;
1704 // Set up state to walk this MonoException's stack
1705 MonoSummarizeUserData data;
1706 memset (&data, 0, sizeof (MonoSummarizeUserData));
1707 data.max_frames = MONO_MAX_SUMMARY_FRAMES;
1708 data.num_frames = 0;
1709 data.frames = out->exceptions [exc_index].managed_frames;
1711 // Accumulate all hashes from all exceptions in traveral order
1712 data.hashes = &out->hashes;
1714 mono_exception_walk_trace (inner_exc, summarize_frame_managed_walk, &data);
1716 // Save per-MonoException info
1717 out->exceptions [exc_index].managed_exc_type = inner_exc->object.vtable->klass;
1718 out->exceptions [exc_index].num_managed_frames = data.num_frames;
1720 // Continue to traverse nesting of exceptions
1721 inner_exc = (MonoException *) inner_exc->inner_ex;
1724 out->num_exceptions = exc_index;
1728 static void
1729 mono_summarize_managed_stack (MonoThreadSummary *out)
1731 MonoSummarizeUserData data;
1732 memset (&data, 0, sizeof (MonoSummarizeUserData));
1733 data.max_frames = MONO_MAX_SUMMARY_FRAMES;
1734 data.num_frames = 0;
1735 data.frames = out->managed_frames;
1736 data.hashes = &out->hashes;
1738 // FIXME: collect stack pointer for both and sort frames by SP
1739 // so people can see relative ordering of both managed and unmanaged frames.
1742 // Summarize managed stack
1744 mono_walk_stack_full (summarize_frame, out->ctx, out->domain, out->jit_tls, out->lmf, MONO_UNWIND_LOOKUP_IL_OFFSET, &data, TRUE);
1745 out->num_managed_frames = data.num_frames;
1747 if (data.error != NULL)
1748 out->error_msg = data.error;
1749 out->is_managed = (out->num_managed_frames != 0);
1752 // Always runs on the dumped thread
1753 static void
1754 mono_summarize_unmanaged_stack (MonoThreadSummary *out)
1756 MONO_ARCH_CONTEXT_DEF
1758 // Summarize unmanaged stack
1760 #ifdef HAVE_BACKTRACE_SYMBOLS
1761 intptr_t frame_ips [MONO_MAX_SUMMARY_FRAMES];
1763 out->num_unmanaged_frames = backtrace ((void **)frame_ips, MONO_MAX_SUMMARY_FRAMES);
1765 for (int i =0; i < out->num_unmanaged_frames; ++i) {
1766 intptr_t ip = frame_ips [i];
1767 MonoFrameSummary *frame = &out->unmanaged_frames [i];
1768 const char* module_buf = frame->unmanaged_data.module;
1769 int success = mono_get_portable_ip (ip, &frame->unmanaged_data.ip, &frame->unmanaged_data.offset, &module_buf, (char *) frame->str_descr);
1771 /* attempt to look up any managed method at that ip */
1772 /* TODO: Trampolines - follow examples from mono_print_method_from_ip() */
1774 MonoJitInfo *ji;
1775 MonoDomain *domain = mono_domain_get ();
1776 MonoDomain *target_domain;
1777 ji = mini_jit_info_table_find_ext (domain, (char *)ip, TRUE, &target_domain);
1778 if (ji) {
1779 frame->is_managed = TRUE;
1780 if (!ji->async && !ji->is_trampoline) {
1781 MonoMethod *method = jinfo_get_method (ji);
1782 fill_frame_managed_info (frame, method);
1783 #ifndef MONO_PRIVATE_CRASHES
1784 frame->managed_data.name = method->name;
1785 #endif
1789 if (!success && !ji) {
1790 frame->unmanaged_data.ip = ip;
1791 continue;
1794 if (out->unmanaged_frames [i].str_descr [0] != '\0')
1795 out->unmanaged_frames [i].unmanaged_data.has_name = TRUE;
1797 out->hashes.offset_free_hash = summarize_offset_free_hash (out->hashes.offset_free_hash, frame);
1798 out->hashes.offset_rich_hash = summarize_offset_rich_hash (out->hashes.offset_rich_hash, frame);
1800 #endif
1802 out->lmf = mono_get_lmf ();
1804 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
1805 out->info_addr = (intptr_t) thread;
1806 out->jit_tls = thread->jit_data;
1807 out->domain = mono_domain_get ();
1809 if (!out->ctx) {
1810 out->ctx = &out->ctx_mem;
1811 mono_arch_flush_register_windows ();
1812 MONO_INIT_CONTEXT_FROM_FUNC (out->ctx, mono_summarize_unmanaged_stack);
1815 return;
1817 #endif
1820 MonoBoolean
1821 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
1822 MonoReflectionMethod **method,
1823 gint32 *iloffset, gint32 *native_offset,
1824 MonoString **file, gint32 *line, gint32 *column)
1826 ERROR_DECL (error);
1827 MonoDomain *domain = mono_domain_get ();
1828 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
1829 MonoLMF *lmf = mono_get_lmf ();
1830 MonoJitInfo *ji = NULL;
1831 MonoContext ctx, new_ctx;
1832 MonoDebugSourceLocation *location;
1833 MonoMethod *jmethod = NULL, *actual_method;
1834 StackFrameInfo frame;
1835 gboolean res;
1836 Unwinder unwinder;
1837 int il_offset = -1;
1839 MONO_ARCH_CONTEXT_DEF;
1841 g_assert (skip >= 0);
1843 if (mono_llvm_only) {
1844 GSList *l, *ips;
1845 MonoDomain *frame_domain;
1846 guint8 *frame_ip = NULL;
1848 /* FIXME: Generalize this code with an interface which returns an array of StackFrame structures */
1849 jmethod = NULL;
1850 ips = get_unwind_backtrace ();
1851 for (l = ips; l && skip >= 0; l = l->next) {
1852 guint8 *ip = (guint8*)l->data;
1854 frame_ip = ip;
1856 ji = mini_jit_info_table_find (mono_domain_get (), ip, &frame_domain);
1857 if (!ji || ji->is_trampoline)
1858 continue;
1860 /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
1861 jmethod = jinfo_get_method (ji);
1862 if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
1863 continue;
1864 skip--;
1866 g_slist_free (ips);
1867 if (!jmethod || !l)
1868 return FALSE;
1869 /* No way to resolve generic instances */
1870 actual_method = jmethod;
1871 *native_offset = frame_ip - (guint8*)ji->code_start;
1872 } else {
1873 mono_arch_flush_register_windows ();
1874 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
1876 unwinder_init (&unwinder);
1878 new_ctx = ctx;
1879 do {
1880 ctx = new_ctx;
1881 res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
1882 if (!res)
1883 return FALSE;
1884 switch (frame.type) {
1885 case FRAME_TYPE_MANAGED_TO_NATIVE:
1886 case FRAME_TYPE_DEBUGGER_INVOKE:
1887 case FRAME_TYPE_TRAMPOLINE:
1888 case FRAME_TYPE_INTERP_TO_MANAGED:
1889 case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX:
1890 continue;
1891 case FRAME_TYPE_INTERP:
1892 case FRAME_TYPE_MANAGED:
1893 ji = frame.ji;
1894 *native_offset = frame.native_offset;
1896 /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
1897 jmethod = jinfo_get_method (ji);
1898 if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
1899 continue;
1900 skip--;
1901 break;
1902 default:
1903 g_assert_not_reached ();
1905 } while (skip >= 0);
1907 if (frame.type == FRAME_TYPE_INTERP) {
1908 jmethod = frame.method;
1909 actual_method = frame.actual_method;
1910 } else {
1911 actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ctx));
1915 MonoReflectionMethod *rm = mono_method_get_object_checked (domain, actual_method, NULL, error);
1916 if (!is_ok (error)) {
1917 mono_error_set_pending_exception (error);
1918 return FALSE;
1920 mono_gc_wbarrier_generic_store_internal (method, (MonoObject*) rm);
1922 if (il_offset != -1) {
1923 location = mono_debug_lookup_source_location_by_il (jmethod, il_offset, domain);
1924 } else {
1925 location = mono_debug_lookup_source_location (jmethod, *native_offset, domain);
1927 if (location)
1928 *iloffset = location->il_offset;
1929 else
1930 *iloffset = 0;
1932 if (need_file_info) {
1933 if (location) {
1934 MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
1935 if (!is_ok (error)) {
1936 mono_error_set_pending_exception (error);
1937 return FALSE;
1939 mono_gc_wbarrier_generic_store_internal (file, (MonoObject*)filename);
1940 *line = location->row;
1941 *column = location->column;
1942 } else {
1943 *file = NULL;
1944 *line = *column = 0;
1948 mono_debug_free_source_location (location);
1950 return TRUE;
1953 static MonoClass*
1954 get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContext *ctx)
1956 ERROR_DECL (error);
1957 MonoClass *catch_class = ei->data.catch_class;
1958 MonoType *inflated_type;
1959 MonoGenericContext context;
1961 /*MonoJitExceptionInfo::data is an union used by filter and finally clauses too.*/
1962 if (!catch_class || ei->flags != MONO_EXCEPTION_CLAUSE_NONE)
1963 return NULL;
1965 if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this)
1966 return catch_class;
1967 context = mono_get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx));
1969 /* FIXME: we shouldn't inflate but instead put the
1970 type in the rgctx and fetch it from there. It
1971 might be a good idea to do this lazily, i.e. only
1972 when the exception is actually thrown, so as not to
1973 waste space for exception clauses which might never
1974 be encountered. */
1975 inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (catch_class), &context, error);
1976 mono_error_assert_ok (error); /* FIXME don't swallow the error */
1978 catch_class = mono_class_from_mono_type_internal (inflated_type);
1979 mono_metadata_free_type (inflated_type);
1981 return catch_class;
1985 * mini_jit_info_table_find_ext:
1987 * Same as mono_jit_info_table_find, but search all the domains of the current thread
1988 * if ADDR is not found in DOMAIN. The domain where the method was found is stored into
1989 * OUT_DOMAIN if it is not NULL.
1991 MonoJitInfo*
1992 mini_jit_info_table_find_ext (MonoDomain *domain, gpointer addr, gboolean allow_trampolines, MonoDomain **out_domain)
1994 MonoJitInfo *ji;
1995 MonoInternalThread *t = mono_thread_internal_current ();
1996 gpointer *refs;
1998 if (out_domain)
1999 *out_domain = NULL;
2001 ji = mono_jit_info_table_find_internal (domain, addr, TRUE, allow_trampolines);
2002 if (ji) {
2003 if (out_domain)
2004 *out_domain = domain;
2005 return ji;
2008 /* maybe it is shared code, so we also search in the root domain */
2009 if (domain != mono_get_root_domain ()) {
2010 ji = mono_jit_info_table_find_internal (mono_get_root_domain (), addr, TRUE, allow_trampolines);
2011 if (ji) {
2012 if (out_domain)
2013 *out_domain = mono_get_root_domain ();
2014 return ji;
2018 if (!t)
2019 return NULL;
2021 refs = (gpointer *)((t->appdomain_refs) ? *(gpointer *) t->appdomain_refs : NULL);
2022 for (; refs && *refs; refs++) {
2023 if (*refs != domain && *refs != mono_get_root_domain ()) {
2024 ji = mono_jit_info_table_find_internal ((MonoDomain*) *refs, addr, TRUE, allow_trampolines);
2025 if (ji) {
2026 if (out_domain)
2027 *out_domain = (MonoDomain*) *refs;
2028 return ji;
2033 return NULL;
2036 MonoJitInfo*
2037 mini_jit_info_table_find (MonoDomain *domain, gpointer addr, MonoDomain **out_domain)
2039 return mini_jit_info_table_find_ext (domain, addr, FALSE, out_domain);
2042 /* Class lazy loading functions */
2043 static GENERATE_GET_CLASS_WITH_CACHE (runtime_compat_attr, "System.Runtime.CompilerServices", "RuntimeCompatibilityAttribute")
2046 * wrap_non_exception_throws:
2048 * Determine whenever M's assembly has a RuntimeCompatibilityAttribute with the
2049 * WrapNonExceptionThrows flag set.
2051 static gboolean
2052 wrap_non_exception_throws (MonoMethod *m)
2054 ERROR_DECL (error);
2055 MonoAssembly *ass = m_class_get_image (m->klass)->assembly;
2056 MonoCustomAttrInfo* attrs;
2057 MonoClass *klass;
2058 int i;
2059 gboolean val = FALSE;
2061 if (m->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
2062 MonoDynamicMethod *dm = (MonoDynamicMethod *)m;
2063 if (dm->assembly)
2064 ass = dm->assembly;
2066 g_assert (ass);
2067 if (ass->wrap_non_exception_throws_inited)
2068 return ass->wrap_non_exception_throws;
2070 klass = mono_class_get_runtime_compat_attr_class ();
2072 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, error);
2073 mono_error_cleanup (error); /* FIXME don't swallow the error */
2074 if (attrs) {
2075 for (i = 0; i < attrs->num_attrs; ++i) {
2076 MonoCustomAttrEntry *attr = &attrs->attrs [i];
2077 const gchar *p;
2078 int num_named, named_type, name_len;
2079 char *name;
2081 if (!attr->ctor || attr->ctor->klass != klass)
2082 continue;
2083 /* Decode the RuntimeCompatibilityAttribute. See reflection.c */
2084 p = (const char*)attr->data;
2085 g_assert (read16 (p) == 0x0001);
2086 p += 2;
2087 num_named = read16 (p);
2088 if (num_named != 1)
2089 continue;
2090 p += 2;
2091 named_type = *p;
2092 p ++;
2093 /* data_type = *p; */
2094 p ++;
2095 /* Property */
2096 if (named_type != 0x54)
2097 continue;
2098 name_len = mono_metadata_decode_blob_size (p, &p);
2099 name = (char *)g_malloc (name_len + 1);
2100 memcpy (name, p, name_len);
2101 name [name_len] = 0;
2102 p += name_len;
2103 g_assert (!strcmp (name, "WrapNonExceptionThrows"));
2104 g_free (name);
2105 /* The value is a BOOLEAN */
2106 val = *p;
2108 mono_custom_attrs_free (attrs);
2111 ass->wrap_non_exception_throws = val;
2112 mono_memory_barrier ();
2113 ass->wrap_non_exception_throws_inited = TRUE;
2115 return val;
2118 #define MAX_UNMANAGED_BACKTRACE 128
2119 static MonoArray*
2120 build_native_trace (MonoError *error)
2122 error_init (error);
2123 /* This puppy only makes sense on mobile, IOW, ARM. */
2124 #if defined (HAVE_BACKTRACE_SYMBOLS) && defined (TARGET_ARM)
2125 MonoArray *res;
2126 void *native_trace [MAX_UNMANAGED_BACKTRACE];
2127 int size = -1;
2128 MONO_ENTER_GC_SAFE;
2129 size = backtrace (native_trace, MAX_UNMANAGED_BACKTRACE);
2130 MONO_EXIT_GC_SAFE;
2131 int i;
2133 if (!size)
2134 return NULL;
2135 res = mono_array_new_checked (mono_domain_get (), mono_defaults.int_class, size, error);
2136 return_val_if_nok (error, NULL);
2138 for (i = 0; i < size; i++)
2139 mono_array_set_internal (res, gpointer, i, native_trace [i]);
2140 return res;
2141 #else
2142 return NULL;
2143 #endif
2146 static void
2147 remove_wrappers_from_trace (GList **trace_ips_p)
2149 GList *trace_ips = *trace_ips_p;
2150 GList *p = trace_ips;
2152 /* jit info, generic info, ip */
2153 while (p) {
2154 MonoJitInfo *jinfo = (MonoJitInfo*) p->data;
2155 GList *next_p = p->next->next->next;
2156 /* FIXME Maybe remove more wrapper types */
2157 if (jinfo->d.method->wrapper_type == MONO_WRAPPER_OTHER) {
2158 trace_ips = g_list_delete_link (trace_ips, p->next->next);
2159 trace_ips = g_list_delete_link (trace_ips, p->next);
2160 trace_ips = g_list_delete_link (trace_ips, p);
2162 p = next_p;
2165 *trace_ips_p = trace_ips;
2168 /* This can be called more than once on a MonoException. */
2169 static void
2170 setup_stack_trace (MonoException *mono_ex, GSList **dynamic_methods, GList *trace_ips, gboolean remove_wrappers)
2172 if (mono_ex) {
2173 GList *trace_ips_copy = g_list_copy (trace_ips);
2174 if (remove_wrappers)
2175 remove_wrappers_from_trace (&trace_ips_copy);
2176 trace_ips_copy = g_list_reverse (trace_ips_copy);
2177 ERROR_DECL (error);
2178 MonoArray *ips_arr = mono_glist_to_array (trace_ips_copy, mono_defaults.int_class, error);
2179 mono_error_assert_ok (error);
2180 MONO_OBJECT_SETREF_INTERNAL (mono_ex, trace_ips, ips_arr);
2181 MONO_OBJECT_SETREF_INTERNAL (mono_ex, native_trace_ips, build_native_trace (error));
2182 mono_error_assert_ok (error);
2183 if (*dynamic_methods) {
2184 /* These methods could go away anytime, so save a reference to them in the exception object */
2185 GSList *l;
2186 MonoMList *list = (MonoMList*)mono_ex->dynamic_methods;
2188 for (l = *dynamic_methods; l; l = l->next) {
2189 MonoGCHandle dis_link;
2190 MonoDomain *domain = mono_domain_get ();
2192 if (domain->method_to_dyn_method) {
2193 mono_domain_lock (domain);
2194 dis_link = (MonoGCHandle)g_hash_table_lookup (domain->method_to_dyn_method, l->data);
2195 mono_domain_unlock (domain);
2196 if (dis_link) {
2197 MonoObject *o = mono_gchandle_get_target_internal (dis_link);
2198 if (o) {
2199 list = mono_mlist_prepend_checked (list, o, error);
2200 mono_error_assert_ok (error);
2206 MONO_OBJECT_SETREF_INTERNAL (mono_ex, dynamic_methods, list);
2208 g_slist_free (*dynamic_methods);
2209 *dynamic_methods = NULL;
2212 g_list_free (trace_ips_copy);
2216 typedef enum {
2217 MONO_FIRST_PASS_UNHANDLED,
2218 MONO_FIRST_PASS_CALLBACK_TO_NATIVE,
2219 MONO_FIRST_PASS_HANDLED,
2220 } MonoFirstPassResult;
2223 * handle_exception_first_pass:
2225 * The first pass of exception handling. Unwind the stack until a catch
2226 * clause which can catch OBJ is found. Store the index of the filter clause
2227 * which caught the exception into OUT_FILTER_IDX. Return
2228 * \c MONO_FIRST_PASS_HANDLED if the exception is caught,
2229 * \c MONO_FIRST_PASS_UNHANDLED otherwise, unless there is a native-to-managed
2230 * wrapper and an exception handling callback is installed (in which case
2231 * return \c MONO_FIRST_PASS_CALLBACK_TO_NATIVE).
2233 static MonoFirstPassResult
2234 handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filter_idx, MonoJitInfo **out_ji, MonoJitInfo **out_prev_ji, MonoObject *non_exception, StackFrameInfo *catch_frame, gboolean *last_mono_wrapper_runtime_invoke)
2236 ERROR_DECL (error);
2237 MonoDomain *domain = mono_domain_get ();
2238 MonoJitInfo *ji = NULL;
2239 static int (*call_filter) (MonoContext *, gpointer) = NULL;
2240 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
2241 MonoLMF *lmf = mono_get_lmf ();
2242 GList *trace_ips = NULL;
2243 GSList *dynamic_methods = NULL;
2244 MonoException *mono_ex;
2245 gboolean stack_overflow = FALSE;
2246 MonoContext initial_ctx;
2247 MonoMethod *method;
2248 int frame_count = 0;
2249 gint32 filter_idx;
2250 int i;
2251 MonoObject *ex_obj;
2252 Unwinder unwinder;
2253 gboolean in_interp;
2255 MonoFirstPassResult result = MONO_FIRST_PASS_UNHANDLED;
2257 g_assert (ctx != NULL);
2258 *last_mono_wrapper_runtime_invoke = TRUE;
2259 if (obj == (MonoObject *)domain->stack_overflow_ex)
2260 stack_overflow = TRUE;
2262 mono_ex = (MonoException*)obj;
2263 MonoArray *initial_trace_ips = mono_ex->trace_ips;
2264 if (initial_trace_ips) {
2265 int len = mono_array_length_internal (initial_trace_ips) / TRACE_IP_ENTRY_SIZE;
2267 // If we catch in managed/non-wrapper, we don't save the catching frame
2268 if (!mono_ex->caught_in_unmanaged)
2269 len -= 1;
2271 for (i = 0; i < len; i++) {
2272 for (int j = 0; j < TRACE_IP_ENTRY_SIZE; ++j) {
2273 gpointer p = mono_array_get_internal (initial_trace_ips, gpointer, (i * TRACE_IP_ENTRY_SIZE) + j);
2274 trace_ips = g_list_prepend (trace_ips, p);
2279 // Reset the state because we're making it be caught somewhere
2280 if (mono_ex->caught_in_unmanaged)
2281 MONO_OBJECT_SETREF_INTERNAL (mono_ex, caught_in_unmanaged, 0);
2283 if (!mono_object_isinst_checked (obj, mono_defaults.exception_class, error)) {
2284 mono_error_assert_ok (error);
2285 mono_ex = NULL;
2288 if (!call_filter)
2289 call_filter = (int (*) (MonoContext *, void *))mono_get_call_filter ();
2291 g_assert (jit_tls->end_of_stack);
2292 g_assert (jit_tls->abort_func);
2294 if (out_filter_idx)
2295 *out_filter_idx = -1;
2296 if (out_ji)
2297 *out_ji = NULL;
2298 if (out_prev_ji)
2299 *out_prev_ji = NULL;
2300 filter_idx = 0;
2301 initial_ctx = *ctx;
2303 unwinder_init (&unwinder);
2305 while (1) {
2306 MonoContext new_ctx;
2307 guint32 free_stack;
2308 int clause_index_start = 0;
2309 gboolean unwind_res = TRUE;
2311 StackFrameInfo frame;
2313 if (out_prev_ji)
2314 *out_prev_ji = ji;
2316 unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
2317 if (!unwind_res) {
2318 setup_stack_trace (mono_ex, &dynamic_methods, trace_ips, FALSE);
2319 g_list_free (trace_ips);
2320 return result;
2323 switch (frame.type) {
2324 case FRAME_TYPE_DEBUGGER_INVOKE:
2325 case FRAME_TYPE_MANAGED_TO_NATIVE:
2326 case FRAME_TYPE_TRAMPOLINE:
2327 case FRAME_TYPE_INTERP_TO_MANAGED:
2328 case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX:
2329 *ctx = new_ctx;
2330 continue;
2331 case FRAME_TYPE_INTERP:
2332 case FRAME_TYPE_MANAGED:
2333 break;
2334 default:
2335 g_assert_not_reached ();
2336 break;
2339 in_interp = frame.type == FRAME_TYPE_INTERP;
2340 ji = frame.ji;
2342 gpointer ip;
2343 if (in_interp)
2344 ip = (guint8*)ji->code_start + frame.native_offset;
2345 else
2346 ip = MONO_CONTEXT_GET_IP (ctx);
2348 frame_count ++;
2349 method = jinfo_get_method (ji);
2350 //printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
2352 if (mini_debug_options.reverse_pinvoke_exceptions && method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
2353 g_error ("A native frame was found while unwinding the stack after an exception.\n"
2354 "The native frame called the managed method:\n%s\n",
2355 mono_method_full_name (method, TRUE));
2358 if (method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
2359 // avoid giant stack traces during a stack overflow
2360 if (frame_count < 1000) {
2361 trace_ips = g_list_prepend (trace_ips, ip);
2362 trace_ips = g_list_prepend (trace_ips, get_generic_info_from_stack_frame (ji, ctx));
2363 trace_ips = g_list_prepend (trace_ips, ji);
2367 if (method->dynamic)
2368 dynamic_methods = g_slist_prepend (dynamic_methods, method);
2370 if (stack_overflow) {
2371 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
2372 } else {
2373 free_stack = 0xffffff;
2376 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) {
2377 result = MONO_FIRST_PASS_CALLBACK_TO_NATIVE;
2381 for (i = clause_index_start; i < ji->num_clauses; i++) {
2382 MonoJitExceptionInfo *ei = &ji->clauses [i];
2383 gboolean filtered = FALSE;
2386 * During stack overflow, wait till the unwinding frees some stack
2387 * space before running handlers/finalizers.
2389 if (free_stack <= (64 * 1024))
2390 continue;
2392 if (is_address_protected (ji, ei, ip)) {
2393 /* catch block */
2394 MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
2397 * Have to unwrap RuntimeWrappedExceptions if the
2398 * method's assembly doesn't have a RuntimeCompatibilityAttribute.
2400 if (non_exception && !wrap_non_exception_throws (method))
2401 ex_obj = non_exception;
2402 else
2403 ex_obj = obj;
2405 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
2406 setup_stack_trace (mono_ex, &dynamic_methods, trace_ips, FALSE);
2408 #ifndef DISABLE_PERFCOUNTERS
2409 mono_atomic_inc_i32 (&mono_perfcounters->exceptions_filters);
2410 #endif
2412 if (!ji->is_interp) {
2413 #ifndef MONO_CROSS_COMPILE
2414 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
2415 if (ji->from_llvm)
2416 MONO_CONTEXT_SET_LLVM_EXC_REG (ctx, ex_obj);
2417 else
2418 /* Can't pass the ex object in a register yet to filter clauses, because call_filter () might not support it */
2419 *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = ex_obj;
2420 #else
2421 g_assert (!ji->from_llvm);
2422 /* store the exception object in bp + ei->exvar_offset */
2423 *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = ex_obj;
2424 #endif
2425 #endif
2427 #ifdef MONO_CONTEXT_SET_LLVM_EH_SELECTOR_REG
2429 * Pass the original il clause index to the landing pad so it can
2430 * branch to the landing pad associated with the il clause.
2431 * This is needed because llvm compiled code assumes that the EH
2432 * code always branches to the innermost landing pad.
2434 if (ji->from_llvm)
2435 MONO_CONTEXT_SET_LLVM_EH_SELECTOR_REG (ctx, ei->clause_index);
2436 #endif
2439 mini_get_dbg_callbacks ()->begin_exception_filter (mono_ex, ctx, &initial_ctx);
2441 if (G_UNLIKELY (mono_profiler_clauses_enabled ())) {
2442 jit_tls->orig_ex_ctx_set = TRUE;
2443 MONO_PROFILER_RAISE (exception_clause, (method, i, (MonoExceptionEnum)ei->flags, ex_obj));
2444 jit_tls->orig_ex_ctx_set = FALSE;
2447 if (ji->is_interp) {
2448 /* The filter ends where the exception handler starts */
2449 filtered = mini_get_interp_callbacks ()->run_filter (&frame, (MonoException*)ex_obj, i, ei->data.filter, ei->handler_start);
2450 } else {
2451 filtered = call_filter (ctx, ei->data.filter);
2453 mini_get_dbg_callbacks ()->end_exception_filter (mono_ex, ctx, &initial_ctx);
2454 if (filtered && out_filter_idx)
2455 *out_filter_idx = filter_idx;
2456 if (out_ji)
2457 *out_ji = ji;
2458 filter_idx ++;
2460 if (filtered) {
2461 g_list_free (trace_ips);
2462 /* mono_debugger_agent_handle_exception () needs this */
2463 mini_set_abort_threshold (&frame);
2464 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
2465 frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start;
2466 *catch_frame = frame;
2467 result = MONO_FIRST_PASS_HANDLED;
2468 return result;
2472 ERROR_DECL (isinst_error); // FIXME not used https://github.com/mono/mono/pull/3055/files#r240548187
2473 if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst_checked (ex_obj, catch_class, error)) {
2474 /* runtime invokes catch even unhandled exceptions */
2475 setup_stack_trace (mono_ex, &dynamic_methods, trace_ips, method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE);
2476 g_list_free (trace_ips);
2478 if (out_ji)
2479 *out_ji = ji;
2481 /* mono_debugger_agent_handle_exception () needs this */
2482 if (!in_interp)
2483 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
2484 frame.native_offset = (char*)ei->handler_start - (char*)ji->code_start;
2485 *catch_frame = frame;
2486 result = MONO_FIRST_PASS_HANDLED;
2487 if (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) {
2488 //try to find threadpool_perform_wait_callback_method
2489 unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &new_ctx, &new_ctx, NULL, &lmf, NULL, &frame);
2490 while (unwind_res) {
2491 if (frame.ji && !frame.ji->is_trampoline && jinfo_get_method (frame.ji)->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) {
2492 *last_mono_wrapper_runtime_invoke = FALSE;
2493 break;
2495 unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &new_ctx, &new_ctx, NULL, &lmf, NULL, &frame);
2498 return result;
2500 mono_error_cleanup (isinst_error);
2504 *ctx = new_ctx;
2507 g_assert_not_reached ();
2511 * We implement delaying of aborts when in finally blocks by reusing the
2512 * abort protected block mechanism. The problem is that when throwing an
2513 * exception in a finally block we don't get to exit the protected block.
2514 * We exit it here when unwinding. Given that the order of the clauses
2515 * in the jit info is from inner clauses to the outer clauses, when we
2516 * want to exit the finally blocks inner to the clause that handles the
2517 * exception, we need to search up to its index.
2519 * FIXME We should do this inside interp, but with mixed mode we can
2520 * resume directly, without giving control back to the interp.
2522 static void
2523 interp_exit_finally_abort_blocks (MonoJitInfo *ji, int start_clause, int end_clause, gpointer ip)
2525 int i;
2526 for (i = start_clause; i < end_clause; i++) {
2527 MonoJitExceptionInfo *ei = &ji->clauses [i];
2528 if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY &&
2529 ip >= ei->handler_start &&
2530 ip < ei->data.handler_end) {
2531 mono_threads_end_abort_protected_block ();
2536 static MonoException *
2537 mono_get_exception_runtime_wrapped_checked (MonoObject *wrapped_exception_raw, MonoError *error)
2539 HANDLE_FUNCTION_ENTER ();
2540 MONO_HANDLE_DCL (MonoObject, wrapped_exception);
2541 MonoExceptionHandle ret = mono_get_exception_runtime_wrapped_handle (wrapped_exception, error);
2542 HANDLE_FUNCTION_RETURN_OBJ (ret);
2546 * mono_handle_exception_internal:
2547 * \param ctx saved processor state
2548 * \param obj the exception object
2549 * \param resume whenever to resume unwinding based on the state in \c MonoJitTlsData.
2551 static gboolean
2552 mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resume, MonoJitInfo **out_ji)
2554 ERROR_DECL (error);
2555 MonoDomain *domain = mono_domain_get ();
2556 MonoJitInfo *ji, *prev_ji;
2557 static int (*call_filter) (MonoContext *, gpointer) = NULL;
2558 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
2559 MonoLMF *lmf = mono_get_lmf ();
2560 MonoException *mono_ex;
2561 gboolean stack_overflow = FALSE;
2562 MonoContext initial_ctx;
2563 MonoMethod *method;
2564 int frame_count = 0;
2565 gint32 filter_idx, first_filter_idx = 0;
2566 int i;
2567 MonoObject *ex_obj = NULL;
2568 MonoObject *non_exception = NULL;
2569 Unwinder unwinder;
2570 gboolean in_interp;
2571 gboolean is_caught_unmanaged = FALSE;
2572 gboolean last_mono_wrapper_runtime_invoke = TRUE;
2574 g_assert (ctx != NULL);
2575 if (!obj) {
2576 MonoException *ex = mono_get_exception_null_reference ();
2577 MonoString *msg = mono_string_new_checked (domain, "Object reference not set to an instance of an object", error);
2578 mono_error_assert_ok (error);
2579 MONO_OBJECT_SETREF_INTERNAL (ex, message, msg);
2580 obj = (MonoObject *)ex;
2584 * Allocate a new exception object instead of the preconstructed ones.
2586 if (obj == (MonoObject *)domain->stack_overflow_ex) {
2588 * It is not a good idea to try and put even more pressure on the little stack available.
2589 * obj = mono_get_exception_stack_overflow ();
2591 stack_overflow = TRUE;
2593 else if (obj == (MonoObject *)domain->null_reference_ex) {
2594 obj = (MonoObject *)mono_get_exception_null_reference ();
2597 if (!mono_object_isinst_checked (obj, mono_defaults.exception_class, error)) {
2598 mono_error_assert_ok (error);
2599 non_exception = obj;
2600 obj = (MonoObject *)mono_get_exception_runtime_wrapped_checked (obj, error);
2601 mono_error_assert_ok (error);
2604 mono_ex = (MonoException*)obj;
2606 if (mini_debug_options.suspend_on_exception) {
2607 mono_runtime_printf_err ("Exception thrown, suspending...");
2608 while (1)
2612 if (mono_ex->caught_in_unmanaged)
2613 is_caught_unmanaged = TRUE;
2616 if (mono_object_isinst_checked (obj, mono_defaults.exception_class, error)) {
2617 mono_ex = (MonoException*)obj;
2618 } else {
2619 mono_error_assert_ok (error);
2620 mono_ex = NULL;
2623 if (mono_ex && jit_tls->class_cast_from) {
2624 if (!strcmp (m_class_get_name (mono_ex->object.vtable->klass), "InvalidCastException")) {
2625 char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
2626 char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
2627 char *msg = g_strdup_printf ("Unable to cast object of type '%s' to type '%s'.", from_name, to_name);
2628 mono_ex->message = mono_string_new_checked (domain, msg, error);
2629 g_free (from_name);
2630 g_free (to_name);
2631 if (!is_ok (error)) {
2632 mono_runtime_printf_err ("Error creating class cast exception message '%s'\n", msg);
2633 mono_error_assert_ok (error);
2635 g_free (msg);
2637 if (!strcmp (m_class_get_name (mono_ex->object.vtable->klass), "ArrayTypeMismatchException")) {
2638 char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
2639 char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
2640 char *msg = g_strdup_printf ("Source array of type '%s' cannot be cast to destination array type '%s'.", from_name, to_name);
2641 mono_ex->message = mono_string_new_checked (domain, msg, error);
2642 g_free (from_name);
2643 g_free (to_name);
2644 if (!is_ok (error)) {
2645 mono_runtime_printf_err ("Error creating array type mismatch exception message '%s'\n", msg);
2646 mono_error_assert_ok (error);
2648 g_free (msg);
2652 if (!call_filter)
2653 call_filter = (int (*)(MonoContext *, void*))mono_get_call_filter ();
2655 g_assert (jit_tls->end_of_stack);
2656 g_assert (jit_tls->abort_func);
2659 * We set orig_ex_ctx_set to TRUE/FALSE around profiler calls to make sure it doesn't
2660 * end up being TRUE on any code path.
2662 memcpy (&jit_tls->orig_ex_ctx, ctx, sizeof (MonoContext));
2664 if (!resume) {
2665 MonoContext ctx_cp = *ctx;
2666 if (mono_trace_is_enabled ()) {
2667 ERROR_DECL (error);
2668 MonoMethod *system_exception_get_message = mono_class_get_method_from_name_checked (mono_defaults.exception_class, "get_Message", 0, 0, error);
2669 mono_error_cleanup (error);
2670 error_init (error);
2671 MonoMethod *get_message = system_exception_get_message == NULL ? NULL : mono_object_get_virtual_method_internal (obj, system_exception_get_message);
2672 MonoObject *message;
2673 const char *type_name = m_class_get_name (mono_object_class (mono_ex));
2674 char *msg = NULL;
2675 if (get_message == NULL) {
2676 message = NULL;
2677 } else if (!strcmp (type_name, "OutOfMemoryException") || !strcmp (type_name, "StackOverflowException")) {
2678 message = NULL;
2679 msg = g_strdup_printf ("(No exception message for: %s)\n", type_name);
2680 } else {
2681 MonoObject *exc = NULL;
2682 message = mono_runtime_try_invoke (get_message, obj, NULL, &exc, error);
2683 g_assert (exc == NULL);
2684 mono_error_assert_ok (error);
2686 if (msg == NULL) {
2687 if (message) {
2688 msg = mono_string_to_utf8_checked_internal ((MonoString *) message, error);
2689 if (!is_ok (error)) {
2690 mono_error_cleanup (error);
2691 msg = g_strdup ("(error while display System.Exception.Message property)");
2693 } else {
2694 msg = g_strdup ("(System.Exception.Message property not available)");
2697 g_print ("[%p:] EXCEPTION handling: %s.%s: %s\n", (void*)(gsize)mono_native_thread_id_get (), m_class_get_name_space (mono_object_class (obj)), m_class_get_name (mono_object_class (obj)), msg);
2698 g_free (msg);
2699 if (mono_ex && mono_trace_eval_exception (mono_object_class (mono_ex)))
2700 mono_print_thread_dump_from_ctx (ctx);
2702 jit_tls->orig_ex_ctx_set = TRUE;
2703 MONO_PROFILER_RAISE (exception_throw, (obj));
2704 jit_tls->orig_ex_ctx_set = FALSE;
2706 #ifdef ENABLE_NETCORE
2707 mono_first_chance_exception_internal (obj);
2708 #endif
2710 StackFrameInfo catch_frame;
2711 MonoFirstPassResult res;
2712 res = handle_exception_first_pass (&ctx_cp, obj, &first_filter_idx, &ji, &prev_ji, non_exception, &catch_frame, &last_mono_wrapper_runtime_invoke);
2714 if (res == MONO_FIRST_PASS_UNHANDLED) {
2715 if (mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP) {
2716 /* Reached the top interpreted frames, but there might be native frames above us */
2717 throw_exception (obj, TRUE);
2718 g_assert_not_reached ();
2720 if (mini_debug_options.break_on_exc)
2721 G_BREAKPOINT ();
2722 mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL);
2724 // FIXME: This runs managed code so it might cause another stack overflow when
2725 // we are handling a stack overflow
2726 mini_set_abort_threshold (&catch_frame);
2727 mono_unhandled_exception_internal (obj);
2728 } else {
2729 gboolean unhandled = FALSE;
2732 * The exceptions caught by the mono_runtime_invoke_checked () calls
2733 * in the threadpool needs to be treated as unhandled (#669836).
2735 * FIXME: The check below is hackish, but its hard to distinguish
2736 * these runtime invoke calls from others in the runtime.
2738 #ifndef ENABLE_NETCORE
2739 if (ji && jinfo_get_method (ji)->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) {
2740 if (prev_ji && jinfo_get_method (prev_ji) == mono_defaults.threadpool_perform_wait_callback_method)
2741 unhandled = TRUE;
2743 #endif
2745 if (unhandled)
2746 mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL);
2747 else if (!ji || (jinfo_get_method (ji)->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)) {
2748 if (last_mono_wrapper_runtime_invoke && !mono_thread_internal_current ()->threadpool_thread) {
2749 mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, NULL, NULL);
2750 if (mini_get_debug_options ()->top_runtime_invoke_unhandled) {
2751 mini_set_abort_threshold (&catch_frame);
2752 mono_unhandled_exception_internal (obj);
2754 } else {
2755 mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame);
2758 else if (res != MONO_FIRST_PASS_CALLBACK_TO_NATIVE)
2759 if (!is_caught_unmanaged)
2760 mini_get_dbg_callbacks ()->handle_exception ((MonoException *)obj, ctx, &ctx_cp, &catch_frame);
2764 if (out_ji)
2765 *out_ji = NULL;
2766 filter_idx = 0;
2767 initial_ctx = *ctx;
2769 unwinder_init (&unwinder);
2771 while (1) {
2772 MonoContext new_ctx;
2773 guint32 free_stack;
2774 int clause_index_start = 0;
2775 gboolean unwind_res = TRUE;
2776 StackFrameInfo frame;
2777 gpointer ip;
2779 if (resume) {
2780 resume = FALSE;
2781 ji = jit_tls->resume_state.ji;
2782 new_ctx = jit_tls->resume_state.new_ctx;
2783 clause_index_start = jit_tls->resume_state.clause_index;
2784 lmf = jit_tls->resume_state.lmf;
2785 first_filter_idx = jit_tls->resume_state.first_filter_idx;
2786 filter_idx = jit_tls->resume_state.filter_idx;
2787 in_interp = FALSE;
2788 } else {
2789 unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
2790 if (!unwind_res) {
2791 *(mono_get_lmf_addr ()) = lmf;
2793 jit_tls->abort_func (obj);
2794 g_assert_not_reached ();
2796 switch (frame.type) {
2797 case FRAME_TYPE_DEBUGGER_INVOKE:
2798 case FRAME_TYPE_MANAGED_TO_NATIVE:
2799 case FRAME_TYPE_TRAMPOLINE:
2800 case FRAME_TYPE_INTERP_TO_MANAGED_WITH_CTX:
2801 *ctx = new_ctx;
2802 continue;
2803 case FRAME_TYPE_INTERP_TO_MANAGED:
2804 continue;
2805 case FRAME_TYPE_INTERP:
2806 case FRAME_TYPE_MANAGED:
2807 break;
2808 default:
2809 g_assert_not_reached ();
2810 break;
2812 in_interp = frame.type == FRAME_TYPE_INTERP;
2813 ji = frame.ji;
2816 if (in_interp)
2817 ip = (guint8*)ji->code_start + frame.native_offset;
2818 else
2819 ip = MONO_CONTEXT_GET_IP (ctx);
2821 method = jinfo_get_method (ji);
2822 frame_count ++;
2823 //printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
2825 if (stack_overflow) {
2826 free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
2827 } else {
2828 free_stack = 0xffffff;
2831 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED && ftnptr_eh_callback) {
2832 MonoGCHandle handle = mono_gchandle_new_internal (obj, FALSE);
2833 MONO_STACKDATA (stackptr);
2835 mono_threads_enter_gc_safe_region_unbalanced_internal (&stackptr);
2836 mono_set_lmf (lmf);
2837 ftnptr_eh_callback (handle);
2838 g_error ("Did not expect ftnptr_eh_callback to return.");
2841 for (i = clause_index_start; i < ji->num_clauses; i++) {
2842 MonoJitExceptionInfo *ei = &ji->clauses [i];
2843 gboolean filtered = FALSE;
2846 * During stack overflow, wait till the unwinding frees some stack
2847 * space before running handlers/finalizers.
2849 if (free_stack <= (64 * 1024))
2850 continue;
2852 if (is_address_protected (ji, ei, ip)) {
2853 /* catch block */
2854 MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
2857 * Have to unwrap RuntimeWrappedExceptions if the
2858 * method's assembly doesn't have a RuntimeCompatibilityAttribute.
2860 if (non_exception && !wrap_non_exception_throws (method))
2861 ex_obj = non_exception;
2862 else
2863 ex_obj = obj;
2865 if (((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER))) {
2866 #ifndef MONO_CROSS_COMPILE
2867 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
2868 MONO_CONTEXT_SET_LLVM_EXC_REG (ctx, ex_obj);
2869 #else
2870 g_assert (!ji->from_llvm);
2871 /* store the exception object in bp + ei->exvar_offset */
2872 *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = ex_obj;
2873 #endif
2874 #endif
2877 #ifdef MONO_CONTEXT_SET_LLVM_EH_SELECTOR_REG
2878 if (ji->from_llvm)
2879 MONO_CONTEXT_SET_LLVM_EH_SELECTOR_REG (ctx, ei->clause_index);
2880 #endif
2882 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
2884 * Filter clauses should only be run in the
2885 * first pass of exception handling.
2887 filtered = (filter_idx == first_filter_idx);
2888 filter_idx ++;
2891 error_init (error);
2892 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
2893 mono_object_isinst_checked (ex_obj, catch_class, error)) || filtered) {
2895 * This guards against the situation that we abort a thread that is executing a finally clause
2896 * that was called by the EH machinery. It won't have a guard trampoline installed, so we must
2897 * check for this situation here and resume interruption if we are below the guarded block.
2899 if (G_UNLIKELY (jit_tls->handler_block)) {
2900 gboolean is_outside = FALSE;
2901 gpointer prot_bp = MONO_CONTEXT_GET_BP (&jit_tls->handler_block_context);
2902 gpointer catch_bp = MONO_CONTEXT_GET_BP (ctx);
2903 //FIXME make this stack direction aware
2905 if (catch_bp > prot_bp) {
2906 is_outside = TRUE;
2907 } else if (catch_bp == prot_bp) {
2908 /* Can be either try { try { } catch {} } finally {} or try { try { } finally {} } catch {}
2909 * So we check if the catch handler_start is protected by the guarded handler protected region
2911 * Assumptions:
2912 * If there is an outstanding guarded_block return address, it means the current thread must be aborted.
2913 * This is the only way to reach out the guarded block as other cases are handled by the trampoline.
2914 * There aren't any further finally/fault handler blocks down the stack over this exception.
2915 * This must be ensured by the code that installs the guard trampoline.
2917 g_assert (ji == mini_jit_info_table_find (domain, (char *)MONO_CONTEXT_GET_IP (&jit_tls->handler_block_context), NULL));
2919 if (!is_address_protected (ji, jit_tls->handler_block, ei->handler_start)) {
2920 is_outside = TRUE;
2923 if (is_outside) {
2924 jit_tls->handler_block = NULL;
2925 mono_thread_resume_interruption (TRUE); /*We ignore the exception here, it will be raised later*/
2929 if (mono_trace_is_enabled () && mono_trace_eval (method))
2930 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (method, TRUE));
2933 * At this point, ei->flags can be either MONO_EXCEPTION_CLAUSE_NONE for a
2934 * a try-catch clause or MONO_EXCEPTION_CLAUSE_FILTER for a try-filter-catch
2935 * clause. Since we specifically want to indicate that we're executing the
2936 * catch portion of this EH clause, pass MONO_EXCEPTION_CLAUSE_NONE explicitly
2937 * instead of ei->flags.
2939 if (G_UNLIKELY (mono_profiler_clauses_enabled ())) {
2940 jit_tls->orig_ex_ctx_set = TRUE;
2941 MONO_PROFILER_RAISE (exception_clause, (method, i, MONO_EXCEPTION_CLAUSE_NONE, ex_obj));
2942 jit_tls->orig_ex_ctx_set = FALSE;
2945 mini_set_abort_threshold (&frame);
2947 if (in_interp) {
2948 interp_exit_finally_abort_blocks (ji, clause_index_start, i, ip);
2950 * ctx->pc points into the interpreter, after the call which transitioned to
2951 * JITted code. Store the unwind state into the
2952 * interpeter state, then resume, the interpreter will unwind itself until
2953 * it reaches the target frame and will continue execution from there.
2954 * The resuming is kinda hackish, from the native code standpoint, it looks
2955 * like the call which transitioned to JITted code has succeeded, but the
2956 * return value register etc. is not set, so we have to be careful.
2958 mini_get_interp_callbacks ()->set_resume_state (jit_tls, ex_obj, ei, frame.interp_frame, ei->handler_start);
2959 /* Undo the IP adjustment done by mono_arch_unwind_frame () */
2960 /* ip == 0 means an interpreter frame */
2961 if (MONO_CONTEXT_GET_IP (ctx) != 0)
2962 mono_arch_undo_ip_adjustment (ctx);
2963 } else {
2964 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
2966 mono_set_lmf (lmf);
2967 #ifndef DISABLE_PERFCOUNTERS
2968 mono_atomic_fetch_add_i32 (&mono_perfcounters->exceptions_depth, frame_count);
2969 #endif
2970 if (obj == (MonoObject *)domain->stack_overflow_ex)
2971 jit_tls->handling_stack_ovf = FALSE;
2973 return 0;
2975 mono_error_cleanup (error);
2976 if (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
2977 if (mono_trace_is_enabled () && mono_trace_eval (method))
2978 g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (method, TRUE));
2980 if (G_UNLIKELY (mono_profiler_clauses_enabled ())) {
2981 jit_tls->orig_ex_ctx_set = TRUE;
2982 MONO_PROFILER_RAISE (exception_clause, (method, i, (MonoExceptionEnum)ei->flags, ex_obj));
2983 jit_tls->orig_ex_ctx_set = FALSE;
2986 if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
2987 if (mono_trace_is_enabled () && mono_trace_eval (method))
2988 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (method, TRUE));
2990 if (G_UNLIKELY (mono_profiler_clauses_enabled ())) {
2991 jit_tls->orig_ex_ctx_set = TRUE;
2992 MONO_PROFILER_RAISE (exception_clause, (method, i, (MonoExceptionEnum)ei->flags, ex_obj));
2993 jit_tls->orig_ex_ctx_set = FALSE;
2996 #ifndef DISABLE_PERFCOUNTERS
2997 mono_atomic_inc_i32 (&mono_perfcounters->exceptions_finallys);
2998 #endif
3000 if (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
3001 mono_set_lmf (lmf);
3002 if (ji->from_llvm) {
3004 * LLVM compiled finally handlers follow the design
3005 * of the c++ ehabi, i.e. they call a resume function
3006 * at the end instead of returning to the caller.
3007 * So save the exception handling state,
3008 * mono_resume_unwind () will call us again to continue
3009 * the unwinding.
3011 jit_tls->resume_state.ex_obj = obj;
3012 jit_tls->resume_state.ji = ji;
3013 jit_tls->resume_state.clause_index = i + 1;
3014 jit_tls->resume_state.ctx = *ctx;
3015 jit_tls->resume_state.new_ctx = new_ctx;
3016 jit_tls->resume_state.lmf = lmf;
3017 jit_tls->resume_state.first_filter_idx = first_filter_idx;
3018 jit_tls->resume_state.filter_idx = filter_idx;
3019 mini_set_abort_threshold (&frame);
3020 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
3021 return 0;
3022 } else {
3023 mini_set_abort_threshold (&frame);
3024 if (in_interp) {
3025 gboolean has_ex = mini_get_interp_callbacks ()->run_finally (&frame, i, ei->handler_start, ei->data.handler_end);
3026 if (has_ex) {
3028 * If run_finally didn't resume to a context, it means that the handler frame
3029 * is linked to the frame calling finally through interpreter frames. This
3030 * means that we will reach the handler frame by resuming the current context.
3032 if (MONO_CONTEXT_GET_IP (ctx) != 0)
3033 mono_arch_undo_ip_adjustment (ctx);
3034 return 0;
3036 } else {
3037 call_filter (ctx, ei->handler_start);
3044 if (in_interp)
3045 interp_exit_finally_abort_blocks (ji, clause_index_start, ji->num_clauses, ip);
3047 if (MONO_PROFILER_ENABLED (method_exception_leave) &&
3048 mono_profiler_get_call_instrumentation_flags (method) & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE) {
3049 jit_tls->orig_ex_ctx_set = TRUE;
3050 MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj));
3051 jit_tls->orig_ex_ctx_set = FALSE;
3054 *ctx = new_ctx;
3057 g_assert_not_reached ();
3061 * mono_debugger_run_finally:
3062 * \param start_ctx saved processor state
3063 * This method is called by the Mono Debugger to call all \c finally clauses of the
3064 * current stack frame. It's used when the user issues a \c return command to make
3065 * the current stack frame return. After returning from this method, the debugger
3066 * unwinds the stack one frame and gives control back to the user.
3067 * NOTE: This method is only used when running inside the Mono Debugger.
3069 void
3070 mono_debugger_run_finally (MonoContext *start_ctx)
3072 static int (*call_filter) (MonoContext *, gpointer) = NULL;
3073 MonoDomain *domain = mono_domain_get ();
3074 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3075 MonoLMF *lmf = mono_get_lmf ();
3076 MonoContext ctx, new_ctx;
3077 MonoJitInfo *ji, rji;
3078 int i;
3080 ctx = *start_ctx;
3082 ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, NULL);
3083 if (!ji || ji == (gpointer)-1)
3084 return;
3086 if (!call_filter)
3087 call_filter = (int (*)(MonoContext *, void *))mono_get_call_filter ();
3089 for (i = 0; i < ji->num_clauses; i++) {
3090 MonoJitExceptionInfo *ei = &ji->clauses [i];
3092 if (is_address_protected (ji, ei, MONO_CONTEXT_GET_IP (&ctx)) &&
3093 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
3094 call_filter (&ctx, ei->handler_start);
3100 * mono_handle_exception:
3101 * \param ctx saved processor state
3102 * \param obj the exception object
3104 * Handle the exception OBJ starting from the state CTX. Modify CTX to point to the handler clause if the exception is caught, and
3105 * return TRUE.
3107 gboolean
3108 mono_handle_exception (MonoContext *ctx, gpointer void_obj)
3110 MonoObject *obj = (MonoObject*)void_obj;
3112 MONO_REQ_GC_UNSAFE_MODE;
3114 #ifndef DISABLE_PERFCOUNTERS
3115 mono_atomic_inc_i32 (&mono_perfcounters->exceptions_thrown);
3116 #endif
3118 return mono_handle_exception_internal (ctx, obj, FALSE, NULL);
3121 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
3123 #ifndef MONO_ARCH_USE_SIGACTION
3124 #error "Can't use sigaltstack without sigaction"
3125 #endif
3127 void
3128 mono_setup_altstack (MonoJitTlsData *tls)
3130 size_t stsize = 0;
3131 stack_t sa;
3132 guint8 *staddr = NULL;
3133 #if defined(TARGET_OSX) || defined(_AIX)
3135 * On macOS Mojave we are encountering a bug when changing mapping for main thread
3136 * stack pages. Stack overflow on main thread will kill the app.
3138 * AIX seems problematic as well; it gives ENOMEM for mprotect and valloc, if we
3139 * do this for thread 1 with its stack at the top of memory. Other threads seem
3140 * fine for the altstack guard page, though.
3142 gboolean disable_stack_guard = mono_threads_platform_is_main_thread ();
3143 #else
3144 gboolean disable_stack_guard = FALSE;
3145 #endif
3147 if (mono_running_on_valgrind ())
3148 return;
3150 mono_thread_info_get_stack_bounds (&staddr, &stsize);
3152 g_assert (staddr);
3154 tls->end_of_stack = staddr + stsize;
3155 tls->stack_size = stsize;
3157 /*g_print ("thread %p, stack_base: %p, stack_size: %d\n", (gpointer)pthread_self (), staddr, stsize);*/
3159 if (!disable_stack_guard) {
3160 tls->stack_ovf_guard_base = staddr + mono_pagesize ();
3161 tls->stack_ovf_guard_size = ALIGN_TO (MONO_STACK_OVERFLOW_GUARD_SIZE, mono_pagesize ());
3163 g_assert ((guint8*)&sa >= (guint8*)tls->stack_ovf_guard_base + tls->stack_ovf_guard_size);
3165 if (mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE)) {
3166 /* mprotect can fail for the main thread stack */
3167 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, MONO_MEM_ACCOUNT_EXCEPTIONS);
3168 if (gaddr) {
3169 g_assert (gaddr == tls->stack_ovf_guard_base);
3170 tls->stack_ovf_valloced = TRUE;
3171 } else {
3172 g_warning ("couldn't allocate guard page, continue without it");
3173 tls->stack_ovf_guard_base = NULL;
3174 tls->stack_ovf_guard_size = 0;
3179 /* Setup an alternate signal stack */
3180 tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON, MONO_MEM_ACCOUNT_EXCEPTIONS);
3181 tls->signal_stack_size = MONO_ARCH_SIGNAL_STACK_SIZE;
3183 g_assert (tls->signal_stack);
3185 sa.ss_sp = tls->signal_stack;
3186 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
3187 sa.ss_flags = 0;
3188 g_assert (sigaltstack (&sa, NULL) == 0);
3190 if (tls->stack_ovf_guard_base)
3191 mono_gc_register_altstack ((char*)tls->stack_ovf_guard_base + tls->stack_ovf_guard_size, (char*)staddr + stsize - ((char*)tls->stack_ovf_guard_base + tls->stack_ovf_guard_size), tls->signal_stack, tls->signal_stack_size);
3192 else
3193 mono_gc_register_altstack (staddr, stsize, tls->signal_stack, tls->signal_stack_size);
3197 void
3198 mono_free_altstack (MonoJitTlsData *tls)
3200 stack_t sa;
3201 int err;
3203 sa.ss_sp = tls->signal_stack;
3204 sa.ss_size = MONO_ARCH_SIGNAL_STACK_SIZE;
3205 sa.ss_flags = SS_DISABLE;
3206 err = sigaltstack (&sa, NULL);
3207 g_assert (err == 0);
3209 if (tls->signal_stack)
3210 mono_vfree (tls->signal_stack, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MEM_ACCOUNT_EXCEPTIONS);
3212 if (!tls->stack_ovf_guard_base)
3213 return;
3214 if (tls->stack_ovf_valloced)
3215 mono_vfree (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MEM_ACCOUNT_EXCEPTIONS);
3216 else
3217 mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_READ|MONO_MMAP_WRITE);
3220 #elif HAVE_API_SUPPORT_WIN32_SET_THREAD_STACK_GUARANTEE && defined(HOST_WIN32)
3221 void
3222 mono_setup_altstack (MonoJitTlsData *tls)
3224 // Alt stack is not supported on Windows, but we can use this point to at least
3225 // reserve a stack guarantee of available stack memory when handling stack overflow.
3226 ULONG new_stack_guarantee = (ULONG)ALIGN_TO (MONO_STACK_OVERFLOW_GUARD_SIZE, ((gssize)mono_pagesize ()));
3227 SetThreadStackGuarantee (&new_stack_guarantee);
3230 void
3231 mono_free_altstack (MonoJitTlsData *tls)
3235 #else /* !MONO_ARCH_SIGSEGV_ON_ALTSTACK */
3237 void
3238 mono_setup_altstack (MonoJitTlsData *tls)
3242 void
3243 mono_free_altstack (MonoJitTlsData *tls)
3247 #endif /* MONO_ARCH_SIGSEGV_ON_ALTSTACK */
3249 gboolean
3250 mono_handle_soft_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, void *ctx, MONO_SIG_HANDLER_INFO_TYPE *siginfo, guint8* fault_addr)
3252 if (!jit_tls)
3253 return FALSE;
3255 if (mono_llvm_only)
3256 return FALSE;
3258 /* we got a stack overflow in the soft-guard pages
3259 * There are two cases:
3260 * 1) managed code caused the overflow: we unprotect the soft-guard page
3261 * and let the arch-specific code trigger the exception handling mechanism
3262 * in the thread stack. The soft-guard pages will be protected again as the stack is unwound.
3263 * 2) unmanaged code caused the overflow: we unprotect the soft-guard page
3264 * and hope we can continue with those enabled, at least until the hard-guard page
3265 * is hit. The alternative to continuing here is to just print a message and abort.
3266 * We may add in the future the code to protect the pages again in the codepath
3267 * when we return from unmanaged to managed code.
3269 if (jit_tls->stack_ovf_guard_size && fault_addr >= (guint8*)jit_tls->stack_ovf_guard_base &&
3270 fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) {
3271 gboolean handled = FALSE;
3273 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_READ|MONO_MMAP_WRITE);
3274 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
3275 if (ji) {
3276 mono_arch_handle_altstack_exception (ctx, siginfo, fault_addr, TRUE);
3277 handled = TRUE;
3279 #endif
3280 if (!handled) {
3281 /* We print a message: after this even managed stack overflows
3282 * may crash the runtime
3284 mono_runtime_printf_err ("Stack overflow in unmanaged: IP: %p, fault addr: %p", mono_arch_ip_from_context (ctx), fault_addr);
3285 if (!jit_tls->handling_stack_ovf) {
3286 jit_tls->handling_stack_ovf = 1;
3287 } else {
3288 /*fprintf (stderr, "Already handling stack overflow\n");*/
3291 return TRUE;
3293 return FALSE;
3296 typedef struct {
3297 MonoMethod *omethod;
3298 int count;
3299 } PrintOverflowUserData;
3301 static gboolean
3302 print_overflow_stack_frame (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
3304 MonoMethod *method = NULL;
3305 PrintOverflowUserData *user_data = (PrintOverflowUserData *)data;
3306 gchar *location;
3308 if (frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE)
3309 method = jinfo_get_method (frame->ji);
3311 if (method) {
3312 if (user_data->count == 0) {
3313 /* The first frame is in its prolog, so a line number cannot be computed */
3314 user_data->count ++;
3315 return FALSE;
3318 /* If this is a one method overflow, skip the other instances */
3319 if (method == user_data->omethod)
3320 return FALSE;
3322 location = mono_debug_print_stack_frame (method, frame->native_offset, mono_domain_get ());
3323 mono_runtime_printf_err (" %s", location);
3324 g_free (location);
3326 if (user_data->count == 1) {
3327 mono_runtime_printf_err (" <...>");
3328 user_data->omethod = method;
3329 } else {
3330 user_data->omethod = NULL;
3333 user_data->count ++;
3334 } else
3335 mono_runtime_printf_err (" at <unknown> <0x%05x>", frame->native_offset);
3337 return FALSE;
3340 void
3341 mono_handle_hard_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *mctx, guint8* fault_addr)
3343 PrintOverflowUserData ud;
3345 /* we don't do much now, but we can warn the user with a useful message */
3346 mono_runtime_printf_err ("Stack overflow: IP: %p, fault addr: %p", MONO_CONTEXT_GET_IP (mctx), fault_addr);
3348 mono_runtime_printf_err ("Stacktrace:");
3350 memset (&ud, 0, sizeof (ud));
3352 mono_walk_stack_with_ctx (print_overflow_stack_frame, mctx, MONO_UNWIND_LOOKUP_ACTUAL_METHOD, &ud);
3354 _exit (1);
3357 static gboolean
3358 print_stack_frame_signal_safe (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
3360 MonoMethod *method = NULL;
3362 if (frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE)
3363 method = jinfo_get_method (frame->ji);
3365 if (method) {
3366 const char *name_space = m_class_get_name_space (method->klass);
3367 g_async_safe_printf("\t at %s%s%s:%s <0x%05x>\n", name_space, (name_space [0] != '\0' ? "." : ""), m_class_get_name (method->klass), method->name, frame->native_offset);
3368 } else {
3369 g_async_safe_printf("\t at <unknown> <0x%05x>\n", frame->native_offset);
3372 return FALSE;
3375 static G_GNUC_UNUSED gboolean
3376 print_stack_frame_to_string (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
3378 GString *p = (GString*)data;
3379 MonoMethod *method = NULL;
3381 if (frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE)
3382 method = jinfo_get_method (frame->ji);
3384 if (method && frame->domain) {
3385 gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
3386 g_string_append_printf (p, " %s\n", location);
3387 g_free (location);
3388 } else
3389 g_string_append_printf (p, " at <unknown> <0x%05x>\n", frame->native_offset);
3391 return FALSE;
3394 #ifndef MONO_CROSS_COMPILE
3397 * mono_handle_native_crash:
3399 * Handle a native crash (e.g. SIGSEGV) while in native code by
3400 * printing diagnostic information and aborting.
3402 void
3403 mono_handle_native_crash (const char *signal, MonoContext *mctx, MONO_SIG_HANDLER_INFO_TYPE *info)
3405 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3407 #ifdef MONO_ARCH_USE_SIGACTION
3408 struct sigaction sa;
3409 sa.sa_handler = SIG_DFL;
3410 sigemptyset (&sa.sa_mask);
3411 sa.sa_flags = 0;
3413 /* Remove our SIGABRT handler */
3414 g_assert (sigaction (SIGABRT, &sa, NULL) != -1);
3416 /* On some systems we get a SIGILL when calling abort (), because it might
3417 * fail to raise SIGABRT */
3418 g_assert (sigaction (SIGILL, &sa, NULL) != -1);
3420 /* Remove SIGCHLD, it uses the finalizer thread */
3421 g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
3423 /* Remove SIGQUIT, we are already dumping threads */
3424 g_assert (sigaction (SIGQUIT, &sa, NULL) != -1);
3426 #endif
3428 if (mini_debug_options.suspend_on_native_crash) {
3429 g_async_safe_printf ("Received %s, suspending...\n", signal);
3430 while (1) {
3431 // Sleep for 1 second.
3432 g_usleep (1000 * 1000);
3437 * A crash indicates something went very wrong so we can no longer depend
3438 * on anything working. So try to print out lots of diagnostics, starting
3439 * with ones which have a greater chance of working.
3442 g_async_safe_printf("\n=================================================================\n");
3443 g_async_safe_printf("\tNative Crash Reporting\n");
3444 g_async_safe_printf("=================================================================\n");
3445 g_async_safe_printf("Got a %s while executing native code. This usually indicates\n", signal);
3446 g_async_safe_printf("a fatal error in the mono runtime or one of the native libraries \n");
3447 g_async_safe_printf("used by your application.\n");
3448 g_async_safe_printf("=================================================================\n");
3449 mono_dump_native_crash_info (signal, mctx, info);
3451 /* !jit_tls means the thread was not registered with the runtime */
3452 // This must be below the native crash dump, because we can't safely
3453 // do runtime state probing after we have walked the managed stack here.
3454 if (jit_tls && mono_thread_internal_current () && mctx) {
3455 g_async_safe_printf ("\n=================================================================\n");
3456 g_async_safe_printf ("\tManaged Stacktrace:\n");
3457 g_async_safe_printf ("=================================================================\n");
3459 mono_walk_stack_full (print_stack_frame_signal_safe, mctx, mono_domain_get (), jit_tls, mono_get_lmf (), MONO_UNWIND_LOOKUP_IL_OFFSET, NULL, TRUE);
3460 g_async_safe_printf ("=================================================================\n");
3463 mono_post_native_crash_handler (signal, mctx, info, mono_do_crash_chaining);
3466 #else
3468 void
3469 mono_handle_native_crash (const char *signal, MonoContext *mctx, MONO_SIG_HANDLER_INFO_TYPE *info)
3471 g_assert_not_reached ();
3474 #endif /* !MONO_CROSS_COMPILE */
3476 static void
3477 mono_print_thread_dump_internal (void *sigctx, MonoContext *start_ctx)
3479 MonoInternalThread *thread = mono_thread_internal_current ();
3480 MonoContext ctx;
3481 GString* text;
3483 if (!thread)
3484 return;
3486 text = g_string_new (0);
3488 mono_gstring_append_thread_name (text, thread);
3490 g_string_append_printf (text, " tid=%p this=%p ", (gpointer)(gsize)thread->tid, thread);
3491 mono_thread_internal_describe (thread, text);
3492 g_string_append (text, "\n");
3494 if (start_ctx) {
3495 memcpy (&ctx, start_ctx, sizeof (MonoContext));
3496 } else if (!sigctx)
3497 MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_print_thread_dump);
3498 else
3499 mono_sigctx_to_monoctx (sigctx, &ctx);
3501 mono_walk_stack_with_ctx (print_stack_frame_to_string, &ctx, MONO_UNWIND_LOOKUP_ALL, text);
3503 mono_runtime_printf ("%s", text->str);
3505 #if HOST_WIN32 && TARGET_WIN32 && _DEBUG
3506 OutputDebugStringA(text->str);
3507 #endif
3509 g_string_free (text, TRUE);
3510 mono_runtime_stdout_fflush ();
3514 * mono_print_thread_dump:
3516 * Print information about the current thread to stdout.
3517 * \p sigctx can be NULL, allowing this to be called from gdb.
3519 void
3520 mono_print_thread_dump (void *sigctx)
3522 mono_print_thread_dump_internal (sigctx, NULL);
3525 void
3526 mono_print_thread_dump_from_ctx (MonoContext *ctx)
3528 mono_print_thread_dump_internal (NULL, ctx);
3532 * mono_resume_unwind:
3534 * This is called by a trampoline from LLVM compiled finally clauses to continue
3535 * unwinding.
3537 void
3538 mono_resume_unwind (MonoContext *ctx)
3540 MONO_REQ_GC_UNSAFE_MODE;
3542 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3543 MonoContext new_ctx;
3545 MONO_CONTEXT_SET_IP (ctx, MONO_CONTEXT_GET_IP (&jit_tls->resume_state.ctx));
3546 MONO_CONTEXT_SET_SP (ctx, MONO_CONTEXT_GET_SP (&jit_tls->resume_state.ctx));
3547 new_ctx = *ctx;
3549 mono_handle_exception_internal (&new_ctx, (MonoObject *)jit_tls->resume_state.ex_obj, TRUE, NULL);
3551 mono_restore_context (&new_ctx);
3554 typedef struct {
3555 MonoJitInfo *ji;
3556 MonoContext ctx;
3557 MonoJitExceptionInfo *ei;
3558 } FindHandlerBlockData;
3560 static gboolean
3561 find_last_handler_block (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
3563 int i;
3564 gpointer ip;
3565 FindHandlerBlockData *pdata = (FindHandlerBlockData *)data;
3566 MonoJitInfo *ji = frame->ji;
3568 if (!ji)
3569 return FALSE;
3571 ip = MONO_CONTEXT_GET_IP (ctx);
3573 for (i = 0; i < ji->num_clauses; ++i) {
3574 MonoJitExceptionInfo *ei = ji->clauses + i;
3575 if (ei->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
3576 continue;
3577 /*If ip points to the first instruction it means the handler block didn't start
3578 so we can leave its execution to the EH machinery*/
3579 if (ei->handler_start <= ip && ip < ei->data.handler_end) {
3580 pdata->ji = ji;
3581 pdata->ei = ei;
3582 pdata->ctx = *ctx;
3583 break;
3586 return FALSE;
3590 static void
3591 install_handler_block_guard (MonoJitInfo *ji, MonoContext *ctx)
3593 int i;
3594 MonoJitExceptionInfo *clause = NULL;
3595 gpointer ip;
3596 guint8 *bp;
3598 ip = MONO_CONTEXT_GET_IP (ctx);
3600 for (i = 0; i < ji->num_clauses; ++i) {
3601 clause = &ji->clauses [i];
3602 if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
3603 continue;
3604 if (clause->handler_start <= ip && clause->data.handler_end > ip)
3605 break;
3608 /*no matching finally - can't happen, we parallel the logic in find_last_handler_block. */
3609 g_assert (i < ji->num_clauses);
3611 /*Load the spvar*/
3612 bp = (guint8*)MONO_CONTEXT_GET_BP (ctx);
3613 *(bp + clause->exvar_offset) = 1;
3617 * Finds the bottom handler block running and install a block guard if needed.
3619 static gboolean
3620 mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
3622 FindHandlerBlockData data = { 0 };
3623 MonoJitTlsData *jit_tls = (MonoJitTlsData *)ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS];
3625 /* Guard against a null MonoJitTlsData. This can happens if the thread receives the
3626 * interrupt signal before the JIT has time to initialize its TLS data for the given thread.
3628 if (!jit_tls || jit_tls->handler_block)
3629 return FALSE;
3631 /* Do an async safe stack walk */
3632 mono_thread_info_set_is_async_context (TRUE);
3633 mono_walk_stack_with_state (find_last_handler_block, ctx, MONO_UNWIND_NONE, &data);
3634 mono_thread_info_set_is_async_context (FALSE);
3636 if (!data.ji)
3637 return FALSE;
3639 memcpy (&jit_tls->handler_block_context, &data.ctx, sizeof (MonoContext));
3641 install_handler_block_guard (data.ji, &data.ctx);
3643 jit_tls->handler_block = data.ei;
3645 return TRUE;
3648 static void
3649 mono_uninstall_current_handler_block_guard (void)
3651 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3652 if (jit_tls)
3653 jit_tls->handler_block = NULL;
3657 static gboolean
3658 mono_current_thread_has_handle_block_guard (void)
3660 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3661 return jit_tls && jit_tls->handler_block != NULL;
3664 void
3665 mono_set_cast_details (MonoClass *from, MonoClass *to)
3667 MonoJitTlsData *jit_tls = NULL;
3669 if (mini_debug_options.better_cast_details) {
3670 jit_tls = mono_tls_get_jit_tls ();
3671 jit_tls->class_cast_from = from;
3672 jit_tls->class_cast_to = to;
3677 /*returns false if the thread is not attached*/
3678 gboolean
3679 mono_thread_state_init_from_sigctx (MonoThreadUnwindState *ctx, void *sigctx)
3681 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
3682 if (!thread) {
3683 ctx->valid = FALSE;
3684 return FALSE;
3687 if (sigctx) {
3688 mono_sigctx_to_monoctx (sigctx, &ctx->ctx);
3690 ctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
3691 ctx->unwind_data [MONO_UNWIND_DATA_LMF] = mono_get_lmf ();
3692 ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = thread->jit_data;
3694 else {
3695 mono_thread_state_init (ctx);
3698 if (!ctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] || !ctx->unwind_data [MONO_UNWIND_DATA_LMF])
3699 return FALSE;
3701 ctx->valid = TRUE;
3702 return TRUE;
3705 void
3706 mono_thread_state_init (MonoThreadUnwindState *ctx)
3708 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
3710 #if defined(MONO_CROSS_COMPILE)
3711 ctx->valid = FALSE; //A cross compiler doesn't need to suspend.
3712 #elif MONO_ARCH_HAS_MONO_CONTEXT
3713 MONO_CONTEXT_GET_CURRENT (ctx->ctx);
3714 #else
3715 g_error ("Use a null sigctx requires a working mono-context");
3716 #endif
3718 ctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
3719 ctx->unwind_data [MONO_UNWIND_DATA_LMF] = mono_get_lmf ();
3720 ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = thread ? thread->jit_data : NULL;
3721 ctx->valid = TRUE;
3725 gboolean
3726 mono_thread_state_init_from_monoctx (MonoThreadUnwindState *ctx, MonoContext *mctx)
3728 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
3729 if (!thread) {
3730 ctx->valid = FALSE;
3731 return FALSE;
3734 ctx->ctx = *mctx;
3735 ctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
3736 ctx->unwind_data [MONO_UNWIND_DATA_LMF] = mono_get_lmf ();
3737 ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = thread->jit_data;
3738 ctx->valid = TRUE;
3739 return TRUE;
3742 /*returns false if the thread is not attached*/
3743 gboolean
3744 mono_thread_state_init_from_current (MonoThreadUnwindState *ctx)
3746 MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
3747 MONO_ARCH_CONTEXT_DEF
3749 mono_arch_flush_register_windows ();
3751 if (!thread || !thread->jit_data) {
3752 ctx->valid = FALSE;
3753 return FALSE;
3755 MONO_INIT_CONTEXT_FROM_FUNC (&ctx->ctx, mono_thread_state_init_from_current);
3757 ctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
3758 ctx->unwind_data [MONO_UNWIND_DATA_LMF] = mono_get_lmf ();
3759 ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = thread->jit_data;
3760 ctx->valid = TRUE;
3761 return TRUE;
3764 static void
3765 mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx)
3767 mono_handle_exception (ctx, (MonoObject *)exc);
3768 mono_restore_context (ctx);
3771 /*FIXME Move all monoctx -> sigctx conversion to signal handlers once all archs support utils/mono-context */
3772 void
3773 mono_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
3775 #ifdef MONO_ARCH_HAVE_SETUP_ASYNC_CALLBACK
3776 MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
3777 jit_tls->ex_ctx = *ctx;
3779 mono_arch_setup_async_callback (ctx, async_cb, user_data);
3780 #else
3781 g_error ("This target doesn't support mono_arch_setup_async_callback");
3782 #endif
3786 * mono_restore_context:
3788 * Call the architecture specific restore context function.
3790 void
3791 mono_restore_context (MonoContext *ctx)
3793 static void (*restore_context) (MonoContext *);
3795 if (!restore_context)
3796 restore_context = (void (*)(MonoContext *))mono_get_restore_context ();
3797 restore_context (ctx);
3798 g_assert_not_reached ();
3802 * mono_jinfo_get_unwind_info:
3804 * Return the unwind info for JI.
3806 guint8*
3807 mono_jinfo_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len)
3809 if (ji->has_unwind_info) {
3810 /* The address/length in the MonoJitInfo structure itself */
3811 MonoUnwindJitInfo *info = mono_jit_info_get_unwind_info (ji);
3812 *unwind_info_len = info->unw_info_len;
3813 return info->unw_info;
3814 } else if (ji->from_aot)
3815 return mono_aot_get_unwind_info (ji, unwind_info_len);
3816 else
3817 return mono_get_cached_unwind_info (ji->unwind_info, unwind_info_len);
3821 mono_jinfo_get_epilog_size (MonoJitInfo *ji)
3823 MonoArchEHJitInfo *info;
3825 info = mono_jit_info_get_arch_eh_info (ji);
3826 g_assert (info);
3828 return info->epilog_size;
3832 * mono_install_ftnptr_eh_callback:
3834 * Install a callback that should be called when there is a managed exception
3835 * in a native-to-managed wrapper. This is mainly used by iOS to convert a
3836 * managed exception to a native exception, to properly unwind the native
3837 * stack; this native exception will then be converted back to a managed
3838 * exception in their managed-to-native wrapper.
3840 void
3841 mono_install_ftnptr_eh_callback (MonoFtnPtrEHCallback callback)
3843 ftnptr_eh_callback = callback;
3847 * LLVM/Bitcode exception handling.
3850 static void
3851 throw_exception (MonoObject *ex, gboolean rethrow)
3853 MONO_REQ_GC_UNSAFE_MODE;
3855 ERROR_DECL (error);
3856 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
3857 MonoException *mono_ex;
3859 if (!mono_object_isinst_checked (ex, mono_defaults.exception_class, error)) {
3860 mono_error_assert_ok (error);
3861 mono_ex = mono_get_exception_runtime_wrapped_checked (ex, error);
3862 mono_error_assert_ok (error);
3863 jit_tls->thrown_non_exc = mono_gchandle_new_internal (ex, FALSE);
3865 else
3866 mono_ex = (MonoException*)ex;
3868 // Note: Not pinned
3869 jit_tls->thrown_exc = mono_gchandle_new_internal ((MonoObject*)mono_ex, FALSE);
3871 if (!rethrow) {
3872 #ifdef MONO_ARCH_HAVE_UNWIND_BACKTRACE
3873 GList *l, *ips = NULL;
3874 GList *trace;
3876 _Unwind_Backtrace (build_stack_trace, &ips);
3877 /* The list contains ip-gshared info pairs */
3878 trace = NULL;
3879 ips = g_list_reverse (ips);
3880 for (l = ips; l; l = l->next) {
3881 trace = g_list_append (trace, l->data);
3882 trace = g_list_append (trace, NULL);
3883 trace = g_list_append (trace, NULL);
3885 MonoArray *ips_arr = mono_glist_to_array (trace, mono_defaults.int_class, error);
3886 mono_error_assert_ok (error);
3887 MONO_OBJECT_SETREF_INTERNAL (mono_ex, trace_ips, ips_arr);
3888 g_list_free (l);
3889 g_list_free (trace);
3890 #endif
3893 mono_llvm_cpp_throw_exception ();
3896 void
3897 mono_llvm_throw_exception (MonoObject *ex)
3899 throw_exception (ex, FALSE);
3902 void
3903 mono_llvm_rethrow_exception (MonoObject *ex)
3905 throw_exception (ex, TRUE);
3908 void
3909 mono_llvm_raise_exception (MonoException *e)
3911 mono_llvm_throw_exception ((MonoObject*)e);
3914 void
3915 mono_llvm_reraise_exception (MonoException *e)
3917 mono_llvm_rethrow_exception ((MonoObject*)e);
3920 void
3921 mono_llvm_throw_corlib_exception (guint32 ex_token_index)
3923 guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
3924 MonoException *ex;
3926 ex = mono_exception_from_token (m_class_get_image (mono_defaults.exception_class), ex_token);
3928 mono_llvm_throw_exception ((MonoObject*)ex);
3932 * mono_llvm_resume_exception:
3934 * Resume exception propagation.
3936 void
3937 mono_llvm_resume_exception (void)
3939 mono_llvm_cpp_throw_exception ();
3943 * mono_llvm_load_exception:
3945 * Return the currently thrown exception.
3947 MonoObject *
3948 mono_llvm_load_exception (void)
3950 ERROR_DECL (error);
3951 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
3953 MonoException *mono_ex = (MonoException*)mono_gchandle_get_target_internal (jit_tls->thrown_exc);
3955 MonoArray *ta = mono_ex->trace_ips;
3957 if (ta) {
3958 GList *trace_ips = NULL;
3959 gpointer ip = MONO_RETURN_ADDRESS ();
3961 size_t upper = mono_array_length_internal (ta);
3963 for (int i = 0; i < upper; i += TRACE_IP_ENTRY_SIZE) {
3964 gpointer curr_ip = mono_array_get_internal (ta, gpointer, i);
3965 for (int j = 0; j < TRACE_IP_ENTRY_SIZE; ++j) {
3966 gpointer p = mono_array_get_internal (ta, gpointer, i + j);
3967 trace_ips = g_list_append (trace_ips, p);
3969 if (ip == curr_ip)
3970 break;
3973 // FIXME: Does this work correctly for rethrows?
3974 // We may be discarding useful information
3975 // when this gets GC'ed
3976 MonoArray *ips_arr = mono_glist_to_array (trace_ips, mono_defaults.int_class, error);
3977 mono_error_assert_ok (error);
3978 MONO_OBJECT_SETREF_INTERNAL (mono_ex, trace_ips, ips_arr);
3979 g_list_free (trace_ips);
3981 // FIXME:
3982 //MONO_OBJECT_SETREF_INTERNAL (mono_ex, stack_trace, ves_icall_System_Exception_get_trace (mono_ex));
3983 } else {
3984 MONO_OBJECT_SETREF_INTERNAL (mono_ex, trace_ips, mono_array_new_checked (mono_domain_get (), mono_defaults.int_class, 0, error));
3985 mono_error_assert_ok (error);
3986 MONO_OBJECT_SETREF_INTERNAL (mono_ex, stack_trace, mono_array_new_checked (mono_domain_get (), mono_defaults.stack_frame_class, 0, error));
3987 mono_error_assert_ok (error);
3990 return &mono_ex->object;
3994 * mono_llvm_clear_exception:
3996 * Mark the currently thrown exception as handled.
3998 void
3999 mono_llvm_clear_exception (void)
4001 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
4002 mono_gchandle_free_internal (jit_tls->thrown_exc);
4003 jit_tls->thrown_exc = 0;
4004 if (jit_tls->thrown_non_exc)
4005 mono_gchandle_free_internal (jit_tls->thrown_non_exc);
4006 jit_tls->thrown_non_exc = 0;
4008 mono_memory_barrier ();
4012 * mono_llvm_match_exception:
4014 * Return the innermost clause containing REGION_START-REGION_END which can handle
4015 * the current exception.
4017 gint32
4018 mono_llvm_match_exception (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj)
4020 ERROR_DECL (error);
4021 MonoJitTlsData *jit_tls = mono_get_jit_tls ();
4022 MonoObject *exc;
4023 gint32 index = -1;
4025 g_assert (jit_tls->thrown_exc);
4026 exc = mono_gchandle_get_target_internal (jit_tls->thrown_exc);
4027 if (jit_tls->thrown_non_exc) {
4029 * Have to unwrap RuntimeWrappedExceptions if the
4030 * method's assembly doesn't have a RuntimeCompatibilityAttribute.
4032 if (!wrap_non_exception_throws (jinfo_get_method (jinfo)))
4033 exc = mono_gchandle_get_target_internal (jit_tls->thrown_non_exc);
4036 for (int i = 0; i < jinfo->num_clauses; i++) {
4037 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
4038 MonoClass *catch_class;
4040 if (! (ei->try_offset == region_start && ei->try_offset + ei->try_len == region_end) )
4041 continue;
4043 catch_class = ei->data.catch_class;
4044 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (catch_class))) {
4045 MonoGenericContext context;
4046 MonoType *inflated_type;
4048 g_assert (rgctx || this_obj);
4049 context = mono_get_generic_context_from_stack_frame (jinfo, rgctx ? rgctx : this_obj->vtable);
4050 inflated_type = mono_class_inflate_generic_type_checked (m_class_get_byval_arg (catch_class), &context, error);
4051 mono_error_assert_ok (error); /* FIXME don't swallow the error */
4053 catch_class = mono_class_from_mono_type_internal (inflated_type);
4054 mono_metadata_free_type (inflated_type);
4057 // FIXME: Handle edge cases handled in get_exception_catch_class
4058 if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst_checked (exc, catch_class, error)) {
4059 index = ei->clause_index;
4060 break;
4061 } else
4062 mono_error_assert_ok (error);
4064 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
4065 g_assert_not_reached ();
4069 return index;
4072 #if defined(ENABLE_LLVM) && defined(HAVE_UNWIND_H)
4073 G_EXTERN_C _Unwind_Reason_Code mono_debug_personality (int a, _Unwind_Action b,
4074 uint64_t c, struct _Unwind_Exception *d, struct _Unwind_Context *e)
4076 g_assert_not_reached ();
4078 #else
4079 G_EXTERN_C void mono_debug_personality (void);
4081 void
4082 mono_debug_personality (void)
4084 g_assert_not_reached ();
4086 #endif