2 * exceptions-x86.c: exception support for x86
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
15 #include <mono/arch/x86/x86-codegen.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/debug-helpers.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/mono-debug.h>
30 /* use SIG* defines if possible */
35 /* sigcontext surrogate */
49 typedef void (* MonoW32ExceptionHandler) (int);
50 void win32_seh_init(void);
51 void win32_seh_cleanup(void);
52 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler);
66 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep);
68 static MonoW32ExceptionHandler fpe_handler;
69 static MonoW32ExceptionHandler ill_handler;
70 static MonoW32ExceptionHandler segv_handler;
72 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
74 #define W32_SEH_HANDLE_EX(_ex) \
75 if (_ex##_handler) _ex##_handler((int)sctx)
78 * Unhandled Exception Filter
79 * Top-level per-process exception handler.
81 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
85 struct sigcontext* sctx;
88 res = EXCEPTION_CONTINUE_EXECUTION;
90 er = ep->ExceptionRecord;
91 ctx = ep->ContextRecord;
92 sctx = g_malloc(sizeof(struct sigcontext));
94 /* Copy Win32 context to UNIX style context */
100 sctx->esp = ctx->Esp;
101 sctx->esi = ctx->Esi;
102 sctx->edi = ctx->Edi;
103 sctx->eip = ctx->Eip;
105 switch (er->ExceptionCode) {
106 case EXCEPTION_ACCESS_VIOLATION:
107 W32_SEH_HANDLE_EX(segv);
109 case EXCEPTION_ILLEGAL_INSTRUCTION:
110 W32_SEH_HANDLE_EX(ill);
112 case EXCEPTION_INT_DIVIDE_BY_ZERO:
113 case EXCEPTION_INT_OVERFLOW:
114 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
115 case EXCEPTION_FLT_OVERFLOW:
116 case EXCEPTION_FLT_UNDERFLOW:
117 case EXCEPTION_FLT_INEXACT_RESULT:
118 W32_SEH_HANDLE_EX(fpe);
124 /* Copy context back */
125 ctx->Eax = sctx->eax;
126 ctx->Ebx = sctx->ebx;
127 ctx->Ecx = sctx->ecx;
128 ctx->Edx = sctx->edx;
129 ctx->Ebp = sctx->ebp;
130 ctx->Esp = sctx->esp;
131 ctx->Esi = sctx->esi;
132 ctx->Edi = sctx->edi;
133 ctx->Eip = sctx->eip;
138 void win32_seh_init()
140 old_handler = SetUnhandledExceptionFilter(seh_handler);
143 void win32_seh_cleanup()
145 if (old_handler) SetUnhandledExceptionFilter(old_handler);
148 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
152 fpe_handler = handler;
155 ill_handler = handler;
158 segv_handler = handler;
165 #endif /* PLATFORM_WIN32 */
167 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
168 # define SC_EAX sc_eax
169 # define SC_EBX sc_ebx
170 # define SC_ECX sc_ecx
171 # define SC_EDX sc_edx
172 # define SC_EBP sc_ebp
173 # define SC_EIP sc_eip
174 # define SC_ESP sc_esp
175 # define SC_EDI sc_edi
176 # define SC_ESI sc_esi
189 gboolean mono_arch_handle_exception (struct sigcontext *ctx, gpointer obj, gboolean test_only);
191 typedef struct sigcontext MonoContext;
193 #define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0);
194 #define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0);
196 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP))
197 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP))
199 #ifdef MONO_USE_EXC_TABLES
201 /*************************************/
202 /* STACK UNWINDING STUFF */
203 /*************************************/
205 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
208 #define DWARF_FRAME_REGISTERS 17
210 typedef struct frame_state
216 long reg_or_offset[DWARF_FRAME_REGISTERS+1];
217 unsigned short cfa_reg;
218 unsigned short retaddr_column;
219 char saved[DWARF_FRAME_REGISTERS+1];
223 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
225 switch (dwarf_regnum) {
243 g_assert_not_reached ();
250 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
252 switch (dwarf_regnum) {
281 g_assert_not_reached ();
285 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
287 static framesf frame_state_for = NULL;
289 static gboolean inited = FALSE;
291 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
293 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
296 init_frame_state_for (void)
301 * There are two versions of __frame_state_for: one in libgcc.a and the
302 * other in glibc.so. We need the version from glibc.
303 * For more info, see this:
304 * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
306 if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
308 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
309 frame_state_for = NULL;
311 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
312 get_backtrace_symbols = NULL;
313 frame_state_for = NULL;
316 g_module_close (module);
322 /* mono_arch_has_unwind_info:
324 * Tests if a function has an DWARF exception table able to restore
325 * all caller saved registers.
328 mono_arch_has_unwind_info (gconstpointer addr)
330 struct frame_state state_in;
331 struct frame_state *res;
334 init_frame_state_for ();
336 if (!frame_state_for)
341 memset (&state_in, 0, sizeof (state_in));
343 /* offset 10 is just a guess, but it works for all methods tested */
344 if ((res = frame_state_for ((char *)addr + 10, &state_in))) {
346 if (res->saved [X86_EBX] == 1 &&
347 res->saved [X86_EDI] == 1 &&
348 res->saved [X86_EBP] == 1 &&
349 res->saved [X86_ESI] == 1)
359 void *return_address;
363 x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx,
364 struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
366 struct stack_frame *frame;
369 struct frame_state state_in;
370 struct frame_state *res;
376 init_frame_state_for ();
378 if (!frame_state_for)
381 frame = MONO_CONTEXT_GET_BP (ctx);
383 max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
387 memset (&state_in, 0, sizeof (state_in));
389 while ((gpointer)frame->next < (gpointer)max_stack) {
390 gpointer ip, addr = frame->return_address;
392 char *tmp, **symbols;
395 ip = MONO_CONTEXT_GET_IP (new_ctx);
396 symbols = get_backtrace_symbols (&ip, 1);
398 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
400 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
407 if ((res = frame_state_for (addr, &state_in))) {
410 cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
411 frame = (struct stack_frame *)((gint8*)cfa - 8);
412 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
413 int how = res->saved[i];
415 g_assert ((how == 0) || (how == 1));
418 val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
419 set_sigcontext_reg (new_ctx, i, val);
422 new_ctx->SC_ESP = (long)cfa;
424 if (res->saved [X86_EBX] == 1 &&
425 res->saved [X86_EDI] == 1 &&
426 res->saved [X86_EBP] == 1 &&
427 res->saved [X86_ESI] == 1 &&
428 (ji = mono_jit_info_table_find (domain, frame->return_address))) {
429 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
434 //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx)));
436 MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
438 MONO_CONTEXT_SET_BP (new_ctx, frame);
440 /* stop if !frame or when we detect an unexpected managed frame */
441 if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
452 //g_assert_not_reached ();
464 * arch_get_restore_context:
466 * Returns a pointer to a method which restores a previously saved sigcontext.
469 arch_get_restore_context (void)
471 static guint8 *start = NULL;
477 /* restore_contect (struct sigcontext *ctx) */
478 /* we do not restore X86_EAX, X86_EDX */
480 start = code = g_malloc (1024);
483 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
485 /* get return address, stored in EDX */
486 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
488 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
490 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
492 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
494 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
496 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
498 /* jump to the saved IP */
499 x86_jump_reg (code, X86_EDX);
505 * arch_get_call_filter:
507 * Returns a pointer to a method which calls an exception filter. We
508 * also use this function to call finally handlers (we pass NULL as
509 * @exc object in this case).
512 arch_get_call_filter (void)
514 static guint8 start [64];
515 static int inited = 0;
522 /* call_filter (struct sigcontext *ctx, unsigned long eip) */
525 x86_push_reg (code, X86_EBP);
526 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
527 x86_push_reg (code, X86_EBX);
528 x86_push_reg (code, X86_EDI);
529 x86_push_reg (code, X86_ESI);
532 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
534 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
536 x86_push_reg (code, X86_EBP);
539 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
540 /* restore registers used by global register allocation (EBX & ESI) */
541 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
542 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
543 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
545 /* call the handler */
546 x86_call_reg (code, X86_ECX);
549 x86_pop_reg (code, X86_EBP);
551 /* restore saved regs */
552 x86_pop_reg (code, X86_ESI);
553 x86_pop_reg (code, X86_EDI);
554 x86_pop_reg (code, X86_EBX);
558 g_assert ((code - start) < 64);
563 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
564 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
565 unsigned long eip, unsigned long esp)
567 static void (*restore_context) (struct sigcontext *);
568 struct sigcontext ctx;
570 if (!restore_context)
571 restore_context = arch_get_restore_context ();
573 /* adjust eip so that it point into the call instruction */
576 /* Pop argument and return address */
577 ctx.SC_ESP = esp + (2 * sizeof (gpointer));
587 mono_arch_handle_exception (&ctx, exc, FALSE);
588 restore_context (&ctx);
590 g_assert_not_reached ();
594 * arch_get_throw_exception:
596 * Returns a function pointer which can be used to raise
597 * exceptions. The returned function has the following
598 * signature: void (*func) (MonoException *exc);
599 * For example to raise an arithmetic exception you can use:
601 * x86_push_imm (code, mono_get_exception_arithmetic ());
602 * x86_call_code (code, arch_get_throw_exception ());
606 mono_arch_get_throw_exception (void)
608 static guint8 start [24];
609 static int inited = 0;
618 x86_push_reg (code, X86_ESP);
619 x86_push_membase (code, X86_ESP, 4); /* IP */
620 x86_push_membase (code, X86_ESP, 12); /* exception */
621 x86_push_reg (code, X86_EBP);
622 x86_push_reg (code, X86_EDI);
623 x86_push_reg (code, X86_ESI);
624 x86_push_reg (code, X86_EBX);
625 x86_push_reg (code, X86_EDX);
626 x86_push_reg (code, X86_ECX);
627 x86_push_reg (code, X86_EAX);
628 x86_call_code (code, throw_exception);
629 /* we should never reach this breakpoint */
630 x86_breakpoint (code);
632 g_assert ((code - start) < 24);
637 * arch_get_throw_exception_by_name:
639 * Returns a function pointer which can be used to raise
640 * corlib exceptions. The returned function has the following
641 * signature: void (*func) (char *exc_name);
642 * For example to raise an arithmetic exception you can use:
644 * x86_push_imm (code, "ArithmeticException");
645 * x86_call_code (code, arch_get_throw_exception_by_name ());
649 mono_arch_get_throw_exception_by_name (void)
651 static guint8 start [32];
652 static int inited = 0;
661 x86_push_membase (code, X86_ESP, 4); /* exception name */
662 x86_push_imm (code, "System");
663 x86_push_imm (code, mono_defaults.exception_class->image);
664 x86_call_code (code, mono_exception_from_name);
665 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
666 /* save the newly create object (overwrite exception name)*/
667 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
668 x86_jump_code (code, mono_arch_get_throw_exception ());
670 g_assert ((code - start) < 32);
676 glist_to_array (GList *list)
678 MonoDomain *domain = mono_domain_get ();
685 len = g_list_length (list);
686 res = mono_array_new (domain, mono_defaults.int_class, len);
688 for (i = 0; list; list = list->next, i++)
689 mono_array_set (res, gpointer, i, list->data);
694 /* mono_arch_find_jit_info:
696 * This function is used to gather information from @ctx. It return the
697 * MonoJitInfo of the corresponding function, unwinds one stack frame and
698 * stores the resulting context into @new_ctx. It also stores a string
699 * describing the stack location into @trace (if not NULL), and modifies
700 * the @lmf if necessary. @native_offset return the IP offset from the
701 * start of the function or -1 if that info is not available.
704 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoContext *ctx,
705 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
709 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
711 ji = mono_jit_info_table_find (domain, ip);
723 char *source_location, *tmpaddr, *fname;
724 gint32 address, iloffset;
729 address = (char *)ip - (char *)ji->code_start;
732 *native_offset = address;
735 if (!ji->method->wrapper_type)
739 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
740 iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
743 tmpaddr = g_strdup_printf ("<0x%05x>", address);
745 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
747 fname = mono_method_full_name (ji->method, TRUE);
750 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
752 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
755 g_free (source_location);
760 * Some managed methods like pinvoke wrappers might have save_lmf set.
761 * In this case, register save/restore code is not generated by the
762 * JIT, so we have to restore callee saved registers from the lmf.
764 if (ji->method->save_lmf) {
766 * We only need to do this if the exception was raised in managed
767 * code, since otherwise the lmf was already popped of the stack.
769 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
770 new_ctx->SC_ESI = (*lmf)->esi;
771 new_ctx->SC_EDI = (*lmf)->edi;
772 new_ctx->SC_EBX = (*lmf)->ebx;
777 /* restore caller saved registers */
778 if (ji->used_regs & X86_EBX_MASK) {
779 new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
782 if (ji->used_regs & X86_EDI_MASK) {
783 new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
786 if (ji->used_regs & X86_ESI_MASK) {
787 new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
791 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
792 /* remove any unused lmf */
793 *lmf = (*lmf)->previous_lmf;
796 /* Pop EBP and the return address */
797 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
798 /* we substract 1, so that the IP points into the call instruction */
799 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
800 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
804 #ifdef MONO_USE_EXC_TABLES
805 } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
817 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
819 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
822 memset (res, 0, sizeof (MonoJitInfo));
823 res->method = (*lmf)->method;
826 new_ctx->SC_ESI = (*lmf)->esi;
827 new_ctx->SC_EDI = (*lmf)->edi;
828 new_ctx->SC_EBX = (*lmf)->ebx;
829 new_ctx->SC_EBP = (*lmf)->ebp;
830 new_ctx->SC_EIP = (*lmf)->eip;
831 /* the lmf is always stored on the stack, so the following
832 * expression points to a stack location which can be used as ESP */
833 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
835 *lmf = (*lmf)->previous_lmf;
845 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
847 MonoDomain *domain = mono_domain_get ();
849 MonoArray *ta = exc->trace_ips;
853 /* Exception is not thrown yet */
854 return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
857 len = mono_array_length (ta);
859 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
861 for (i = skip; i < len; i++) {
863 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
864 gpointer ip = mono_array_get (ta, gpointer, i);
866 ji = mono_jit_info_table_find (domain, ip);
868 /* Unmanaged frame */
869 mono_array_set (res, gpointer, i, sf);
873 g_assert (ji != NULL);
875 sf->method = mono_method_get_object (domain, ji->method, NULL);
876 sf->native_offset = (char *)ip - (char *)ji->code_start;
878 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset, domain);
880 if (need_file_info) {
883 filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
885 sf->filename = filename? mono_string_new (domain, filename): NULL;
891 mono_array_set (res, gpointer, i, sf);
898 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
899 MonoDomain *domain = mono_domain_get ();
900 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
901 MonoLMF *lmf = jit_tls->lmf;
902 MonoJitInfo *ji, rji;
903 gint native_offset, il_offset;
906 MonoContext ctx, new_ctx;
908 MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
909 MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
911 while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
913 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
916 if (ji == (gpointer)-1)
919 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
921 if (func (ji->method, native_offset, il_offset, managed, user_data))
929 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
930 MonoReflectionMethod **method,
931 gint32 *iloffset, gint32 *native_offset,
932 MonoString **file, gint32 *line, gint32 *column)
934 MonoDomain *domain = mono_domain_get ();
935 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
936 MonoLMF *lmf = jit_tls->lmf;
937 MonoJitInfo *ji, rji;
938 MonoContext ctx, new_ctx;
940 MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
941 MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
946 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
950 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
953 /* skip all wrappers ??*/
954 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
955 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
956 ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE)
963 *method = mono_method_get_object (domain, ji->method, NULL);
964 *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset, domain);
966 if (need_file_info) {
969 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
971 *file = filename? mono_string_new (domain, filename): NULL;
981 * arch_handle_exception:
982 * @ctx: saved processor state
983 * @obj: the exception object
984 * @test_only: only test if the exception is caught, but dont call handlers
989 mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
991 MonoDomain *domain = mono_domain_get ();
992 MonoJitInfo *ji, rji;
993 static int (*call_filter) (MonoContext *, gpointer) = NULL;
994 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
995 MonoLMF *lmf = jit_tls->lmf;
996 GList *trace_ips = NULL;
997 MonoException *mono_ex;
999 g_assert (ctx != NULL);
1001 MonoException *ex = mono_get_exception_null_reference ();
1002 ex->message = mono_string_new (domain,
1003 "Object reference not set to an instance of an object");
1004 obj = (MonoObject *)ex;
1007 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
1008 mono_ex = (MonoException*)obj;
1009 mono_ex->stack_trace = NULL;
1015 call_filter = arch_get_call_filter ();
1017 g_assert (jit_tls->end_of_stack);
1018 g_assert (jit_tls->abort_func);
1021 MonoContext ctx_cp = *ctx;
1022 if (mono_jit_trace_calls != NULL)
1023 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
1024 if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) {
1025 if (mono_break_on_exc)
1027 mono_unhandled_exception (obj);
1032 MonoContext new_ctx;
1035 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx,
1036 test_only ? &trace : NULL, &lmf, NULL, NULL);
1038 g_warning ("Exception inside function without unwind info");
1039 g_assert_not_reached ();
1042 if (ji != (gpointer)-1) {
1044 if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
1047 trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
1049 if (!mono_ex->stack_trace)
1050 strace = g_strdup ("");
1052 strace = mono_string_to_utf8 (mono_ex->stack_trace);
1054 tmp = g_strdup_printf ("%s%s\n", strace, trace);
1057 mono_ex->stack_trace = mono_string_new (domain, tmp);
1062 if (ji->num_clauses) {
1065 g_assert (ji->clauses);
1067 for (i = 0; i < ji->num_clauses; i++) {
1068 MonoJitExceptionInfo *ei = &ji->clauses [i];
1070 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1071 MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
1074 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
1075 /* store the exception object int cfg->excvar */
1076 g_assert (ji->exvar_offset);
1077 *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
1080 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
1081 mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
1082 ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
1083 call_filter (ctx, ei->data.filter)))) {
1086 mono_ex->trace_ips = glist_to_array (trace_ips);
1087 g_list_free (trace_ips);
1091 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
1092 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1093 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
1098 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
1099 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1100 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
1101 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
1102 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1103 call_filter (ctx, ei->handler_start);
1115 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
1118 jit_tls->abort_func (obj);
1119 g_assert_not_reached ();
1122 mono_ex->trace_ips = glist_to_array (trace_ips);
1123 g_list_free (trace_ips);
1129 g_assert_not_reached ();