2008-01-15 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_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rip), 8);
160
161         /* Restore registers */
162         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
163         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
164         amd64_mov_reg_membase (code, AMD64_R12, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, r12), 8);
165         amd64_mov_reg_membase (code, AMD64_R13, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, r13), 8);
166         amd64_mov_reg_membase (code, AMD64_R14, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, r14), 8);
167         amd64_mov_reg_membase (code, AMD64_R15, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, r15), 8);
168 #ifdef PLATFORM_WIN32
169         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
170         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
171 #endif
172
173         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
174
175         /* jump to the saved IP */
176         amd64_jump_reg (code, AMD64_RAX);
177
178         inited = TRUE;
179
180         return start;
181 }
182
183 /*
184  * mono_arch_get_call_filter:
185  *
186  * Returns a pointer to a method which calls an exception filter. We
187  * also use this function to call finally handlers (we pass NULL as 
188  * @exc object in this case).
189  */
190 gpointer
191 mono_arch_get_call_filter (void)
192 {
193         static guint8 *start;
194         static gboolean inited = FALSE;
195         int i;
196         guint8 *code;
197         guint32 pos;
198
199         if (inited)
200                 return start;
201
202         start = code = mono_global_codeman_reserve (128);
203
204         /* call_filter (MonoContext *ctx, unsigned long eip) */
205         code = start;
206
207         /* Alloc new frame */
208         amd64_push_reg (code, AMD64_RBP);
209         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
210
211         /* Save callee saved regs */
212         pos = 0;
213         for (i = 0; i < AMD64_NREG; ++i)
214                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
215                         amd64_push_reg (code, i);
216                         pos += 8;
217                 }
218
219         /* Save EBP */
220         pos += 8;
221         amd64_push_reg (code, AMD64_RBP);
222
223         /* Make stack misaligned, the call will make it aligned again */
224         if (! (pos & 8))
225                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
226
227         /* set new EBP */
228         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbp), 8);
229         /* load callee saved regs */
230         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbx), 8);
231         amd64_mov_reg_membase (code, AMD64_R12, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r12), 8);
232         amd64_mov_reg_membase (code, AMD64_R13, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r13), 8);
233         amd64_mov_reg_membase (code, AMD64_R14, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r14), 8);
234         amd64_mov_reg_membase (code, AMD64_R15, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r15), 8);
235 #ifdef PLATFORM_WIN32
236         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
237         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
238 #endif
239
240         /* call the handler */
241         amd64_call_reg (code, AMD64_ARG_REG2);
242
243         if (! (pos & 8))
244                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
245
246         /* restore RBP */
247         amd64_pop_reg (code, AMD64_RBP);
248
249         /* Restore callee saved regs */
250         for (i = AMD64_NREG; i >= 0; --i)
251                 if (AMD64_IS_CALLEE_SAVED_REG (i))
252                         amd64_pop_reg (code, i);
253
254         amd64_leave (code);
255         amd64_ret (code);
256
257         g_assert ((code - start) < 128);
258
259         inited = TRUE;
260
261         return start;
262 }
263 #ifdef PLATFORM_WIN32
264 static void
265 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
266                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
267                  guint64 r14, guint64 r15, guint64 rdi, guint64 rsi, guint64 rethrow)
268 #else
269 static void
270 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
271                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
272                  guint64 r14, guint64 r15, guint64 rethrow)
273 #endif
274 {
275         static void (*restore_context) (MonoContext *);
276         MonoContext ctx;
277
278         if (!restore_context)
279                 restore_context = mono_arch_get_restore_context ();
280
281         ctx.rsp = rsp;
282         ctx.rip = rip;
283         ctx.rbx = rbx;
284         ctx.rbp = rbp;
285         ctx.r12 = r12;
286         ctx.r13 = r13;
287         ctx.r14 = r14;
288         ctx.r15 = r15;
289 #ifdef PLATFORM_WIN32
290         ctx.rdi = rdi;
291         ctx.rsi = rsi;
292 #endif
293
294         if (!rethrow && mono_debugger_throw_exception ((gpointer)(rip - 8), (gpointer)rsp, exc)) {
295                 /*
296                  * The debugger wants us to stop on the `throw' instruction.
297                  * By the time we get here, it already inserted a breakpoint on
298                  * eip - 8 (which is the address of the `mov %r15,%rdi ; callq throw').
299                  */
300
301                 /* FIXME FIXME
302                  *
303                  * In case of a rethrow, the JIT is emitting code like this:
304                  *
305                  *    mov    0xffffffffffffffd0(%rbp),%rax'
306                  *    mov    %rax,%rdi
307                  *    callq  throw
308                  *
309                  * Here, restore_context() wouldn't restore the %rax register correctly.
310                  */
311                 ctx.rip = rip - 8;
312                 ctx.rsp = rsp + 8;
313                 restore_context (&ctx);
314                 g_assert_not_reached ();
315         }
316
317         /* adjust eip so that it point into the call instruction */
318         ctx.rip -= 1;
319
320         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
321                 MonoException *mono_ex = (MonoException*)exc;
322                 if (!rethrow)
323                         mono_ex->stack_trace = NULL;
324         }
325         mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
326         restore_context (&ctx);
327
328         g_assert_not_reached ();
329 }
330
331 static gpointer
332 get_throw_trampoline (gboolean rethrow)
333 {
334         guint8* start;
335         guint8 *code;
336
337         start = code = mono_global_codeman_reserve (64);
338
339         code = start;
340
341         /* Exception */
342         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_ARG_REG1, 8);
343         /* IP */
344         amd64_mov_reg_membase (code, AMD64_ARG_REG2, AMD64_RSP, 0, 8);
345         /* SP */
346         amd64_lea_membase (code, AMD64_ARG_REG3, AMD64_RSP, 8);
347
348 #ifdef PLATFORM_WIN32
349         /* Callee saved regs */
350         amd64_mov_reg_reg (code, AMD64_R9, AMD64_RBX, 8);
351         /* reverse order */
352         amd64_push_imm (code, rethrow);
353         amd64_push_reg (code, AMD64_RSI);
354         amd64_push_reg (code, AMD64_RDI);
355         amd64_push_reg (code, AMD64_R15);
356         amd64_push_reg (code, AMD64_R14);
357         amd64_push_reg (code, AMD64_R13);
358         amd64_push_reg (code, AMD64_R12);
359         amd64_push_reg (code, AMD64_RBP);
360         /* align stack */
361         amd64_push_imm (code, 0);
362         amd64_push_imm (code, 0);
363         amd64_push_imm (code, 0);
364         amd64_push_imm (code, 0);
365 #else
366         /* Callee saved regs */
367         amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
368         amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
369         amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
370         /* align stack */
371         amd64_push_imm (code, 0);
372         /* reverse order */
373         amd64_push_imm (code, rethrow);
374         amd64_push_reg (code, AMD64_R15);
375         amd64_push_reg (code, AMD64_R14);
376         amd64_push_reg (code, AMD64_R13);
377 #endif
378
379         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
380         amd64_call_reg (code, AMD64_R11);
381         amd64_breakpoint (code);
382
383         g_assert ((code - start) < 64);
384
385         return start;
386 }
387
388 /**
389  * mono_arch_get_throw_exception:
390  *
391  * Returns a function pointer which can be used to raise 
392  * exceptions. The returned function has the following 
393  * signature: void (*func) (MonoException *exc); 
394  *
395  */
396 gpointer 
397 mono_arch_get_throw_exception (void)
398 {
399         static guint8* start;
400         static gboolean inited = FALSE;
401
402         if (inited)
403                 return start;
404
405         start = get_throw_trampoline (FALSE);
406
407         inited = TRUE;
408
409         return start;
410 }
411
412 gpointer 
413 mono_arch_get_rethrow_exception (void)
414 {
415         static guint8* start;
416         static gboolean inited = FALSE;
417
418         if (inited)
419                 return start;
420
421         start = get_throw_trampoline (TRUE);
422
423         inited = TRUE;
424
425         return start;
426 }
427
428 gpointer 
429 mono_arch_get_throw_exception_by_name (void)
430 {       
431         static guint8* start;
432         static gboolean inited = FALSE;
433         guint8 *code;
434
435         if (inited)
436                 return start;
437
438         start = code = mono_global_codeman_reserve (64);
439
440         /* Not used on amd64 */
441         amd64_breakpoint (code);
442
443         return start;
444 }
445
446 /**
447  * mono_arch_get_throw_corlib_exception:
448  *
449  * Returns a function pointer which can be used to raise 
450  * corlib exceptions. The returned function has the following 
451  * signature: void (*func) (guint32 ex_token, guint32 offset); 
452  * Here, offset is the offset which needs to be substracted from the caller IP 
453  * to get the IP of the throw. Passing the offset has the advantage that it 
454  * needs no relocations in the caller.
455  */
456 gpointer 
457 mono_arch_get_throw_corlib_exception (void)
458 {
459         static guint8* start;
460         static gboolean inited = FALSE;
461         guint8 *code;
462         guint64 throw_ex;
463
464         if (inited)
465                 return start;
466
467         start = code = mono_global_codeman_reserve (64);
468
469         /* Push throw_ip */
470         amd64_push_reg (code, AMD64_ARG_REG2);
471
472         /* Call exception_from_token */
473         amd64_mov_reg_reg (code, AMD64_ARG_REG2, AMD64_ARG_REG1, 8);
474         amd64_mov_reg_imm (code, AMD64_ARG_REG1, mono_defaults.exception_class->image);
475         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
476         amd64_call_reg (code, AMD64_R11);
477
478         /* Compute throw_ip */
479         amd64_pop_reg (code, AMD64_ARG_REG2);
480         /* return addr */
481         amd64_pop_reg (code, AMD64_ARG_REG3);
482         amd64_alu_reg_reg (code, X86_SUB, AMD64_ARG_REG3, AMD64_ARG_REG2);
483
484         /* Put the throw_ip at the top of the misaligned stack */
485         amd64_push_reg (code, AMD64_ARG_REG3);
486
487         throw_ex = (guint64)mono_arch_get_throw_exception ();
488
489         /* Call throw_exception */
490         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_RAX, 8);
491         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
492         /* The original IP is on the stack */
493         amd64_jump_reg (code, AMD64_R11);
494
495         g_assert ((code - start) < 64);
496
497         inited = TRUE;
498
499         return start;
500 }
501
502 /* mono_arch_find_jit_info:
503  *
504  * This function is used to gather information from @ctx. It return the 
505  * MonoJitInfo of the corresponding function, unwinds one stack frame and
506  * stores the resulting context into @new_ctx. It also stores a string 
507  * describing the stack location into @trace (if not NULL), and modifies
508  * the @lmf if necessary. @native_offset return the IP offset from the 
509  * start of the function or -1 if that info is not available.
510  */
511 MonoJitInfo *
512 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
513                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
514                          gboolean *managed)
515 {
516         MonoJitInfo *ji;
517         int i;
518         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
519
520         /* Avoid costly table lookup during stack overflow */
521         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
522                 ji = prev_ji;
523         else
524                 ji = mono_jit_info_table_find (domain, ip);
525
526         if (managed)
527                 *managed = FALSE;
528
529         *new_ctx = *ctx;
530
531         if (ji != NULL) {
532                 int offset;
533                 gboolean omit_fp = (ji->used_regs & (1 << 31)) > 0;
534
535                 if (managed)
536                         if (!ji->method->wrapper_type)
537                                 *managed = TRUE;
538
539                 /*
540                  * If a method has save_lmf set, then register save/restore code is not generated 
541                  * by the JIT, so we have to restore callee saved registers from the lmf.
542                  */
543                 if (ji->method->save_lmf) {
544                         MonoLMF *lmf_addr;
545
546                         /* 
547                          * *lmf might not point to the LMF pushed by this method, so compute the LMF
548                          * address instead.
549                          */
550                         if (omit_fp)
551                                 lmf_addr = (MonoLMF*)ctx->rsp;
552                         else
553                                 lmf_addr = (MonoLMF*)(ctx->rbp - sizeof (MonoLMF));
554
555                         new_ctx->rbp = lmf_addr->rbp;
556                         new_ctx->rbx = lmf_addr->rbx;
557                         new_ctx->r12 = lmf_addr->r12;
558                         new_ctx->r13 = lmf_addr->r13;
559                         new_ctx->r14 = lmf_addr->r14;
560                         new_ctx->r15 = lmf_addr->r15;
561                 }
562                 else {
563                         offset = omit_fp ? 0 : -1;
564                         /* restore caller saved registers */
565                         for (i = 0; i < AMD64_NREG; i ++)
566                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
567                                         guint64 reg;
568
569                                         if (omit_fp) {
570                                                 reg = *((guint64*)ctx->rsp + offset);
571                                                 offset ++;
572                                         }
573                                         else {
574                                                 reg = *((guint64 *)ctx->rbp + offset);
575                                                 offset --;
576                                         }
577
578                                         switch (i) {
579                                         case AMD64_RBX:
580                                                 new_ctx->rbx = reg;
581                                                 break;
582                                         case AMD64_R12:
583                                                 new_ctx->r12 = reg;
584                                                 break;
585                                         case AMD64_R13:
586                                                 new_ctx->r13 = reg;
587                                                 break;
588                                         case AMD64_R14:
589                                                 new_ctx->r14 = reg;
590                                                 break;
591                                         case AMD64_R15:
592                                                 new_ctx->r15 = reg;
593                                                 break;
594                                         case AMD64_RBP:
595                                                 new_ctx->rbp = reg;
596                                                 break;
597                                         default:
598                                                 g_assert_not_reached ();
599                                         }
600                                 }
601                 }
602
603                 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
604                         /* remove any unused lmf */
605                         *lmf = (*lmf)->previous_lmf;
606                 }
607
608                 if (omit_fp) {
609                         /* Pop frame */
610                         new_ctx->rsp += (ji->used_regs >> 16) & (0x7fff);
611                         new_ctx->rip = *((guint64 *)new_ctx->rsp) - 1;
612                         /* Pop return address */
613                         new_ctx->rsp += 8;
614                 }
615                 else {
616                         /* Pop EBP and the return address */
617                         new_ctx->rsp = ctx->rbp + (2 * sizeof (gpointer));
618                         /* we substract 1, so that the IP points into the call instruction */
619                         new_ctx->rip = *((guint64 *)ctx->rbp + 1) - 1;
620                         new_ctx->rbp = *((guint64 *)ctx->rbp);
621                 }
622
623                 /* Pop arguments off the stack */
624                 {
625                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
626
627                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
628                         new_ctx->rsp += stack_to_pop;
629                 }
630
631                 return ji;
632         } else if (*lmf) {
633                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
634                 } else {
635                         if (!(*lmf)->method)
636                                 /* Top LMF entry */
637                                 return (gpointer)-1;
638                         /* Trampoline lmf frame */
639                         memset (res, 0, sizeof (MonoJitInfo));
640                         res->method = (*lmf)->method;
641                 }
642
643                 new_ctx->rip = (*lmf)->rip;
644                 new_ctx->rbp = (*lmf)->rbp;
645                 new_ctx->rsp = (*lmf)->rsp;
646
647                 new_ctx->rbx = (*lmf)->rbx;
648                 new_ctx->r12 = (*lmf)->r12;
649                 new_ctx->r13 = (*lmf)->r13;
650                 new_ctx->r14 = (*lmf)->r14;
651                 new_ctx->r15 = (*lmf)->r15;
652
653                 *lmf = (*lmf)->previous_lmf;
654
655                 return ji ? ji : res;
656         }
657
658         return NULL;
659 }
660
661 /**
662  * mono_arch_handle_exception:
663  *
664  * @ctx: saved processor state
665  * @obj: the exception object
666  */
667 gboolean
668 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
669 {
670         MonoContext mctx;
671
672         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
673
674         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
675
676         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
677
678         return TRUE;
679 }
680
681 #ifdef MONO_ARCH_USE_SIGACTION
682 static inline guint64*
683 gregs_from_ucontext (ucontext_t *ctx)
684 {
685 #ifdef __FreeBSD__
686     guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
687 #else
688     guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
689 #endif
690
691         return gregs;
692 }
693 #endif
694 void
695 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
696 {
697 #ifdef MONO_ARCH_USE_SIGACTION
698         ucontext_t *ctx = (ucontext_t*)sigctx;
699
700     guint64 *gregs = gregs_from_ucontext (ctx);
701
702         mctx->rax = gregs [REG_RAX];
703         mctx->rbx = gregs [REG_RBX];
704         mctx->rcx = gregs [REG_RCX];
705         mctx->rdx = gregs [REG_RDX];
706         mctx->rbp = gregs [REG_RBP];
707         mctx->rsp = gregs [REG_RSP];
708         mctx->rsi = gregs [REG_RSI];
709         mctx->rdi = gregs [REG_RDI];
710         mctx->rip = gregs [REG_RIP];
711         mctx->r12 = gregs [REG_R12];
712         mctx->r13 = gregs [REG_R13];
713         mctx->r14 = gregs [REG_R14];
714         mctx->r15 = gregs [REG_R15];
715 #else
716         MonoContext *ctx = (MonoContext *)sigctx;
717
718         mctx->rax = ctx->rax;
719         mctx->rbx = ctx->rbx;
720         mctx->rcx = ctx->rcx;
721         mctx->rdx = ctx->rdx;
722         mctx->rbp = ctx->rbp;
723         mctx->rsp = ctx->rsp;
724         mctx->rsi = ctx->rsi;
725         mctx->rdi = ctx->rdi;
726         mctx->rip = ctx->rip;
727         mctx->r12 = ctx->r12;
728         mctx->r13 = ctx->r13;
729         mctx->r14 = ctx->r14;
730         mctx->r15 = ctx->r15;
731 #endif
732 }
733
734 void
735 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
736 {
737 #ifdef MONO_ARCH_USE_SIGACTION
738         ucontext_t *ctx = (ucontext_t*)sigctx;
739
740     guint64 *gregs = gregs_from_ucontext (ctx);
741
742         gregs [REG_RAX] = mctx->rax;
743         gregs [REG_RBX] = mctx->rbx;
744         gregs [REG_RCX] = mctx->rcx;
745         gregs [REG_RDX] = mctx->rdx;
746         gregs [REG_RBP] = mctx->rbp;
747         gregs [REG_RSP] = mctx->rsp;
748         gregs [REG_RSI] = mctx->rsi;
749         gregs [REG_RDI] = mctx->rdi;
750         gregs [REG_RIP] = mctx->rip;
751         gregs [REG_R12] = mctx->r12;
752         gregs [REG_R13] = mctx->r13;
753         gregs [REG_R14] = mctx->r14;
754         gregs [REG_R15] = mctx->r15;
755 #else
756         MonoContext *ctx = (MonoContext *)sigctx;
757
758         ctx->rax = mctx->rax;
759         ctx->rbx = mctx->rbx;
760         ctx->rcx = mctx->rcx;
761         ctx->rdx = mctx->rdx;
762         ctx->rbp = mctx->rbp;
763         ctx->rsp = mctx->rsp;
764         ctx->rsi = mctx->rsi;
765         ctx->rdi = mctx->rdi;
766         ctx->rip = mctx->rip;
767         ctx->r12 = mctx->r12;
768         ctx->r13 = mctx->r13;
769         ctx->r14 = mctx->r14;
770         ctx->r15 = mctx->r15;
771 #endif
772 }
773
774 gpointer
775 mono_arch_ip_from_context (void *sigctx)
776 {
777         
778 #ifdef MONO_ARCH_USE_SIGACTION
779
780         ucontext_t *ctx = (ucontext_t*)sigctx;
781
782     guint64 *gregs = gregs_from_ucontext (ctx);
783
784         return (gpointer)gregs [REG_RIP];
785 #else
786         MonoContext *ctx = sigctx;
787         return (gpointer)ctx->rip;
788 #endif  
789 }
790
791 static void
792 restore_soft_guard_pages (void)
793 {
794         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
795         if (jit_tls->stack_ovf_guard_base)
796                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
797 }
798
799 /* 
800  * this function modifies mctx so that when it is restored, it
801  * won't execcute starting at mctx.eip, but in a function that
802  * will restore the protection on the soft-guard pages and return back to
803  * continue at mctx.eip.
804  */
805 static void
806 prepare_for_guard_pages (MonoContext *mctx)
807 {
808         gpointer *sp;
809         sp = (gpointer)(mctx->rsp);
810         sp -= 1;
811         /* the return addr */
812         sp [0] = (gpointer)(mctx->rip);
813         mctx->rip = (unsigned long)restore_soft_guard_pages;
814         mctx->rsp = (unsigned long)sp;
815 }
816
817 static void
818 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
819 {
820         void (*restore_context) (MonoContext *);
821         MonoContext mctx;
822
823         restore_context = mono_arch_get_restore_context ();
824         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
825         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
826         if (stack_ovf)
827                 prepare_for_guard_pages (&mctx);
828         restore_context (&mctx);
829 }
830
831 void
832 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
833 {
834 #ifdef MONO_ARCH_USE_SIGACTION
835         MonoException *exc = NULL;
836         ucontext_t *ctx = (ucontext_t*)sigctx;
837         guint64 *gregs = gregs_from_ucontext (ctx);
838         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
839         gpointer *sp;
840         int frame_size;
841
842         if (stack_ovf)
843                 exc = mono_domain_get ()->stack_overflow_ex;
844         if (!ji)
845                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
846
847         /* setup a call frame on the real stack so that control is returned there
848          * and exception handling can continue.
849          * The frame looks like:
850          *   ucontext struct
851          *   ...
852          *   return ip
853          * 128 is the size of the red zone
854          */
855         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
856         frame_size += 15;
857         frame_size &= ~15;
858         sp = (gpointer)(gregs [REG_RSP] & ~15);
859         sp = (gpointer)((char*)sp - frame_size);
860         /* the arguments must be aligned */
861         sp [-1] = (gpointer)gregs [REG_RIP];
862         /* may need to adjust pointers in the new struct copy, depending on the OS */
863         memcpy (sp + 4, ctx, sizeof (ucontext_t));
864         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
865         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
866         gregs [REG_RSP] = (unsigned long)(sp - 1);
867         gregs [REG_RDI] = (unsigned long)(sp + 4);
868         gregs [REG_RSI] = (guint64)exc;
869         gregs [REG_RDX] = stack_ovf;
870 #endif
871 }
872