2005-03-29 Zoltan Varga <vargaz@freemail.hu>
[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 #include <sys/ucontext.h>
15
16 #include <mono/arch/amd64/amd64-codegen.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/gc-internal.h>
23 #include <mono/metadata/mono-debug.h>
24
25 #include "mini.h"
26 #include "mini-amd64.h"
27
28 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
29
30 #ifdef PLATFORM_WIN32
31 static MonoW32ExceptionHandler fpe_handler;
32 static MonoW32ExceptionHandler ill_handler;
33 static MonoW32ExceptionHandler segv_handler;
34
35 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
36
37 #define W32_SEH_HANDLE_EX(_ex) \
38         if (_ex##_handler) _ex##_handler((int)sctx)
39
40 /*
41  * Unhandled Exception Filter
42  * Top-level per-process exception handler.
43  */
44 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
45 {
46         EXCEPTION_RECORD* er;
47         CONTEXT* ctx;
48         MonoContext* sctx;
49         LONG res;
50
51         res = EXCEPTION_CONTINUE_EXECUTION;
52
53         er = ep->ExceptionRecord;
54         ctx = ep->ContextRecord;
55         sctx = g_malloc(sizeof(MonoContext));
56
57         /* Copy Win32 context to UNIX style context */
58         sctx->eax = ctx->Eax;
59         sctx->ebx = ctx->Ebx;
60         sctx->ecx = ctx->Ecx;
61         sctx->edx = ctx->Edx;
62         sctx->ebp = ctx->Ebp;
63         sctx->esp = ctx->Esp;
64         sctx->esi = ctx->Esi;
65         sctx->edi = ctx->Edi;
66         sctx->eip = ctx->Eip;
67
68         switch (er->ExceptionCode) {
69         case EXCEPTION_ACCESS_VIOLATION:
70                 W32_SEH_HANDLE_EX(segv);
71                 break;
72         case EXCEPTION_ILLEGAL_INSTRUCTION:
73                 W32_SEH_HANDLE_EX(ill);
74                 break;
75         case EXCEPTION_INT_DIVIDE_BY_ZERO:
76         case EXCEPTION_INT_OVERFLOW:
77         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
78         case EXCEPTION_FLT_OVERFLOW:
79         case EXCEPTION_FLT_UNDERFLOW:
80         case EXCEPTION_FLT_INEXACT_RESULT:
81                 W32_SEH_HANDLE_EX(fpe);
82                 break;
83         default:
84                 break;
85         }
86
87         /* Copy context back */
88         ctx->Eax = sctx->eax;
89         ctx->Ebx = sctx->ebx;
90         ctx->Ecx = sctx->ecx;
91         ctx->Edx = sctx->edx;
92         ctx->Ebp = sctx->ebp;
93         ctx->Esp = sctx->esp;
94         ctx->Esi = sctx->esi;
95         ctx->Edi = sctx->edi;
96         ctx->Eip = sctx->eip;
97
98         return res;
99 }
100
101 void win32_seh_init()
102 {
103         old_handler = SetUnhandledExceptionFilter(seh_handler);
104 }
105
106 void win32_seh_cleanup()
107 {
108         if (old_handler) SetUnhandledExceptionFilter(old_handler);
109 }
110
111 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
112 {
113         switch (type) {
114         case SIGFPE:
115                 fpe_handler = handler;
116                 break;
117         case SIGILL:
118                 ill_handler = handler;
119                 break;
120         case SIGSEGV:
121                 segv_handler = handler;
122                 break;
123         default:
124                 break;
125         }
126 }
127
128 #endif /* PLATFORM_WIN32 */
129
130 /*
131  * mono_arch_get_restore_context:
132  *
133  * Returns a pointer to a method which restores a previously saved sigcontext.
134  */
135 gpointer
136 mono_arch_get_restore_context (void)
137 {
138         static guint8 *start = NULL;
139         static gboolean inited = FALSE;
140         guint8 *code;
141
142         if (inited)
143                 return start;
144
145         /* restore_contect (MonoContext *ctx) */
146
147         start = code = mono_global_codeman_reserve (256);
148
149         /* get return address */
150         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rip), 8);
151
152         /* Restore registers */
153         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
154         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
155         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r12), 8);
156         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r13), 8);
157         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r14), 8);
158         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r15), 8);
159
160         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
161
162         /* jump to the saved IP */
163         amd64_jump_reg (code, AMD64_RAX);
164
165         inited = TRUE;
166
167         return start;
168 }
169
170 /*
171  * mono_arch_get_call_filter:
172  *
173  * Returns a pointer to a method which calls an exception filter. We
174  * also use this function to call finally handlers (we pass NULL as 
175  * @exc object in this case).
176  */
177 gpointer
178 mono_arch_get_call_filter (void)
179 {
180         static guint8 *start;
181         static gboolean inited = FALSE;
182         int i;
183         guint8 *code;
184         guint32 pos;
185
186         if (inited)
187                 return start;
188
189         start = code = mono_global_codeman_reserve (64);
190
191         /* call_filter (MonoContext *ctx, unsigned long eip) */
192         code = start;
193
194         /* Alloc new frame */
195         amd64_push_reg (code, AMD64_RBP);
196         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
197
198         /* Save callee saved regs */
199         pos = 0;
200         for (i = 0; i < AMD64_NREG; ++i)
201                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
202                         amd64_push_reg (code, i);
203                         pos += 8;
204                 }
205
206         /* Save EBP */
207         pos += 8;
208         amd64_push_reg (code, AMD64_RBP);
209
210         /* Make stack misaligned, the call will make it aligned again */
211         if (! (pos & 8))
212                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
213
214         /* set new EBP */
215         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
216         /* load callee saved regs */
217         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
218         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
219         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
220         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
221         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
222
223         /* call the handler */
224         amd64_call_reg (code, AMD64_RSI);
225
226         if (! (pos & 8))
227                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
228
229         /* restore RBP */
230         amd64_pop_reg (code, AMD64_RBP);
231
232         /* Restore callee saved regs */
233         for (i = AMD64_NREG; i >= 0; --i)
234                 if (AMD64_IS_CALLEE_SAVED_REG (i))
235                         amd64_pop_reg (code, i);
236
237         amd64_leave (code);
238         amd64_ret (code);
239
240         g_assert ((code - start) < 64);
241
242         inited = TRUE;
243
244         return start;
245 }
246
247 static void
248 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
249                                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
250                                  guint64 r14, guint64 r15, guint64 rethrow)
251 {
252         static void (*restore_context) (MonoContext *);
253         MonoContext ctx;
254
255         if (!restore_context)
256                 restore_context = mono_arch_get_restore_context ();
257
258         /* adjust eip so that it point into the call instruction */
259         rip -= 1;
260
261         ctx.rsp = rsp;
262         ctx.rip = rip;
263         ctx.rbx = rbx;
264         ctx.rbp = rbp;
265         ctx.r12 = r12;
266         ctx.r13 = r13;
267         ctx.r14 = r14;
268         ctx.r15 = r15;
269
270         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
271                 MonoException *mono_ex = (MonoException*)exc;
272                 if (!rethrow)
273                         mono_ex->stack_trace = NULL;
274         }
275         mono_handle_exception (&ctx, exc, (gpointer)(rip + 1), FALSE);
276         restore_context (&ctx);
277
278         g_assert_not_reached ();
279 }
280
281 static gpointer
282 get_throw_trampoline (gboolean rethrow)
283 {
284         guint8* start;
285         guint8 *code;
286
287         start = code = mono_global_codeman_reserve (64);
288
289         code = start;
290
291         /* Exception */
292         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RDI, 8);
293         /* IP */
294         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, 0, 8);
295         /* SP */
296         amd64_lea_membase (code, AMD64_RDX, AMD64_RSP, 8);
297         /* Callee saved regs */
298         amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
299         amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
300         amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
301         /* reverse order */
302         amd64_push_imm (code, rethrow);
303         amd64_push_reg (code, AMD64_R15);
304         amd64_push_reg (code, AMD64_R14);
305         amd64_push_reg (code, AMD64_R13);
306
307         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
308         amd64_call_reg (code, AMD64_R11);
309         amd64_breakpoint (code);
310
311         g_assert ((code - start) < 64);
312
313         return start;
314 }
315
316 /**
317  * mono_arch_get_throw_exception:
318  *
319  * Returns a function pointer which can be used to raise 
320  * exceptions. The returned function has the following 
321  * signature: void (*func) (MonoException *exc); 
322  *
323  */
324 gpointer 
325 mono_arch_get_throw_exception (void)
326 {
327         static guint8* start;
328         static gboolean inited = FALSE;
329
330         if (inited)
331                 return start;
332
333         start = get_throw_trampoline (FALSE);
334
335         inited = TRUE;
336
337         return start;
338 }
339
340 gpointer 
341 mono_arch_get_rethrow_exception (void)
342 {
343         static guint8* start;
344         static gboolean inited = FALSE;
345
346         if (inited)
347                 return start;
348
349         start = get_throw_trampoline (TRUE);
350
351         inited = TRUE;
352
353         return start;
354 }
355
356 /**
357  * mono_arch_get_throw_exception_by_name:
358  *
359  * Returns a function pointer which can be used to raise 
360  * corlib exceptions. The returned function has the following 
361  * signature: void (*func) (char *exc_name); 
362  */
363 gpointer 
364 mono_arch_get_throw_exception_by_name (void)
365 {
366         static guint8* start;
367         static gboolean inited = FALSE;
368         guint8 *code;
369         guint64 throw_ex;
370
371         if (inited)
372                 return start;
373
374         start = code = mono_global_codeman_reserve (64);
375
376         code = start;
377
378         /* Push return address */
379         amd64_push_reg (code, AMD64_RSI);
380
381         /* Call exception_from_name */
382         amd64_mov_reg_reg (code, AMD64_RDX, AMD64_RDI, 8);
383         amd64_mov_reg_imm (code, AMD64_RSI, "System");
384         amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
385
386         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_name);
387         amd64_call_reg (code, AMD64_R11);
388
389         /* Put the original return address at the top of the misaligned stack */
390         amd64_pop_reg (code, AMD64_RSI);
391         amd64_push_reg (code, AMD64_R11);
392         amd64_push_reg (code, AMD64_RSI);
393
394         throw_ex = (guint64)mono_arch_get_throw_exception ();
395
396         /* Call throw_exception */
397         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
398         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
399         /* The original IP is on the stack */
400         amd64_jump_reg (code, AMD64_R11);
401
402         g_assert ((code - start) < 64);
403
404         inited = TRUE;
405
406         return start;
407 }
408
409 /**
410  * mono_arch_get_throw_corlib_exception:
411  *
412  * Returns a function pointer which can be used to raise 
413  * corlib exceptions. The returned function has the following 
414  * signature: void (*func) (guint32 ex_token, guint32 offset); 
415  * Here, offset is the offset which needs to be substracted from the caller IP 
416  * to get the IP of the throw. Passing the offset has the advantage that it 
417  * needs no relocations in the caller.
418  */
419 gpointer 
420 mono_arch_get_throw_corlib_exception (void)
421 {
422         static guint8* start;
423         static gboolean inited = FALSE;
424         guint8 *code;
425         guint64 throw_ex;
426
427         if (inited)
428                 return start;
429
430         start = code = mono_global_codeman_reserve (64);
431
432         /* Push throw_ip */
433         amd64_push_reg (code, AMD64_RSI);
434
435         /* Call exception_from_token */
436         amd64_mov_reg_reg (code, AMD64_RSI, AMD64_RDI, 8);
437         amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
438         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
439         amd64_call_reg (code, AMD64_R11);
440
441         /* Compute throw_ip */
442         amd64_pop_reg (code, AMD64_RSI);
443         /* return addr */
444         amd64_pop_reg (code, AMD64_RDX);
445         amd64_alu_reg_reg (code, X86_SUB, AMD64_RDX, AMD64_RSI);
446
447         /* Put the throw_ip at the top of the misaligned stack */
448         amd64_push_reg (code, AMD64_RDX);
449
450         throw_ex = (guint64)mono_arch_get_throw_exception ();
451
452         /* Call throw_exception */
453         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
454         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
455         /* The original IP is on the stack */
456         amd64_jump_reg (code, AMD64_R11);
457
458         g_assert ((code - start) < 64);
459
460         inited = TRUE;
461
462         return start;
463 }
464
465 /* mono_arch_find_jit_info:
466  *
467  * This function is used to gather information from @ctx. It return the 
468  * MonoJitInfo of the corresponding function, unwinds one stack frame and
469  * stores the resulting context into @new_ctx. It also stores a string 
470  * describing the stack location into @trace (if not NULL), and modifies
471  * the @lmf if necessary. @native_offset return the IP offset from the 
472  * start of the function or -1 if that info is not available.
473  */
474 MonoJitInfo *
475 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
476                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
477                          gboolean *managed)
478 {
479         MonoJitInfo *ji;
480         int i;
481         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
482
483         /* Avoid costly table lookup during stack overflow */
484         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
485                 ji = prev_ji;
486         else
487                 ji = mono_jit_info_table_find (domain, ip);
488
489         if (managed)
490                 *managed = FALSE;
491
492         if (ji != NULL) {
493                 int offset;
494
495                 *new_ctx = *ctx;
496
497                 if (managed)
498                         if (!ji->method->wrapper_type)
499                                 *managed = TRUE;
500
501                 /*
502                  * Some managed methods like pinvoke wrappers might have save_lmf set.
503                  * In this case, register save/restore code is not generated by the 
504                  * JIT, so we have to restore callee saved registers from the lmf.
505                  */
506                 if (ji->method->save_lmf) {
507                         /* 
508                          * We only need to do this if the exception was raised in managed
509                          * code, since otherwise the lmf was already popped of the stack.
510                          */
511                         if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
512                                 new_ctx->rbx = (*lmf)->rbx;
513                                 new_ctx->r12 = (*lmf)->r12;
514                                 new_ctx->r13 = (*lmf)->r13;
515                                 new_ctx->r14 = (*lmf)->r14;
516                                 new_ctx->r15 = (*lmf)->r15;
517                         }
518                 }
519                 else {
520                         offset = -1;
521                         /* restore caller saved registers */
522                         for (i = 0; i < AMD64_NREG; i ++)
523                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
524                                         guint64 reg = *((guint64 *)ctx->SC_EBP + offset);
525                                         offset --;
526                                         switch (i) {
527                                         case AMD64_RBX:
528                                                 new_ctx->rbx = reg;
529                                                 break;
530                                         case AMD64_R12:
531                                                 new_ctx->r12 = reg;
532                                                 break;
533                                         case AMD64_R13:
534                                                 new_ctx->r13 = reg;
535                                                 break;
536                                         case AMD64_R14:
537                                                 new_ctx->r14 = reg;
538                                                 break;
539                                         case AMD64_R15:
540                                                 new_ctx->r15 = reg;
541                                                 break;
542                                         default:
543                                                 g_assert_not_reached ();
544                                         }
545                                 }
546                 }
547
548                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
549                         /* remove any unused lmf */
550                         *lmf = (*lmf)->previous_lmf;
551                 }
552
553                 /* Pop EBP and the return address */
554                 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
555                 /* we substract 1, so that the IP points into the call instruction */
556                 new_ctx->SC_EIP = *((guint64 *)ctx->SC_EBP + 1) - 1;
557                 new_ctx->SC_EBP = *((guint64 *)ctx->SC_EBP);
558
559                 /* Pop arguments off the stack */
560                 {
561                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
562
563                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
564                         new_ctx->SC_ESP += stack_to_pop;
565                 }
566
567                 return ji;
568         } else if (*lmf) {
569                 
570                 *new_ctx = *ctx;
571
572                 if (!(*lmf)->method)
573                         return (gpointer)-1;
574
575                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
576                 } else {
577                         memset (res, 0, sizeof (MonoJitInfo));
578                         res->method = (*lmf)->method;
579                 }
580
581                 new_ctx->SC_RIP = (*lmf)->rip;
582                 new_ctx->SC_RBP = (*lmf)->ebp;
583
584                 new_ctx->SC_RBX = (*lmf)->rbx;
585                 new_ctx->SC_R12 = (*lmf)->r12;
586                 new_ctx->SC_R13 = (*lmf)->r13;
587                 new_ctx->SC_R14 = (*lmf)->r14;
588                 new_ctx->SC_R15 = (*lmf)->r15;
589
590                 /* the lmf is always stored on the stack, so the following
591                  * expression points to a stack location which can be used as ESP */
592                 new_ctx->SC_ESP = ALIGN_TO ((guint64)&((*lmf)->rip), 16);
593
594                 *lmf = (*lmf)->previous_lmf;
595
596                 return ji ? ji : res;
597         }
598
599         return NULL;
600 }
601
602 /**
603  * mono_arch_handle_exception:
604  *
605  * @ctx: saved processor state
606  * @obj: the exception object
607  */
608 gboolean
609 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
610 {
611         ucontext_t *ctx = (ucontext_t*)sigctx;
612         MonoContext mctx;
613
614         mctx.rax = ctx->uc_mcontext.gregs [REG_RAX];
615         mctx.rbx = ctx->uc_mcontext.gregs [REG_RBX];
616         mctx.rcx = ctx->uc_mcontext.gregs [REG_RCX];
617         mctx.rdx = ctx->uc_mcontext.gregs [REG_RDX];
618         mctx.rbp = ctx->uc_mcontext.gregs [REG_RBP];
619         mctx.rsp = ctx->uc_mcontext.gregs [REG_RSP];
620         mctx.rsi = ctx->uc_mcontext.gregs [REG_RSI];
621         mctx.rdi = ctx->uc_mcontext.gregs [REG_RDI];
622         mctx.rip = ctx->uc_mcontext.gregs [REG_RIP];
623         mctx.r12 = ctx->uc_mcontext.gregs [REG_R12];
624         mctx.r13 = ctx->uc_mcontext.gregs [REG_R13];
625         mctx.r14 = ctx->uc_mcontext.gregs [REG_R14];
626         mctx.r15 = ctx->uc_mcontext.gregs [REG_R15];
627
628         mono_handle_exception (&mctx, obj, (gpointer)mctx.rip, test_only);
629
630         ctx->uc_mcontext.gregs [REG_RAX] = mctx.rax;
631         ctx->uc_mcontext.gregs [REG_RBX] = mctx.rbx;
632         ctx->uc_mcontext.gregs [REG_RCX] = mctx.rcx;
633         ctx->uc_mcontext.gregs [REG_RDX] = mctx.rdx;
634         ctx->uc_mcontext.gregs [REG_RBP] = mctx.rbp;
635         ctx->uc_mcontext.gregs [REG_RSP] = mctx.rsp;
636         ctx->uc_mcontext.gregs [REG_RSI] = mctx.rsi;
637         ctx->uc_mcontext.gregs [REG_RDI] = mctx.rdi;
638         ctx->uc_mcontext.gregs [REG_RIP] = mctx.rip;
639         ctx->uc_mcontext.gregs [REG_R12] = mctx.r12;
640         ctx->uc_mcontext.gregs [REG_R13] = mctx.r13;
641         ctx->uc_mcontext.gregs [REG_R14] = mctx.r14;
642         ctx->uc_mcontext.gregs [REG_R15] = mctx.r15;
643
644         return TRUE;
645 }
646
647 gpointer
648 mono_arch_ip_from_context (void *sigctx)
649 {
650         ucontext_t *ctx = (ucontext_t*)sigctx;
651         return (gpointer)ctx->uc_mcontext.gregs [REG_RIP];
652 }
653