2 * exceptions-amd64.c: exception support for AMD64
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
14 #ifndef PLATFORM_WIN32
15 #include <sys/ucontext.h>
18 #include <mono/arch/amd64/amd64-codegen.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/threads.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/gc-internal.h>
25 #include <mono/metadata/mono-debug.h>
28 #include "mini-amd64.h"
30 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
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)
53 res = EXCEPTION_CONTINUE_EXECUTION;
55 er = ep->ExceptionRecord;
56 ctx = ep->ContextRecord;
57 sctx = g_malloc(sizeof(MonoContext));
59 /* Copy Win32 context to UNIX style context */
74 switch (er->ExceptionCode) {
75 case EXCEPTION_ACCESS_VIOLATION:
76 W32_SEH_HANDLE_EX(segv);
78 case EXCEPTION_ILLEGAL_INSTRUCTION:
79 W32_SEH_HANDLE_EX(ill);
81 case EXCEPTION_INT_DIVIDE_BY_ZERO:
82 case EXCEPTION_INT_OVERFLOW:
83 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
84 case EXCEPTION_FLT_OVERFLOW:
85 case EXCEPTION_FLT_UNDERFLOW:
86 case EXCEPTION_FLT_INEXACT_RESULT:
87 W32_SEH_HANDLE_EX(fpe);
93 /* Copy context back */
100 ctx->Rsi = sctx->rsi;
101 ctx->Rdi = sctx->rdi;
102 ctx->Rip = sctx->rip;
109 void win32_seh_init()
111 old_handler = SetUnhandledExceptionFilter(seh_handler);
114 void win32_seh_cleanup()
116 if (old_handler) SetUnhandledExceptionFilter(old_handler);
119 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
123 fpe_handler = handler;
126 ill_handler = handler;
129 segv_handler = handler;
136 #endif /* PLATFORM_WIN32 */
139 * mono_arch_get_restore_context:
141 * Returns a pointer to a method which restores a previously saved sigcontext.
144 mono_arch_get_restore_context (void)
146 static guint8 *start = NULL;
147 static gboolean inited = FALSE;
153 /* restore_contect (MonoContext *ctx) */
155 start = code = mono_global_codeman_reserve (256);
157 /* get return address */
158 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rip), 8);
160 /* Restore registers */
161 amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
162 amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
163 amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
164 amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
165 amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
166 amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
168 amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rsp), 8);
170 /* jump to the saved IP */
171 amd64_jump_reg (code, AMD64_RAX);
179 * mono_arch_get_call_filter:
181 * Returns a pointer to a method which calls an exception filter. We
182 * also use this function to call finally handlers (we pass NULL as
183 * @exc object in this case).
186 mono_arch_get_call_filter (void)
188 static guint8 *start;
189 static gboolean inited = FALSE;
197 start = code = mono_global_codeman_reserve (64);
199 /* call_filter (MonoContext *ctx, unsigned long eip) */
202 /* Alloc new frame */
203 amd64_push_reg (code, AMD64_RBP);
204 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
206 /* Save callee saved regs */
208 for (i = 0; i < AMD64_NREG; ++i)
209 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
210 amd64_push_reg (code, i);
216 amd64_push_reg (code, AMD64_RBP);
218 /* Make stack misaligned, the call will make it aligned again */
220 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
223 amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
224 /* load callee saved regs */
225 amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
226 amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
227 amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
228 amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
229 amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
231 /* call the handler */
232 amd64_call_reg (code, AMD64_RSI);
235 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
238 amd64_pop_reg (code, AMD64_RBP);
240 /* Restore callee saved regs */
241 for (i = AMD64_NREG; i >= 0; --i)
242 if (AMD64_IS_CALLEE_SAVED_REG (i))
243 amd64_pop_reg (code, i);
248 g_assert ((code - start) < 64);
256 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
257 guint64 rbx, guint64 rbp, guint64 r12, guint64 r13,
258 guint64 r14, guint64 r15, guint64 rethrow)
260 static void (*restore_context) (MonoContext *);
263 if (!restore_context)
264 restore_context = mono_arch_get_restore_context ();
275 if (!rethrow && mono_debugger_throw_exception ((gpointer)(rip - 8), (gpointer)rsp, exc)) {
277 * The debugger wants us to stop on the `throw' instruction.
278 * By the time we get here, it already inserted a breakpoint on
279 * eip - 8 (which is the address of the `mov %r15,%rdi ; callq throw').
284 * In case of a rethrow, the JIT is emitting code like this:
286 * mov 0xffffffffffffffd0(%rbp),%rax'
290 * Here, restore_context() wouldn't restore the %rax register correctly.
294 restore_context (&ctx);
295 g_assert_not_reached ();
298 /* adjust eip so that it point into the call instruction */
301 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
302 MonoException *mono_ex = (MonoException*)exc;
304 mono_ex->stack_trace = NULL;
306 mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
307 restore_context (&ctx);
309 g_assert_not_reached ();
313 get_throw_trampoline (gboolean rethrow)
318 start = code = mono_global_codeman_reserve (64);
323 amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RDI, 8);
325 amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, 0, 8);
327 amd64_lea_membase (code, AMD64_RDX, AMD64_RSP, 8);
328 /* Callee saved regs */
329 amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
330 amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
331 amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
333 amd64_push_imm (code, 0);
335 amd64_push_imm (code, rethrow);
336 amd64_push_reg (code, AMD64_R15);
337 amd64_push_reg (code, AMD64_R14);
338 amd64_push_reg (code, AMD64_R13);
340 amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
341 amd64_call_reg (code, AMD64_R11);
342 amd64_breakpoint (code);
344 g_assert ((code - start) < 64);
350 * mono_arch_get_throw_exception:
352 * Returns a function pointer which can be used to raise
353 * exceptions. The returned function has the following
354 * signature: void (*func) (MonoException *exc);
358 mono_arch_get_throw_exception (void)
360 static guint8* start;
361 static gboolean inited = FALSE;
366 start = get_throw_trampoline (FALSE);
374 mono_arch_get_rethrow_exception (void)
376 static guint8* start;
377 static gboolean inited = FALSE;
382 start = get_throw_trampoline (TRUE);
390 mono_arch_get_throw_exception_by_name (void)
392 static guint8* start;
393 static gboolean inited = FALSE;
399 start = code = mono_global_codeman_reserve (64);
401 /* Not used on amd64 */
402 amd64_breakpoint (code);
408 * mono_arch_get_throw_corlib_exception:
410 * Returns a function pointer which can be used to raise
411 * corlib exceptions. The returned function has the following
412 * signature: void (*func) (guint32 ex_token, guint32 offset);
413 * Here, offset is the offset which needs to be substracted from the caller IP
414 * to get the IP of the throw. Passing the offset has the advantage that it
415 * needs no relocations in the caller.
418 mono_arch_get_throw_corlib_exception (void)
420 static guint8* start;
421 static gboolean inited = FALSE;
428 start = code = mono_global_codeman_reserve (64);
431 amd64_push_reg (code, AMD64_RSI);
433 /* Call exception_from_token */
434 amd64_mov_reg_reg (code, AMD64_RSI, AMD64_RDI, 8);
435 amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
436 amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
437 amd64_call_reg (code, AMD64_R11);
439 /* Compute throw_ip */
440 amd64_pop_reg (code, AMD64_RSI);
442 amd64_pop_reg (code, AMD64_RDX);
443 amd64_alu_reg_reg (code, X86_SUB, AMD64_RDX, AMD64_RSI);
445 /* Put the throw_ip at the top of the misaligned stack */
446 amd64_push_reg (code, AMD64_RDX);
448 throw_ex = (guint64)mono_arch_get_throw_exception ();
450 /* Call throw_exception */
451 amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
452 amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
453 /* The original IP is on the stack */
454 amd64_jump_reg (code, AMD64_R11);
456 g_assert ((code - start) < 64);
463 /* mono_arch_find_jit_info:
465 * This function is used to gather information from @ctx. It return the
466 * MonoJitInfo of the corresponding function, unwinds one stack frame and
467 * stores the resulting context into @new_ctx. It also stores a string
468 * describing the stack location into @trace (if not NULL), and modifies
469 * the @lmf if necessary. @native_offset return the IP offset from the
470 * start of the function or -1 if that info is not available.
473 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
474 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
479 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
481 /* Avoid costly table lookup during stack overflow */
482 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
485 ji = mono_jit_info_table_find (domain, ip);
492 gboolean omit_fp = (ji->used_regs & (1 << 31)) > 0;
497 if (!ji->method->wrapper_type)
501 * Some managed methods like pinvoke wrappers might have save_lmf set.
502 * In this case, register save/restore code is not generated by the
503 * JIT, so we have to restore callee saved registers from the lmf.
505 if (ji->method->save_lmf) {
507 * We only need to do this if the exception was raised in managed
508 * code, since otherwise the lmf was already popped of the stack.
510 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
511 new_ctx->rbp = (*lmf)->ebp;
512 new_ctx->rbx = (*lmf)->rbx;
513 new_ctx->rsp = (*lmf)->rsp;
514 new_ctx->r12 = (*lmf)->r12;
515 new_ctx->r13 = (*lmf)->r13;
516 new_ctx->r14 = (*lmf)->r14;
517 new_ctx->r15 = (*lmf)->r15;
521 offset = omit_fp ? 0 : -1;
522 /* restore caller saved registers */
523 for (i = 0; i < AMD64_NREG; i ++)
524 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
528 reg = *((guint64*)ctx->rsp + offset);
532 reg = *((guint64 *)ctx->rbp + offset);
556 g_assert_not_reached ();
561 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
562 /* remove any unused lmf */
563 *lmf = (*lmf)->previous_lmf;
568 new_ctx->rsp += (ji->used_regs >> 16) & (0x7fff);
569 new_ctx->rip = *((guint64 *)new_ctx->rsp) - 1;
570 /* Pop return address */
574 /* Pop EBP and the return address */
575 new_ctx->rsp = ctx->rbp + (2 * sizeof (gpointer));
576 /* we substract 1, so that the IP points into the call instruction */
577 new_ctx->rip = *((guint64 *)ctx->rbp + 1) - 1;
578 new_ctx->rbp = *((guint64 *)ctx->rbp);
581 /* Pop arguments off the stack */
583 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
585 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
586 new_ctx->rsp += stack_to_pop;
594 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
599 /* Trampoline lmf frame */
600 memset (res, 0, sizeof (MonoJitInfo));
601 res->method = (*lmf)->method;
604 new_ctx->rip = (*lmf)->rip;
605 new_ctx->rbp = (*lmf)->ebp;
606 new_ctx->rsp = (*lmf)->rsp;
608 new_ctx->rbx = (*lmf)->rbx;
609 new_ctx->r12 = (*lmf)->r12;
610 new_ctx->r13 = (*lmf)->r13;
611 new_ctx->r14 = (*lmf)->r14;
612 new_ctx->r15 = (*lmf)->r15;
614 *lmf = (*lmf)->previous_lmf;
616 return ji ? ji : res;
623 * mono_arch_handle_exception:
625 * @ctx: saved processor state
626 * @obj: the exception object
629 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
633 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
635 mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
637 mono_arch_monoctx_to_sigctx (&mctx, sigctx);
642 #ifdef MONO_ARCH_USE_SIGACTION
643 static inline guint64*
644 gregs_from_ucontext (ucontext_t *ctx)
647 guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
649 guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
656 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
658 #ifdef MONO_ARCH_USE_SIGACTION
659 ucontext_t *ctx = (ucontext_t*)sigctx;
661 guint64 *gregs = gregs_from_ucontext (ctx);
663 mctx->rax = gregs [REG_RAX];
664 mctx->rbx = gregs [REG_RBX];
665 mctx->rcx = gregs [REG_RCX];
666 mctx->rdx = gregs [REG_RDX];
667 mctx->rbp = gregs [REG_RBP];
668 mctx->rsp = gregs [REG_RSP];
669 mctx->rsi = gregs [REG_RSI];
670 mctx->rdi = gregs [REG_RDI];
671 mctx->rip = gregs [REG_RIP];
672 mctx->r12 = gregs [REG_R12];
673 mctx->r13 = gregs [REG_R13];
674 mctx->r14 = gregs [REG_R14];
675 mctx->r15 = gregs [REG_R15];
677 MonoContext *ctx = (MonoContext *)sigctx;
679 mctx->rax = ctx->rax;
680 mctx->rbx = ctx->rbx;
681 mctx->rcx = ctx->rcx;
682 mctx->rdx = ctx->rdx;
683 mctx->rbp = ctx->rbp;
684 mctx->rsp = ctx->rsp;
685 mctx->rsi = ctx->rsi;
686 mctx->rdi = ctx->rdi;
687 mctx->rip = ctx->rip;
688 mctx->r12 = ctx->r12;
689 mctx->r13 = ctx->r13;
690 mctx->r14 = ctx->r14;
691 mctx->r15 = ctx->r15;
696 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
698 #ifdef MONO_ARCH_USE_SIGACTION
699 ucontext_t *ctx = (ucontext_t*)sigctx;
701 guint64 *gregs = gregs_from_ucontext (ctx);
703 gregs [REG_RAX] = mctx->rax;
704 gregs [REG_RBX] = mctx->rbx;
705 gregs [REG_RCX] = mctx->rcx;
706 gregs [REG_RDX] = mctx->rdx;
707 gregs [REG_RBP] = mctx->rbp;
708 gregs [REG_RSP] = mctx->rsp;
709 gregs [REG_RSI] = mctx->rsi;
710 gregs [REG_RDI] = mctx->rdi;
711 gregs [REG_RIP] = mctx->rip;
712 gregs [REG_R12] = mctx->r12;
713 gregs [REG_R13] = mctx->r13;
714 gregs [REG_R14] = mctx->r14;
715 gregs [REG_R15] = mctx->r15;
717 MonoContext *ctx = (MonoContext *)sigctx;
719 ctx->rax = mctx->rax;
720 ctx->rbx = mctx->rbx;
721 ctx->rcx = mctx->rcx;
722 ctx->rdx = mctx->rdx;
723 ctx->rbp = mctx->rbp;
724 ctx->rsp = mctx->rsp;
725 ctx->rsi = mctx->rsi;
726 ctx->rdi = mctx->rdi;
727 ctx->rip = mctx->rip;
728 ctx->r12 = mctx->r12;
729 ctx->r13 = mctx->r13;
730 ctx->r14 = mctx->r14;
731 ctx->r15 = mctx->r15;
736 mono_arch_ip_from_context (void *sigctx)
739 #ifdef MONO_ARCH_USE_SIGACTION
741 ucontext_t *ctx = (ucontext_t*)sigctx;
743 guint64 *gregs = gregs_from_ucontext (ctx);
745 return (gpointer)gregs [REG_RIP];
747 MonoContext *ctx = sigctx;
748 return (gpointer)ctx->rip;