2008-04-19 Jonathan Chambers <joncham@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/threads-types.h>
23 #include <mono/metadata/debug-helpers.h>
24 #include <mono/metadata/exception.h>
25 #include <mono/metadata/gc-internal.h>
26 #include <mono/metadata/mono-debug.h>
27 #include <mono/utils/mono-mmap.h>
28
29 #include "mini.h"
30 #include "mini-amd64.h"
31
32 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
33
34 #ifdef PLATFORM_WIN32
35 static MonoW32ExceptionHandler fpe_handler;
36 static MonoW32ExceptionHandler ill_handler;
37 static MonoW32ExceptionHandler segv_handler;
38
39 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
40
41 #define W32_SEH_HANDLE_EX(_ex) \
42         if (_ex##_handler) _ex##_handler((int)sctx)
43
44 /*
45  * Unhandled Exception Filter
46  * Top-level per-process exception handler.
47  */
48 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
49 {
50         EXCEPTION_RECORD* er;
51         CONTEXT* ctx;
52         MonoContext* sctx;
53         LONG res;
54
55         res = EXCEPTION_CONTINUE_EXECUTION;
56
57         er = ep->ExceptionRecord;
58         ctx = ep->ContextRecord;
59         sctx = g_malloc(sizeof(MonoContext));
60
61         /* Copy Win32 context to UNIX style context */
62         sctx->rax = ctx->Rax;
63         sctx->rbx = ctx->Rbx;
64         sctx->rcx = ctx->Rcx;
65         sctx->rdx = ctx->Rdx;
66         sctx->rbp = ctx->Rbp;
67         sctx->rsp = ctx->Rsp;
68         sctx->rsi = ctx->Rsi;
69         sctx->rdi = ctx->Rdi;
70         sctx->rip = ctx->Rip;
71         sctx->r12 = ctx->R12;
72         sctx->r13 = ctx->R13;
73         sctx->r14 = ctx->R14;
74         sctx->r15 = ctx->R15;
75
76         switch (er->ExceptionCode) {
77         case EXCEPTION_ACCESS_VIOLATION:
78                 W32_SEH_HANDLE_EX(segv);
79                 break;
80         case EXCEPTION_ILLEGAL_INSTRUCTION:
81                 W32_SEH_HANDLE_EX(ill);
82                 break;
83         case EXCEPTION_INT_DIVIDE_BY_ZERO:
84         case EXCEPTION_INT_OVERFLOW:
85         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
86         case EXCEPTION_FLT_OVERFLOW:
87         case EXCEPTION_FLT_UNDERFLOW:
88         case EXCEPTION_FLT_INEXACT_RESULT:
89                 W32_SEH_HANDLE_EX(fpe);
90                 break;
91         default:
92                 break;
93         }
94
95         /* Copy context back */
96         ctx->Rax = sctx->rax;
97         ctx->Rbx = sctx->rbx;
98         ctx->Rcx = sctx->rcx;
99         ctx->Rdx = sctx->rdx;
100         ctx->Rbp = sctx->rbp;
101         ctx->Rsp = sctx->rsp;
102         ctx->Rsi = sctx->rsi;
103         ctx->Rdi = sctx->rdi;
104         ctx->Rip = sctx->rip;
105
106         g_free (sctx);
107
108         return res;
109 }
110
111 void win32_seh_init()
112 {
113         old_handler = SetUnhandledExceptionFilter(seh_handler);
114 }
115
116 void win32_seh_cleanup()
117 {
118         if (old_handler) SetUnhandledExceptionFilter(old_handler);
119 }
120
121 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
122 {
123         switch (type) {
124         case SIGFPE:
125                 fpe_handler = handler;
126                 break;
127         case SIGILL:
128                 ill_handler = handler;
129                 break;
130         case SIGSEGV:
131                 segv_handler = handler;
132                 break;
133         default:
134                 break;
135         }
136 }
137
138 #endif /* PLATFORM_WIN32 */
139
140 /*
141  * mono_arch_get_restore_context:
142  *
143  * Returns a pointer to a method which restores a previously saved sigcontext.
144  */
145 gpointer
146 mono_arch_get_restore_context (void)
147 {
148         static guint8 *start = NULL;
149         static gboolean inited = FALSE;
150         guint8 *code;
151
152         if (inited)
153                 return start;
154
155         /* restore_contect (MonoContext *ctx) */
156
157         start = code = mono_global_codeman_reserve (256);
158
159         amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);
160
161         /* Restore all registers except %rip and %r11 */
162         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rax), 8);
163         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rcx), 8);
164         amd64_mov_reg_membase (code, AMD64_RDX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rdx), 8);
165         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
166         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
167         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
168         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
169         //amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r8), 8);
170         //amd64_mov_reg_membase (code, AMD64_R9, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r9), 8);
171         //amd64_mov_reg_membase (code, AMD64_R10, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r10), 8);
172         amd64_mov_reg_membase (code, AMD64_R12, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r12), 8);
173         amd64_mov_reg_membase (code, AMD64_R13, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r13), 8);
174         amd64_mov_reg_membase (code, AMD64_R14, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r14), 8);
175         amd64_mov_reg_membase (code, AMD64_R15, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r15), 8);
176
177         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
178
179         /* get return address */
180         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rip), 8);
181
182         /* jump to the saved IP */
183         amd64_jump_reg (code, AMD64_R11);
184
185         inited = TRUE;
186
187         return start;
188 }
189
190 /*
191  * mono_arch_get_call_filter:
192  *
193  * Returns a pointer to a method which calls an exception filter. We
194  * also use this function to call finally handlers (we pass NULL as 
195  * @exc object in this case).
196  */
197 gpointer
198 mono_arch_get_call_filter (void)
199 {
200         static guint8 *start;
201         static gboolean inited = FALSE;
202         int i;
203         guint8 *code;
204         guint32 pos;
205
206         if (inited)
207                 return start;
208
209         start = code = mono_global_codeman_reserve (128);
210
211         /* call_filter (MonoContext *ctx, unsigned long eip) */
212         code = start;
213
214         /* Alloc new frame */
215         amd64_push_reg (code, AMD64_RBP);
216         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
217
218         /* Save callee saved regs */
219         pos = 0;
220         for (i = 0; i < AMD64_NREG; ++i)
221                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
222                         amd64_push_reg (code, i);
223                         pos += 8;
224                 }
225
226         /* Save EBP */
227         pos += 8;
228         amd64_push_reg (code, AMD64_RBP);
229
230         /* Make stack misaligned, the call will make it aligned again */
231         if (! (pos & 8))
232                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
233
234         /* set new EBP */
235         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbp), 8);
236         /* load callee saved regs */
237         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbx), 8);
238         amd64_mov_reg_membase (code, AMD64_R12, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r12), 8);
239         amd64_mov_reg_membase (code, AMD64_R13, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r13), 8);
240         amd64_mov_reg_membase (code, AMD64_R14, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r14), 8);
241         amd64_mov_reg_membase (code, AMD64_R15, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r15), 8);
242 #ifdef PLATFORM_WIN32
243         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
244         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
245 #endif
246
247         /* call the handler */
248         amd64_call_reg (code, AMD64_ARG_REG2);
249
250         if (! (pos & 8))
251                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
252
253         /* restore RBP */
254         amd64_pop_reg (code, AMD64_RBP);
255
256         /* Restore callee saved regs */
257         for (i = AMD64_NREG; i >= 0; --i)
258                 if (AMD64_IS_CALLEE_SAVED_REG (i))
259                         amd64_pop_reg (code, i);
260
261         amd64_leave (code);
262         amd64_ret (code);
263
264         g_assert ((code - start) < 128);
265
266         inited = TRUE;
267
268         return start;
269 }
270
271 /* 
272  * The first few arguments are dummy, to force the other arguments to be passed on
273  * the stack, this avoids overwriting the argument registers in the throw trampoline.
274  */
275 static void
276 throw_exception (guint64 dummy1, guint64 dummy2, guint64 dummy3, guint64 dummy4,
277                                  guint64 dummy5, guint64 dummy6,
278                                  MonoObject *exc, guint64 rip, guint64 rsp,
279                                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
280                                  guint64 r14, guint64 r15, guint64 rdi, guint64 rsi, 
281                                  guint64 rax, guint64 rcx, guint64 rdx,
282                                  guint64 rethrow)
283 {
284         static void (*restore_context) (MonoContext *);
285         MonoContext ctx;
286
287         if (!restore_context)
288                 restore_context = mono_arch_get_restore_context ();
289
290         ctx.rsp = rsp;
291         ctx.rip = rip;
292         ctx.rbx = rbx;
293         ctx.rbp = rbp;
294         ctx.r12 = r12;
295         ctx.r13 = r13;
296         ctx.r14 = r14;
297         ctx.r15 = r15;
298         ctx.rdi = rdi;
299         ctx.rsi = rsi;
300         ctx.rax = rax;
301         ctx.rcx = rcx;
302         ctx.rdx = rdx;
303
304         if (!rethrow && mono_debugger_throw_exception ((gpointer)(rip - 8), (gpointer)rsp, exc)) {
305                 /*
306                  * The debugger wants us to stop on the `throw' instruction.
307                  * By the time we get here, it already inserted a breakpoint on
308                  * eip - 8 (which is the address of the `mov %r15,%rdi ; callq throw').
309                  */
310
311                 /* FIXME FIXME
312                  *
313                  * In case of a rethrow, the JIT is emitting code like this:
314                  *
315                  *    mov    0xffffffffffffffd0(%rbp),%rax'
316                  *    mov    %rax,%rdi
317                  *    callq  throw
318                  *
319                  * Here, restore_context() wouldn't restore the %rax register correctly.
320                  */
321                 ctx.rip = rip - 8;
322                 ctx.rsp = rsp + 8;
323                 restore_context (&ctx);
324                 g_assert_not_reached ();
325         }
326
327         /* adjust eip so that it point into the call instruction */
328         ctx.rip -= 1;
329
330         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
331                 MonoException *mono_ex = (MonoException*)exc;
332                 if (!rethrow)
333                         mono_ex->stack_trace = NULL;
334         }
335         mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
336         restore_context (&ctx);
337
338         g_assert_not_reached ();
339 }
340
341 static gpointer
342 get_throw_trampoline (gboolean rethrow)
343 {
344         guint8* start;
345         guint8 *code;
346
347         start = code = mono_global_codeman_reserve (64);
348
349         code = start;
350
351         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, 8);
352
353         /* reverse order */
354         amd64_push_imm (code, rethrow);
355         amd64_push_reg (code, AMD64_RDX);
356         amd64_push_reg (code, AMD64_RCX);
357         amd64_push_reg (code, AMD64_RAX);
358         amd64_push_reg (code, AMD64_RSI);
359         amd64_push_reg (code, AMD64_RDI);
360         amd64_push_reg (code, AMD64_R15);
361         amd64_push_reg (code, AMD64_R14);
362         amd64_push_reg (code, AMD64_R13);
363         amd64_push_reg (code, AMD64_R12);
364         amd64_push_reg (code, AMD64_RBP);
365         amd64_push_reg (code, AMD64_RBX);
366
367         /* SP */
368         amd64_lea_membase (code, AMD64_RAX, AMD64_R11, 8);
369         amd64_push_reg (code, AMD64_RAX);
370
371         /* IP */
372         amd64_push_membase (code, AMD64_R11, 0);
373
374         /* Exception */
375         amd64_push_reg (code, AMD64_ARG_REG1);
376
377 #ifdef PLATFORM_WIN32
378         /* align stack */
379         amd64_push_imm (code, 0);
380         amd64_push_imm (code, 0);
381         amd64_push_imm (code, 0);
382         amd64_push_imm (code, 0);
383         amd64_push_imm (code, 0);
384         amd64_push_imm (code, 0);
385 #endif
386
387         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
388         amd64_call_reg (code, AMD64_R11);
389         amd64_breakpoint (code);
390
391         g_assert ((code - start) < 64);
392
393         return start;
394 }
395
396 /**
397  * mono_arch_get_throw_exception:
398  *
399  * Returns a function pointer which can be used to raise 
400  * exceptions. The returned function has the following 
401  * signature: void (*func) (MonoException *exc); 
402  *
403  */
404 gpointer 
405 mono_arch_get_throw_exception (void)
406 {
407         static guint8* start;
408         static gboolean inited = FALSE;
409
410         if (inited)
411                 return start;
412
413         start = get_throw_trampoline (FALSE);
414
415         inited = TRUE;
416
417         return start;
418 }
419
420 gpointer 
421 mono_arch_get_rethrow_exception (void)
422 {
423         static guint8* start;
424         static gboolean inited = FALSE;
425
426         if (inited)
427                 return start;
428
429         start = get_throw_trampoline (TRUE);
430
431         inited = TRUE;
432
433         return start;
434 }
435
436 gpointer 
437 mono_arch_get_throw_exception_by_name (void)
438 {       
439         static guint8* start;
440         static gboolean inited = FALSE;
441         guint8 *code;
442
443         if (inited)
444                 return start;
445
446         start = code = mono_global_codeman_reserve (64);
447
448         /* Not used on amd64 */
449         amd64_breakpoint (code);
450
451         return start;
452 }
453
454 /**
455  * mono_arch_get_throw_corlib_exception:
456  *
457  * Returns a function pointer which can be used to raise 
458  * corlib exceptions. The returned function has the following 
459  * signature: void (*func) (guint32 ex_token, guint32 offset); 
460  * Here, offset is the offset which needs to be substracted from the caller IP 
461  * to get the IP of the throw. Passing the offset has the advantage that it 
462  * needs no relocations in the caller.
463  */
464 gpointer 
465 mono_arch_get_throw_corlib_exception (void)
466 {
467         static guint8* start;
468         static gboolean inited = FALSE;
469         guint8 *code;
470         guint64 throw_ex;
471
472         if (inited)
473                 return start;
474
475         start = code = mono_global_codeman_reserve (64);
476
477         /* Push throw_ip */
478         amd64_push_reg (code, AMD64_ARG_REG2);
479
480         /* Call exception_from_token */
481         amd64_mov_reg_reg (code, AMD64_ARG_REG2, AMD64_ARG_REG1, 8);
482         amd64_mov_reg_imm (code, AMD64_ARG_REG1, mono_defaults.exception_class->image);
483         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
484         amd64_call_reg (code, AMD64_R11);
485
486         /* Compute throw_ip */
487         amd64_pop_reg (code, AMD64_ARG_REG2);
488         /* return addr */
489         amd64_pop_reg (code, AMD64_ARG_REG3);
490         amd64_alu_reg_reg (code, X86_SUB, AMD64_ARG_REG3, AMD64_ARG_REG2);
491
492         /* Put the throw_ip at the top of the misaligned stack */
493         amd64_push_reg (code, AMD64_ARG_REG3);
494
495         throw_ex = (guint64)mono_arch_get_throw_exception ();
496
497         /* Call throw_exception */
498         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_RAX, 8);
499         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
500         /* The original IP is on the stack */
501         amd64_jump_reg (code, AMD64_R11);
502
503         g_assert ((code - start) < 64);
504
505         inited = TRUE;
506
507         return start;
508 }
509
510 /* mono_arch_find_jit_info:
511  *
512  * This function is used to gather information from @ctx. It return the 
513  * MonoJitInfo of the corresponding function, unwinds one stack frame and
514  * stores the resulting context into @new_ctx. It also stores a string 
515  * describing the stack location into @trace (if not NULL), and modifies
516  * the @lmf if necessary. @native_offset return the IP offset from the 
517  * start of the function or -1 if that info is not available.
518  */
519 MonoJitInfo *
520 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
521                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
522                          gboolean *managed)
523 {
524         MonoJitInfo *ji;
525         int i;
526         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
527
528         /* Avoid costly table lookup during stack overflow */
529         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
530                 ji = prev_ji;
531         else
532                 ji = mono_jit_info_table_find (domain, ip);
533
534         if (managed)
535                 *managed = FALSE;
536
537         *new_ctx = *ctx;
538
539         if (ji != NULL) {
540                 int offset;
541                 gboolean omit_fp = (ji->used_regs & (1 << 31)) > 0;
542
543                 if (managed)
544                         if (!ji->method->wrapper_type)
545                                 *managed = TRUE;
546
547                 /*
548                  * If a method has save_lmf set, then register save/restore code is not generated 
549                  * by the JIT, so we have to restore callee saved registers from the lmf.
550                  */
551                 if (ji->method->save_lmf) {
552                         MonoLMF *lmf_addr;
553
554                         /* 
555                          * *lmf might not point to the LMF pushed by this method, so compute the LMF
556                          * address instead.
557                          */
558                         if (omit_fp)
559                                 lmf_addr = (MonoLMF*)ctx->rsp;
560                         else
561                                 lmf_addr = (MonoLMF*)(ctx->rbp - sizeof (MonoLMF));
562
563                         new_ctx->rbp = lmf_addr->rbp;
564                         new_ctx->rbx = lmf_addr->rbx;
565                         new_ctx->r12 = lmf_addr->r12;
566                         new_ctx->r13 = lmf_addr->r13;
567                         new_ctx->r14 = lmf_addr->r14;
568                         new_ctx->r15 = lmf_addr->r15;
569                 }
570                 else {
571                         offset = omit_fp ? 0 : -1;
572                         /* restore caller saved registers */
573                         for (i = 0; i < AMD64_NREG; i ++)
574                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
575                                         guint64 reg;
576
577                                         if (omit_fp) {
578                                                 reg = *((guint64*)ctx->rsp + offset);
579                                                 offset ++;
580                                         }
581                                         else {
582                                                 reg = *((guint64 *)ctx->rbp + offset);
583                                                 offset --;
584                                         }
585
586                                         switch (i) {
587                                         case AMD64_RBX:
588                                                 new_ctx->rbx = reg;
589                                                 break;
590                                         case AMD64_R12:
591                                                 new_ctx->r12 = reg;
592                                                 break;
593                                         case AMD64_R13:
594                                                 new_ctx->r13 = reg;
595                                                 break;
596                                         case AMD64_R14:
597                                                 new_ctx->r14 = reg;
598                                                 break;
599                                         case AMD64_R15:
600                                                 new_ctx->r15 = reg;
601                                                 break;
602                                         case AMD64_RBP:
603                                                 new_ctx->rbp = reg;
604                                                 break;
605                                         default:
606                                                 g_assert_not_reached ();
607                                         }
608                                 }
609                 }
610
611                 if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
612                         /* remove any unused lmf */
613                         *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~1);
614                 }
615
616                 if (omit_fp) {
617                         /* Pop frame */
618                         new_ctx->rsp += (ji->used_regs >> 16) & (0x7fff);
619                         new_ctx->rip = *((guint64 *)new_ctx->rsp) - 1;
620                         /* Pop return address */
621                         new_ctx->rsp += 8;
622                 }
623                 else {
624                         /* Pop EBP and the return address */
625                         new_ctx->rsp = ctx->rbp + (2 * sizeof (gpointer));
626                         /* we substract 1, so that the IP points into the call instruction */
627                         new_ctx->rip = *((guint64 *)ctx->rbp + 1) - 1;
628                         new_ctx->rbp = *((guint64 *)ctx->rbp);
629                 }
630
631                 /* Pop arguments off the stack */
632                 {
633                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
634
635                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
636                         new_ctx->rsp += stack_to_pop;
637                 }
638
639                 return ji;
640         } else if (*lmf) {
641                 guint64 rip;
642
643                 if (((guint64)(*lmf)->previous_lmf) & 1) {
644                         /* This LMF has the rip field set */
645                         rip = (*lmf)->rip;
646                 } else if ((*lmf)->rsp == 0) {
647                         /* Top LMF entry */
648                         return (gpointer)-1;
649                 } else {
650                         /* 
651                          * The rsp field is set just before the call which transitioned to native 
652                          * code. Obtain the rip from the stack.
653                          */
654                         rip = *(guint64*)((*lmf)->rsp - sizeof (gpointer));
655                 }
656
657                 ji = mono_jit_info_table_find (domain, (gpointer)rip);
658                 if (!ji) {
659                         if (!(*lmf)->method)
660                                 /* Top LMF entry */
661                                 return (gpointer)-1;
662                         /* Trampoline lmf frame */
663                         memset (res, 0, sizeof (MonoJitInfo));
664                         res->method = (*lmf)->method;
665                 }
666
667                 new_ctx->rip = rip;
668                 new_ctx->rbp = (*lmf)->rbp;
669                 new_ctx->rsp = (*lmf)->rsp;
670
671                 new_ctx->rbx = (*lmf)->rbx;
672                 new_ctx->r12 = (*lmf)->r12;
673                 new_ctx->r13 = (*lmf)->r13;
674                 new_ctx->r14 = (*lmf)->r14;
675                 new_ctx->r15 = (*lmf)->r15;
676
677                 *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~1);
678
679                 return ji ? ji : res;
680         }
681
682         return NULL;
683 }
684
685 /**
686  * mono_arch_handle_exception:
687  *
688  * @ctx: saved processor state
689  * @obj: the exception object
690  */
691 gboolean
692 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
693 {
694         MonoContext mctx;
695
696         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
697
698         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
699
700         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
701
702         return TRUE;
703 }
704
705 #ifdef MONO_ARCH_USE_SIGACTION
706 static inline guint64*
707 gregs_from_ucontext (ucontext_t *ctx)
708 {
709 #ifdef __FreeBSD__
710     guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
711 #else
712     guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
713 #endif
714
715         return gregs;
716 }
717 #endif
718 void
719 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
720 {
721 #ifdef MONO_ARCH_USE_SIGACTION
722         ucontext_t *ctx = (ucontext_t*)sigctx;
723
724     guint64 *gregs = gregs_from_ucontext (ctx);
725
726         mctx->rax = gregs [REG_RAX];
727         mctx->rbx = gregs [REG_RBX];
728         mctx->rcx = gregs [REG_RCX];
729         mctx->rdx = gregs [REG_RDX];
730         mctx->rbp = gregs [REG_RBP];
731         mctx->rsp = gregs [REG_RSP];
732         mctx->rsi = gregs [REG_RSI];
733         mctx->rdi = gregs [REG_RDI];
734         mctx->rip = gregs [REG_RIP];
735         mctx->r12 = gregs [REG_R12];
736         mctx->r13 = gregs [REG_R13];
737         mctx->r14 = gregs [REG_R14];
738         mctx->r15 = gregs [REG_R15];
739 #else
740         MonoContext *ctx = (MonoContext *)sigctx;
741
742         mctx->rax = ctx->rax;
743         mctx->rbx = ctx->rbx;
744         mctx->rcx = ctx->rcx;
745         mctx->rdx = ctx->rdx;
746         mctx->rbp = ctx->rbp;
747         mctx->rsp = ctx->rsp;
748         mctx->rsi = ctx->rsi;
749         mctx->rdi = ctx->rdi;
750         mctx->rip = ctx->rip;
751         mctx->r12 = ctx->r12;
752         mctx->r13 = ctx->r13;
753         mctx->r14 = ctx->r14;
754         mctx->r15 = ctx->r15;
755 #endif
756 }
757
758 void
759 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
760 {
761 #ifdef MONO_ARCH_USE_SIGACTION
762         ucontext_t *ctx = (ucontext_t*)sigctx;
763
764     guint64 *gregs = gregs_from_ucontext (ctx);
765
766         gregs [REG_RAX] = mctx->rax;
767         gregs [REG_RBX] = mctx->rbx;
768         gregs [REG_RCX] = mctx->rcx;
769         gregs [REG_RDX] = mctx->rdx;
770         gregs [REG_RBP] = mctx->rbp;
771         gregs [REG_RSP] = mctx->rsp;
772         gregs [REG_RSI] = mctx->rsi;
773         gregs [REG_RDI] = mctx->rdi;
774         gregs [REG_RIP] = mctx->rip;
775         gregs [REG_R12] = mctx->r12;
776         gregs [REG_R13] = mctx->r13;
777         gregs [REG_R14] = mctx->r14;
778         gregs [REG_R15] = mctx->r15;
779 #else
780         MonoContext *ctx = (MonoContext *)sigctx;
781
782         ctx->rax = mctx->rax;
783         ctx->rbx = mctx->rbx;
784         ctx->rcx = mctx->rcx;
785         ctx->rdx = mctx->rdx;
786         ctx->rbp = mctx->rbp;
787         ctx->rsp = mctx->rsp;
788         ctx->rsi = mctx->rsi;
789         ctx->rdi = mctx->rdi;
790         ctx->rip = mctx->rip;
791         ctx->r12 = mctx->r12;
792         ctx->r13 = mctx->r13;
793         ctx->r14 = mctx->r14;
794         ctx->r15 = mctx->r15;
795 #endif
796 }
797
798 gpointer
799 mono_arch_ip_from_context (void *sigctx)
800 {
801         
802 #ifdef MONO_ARCH_USE_SIGACTION
803
804         ucontext_t *ctx = (ucontext_t*)sigctx;
805
806     guint64 *gregs = gregs_from_ucontext (ctx);
807
808         return (gpointer)gregs [REG_RIP];
809 #else
810         MonoContext *ctx = sigctx;
811         return (gpointer)ctx->rip;
812 #endif  
813 }
814
815 static void
816 restore_soft_guard_pages (void)
817 {
818         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
819         if (jit_tls->stack_ovf_guard_base)
820                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
821 }
822
823 /* 
824  * this function modifies mctx so that when it is restored, it
825  * won't execcute starting at mctx.eip, but in a function that
826  * will restore the protection on the soft-guard pages and return back to
827  * continue at mctx.eip.
828  */
829 static void
830 prepare_for_guard_pages (MonoContext *mctx)
831 {
832         gpointer *sp;
833         sp = (gpointer)(mctx->rsp);
834         sp -= 1;
835         /* the return addr */
836         sp [0] = (gpointer)(mctx->rip);
837         mctx->rip = (guint64)restore_soft_guard_pages;
838         mctx->rsp = (guint64)sp;
839 }
840
841 static void
842 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
843 {
844         void (*restore_context) (MonoContext *);
845         MonoContext mctx;
846
847         restore_context = mono_arch_get_restore_context ();
848         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
849         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
850         if (stack_ovf)
851                 prepare_for_guard_pages (&mctx);
852         restore_context (&mctx);
853 }
854
855 void
856 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
857 {
858 #ifdef MONO_ARCH_USE_SIGACTION
859         MonoException *exc = NULL;
860         ucontext_t *ctx = (ucontext_t*)sigctx;
861         guint64 *gregs = gregs_from_ucontext (ctx);
862         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
863         gpointer *sp;
864         int frame_size;
865
866         if (stack_ovf)
867                 exc = mono_domain_get ()->stack_overflow_ex;
868         if (!ji)
869                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
870
871         /* setup a call frame on the real stack so that control is returned there
872          * and exception handling can continue.
873          * The frame looks like:
874          *   ucontext struct
875          *   ...
876          *   return ip
877          * 128 is the size of the red zone
878          */
879         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
880         frame_size += 15;
881         frame_size &= ~15;
882         sp = (gpointer)(gregs [REG_RSP] & ~15);
883         sp = (gpointer)((char*)sp - frame_size);
884         /* the arguments must be aligned */
885         sp [-1] = (gpointer)gregs [REG_RIP];
886         /* may need to adjust pointers in the new struct copy, depending on the OS */
887         memcpy (sp + 4, ctx, sizeof (ucontext_t));
888         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
889         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
890         gregs [REG_RSP] = (unsigned long)(sp - 1);
891         gregs [REG_RDI] = (unsigned long)(sp + 4);
892         gregs [REG_RSI] = (guint64)exc;
893         gregs [REG_RDX] = stack_ovf;
894 #endif
895 }
896
897 static guint64
898 get_original_ip (void)
899 {
900         MonoLMF *lmf = mono_get_lmf ();
901
902         g_assert (lmf);
903
904         /* Reset the change to previous_lmf */
905         lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf & ~1);
906
907         return lmf->rip;
908 }
909
910 static gpointer 
911 get_throw_pending_exception (void)
912 {
913         static guint8* start;
914         static gboolean inited = FALSE;
915         guint8 *code;
916         guint8 *br[1];
917         gpointer throw_trampoline;
918
919         if (inited)
920                 return start;
921
922         start = code = mono_global_codeman_reserve (128);
923
924         /* We are in the frame of a managed method after a call */
925         /* 
926          * We would like to throw the pending exception in such a way that it looks to
927          * be thrown from the managed method.
928          */
929
930         /* Save registers which might contain the return value of the call */
931         amd64_push_reg (code, AMD64_RAX);
932         amd64_push_reg (code, AMD64_RDX);
933
934         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
935         amd64_movsd_membase_reg (code, AMD64_RSP, 0, AMD64_XMM0);
936
937         /* Align stack */
938         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
939
940         /* Obtain the pending exception */
941         amd64_mov_reg_imm (code, AMD64_R11, mono_thread_get_and_clear_pending_exception);
942         amd64_call_reg (code, AMD64_R11);
943
944         /* Check if it is NULL, and branch */
945         amd64_alu_reg_imm (code, X86_CMP, AMD64_RAX, 0);
946         br[0] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE);
947
948         /* exc != NULL branch */
949
950         /* Save the exc on the stack */
951         amd64_push_reg (code, AMD64_RAX);
952         /* Align stack */
953         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
954
955         /* Obtain the original ip and clear the flag in previous_lmf */
956         amd64_mov_reg_imm (code, AMD64_R11, get_original_ip);
957         amd64_call_reg (code, AMD64_R11);       
958
959         /* Load exc */
960         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, 8, 8);
961
962         /* Pop saved stuff from the stack */
963         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 6 * 8);
964
965         /* Setup arguments for the throw trampoline */
966         /* Exception */
967         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_R11, 8);
968         /* The trampoline expects the caller ip to be pushed on the stack */
969         amd64_push_reg (code, AMD64_RAX);
970
971         /* Call the throw trampoline */
972         throw_trampoline = mono_arch_get_throw_exception ();
973         amd64_mov_reg_imm (code, AMD64_R11, throw_trampoline);
974         /* We use a jump instead of a call so we can push the original ip on the stack */
975         amd64_jump_reg (code, AMD64_R11);
976
977         /* ex == NULL branch */
978         mono_amd64_patch (br [0], code);
979
980         /* Obtain the original ip and clear the flag in previous_lmf */
981         amd64_mov_reg_imm (code, AMD64_R11, get_original_ip);
982         amd64_call_reg (code, AMD64_R11);       
983         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, 8);
984
985         /* Restore registers */
986         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
987         amd64_movsd_reg_membase (code, AMD64_XMM0, AMD64_RSP, 0);
988         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
989         amd64_pop_reg (code, AMD64_RDX);
990         amd64_pop_reg (code, AMD64_RAX);
991
992         /* Return to original code */
993         amd64_jump_reg (code, AMD64_R11);
994
995         g_assert ((code - start) < 128);
996
997         inited = TRUE;
998
999         return start;
1000 }
1001
1002 /*
1003  * Called when a thread receives an async exception while executing unmanaged code.
1004  * Instead of checking for this exception in the managed-to-native wrapper, we hijack 
1005  * the return address on the stack to point to a helper routine which throws the
1006  * exception.
1007  */
1008 void
1009 mono_arch_notify_pending_exc (void)
1010 {
1011         MonoLMF *lmf = mono_get_lmf ();
1012
1013         if (lmf->rsp == 0)
1014                 /* Initial LMF */
1015                 return;
1016
1017         if ((guint64)lmf->previous_lmf & 1)
1018                 /* Already hijacked or trampoline LMF entry */
1019                 return;
1020
1021         /* lmf->rsp is set just before making the call which transitions to unmanaged code */
1022         lmf->rip = *(guint64*)(lmf->rsp - 8);
1023         /* Signal that lmf->rip is set */
1024         lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf | 1);
1025
1026         *(gpointer*)(lmf->rsp - 8) = get_throw_pending_exception ();
1027 }