2009-05-14 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / exceptions-amd64.c
1 /*
2  * exceptions-amd64.c: exception support for AMD64
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <signal.h>
13 #include <string.h>
14 #ifdef HAVE_UCONTEXT_H
15 #include <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 #include "tasklets.h"
32 #include "debug-mini.h"
33
34 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
35
36 #ifdef PLATFORM_WIN32
37 static MonoW32ExceptionHandler fpe_handler;
38 static MonoW32ExceptionHandler ill_handler;
39 static MonoW32ExceptionHandler segv_handler;
40
41 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
42
43 #define W32_SEH_HANDLE_EX(_ex) \
44         if (_ex##_handler) _ex##_handler((int)sctx)
45
46 /*
47  * Unhandled Exception Filter
48  * Top-level per-process exception handler.
49  */
50 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
51 {
52         EXCEPTION_RECORD* er;
53         CONTEXT* ctx;
54         MonoContext* sctx;
55         LONG res;
56
57         res = EXCEPTION_CONTINUE_EXECUTION;
58
59         er = ep->ExceptionRecord;
60         ctx = ep->ContextRecord;
61         sctx = g_malloc(sizeof(MonoContext));
62
63         /* Copy Win32 context to UNIX style context */
64         sctx->rax = ctx->Rax;
65         sctx->rbx = ctx->Rbx;
66         sctx->rcx = ctx->Rcx;
67         sctx->rdx = ctx->Rdx;
68         sctx->rbp = ctx->Rbp;
69         sctx->rsp = ctx->Rsp;
70         sctx->rsi = ctx->Rsi;
71         sctx->rdi = ctx->Rdi;
72         sctx->rip = ctx->Rip;
73         sctx->r12 = ctx->R12;
74         sctx->r13 = ctx->R13;
75         sctx->r14 = ctx->R14;
76         sctx->r15 = ctx->R15;
77
78         switch (er->ExceptionCode) {
79         case EXCEPTION_ACCESS_VIOLATION:
80                 W32_SEH_HANDLE_EX(segv);
81                 break;
82         case EXCEPTION_ILLEGAL_INSTRUCTION:
83                 W32_SEH_HANDLE_EX(ill);
84                 break;
85         case EXCEPTION_INT_DIVIDE_BY_ZERO:
86         case EXCEPTION_INT_OVERFLOW:
87         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
88         case EXCEPTION_FLT_OVERFLOW:
89         case EXCEPTION_FLT_UNDERFLOW:
90         case EXCEPTION_FLT_INEXACT_RESULT:
91                 W32_SEH_HANDLE_EX(fpe);
92                 break;
93         default:
94                 break;
95         }
96
97         /* Copy context back */
98         /* Nonvolatile */
99         ctx->Rsp = sctx->rsp; 
100         ctx->Rdi = sctx->rdi; 
101         ctx->Rsi = sctx->rsi; 
102         ctx->Rbx = sctx->rbx; 
103         ctx->Rbp = sctx->rbp;
104         ctx->R12 = sctx->r12; 
105         ctx->R13 = sctx->r13; 
106         ctx->R14 = sctx->r14;
107         ctx->R15 = sctx->r15;
108         ctx->Rip = sctx->rip; 
109
110         /* Volatile But should not matter?*/
111         ctx->Rax = sctx->rax; 
112         ctx->Rcx = sctx->rcx; 
113         ctx->Rdx = sctx->rdx;
114
115         g_free (sctx);
116
117         return res;
118 }
119
120 void win32_seh_init()
121 {
122         old_handler = SetUnhandledExceptionFilter(seh_handler);
123 }
124
125 void win32_seh_cleanup()
126 {
127         if (old_handler) SetUnhandledExceptionFilter(old_handler);
128 }
129
130 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
131 {
132         switch (type) {
133         case SIGFPE:
134                 fpe_handler = handler;
135                 break;
136         case SIGILL:
137                 ill_handler = handler;
138                 break;
139         case SIGSEGV:
140                 segv_handler = handler;
141                 break;
142         default:
143                 break;
144         }
145 }
146
147 #endif /* PLATFORM_WIN32 */
148
149 /*
150  * mono_arch_get_restore_context:
151  *
152  * Returns a pointer to a method which restores a previously saved sigcontext.
153  */
154 gpointer
155 mono_arch_get_restore_context_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
156 {
157         guint8 *start = NULL;
158         guint8 *code;
159
160         /* restore_contect (MonoContext *ctx) */
161
162         *ji = NULL;
163
164         start = code = mono_global_codeman_reserve (256);
165
166         amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);
167
168         /* Restore all registers except %rip and %r11 */
169         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rax), 8);
170         amd64_mov_reg_membase (code, AMD64_RCX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rcx), 8);
171         amd64_mov_reg_membase (code, AMD64_RDX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rdx), 8);
172         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
173         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
174         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
175         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
176         //amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r8), 8);
177         //amd64_mov_reg_membase (code, AMD64_R9, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r9), 8);
178         //amd64_mov_reg_membase (code, AMD64_R10, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r10), 8);
179         amd64_mov_reg_membase (code, AMD64_R12, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r12), 8);
180         amd64_mov_reg_membase (code, AMD64_R13, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r13), 8);
181         amd64_mov_reg_membase (code, AMD64_R14, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r14), 8);
182         amd64_mov_reg_membase (code, AMD64_R15, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, r15), 8);
183
184         if (mono_running_on_valgrind ()) {
185                 /* Prevent 'Address 0x... is just below the stack ptr.' errors */
186                 amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
187                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rip), 8);
188                 amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8);
189         } else {
190                 amd64_mov_reg_membase (code, AMD64_RSP, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
191                 /* get return address */
192                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  G_STRUCT_OFFSET (MonoContext, rip), 8);
193         }
194
195         /* jump to the saved IP */
196         amd64_jump_reg (code, AMD64_R11);
197
198         mono_arch_flush_icache (start, code - start);
199
200         *code_size = code - start;
201
202         return start;
203 }
204
205 /*
206  * mono_arch_get_call_filter:
207  *
208  * Returns a pointer to a method which calls an exception filter. We
209  * also use this function to call finally handlers (we pass NULL as 
210  * @exc object in this case).
211  */
212 gpointer
213 mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
214 {
215         guint8 *start;
216         int i;
217         guint8 *code;
218         guint32 pos;
219
220         *ji = NULL;
221
222         start = code = mono_global_codeman_reserve (128);
223
224         /* call_filter (MonoContext *ctx, unsigned long eip) */
225         code = start;
226
227         /* Alloc new frame */
228         amd64_push_reg (code, AMD64_RBP);
229         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
230
231         /* Save callee saved regs */
232         pos = 0;
233         for (i = 0; i < AMD64_NREG; ++i)
234                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
235                         amd64_push_reg (code, i);
236                         pos += 8;
237                 }
238
239         /* Save EBP */
240         pos += 8;
241         amd64_push_reg (code, AMD64_RBP);
242
243         /* Make stack misaligned, the call will make it aligned again */
244         if (! (pos & 8))
245                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
246
247         /* set new EBP */
248         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbp), 8);
249         /* load callee saved regs */
250         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, rbx), 8);
251         amd64_mov_reg_membase (code, AMD64_R12, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r12), 8);
252         amd64_mov_reg_membase (code, AMD64_R13, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r13), 8);
253         amd64_mov_reg_membase (code, AMD64_R14, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r14), 8);
254         amd64_mov_reg_membase (code, AMD64_R15, AMD64_ARG_REG1, G_STRUCT_OFFSET (MonoContext, r15), 8);
255 #ifdef PLATFORM_WIN32
256         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rdi), 8);
257         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_ARG_REG1,  G_STRUCT_OFFSET (MonoContext, rsi), 8);
258 #endif
259
260         /* call the handler */
261         amd64_call_reg (code, AMD64_ARG_REG2);
262
263         if (! (pos & 8))
264                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
265
266         /* restore RBP */
267         amd64_pop_reg (code, AMD64_RBP);
268
269         /* Restore callee saved regs */
270         for (i = AMD64_NREG; i >= 0; --i)
271                 if (AMD64_IS_CALLEE_SAVED_REG (i))
272                         amd64_pop_reg (code, i);
273
274         amd64_leave (code);
275         amd64_ret (code);
276
277         g_assert ((code - start) < 128);
278
279         mono_arch_flush_icache (start, code - start);
280
281         *code_size = code - start;
282
283         return start;
284 }
285
286 /* 
287  * The first few arguments are dummy, to force the other arguments to be passed on
288  * the stack, this avoids overwriting the argument registers in the throw trampoline.
289  */
290 void
291 mono_amd64_throw_exception (guint64 dummy1, guint64 dummy2, guint64 dummy3, guint64 dummy4,
292                                                         guint64 dummy5, guint64 dummy6,
293                                                         MonoObject *exc, guint64 rip, guint64 rsp,
294                                                         guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
295                                                         guint64 r14, guint64 r15, guint64 rdi, guint64 rsi, 
296                                                         guint64 rax, guint64 rcx, guint64 rdx,
297                                                         guint64 rethrow)
298 {
299         static void (*restore_context) (MonoContext *);
300         MonoContext ctx;
301
302         if (!restore_context)
303                 restore_context = mono_get_restore_context ();
304
305         ctx.rsp = rsp;
306         ctx.rip = rip;
307         ctx.rbx = rbx;
308         ctx.rbp = rbp;
309         ctx.r12 = r12;
310         ctx.r13 = r13;
311         ctx.r14 = r14;
312         ctx.r15 = r15;
313         ctx.rdi = rdi;
314         ctx.rsi = rsi;
315         ctx.rax = rax;
316         ctx.rcx = rcx;
317         ctx.rdx = rdx;
318
319         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
320                 MonoException *mono_ex = (MonoException*)exc;
321                 if (!rethrow)
322                         mono_ex->stack_trace = NULL;
323         }
324
325         if (mono_debug_using_mono_debugger ()) {
326                 guint8 buf [16], *code;
327
328                 mono_breakpoint_clean_code (NULL, (gpointer)rip, 8, buf, sizeof (buf));
329                 code = buf + 8;
330
331                 if (buf [3] == 0xe8) {
332                         MonoContext ctx_cp = ctx;
333                         ctx_cp.rip = rip - 5;
334
335                         if (mono_debugger_handle_exception (&ctx_cp, exc)) {
336                                 restore_context (&ctx_cp);
337                                 g_assert_not_reached ();
338                         }
339                 }
340         }
341
342         /* adjust eip so that it point into the call instruction */
343         ctx.rip -= 1;
344
345         mono_handle_exception (&ctx, exc, (gpointer)rip, FALSE);
346         restore_context (&ctx);
347
348         g_assert_not_reached ();
349 }
350
351 static gpointer
352 get_throw_trampoline (gboolean rethrow, guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
353 {
354         guint8* start;
355         guint8 *code;
356
357         start = code = mono_global_codeman_reserve (64);
358
359         code = start;
360
361         *ji = NULL;
362
363         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, 8);
364
365         /* reverse order */
366         amd64_push_imm (code, rethrow);
367         amd64_push_reg (code, AMD64_RDX);
368         amd64_push_reg (code, AMD64_RCX);
369         amd64_push_reg (code, AMD64_RAX);
370         amd64_push_reg (code, AMD64_RSI);
371         amd64_push_reg (code, AMD64_RDI);
372         amd64_push_reg (code, AMD64_R15);
373         amd64_push_reg (code, AMD64_R14);
374         amd64_push_reg (code, AMD64_R13);
375         amd64_push_reg (code, AMD64_R12);
376         amd64_push_reg (code, AMD64_RBP);
377         amd64_push_reg (code, AMD64_RBX);
378
379         /* SP */
380         amd64_lea_membase (code, AMD64_RAX, AMD64_R11, 8);
381         amd64_push_reg (code, AMD64_RAX);
382
383         /* IP */
384         amd64_push_membase (code, AMD64_R11, 0);
385
386         /* Exception */
387         amd64_push_reg (code, AMD64_ARG_REG1);
388
389 #ifdef PLATFORM_WIN32
390         /* align stack */
391         amd64_push_imm (code, 0);
392         amd64_push_imm (code, 0);
393         amd64_push_imm (code, 0);
394         amd64_push_imm (code, 0);
395         amd64_push_imm (code, 0);
396         amd64_push_imm (code, 0);
397 #endif
398
399         if (aot) {
400                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_throw_exception");
401                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
402         } else {
403                 amd64_mov_reg_imm (code, AMD64_R11, mono_amd64_throw_exception);
404         }
405         amd64_call_reg (code, AMD64_R11);
406         amd64_breakpoint (code);
407
408         mono_arch_flush_icache (start, code - start);
409
410         g_assert ((code - start) < 64);
411
412         *code_size = code - start;
413
414         return start;
415 }
416
417 /**
418  * mono_arch_get_throw_exception:
419  *
420  * Returns a function pointer which can be used to raise 
421  * exceptions. The returned function has the following 
422  * signature: void (*func) (MonoException *exc); 
423  *
424  */
425 gpointer 
426 mono_arch_get_throw_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
427 {
428         return get_throw_trampoline (FALSE, code_size, ji, aot);
429 }
430
431 gpointer 
432 mono_arch_get_rethrow_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
433 {
434         return get_throw_trampoline (TRUE, code_size, ji, aot);
435 }
436
437 gpointer 
438 mono_arch_get_throw_exception_by_name_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
439 {       
440         guint8* start;
441         guint8 *code;
442
443         start = code = mono_global_codeman_reserve (64);
444
445         *ji = NULL;
446
447         /* Not used on amd64 */
448         amd64_breakpoint (code);
449
450         mono_arch_flush_icache (start, code - start);
451
452         *code_size = code - start;
453
454         return start;
455 }
456
457 /**
458  * mono_arch_get_throw_corlib_exception:
459  *
460  * Returns a function pointer which can be used to raise 
461  * corlib exceptions. The returned function has the following 
462  * signature: void (*func) (guint32 ex_token, guint32 offset); 
463  * Here, offset is the offset which needs to be substracted from the caller IP 
464  * to get the IP of the throw. Passing the offset has the advantage that it 
465  * needs no relocations in the caller.
466  */
467 gpointer 
468 mono_arch_get_throw_corlib_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
469 {
470         static guint8* start;
471         guint8 *code;
472         guint64 throw_ex;
473
474         start = code = mono_global_codeman_reserve (64);
475
476         *ji = NULL;
477
478         /* Push throw_ip */
479         amd64_push_reg (code, AMD64_ARG_REG2);
480
481         /* Call exception_from_token */
482         amd64_mov_reg_reg (code, AMD64_ARG_REG2, AMD64_ARG_REG1, 8);
483         if (aot) {
484                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_IMAGE, mono_defaults.exception_class->image);
485                 amd64_mov_reg_membase (code, AMD64_ARG_REG1, AMD64_RIP, 0, 8);
486                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_exception_from_token");
487                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
488         } else {
489                 amd64_mov_reg_imm (code, AMD64_ARG_REG1, mono_defaults.exception_class->image);
490                 amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_token);
491         }
492 #ifdef PLATFORM_WIN32
493         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 32);
494 #endif
495         amd64_call_reg (code, AMD64_R11);
496 #ifdef PLATFORM_WIN32
497         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 32);
498 #endif
499
500         /* Compute throw_ip */
501         amd64_pop_reg (code, AMD64_ARG_REG2);
502         /* return addr */
503         amd64_pop_reg (code, AMD64_ARG_REG3);
504         amd64_alu_reg_reg (code, X86_SUB, AMD64_ARG_REG3, AMD64_ARG_REG2);
505
506         /* Put the throw_ip at the top of the misaligned stack */
507         amd64_push_reg (code, AMD64_ARG_REG3);
508
509         throw_ex = (guint64)mono_get_throw_exception ();
510
511         /* Call throw_exception */
512         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_RAX, 8);
513         if (aot) {
514                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_throw_exception");
515                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
516         } else {
517                 amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
518         }
519         /* The original IP is on the stack */
520         amd64_jump_reg (code, AMD64_R11);
521
522         g_assert ((code - start) < 64);
523
524         mono_arch_flush_icache (start, code - start);
525
526         *code_size = code - start;
527
528         return start;
529 }
530
531 /* mono_arch_find_jit_info:
532  *
533  * This function is used to gather information from @ctx. It return the 
534  * MonoJitInfo of the corresponding function, unwinds one stack frame and
535  * stores the resulting context into @new_ctx. It also stores a string 
536  * describing the stack location into @trace (if not NULL), and modifies
537  * the @lmf if necessary. @native_offset return the IP offset from the 
538  * start of the function or -1 if that info is not available.
539  */
540 MonoJitInfo *
541 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
542                          MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
543 {
544         MonoJitInfo *ji;
545         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
546
547         /* Avoid costly table lookup during stack overflow */
548         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
549                 ji = prev_ji;
550         else
551                 ji = mono_jit_info_table_find (domain, ip);
552
553         if (managed)
554                 *managed = FALSE;
555
556         *new_ctx = *ctx;
557
558         if (ji != NULL) {
559                 gssize regs [MONO_MAX_IREGS + 1];
560                 guint8 *cfa;
561                 guint32 unwind_info_len;
562                 guint8 *unwind_info;
563
564                 if (managed)
565                         if (!ji->method->wrapper_type)
566                                 *managed = TRUE;
567
568                 if (ji->from_aot)
569                         unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
570                 else
571                         unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
572  
573                 regs [AMD64_RAX] = new_ctx->rax;
574                 regs [AMD64_RBX] = new_ctx->rbx;
575                 regs [AMD64_RCX] = new_ctx->rcx;
576                 regs [AMD64_RDX] = new_ctx->rdx;
577                 regs [AMD64_RBP] = new_ctx->rbp;
578                 regs [AMD64_RSP] = new_ctx->rsp;
579                 regs [AMD64_RSI] = new_ctx->rsi;
580                 regs [AMD64_RDI] = new_ctx->rdi;
581                 regs [AMD64_RIP] = new_ctx->rip;
582                 regs [AMD64_R12] = new_ctx->r12;
583                 regs [AMD64_R13] = new_ctx->r13;
584                 regs [AMD64_R14] = new_ctx->r14;
585                 regs [AMD64_R15] = new_ctx->r15;
586
587                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
588                                                    (guint8*)ji->code_start + ji->code_size,
589                                                    ip, regs, MONO_MAX_IREGS + 1, &cfa);
590
591                 new_ctx->rax = regs [AMD64_RAX];
592                 new_ctx->rbx = regs [AMD64_RBX];
593                 new_ctx->rcx = regs [AMD64_RCX];
594                 new_ctx->rdx = regs [AMD64_RDX];
595                 new_ctx->rbp = regs [AMD64_RBP];
596                 new_ctx->rsp = regs [AMD64_RSP];
597                 new_ctx->rsi = regs [AMD64_RSI];
598                 new_ctx->rdi = regs [AMD64_RDI];
599                 new_ctx->rip = regs [AMD64_RIP];
600                 new_ctx->r12 = regs [AMD64_R12];
601                 new_ctx->r13 = regs [AMD64_R13];
602                 new_ctx->r14 = regs [AMD64_R14];
603                 new_ctx->r15 = regs [AMD64_R15];
604  
605                 /* The CFA becomes the new SP value */
606                 new_ctx->rsp = (gssize)cfa;
607
608                 /* Adjust IP */
609                 new_ctx->rip --;
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 #ifndef MONO_AMD64_NO_PUSHES
617                 /* Pop arguments off the stack */
618                 {
619                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
620
621                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
622                         new_ctx->rsp += stack_to_pop;
623                 }
624 #endif
625
626                 return ji;
627         } else if (*lmf) {
628                 guint64 rip;
629
630                 if (((guint64)(*lmf)->previous_lmf) & 1) {
631                         /* This LMF has the rip field set */
632                         rip = (*lmf)->rip;
633                 } else if ((*lmf)->rsp == 0) {
634                         /* Top LMF entry */
635                         return (gpointer)-1;
636                 } else {
637                         /* 
638                          * The rsp field is set just before the call which transitioned to native 
639                          * code. Obtain the rip from the stack.
640                          */
641                         rip = *(guint64*)((*lmf)->rsp - sizeof (gpointer));
642                 }
643
644                 ji = mono_jit_info_table_find (domain, (gpointer)rip);
645                 if (!ji) {
646                         // FIXME: This can happen with multiple appdomains (bug #444383)
647                         return (gpointer)-1;
648                 }
649
650                 new_ctx->rip = rip;
651                 new_ctx->rbp = (*lmf)->rbp;
652                 new_ctx->rsp = (*lmf)->rsp;
653
654                 new_ctx->rbx = (*lmf)->rbx;
655                 new_ctx->r12 = (*lmf)->r12;
656                 new_ctx->r13 = (*lmf)->r13;
657                 new_ctx->r14 = (*lmf)->r14;
658                 new_ctx->r15 = (*lmf)->r15;
659 #ifdef PLATFORM_WIN32
660                 new_ctx->rdi = (*lmf)->rdi;
661                 new_ctx->rsi = (*lmf)->rsi;
662 #endif
663
664                 *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~1);
665
666                 return ji ? ji : res;
667         }
668
669         return NULL;
670 }
671
672 /**
673  * mono_arch_handle_exception:
674  *
675  * @ctx: saved processor state
676  * @obj: the exception object
677  */
678 gboolean
679 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
680 {
681         MonoContext mctx;
682
683         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
684
685         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj))
686                 return TRUE;
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 = (guint64)restore_soft_guard_pages;
828         mctx->rsp = (guint64)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_get_restore_context ();
838         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
839
840         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) {
841                 if (stack_ovf)
842                         prepare_for_guard_pages (&mctx);
843                 restore_context (&mctx);
844         }
845
846         mono_handle_exception (&mctx, obj, MONO_CONTEXT_GET_IP (&mctx), FALSE);
847         if (stack_ovf)
848                 prepare_for_guard_pages (&mctx);
849         restore_context (&mctx);
850 }
851
852 void
853 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
854 {
855 #ifdef MONO_ARCH_USE_SIGACTION
856         MonoException *exc = NULL;
857         ucontext_t *ctx = (ucontext_t*)sigctx;
858         guint64 *gregs = gregs_from_ucontext (ctx);
859         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)gregs [REG_RIP]);
860         gpointer *sp;
861         int frame_size;
862
863         if (stack_ovf)
864                 exc = mono_domain_get ()->stack_overflow_ex;
865         if (!ji)
866                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
867
868         /* setup a call frame on the real stack so that control is returned there
869          * and exception handling can continue.
870          * The frame looks like:
871          *   ucontext struct
872          *   ...
873          *   return ip
874          * 128 is the size of the red zone
875          */
876         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4 + 128;
877         frame_size += 15;
878         frame_size &= ~15;
879         sp = (gpointer)(gregs [REG_RSP] & ~15);
880         sp = (gpointer)((char*)sp - frame_size);
881         /* the arguments must be aligned */
882         sp [-1] = (gpointer)gregs [REG_RIP];
883         /* may need to adjust pointers in the new struct copy, depending on the OS */
884         memcpy (sp + 4, ctx, sizeof (ucontext_t));
885         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
886         gregs [REG_RIP] = (unsigned long)altstack_handle_and_restore;
887         gregs [REG_RSP] = (unsigned long)(sp - 1);
888         gregs [REG_RDI] = (unsigned long)(sp + 4);
889         gregs [REG_RSI] = (guint64)exc;
890         gregs [REG_RDX] = stack_ovf;
891 #endif
892 }
893
894 guint64
895 mono_amd64_get_original_ip (void)
896 {
897         MonoLMF *lmf = mono_get_lmf ();
898
899         g_assert (lmf);
900
901         /* Reset the change to previous_lmf */
902         lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf & ~1);
903
904         return lmf->rip;
905 }
906
907 gpointer
908 mono_arch_get_throw_pending_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
909 {
910         guint8 *code, *start;
911         guint8 *br[1];
912         gpointer throw_trampoline;
913
914         *ji = NULL;
915
916         start = code = mono_global_codeman_reserve (128);
917
918         /* We are in the frame of a managed method after a call */
919         /* 
920          * We would like to throw the pending exception in such a way that it looks to
921          * be thrown from the managed method.
922          */
923
924         /* Save registers which might contain the return value of the call */
925         amd64_push_reg (code, AMD64_RAX);
926         amd64_push_reg (code, AMD64_RDX);
927
928         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
929         amd64_movsd_membase_reg (code, AMD64_RSP, 0, AMD64_XMM0);
930
931         /* Align stack */
932         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
933
934         /* Obtain the pending exception */
935         if (aot) {
936                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_get_and_clear_pending_exception");
937                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
938         } else {
939                 amd64_mov_reg_imm (code, AMD64_R11, mono_thread_get_and_clear_pending_exception);
940         }
941         amd64_call_reg (code, AMD64_R11);
942
943         /* Check if it is NULL, and branch */
944         amd64_alu_reg_imm (code, X86_CMP, AMD64_RAX, 0);
945         br[0] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE);
946
947         /* exc != NULL branch */
948
949         /* Save the exc on the stack */
950         amd64_push_reg (code, AMD64_RAX);
951         /* Align stack */
952         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
953
954         /* Obtain the original ip and clear the flag in previous_lmf */
955         if (aot) {
956                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_get_original_ip");
957                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
958         } else {
959                 amd64_mov_reg_imm (code, AMD64_R11, mono_amd64_get_original_ip);
960         }
961         amd64_call_reg (code, AMD64_R11);       
962
963         /* Load exc */
964         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, 8, 8);
965
966         /* Pop saved stuff from the stack */
967         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 6 * 8);
968
969         /* Setup arguments for the throw trampoline */
970         /* Exception */
971         amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_R11, 8);
972         /* The trampoline expects the caller ip to be pushed on the stack */
973         amd64_push_reg (code, AMD64_RAX);
974
975         /* Call the throw trampoline */
976         if (aot) {
977                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_throw_exception");
978                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
979         } else {
980                 throw_trampoline = mono_get_throw_exception ();
981                 amd64_mov_reg_imm (code, AMD64_R11, throw_trampoline);
982         }
983         /* We use a jump instead of a call so we can push the original ip on the stack */
984         amd64_jump_reg (code, AMD64_R11);
985
986         /* ex == NULL branch */
987         mono_amd64_patch (br [0], code);
988
989         /* Obtain the original ip and clear the flag in previous_lmf */
990         if (aot) {
991                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_get_original_ip");
992                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
993         } else {
994                 amd64_mov_reg_imm (code, AMD64_R11, mono_amd64_get_original_ip);
995         }
996         amd64_call_reg (code, AMD64_R11);       
997         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, 8);
998
999         /* Restore registers */
1000         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
1001         amd64_movsd_reg_membase (code, AMD64_XMM0, AMD64_RSP, 0);
1002         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
1003         amd64_pop_reg (code, AMD64_RDX);
1004         amd64_pop_reg (code, AMD64_RAX);
1005
1006         /* Return to original code */
1007         amd64_jump_reg (code, AMD64_R11);
1008
1009         g_assert ((code - start) < 128);
1010
1011         *code_size = code - start;
1012
1013         return start;
1014 }
1015
1016 static gpointer throw_pending_exception;
1017
1018 /*
1019  * Called when a thread receives an async exception while executing unmanaged code.
1020  * Instead of checking for this exception in the managed-to-native wrapper, we hijack 
1021  * the return address on the stack to point to a helper routine which throws the
1022  * exception.
1023  */
1024 void
1025 mono_arch_notify_pending_exc (void)
1026 {
1027         MonoLMF *lmf = mono_get_lmf ();
1028
1029         if (lmf->rsp == 0)
1030                 /* Initial LMF */
1031                 return;
1032
1033         if ((guint64)lmf->previous_lmf & 1)
1034                 /* Already hijacked or trampoline LMF entry */
1035                 return;
1036
1037         /* lmf->rsp is set just before making the call which transitions to unmanaged code */
1038         lmf->rip = *(guint64*)(lmf->rsp - 8);
1039         /* Signal that lmf->rip is set */
1040         lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf | 1);
1041
1042         *(gpointer*)(lmf->rsp - 8) = throw_pending_exception;
1043 }
1044
1045 void
1046 mono_arch_exceptions_init (void)
1047 {
1048         guint32 code_size;
1049         MonoJumpInfo *ji;
1050
1051         if (mono_aot_only) {
1052                 throw_pending_exception = mono_aot_get_named_code ("throw_pending_exception");
1053         } else {
1054                 /* Call this to avoid initialization races */
1055                 throw_pending_exception = mono_arch_get_throw_pending_exception_full (&code_size, &ji, FALSE);
1056         }
1057 }
1058
1059 #ifdef PLATFORM_WIN32
1060
1061 /*
1062  * The mono_arch_unwindinfo* methods are used to build and add
1063  * function table info for each emitted method from mono.  On Winx64
1064  * the seh handler will not be called if the mono methods are not
1065  * added to the function table.  
1066  *
1067  * We should not need to add non-volatile register info to the 
1068  * table since mono stores that info elsewhere. (Except for the register 
1069  * used for the fp.)
1070  */
1071
1072 #define MONO_MAX_UNWIND_CODES 22
1073
1074 typedef union _UNWIND_CODE {
1075     struct {
1076         guchar CodeOffset;
1077         guchar UnwindOp : 4;
1078         guchar OpInfo   : 4;
1079     };
1080     gushort FrameOffset;
1081 } UNWIND_CODE, *PUNWIND_CODE;
1082
1083 typedef struct _UNWIND_INFO {
1084         guchar Version       : 3;
1085         guchar Flags         : 5;
1086         guchar SizeOfProlog;
1087         guchar CountOfCodes;
1088         guchar FrameRegister : 4;
1089         guchar FrameOffset   : 4;
1090         /* custom size for mono allowing for mono allowing for*/
1091         /*UWOP_PUSH_NONVOL ebp offset = 21*/
1092         /*UWOP_ALLOC_LARGE : requires 2 or 3 offset = 20*/
1093         /*UWOP_SET_FPREG : requires 2 offset = 17*/
1094         /*UWOP_PUSH_NONVOL offset = 15-0*/
1095         UNWIND_CODE UnwindCode[MONO_MAX_UNWIND_CODES]; 
1096
1097 /*      UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
1098  *      union {
1099  *          OPTIONAL ULONG ExceptionHandler;
1100  *          OPTIONAL ULONG FunctionEntry;
1101  *      };
1102  *      OPTIONAL ULONG ExceptionData[]; */
1103 } UNWIND_INFO, *PUNWIND_INFO;
1104
1105 typedef struct
1106 {
1107         RUNTIME_FUNCTION runtimeFunction;
1108         UNWIND_INFO unwindInfo;
1109 } MonoUnwindInfo, *PMonoUnwindInfo;
1110
1111 static void
1112 mono_arch_unwindinfo_create (gpointer* monoui)
1113 {
1114         PMonoUnwindInfo newunwindinfo;
1115         *monoui = newunwindinfo = g_new0 (MonoUnwindInfo, 1);
1116         newunwindinfo->unwindInfo.Version = 1;
1117 }
1118
1119 void
1120 mono_arch_unwindinfo_add_push_nonvol (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg )
1121 {
1122         PMonoUnwindInfo unwindinfo;
1123         PUNWIND_CODE unwindcode;
1124         guchar codeindex;
1125         if (!*monoui)
1126                 mono_arch_unwindinfo_create (monoui);
1127         
1128         unwindinfo = (MonoUnwindInfo*)*monoui;
1129
1130         if (unwindinfo->unwindInfo.CountOfCodes >= MONO_MAX_UNWIND_CODES)
1131                 g_error ("Larger allocation needed for the unwind information.");
1132
1133         codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->unwindInfo.CountOfCodes);
1134         unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
1135         unwindcode->UnwindOp = 0; /*UWOP_PUSH_NONVOL*/
1136         unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
1137         unwindcode->OpInfo = reg;
1138
1139         if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
1140                 g_error ("Adding unwind info in wrong order.");
1141         
1142         unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
1143 }
1144
1145 void
1146 mono_arch_unwindinfo_add_set_fpreg (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg )
1147 {
1148         PMonoUnwindInfo unwindinfo;
1149         PUNWIND_CODE unwindcode;
1150         guchar codeindex;
1151         if (!*monoui)
1152                 mono_arch_unwindinfo_create (monoui);
1153         
1154         unwindinfo = (MonoUnwindInfo*)*monoui;
1155
1156         if (unwindinfo->unwindInfo.CountOfCodes + 1 >= MONO_MAX_UNWIND_CODES)
1157                 g_error ("Larger allocation needed for the unwind information.");
1158
1159         codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->unwindInfo.CountOfCodes += 2);
1160         unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
1161         unwindcode->FrameOffset = 0; /*Assuming no frame pointer offset for mono*/
1162         unwindcode++;
1163         unwindcode->UnwindOp = 3; /*UWOP_SET_FPREG*/
1164         unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
1165         unwindcode->OpInfo = reg;
1166         
1167         unwindinfo->unwindInfo.FrameRegister = reg;
1168
1169         if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
1170                 g_error ("Adding unwind info in wrong order.");
1171         
1172         unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
1173 }
1174
1175 void
1176 mono_arch_unwindinfo_add_alloc_stack (gpointer* monoui, gpointer codebegin, gpointer nextip, guint size )
1177 {
1178         PMonoUnwindInfo unwindinfo;
1179         PUNWIND_CODE unwindcode;
1180         guchar codeindex;
1181         guchar codesneeded;
1182         if (!*monoui)
1183                 mono_arch_unwindinfo_create (monoui);
1184         
1185         unwindinfo = (MonoUnwindInfo*)*monoui;
1186
1187         if (size < 0x8)
1188                 g_error ("Stack allocation must be equal to or greater than 0x8.");
1189         
1190         if (size <= 0x80)
1191                 codesneeded = 1;
1192         else if (size <= 0x7FFF8)
1193                 codesneeded = 2;
1194         else
1195                 codesneeded = 3;
1196         
1197         if (unwindinfo->unwindInfo.CountOfCodes + codesneeded > MONO_MAX_UNWIND_CODES)
1198                 g_error ("Larger allocation needed for the unwind information.");
1199
1200         codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->unwindInfo.CountOfCodes += codesneeded);
1201         unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
1202
1203         if (codesneeded == 1) {
1204                 /*The size of the allocation is 
1205                   (the number in the OpInfo member) times 8 plus 8*/
1206                 unwindcode->OpInfo = (size - 8)/8;
1207                 unwindcode->UnwindOp = 2; /*UWOP_ALLOC_SMALL*/
1208         }
1209         else {
1210                 if (codesneeded == 3) {
1211                         /*the unscaled size of the allocation is recorded
1212                           in the next two slots in little-endian format*/
1213                         *((unsigned int*)(&unwindcode->FrameOffset)) = size;
1214                         unwindcode += 2;
1215                         unwindcode->OpInfo = 1;
1216                 }
1217                 else {
1218                         /*the size of the allocation divided by 8
1219                           is recorded in the next slot*/
1220                         unwindcode->FrameOffset = size/8; 
1221                         unwindcode++;   
1222                         unwindcode->OpInfo = 0;
1223                         
1224                 }
1225                 unwindcode->UnwindOp = 1; /*UWOP_ALLOC_LARGE*/
1226         }
1227
1228         unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
1229
1230         if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
1231                 g_error ("Adding unwind info in wrong order.");
1232         
1233         unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
1234 }
1235
1236 guint
1237 mono_arch_unwindinfo_get_size (gpointer monoui)
1238 {
1239         PMonoUnwindInfo unwindinfo;
1240         if (!monoui)
1241                 return 0;
1242         
1243         unwindinfo = (MonoUnwindInfo*)monoui;
1244         return (8 + sizeof (MonoUnwindInfo)) - 
1245                 (sizeof (UNWIND_CODE) * (MONO_MAX_UNWIND_CODES - unwindinfo->unwindInfo.CountOfCodes));
1246 }
1247
1248 PRUNTIME_FUNCTION
1249 MONO_GET_RUNTIME_FUNCTION_CALLBACK ( DWORD64 ControlPc, IN PVOID Context )
1250 {
1251         MonoJitInfo *ji;
1252         guint64 pos;
1253         PMonoUnwindInfo targetinfo;
1254         MonoDomain *domain = mono_domain_get ();
1255
1256         ji = mono_jit_info_table_find (domain, (char*)ControlPc);
1257         if (!ji)
1258                 return 0;
1259
1260         pos = (guint64)(((char*)ji->code_start) + ji->code_size);
1261         
1262         targetinfo = (PMonoUnwindInfo)ALIGN_TO (pos, 8);
1263
1264         targetinfo->runtimeFunction.UnwindData = ((DWORD64)&targetinfo->unwindInfo) - ((DWORD64)Context);
1265
1266         return &targetinfo->runtimeFunction;
1267 }
1268
1269 void
1270 mono_arch_unwindinfo_install_unwind_info (gpointer* monoui, gpointer code, guint code_size)
1271 {
1272         PMonoUnwindInfo unwindinfo, targetinfo;
1273         guchar codecount;
1274         guint64 targetlocation;
1275         if (!*monoui)
1276                 return;
1277
1278         unwindinfo = (MonoUnwindInfo*)*monoui;
1279         targetlocation = (guint64)&(((guchar*)code)[code_size]);
1280         targetinfo = (PMonoUnwindInfo) ALIGN_TO(targetlocation, 8);
1281
1282         unwindinfo->runtimeFunction.EndAddress = code_size;
1283         unwindinfo->runtimeFunction.UnwindData = ((guchar*)&targetinfo->unwindInfo) - ((guchar*)code);
1284         
1285         memcpy (targetinfo, unwindinfo, sizeof (MonoUnwindInfo) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES));
1286         
1287         codecount = unwindinfo->unwindInfo.CountOfCodes;
1288         if (codecount) {
1289                 memcpy (&targetinfo->unwindInfo.UnwindCode[0], &unwindinfo->unwindInfo.UnwindCode[MONO_MAX_UNWIND_CODES-codecount], 
1290                         sizeof (UNWIND_CODE) * unwindinfo->unwindInfo.CountOfCodes);
1291         }
1292
1293         g_free (unwindinfo);
1294         *monoui = 0;
1295
1296         RtlInstallFunctionTableCallback (((DWORD64)code) | 0x3, (DWORD64)code, code_size, MONO_GET_RUNTIME_FUNCTION_CALLBACK, code, NULL);
1297 }
1298
1299 #endif
1300
1301 #if MONO_SUPPORT_TASKLETS
1302 MonoContinuationRestore
1303 mono_tasklets_arch_restore (void)
1304 {
1305         static guint8* saved = NULL;
1306         guint8 *code, *start;
1307         int cont_reg = AMD64_R9; /* register usable on both call conventions */
1308
1309         if (saved)
1310                 return (MonoContinuationRestore)saved;
1311         code = start = mono_global_codeman_reserve (64);
1312         /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
1313         /* cont is in AMD64_ARG_REG1 ($rcx or $rdi)
1314          * state is in AMD64_ARG_REG2 ($rdx or $rsi)
1315          * lmf_addr is in AMD64_ARG_REG3 ($r8 or $rdx)
1316          * We move cont to cont_reg since we need both rcx and rdi for the copy
1317          * state is moved to $rax so it's setup as the return value and we can overwrite $rsi
1318          */
1319         amd64_mov_reg_reg (code, cont_reg, MONO_AMD64_ARG_REG1, 8);
1320         amd64_mov_reg_reg (code, AMD64_RAX, MONO_AMD64_ARG_REG2, 8);
1321         /* setup the copy of the stack */
1322         amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, G_STRUCT_OFFSET (MonoContinuation, stack_used_size), sizeof (int));
1323         amd64_shift_reg_imm (code, X86_SHR, AMD64_RCX, 3);
1324         x86_cld (code);
1325         amd64_mov_reg_membase (code, AMD64_RSI, cont_reg, G_STRUCT_OFFSET (MonoContinuation, saved_stack), sizeof (gpointer));
1326         amd64_mov_reg_membase (code, AMD64_RDI, cont_reg, G_STRUCT_OFFSET (MonoContinuation, return_sp), sizeof (gpointer));
1327         amd64_prefix (code, X86_REP_PREFIX);
1328         amd64_movsl (code);
1329
1330         /* now restore the registers from the LMF */
1331         amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, G_STRUCT_OFFSET (MonoContinuation, lmf), 8);
1332         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, rbx), 8);
1333         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, rbp), 8);
1334         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, r12), 8);
1335         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, r13), 8);
1336         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, r14), 8);
1337         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, r15), 8);
1338 #ifdef PLATFORM_WIN32
1339         amd64_mov_reg_membase (code, AMD64_RDI, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, rdi), 8);
1340         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, rsi), 8);
1341 #endif
1342         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RCX, G_STRUCT_OFFSET (MonoLMF, rsp), 8);
1343
1344         /* restore the lmf chain */
1345         /*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
1346         x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/
1347
1348         /* state is already in rax */
1349         amd64_jump_membase (code, cont_reg, G_STRUCT_OFFSET (MonoContinuation, return_ip));
1350         g_assert ((code - start) <= 64);
1351         saved = start;
1352         return (MonoContinuationRestore)saved;
1353 }
1354 #endif
1355