for TARGET_J2EE only:
[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         if (ji != NULL) {
530                 int offset;
531                 gboolean omit_fp = (ji->used_regs & (1 << 31)) > 0;
532
533                 *new_ctx = *ctx;
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                 
645                 *new_ctx = *ctx;
646
647                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
648                 } else {
649                         if (!(*lmf)->method)
650                                 /* Top LMF entry */
651                                 return (gpointer)-1;
652                         /* Trampoline lmf frame */
653                         memset (res, 0, sizeof (MonoJitInfo));
654                         res->method = (*lmf)->method;
655                 }
656
657                 new_ctx->rip = (*lmf)->rip;
658                 new_ctx->rbp = (*lmf)->rbp;
659                 new_ctx->rsp = (*lmf)->rsp;
660
661                 new_ctx->rbx = (*lmf)->rbx;
662                 new_ctx->r12 = (*lmf)->r12;
663                 new_ctx->r13 = (*lmf)->r13;
664                 new_ctx->r14 = (*lmf)->r14;
665                 new_ctx->r15 = (*lmf)->r15;
666
667                 *lmf = (*lmf)->previous_lmf;
668
669                 return ji ? ji : res;
670         }
671
672         return NULL;
673 }
674
675 /**
676  * mono_arch_handle_exception:
677  *
678  * @ctx: saved processor state
679  * @obj: the exception object
680  */
681 gboolean
682 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
683 {
684         MonoContext mctx;
685
686         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
687
688         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), test_only);
689
690         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
691
692         return TRUE;
693 }
694
695 #ifdef MONO_ARCH_USE_SIGACTION
696 static inline guint64*
697 gregs_from_ucontext (ucontext_t *ctx)
698 {
699 #ifdef __FreeBSD__
700     guint64 *gregs = (guint64 *) &ctx->uc_mcontext;
701 #else
702     guint64 *gregs = (guint64 *) &ctx->uc_mcontext.gregs;
703 #endif
704
705         return gregs;
706 }
707 #endif
708 void
709 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
710 {
711 #ifdef MONO_ARCH_USE_SIGACTION
712         ucontext_t *ctx = (ucontext_t*)sigctx;
713
714     guint64 *gregs = gregs_from_ucontext (ctx);
715
716         mctx->rax = gregs [REG_RAX];
717         mctx->rbx = gregs [REG_RBX];
718         mctx->rcx = gregs [REG_RCX];
719         mctx->rdx = gregs [REG_RDX];
720         mctx->rbp = gregs [REG_RBP];
721         mctx->rsp = gregs [REG_RSP];
722         mctx->rsi = gregs [REG_RSI];
723         mctx->rdi = gregs [REG_RDI];
724         mctx->rip = gregs [REG_RIP];
725         mctx->r12 = gregs [REG_R12];
726         mctx->r13 = gregs [REG_R13];
727         mctx->r14 = gregs [REG_R14];
728         mctx->r15 = gregs [REG_R15];
729 #else
730         MonoContext *ctx = (MonoContext *)sigctx;
731
732         mctx->rax = ctx->rax;
733         mctx->rbx = ctx->rbx;
734         mctx->rcx = ctx->rcx;
735         mctx->rdx = ctx->rdx;
736         mctx->rbp = ctx->rbp;
737         mctx->rsp = ctx->rsp;
738         mctx->rsi = ctx->rsi;
739         mctx->rdi = ctx->rdi;
740         mctx->rip = ctx->rip;
741         mctx->r12 = ctx->r12;
742         mctx->r13 = ctx->r13;
743         mctx->r14 = ctx->r14;
744         mctx->r15 = ctx->r15;
745 #endif
746 }
747
748 void
749 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
750 {
751 #ifdef MONO_ARCH_USE_SIGACTION
752         ucontext_t *ctx = (ucontext_t*)sigctx;
753
754     guint64 *gregs = gregs_from_ucontext (ctx);
755
756         gregs [REG_RAX] = mctx->rax;
757         gregs [REG_RBX] = mctx->rbx;
758         gregs [REG_RCX] = mctx->rcx;
759         gregs [REG_RDX] = mctx->rdx;
760         gregs [REG_RBP] = mctx->rbp;
761         gregs [REG_RSP] = mctx->rsp;
762         gregs [REG_RSI] = mctx->rsi;
763         gregs [REG_RDI] = mctx->rdi;
764         gregs [REG_RIP] = mctx->rip;
765         gregs [REG_R12] = mctx->r12;
766         gregs [REG_R13] = mctx->r13;
767         gregs [REG_R14] = mctx->r14;
768         gregs [REG_R15] = mctx->r15;
769 #else
770         MonoContext *ctx = (MonoContext *)sigctx;
771
772         ctx->rax = mctx->rax;
773         ctx->rbx = mctx->rbx;
774         ctx->rcx = mctx->rcx;
775         ctx->rdx = mctx->rdx;
776         ctx->rbp = mctx->rbp;
777         ctx->rsp = mctx->rsp;
778         ctx->rsi = mctx->rsi;
779         ctx->rdi = mctx->rdi;
780         ctx->rip = mctx->rip;
781         ctx->r12 = mctx->r12;
782         ctx->r13 = mctx->r13;
783         ctx->r14 = mctx->r14;
784         ctx->r15 = mctx->r15;
785 #endif
786 }
787
788 gpointer
789 mono_arch_ip_from_context (void *sigctx)
790 {
791         
792 #ifdef MONO_ARCH_USE_SIGACTION
793
794         ucontext_t *ctx = (ucontext_t*)sigctx;
795
796     guint64 *gregs = gregs_from_ucontext (ctx);
797
798         return (gpointer)gregs [REG_RIP];
799 #else
800         MonoContext *ctx = sigctx;
801         return (gpointer)ctx->rip;
802 #endif  
803 }
804
805 static void
806 restore_soft_guard_pages (void)
807 {
808         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
809         if (jit_tls->stack_ovf_guard_base)
810                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
811 }
812
813 /* 
814  * this function modifies mctx so that when it is restored, it
815  * won't execcute starting at mctx.eip, but in a function that
816  * will restore the protection on the soft-guard pages and return back to
817  * continue at mctx.eip.
818  */
819 static void
820 prepare_for_guard_pages (MonoContext *mctx)
821 {
822         gpointer *sp;
823         sp = (gpointer)(mctx->rsp);
824         sp -= 1;
825         /* the return addr */
826         sp [0] = (gpointer)(mctx->rip);
827         mctx->rip = (unsigned long)restore_soft_guard_pages;
828         mctx->rsp = (unsigned long)sp;
829 }
830
831 static void
832 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
833 {
834         void (*restore_context) (MonoContext *);
835         MonoContext mctx;
836
837         restore_context = mono_arch_get_restore_context ();
838         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
839         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
840         if (stack_ovf)
841                 prepare_for_guard_pages (&mctx);
842         restore_context (&mctx);
843 }
844
845 void
846 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
847 {
848 #ifdef MONO_ARCH_USE_SIGACTION
849         MonoException *exc = NULL;
850         ucontext_t *ctx = (ucontext_t*)sigctx;
851         guint64 *gregs = gregs_from_ucontext (ctx);
852         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
853         gpointer *sp;
854         int frame_size;
855
856         if (stack_ovf)
857                 exc = mono_domain_get ()->stack_overflow_ex;
858         if (!ji)
859                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
860
861         /* setup a call frame on the real stack so that control is returned there
862          * and exception handling can continue.
863          * The frame looks like:
864          *   ucontext struct
865          *   ...
866          *   return ip
867          * 128 is the size of the red zone
868          */
869         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
870         frame_size += 15;
871         frame_size &= ~15;
872         sp = (gpointer)(gregs [REG_RSP] & ~15);
873         sp = (gpointer)((char*)sp - frame_size);
874         /* the arguments must be aligned */
875         sp [-1] = (gpointer)gregs [REG_RIP];
876         /* may need to adjust pointers in the new struct copy, depending on the OS */
877         memcpy (sp + 4, ctx, sizeof (ucontext_t));
878         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
879         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
880         gregs [REG_RSP] = (unsigned long)(sp - 1);
881         gregs [REG_RDI] = (unsigned long)(sp + 4);
882         gregs [REG_RSI] = (guint64)exc;
883         gregs [REG_RDX] = stack_ovf;
884 #endif
885 }
886