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