Shorten function name length for static functions.
[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  *   Johan Lorensson (lateralusx.github@gmail.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #include <config.h>
14
15 #include <glib.h>
16 #include <string.h>
17
18 #ifdef HAVE_SIGNAL_H
19 #include <signal.h>
20 #endif
21 #ifdef HAVE_UCONTEXT_H
22 #include <ucontext.h>
23 #endif
24
25 #include <mono/arch/amd64/amd64-codegen.h>
26 #include <mono/metadata/abi-details.h>
27 #include <mono/metadata/appdomain.h>
28 #include <mono/metadata/tabledefs.h>
29 #include <mono/metadata/threads.h>
30 #include <mono/metadata/threads-types.h>
31 #include <mono/metadata/debug-helpers.h>
32 #include <mono/metadata/exception.h>
33 #include <mono/metadata/gc-internals.h>
34 #include <mono/metadata/mono-debug.h>
35 #include <mono/utils/mono-mmap.h>
36
37 #include "mini.h"
38 #include "mini-amd64.h"
39 #include "tasklets.h"
40
41 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
42
43 #ifdef TARGET_WIN32
44 static MonoW32ExceptionHandler fpe_handler;
45 static MonoW32ExceptionHandler ill_handler;
46 static MonoW32ExceptionHandler segv_handler;
47
48 LPTOP_LEVEL_EXCEPTION_FILTER mono_old_win_toplevel_exception_filter;
49 void *mono_win_vectored_exception_handle;
50
51 #define W32_SEH_HANDLE_EX(_ex) \
52         if (_ex##_handler) _ex##_handler(0, ep, ctx)
53
54 static LONG CALLBACK seh_unhandled_exception_filter(EXCEPTION_POINTERS* ep)
55 {
56 #ifndef MONO_CROSS_COMPILE
57         if (mono_old_win_toplevel_exception_filter) {
58                 return (*mono_old_win_toplevel_exception_filter)(ep);
59         }
60 #endif
61
62         mono_handle_native_crash ("SIGSEGV", NULL, NULL);
63
64         return EXCEPTION_CONTINUE_SEARCH;
65 }
66
67 /*
68  * Unhandled Exception Filter
69  * Top-level per-process exception handler.
70  */
71 static LONG CALLBACK seh_vectored_exception_handler(EXCEPTION_POINTERS* ep)
72 {
73         EXCEPTION_RECORD* er;
74         CONTEXT* ctx;
75         LONG res;
76         MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
77
78         /* If the thread is not managed by the runtime return early */
79         if (!jit_tls)
80                 return EXCEPTION_CONTINUE_SEARCH;
81
82         jit_tls->mono_win_chained_exception_needs_run = FALSE;
83         res = EXCEPTION_CONTINUE_EXECUTION;
84
85         er = ep->ExceptionRecord;
86         ctx = ep->ContextRecord;
87
88         switch (er->ExceptionCode) {
89         case EXCEPTION_ACCESS_VIOLATION:
90                 W32_SEH_HANDLE_EX(segv);
91                 break;
92         case EXCEPTION_ILLEGAL_INSTRUCTION:
93                 W32_SEH_HANDLE_EX(ill);
94                 break;
95         case EXCEPTION_INT_DIVIDE_BY_ZERO:
96         case EXCEPTION_INT_OVERFLOW:
97         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
98         case EXCEPTION_FLT_OVERFLOW:
99         case EXCEPTION_FLT_UNDERFLOW:
100         case EXCEPTION_FLT_INEXACT_RESULT:
101                 W32_SEH_HANDLE_EX(fpe);
102                 break;
103         default:
104                 jit_tls->mono_win_chained_exception_needs_run = TRUE;
105                 break;
106         }
107
108         if (jit_tls->mono_win_chained_exception_needs_run) {
109                 /* Don't copy context back if we chained exception
110                 * as the handler may have modfied the EXCEPTION_POINTERS
111                 * directly. We don't pass sigcontext to chained handlers.
112                 * Return continue search so the UnhandledExceptionFilter
113                 * can correctly chain the exception.
114                 */
115                 res = EXCEPTION_CONTINUE_SEARCH;
116         }
117
118         return res;
119 }
120
121 void win32_seh_init()
122 {
123         mono_old_win_toplevel_exception_filter = SetUnhandledExceptionFilter(seh_unhandled_exception_filter);
124         mono_win_vectored_exception_handle = AddVectoredExceptionHandler (1, seh_vectored_exception_handler);
125 }
126
127 void win32_seh_cleanup()
128 {
129         guint32 ret = 0;
130
131         if (mono_old_win_toplevel_exception_filter) SetUnhandledExceptionFilter(mono_old_win_toplevel_exception_filter);
132
133         ret = RemoveVectoredExceptionHandler (mono_win_vectored_exception_handle);
134         g_assert (ret);
135 }
136
137 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
138 {
139         switch (type) {
140         case SIGFPE:
141                 fpe_handler = handler;
142                 break;
143         case SIGILL:
144                 ill_handler = handler;
145                 break;
146         case SIGSEGV:
147                 segv_handler = handler;
148                 break;
149         default:
150                 break;
151         }
152 }
153
154 #endif /* TARGET_WIN32 */
155
156 #ifndef DISABLE_JIT
157 /*
158  * mono_arch_get_restore_context:
159  *
160  * Returns a pointer to a method which restores a previously saved sigcontext.
161  */
162 gpointer
163 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
164 {
165         guint8 *start = NULL;
166         guint8 *code;
167         MonoJumpInfo *ji = NULL;
168         GSList *unwind_ops = NULL;
169         int i, gregs_offset;
170
171         /* restore_contect (MonoContext *ctx) */
172
173         start = code = (guint8 *)mono_global_codeman_reserve (256);
174
175         amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);
176
177         /* Restore all registers except %rip and %r11 */
178         gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs);
179         for (i = 0; i < AMD64_NREG; ++i) {
180                 if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_R8 && i != AMD64_R9 && i != AMD64_R10 && i != AMD64_R11)
181                         amd64_mov_reg_membase (code, i, AMD64_R11, gregs_offset + (i * 8), 8);
182         }
183
184         /*
185          * The context resides on the stack, in the stack frame of the
186          * caller of this function.  The stack pointer that we need to
187          * restore is potentially many stack frames higher up, so the
188          * distance between them can easily be more than the red zone
189          * size.  Hence the stack pointer can be restored only after
190          * we have finished loading everything from the context.
191          */
192         amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  gregs_offset + (AMD64_RSP * 8), 8);
193         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  gregs_offset + (AMD64_RIP * 8), 8);
194         amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8);
195
196         /* jump to the saved IP */
197         amd64_jump_reg (code, AMD64_R11);
198
199         mono_arch_flush_icache (start, code - start);
200         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
201
202         if (info)
203                 *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);
204
205         return start;
206 }
207
208 /*
209  * mono_arch_get_call_filter:
210  *
211  * Returns a pointer to a method which calls an exception filter. We
212  * also use this function to call finally handlers (we pass NULL as 
213  * @exc object in this case).
214  */
215 gpointer
216 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
217 {
218         guint8 *start;
219         int i, gregs_offset;
220         guint8 *code;
221         guint32 pos;
222         MonoJumpInfo *ji = NULL;
223         GSList *unwind_ops = NULL;
224         const guint kMaxCodeSize = 128;
225
226         start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
227
228         /* call_filter (MonoContext *ctx, unsigned long eip) */
229         code = start;
230
231         /* Alloc new frame */
232         amd64_push_reg (code, AMD64_RBP);
233         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
234
235         /* Save callee saved regs */
236         pos = 0;
237         for (i = 0; i < AMD64_NREG; ++i)
238                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
239                         amd64_push_reg (code, i);
240                         pos += 8;
241                 }
242
243         /* Save EBP */
244         pos += 8;
245         amd64_push_reg (code, AMD64_RBP);
246
247         /* Make stack misaligned, the call will make it aligned again */
248         if (! (pos & 8))
249                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
250
251         gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs);
252
253         /* set new EBP */
254         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, gregs_offset + (AMD64_RBP * 8), 8);
255         /* load callee saved regs */
256         for (i = 0; i < AMD64_NREG; ++i) {
257                 if (AMD64_IS_CALLEE_SAVED_REG (i) && i != AMD64_RBP)
258                         amd64_mov_reg_membase (code, i, AMD64_ARG_REG1, gregs_offset + (i * 8), 8);
259         }
260         /* load exc register */
261         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1,  gregs_offset + (AMD64_RAX * 8), 8);
262
263         /* call the handler */
264         amd64_call_reg (code, AMD64_ARG_REG2);
265
266         if (! (pos & 8))
267                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
268
269         /* restore RBP */
270         amd64_pop_reg (code, AMD64_RBP);
271
272         /* Restore callee saved regs */
273         for (i = AMD64_NREG; i >= 0; --i)
274                 if (AMD64_IS_CALLEE_SAVED_REG (i))
275                         amd64_pop_reg (code, i);
276
277 #if TARGET_WIN32
278         amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
279         amd64_pop_reg (code, AMD64_RBP);
280 #else
281         amd64_leave (code);
282 #endif
283         amd64_ret (code);
284
285         g_assert ((code - start) < kMaxCodeSize);
286
287         mono_arch_flush_icache (start, code - start);
288         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
289
290         if (info)
291                 *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);
292
293         return start;
294 }
295 #endif /* !DISABLE_JIT */
296
297 /* 
298  * The first few arguments are dummy, to force the other arguments to be passed on
299  * the stack, this avoids overwriting the argument registers in the throw trampoline.
300  */
301 void
302 mono_amd64_throw_exception (guint64 dummy1, guint64 dummy2, guint64 dummy3, guint64 dummy4,
303                                                         guint64 dummy5, guint64 dummy6,
304                                                         MonoContext *mctx, MonoObject *exc, gboolean rethrow)
305 {
306         MonoError error;
307         MonoContext ctx;
308
309         /* mctx is on the caller's stack */
310         memcpy (&ctx, mctx, sizeof (MonoContext));
311
312         if (mono_object_isinst_checked (exc, mono_defaults.exception_class, &error)) {
313                 MonoException *mono_ex = (MonoException*)exc;
314                 if (!rethrow) {
315                         mono_ex->stack_trace = NULL;
316                         mono_ex->trace_ips = NULL;
317                 }
318         }
319         mono_error_assert_ok (&error);
320
321         /* adjust eip so that it point into the call instruction */
322         ctx.gregs [AMD64_RIP] --;
323
324         mono_handle_exception (&ctx, exc);
325         mono_restore_context (&ctx);
326         g_assert_not_reached ();
327 }
328
329 void
330 mono_amd64_throw_corlib_exception (guint64 dummy1, guint64 dummy2, guint64 dummy3, guint64 dummy4,
331                                                                    guint64 dummy5, guint64 dummy6,
332                                                                    MonoContext *mctx, guint32 ex_token_index, gint64 pc_offset)
333 {
334         guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
335         MonoException *ex;
336
337         ex = mono_exception_from_token (mono_defaults.exception_class->image, ex_token);
338
339         mctx->gregs [AMD64_RIP] -= pc_offset;
340
341         /* Negate the ip adjustment done in mono_amd64_throw_exception () */
342         mctx->gregs [AMD64_RIP] += 1;
343
344         mono_amd64_throw_exception (dummy1, dummy2, dummy3, dummy4, dummy5, dummy6, mctx, (MonoObject*)ex, FALSE);
345 }
346
347 void
348 mono_amd64_resume_unwind (guint64 dummy1, guint64 dummy2, guint64 dummy3, guint64 dummy4,
349                                                   guint64 dummy5, guint64 dummy6,
350                                                   MonoContext *mctx, guint32 dummy7, gint64 dummy8)
351 {
352         /* Only the register parameters are valid */
353         MonoContext ctx;
354
355         /* mctx is on the caller's stack */
356         memcpy (&ctx, mctx, sizeof (MonoContext));
357
358         mono_resume_unwind (&ctx);
359 }
360
361 #ifndef DISABLE_JIT
362 /*
363  * get_throw_trampoline:
364  *
365  *  Generate a call to mono_amd64_throw_exception/
366  * mono_amd64_throw_corlib_exception.
367  */
368 static gpointer
369 get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, gboolean llvm_abs, gboolean resume_unwind, const char *tramp_name, gboolean aot)
370 {
371         guint8* start;
372         guint8 *code;
373         MonoJumpInfo *ji = NULL;
374         GSList *unwind_ops = NULL;
375         int i, stack_size, arg_offsets [16], ctx_offset, regs_offset, dummy_stack_space;
376         const guint kMaxCodeSize = 256;
377
378 #ifdef TARGET_WIN32
379         dummy_stack_space = 6 * sizeof(mgreg_t);        /* Windows expects stack space allocated for all 6 dummy args. */
380 #else
381         dummy_stack_space = 0;
382 #endif
383
384         if (info)
385                 start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
386         else
387                 start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
388
389         /* The stack is unaligned on entry */
390         stack_size = ALIGN_TO (sizeof (MonoContext) + 64 + dummy_stack_space, MONO_ARCH_FRAME_ALIGNMENT) + 8;
391
392         code = start;
393
394         if (info)
395                 unwind_ops = mono_arch_get_cie_program ();
396
397         /* Alloc frame */
398         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, stack_size);
399         if (info) {
400                 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 8);
401                 mono_add_unwind_op_sp_alloc (unwind_ops, code, start, stack_size);
402         }
403
404         /*
405          * To hide linux/windows calling convention differences, we pass all arguments on
406          * the stack by passing 6 dummy values in registers.
407          */
408
409         arg_offsets [0] = dummy_stack_space + 0;
410         arg_offsets [1] = dummy_stack_space + sizeof(mgreg_t);
411         arg_offsets [2] = dummy_stack_space + sizeof(mgreg_t) * 2;
412         ctx_offset = dummy_stack_space + sizeof(mgreg_t) * 4;
413         regs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs);
414
415         /* Save registers */
416         for (i = 0; i < AMD64_NREG; ++i)
417                 if (i != AMD64_RSP)
418                         amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (i * sizeof(mgreg_t)), i, sizeof(mgreg_t));
419         /* Save RSP */
420         amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, stack_size + sizeof(mgreg_t));
421         amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RSP * sizeof(mgreg_t)), X86_EAX, sizeof(mgreg_t));
422         /* Save IP */
423         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof(mgreg_t));
424         amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RIP * sizeof(mgreg_t)), AMD64_RAX, sizeof(mgreg_t));
425         /* Set arg1 == ctx */
426         amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset);
427         amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [0], AMD64_RAX, sizeof(mgreg_t));
428         /* Set arg2 == exc/ex_token_index */
429         if (resume_unwind)
430                 amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [1], 0, sizeof(mgreg_t));
431         else
432                 amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [1], AMD64_ARG_REG1, sizeof(mgreg_t));
433         /* Set arg3 == rethrow/pc offset */
434         if (resume_unwind) {
435                 amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 0, sizeof(mgreg_t));
436         } else if (corlib) {
437                 if (llvm_abs)
438                         /*
439                          * The caller doesn't pass in a pc/pc offset, instead we simply use the
440                          * caller ip. Negate the pc adjustment done in mono_amd64_throw_corlib_exception ().
441                          */
442                         amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 1, sizeof(mgreg_t));
443                 else
444                         amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [2], AMD64_ARG_REG2, sizeof(mgreg_t));
445         } else {
446                 amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], rethrow, sizeof(mgreg_t));
447         }
448
449         if (aot) {
450                 const char *icall_name;
451
452                 if (resume_unwind)
453                         icall_name = "mono_amd64_resume_unwind";
454                 else if (corlib)
455                         icall_name = "mono_amd64_throw_corlib_exception";
456                 else
457                         icall_name = "mono_amd64_throw_exception";
458                 ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
459                 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
460         } else {
461                 amd64_mov_reg_imm (code, AMD64_R11, resume_unwind ? ((gpointer)mono_amd64_resume_unwind) : (corlib ? (gpointer)mono_amd64_throw_corlib_exception : (gpointer)mono_amd64_throw_exception));
462         }
463         amd64_call_reg (code, AMD64_R11);
464         amd64_breakpoint (code);
465
466         mono_arch_flush_icache (start, code - start);
467
468         g_assert ((code - start) < kMaxCodeSize);
469         g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
470
471         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
472
473         if (info)
474                 *info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops);
475
476         return start;
477 }
478
479 /**
480  * mono_arch_get_throw_exception:
481  *
482  * Returns a function pointer which can be used to raise 
483  * exceptions. The returned function has the following 
484  * signature: void (*func) (MonoException *exc); 
485  *
486  */
487 gpointer
488 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
489 {
490         return get_throw_trampoline (info, FALSE, FALSE, FALSE, FALSE, "throw_exception", aot);
491 }
492
493 gpointer 
494 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
495 {
496         return get_throw_trampoline (info, TRUE, FALSE, FALSE, FALSE, "rethrow_exception", aot);
497 }
498
499 /**
500  * mono_arch_get_throw_corlib_exception:
501  *
502  * Returns a function pointer which can be used to raise 
503  * corlib exceptions. The returned function has the following 
504  * signature: void (*func) (guint32 ex_token, guint32 offset); 
505  * Here, offset is the offset which needs to be substracted from the caller IP 
506  * to get the IP of the throw. Passing the offset has the advantage that it 
507  * needs no relocations in the caller.
508  */
509 gpointer 
510 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
511 {
512         return get_throw_trampoline (info, FALSE, TRUE, FALSE, FALSE, "throw_corlib_exception", aot);
513 }
514 #endif /* !DISABLE_JIT */
515
516 /*
517  * mono_arch_unwind_frame:
518  *
519  * This function is used to gather information from @ctx, and store it in @frame_info.
520  * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf
521  * is modified if needed.
522  * Returns TRUE on success, FALSE otherwise.
523  */
524 gboolean
525 mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, 
526                                                          MonoJitInfo *ji, MonoContext *ctx, 
527                                                          MonoContext *new_ctx, MonoLMF **lmf,
528                                                          mgreg_t **save_locations,
529                                                          StackFrameInfo *frame)
530 {
531         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
532         int i;
533
534         memset (frame, 0, sizeof (StackFrameInfo));
535         frame->ji = ji;
536
537         *new_ctx = *ctx;
538
539         if (ji != NULL) {
540                 mgreg_t regs [MONO_MAX_IREGS + 1];
541                 guint8 *cfa;
542                 guint32 unwind_info_len;
543                 guint8 *unwind_info;
544                 guint8 *epilog = NULL;
545
546                 if (ji->is_trampoline)
547                         frame->type = FRAME_TYPE_TRAMPOLINE;
548                 else
549                         frame->type = FRAME_TYPE_MANAGED;
550
551                 unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len);
552
553                 frame->unwind_info = unwind_info;
554                 frame->unwind_info_len = unwind_info_len;
555
556                 /*
557                 printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip);
558                 mono_print_unwind_info (unwind_info, unwind_info_len);
559                 */
560                 /* LLVM compiled code doesn't have this info */
561                 if (ji->has_arch_eh_info)
562                         epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji);
563  
564                 for (i = 0; i < AMD64_NREG; ++i)
565                         regs [i] = new_ctx->gregs [i];
566
567                 mono_unwind_frame (unwind_info, unwind_info_len, (guint8 *)ji->code_start,
568                                                    (guint8*)ji->code_start + ji->code_size,
569                                                    (guint8 *)ip, epilog ? &epilog : NULL, regs, MONO_MAX_IREGS + 1,
570                                                    save_locations, MONO_MAX_IREGS, &cfa);
571
572                 for (i = 0; i < AMD64_NREG; ++i)
573                         new_ctx->gregs [i] = regs [i];
574  
575                 /* The CFA becomes the new SP value */
576                 new_ctx->gregs [AMD64_RSP] = (mgreg_t)cfa;
577
578                 /* Adjust IP */
579                 new_ctx->gregs [AMD64_RIP] --;
580
581                 return TRUE;
582         } else if (*lmf) {
583                 guint64 rip;
584
585                 if (((guint64)(*lmf)->previous_lmf) & 2) {
586                         /* 
587                          * This LMF entry is created by the soft debug code to mark transitions to
588                          * managed code done during invokes.
589                          */
590                         MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
591
592                         g_assert (ext->debugger_invoke);
593
594                         memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
595
596                         *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7);
597
598                         frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
599
600                         return TRUE;
601                 }
602
603                 if (((guint64)(*lmf)->previous_lmf) & 4) {
604                         MonoLMFTramp *ext = (MonoLMFTramp*)(*lmf);
605
606                         rip = (guint64)MONO_CONTEXT_GET_IP (ext->ctx);
607                 } else if (((guint64)(*lmf)->previous_lmf) & 1) {
608                         /* This LMF has the rip field set */
609                         rip = (*lmf)->rip;
610                 } else if ((*lmf)->rsp == 0) {
611                         /* Top LMF entry */
612                         return FALSE;
613                 } else {
614                         /* 
615                          * The rsp field is set just before the call which transitioned to native 
616                          * code. Obtain the rip from the stack.
617                          */
618                         rip = *(guint64*)((*lmf)->rsp - sizeof(mgreg_t));
619                 }
620
621                 ji = mini_jit_info_table_find (domain, (char *)rip, NULL);
622                 /*
623                  * FIXME: ji == NULL can happen when a managed-to-native wrapper is interrupted
624                  * in the soft debugger suspend code, since (*lmf)->rsp no longer points to the
625                  * return address.
626                  */
627                 //g_assert (ji);
628                 if (!ji)
629                         return FALSE;
630
631                 frame->ji = ji;
632                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
633
634                 if (((guint64)(*lmf)->previous_lmf) & 4) {
635                         MonoLMFTramp *ext = (MonoLMFTramp*)(*lmf);
636
637                         /* Trampoline frame */
638                         for (i = 0; i < AMD64_NREG; ++i)
639                                 new_ctx->gregs [i] = ext->ctx->gregs [i];
640                         /* Adjust IP */
641                         new_ctx->gregs [AMD64_RIP] --;
642                 } else {
643                         /*
644                          * The registers saved in the LMF will be restored using the normal unwind info,
645                          * when the wrapper frame is processed.
646                          */
647                         /* Adjust IP */
648                         rip --;
649                         new_ctx->gregs [AMD64_RIP] = rip;
650                         new_ctx->gregs [AMD64_RSP] = (*lmf)->rsp;
651                         new_ctx->gregs [AMD64_RBP] = (*lmf)->rbp;
652                         for (i = 0; i < AMD64_NREG; ++i) {
653                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && i != AMD64_RBP)
654                                         new_ctx->gregs [i] = 0;
655                         }
656                 }
657
658                 *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7);
659
660                 return TRUE;
661         }
662
663         return FALSE;
664 }
665
666 /*
667  * handle_exception:
668  *
669  *   Called by resuming from a signal handler.
670  */
671 static void
672 handle_signal_exception (gpointer obj)
673 {
674         MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
675         MonoContext ctx;
676
677         memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
678
679         mono_handle_exception (&ctx, (MonoObject *)obj);
680
681         mono_restore_context (&ctx);
682 }
683
684 void
685 mono_arch_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
686 {
687         guint64 sp = ctx->gregs [AMD64_RSP];
688
689         ctx->gregs [AMD64_RDI] = (guint64)user_data;
690
691         /* Allocate a stack frame below the red zone */
692         sp -= 128;
693         /* The stack should be unaligned */
694         if ((sp % 16) == 0)
695                 sp -= 8;
696 #ifdef __linux__
697         /* Preserve the call chain to prevent crashes in the libgcc unwinder (#15969) */
698         *(guint64*)sp = ctx->gregs [AMD64_RIP];
699 #endif
700         ctx->gregs [AMD64_RSP] = sp;
701         ctx->gregs [AMD64_RIP] = (guint64)async_cb;
702 }
703
704 /**
705  * mono_arch_handle_exception:
706  *
707  * @ctx: saved processor state
708  * @obj: the exception object
709  */
710 gboolean
711 mono_arch_handle_exception (void *sigctx, gpointer obj)
712 {
713 #if defined(MONO_ARCH_USE_SIGACTION)
714         MonoContext mctx;
715
716         /*
717          * Handling the exception in the signal handler is problematic, since the original
718          * signal is disabled, and we could run arbitrary code though the debugger. So
719          * resume into the normal stack and do most work there if possible.
720          */
721         MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
722
723         /* Pass the ctx parameter in TLS */
724         mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx);
725
726         mctx = jit_tls->ex_ctx;
727         mono_arch_setup_async_callback (&mctx, handle_signal_exception, obj);
728         mono_monoctx_to_sigctx (&mctx, sigctx);
729
730         return TRUE;
731 #else
732         MonoContext mctx;
733
734         mono_sigctx_to_monoctx (sigctx, &mctx);
735
736         mono_handle_exception (&mctx, obj);
737
738         mono_monoctx_to_sigctx (&mctx, sigctx);
739
740         return TRUE;
741 #endif
742 }
743
744 gpointer
745 mono_arch_ip_from_context (void *sigctx)
746 {
747 #if defined(MONO_ARCH_USE_SIGACTION)
748         ucontext_t *ctx = (ucontext_t*)sigctx;
749
750         return (gpointer)UCONTEXT_REG_RIP (ctx);
751 #elif defined(HOST_WIN32)
752         return ((CONTEXT*)sigctx)->Rip;
753 #else
754         MonoContext *ctx = sigctx;
755         return (gpointer)ctx->gregs [AMD64_RIP];
756 #endif  
757 }
758
759 static void
760 restore_soft_guard_pages (void)
761 {
762         MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
763         if (jit_tls->stack_ovf_guard_base)
764                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
765 }
766
767 /* 
768  * this function modifies mctx so that when it is restored, it
769  * won't execcute starting at mctx.eip, but in a function that
770  * will restore the protection on the soft-guard pages and return back to
771  * continue at mctx.eip.
772  */
773 static void
774 prepare_for_guard_pages (MonoContext *mctx)
775 {
776         gpointer *sp;
777         sp = (gpointer *)(mctx->gregs [AMD64_RSP]);
778         sp -= 1;
779         /* the return addr */
780         sp [0] = (gpointer)(mctx->gregs [AMD64_RIP]);
781         mctx->gregs [AMD64_RIP] = (guint64)restore_soft_guard_pages;
782         mctx->gregs [AMD64_RSP] = (guint64)sp;
783 }
784
785 static void
786 altstack_handle_and_restore (MonoContext *ctx, MonoObject *obj, gboolean stack_ovf)
787 {
788         MonoContext mctx;
789         MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (ctx), NULL);
790
791         if (!ji)
792                 mono_handle_native_crash ("SIGSEGV", NULL, NULL);
793
794         mctx = *ctx;
795
796         mono_handle_exception (&mctx, obj);
797         if (stack_ovf)
798                 prepare_for_guard_pages (&mctx);
799         mono_restore_context (&mctx);
800 }
801
802 void
803 mono_arch_handle_altstack_exception (void *sigctx, MONO_SIG_HANDLER_INFO_TYPE *siginfo, gpointer fault_addr, gboolean stack_ovf)
804 {
805 #if defined(MONO_ARCH_USE_SIGACTION)
806         MonoException *exc = NULL;
807         gpointer *sp;
808         int frame_size;
809         MonoContext *copied_ctx;
810
811         if (stack_ovf)
812                 exc = mono_domain_get ()->stack_overflow_ex;
813
814         /* setup a call frame on the real stack so that control is returned there
815          * and exception handling can continue.
816          * The frame looks like:
817          *   ucontext struct
818          *   ...
819          *   return ip
820          * 128 is the size of the red zone
821          */
822         frame_size = sizeof (MonoContext) + sizeof (gpointer) * 4 + 128;
823         frame_size += 15;
824         frame_size &= ~15;
825         sp = (gpointer *)(UCONTEXT_REG_RSP (sigctx) & ~15);
826         sp = (gpointer *)((char*)sp - frame_size);
827         copied_ctx = (MonoContext*)(sp + 4);
828         /* the arguments must be aligned */
829         sp [-1] = (gpointer)UCONTEXT_REG_RIP (sigctx);
830         mono_sigctx_to_monoctx (sigctx, copied_ctx);
831         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
832         UCONTEXT_REG_RIP (sigctx) = (unsigned long)altstack_handle_and_restore;
833         UCONTEXT_REG_RSP (sigctx) = (unsigned long)(sp - 1);
834         UCONTEXT_REG_RDI (sigctx) = (unsigned long)(copied_ctx);
835         UCONTEXT_REG_RSI (sigctx) = (guint64)exc;
836         UCONTEXT_REG_RDX (sigctx) = stack_ovf;
837 #endif
838 }
839
840 guint64
841 mono_amd64_get_original_ip (void)
842 {
843         MonoLMF *lmf = mono_get_lmf ();
844
845         g_assert (lmf);
846
847         /* Reset the change to previous_lmf */
848         lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf & ~1);
849
850         return lmf->rip;
851 }
852
853 #ifndef DISABLE_JIT
854 GSList*
855 mono_amd64_get_exception_trampolines (gboolean aot)
856 {
857         MonoTrampInfo *info;
858         GSList *tramps = NULL;
859
860         /* LLVM needs different throw trampolines */
861         get_throw_trampoline (&info, FALSE, TRUE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", aot);
862         tramps = g_slist_prepend (tramps, info);
863
864         get_throw_trampoline (&info, FALSE, TRUE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", aot);
865         tramps = g_slist_prepend (tramps, info);
866
867         get_throw_trampoline (&info, FALSE, TRUE, TRUE, TRUE, "llvm_resume_unwind_trampoline", aot);
868         tramps = g_slist_prepend (tramps, info);
869
870         return tramps;
871 }
872 #endif /* !DISABLE_JIT */
873
874 void
875 mono_arch_exceptions_init (void)
876 {
877         GSList *tramps, *l;
878         gpointer tramp;
879
880         if (mono_aot_only) {
881                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
882                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE);
883                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
884                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE);
885                 tramp = mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
886                 mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE);
887         } else {
888                 /* Call this to avoid initialization races */
889                 tramps = mono_amd64_get_exception_trampolines (FALSE);
890                 for (l = tramps; l; l = l->next) {
891                         MonoTrampInfo *info = (MonoTrampInfo *)l->data;
892
893                         mono_register_jit_icall (info->code, g_strdup (info->name), NULL, TRUE);
894                         mono_tramp_info_register (info, NULL);
895                 }
896                 g_slist_free (tramps);
897         }
898 }
899
900 #ifdef MONO_ARCH_HAVE_UNWIND_TABLE
901
902 static void
903 mono_arch_unwindinfo_create (gpointer* monoui)
904 {
905         PUNWIND_INFO newunwindinfo;
906         *monoui = newunwindinfo = g_new0 (UNWIND_INFO, 1);
907         newunwindinfo->Version = 1;
908 }
909
910 void
911 mono_arch_unwindinfo_add_push_nonvol (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
912 {
913         PUNWIND_CODE unwindcode;
914         guchar codeindex;
915
916         g_assert (unwindinfo != NULL);
917
918         if (unwindinfo->CountOfCodes >= MONO_MAX_UNWIND_CODES)
919                 g_error ("Larger allocation needed for the unwind information.");
920
921         codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
922         unwindcode = &unwindinfo->UnwindCode [codeindex];
923         unwindcode->UnwindOp = UWOP_PUSH_NONVOL;
924         unwindcode->CodeOffset = (guchar)unwind_op->when;
925         unwindcode->OpInfo = unwind_op->reg;
926
927         if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
928                 g_error ("Adding unwind info in wrong order.");
929
930         unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
931 }
932
933 void
934 mono_arch_unwindinfo_add_set_fpreg (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
935 {
936         PUNWIND_CODE unwindcode;
937         guchar codeindex;
938
939         g_assert (unwindinfo != NULL);
940
941         if (unwindinfo->CountOfCodes + 1 >= MONO_MAX_UNWIND_CODES)
942                 g_error ("Larger allocation needed for the unwind information.");
943
944         codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
945         unwindcode = &unwindinfo->UnwindCode [codeindex];
946         unwindcode->UnwindOp = UWOP_SET_FPREG;
947         unwindcode->CodeOffset = (guchar)unwind_op->when;
948
949         g_assert (unwind_op->val % 16 == 0);
950         unwindinfo->FrameRegister = unwind_op->reg;
951         unwindinfo->FrameOffset = unwind_op->val / 16;
952
953         if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
954                 g_error ("Adding unwind info in wrong order.");
955
956         unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
957 }
958
959 void
960 mono_arch_unwindinfo_add_alloc_stack (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
961 {
962         PUNWIND_CODE unwindcode;
963         guchar codeindex;
964         guchar codesneeded;
965         guint size;
966
967         g_assert (unwindinfo != NULL);
968
969         size = unwind_op->val;
970
971         if (size < 0x8)
972                 g_error ("Stack allocation must be equal to or greater than 0x8.");
973
974         if (size <= 0x80)
975                 codesneeded = 1;
976         else if (size <= 0x7FFF8)
977                 codesneeded = 2;
978         else
979                 codesneeded = 3;
980
981         if (unwindinfo->CountOfCodes + codesneeded > MONO_MAX_UNWIND_CODES)
982                 g_error ("Larger allocation needed for the unwind information.");
983
984         codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->CountOfCodes += codesneeded);
985         unwindcode = &unwindinfo->UnwindCode [codeindex];
986
987         unwindcode->CodeOffset = (guchar)unwind_op->when;
988
989         if (codesneeded == 1) {
990                 /*The size of the allocation is
991                   (the number in the OpInfo member) times 8 plus 8*/
992                 unwindcode->UnwindOp = UWOP_ALLOC_SMALL;
993                 unwindcode->OpInfo = (size - 8)/8;
994         }
995         else {
996                 if (codesneeded == 3) {
997                         /*the unscaled size of the allocation is recorded
998                           in the next two slots in little-endian format.
999                           NOTE, unwind codes are allocated from end to begining of list so
1000                           unwind code will have right execution order. List is sorted on CodeOffset
1001                           using descending sort order.*/
1002                         unwindcode->UnwindOp = UWOP_ALLOC_LARGE;
1003                         unwindcode->OpInfo = 1;
1004                         *((unsigned int*)(&(unwindcode + 1)->FrameOffset)) = size;
1005                 }
1006                 else {
1007                         /*the size of the allocation divided by 8
1008                           is recorded in the next slot.
1009                           NOTE, unwind codes are allocated from end to begining of list so
1010                           unwind code will have right execution order. List is sorted on CodeOffset
1011                           using descending sort order.*/
1012                         unwindcode->UnwindOp = UWOP_ALLOC_LARGE;
1013                         unwindcode->OpInfo = 0;
1014                         (unwindcode + 1)->FrameOffset = (gushort)(size/8);
1015                 }
1016         }
1017
1018         if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
1019                 g_error ("Adding unwind info in wrong order.");
1020
1021         unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
1022 }
1023
1024 static gboolean g_dyn_func_table_inited;
1025
1026 // Dynamic function table used when registering unwind info for OS unwind support.
1027 static GList *g_dynamic_function_table_begin;
1028 static GList *g_dynamic_function_table_end;
1029
1030 // SRW lock (lightweight read/writer lock) protecting dynamic function table.
1031 static SRWLOCK g_dynamic_function_table_lock = SRWLOCK_INIT;
1032
1033 // Module handle used when explicit loading ntdll.
1034 static HMODULE g_ntdll;
1035
1036 // If Win8 or Win2012Server or later, use growable function tables instead
1037 // of callbacks. Callback solution will still be fallback on older systems.
1038 static RtlAddGrowableFunctionTablePtr g_rtl_add_growable_function_table;
1039 static RtlGrowFunctionTablePtr g_rtl_grow_function_table;
1040 static RtlDeleteGrowableFunctionTablePtr g_rtl_delete_growable_function_table;
1041
1042 // When using function table callback solution an out of proc module is needed by
1043 // debuggers in order to read unwind info from debug target.
1044 #ifdef _MSC_VER
1045 #define MONO_DAC_MODULE TEXT("mono-2.0-dac-sgen.dll")
1046 #else
1047 #define MONO_DAC_MODULE TEXT("mono-2.0-sgen.dll")
1048 #endif
1049
1050 #define MONO_DAC_MODULE_MAX_PATH 1024
1051
1052 static void
1053 init_table_no_lock (void)
1054 {
1055         if (g_dyn_func_table_inited == FALSE) {
1056                 g_assert_checked (g_dynamic_function_table_begin == NULL);
1057                 g_assert_checked (g_dynamic_function_table_end == NULL);
1058                 g_assert_checked (g_rtl_add_growable_function_table == NULL);
1059                 g_assert_checked (g_rtl_grow_function_table == NULL);
1060                 g_assert_checked (g_rtl_delete_growable_function_table == NULL);
1061                 g_assert_checked (g_ntdll == NULL);
1062
1063                 // Load functions available on Win8/Win2012Server or later. If running on earlier
1064                 // systems the below GetProceAddress will fail, this is expected behavior.
1065                 if (GetModuleHandleEx (0, TEXT("ntdll.dll"), &g_ntdll) == TRUE) {
1066                         g_rtl_add_growable_function_table = (RtlAddGrowableFunctionTablePtr)GetProcAddress (g_ntdll, "RtlAddGrowableFunctionTable");
1067                         g_rtl_grow_function_table = (RtlGrowFunctionTablePtr)GetProcAddress (g_ntdll, "RtlGrowFunctionTable");
1068                         g_rtl_delete_growable_function_table = (RtlDeleteGrowableFunctionTablePtr)GetProcAddress (g_ntdll, "RtlDeleteGrowableFunctionTable");
1069                 }
1070
1071                 g_dyn_func_table_inited = TRUE;
1072         }
1073 }
1074
1075 void
1076 mono_arch_unwindinfo_init_table (void)
1077 {
1078         if (g_dyn_func_table_inited == FALSE) {
1079
1080                 AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
1081
1082                 init_table_no_lock ();
1083
1084                 ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
1085         }
1086 }
1087
1088 static void
1089 terminate_table_no_lock (void)
1090 {
1091         if (g_dyn_func_table_inited == TRUE) {
1092                 if (g_dynamic_function_table_begin != NULL) {
1093                         // Free all list elements.
1094                         for (GList *l = g_dynamic_function_table_begin; l; l = l->next) {
1095                                 if (l->data) {
1096                                         g_free (l->data);
1097                                         l->data = NULL;
1098                                 }
1099                         }
1100
1101                         //Free the list.
1102                         g_list_free (g_dynamic_function_table_begin);
1103                         g_dynamic_function_table_begin = NULL;
1104                         g_dynamic_function_table_end = NULL;
1105                 }
1106
1107                 g_rtl_delete_growable_function_table = NULL;
1108                 g_rtl_grow_function_table = NULL;
1109                 g_rtl_add_growable_function_table = NULL;
1110
1111                 if (g_ntdll != NULL) {
1112                         FreeLibrary (g_ntdll);
1113                         g_ntdll = NULL;
1114                 }
1115
1116                 g_dyn_func_table_inited = FALSE;
1117         }
1118 }
1119
1120 void
1121 mono_arch_unwindinfo_terminate_table (void)
1122 {
1123         if (g_dyn_func_table_inited == TRUE) {
1124
1125                 AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
1126
1127                 terminate_table_no_lock ();
1128
1129                 ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
1130         }
1131 }
1132
1133 static GList *
1134 fast_find_range_in_table_no_lock_ex (gsize begin_range, gsize end_range, gboolean *continue_search)
1135 {
1136         GList *found_entry = NULL;
1137
1138         // Fast path, look at boundaries.
1139         if (g_dynamic_function_table_begin != NULL) {
1140                 DynamicFunctionTableEntry *first_entry = g_dynamic_function_table_begin->data;
1141                 DynamicFunctionTableEntry *last_entry = (g_dynamic_function_table_end != NULL ) ? g_dynamic_function_table_end->data : first_entry;
1142
1143                 // Sorted in descending order based on begin_range, check first item, that is the entry with highest range.
1144                 if (first_entry != NULL && first_entry->begin_range <= begin_range && first_entry->end_range >= end_range) {
1145                                 // Entry belongs to first entry in list.
1146                                 found_entry = g_dynamic_function_table_begin;
1147                                 *continue_search = FALSE;
1148                 } else {
1149                         if (first_entry != NULL && first_entry->begin_range >= begin_range) {
1150                                 if (last_entry != NULL && last_entry->begin_range <= begin_range) {
1151                                         // Entry has a range that could exist in table, continue search.
1152                                         *continue_search = TRUE;
1153                                 }
1154                         }
1155                 }
1156         }
1157
1158         return found_entry;
1159 }
1160
1161 static inline DynamicFunctionTableEntry *
1162 fast_find_range_in_table_no_lock (gsize begin_range, gsize end_range, gboolean *continue_search)
1163 {
1164         GList *found_entry = fast_find_range_in_table_no_lock_ex (begin_range, end_range, continue_search);
1165         return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
1166 }
1167
1168 static GList *
1169 find_range_in_table_no_lock_ex (const gpointer code_block, gsize block_size)
1170 {
1171         GList *found_entry = NULL;
1172         gboolean continue_search = FALSE;
1173
1174         gsize begin_range = (gsize)code_block;
1175         gsize end_range = begin_range + block_size;
1176
1177         // Fast path, check table boundaries.
1178         found_entry = fast_find_range_in_table_no_lock_ex (begin_range, end_range, &continue_search);
1179         if (found_entry || continue_search == FALSE)
1180                 return found_entry;
1181
1182         // Scan table for an entry including range.
1183         for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
1184                 DynamicFunctionTableEntry *current_entry = (DynamicFunctionTableEntry *)node->data;
1185                 g_assert_checked (current_entry != NULL);
1186
1187                 // Do we have a match?
1188                 if (current_entry->begin_range == begin_range && current_entry->end_range == end_range) {
1189                         found_entry = node;
1190                         break;
1191                 }
1192         }
1193
1194         return found_entry;
1195 }
1196
1197 static inline DynamicFunctionTableEntry *
1198 find_range_in_table_no_lock (const gpointer code_block, gsize block_size)
1199 {
1200         GList *found_entry = find_range_in_table_no_lock_ex (code_block, block_size);
1201         return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
1202 }
1203
1204 static GList *
1205 find_pc_in_table_no_lock_ex (const gpointer pc)
1206 {
1207         GList *found_entry = NULL;
1208         gboolean continue_search = FALSE;
1209
1210         gsize begin_range = (gsize)pc;
1211         gsize end_range = begin_range;
1212
1213         // Fast path, check table boundaries.
1214         found_entry = fast_find_range_in_table_no_lock_ex (begin_range, begin_range, &continue_search);
1215         if (found_entry || continue_search == FALSE)
1216                 return found_entry;
1217
1218         // Scan table for a entry including range.
1219         for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
1220                 DynamicFunctionTableEntry *current_entry = (DynamicFunctionTableEntry *)node->data;
1221                 g_assert_checked (current_entry != NULL);
1222
1223                 // Do we have a match?
1224                 if (current_entry->begin_range <= begin_range && current_entry->end_range >= end_range) {
1225                         found_entry = node;
1226                         break;
1227                 }
1228         }
1229
1230         return found_entry;
1231 }
1232
1233 static inline DynamicFunctionTableEntry *
1234 find_pc_in_table_no_lock (const gpointer pc)
1235 {
1236         GList *found_entry = find_pc_in_table_no_lock_ex (pc);
1237         return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
1238 }
1239
1240 #ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
1241 static void
1242 validate_table_no_lock (void)
1243 {
1244         // Validation method checking that table is sorted as expected and don't include overlapped regions.
1245         // Method will assert on failure to explicitly indicate what check failed.
1246         if (g_dynamic_function_table_begin != NULL) {
1247                 g_assert_checked (g_dynamic_function_table_end != NULL);
1248
1249                 DynamicFunctionTableEntry *prevoious_entry = NULL;
1250                 DynamicFunctionTableEntry *current_entry = NULL;
1251                 for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
1252                         current_entry = (DynamicFunctionTableEntry *)node->data;
1253
1254                         g_assert_checked (current_entry != NULL);
1255                         g_assert_checked (current_entry->end_range > current_entry->begin_range);
1256
1257                         if (prevoious_entry != NULL) {
1258                                 // List should be sorted in descending order on begin_range.
1259                                 g_assert_checked (prevoious_entry->begin_range > current_entry->begin_range);
1260
1261                                 // Check for overlapped regions.
1262                                 g_assert_checked (prevoious_entry->begin_range >= current_entry->end_range);
1263                         }
1264
1265                         prevoious_entry = current_entry;
1266                 }
1267         }
1268 }
1269
1270 #else
1271
1272 static inline void
1273 validate_table_no_lock (void)
1274 {
1275         ;
1276 }
1277 #endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
1278
1279 // Forward declare.
1280 static PRUNTIME_FUNCTION MONO_GET_RUNTIME_FUNCTION_CALLBACK (DWORD64 ControlPc, IN PVOID Context);
1281
1282 DynamicFunctionTableEntry *
1283 mono_arch_unwindinfo_insert_range_in_table (const gpointer code_block, gsize block_size)
1284 {
1285         DynamicFunctionTableEntry *new_entry = NULL;
1286
1287         gsize begin_range = (gsize)code_block;
1288         gsize end_range = begin_range + block_size;
1289
1290         AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
1291         init_table_no_lock ();
1292         new_entry = find_range_in_table_no_lock (code_block, block_size);
1293         if (new_entry == NULL) {
1294                 // Allocate new entry.
1295                 new_entry = g_new0 (DynamicFunctionTableEntry, 1);
1296                 if (new_entry != NULL) {
1297
1298                         // Pre-allocate RUNTIME_FUNCTION array, assume average method size of
1299                         // MONO_UNWIND_INFO_RT_FUNC_SIZE bytes.
1300                         InitializeSRWLock (&new_entry->lock);
1301                         new_entry->handle = NULL;
1302                         new_entry->begin_range = begin_range;
1303                         new_entry->end_range = end_range;
1304                         new_entry->rt_funcs_max_count = (block_size / MONO_UNWIND_INFO_RT_FUNC_SIZE) + 1;
1305                         new_entry->rt_funcs_current_count = 0;
1306                         new_entry->rt_funcs = g_new0 (RUNTIME_FUNCTION, new_entry->rt_funcs_max_count);
1307
1308                         if (new_entry->rt_funcs != NULL) {
1309                                 // Check insert on boundaries. List is sorted descending on begin_range.
1310                                 if (g_dynamic_function_table_begin == NULL) {
1311                                         g_dynamic_function_table_begin = g_list_append (g_dynamic_function_table_begin, new_entry);
1312                                         g_dynamic_function_table_end = g_dynamic_function_table_begin;
1313                                 } else if (((DynamicFunctionTableEntry *)(g_dynamic_function_table_begin->data))->begin_range < begin_range) {
1314                                         // Insert at the head.
1315                                         g_dynamic_function_table_begin = g_list_prepend (g_dynamic_function_table_begin, new_entry);
1316                                 } else if (((DynamicFunctionTableEntry *)(g_dynamic_function_table_end->data))->begin_range > begin_range) {
1317                                         // Insert at tail.
1318                                         g_list_append (g_dynamic_function_table_end, new_entry);
1319                                         g_dynamic_function_table_end = g_dynamic_function_table_end->next;
1320                                 } else {
1321                                         //Search and insert at correct position.
1322                                         for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
1323                                                 DynamicFunctionTableEntry * current_entry = (DynamicFunctionTableEntry *)node->data;
1324                                                 g_assert_checked (current_entry != NULL);
1325
1326                                                 if (current_entry->begin_range < new_entry->begin_range) {
1327                                                         g_dynamic_function_table_begin = g_list_insert_before (g_dynamic_function_table_begin, node, new_entry);
1328                                                         break;
1329                                                 }
1330                                         }
1331                                 }
1332
1333                                 // Register dynamic function table entry with OS.
1334                                 if (g_rtl_add_growable_function_table != NULL) {
1335                                         // Allocate new growable handle table for entry.
1336                                         g_assert_checked (new_entry->handle == NULL);
1337                                         DWORD result = g_rtl_add_growable_function_table (&new_entry->handle,
1338                                                                                 new_entry->rt_funcs, new_entry->rt_funcs_current_count,
1339                                                                                 new_entry->rt_funcs_max_count, new_entry->begin_range, new_entry->end_range);
1340                                         g_assert (!result);
1341                                 } else {
1342                                         WCHAR buffer [MONO_DAC_MODULE_MAX_PATH] = { 0 };
1343                                         WCHAR *path = buffer;
1344
1345                                         // DAC module should be in the same directory as the
1346                                         // main executable.
1347                                         GetModuleFileNameW (NULL, buffer, G_N_ELEMENTS(buffer));
1348                                         path = wcsrchr (buffer, TEXT('\\'));
1349                                         if (path != NULL) {
1350                                                 path++;
1351                                                 *path = TEXT('\0');
1352                                         }
1353
1354                                         wcscat_s (buffer, G_N_ELEMENTS(buffer), MONO_DAC_MODULE);
1355                                         path = buffer;
1356
1357                                         // Register function table callback + out of proc module.
1358                                         new_entry->handle = (PVOID)((DWORD64)(new_entry->begin_range) | 3);
1359                                         BOOLEAN result = RtlInstallFunctionTableCallback ((DWORD64)(new_entry->handle),
1360                                                                                 (DWORD64)(new_entry->begin_range), (DWORD)(new_entry->end_range - new_entry->begin_range),
1361                                                                                 MONO_GET_RUNTIME_FUNCTION_CALLBACK, new_entry, path);
1362                                         g_assert(result);
1363                                 }
1364
1365                                 // Only included in checked builds. Validates the structure of table after insert.
1366                                 validate_table_no_lock ();
1367
1368                         } else {
1369                                 g_free (new_entry);
1370                                 new_entry = NULL;
1371                         }
1372                 }
1373         }
1374         ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
1375
1376         return new_entry;
1377 }
1378
1379 static void
1380 remove_range_in_table_no_lock (GList *entry)
1381 {
1382         if (entry != NULL) {
1383                 if (entry == g_dynamic_function_table_end)
1384                         g_dynamic_function_table_end = entry->prev;
1385
1386                 g_dynamic_function_table_begin = g_list_remove_link (g_dynamic_function_table_begin, entry);
1387                 DynamicFunctionTableEntry *removed_entry = (DynamicFunctionTableEntry *)entry->data;
1388
1389                 g_assert_checked (removed_entry != NULL);
1390                 g_assert_checked (removed_entry->rt_funcs != NULL);
1391
1392                 // Remove function table from OS.
1393                 if (removed_entry->handle != NULL) {
1394                         if (g_rtl_delete_growable_function_table != NULL) {
1395                                 g_rtl_delete_growable_function_table (removed_entry->handle);
1396                         } else {
1397                                 RtlDeleteFunctionTable ((PRUNTIME_FUNCTION)removed_entry->handle);
1398                         }
1399                 }
1400
1401                 g_free (removed_entry->rt_funcs);
1402                 g_free (removed_entry);
1403
1404                 g_list_free_1 (entry);
1405         }
1406
1407         // Only included in checked builds. Validates the structure of table after remove.
1408         validate_table_no_lock ();
1409 }
1410
1411 void
1412 mono_arch_unwindinfo_remove_pc_range_in_table (const gpointer code)
1413 {
1414         AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
1415
1416         GList *found_entry = find_pc_in_table_no_lock_ex (code);
1417
1418         g_assert_checked (found_entry != NULL || ((DynamicFunctionTableEntry *)found_entry->data)->begin_range == (gsize)code);
1419         remove_range_in_table_no_lock (found_entry);
1420
1421         ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
1422 }
1423
1424 void
1425 mono_arch_unwindinfo_remove_range_in_table (const gpointer code_block, gsize block_size)
1426 {
1427         AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
1428
1429         GList *found_entry = find_range_in_table_no_lock_ex (code_block, block_size);
1430
1431         g_assert_checked (found_entry != NULL || ((DynamicFunctionTableEntry *)found_entry->data)->begin_range == (gsize)code_block);
1432         remove_range_in_table_no_lock (found_entry);
1433
1434         ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
1435 }
1436
1437 PRUNTIME_FUNCTION
1438 mono_arch_unwindinfo_find_rt_func_in_table (const gpointer code, gsize code_size)
1439 {
1440         PRUNTIME_FUNCTION found_rt_func = NULL;
1441
1442         gsize begin_range = (gsize)code;
1443         gsize end_range = begin_range + code_size;
1444
1445         AcquireSRWLockShared (&g_dynamic_function_table_lock);
1446
1447         DynamicFunctionTableEntry *found_entry = find_pc_in_table_no_lock (code);
1448
1449         if (found_entry != NULL) {
1450
1451                 AcquireSRWLockShared (&found_entry->lock);
1452
1453                 g_assert_checked (found_entry->begin_range <= begin_range);
1454                 g_assert_checked (found_entry->end_range >= begin_range && found_entry->end_range >= end_range);
1455                 g_assert_checked (found_entry->rt_funcs != NULL);
1456
1457                 for (int i = 0; i < found_entry->rt_funcs_current_count; ++i) {
1458                         PRUNTIME_FUNCTION current_rt_func = (PRUNTIME_FUNCTION)(&found_entry->rt_funcs [i]);
1459
1460                         // Is this our RT function entry?
1461                         if (found_entry->begin_range + current_rt_func->BeginAddress <= begin_range &&
1462                                 found_entry->begin_range + current_rt_func->EndAddress >= end_range) {
1463                                 found_rt_func = current_rt_func;
1464                                 break;
1465                         }
1466                 }
1467
1468                 ReleaseSRWLockShared (&found_entry->lock);
1469         }
1470
1471         ReleaseSRWLockShared (&g_dynamic_function_table_lock);
1472
1473         return found_rt_func;
1474 }
1475
1476 inline PRUNTIME_FUNCTION
1477 mono_arch_unwindinfo_find_pc_rt_func_in_table (const gpointer pc)
1478 {
1479         return mono_arch_unwindinfo_find_rt_func_in_table (pc, 0);
1480 }
1481
1482 #ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
1483 static void
1484 validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
1485 {
1486         // Validation method checking that runtime function table is sorted as expected and don't include overlapped regions.
1487         // Method will assert on failure to explicitly indicate what check failed.
1488         g_assert_checked (entry != NULL);
1489         g_assert_checked (entry->rt_funcs_max_count >= entry->rt_funcs_current_count);
1490         g_assert_checked (entry->rt_funcs != NULL);
1491
1492         PRUNTIME_FUNCTION current_rt_func = NULL;
1493         PRUNTIME_FUNCTION previous_rt_func = NULL;
1494         for (int i = 0; i < entry->rt_funcs_current_count; ++i) {
1495                 current_rt_func = &(entry->rt_funcs [i]);
1496
1497                 g_assert_checked (current_rt_func->BeginAddress < current_rt_func->EndAddress);
1498                 g_assert_checked (current_rt_func->EndAddress <= current_rt_func->UnwindData);
1499
1500                 if (previous_rt_func != NULL) {
1501                         // List should be sorted in ascending order based on BeginAddress.
1502                         g_assert_checked (previous_rt_func->BeginAddress < current_rt_func->BeginAddress);
1503
1504                         // Check for overlapped regions.
1505                         g_assert_checked (previous_rt_func->EndAddress <= current_rt_func->BeginAddress);
1506                 }
1507
1508                 previous_rt_func = current_rt_func;
1509         }
1510 }
1511
1512 #else
1513
1514 static inline void
1515 validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
1516 {
1517         ;
1518 }
1519 #endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
1520
1521 PRUNTIME_FUNCTION
1522 mono_arch_unwindinfo_insert_rt_func_in_table (const gpointer code, gsize code_size)
1523 {
1524         PRUNTIME_FUNCTION new_rt_func = NULL;
1525
1526         gsize begin_range = (gsize)code;
1527         gsize end_range = begin_range + code_size;
1528
1529         AcquireSRWLockShared (&g_dynamic_function_table_lock);
1530
1531         DynamicFunctionTableEntry *found_entry = find_pc_in_table_no_lock (code);
1532
1533         if (found_entry != NULL) {
1534
1535                 AcquireSRWLockExclusive (&found_entry->lock);
1536
1537                 g_assert_checked (found_entry->begin_range <= begin_range);
1538                 g_assert_checked (found_entry->end_range >= begin_range && found_entry->end_range >= end_range);
1539                 g_assert_checked (found_entry->rt_funcs != NULL);
1540                 g_assert_checked ((guchar*)code - found_entry->begin_range >= 0);
1541
1542                 gsize code_offset = (gsize)code - found_entry->begin_range;
1543                 gsize entry_count = found_entry->rt_funcs_current_count;
1544                 gsize max_entry_count = found_entry->rt_funcs_max_count;
1545                 PRUNTIME_FUNCTION current_rt_funcs = found_entry->rt_funcs;
1546
1547                 RUNTIME_FUNCTION new_rt_func_data;
1548                 new_rt_func_data.BeginAddress = code_offset;
1549                 new_rt_func_data.EndAddress = code_offset + code_size;
1550
1551                 gsize aligned_unwind_data = ALIGN_TO(end_range, sizeof (mgreg_t));
1552                 new_rt_func_data.UnwindData = aligned_unwind_data - found_entry->begin_range;
1553
1554                 g_assert_checked (new_rt_func_data.UnwindData == ALIGN_TO(new_rt_func_data.EndAddress, sizeof (mgreg_t)));
1555
1556                 PRUNTIME_FUNCTION new_rt_funcs = NULL;
1557
1558                 // List needs to be sorted in ascending order based on BeginAddress (Windows requirement if list
1559                 // going to be directly reused in OS func tables. Check if we can append to end of existing table without realloc.
1560                 if (entry_count == 0 || (entry_count < max_entry_count) && (current_rt_funcs [entry_count - 1].BeginAddress) < code_offset) {
1561                         new_rt_func = &(current_rt_funcs [entry_count]);
1562                         *new_rt_func = new_rt_func_data;
1563                         entry_count++;
1564                 } else {
1565                         // No easy way out, need to realloc, grow to double size (or current max, if to small).
1566                         max_entry_count = entry_count * 2 > max_entry_count ? entry_count * 2 : max_entry_count;
1567                         new_rt_funcs = g_new0 (RUNTIME_FUNCTION, max_entry_count);
1568
1569                         if (new_rt_funcs != NULL) {
1570                                 gsize from_index = 0;
1571                                 gsize to_index = 0;
1572
1573                                 // Copy from old table into new table. Make sure new rt func gets inserted
1574                                 // into correct location based on sort order.
1575                                 for (; from_index < entry_count; ++from_index) {
1576                                         if (new_rt_func == NULL && current_rt_funcs [from_index].BeginAddress > new_rt_func_data.BeginAddress) {
1577                                                 new_rt_func = &(new_rt_funcs [to_index++]);
1578                                                 *new_rt_func = new_rt_func_data;
1579                                         }
1580
1581                                         if (current_rt_funcs [from_index].UnwindData != 0)
1582                                                 new_rt_funcs [to_index++] = current_rt_funcs [from_index];
1583                                 }
1584
1585                                 // If we didn't insert by now, put it last in the list.
1586                                 if (new_rt_func == NULL) {
1587                                         new_rt_func = &(new_rt_funcs [to_index]);
1588                                         *new_rt_func = new_rt_func_data;
1589                                 }
1590                         }
1591
1592                         entry_count++;
1593                 }
1594
1595                 // Update the stats for current entry.
1596                 found_entry->rt_funcs_current_count = entry_count;
1597                 found_entry->rt_funcs_max_count = max_entry_count;
1598
1599                 if (new_rt_funcs == NULL && g_rtl_grow_function_table != NULL) {
1600                         // No new table just report increase in use.
1601                         g_assert_checked (found_entry->handle != NULL);
1602                         g_rtl_grow_function_table (found_entry->handle, found_entry->rt_funcs_current_count);
1603                 } else if (new_rt_funcs != NULL && g_rtl_add_growable_function_table != NULL) {
1604                         // New table, delete old table and rt funcs, and register a new one.
1605                         g_assert_checked (g_rtl_delete_growable_function_table != NULL);
1606                         g_rtl_delete_growable_function_table (found_entry->handle);
1607                         found_entry->handle = NULL;
1608                         g_free (found_entry->rt_funcs);
1609                         found_entry->rt_funcs = new_rt_funcs;
1610                         DWORD result = g_rtl_add_growable_function_table (&found_entry->handle,
1611                                                                 found_entry->rt_funcs, found_entry->rt_funcs_current_count,
1612                                                                 found_entry->rt_funcs_max_count, found_entry->begin_range, found_entry->end_range);
1613                         g_assert (!result);
1614                 } else if (new_rt_funcs != NULL && g_rtl_add_growable_function_table == NULL) {
1615                         // No table registered with OS, callback solution in use. Switch tables.
1616                         g_free (found_entry->rt_funcs);
1617                         found_entry->rt_funcs = new_rt_funcs;
1618                 } else if (new_rt_funcs == NULL && g_rtl_grow_function_table == NULL) {
1619                         // No table registered with OS, callback solution in use, nothing to do.
1620                 } else {
1621                         g_assert_not_reached ();
1622                 }
1623
1624                 // Only included in checked builds. Validates the structure of table after insert.
1625                 validate_rt_funcs_in_table_no_lock (found_entry);
1626
1627                 ReleaseSRWLockExclusive (&found_entry->lock);
1628         }
1629
1630         ReleaseSRWLockShared (&g_dynamic_function_table_lock);
1631
1632         return new_rt_func;
1633 }
1634
1635 static PRUNTIME_FUNCTION
1636 MONO_GET_RUNTIME_FUNCTION_CALLBACK ( DWORD64 ControlPc, IN PVOID Context )
1637 {
1638         return mono_arch_unwindinfo_find_pc_rt_func_in_table ((gpointer)ControlPc);
1639 }
1640
1641 static void
1642 initialize_unwind_info_internal_ex (GSList *unwind_ops, PUNWIND_INFO unwindinfo)
1643 {
1644         if (unwind_ops != NULL && unwindinfo != NULL) {
1645                 MonoUnwindOp *unwind_op_data;
1646                 gboolean sp_alloced = FALSE;
1647                 gboolean fp_alloced = FALSE;
1648
1649                 // Replay collected unwind info and setup Windows format.
1650                 for (GSList *l = unwind_ops; l; l = l->next) {
1651                         unwind_op_data = (MonoUnwindOp *)l->data;
1652                         switch (unwind_op_data->op) {
1653                                 case DW_CFA_offset : {
1654                                         // Pushes should go before SP/FP allocation to be compliant with Windows x64 ABI.
1655                                         // TODO: DW_CFA_offset can also be used to move saved regs into frame.
1656                                         if (unwind_op_data->reg != AMD64_RIP && sp_alloced == FALSE && fp_alloced == FALSE)
1657                                                 mono_arch_unwindinfo_add_push_nonvol (unwindinfo, unwind_op_data);
1658                                         break;
1659                                 }
1660                                 case DW_CFA_mono_sp_alloc_info_win64 : {
1661                                         mono_arch_unwindinfo_add_alloc_stack (unwindinfo, unwind_op_data);
1662                                         sp_alloced = TRUE;
1663                                         break;
1664                                 }
1665                                 case DW_CFA_mono_fp_alloc_info_win64 : {
1666                                         mono_arch_unwindinfo_add_set_fpreg (unwindinfo, unwind_op_data);
1667                                         fp_alloced = TRUE;
1668                                         break;
1669                                 }
1670                                 default :
1671                                         break;
1672                         }
1673                 }
1674         }
1675 }
1676
1677 static PUNWIND_INFO
1678 initialize_unwind_info_internal (GSList *unwind_ops)
1679 {
1680         PUNWIND_INFO unwindinfo;
1681
1682         mono_arch_unwindinfo_create (&unwindinfo);
1683         initialize_unwind_info_internal_ex (unwind_ops, unwindinfo);
1684
1685         return unwindinfo;
1686 }
1687
1688 guchar
1689 mono_arch_unwindinfo_get_code_count (GSList *unwind_ops)
1690 {
1691         UNWIND_INFO unwindinfo = {0};
1692         initialize_unwind_info_internal_ex (unwind_ops, &unwindinfo);
1693         return unwindinfo.CountOfCodes;
1694 }
1695
1696 guint
1697 mono_arch_unwindinfo_init_method_unwind_info (gpointer cfg)
1698 {
1699         MonoCompile * current_cfg = (MonoCompile *)cfg;
1700         g_assert (current_cfg->arch.unwindinfo == NULL);
1701         current_cfg->arch.unwindinfo = initialize_unwind_info_internal (current_cfg->unwind_ops);
1702         return mono_arch_unwindinfo_get_size (((PUNWIND_INFO)(current_cfg->arch.unwindinfo))->CountOfCodes);
1703 }
1704
1705 void
1706 mono_arch_unwindinfo_install_method_unwind_info (gpointer *monoui, gpointer code, guint code_size)
1707 {
1708         PUNWIND_INFO unwindinfo, targetinfo;
1709         guchar codecount;
1710         guint64 targetlocation;
1711         if (!*monoui)
1712                 return;
1713
1714         unwindinfo = (PUNWIND_INFO)*monoui;
1715         targetlocation = (guint64)&(((guchar*)code)[code_size]);
1716         targetinfo = (PUNWIND_INFO) ALIGN_TO(targetlocation, sizeof (mgreg_t));
1717
1718         memcpy (targetinfo, unwindinfo, sizeof (UNWIND_INFO) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES));
1719
1720         codecount = unwindinfo->CountOfCodes;
1721         if (codecount) {
1722                 memcpy (&targetinfo->UnwindCode [0], &unwindinfo->UnwindCode [MONO_MAX_UNWIND_CODES - codecount],
1723                         sizeof (UNWIND_CODE) * codecount);
1724         }
1725
1726 #ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
1727         if (codecount) {
1728                 // Validate the order of unwind op codes in checked builds. Offset should be in descending order.
1729                 // In first iteration previous == current, this is intended to handle UWOP_ALLOC_LARGE as first item.
1730                 int previous = 0;
1731                 for (int current = 0; current < codecount; current++) {
1732                         g_assert_checked (targetinfo->UnwindCode [previous].CodeOffset >= targetinfo->UnwindCode [current].CodeOffset);
1733                         previous = current;
1734                         if (targetinfo->UnwindCode [current].UnwindOp == UWOP_ALLOC_LARGE) {
1735                                 if (targetinfo->UnwindCode [current].OpInfo == 0) {
1736                                         current++;
1737                                 } else {
1738                                         current += 2;
1739                                 }
1740                         }
1741                 }
1742         }
1743 #endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
1744
1745         g_free (unwindinfo);
1746         *monoui = 0;
1747
1748         // Register unwind info in table.
1749         mono_arch_unwindinfo_insert_rt_func_in_table (code, code_size);
1750 }
1751
1752 void
1753 mono_arch_unwindinfo_install_tramp_unwind_info (GSList *unwind_ops, gpointer code, guint code_size)
1754 {
1755         PUNWIND_INFO unwindinfo = initialize_unwind_info_internal (unwind_ops);
1756         if (unwindinfo != NULL) {
1757                 mono_arch_unwindinfo_install_method_unwind_info (&unwindinfo, code, code_size);
1758         }
1759 }
1760
1761 void
1762 mono_arch_code_chunk_new (void *chunk, int size)
1763 {
1764         mono_arch_unwindinfo_insert_range_in_table (chunk, size);
1765 }
1766
1767 void mono_arch_code_chunk_destroy (void *chunk)
1768 {
1769         mono_arch_unwindinfo_remove_pc_range_in_table (chunk);
1770 }
1771 #endif /* MONO_ARCH_HAVE_UNWIND_TABLE */
1772
1773 #if MONO_SUPPORT_TASKLETS && !defined(DISABLE_JIT)
1774 MonoContinuationRestore
1775 mono_tasklets_arch_restore (void)
1776 {
1777         static guint8* saved = NULL;
1778         guint8 *code, *start;
1779         int cont_reg = AMD64_R9; /* register usable on both call conventions */
1780         const guint kMaxCodeSize = 64;
1781         
1782
1783         if (saved)
1784                 return (MonoContinuationRestore)saved;
1785         code = start = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
1786         /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
1787         /* cont is in AMD64_ARG_REG1 ($rcx or $rdi)
1788          * state is in AMD64_ARG_REG2 ($rdx or $rsi)
1789          * lmf_addr is in AMD64_ARG_REG3 ($r8 or $rdx)
1790          * We move cont to cont_reg since we need both rcx and rdi for the copy
1791          * state is moved to $rax so it's setup as the return value and we can overwrite $rsi
1792          */
1793         amd64_mov_reg_reg (code, cont_reg, MONO_AMD64_ARG_REG1, 8);
1794         amd64_mov_reg_reg (code, AMD64_RAX, MONO_AMD64_ARG_REG2, 8);
1795         /* setup the copy of the stack */
1796         amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), sizeof (int));
1797         amd64_shift_reg_imm (code, X86_SHR, AMD64_RCX, 3);
1798         x86_cld (code);
1799         amd64_mov_reg_membase (code, AMD64_RSI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), sizeof (gpointer));
1800         amd64_mov_reg_membase (code, AMD64_RDI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), sizeof (gpointer));
1801         amd64_prefix (code, X86_REP_PREFIX);
1802         amd64_movsl (code);
1803
1804         /* now restore the registers from the LMF */
1805         amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 8);
1806         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rbp), 8);
1807         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rsp), 8);
1808
1809 #ifdef WIN32
1810         amd64_mov_reg_reg (code, AMD64_R14, AMD64_ARG_REG3, 8);
1811 #else
1812         amd64_mov_reg_reg (code, AMD64_R12, AMD64_ARG_REG3, 8);
1813 #endif
1814
1815         /* state is already in rax */
1816         amd64_jump_membase (code, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_ip));
1817         g_assert ((code - start) <= kMaxCodeSize);
1818
1819         mono_arch_flush_icache (start, code - start);
1820         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
1821
1822         saved = start;
1823         return (MonoContinuationRestore)saved;
1824 }
1825 #endif /* MONO_SUPPORT_TASKLETS && !defined(DISABLE_JIT) */
1826
1827 /*
1828  * mono_arch_setup_resume_sighandler_ctx:
1829  *
1830  *   Setup CTX so execution continues at FUNC.
1831  */
1832 void
1833 mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func)
1834 {
1835         /* 
1836          * When resuming from a signal handler, the stack should be misaligned, just like right after
1837          * a call.
1838          */
1839         if ((((guint64)MONO_CONTEXT_GET_SP (ctx)) % 16) == 0)
1840                 MONO_CONTEXT_SET_SP (ctx, (guint64)MONO_CONTEXT_GET_SP (ctx) - 8);
1841         MONO_CONTEXT_SET_IP (ctx, func);
1842 }
1843
1844 #ifdef DISABLE_JIT
1845 gpointer
1846 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
1847 {
1848         g_assert_not_reached ();
1849         return NULL;
1850 }
1851
1852 gpointer
1853 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
1854 {
1855         g_assert_not_reached ();
1856         return NULL;
1857 }
1858
1859 gpointer
1860 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
1861 {
1862         g_assert_not_reached ();
1863         return NULL;
1864 }
1865
1866 gpointer
1867 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
1868 {
1869         g_assert_not_reached ();
1870         return NULL;
1871 }
1872
1873 gpointer
1874 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
1875 {
1876         g_assert_not_reached ();
1877         return NULL;
1878 }
1879
1880 GSList*
1881 mono_amd64_get_exception_trampolines (gboolean aot)
1882 {
1883         g_assert_not_reached ();
1884         return NULL;
1885 }
1886 #endif /* DISABLE_JIT */
1887
1888 #if !MONO_SUPPORT_TASKLETS || defined(DISABLE_JIT)
1889 MonoContinuationRestore
1890 mono_tasklets_arch_restore (void)
1891 {
1892         g_assert_not_reached ();
1893         return NULL;
1894 }
1895 #endif /* !MONO_SUPPORT_TASKLETS || defined(DISABLE_JIT) */