2007-08-11 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / exceptions-amd64.c
1 /*
2  * exceptions-amd64.c: exception support for AMD64
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <signal.h>
13 #include <string.h>
14 #ifndef PLATFORM_WIN32
15 #include <sys/ucontext.h>
16 #endif
17
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>
26 #include <mono/utils/mono-mmap.h>
27
28 #include "mini.h"
29 #include "mini-amd64.h"
30
31 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
32
33 #ifdef PLATFORM_WIN32
34 static MonoW32ExceptionHandler fpe_handler;
35 static MonoW32ExceptionHandler ill_handler;
36 static MonoW32ExceptionHandler segv_handler;
37
38 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
39
40 #define W32_SEH_HANDLE_EX(_ex) \
41         if (_ex##_handler) _ex##_handler((int)sctx)
42
43 /*
44  * Unhandled Exception Filter
45  * Top-level per-process exception handler.
46  */
47 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
48 {
49         EXCEPTION_RECORD* er;
50         CONTEXT* ctx;
51         MonoContext* sctx;
52         LONG res;
53
54         res = EXCEPTION_CONTINUE_EXECUTION;
55
56         er = ep->ExceptionRecord;
57         ctx = ep->ContextRecord;
58         sctx = g_malloc(sizeof(MonoContext));
59
60         /* Copy Win32 context to UNIX style context */
61         sctx->rax = ctx->Rax;
62         sctx->rbx = ctx->Rbx;
63         sctx->rcx = ctx->Rcx;
64         sctx->rdx = ctx->Rdx;
65         sctx->rbp = ctx->Rbp;
66         sctx->rsp = ctx->Rsp;
67         sctx->rsi = ctx->Rsi;
68         sctx->rdi = ctx->Rdi;
69         sctx->rip = ctx->Rip;
70         sctx->r12 = ctx->R12;
71         sctx->r13 = ctx->R13;
72         sctx->r14 = ctx->R14;
73         sctx->r15 = ctx->R15;
74
75         switch (er->ExceptionCode) {
76         case EXCEPTION_ACCESS_VIOLATION:
77                 W32_SEH_HANDLE_EX(segv);
78                 break;
79         case EXCEPTION_ILLEGAL_INSTRUCTION:
80                 W32_SEH_HANDLE_EX(ill);
81                 break;
82         case EXCEPTION_INT_DIVIDE_BY_ZERO:
83         case EXCEPTION_INT_OVERFLOW:
84         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
85         case EXCEPTION_FLT_OVERFLOW:
86         case EXCEPTION_FLT_UNDERFLOW:
87         case EXCEPTION_FLT_INEXACT_RESULT:
88                 W32_SEH_HANDLE_EX(fpe);
89                 break;
90         default:
91                 break;
92         }
93
94         /* Copy context back */
95         ctx->Rax = sctx->rax;
96         ctx->Rbx = sctx->rbx;
97         ctx->Rcx = sctx->rcx;
98         ctx->Rdx = sctx->rdx;
99         ctx->Rbp = sctx->rbp;
100         ctx->Rsp = sctx->rsp;
101         ctx->Rsi = sctx->rsi;
102         ctx->Rdi = sctx->rdi;
103         ctx->Rip = sctx->rip;
104
105         g_free (sctx);
106
107         return res;
108 }
109
110 void win32_seh_init()
111 {
112         old_handler = SetUnhandledExceptionFilter(seh_handler);
113 }
114
115 void win32_seh_cleanup()
116 {
117         if (old_handler) SetUnhandledExceptionFilter(old_handler);
118 }
119
120 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
121 {
122         switch (type) {
123         case SIGFPE:
124                 fpe_handler = handler;
125                 break;
126         case SIGILL:
127                 ill_handler = handler;
128                 break;
129         case SIGSEGV:
130                 segv_handler = handler;
131                 break;
132         default:
133                 break;
134         }
135 }
136
137 #endif /* PLATFORM_WIN32 */
138
139 /*
140  * mono_arch_get_restore_context:
141  *
142  * Returns a pointer to a method which restores a previously saved sigcontext.
143  */
144 gpointer
145 mono_arch_get_restore_context (void)
146 {
147         static guint8 *start = NULL;
148         static gboolean inited = FALSE;
149         guint8 *code;
150
151         if (inited)
152                 return start;
153
154         /* restore_contect (MonoContext *ctx) */
155
156         start = code = mono_global_codeman_reserve (256);
157
158         /* get return address */
159         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rip), 8);
160
161         /* Restore registers */
162         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
163         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
164         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r12), 8);
165         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r13), 8);
166         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r14), 8);
167         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r15), 8);
168
169         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
170
171         /* jump to the saved IP */
172         amd64_jump_reg (code, AMD64_RAX);
173
174         inited = TRUE;
175
176         return start;
177 }
178
179 /*
180  * mono_arch_get_call_filter:
181  *
182  * Returns a pointer to a method which calls an exception filter. We
183  * also use this function to call finally handlers (we pass NULL as 
184  * @exc object in this case).
185  */
186 gpointer
187 mono_arch_get_call_filter (void)
188 {
189         static guint8 *start;
190         static gboolean inited = FALSE;
191         int i;
192         guint8 *code;
193         guint32 pos;
194
195         if (inited)
196                 return start;
197
198         start = code = mono_global_codeman_reserve (64);
199
200         /* call_filter (MonoContext *ctx, unsigned long eip) */
201         code = start;
202
203         /* Alloc new frame */
204         amd64_push_reg (code, AMD64_RBP);
205         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
206
207         /* Save callee saved regs */
208         pos = 0;
209         for (i = 0; i < AMD64_NREG; ++i)
210                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
211                         amd64_push_reg (code, i);
212                         pos += 8;
213                 }
214
215         /* Save EBP */
216         pos += 8;
217         amd64_push_reg (code, AMD64_RBP);
218
219         /* Make stack misaligned, the call will make it aligned again */
220         if (! (pos & 8))
221                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
222
223         /* set new EBP */
224         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
225         /* load callee saved regs */
226         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
227         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
228         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
229         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
230         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
231
232         /* call the handler */
233         amd64_call_reg (code, AMD64_RSI);
234
235         if (! (pos & 8))
236                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
237
238         /* restore RBP */
239         amd64_pop_reg (code, AMD64_RBP);
240
241         /* Restore callee saved regs */
242         for (i = AMD64_NREG; i >= 0; --i)
243                 if (AMD64_IS_CALLEE_SAVED_REG (i))
244                         amd64_pop_reg (code, i);
245
246         amd64_leave (code);
247         amd64_ret (code);
248
249         g_assert ((code - start) < 64);
250
251         inited = TRUE;
252
253         return start;
254 }
255
256 static void
257 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
258                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
259                  guint64 r14, guint64 r15, guint64 rethrow)
260 {
261         static void (*restore_context) (MonoContext *);
262         MonoContext ctx;
263
264         if (!restore_context)
265                 restore_context = mono_arch_get_restore_context ();
266
267         ctx.rsp = rsp;
268         ctx.rip = rip;
269         ctx.rbx = rbx;
270         ctx.rbp = rbp;
271         ctx.r12 = r12;
272         ctx.r13 = r13;
273         ctx.r14 = r14;
274         ctx.r15 = r15;
275
276         if (!rethrow && mono_debugger_throw_exception ((gpointer)(rip - 8), (gpointer)rsp, exc)) {
277                 /*
278                  * The debugger wants us to stop on the `throw' instruction.
279                  * By the time we get here, it already inserted a breakpoint on
280                  * eip - 8 (which is the address of the `mov %r15,%rdi ; callq throw').
281                  */
282
283                 /* FIXME FIXME
284                  *
285                  * In case of a rethrow, the JIT is emitting code like this:
286                  *
287                  *    mov    0xffffffffffffffd0(%rbp),%rax'
288                  *    mov    %rax,%rdi
289                  *    callq  throw
290                  *
291                  * Here, restore_context() wouldn't restore the %rax register correctly.
292                  */
293                 ctx.rip = rip - 8;
294                 ctx.rsp = rsp + 8;
295                 restore_context (&ctx);
296                 g_assert_not_reached ();
297         }
298
299         /* adjust eip so that it point into the call instruction */
300         ctx.rip -= 1;
301
302         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
303                 MonoException *mono_ex = (MonoException*)exc;
304                 if (!rethrow)
305                         mono_ex->stack_trace = NULL;
306         }
307         mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
308         restore_context (&ctx);
309
310         g_assert_not_reached ();
311 }
312
313 static gpointer
314 get_throw_trampoline (gboolean rethrow)
315 {
316         guint8* start;
317         guint8 *code;
318
319         start = code = mono_global_codeman_reserve (64);
320
321         code = start;
322
323         /* Exception */
324         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RDI, 8);
325         /* IP */
326         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, 0, 8);
327         /* SP */
328         amd64_lea_membase (code, AMD64_RDX, AMD64_RSP, 8);
329         /* Callee saved regs */
330         amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
331         amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
332         amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
333         /* align stack */
334         amd64_push_imm (code, 0);
335         /* reverse order */
336         amd64_push_imm (code, rethrow);
337         amd64_push_reg (code, AMD64_R15);
338         amd64_push_reg (code, AMD64_R14);
339         amd64_push_reg (code, AMD64_R13);
340
341         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
342         amd64_call_reg (code, AMD64_R11);
343         amd64_breakpoint (code);
344
345         g_assert ((code - start) < 64);
346
347         return start;
348 }
349
350 /**
351  * mono_arch_get_throw_exception:
352  *
353  * Returns a function pointer which can be used to raise 
354  * exceptions. The returned function has the following 
355  * signature: void (*func) (MonoException *exc); 
356  *
357  */
358 gpointer 
359 mono_arch_get_throw_exception (void)
360 {
361         static guint8* start;
362         static gboolean inited = FALSE;
363
364         if (inited)
365                 return start;
366
367         start = get_throw_trampoline (FALSE);
368
369         inited = TRUE;
370
371         return start;
372 }
373
374 gpointer 
375 mono_arch_get_rethrow_exception (void)
376 {
377         static guint8* start;
378         static gboolean inited = FALSE;
379
380         if (inited)
381                 return start;
382
383         start = get_throw_trampoline (TRUE);
384
385         inited = TRUE;
386
387         return start;
388 }
389
390 gpointer 
391 mono_arch_get_throw_exception_by_name (void)
392 {       
393         static guint8* start;
394         static gboolean inited = FALSE;
395         guint8 *code;
396
397         if (inited)
398                 return start;
399
400         start = code = mono_global_codeman_reserve (64);
401
402         /* Not used on amd64 */
403         amd64_breakpoint (code);
404
405         return start;
406 }
407
408 /**
409  * mono_arch_get_throw_corlib_exception:
410  *
411  * Returns a function pointer which can be used to raise 
412  * corlib exceptions. The returned function has the following 
413  * signature: void (*func) (guint32 ex_token, guint32 offset); 
414  * Here, offset is the offset which needs to be substracted from the caller IP 
415  * to get the IP of the throw. Passing the offset has the advantage that it 
416  * needs no relocations in the caller.
417  */
418 gpointer 
419 mono_arch_get_throw_corlib_exception (void)
420 {
421         static guint8* start;
422         static gboolean inited = FALSE;
423         guint8 *code;
424         guint64 throw_ex;
425
426         if (inited)
427                 return start;
428
429         start = code = mono_global_codeman_reserve (64);
430
431         /* Push throw_ip */
432         amd64_push_reg (code, AMD64_RSI);
433
434         /* Call exception_from_token */
435         amd64_mov_reg_reg (code, AMD64_RSI, AMD64_RDI, 8);
436         amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
437         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
438         amd64_call_reg (code, AMD64_R11);
439
440         /* Compute throw_ip */
441         amd64_pop_reg (code, AMD64_RSI);
442         /* return addr */
443         amd64_pop_reg (code, AMD64_RDX);
444         amd64_alu_reg_reg (code, X86_SUB, AMD64_RDX, AMD64_RSI);
445
446         /* Put the throw_ip at the top of the misaligned stack */
447         amd64_push_reg (code, AMD64_RDX);
448
449         throw_ex = (guint64)mono_arch_get_throw_exception ();
450
451         /* Call throw_exception */
452         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
453         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
454         /* The original IP is on the stack */
455         amd64_jump_reg (code, AMD64_R11);
456
457         g_assert ((code - start) < 64);
458
459         inited = TRUE;
460
461         return start;
462 }
463
464 /* mono_arch_find_jit_info:
465  *
466  * This function is used to gather information from @ctx. It return the 
467  * MonoJitInfo of the corresponding function, unwinds one stack frame and
468  * stores the resulting context into @new_ctx. It also stores a string 
469  * describing the stack location into @trace (if not NULL), and modifies
470  * the @lmf if necessary. @native_offset return the IP offset from the 
471  * start of the function or -1 if that info is not available.
472  */
473 MonoJitInfo *
474 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
475                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
476                          gboolean *managed)
477 {
478         MonoJitInfo *ji;
479         int i;
480         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
481
482         /* Avoid costly table lookup during stack overflow */
483         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
484                 ji = prev_ji;
485         else
486                 ji = mono_jit_info_table_find (domain, ip);
487
488         if (managed)
489                 *managed = FALSE;
490
491         if (ji != NULL) {
492                 int offset;
493                 gboolean omit_fp = (ji->used_regs & (1 << 31)) > 0;
494
495                 *new_ctx = *ctx;
496
497                 if (managed)
498                         if (!ji->method->wrapper_type)
499                                 *managed = TRUE;
500
501                 /*
502                  * Some managed methods like pinvoke wrappers might have save_lmf set.
503                  * In this case, register save/restore code is not generated by the 
504                  * JIT, so we have to restore callee saved registers from the lmf.
505                  */
506                 if (ji->method->save_lmf) {
507                         /* 
508                          * We only need to do this if the exception was raised in managed
509                          * code, since otherwise the lmf was already popped of the stack.
510                          */
511                         if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
512                                 new_ctx->rbp = (*lmf)->ebp;
513                                 new_ctx->rbx = (*lmf)->rbx;
514                                 new_ctx->rsp = (*lmf)->rsp;
515                                 new_ctx->r12 = (*lmf)->r12;
516                                 new_ctx->r13 = (*lmf)->r13;
517                                 new_ctx->r14 = (*lmf)->r14;
518                                 new_ctx->r15 = (*lmf)->r15;
519                         }
520                 }
521                 else {
522                         offset = omit_fp ? 0 : -1;
523                         /* restore caller saved registers */
524                         for (i = 0; i < AMD64_NREG; i ++)
525                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
526                                         guint64 reg;
527
528                                         if (omit_fp) {
529                                                 reg = *((guint64*)ctx->rsp + offset);
530                                                 offset ++;
531                                         }
532                                         else {
533                                                 reg = *((guint64 *)ctx->rbp + offset);
534                                                 offset --;
535                                         }
536
537                                         switch (i) {
538                                         case AMD64_RBX:
539                                                 new_ctx->rbx = reg;
540                                                 break;
541                                         case AMD64_R12:
542                                                 new_ctx->r12 = reg;
543                                                 break;
544                                         case AMD64_R13:
545                                                 new_ctx->r13 = reg;
546                                                 break;
547                                         case AMD64_R14:
548                                                 new_ctx->r14 = reg;
549                                                 break;
550                                         case AMD64_R15:
551                                                 new_ctx->r15 = reg;
552                                                 break;
553                                         case AMD64_RBP:
554                                                 new_ctx->rbp = reg;
555                                                 break;
556                                         default:
557                                                 g_assert_not_reached ();
558                                         }
559                                 }
560                 }
561
562                 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
563                         /* remove any unused lmf */
564                         *lmf = (*lmf)->previous_lmf;
565                 }
566
567                 if (omit_fp) {
568                         /* Pop frame */
569                         new_ctx->rsp += (ji->used_regs >> 16) & (0x7fff);
570                         new_ctx->rip = *((guint64 *)new_ctx->rsp) - 1;
571                         /* Pop return address */
572                         new_ctx->rsp += 8;
573                 }
574                 else {
575                         /* Pop EBP and the return address */
576                         new_ctx->rsp = ctx->rbp + (2 * sizeof (gpointer));
577                         /* we substract 1, so that the IP points into the call instruction */
578                         new_ctx->rip = *((guint64 *)ctx->rbp + 1) - 1;
579                         new_ctx->rbp = *((guint64 *)ctx->rbp);
580                 }
581
582                 /* Pop arguments off the stack */
583                 {
584                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
585
586                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
587                         new_ctx->rsp += stack_to_pop;
588                 }
589
590                 return ji;
591         } else if (*lmf) {
592                 
593                 *new_ctx = *ctx;
594
595                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
596                 } else {
597                         if (!(*lmf)->method)
598                                 /* Top LMF entry */
599                                 return (gpointer)-1;
600                         /* Trampoline lmf frame */
601                         memset (res, 0, sizeof (MonoJitInfo));
602                         res->method = (*lmf)->method;
603                 }
604
605                 new_ctx->rip = (*lmf)->rip;
606                 new_ctx->rbp = (*lmf)->ebp;
607                 new_ctx->rsp = (*lmf)->rsp;
608
609                 new_ctx->rbx = (*lmf)->rbx;
610                 new_ctx->r12 = (*lmf)->r12;
611                 new_ctx->r13 = (*lmf)->r13;
612                 new_ctx->r14 = (*lmf)->r14;
613                 new_ctx->r15 = (*lmf)->r15;
614
615                 *lmf = (*lmf)->previous_lmf;
616
617                 return ji ? ji : res;
618         }
619
620         return NULL;
621 }
622
623 /**
624  * mono_arch_handle_exception:
625  *
626  * @ctx: saved processor state
627  * @obj: the exception object
628  */
629 gboolean
630 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
631 {
632         MonoContext mctx;
633
634         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
635
636         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
637
638         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
639
640         return TRUE;
641 }
642
643 #ifdef MONO_ARCH_USE_SIGACTION
644 static inline guint64*
645 gregs_from_ucontext (ucontext_t *ctx)
646 {
647 #ifdef __FreeBSD__
648     guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
649 #else
650     guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
651 #endif
652
653         return gregs;
654 }
655 #endif
656 void
657 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
658 {
659 #ifdef MONO_ARCH_USE_SIGACTION
660         ucontext_t *ctx = (ucontext_t*)sigctx;
661
662     guint64 *gregs = gregs_from_ucontext (ctx);
663
664         mctx->rax = gregs [REG_RAX];
665         mctx->rbx = gregs [REG_RBX];
666         mctx->rcx = gregs [REG_RCX];
667         mctx->rdx = gregs [REG_RDX];
668         mctx->rbp = gregs [REG_RBP];
669         mctx->rsp = gregs [REG_RSP];
670         mctx->rsi = gregs [REG_RSI];
671         mctx->rdi = gregs [REG_RDI];
672         mctx->rip = gregs [REG_RIP];
673         mctx->r12 = gregs [REG_R12];
674         mctx->r13 = gregs [REG_R13];
675         mctx->r14 = gregs [REG_R14];
676         mctx->r15 = gregs [REG_R15];
677 #else
678         MonoContext *ctx = (MonoContext *)sigctx;
679
680         mctx->rax = ctx->rax;
681         mctx->rbx = ctx->rbx;
682         mctx->rcx = ctx->rcx;
683         mctx->rdx = ctx->rdx;
684         mctx->rbp = ctx->rbp;
685         mctx->rsp = ctx->rsp;
686         mctx->rsi = ctx->rsi;
687         mctx->rdi = ctx->rdi;
688         mctx->rip = ctx->rip;
689         mctx->r12 = ctx->r12;
690         mctx->r13 = ctx->r13;
691         mctx->r14 = ctx->r14;
692         mctx->r15 = ctx->r15;
693 #endif
694 }
695
696 void
697 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
698 {
699 #ifdef MONO_ARCH_USE_SIGACTION
700         ucontext_t *ctx = (ucontext_t*)sigctx;
701
702     guint64 *gregs = gregs_from_ucontext (ctx);
703
704         gregs [REG_RAX] = mctx->rax;
705         gregs [REG_RBX] = mctx->rbx;
706         gregs [REG_RCX] = mctx->rcx;
707         gregs [REG_RDX] = mctx->rdx;
708         gregs [REG_RBP] = mctx->rbp;
709         gregs [REG_RSP] = mctx->rsp;
710         gregs [REG_RSI] = mctx->rsi;
711         gregs [REG_RDI] = mctx->rdi;
712         gregs [REG_RIP] = mctx->rip;
713         gregs [REG_R12] = mctx->r12;
714         gregs [REG_R13] = mctx->r13;
715         gregs [REG_R14] = mctx->r14;
716         gregs [REG_R15] = mctx->r15;
717 #else
718         MonoContext *ctx = (MonoContext *)sigctx;
719
720         ctx->rax = mctx->rax;
721         ctx->rbx = mctx->rbx;
722         ctx->rcx = mctx->rcx;
723         ctx->rdx = mctx->rdx;
724         ctx->rbp = mctx->rbp;
725         ctx->rsp = mctx->rsp;
726         ctx->rsi = mctx->rsi;
727         ctx->rdi = mctx->rdi;
728         ctx->rip = mctx->rip;
729         ctx->r12 = mctx->r12;
730         ctx->r13 = mctx->r13;
731         ctx->r14 = mctx->r14;
732         ctx->r15 = mctx->r15;
733 #endif
734 }
735
736 gpointer
737 mono_arch_ip_from_context (void *sigctx)
738 {
739         
740 #ifdef MONO_ARCH_USE_SIGACTION
741
742         ucontext_t *ctx = (ucontext_t*)sigctx;
743
744     guint64 *gregs = gregs_from_ucontext (ctx);
745
746         return (gpointer)gregs [REG_RIP];
747 #else
748         MonoContext *ctx = sigctx;
749         return (gpointer)ctx->rip;
750 #endif  
751 }
752
753 static void
754 restore_soft_guard_pages (void)
755 {
756         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
757         if (jit_tls->stack_ovf_guard_base)
758                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
759 }
760
761 /* 
762  * this function modifies mctx so that when it is restored, it
763  * won't execcute starting at mctx.eip, but in a function that
764  * will restore the protection on the soft-guard pages and return back to
765  * continue at mctx.eip.
766  */
767 static void
768 prepare_for_guard_pages (MonoContext *mctx)
769 {
770         gpointer *sp;
771         sp = (gpointer)(mctx->rsp);
772         sp -= 1;
773         /* the return addr */
774         sp [0] = (gpointer)(mctx->rip);
775         mctx->rip = (unsigned long)restore_soft_guard_pages;
776         mctx->rsp = (unsigned long)sp;
777 }
778
779 static void
780 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
781 {
782         void (*restore_context) (MonoContext *);
783         MonoContext mctx;
784
785         restore_context = mono_arch_get_restore_context ();
786         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
787         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
788         if (stack_ovf)
789                 prepare_for_guard_pages (&mctx);
790         restore_context (&mctx);
791 }
792
793 void
794 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
795 {
796 #ifdef MONO_ARCH_USE_SIGACTION
797         MonoException *exc = NULL;
798         ucontext_t *ctx = (ucontext_t*)sigctx;
799         guint64 *gregs = gregs_from_ucontext (ctx);
800         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
801         gpointer *sp;
802         int frame_size;
803
804         if (stack_ovf)
805                 exc = mono_domain_get ()->stack_overflow_ex;
806         if (!ji)
807                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
808
809         /* setup a call frame on the real stack so that control is returned there
810          * and exception handling can continue.
811          * The frame looks like:
812          *   ucontext struct
813          *   ...
814          *   return ip
815          * 128 is the size of the red zone
816          */
817         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
818         frame_size += 15;
819         frame_size &= ~15;
820         sp = (gpointer)(gregs [REG_RSP] & ~15);
821         sp = (gpointer)((char*)sp - frame_size);
822         /* the arguments must be aligned */
823         sp [-1] = (gpointer)gregs [REG_RIP];
824         /* may need to adjust pointers in the new struct copy, depending on the OS */
825         memcpy (sp + 4, ctx, sizeof (ucontext_t));
826         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
827         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
828         gregs [REG_RSP] = (unsigned long)(sp - 1);
829         gregs [REG_RDI] = (unsigned long)(sp + 4);
830         gregs [REG_RSI] = (guint64)exc;
831         gregs [REG_RDX] = stack_ovf;
832 #endif
833 }
834