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/gc-internal.h>
22 #include <mono/metadata/mono-debug.h>
31 /* use SIG* defines if possible */
36 /* sigcontext surrogate */
50 typedef void (* MonoW32ExceptionHandler) (int);
51 void win32_seh_init(void);
52 void win32_seh_cleanup(void);
53 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler);
67 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep);
69 static MonoW32ExceptionHandler fpe_handler;
70 static MonoW32ExceptionHandler ill_handler;
71 static MonoW32ExceptionHandler segv_handler;
73 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
75 #define W32_SEH_HANDLE_EX(_ex) \
76 if (_ex##_handler) _ex##_handler((int)sctx)
79 * Unhandled Exception Filter
80 * Top-level per-process exception handler.
82 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
86 struct sigcontext* sctx;
89 res = EXCEPTION_CONTINUE_EXECUTION;
91 er = ep->ExceptionRecord;
92 ctx = ep->ContextRecord;
93 sctx = g_malloc(sizeof(struct sigcontext));
95 /* Copy Win32 context to UNIX style context */
100 sctx->ebp = ctx->Ebp;
101 sctx->esp = ctx->Esp;
102 sctx->esi = ctx->Esi;
103 sctx->edi = ctx->Edi;
104 sctx->eip = ctx->Eip;
106 switch (er->ExceptionCode) {
107 case EXCEPTION_ACCESS_VIOLATION:
108 W32_SEH_HANDLE_EX(segv);
110 case EXCEPTION_ILLEGAL_INSTRUCTION:
111 W32_SEH_HANDLE_EX(ill);
113 case EXCEPTION_INT_DIVIDE_BY_ZERO:
114 case EXCEPTION_INT_OVERFLOW:
115 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
116 case EXCEPTION_FLT_OVERFLOW:
117 case EXCEPTION_FLT_UNDERFLOW:
118 case EXCEPTION_FLT_INEXACT_RESULT:
119 W32_SEH_HANDLE_EX(fpe);
125 /* Copy context back */
126 ctx->Eax = sctx->eax;
127 ctx->Ebx = sctx->ebx;
128 ctx->Ecx = sctx->ecx;
129 ctx->Edx = sctx->edx;
130 ctx->Ebp = sctx->ebp;
131 ctx->Esp = sctx->esp;
132 ctx->Esi = sctx->esi;
133 ctx->Edi = sctx->edi;
134 ctx->Eip = sctx->eip;
139 void win32_seh_init()
141 old_handler = SetUnhandledExceptionFilter(seh_handler);
144 void win32_seh_cleanup()
146 if (old_handler) SetUnhandledExceptionFilter(old_handler);
149 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
153 fpe_handler = handler;
156 ill_handler = handler;
159 segv_handler = handler;
166 #endif /* PLATFORM_WIN32 */
169 #ifdef MONO_USE_EXC_TABLES
171 /*************************************/
172 /* STACK UNWINDING STUFF */
173 /*************************************/
175 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
178 #define DWARF_FRAME_REGISTERS 17
180 typedef struct frame_state
186 long reg_or_offset[DWARF_FRAME_REGISTERS+1];
187 unsigned short cfa_reg;
188 unsigned short retaddr_column;
189 char saved[DWARF_FRAME_REGISTERS+1];
193 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
195 switch (dwarf_regnum) {
213 g_assert_not_reached ();
220 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
222 switch (dwarf_regnum) {
251 g_assert_not_reached ();
255 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
257 static framesf frame_state_for = NULL;
259 static gboolean inited = FALSE;
261 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
263 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
266 init_frame_state_for (void)
271 * There are two versions of __frame_state_for: one in libgcc.a and the
272 * other in glibc.so. We need the version from glibc.
273 * For more info, see this:
274 * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
276 if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
278 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
279 frame_state_for = NULL;
281 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
282 get_backtrace_symbols = NULL;
283 frame_state_for = NULL;
286 g_module_close (module);
292 /* mono_arch_has_unwind_info:
294 * Tests if a function has an DWARF exception table able to restore
295 * all caller saved registers.
298 mono_arch_has_unwind_info (gconstpointer addr)
300 struct frame_state state_in;
301 struct frame_state *res;
304 init_frame_state_for ();
306 if (!frame_state_for)
311 memset (&state_in, 0, sizeof (state_in));
313 /* offset 10 is just a guess, but it works for all methods tested */
314 if ((res = frame_state_for ((char *)addr + 10, &state_in))) {
316 if (res->saved [X86_EBX] == 1 &&
317 res->saved [X86_EDI] == 1 &&
318 res->saved [X86_EBP] == 1 &&
319 res->saved [X86_ESI] == 1)
329 void *return_address;
333 x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx,
334 struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
336 struct stack_frame *frame;
339 struct frame_state state_in;
340 struct frame_state *res;
346 init_frame_state_for ();
348 if (!frame_state_for)
351 frame = MONO_CONTEXT_GET_BP (ctx);
353 max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
357 memset (&state_in, 0, sizeof (state_in));
359 while ((gpointer)frame->next < (gpointer)max_stack) {
360 gpointer ip, addr = frame->return_address;
362 char *tmp, **symbols;
365 ip = MONO_CONTEXT_GET_IP (new_ctx);
366 symbols = get_backtrace_symbols (&ip, 1);
368 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
370 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
377 if ((res = frame_state_for (addr, &state_in))) {
380 cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
381 frame = (struct stack_frame *)((gint8*)cfa - 8);
382 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
383 int how = res->saved[i];
385 g_assert ((how == 0) || (how == 1));
388 val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
389 set_sigcontext_reg (new_ctx, i, val);
392 new_ctx->SC_ESP = (long)cfa;
394 if (res->saved [X86_EBX] == 1 &&
395 res->saved [X86_EDI] == 1 &&
396 res->saved [X86_EBP] == 1 &&
397 res->saved [X86_ESI] == 1 &&
398 (ji = mono_jit_info_table_find (domain, frame->return_address))) {
399 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
404 //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)));
406 MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
408 MONO_CONTEXT_SET_BP (new_ctx, frame);
410 /* stop if !frame or when we detect an unexpected managed frame */
411 if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
422 //g_assert_not_reached ();
434 * mono_arch_get_restore_context:
436 * Returns a pointer to a method which restores a previously saved sigcontext.
439 mono_arch_get_restore_context (void)
441 static guint8 *start = NULL;
447 /* restore_contect (struct sigcontext *ctx) */
448 /* we do not restore X86_EAX, X86_EDX */
450 start = code = g_malloc (1024);
453 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
455 /* get return address, stored in EDX */
456 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
458 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
460 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
462 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
464 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
466 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
468 /* jump to the saved IP */
469 x86_jump_reg (code, X86_EDX);
475 * mono_arch_get_call_filter:
477 * Returns a pointer to a method which calls an exception filter. We
478 * also use this function to call finally handlers (we pass NULL as
479 * @exc object in this case).
482 mono_arch_get_call_filter (void)
484 static guint8 start [64];
485 static int inited = 0;
492 /* call_filter (struct sigcontext *ctx, unsigned long eip) */
495 x86_push_reg (code, X86_EBP);
496 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
497 x86_push_reg (code, X86_EBX);
498 x86_push_reg (code, X86_EDI);
499 x86_push_reg (code, X86_ESI);
502 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
504 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
506 x86_push_reg (code, X86_EBP);
509 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
510 /* restore registers used by global register allocation (EBX & ESI) */
511 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
512 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
513 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
515 /* call the handler */
516 x86_call_reg (code, X86_ECX);
519 x86_pop_reg (code, X86_EBP);
521 /* restore saved regs */
522 x86_pop_reg (code, X86_ESI);
523 x86_pop_reg (code, X86_EDI);
524 x86_pop_reg (code, X86_EBX);
528 g_assert ((code - start) < 64);
533 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
534 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
535 unsigned long eip, unsigned long esp)
537 static void (*restore_context) (struct sigcontext *);
538 struct sigcontext ctx;
540 if (!restore_context)
541 restore_context = mono_arch_get_restore_context ();
543 /* adjust eip so that it point into the call instruction */
546 /* Pop argument and return address */
547 ctx.SC_ESP = esp + (2 * sizeof (gpointer));
557 mono_arch_handle_exception (&ctx, exc, FALSE);
558 restore_context (&ctx);
560 g_assert_not_reached ();
564 * mono_arch_get_throw_exception:
566 * Returns a function pointer which can be used to raise
567 * exceptions. The returned function has the following
568 * signature: void (*func) (MonoException *exc);
569 * For example to raise an arithmetic exception you can use:
571 * x86_push_imm (code, mono_get_exception_arithmetic ());
572 * x86_call_code (code, arch_get_throw_exception ());
576 mono_arch_get_throw_exception (void)
578 static guint8 start [24];
579 static int inited = 0;
588 x86_push_reg (code, X86_ESP);
589 x86_push_membase (code, X86_ESP, 4); /* IP */
590 x86_push_membase (code, X86_ESP, 12); /* exception */
591 x86_push_reg (code, X86_EBP);
592 x86_push_reg (code, X86_EDI);
593 x86_push_reg (code, X86_ESI);
594 x86_push_reg (code, X86_EBX);
595 x86_push_reg (code, X86_EDX);
596 x86_push_reg (code, X86_ECX);
597 x86_push_reg (code, X86_EAX);
598 x86_call_code (code, throw_exception);
599 /* we should never reach this breakpoint */
600 x86_breakpoint (code);
602 g_assert ((code - start) < 24);
607 * mono_arch_get_throw_exception_by_name:
609 * Returns a function pointer which can be used to raise
610 * corlib exceptions. The returned function has the following
611 * signature: void (*func) (char *exc_name);
612 * For example to raise an arithmetic exception you can use:
614 * x86_push_imm (code, "ArithmeticException");
615 * x86_call_code (code, arch_get_throw_exception_by_name ());
619 mono_arch_get_throw_exception_by_name (void)
621 static guint8 start [32];
622 static int inited = 0;
631 x86_push_membase (code, X86_ESP, 4); /* exception name */
632 x86_push_imm (code, "System");
633 x86_push_imm (code, mono_defaults.exception_class->image);
634 x86_call_code (code, mono_exception_from_name);
635 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
636 /* save the newly create object (overwrite exception name)*/
637 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
638 x86_jump_code (code, mono_arch_get_throw_exception ());
640 g_assert ((code - start) < 32);
645 /* mono_arch_find_jit_info:
647 * This function is used to gather information from @ctx. It return the
648 * MonoJitInfo of the corresponding function, unwinds one stack frame and
649 * stores the resulting context into @new_ctx. It also stores a string
650 * describing the stack location into @trace (if not NULL), and modifies
651 * the @lmf if necessary. @native_offset return the IP offset from the
652 * start of the function or -1 if that info is not available.
655 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
656 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
660 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
662 /* Avoid costly table lookup during stack overflow */
663 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
666 ji = mono_jit_info_table_find (domain, ip);
678 char *source_location, *tmpaddr, *fname;
679 gint32 address, iloffset;
684 address = (char *)ip - (char *)ji->code_start;
687 *native_offset = address;
690 if (!ji->method->wrapper_type)
694 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
695 iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
698 tmpaddr = g_strdup_printf ("<0x%05x>", address);
700 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
702 fname = mono_method_full_name (ji->method, TRUE);
705 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
707 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
710 g_free (source_location);
715 * Some managed methods like pinvoke wrappers might have save_lmf set.
716 * In this case, register save/restore code is not generated by the
717 * JIT, so we have to restore callee saved registers from the lmf.
719 if (ji->method->save_lmf) {
721 * We only need to do this if the exception was raised in managed
722 * code, since otherwise the lmf was already popped of the stack.
724 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
725 new_ctx->SC_ESI = (*lmf)->esi;
726 new_ctx->SC_EDI = (*lmf)->edi;
727 new_ctx->SC_EBX = (*lmf)->ebx;
732 /* restore caller saved registers */
733 if (ji->used_regs & X86_EBX_MASK) {
734 new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
737 if (ji->used_regs & X86_EDI_MASK) {
738 new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
741 if (ji->used_regs & X86_ESI_MASK) {
742 new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
746 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
747 /* remove any unused lmf */
748 *lmf = (*lmf)->previous_lmf;
751 /* Pop EBP and the return address */
752 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
753 /* we substract 1, so that the IP points into the call instruction */
754 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
755 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
759 #ifdef MONO_USE_EXC_TABLES
760 } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
772 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
774 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
777 memset (res, 0, sizeof (MonoJitInfo));
778 res->method = (*lmf)->method;
781 new_ctx->SC_ESI = (*lmf)->esi;
782 new_ctx->SC_EDI = (*lmf)->edi;
783 new_ctx->SC_EBX = (*lmf)->ebx;
784 new_ctx->SC_EBP = (*lmf)->ebp;
785 new_ctx->SC_EIP = (*lmf)->eip;
786 /* the lmf is always stored on the stack, so the following
787 * expression points to a stack location which can be used as ESP */
788 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
790 *lmf = (*lmf)->previous_lmf;
800 * mono_arch_handle_exception:
802 * @ctx: saved processor state
803 * @obj: the exception object
806 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
808 return mono_handle_exception (sigctx, obj, test_only);