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