2008-01-14 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                  * Some managed methods like pinvoke wrappers might have save_lmf set.
541                  * In this case, register save/restore code is not generated by the 
542                  * JIT, so we have to restore callee saved registers from the lmf.
543                  */
544                 if (ji->method->save_lmf) {
545                         gboolean lmf_match;
546
547                         /*
548                          * If the exception was raised in unmanaged code, then the LMF was already
549                          * popped off the stack. We detect that case by comparing the sp (or bp)
550                          * value in the LMF with the one in the context. This works because these
551                          * registers are saved to the LMF at the start of the method.
552                          */
553                         if (omit_fp)
554                                 lmf_match = (*lmf) && (*lmf)->rsp == ctx->rsp;
555                         else
556                                 lmf_match = (*lmf) && (*lmf)->rbp == ctx->rbp;
557
558                         if (*lmf && lmf_match) {
559                                 /* Make sure the LMF belongs to this method */
560                                 g_assert ((*lmf)->rip >= (guint64)ji->code_start && (*lmf)->rip <= ((guint64)((guint8*)ji->code_start + ji->code_size)));
561
562                                 new_ctx->rbp = (*lmf)->rbp;
563                                 new_ctx->rbx = (*lmf)->rbx;
564                                 new_ctx->rsp = (*lmf)->rsp;
565                                 new_ctx->r12 = (*lmf)->r12;
566                                 new_ctx->r13 = (*lmf)->r13;
567                                 new_ctx->r14 = (*lmf)->r14;
568                                 new_ctx->r15 = (*lmf)->r15;
569
570                                 *lmf = (*lmf)->previous_lmf;
571                         }
572                 }
573                 else {
574                         offset = omit_fp ? 0 : -1;
575                         /* restore caller saved registers */
576                         for (i = 0; i < AMD64_NREG; i ++)
577                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
578                                         guint64 reg;
579
580                                         if (omit_fp) {
581                                                 reg = *((guint64*)ctx->rsp + offset);
582                                                 offset ++;
583                                         }
584                                         else {
585                                                 reg = *((guint64 *)ctx->rbp + offset);
586                                                 offset --;
587                                         }
588
589                                         switch (i) {
590                                         case AMD64_RBX:
591                                                 new_ctx->rbx = reg;
592                                                 break;
593                                         case AMD64_R12:
594                                                 new_ctx->r12 = reg;
595                                                 break;
596                                         case AMD64_R13:
597                                                 new_ctx->r13 = reg;
598                                                 break;
599                                         case AMD64_R14:
600                                                 new_ctx->r14 = reg;
601                                                 break;
602                                         case AMD64_R15:
603                                                 new_ctx->r15 = reg;
604                                                 break;
605                                         case AMD64_RBP:
606                                                 new_ctx->rbp = reg;
607                                                 break;
608                                         default:
609                                                 g_assert_not_reached ();
610                                         }
611                                 }
612                 }
613
614                 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
615                         /* remove any unused lmf */
616                         *lmf = (*lmf)->previous_lmf;
617                 }
618
619                 if (omit_fp) {
620                         /* Pop frame */
621                         new_ctx->rsp += (ji->used_regs >> 16) & (0x7fff);
622                         new_ctx->rip = *((guint64 *)new_ctx->rsp) - 1;
623                         /* Pop return address */
624                         new_ctx->rsp += 8;
625                 }
626                 else {
627                         /* Pop EBP and the return address */
628                         new_ctx->rsp = ctx->rbp + (2 * sizeof (gpointer));
629                         /* we substract 1, so that the IP points into the call instruction */
630                         new_ctx->rip = *((guint64 *)ctx->rbp + 1) - 1;
631                         new_ctx->rbp = *((guint64 *)ctx->rbp);
632                 }
633
634                 /* Pop arguments off the stack */
635                 {
636                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
637
638                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
639                         new_ctx->rsp += stack_to_pop;
640                 }
641
642                 return ji;
643         } else if (*lmf) {
644                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
645                 } else {
646                         if (!(*lmf)->method)
647                                 /* Top LMF entry */
648                                 return (gpointer)-1;
649                         /* Trampoline lmf frame */
650                         memset (res, 0, sizeof (MonoJitInfo));
651                         res->method = (*lmf)->method;
652                 }
653
654                 new_ctx->rip = (*lmf)->rip;
655                 new_ctx->rbp = (*lmf)->rbp;
656                 new_ctx->rsp = (*lmf)->rsp;
657
658                 new_ctx->rbx = (*lmf)->rbx;
659                 new_ctx->r12 = (*lmf)->r12;
660                 new_ctx->r13 = (*lmf)->r13;
661                 new_ctx->r14 = (*lmf)->r14;
662                 new_ctx->r15 = (*lmf)->r15;
663
664                 *lmf = (*lmf)->previous_lmf;
665
666                 return ji ? ji : res;
667         }
668
669         return NULL;
670 }
671
672 /**
673  * mono_arch_handle_exception:
674  *
675  * @ctx: saved processor state
676  * @obj: the exception object
677  */
678 gboolean
679 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
680 {
681         MonoContext mctx;
682
683         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
684
685         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
686
687         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
688
689         return TRUE;
690 }
691
692 #ifdef MONO_ARCH_USE_SIGACTION
693 static inline guint64*
694 gregs_from_ucontext (ucontext_t *ctx)
695 {
696 #ifdef __FreeBSD__
697     guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
698 #else
699     guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
700 #endif
701
702         return gregs;
703 }
704 #endif
705 void
706 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
707 {
708 #ifdef MONO_ARCH_USE_SIGACTION
709         ucontext_t *ctx = (ucontext_t*)sigctx;
710
711     guint64 *gregs = gregs_from_ucontext (ctx);
712
713         mctx->rax = gregs [REG_RAX];
714         mctx->rbx = gregs [REG_RBX];
715         mctx->rcx = gregs [REG_RCX];
716         mctx->rdx = gregs [REG_RDX];
717         mctx->rbp = gregs [REG_RBP];
718         mctx->rsp = gregs [REG_RSP];
719         mctx->rsi = gregs [REG_RSI];
720         mctx->rdi = gregs [REG_RDI];
721         mctx->rip = gregs [REG_RIP];
722         mctx->r12 = gregs [REG_R12];
723         mctx->r13 = gregs [REG_R13];
724         mctx->r14 = gregs [REG_R14];
725         mctx->r15 = gregs [REG_R15];
726 #else
727         MonoContext *ctx = (MonoContext *)sigctx;
728
729         mctx->rax = ctx->rax;
730         mctx->rbx = ctx->rbx;
731         mctx->rcx = ctx->rcx;
732         mctx->rdx = ctx->rdx;
733         mctx->rbp = ctx->rbp;
734         mctx->rsp = ctx->rsp;
735         mctx->rsi = ctx->rsi;
736         mctx->rdi = ctx->rdi;
737         mctx->rip = ctx->rip;
738         mctx->r12 = ctx->r12;
739         mctx->r13 = ctx->r13;
740         mctx->r14 = ctx->r14;
741         mctx->r15 = ctx->r15;
742 #endif
743 }
744
745 void
746 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
747 {
748 #ifdef MONO_ARCH_USE_SIGACTION
749         ucontext_t *ctx = (ucontext_t*)sigctx;
750
751     guint64 *gregs = gregs_from_ucontext (ctx);
752
753         gregs [REG_RAX] = mctx->rax;
754         gregs [REG_RBX] = mctx->rbx;
755         gregs [REG_RCX] = mctx->rcx;
756         gregs [REG_RDX] = mctx->rdx;
757         gregs [REG_RBP] = mctx->rbp;
758         gregs [REG_RSP] = mctx->rsp;
759         gregs [REG_RSI] = mctx->rsi;
760         gregs [REG_RDI] = mctx->rdi;
761         gregs [REG_RIP] = mctx->rip;
762         gregs [REG_R12] = mctx->r12;
763         gregs [REG_R13] = mctx->r13;
764         gregs [REG_R14] = mctx->r14;
765         gregs [REG_R15] = mctx->r15;
766 #else
767         MonoContext *ctx = (MonoContext *)sigctx;
768
769         ctx->rax = mctx->rax;
770         ctx->rbx = mctx->rbx;
771         ctx->rcx = mctx->rcx;
772         ctx->rdx = mctx->rdx;
773         ctx->rbp = mctx->rbp;
774         ctx->rsp = mctx->rsp;
775         ctx->rsi = mctx->rsi;
776         ctx->rdi = mctx->rdi;
777         ctx->rip = mctx->rip;
778         ctx->r12 = mctx->r12;
779         ctx->r13 = mctx->r13;
780         ctx->r14 = mctx->r14;
781         ctx->r15 = mctx->r15;
782 #endif
783 }
784
785 gpointer
786 mono_arch_ip_from_context (void *sigctx)
787 {
788         
789 #ifdef MONO_ARCH_USE_SIGACTION
790
791         ucontext_t *ctx = (ucontext_t*)sigctx;
792
793     guint64 *gregs = gregs_from_ucontext (ctx);
794
795         return (gpointer)gregs [REG_RIP];
796 #else
797         MonoContext *ctx = sigctx;
798         return (gpointer)ctx->rip;
799 #endif  
800 }
801
802 static void
803 restore_soft_guard_pages (void)
804 {
805         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
806         if (jit_tls->stack_ovf_guard_base)
807                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
808 }
809
810 /* 
811  * this function modifies mctx so that when it is restored, it
812  * won't execcute starting at mctx.eip, but in a function that
813  * will restore the protection on the soft-guard pages and return back to
814  * continue at mctx.eip.
815  */
816 static void
817 prepare_for_guard_pages (MonoContext *mctx)
818 {
819         gpointer *sp;
820         sp = (gpointer)(mctx->rsp);
821         sp -= 1;
822         /* the return addr */
823         sp [0] = (gpointer)(mctx->rip);
824         mctx->rip = (unsigned long)restore_soft_guard_pages;
825         mctx->rsp = (unsigned long)sp;
826 }
827
828 static void
829 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
830 {
831         void (*restore_context) (MonoContext *);
832         MonoContext mctx;
833
834         restore_context = mono_arch_get_restore_context ();
835         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
836         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
837         if (stack_ovf)
838                 prepare_for_guard_pages (&mctx);
839         restore_context (&mctx);
840 }
841
842 void
843 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
844 {
845 #ifdef MONO_ARCH_USE_SIGACTION
846         MonoException *exc = NULL;
847         ucontext_t *ctx = (ucontext_t*)sigctx;
848         guint64 *gregs = gregs_from_ucontext (ctx);
849         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
850         gpointer *sp;
851         int frame_size;
852
853         if (stack_ovf)
854                 exc = mono_domain_get ()->stack_overflow_ex;
855         if (!ji)
856                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
857
858         /* setup a call frame on the real stack so that control is returned there
859          * and exception handling can continue.
860          * The frame looks like:
861          *   ucontext struct
862          *   ...
863          *   return ip
864          * 128 is the size of the red zone
865          */
866         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
867         frame_size += 15;
868         frame_size &= ~15;
869         sp = (gpointer)(gregs [REG_RSP] & ~15);
870         sp = (gpointer)((char*)sp - frame_size);
871         /* the arguments must be aligned */
872         sp [-1] = (gpointer)gregs [REG_RIP];
873         /* may need to adjust pointers in the new struct copy, depending on the OS */
874         memcpy (sp + 4, ctx, sizeof (ucontext_t));
875         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
876         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
877         gregs [REG_RSP] = (unsigned long)(sp - 1);
878         gregs [REG_RDI] = (unsigned long)(sp + 4);
879         gregs [REG_RSI] = (guint64)exc;
880         gregs [REG_RDX] = stack_ovf;
881 #endif
882 }
883