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>
28 static MonoW32ExceptionHandler fpe_handler;
29 static MonoW32ExceptionHandler ill_handler;
30 static MonoW32ExceptionHandler segv_handler;
32 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
34 #define W32_SEH_HANDLE_EX(_ex) \
35 if (_ex##_handler) _ex##_handler((int)sctx)
38 * Unhandled Exception Filter
39 * Top-level per-process exception handler.
41 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
45 struct sigcontext* sctx;
48 res = EXCEPTION_CONTINUE_EXECUTION;
50 er = ep->ExceptionRecord;
51 ctx = ep->ContextRecord;
52 sctx = g_malloc(sizeof(struct sigcontext));
54 /* Copy Win32 context to UNIX style context */
65 switch (er->ExceptionCode) {
66 case EXCEPTION_ACCESS_VIOLATION:
67 W32_SEH_HANDLE_EX(segv);
69 case EXCEPTION_ILLEGAL_INSTRUCTION:
70 W32_SEH_HANDLE_EX(ill);
72 case EXCEPTION_INT_DIVIDE_BY_ZERO:
73 case EXCEPTION_INT_OVERFLOW:
74 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
75 case EXCEPTION_FLT_OVERFLOW:
76 case EXCEPTION_FLT_UNDERFLOW:
77 case EXCEPTION_FLT_INEXACT_RESULT:
78 W32_SEH_HANDLE_EX(fpe);
84 /* Copy context back */
100 old_handler = SetUnhandledExceptionFilter(seh_handler);
103 void win32_seh_cleanup()
105 if (old_handler) SetUnhandledExceptionFilter(old_handler);
108 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
112 fpe_handler = handler;
115 ill_handler = handler;
118 segv_handler = handler;
125 #endif /* PLATFORM_WIN32 */
128 #ifdef MONO_USE_EXC_TABLES
130 /*************************************/
131 /* STACK UNWINDING STUFF */
132 /*************************************/
134 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
137 #define DWARF_FRAME_REGISTERS 17
139 typedef struct frame_state
145 long reg_or_offset[DWARF_FRAME_REGISTERS+1];
146 unsigned short cfa_reg;
147 unsigned short retaddr_column;
148 char saved[DWARF_FRAME_REGISTERS+1];
152 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
154 switch (dwarf_regnum) {
172 g_assert_not_reached ();
179 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
181 switch (dwarf_regnum) {
210 g_assert_not_reached ();
214 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
216 static framesf frame_state_for = NULL;
218 static gboolean inited = FALSE;
220 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
222 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
225 init_frame_state_for (void)
230 * There are two versions of __frame_state_for: one in libgcc.a and the
231 * other in glibc.so. We need the version from glibc.
232 * For more info, see this:
233 * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
235 if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
237 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
238 frame_state_for = NULL;
240 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
241 get_backtrace_symbols = NULL;
242 frame_state_for = NULL;
245 g_module_close (module);
251 /* mono_arch_has_unwind_info:
253 * Tests if a function has an DWARF exception table able to restore
254 * all caller saved registers.
257 mono_arch_has_unwind_info (gconstpointer addr)
259 struct frame_state state_in;
260 struct frame_state *res;
263 init_frame_state_for ();
265 if (!frame_state_for)
270 memset (&state_in, 0, sizeof (state_in));
272 /* offset 10 is just a guess, but it works for all methods tested */
273 if ((res = frame_state_for ((char *)addr + 10, &state_in))) {
275 if (res->saved [X86_EBX] == 1 &&
276 res->saved [X86_EDI] == 1 &&
277 res->saved [X86_EBP] == 1 &&
278 res->saved [X86_ESI] == 1)
288 void *return_address;
292 x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx,
293 struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
295 struct stack_frame *frame;
298 struct frame_state state_in;
299 struct frame_state *res;
305 init_frame_state_for ();
307 if (!frame_state_for)
310 frame = MONO_CONTEXT_GET_BP (ctx);
312 max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
316 memset (&state_in, 0, sizeof (state_in));
318 while ((gpointer)frame->next < (gpointer)max_stack) {
319 gpointer ip, addr = frame->return_address;
321 char *tmp, **symbols;
324 ip = MONO_CONTEXT_GET_IP (new_ctx);
325 symbols = get_backtrace_symbols (&ip, 1);
327 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
329 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
336 if ((res = frame_state_for (addr, &state_in))) {
339 cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
340 frame = (struct stack_frame *)((gint8*)cfa - 8);
341 for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
342 int how = res->saved[i];
344 g_assert ((how == 0) || (how == 1));
347 val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
348 set_sigcontext_reg (new_ctx, i, val);
351 new_ctx->SC_ESP = (long)cfa;
353 if (res->saved [X86_EBX] == 1 &&
354 res->saved [X86_EDI] == 1 &&
355 res->saved [X86_EBP] == 1 &&
356 res->saved [X86_ESI] == 1 &&
357 (ji = mono_jit_info_table_find (domain, frame->return_address))) {
358 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
363 //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)));
365 MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
367 MONO_CONTEXT_SET_BP (new_ctx, frame);
369 /* stop if !frame or when we detect an unexpected managed frame */
370 if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
381 //g_assert_not_reached ();
393 * mono_arch_get_restore_context:
395 * Returns a pointer to a method which restores a previously saved sigcontext.
398 mono_arch_get_restore_context (void)
400 static guint8 *start = NULL;
406 /* restore_contect (struct sigcontext *ctx) */
407 /* we do not restore X86_EAX, X86_EDX */
409 start = code = g_malloc (1024);
412 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
414 /* get return address, stored in EDX */
415 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
417 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
419 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
421 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
423 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
425 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
427 /* jump to the saved IP */
428 x86_jump_reg (code, X86_EDX);
434 * mono_arch_get_call_filter:
436 * Returns a pointer to a method which calls an exception filter. We
437 * also use this function to call finally handlers (we pass NULL as
438 * @exc object in this case).
441 mono_arch_get_call_filter (void)
443 static guint8 start [64];
444 static int inited = 0;
451 /* call_filter (struct sigcontext *ctx, unsigned long eip) */
454 x86_push_reg (code, X86_EBP);
455 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
456 x86_push_reg (code, X86_EBX);
457 x86_push_reg (code, X86_EDI);
458 x86_push_reg (code, X86_ESI);
461 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
463 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
465 x86_push_reg (code, X86_EBP);
468 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
469 /* restore registers used by global register allocation (EBX & ESI) */
470 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
471 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
472 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
474 /* call the handler */
475 x86_call_reg (code, X86_ECX);
478 x86_pop_reg (code, X86_EBP);
480 /* restore saved regs */
481 x86_pop_reg (code, X86_ESI);
482 x86_pop_reg (code, X86_EDI);
483 x86_pop_reg (code, X86_EBX);
487 g_assert ((code - start) < 64);
492 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
493 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
494 unsigned long eip, unsigned long esp)
496 static void (*restore_context) (struct sigcontext *);
497 struct sigcontext ctx;
499 if (!restore_context)
500 restore_context = mono_arch_get_restore_context ();
502 /* adjust eip so that it point into the call instruction */
505 /* Pop argument and return address */
506 ctx.SC_ESP = esp + (2 * sizeof (gpointer));
516 mono_arch_handle_exception (&ctx, exc, FALSE);
517 restore_context (&ctx);
519 g_assert_not_reached ();
523 * mono_arch_get_throw_exception:
525 * Returns a function pointer which can be used to raise
526 * exceptions. The returned function has the following
527 * signature: void (*func) (MonoException *exc);
528 * For example to raise an arithmetic exception you can use:
530 * x86_push_imm (code, mono_get_exception_arithmetic ());
531 * x86_call_code (code, arch_get_throw_exception ());
535 mono_arch_get_throw_exception (void)
537 static guint8 start [24];
538 static int inited = 0;
547 x86_push_reg (code, X86_ESP);
548 x86_push_membase (code, X86_ESP, 4); /* IP */
549 x86_push_membase (code, X86_ESP, 12); /* exception */
550 x86_push_reg (code, X86_EBP);
551 x86_push_reg (code, X86_EDI);
552 x86_push_reg (code, X86_ESI);
553 x86_push_reg (code, X86_EBX);
554 x86_push_reg (code, X86_EDX);
555 x86_push_reg (code, X86_ECX);
556 x86_push_reg (code, X86_EAX);
557 x86_call_code (code, throw_exception);
558 /* we should never reach this breakpoint */
559 x86_breakpoint (code);
561 g_assert ((code - start) < 24);
566 * mono_arch_get_throw_exception_by_name:
568 * Returns a function pointer which can be used to raise
569 * corlib exceptions. The returned function has the following
570 * signature: void (*func) (char *exc_name);
571 * For example to raise an arithmetic exception you can use:
573 * x86_push_imm (code, "ArithmeticException");
574 * x86_call_code (code, arch_get_throw_exception_by_name ());
578 mono_arch_get_throw_exception_by_name (void)
580 static guint8 start [32];
581 static int inited = 0;
590 x86_push_membase (code, X86_ESP, 4); /* exception name */
591 x86_push_imm (code, "System");
592 x86_push_imm (code, mono_defaults.exception_class->image);
593 x86_call_code (code, mono_exception_from_name);
594 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
595 /* save the newly create object (overwrite exception name)*/
596 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
597 x86_jump_code (code, mono_arch_get_throw_exception ());
599 g_assert ((code - start) < 32);
604 /* mono_arch_find_jit_info:
606 * This function is used to gather information from @ctx. It return the
607 * MonoJitInfo of the corresponding function, unwinds one stack frame and
608 * stores the resulting context into @new_ctx. It also stores a string
609 * describing the stack location into @trace (if not NULL), and modifies
610 * the @lmf if necessary. @native_offset return the IP offset from the
611 * start of the function or -1 if that info is not available.
614 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
615 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
619 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
621 /* Avoid costly table lookup during stack overflow */
622 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
625 ji = mono_jit_info_table_find (domain, ip);
637 char *source_location, *tmpaddr, *fname;
638 gint32 address, iloffset;
643 address = (char *)ip - (char *)ji->code_start;
646 *native_offset = address;
649 if (!ji->method->wrapper_type)
653 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
654 iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
657 tmpaddr = g_strdup_printf ("<0x%05x>", address);
659 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
661 fname = mono_method_full_name (ji->method, TRUE);
664 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
666 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
669 g_free (source_location);
674 * Some managed methods like pinvoke wrappers might have save_lmf set.
675 * In this case, register save/restore code is not generated by the
676 * JIT, so we have to restore callee saved registers from the lmf.
678 if (ji->method->save_lmf) {
680 * We only need to do this if the exception was raised in managed
681 * code, since otherwise the lmf was already popped of the stack.
683 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
684 new_ctx->SC_ESI = (*lmf)->esi;
685 new_ctx->SC_EDI = (*lmf)->edi;
686 new_ctx->SC_EBX = (*lmf)->ebx;
691 /* restore caller saved registers */
692 if (ji->used_regs & X86_EBX_MASK) {
693 new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
696 if (ji->used_regs & X86_EDI_MASK) {
697 new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
700 if (ji->used_regs & X86_ESI_MASK) {
701 new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
705 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
706 /* remove any unused lmf */
707 *lmf = (*lmf)->previous_lmf;
710 /* Pop EBP and the return address */
711 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
712 /* we substract 1, so that the IP points into the call instruction */
713 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
714 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
718 #ifdef MONO_USE_EXC_TABLES
719 } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
731 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
733 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
736 memset (res, 0, sizeof (MonoJitInfo));
737 res->method = (*lmf)->method;
740 new_ctx->SC_ESI = (*lmf)->esi;
741 new_ctx->SC_EDI = (*lmf)->edi;
742 new_ctx->SC_EBX = (*lmf)->ebx;
743 new_ctx->SC_EBP = (*lmf)->ebp;
744 new_ctx->SC_EIP = (*lmf)->eip;
745 /* the lmf is always stored on the stack, so the following
746 * expression points to a stack location which can be used as ESP */
747 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
749 *lmf = (*lmf)->previous_lmf;
759 * mono_arch_handle_exception:
761 * @ctx: saved processor state
762 * @obj: the exception object
765 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
767 return mono_handle_exception (sigctx, obj, test_only);