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>
23 #include <mono/metadata/mono-debug-debugger.h>
28 #if defined(__FreeBSD__)
33 static MonoW32ExceptionHandler fpe_handler;
34 static MonoW32ExceptionHandler ill_handler;
35 static MonoW32ExceptionHandler segv_handler;
37 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
39 #define W32_SEH_HANDLE_EX(_ex) \
40 if (_ex##_handler) _ex##_handler((int)sctx)
43 * Unhandled Exception Filter
44 * Top-level per-process exception handler.
46 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
50 struct sigcontext* sctx;
53 res = EXCEPTION_CONTINUE_EXECUTION;
55 er = ep->ExceptionRecord;
56 ctx = ep->ContextRecord;
57 sctx = g_malloc(sizeof(struct sigcontext));
59 /* Copy Win32 context to UNIX style context */
70 switch (er->ExceptionCode) {
71 case EXCEPTION_ACCESS_VIOLATION:
72 W32_SEH_HANDLE_EX(segv);
74 case EXCEPTION_ILLEGAL_INSTRUCTION:
75 W32_SEH_HANDLE_EX(ill);
77 case EXCEPTION_INT_DIVIDE_BY_ZERO:
78 case EXCEPTION_INT_OVERFLOW:
79 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
80 case EXCEPTION_FLT_OVERFLOW:
81 case EXCEPTION_FLT_UNDERFLOW:
82 case EXCEPTION_FLT_INEXACT_RESULT:
83 W32_SEH_HANDLE_EX(fpe);
89 /* Copy context back */
103 void win32_seh_init()
105 old_handler = SetUnhandledExceptionFilter(seh_handler);
108 void win32_seh_cleanup()
110 if (old_handler) SetUnhandledExceptionFilter(old_handler);
113 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
117 fpe_handler = handler;
120 ill_handler = handler;
123 segv_handler = handler;
130 #endif /* PLATFORM_WIN32 */
133 * mono_arch_get_restore_context:
135 * Returns a pointer to a method which restores a previously saved sigcontext.
138 mono_arch_get_restore_context (void)
140 static guint8 *start = NULL;
146 /* restore_contect (MonoContext *ctx) */
147 /* we do not restore X86_EAX, X86_EDX */
149 start = code = mono_global_codeman_reserve (128);
152 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
154 /* get return address, stored in EDX */
155 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (MonoContext, eip), 4);
157 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
159 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
161 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
163 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (MonoContext, esp), 4);
165 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
167 /* jump to the saved IP */
168 x86_jump_reg (code, X86_EDX);
174 * mono_arch_get_call_filter:
176 * Returns a pointer to a method which calls an exception filter. We
177 * also use this function to call finally handlers (we pass NULL as
178 * @exc object in this case).
181 mono_arch_get_call_filter (void)
183 static guint8* start;
184 static int inited = 0;
191 /* call_filter (MonoContext *ctx, unsigned long eip) */
192 start = code = mono_global_codeman_reserve (64);
194 x86_push_reg (code, X86_EBP);
195 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
196 x86_push_reg (code, X86_EBX);
197 x86_push_reg (code, X86_EDI);
198 x86_push_reg (code, X86_ESI);
201 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
203 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
205 x86_push_reg (code, X86_EBP);
208 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
209 /* restore registers used by global register allocation (EBX & ESI) */
210 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
211 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
212 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
214 /* call the handler */
215 x86_call_reg (code, X86_ECX);
218 x86_pop_reg (code, X86_EBP);
220 /* restore saved regs */
221 x86_pop_reg (code, X86_ESI);
222 x86_pop_reg (code, X86_EDI);
223 x86_pop_reg (code, X86_EBX);
227 g_assert ((code - start) < 64);
232 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
233 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
234 unsigned long eip, unsigned long esp, gboolean rethrow)
236 static void (*restore_context) (MonoContext *);
239 if (!restore_context)
240 restore_context = mono_arch_get_restore_context ();
242 /* Pop argument and return address */
243 ctx.esp = esp + (2 * sizeof (gpointer));
253 if (mono_debugger_throw_exception ((gpointer)(eip - 5), (gpointer)esp, exc)) {
255 * The debugger wants us to stop on the `throw' instruction.
256 * By the time we get here, it already inserted a breakpoint on
257 * eip - 5 (which is the address of the call).
260 ctx.esp = esp + sizeof (gpointer);
261 restore_context (&ctx);
262 g_assert_not_reached ();
265 /* adjust eip so that it point into the call instruction */
268 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
269 MonoException *mono_ex = (MonoException*)exc;
271 mono_ex->stack_trace = NULL;
273 mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
274 restore_context (&ctx);
276 g_assert_not_reached ();
280 get_throw_exception (gboolean rethrow)
282 guint8 *start, *code;
284 start = code = mono_global_codeman_reserve (64);
286 x86_push_reg (code, X86_ESP);
287 x86_push_membase (code, X86_ESP, 4); /* IP */
288 x86_push_membase (code, X86_ESP, 12); /* exception */
289 x86_push_reg (code, X86_EBP);
290 x86_push_reg (code, X86_EDI);
291 x86_push_reg (code, X86_ESI);
292 x86_push_reg (code, X86_EBX);
293 x86_push_reg (code, X86_EDX);
294 x86_push_reg (code, X86_ECX);
295 x86_push_reg (code, X86_EAX);
296 x86_call_code (code, throw_exception);
297 /* we should never reach this breakpoint */
298 x86_breakpoint (code);
300 g_assert ((code - start) < 64);
306 * mono_arch_get_throw_exception:
308 * Returns a function pointer which can be used to raise
309 * exceptions. The returned function has the following
310 * signature: void (*func) (MonoException *exc);
311 * For example to raise an arithmetic exception you can use:
313 * x86_push_imm (code, mono_get_exception_arithmetic ());
314 * x86_call_code (code, arch_get_throw_exception ());
318 mono_arch_get_throw_exception (void)
320 static guint8 *start;
321 static int inited = 0;
326 start = get_throw_exception (FALSE);
334 mono_arch_get_rethrow_exception (void)
336 static guint8 *start;
337 static int inited = 0;
342 start = get_throw_exception (TRUE);
350 * mono_arch_get_throw_exception_by_name:
352 * Returns a function pointer which can be used to raise
353 * corlib exceptions. The returned function has the following
354 * signature: void (*func) (gpointer ip, char *exc_name);
355 * For example to raise an arithmetic exception you can use:
357 * x86_push_imm (code, "ArithmeticException");
358 * x86_push_imm (code, <IP>)
359 * x86_jump_code (code, arch_get_throw_exception_by_name ());
363 mono_arch_get_throw_exception_by_name (void)
365 static guint8* start;
366 static int inited = 0;
373 code = start = mono_global_codeman_reserve (32);
375 x86_push_membase (code, X86_ESP, 4); /* exception name */
376 x86_push_imm (code, "System");
377 x86_push_imm (code, mono_defaults.exception_class->image);
378 x86_call_code (code, mono_exception_from_name);
379 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
380 /* save the newly create object (overwrite exception name)*/
381 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
382 x86_jump_code (code, mono_arch_get_throw_exception ());
384 g_assert ((code - start) < 32);
390 * mono_arch_get_throw_corlib_exception:
392 * Returns a function pointer which can be used to raise
393 * corlib exceptions. The returned function has the following
394 * signature: void (*func) (guint32 ex_token, guint32 offset);
395 * Here, offset is the offset which needs to be substracted from the caller IP
396 * to get the IP of the throw. Passing the offset has the advantage that it
397 * needs no relocations in the caller.
400 mono_arch_get_throw_corlib_exception (void)
402 static guint8* start;
403 static int inited = 0;
410 code = start = mono_global_codeman_reserve (64);
412 x86_push_membase (code, X86_ESP, 4); /* token */
413 x86_push_imm (code, mono_defaults.exception_class->image);
414 x86_call_code (code, mono_exception_from_token);
415 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
416 /* Compute caller ip */
417 x86_pop_reg (code, X86_ECX);
419 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
420 x86_pop_reg (code, X86_EDX);
421 x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
422 /* Push exception object */
423 x86_push_reg (code, X86_EAX);
425 x86_push_reg (code, X86_ECX);
426 x86_jump_code (code, mono_arch_get_throw_exception ());
428 g_assert ((code - start) < 64);
433 /* mono_arch_find_jit_info:
435 * This function is used to gather information from @ctx. It return the
436 * MonoJitInfo of the corresponding function, unwinds one stack frame and
437 * stores the resulting context into @new_ctx. It also stores a string
438 * describing the stack location into @trace (if not NULL), and modifies
439 * the @lmf if necessary. @native_offset return the IP offset from the
440 * start of the function or -1 if that info is not available.
443 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
444 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
448 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
450 /* Avoid costly table lookup during stack overflow */
451 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
454 ji = mono_jit_info_table_find (domain, ip);
465 if (!ji->method->wrapper_type)
469 * Some managed methods like pinvoke wrappers might have save_lmf set.
470 * In this case, register save/restore code is not generated by the
471 * JIT, so we have to restore callee saved registers from the lmf.
473 if (ji->method->save_lmf) {
475 * We only need to do this if the exception was raised in managed
476 * code, since otherwise the lmf was already popped of the stack.
478 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
479 new_ctx->esi = (*lmf)->esi;
480 new_ctx->edi = (*lmf)->edi;
481 new_ctx->ebx = (*lmf)->ebx;
486 /* restore caller saved registers */
487 if (ji->used_regs & X86_EBX_MASK) {
488 new_ctx->ebx = *((int *)ctx->ebp + offset);
491 if (ji->used_regs & X86_EDI_MASK) {
492 new_ctx->edi = *((int *)ctx->ebp + offset);
495 if (ji->used_regs & X86_ESI_MASK) {
496 new_ctx->esi = *((int *)ctx->ebp + offset);
500 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
501 /* remove any unused lmf */
502 *lmf = (*lmf)->previous_lmf;
505 /* Pop EBP and the return address */
506 new_ctx->esp = ctx->ebp + (2 * sizeof (gpointer));
507 /* we substract 1, so that the IP points into the call instruction */
508 new_ctx->eip = *((int *)ctx->ebp + 1) - 1;
509 new_ctx->ebp = *((int *)ctx->ebp);
511 /* Pop arguments off the stack */
513 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
515 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
516 new_ctx->esp += stack_to_pop;
527 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
529 memset (res, 0, sizeof (MonoJitInfo));
530 res->method = (*lmf)->method;
533 new_ctx->esi = (*lmf)->esi;
534 new_ctx->edi = (*lmf)->edi;
535 new_ctx->ebx = (*lmf)->ebx;
536 new_ctx->ebp = (*lmf)->ebp;
537 new_ctx->eip = (*lmf)->eip;
538 /* the lmf is always stored on the stack, so the following
539 * expression points to a stack location which can be used as ESP */
540 new_ctx->esp = (unsigned long)&((*lmf)->eip);
542 *lmf = (*lmf)->previous_lmf;
544 return ji ? ji : res;
551 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
553 #ifdef MONO_ARCH_USE_SIGACTION
554 ucontext_t *ctx = (ucontext_t*)sigctx;
556 #if defined(__FreeBSD__)
557 mctx->eax = ctx->uc_mcontext.mc_eax;
558 mctx->ebx = ctx->uc_mcontext.mc_ebx;
559 mctx->ecx = ctx->uc_mcontext.mc_ecx;
560 mctx->edx = ctx->uc_mcontext.mc_edx;
561 mctx->ebp = ctx->uc_mcontext.mc_ebp;
562 mctx->esp = ctx->uc_mcontext.mc_esp;
563 mctx->esi = ctx->uc_mcontext.mc_esi;
564 mctx->edi = ctx->uc_mcontext.mc_edi;
565 mctx->eip = ctx->uc_mcontext.mc_eip;
567 mctx->eax = ctx->uc_mcontext.gregs [REG_EAX];
568 mctx->ebx = ctx->uc_mcontext.gregs [REG_EBX];
569 mctx->ecx = ctx->uc_mcontext.gregs [REG_ECX];
570 mctx->edx = ctx->uc_mcontext.gregs [REG_EDX];
571 mctx->ebp = ctx->uc_mcontext.gregs [REG_EBP];
572 mctx->esp = ctx->uc_mcontext.gregs [REG_ESP];
573 mctx->esi = ctx->uc_mcontext.gregs [REG_ESI];
574 mctx->edi = ctx->uc_mcontext.gregs [REG_EDI];
575 mctx->eip = ctx->uc_mcontext.gregs [REG_EIP];
578 struct sigcontext *ctx = (struct sigcontext *)sigctx;
580 mctx->eax = ctx->SC_EAX;
581 mctx->ebx = ctx->SC_EBX;
582 mctx->ecx = ctx->SC_ECX;
583 mctx->edx = ctx->SC_EDX;
584 mctx->ebp = ctx->SC_EBP;
585 mctx->esp = ctx->SC_ESP;
586 mctx->esi = ctx->SC_ESI;
587 mctx->edi = ctx->SC_EDI;
588 mctx->eip = ctx->SC_EIP;
593 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
595 #ifdef MONO_ARCH_USE_SIGACTION
596 ucontext_t *ctx = (ucontext_t*)sigctx;
598 #if defined(__FreeBSD__)
599 ctx->uc_mcontext.mc_eax = mctx->eax;
600 ctx->uc_mcontext.mc_ebx = mctx->ebx;
601 ctx->uc_mcontext.mc_ecx = mctx->ecx;
602 ctx->uc_mcontext.mc_edx = mctx->edx;
603 ctx->uc_mcontext.mc_ebp = mctx->ebp;
604 ctx->uc_mcontext.mc_esp = mctx->esp;
605 ctx->uc_mcontext.mc_esi = mctx->esi;
606 ctx->uc_mcontext.mc_edi = mctx->edi;
607 ctx->uc_mcontext.mc_eip = mctx->eip;
610 ctx->uc_mcontext.gregs [REG_EAX] = mctx->eax;
611 ctx->uc_mcontext.gregs [REG_EBX] = mctx->ebx;
612 ctx->uc_mcontext.gregs [REG_ECX] = mctx->ecx;
613 ctx->uc_mcontext.gregs [REG_EDX] = mctx->edx;
614 ctx->uc_mcontext.gregs [REG_EBP] = mctx->ebp;
615 ctx->uc_mcontext.gregs [REG_ESP] = mctx->esp;
616 ctx->uc_mcontext.gregs [REG_ESI] = mctx->esi;
617 ctx->uc_mcontext.gregs [REG_EDI] = mctx->edi;
618 ctx->uc_mcontext.gregs [REG_EIP] = mctx->eip;
621 struct sigcontext *ctx = (struct sigcontext *)sigctx;
623 ctx->SC_EAX = mctx->eax;
624 ctx->SC_EBX = mctx->ebx;
625 ctx->SC_ECX = mctx->ecx;
626 ctx->SC_EDX = mctx->edx;
627 ctx->SC_EBP = mctx->ebp;
628 ctx->SC_ESP = mctx->esp;
629 ctx->SC_ESI = mctx->esi;
630 ctx->SC_EDI = mctx->edi;
631 ctx->SC_EIP = mctx->eip;
636 mono_arch_ip_from_context (void *sigctx)
638 #ifdef MONO_ARCH_USE_SIGACTION
639 ucontext_t *ctx = (ucontext_t*)sigctx;
640 #if defined(__FreeBSD__)
641 return (gpointer)ctx->uc_mcontext.mc_eip;
643 return (gpointer)ctx->uc_mcontext.gregs [REG_EIP];
646 struct sigcontext *ctx = sigctx;
647 return (gpointer)ctx->SC_EIP;
652 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
656 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
658 mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
660 mono_arch_monoctx_to_sigctx (&mctx, sigctx);