Improve unwind support on Windows x64.
authorlateralusX <lateralusx.github@gmail.com>
Thu, 2 Feb 2017 11:51:17 +0000 (12:51 +0100)
committerlateralusX <lateralusx.github@gmail.com>
Tue, 28 Feb 2017 13:52:41 +0000 (14:52 +0100)
This commit improves the unwind support on Windows x64 by implementing a number
of missing features. It also adds support for out of proc function table callback
module on older OS versions and switch to different function table methods
(growable function tables) on Win8/Win2012Server and later OS versions.

Commit aligns more to the windows x64 prolog/epilog ABI, but since mono
uses negative offsets from frame pointer while x64 windows uses positive, it is
not possible to do a 100% "correct" representation in the prolog, but if we don’t
give the OS unwinder the full unwind metadata in the frame pointer use case,
we can trick unwinder to do the right thing presenting correct callstacks in
debuggers.

Commit includes representation of windows unwind info in mono's unwind data
using extensions to better integrate windows specific unwind info in
areas currently not supported (like trampolines). It will also be easier to implement
unwind info in full AOT objects in the future, if windows specific unwind data is
included in mono’s unwind data structures.

On Win8/Win2012Server and later OS versions, all function table data is registered
using growable function tables giving full support for callstacks on live debug targets
and crash dumps. On previous OS versions function table callbacks are used together
with an out of proc module. This works for live debugging in WinDBG and Visual Studio
(if right permissions are given to the debuggers to load out of proc unwind module).
Crash dumps works as expected in WinDBG but currently not in Visual Studio for older OS
versions using callback support. If that use case needs to be supported it needs to be
solved in a separate PR, but since we support crash dumps in Visual Studio on later OS
versions and in WinDBG on earlier, it is still possible to analyze crash dumps by picking
the right tool.

Debuggers needs some additional information to work with function table callbacks out of proc
modules (NOTE, this is NOT needed on Win8/Win2012Server and later).

WinDBG:

To just do live debugging, it is possible to run the following command in WinDBG,

settings set EngineInitialization.VerifyFunctionTableCallbacks=false

To do crash dump analysis the following registry key needs to be set,

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\KnownFunctionTableDlls

Add the full path to mono-2.0-dac-sgen.dll matching the mono-2.0-sgen.dll as a 32-bit REG_DWORD.

NOTE, adding the registry key removes the need to set the setting.

Visual Studio:

To do live debugging, set the same registry key as WinDBG use case.

17 files changed:
eglib/winconfig.h
mono/mini/Makefile.am.in
mono/mini/aot-compiler.c
mono/mini/exceptions-amd64.c
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-runtime.c
mono/mini/mini-unwind.h
mono/mini/mini-windows-dlldac.c [new file with mode: 0644]
mono/mini/mini.c
mono/mini/tramp-amd64-gsharedvt.c
mono/mini/tramp-amd64.c
mono/mini/unwind.c
msvc/libmonodac.vcxproj [new file with mode: 0644]
msvc/libmonodac.vcxproj.filters [new file with mode: 0644]
msvc/mono.sln
winconfig.h

index 238250bba05455b75186c920c11dc70efd3d983d..31bbf89a6a17169c8c7dbb4b30251b09a38cef9f 100755 (executable)
@@ -5,6 +5,32 @@
 #include "cygconfig.h"
 #else
 
+#ifndef HAVE_WINAPI_FAMILY_SUPPORT
+
+#define HAVE_WINAPI_FAMILY_SUPPORT
+
+/* WIN API Family support */
+#include <winapifamily.h>
+
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 1
+       #define HAVE_UWP_WINAPI_SUPPORT 0
+#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
+       #define HAVE_UWP_WINAPI_SUPPORT 1
+#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
+       #error Unsupported WINAPI family
+#endif
+#else
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
+       #define HAVE_UWP_WINAPI_SUPPORT 0
+#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
+       #error Unsupported WINAPI family
+#endif
+#endif
+
+#endif
+
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #define HAVE_DLFCN_H 1
 
 #define VERSION "0.1"
 
 #define HAVE_STRTOK_R 1
-
-#ifndef HAVE_WINAPI_FAMILY_SUPPORT
-
-#define HAVE_WINAPI_FAMILY_SUPPORT
-
-/* WIN API Family support */
-#include <winapifamily.h>
-
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 1
-       #define HAVE_UWP_WINAPI_SUPPORT 0
-#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
-       #define HAVE_UWP_WINAPI_SUPPORT 1
-#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
-       #error Unsupported WINAPI family
-#endif
-#else
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
-       #define HAVE_UWP_WINAPI_SUPPORT 0
-#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
-       #error Unsupported WINAPI family
-#endif
-#endif
-
-#endif
 #endif
index 59888bbc64dc722b2d29ab611d8a06f6ee1a6d7b..3c7822f0fe018e837da805fe5ae40a27b7158e4c 100755 (executable)
@@ -369,7 +369,8 @@ darwin_sources = \
 windows_sources = \
        mini-windows.c \
        mini-windows.h \
-       mini-windows-dllmain.c
+       mini-windows-dllmain.c \
+       mini-windows-dlldac.c
 
 posix_sources = \
        mini-posix.c
index 6d4f50a42388fb4e9641557cf720f6c0756f0c50..864d0b247c2bbebacaa2927b5c55ae19929711bd 100644 (file)
@@ -8201,7 +8201,7 @@ execute_system (const char * command)
 {
        int status = 0;
 
-#if HOST_WIN32
+#if defined(HOST_WIN32) && defined(HAVE_SYSTEM)
        // We need an extra set of quotes around the whole command to properly handle commands 
        // with spaces since internally the command is called through "cmd /c.
        char * quoted_command = g_strdup_printf ("\"%s\"", command);
index 38c333c466cadf36d6e9b77e9c59bc514decc018..79ed9b33755ebeb1833c8237e1be4bfebbe81321 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Authors:
  *   Dietmar Maurer (dietmar@ximian.com)
+ *   Johan Lorensson (lateralusx.github@gmail.com)
  *
  * (C) 2001 Ximian, Inc.
  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
@@ -273,7 +274,12 @@ mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
                if (AMD64_IS_CALLEE_SAVED_REG (i))
                        amd64_pop_reg (code, i);
 
+#if TARGET_WIN32
+       amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+       amd64_pop_reg (code, AMD64_RBP);
+#else
        amd64_leave (code);
+#endif
        amd64_ret (code);
 
        g_assert ((code - start) < kMaxCodeSize);
@@ -375,7 +381,10 @@ get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, g
        dummy_stack_space = 0;
 #endif
 
-       start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
+       if (info)
+               start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
+       else
+               start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
 
        /* The stack is unaligned on entry */
        stack_size = ALIGN_TO (sizeof (MonoContext) + 64 + dummy_stack_space, MONO_ARCH_FRAME_ALIGNMENT) + 8;
@@ -387,8 +396,10 @@ get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, g
 
        /* Alloc frame */
        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, stack_size);
-       if (info)
+       if (info) {
                mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 8);
+               mono_add_unwind_op_sp_alloc (unwind_ops, code, start, stack_size);
+       }
 
        /*
         * To hide linux/windows calling convention differences, we pass all arguments on
@@ -455,6 +466,7 @@ get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, g
        mono_arch_flush_icache (start, code - start);
 
        g_assert ((code - start) < kMaxCodeSize);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
 
        mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
 
@@ -885,260 +897,878 @@ mono_arch_exceptions_init (void)
        }
 }
 
-#if defined(TARGET_WIN32) && !defined(DISABLE_JIT)
-
-/*
- * The mono_arch_unwindinfo* methods are used to build and add
- * function table info for each emitted method from mono.  On Winx64
- * the seh handler will not be called if the mono methods are not
- * added to the function table.  
- *
- * We should not need to add non-volatile register info to the 
- * table since mono stores that info elsewhere. (Except for the register 
- * used for the fp.)
- */
-
-#define MONO_MAX_UNWIND_CODES 22
-
-typedef union _UNWIND_CODE {
-    struct {
-        guchar CodeOffset;
-        guchar UnwindOp : 4;
-        guchar OpInfo   : 4;
-    };
-    gushort FrameOffset;
-} UNWIND_CODE, *PUNWIND_CODE;
-
-typedef struct _UNWIND_INFO {
-       guchar Version       : 3;
-       guchar Flags         : 5;
-       guchar SizeOfProlog;
-       guchar CountOfCodes;
-       guchar FrameRegister : 4;
-       guchar FrameOffset   : 4;
-       /* custom size for mono allowing for mono allowing for*/
-       /*UWOP_PUSH_NONVOL ebp offset = 21*/
-       /*UWOP_ALLOC_LARGE : requires 2 or 3 offset = 20*/
-       /*UWOP_SET_FPREG : requires 2 offset = 17*/
-       /*UWOP_PUSH_NONVOL offset = 15-0*/
-       UNWIND_CODE UnwindCode[MONO_MAX_UNWIND_CODES]; 
-
-/*     UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
- *     union {
- *         OPTIONAL ULONG ExceptionHandler;
- *         OPTIONAL ULONG FunctionEntry;
- *     };
- *     OPTIONAL ULONG ExceptionData[]; */
-} UNWIND_INFO, *PUNWIND_INFO;
-
-typedef struct
-{
-       RUNTIME_FUNCTION runtimeFunction;
-       UNWIND_INFO unwindInfo;
-} MonoUnwindInfo, *PMonoUnwindInfo;
+#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
 
 static void
 mono_arch_unwindinfo_create (gpointer* monoui)
 {
-       PMonoUnwindInfo newunwindinfo;
-       *monoui = newunwindinfo = g_new0 (MonoUnwindInfo, 1);
-       newunwindinfo->unwindInfo.Version = 1;
+       PUNWIND_INFO newunwindinfo;
+       *monoui = newunwindinfo = g_new0 (UNWIND_INFO, 1);
+       newunwindinfo->Version = 1;
 }
 
 void
-mono_arch_unwindinfo_add_push_nonvol (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg )
+mono_arch_unwindinfo_add_push_nonvol (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
 {
-       PMonoUnwindInfo unwindinfo;
        PUNWIND_CODE unwindcode;
        guchar codeindex;
-       if (!*monoui)
-               mono_arch_unwindinfo_create (monoui);
-       
-       unwindinfo = (MonoUnwindInfo*)*monoui;
 
-       if (unwindinfo->unwindInfo.CountOfCodes >= MONO_MAX_UNWIND_CODES)
+       g_assert (unwindinfo != NULL);
+
+       if (unwindinfo->CountOfCodes >= MONO_MAX_UNWIND_CODES)
                g_error ("Larger allocation needed for the unwind information.");
 
-       codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->unwindInfo.CountOfCodes);
-       unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
-       unwindcode->UnwindOp = 0; /*UWOP_PUSH_NONVOL*/
-       unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
-       unwindcode->OpInfo = reg;
+       codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
+       unwindcode = &unwindinfo->UnwindCode[codeindex];
+       unwindcode->UnwindOp = UWOP_PUSH_NONVOL;
+       unwindcode->CodeOffset = (guchar)unwind_op->when;
+       unwindcode->OpInfo = unwind_op->reg;
 
-       if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
+       if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
                g_error ("Adding unwind info in wrong order.");
-       
-       unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
+
+       unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
 }
 
 void
-mono_arch_unwindinfo_add_set_fpreg (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg )
+mono_arch_unwindinfo_add_set_fpreg (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
 {
-       PMonoUnwindInfo unwindinfo;
        PUNWIND_CODE unwindcode;
        guchar codeindex;
-       if (!*monoui)
-               mono_arch_unwindinfo_create (monoui);
-       
-       unwindinfo = (MonoUnwindInfo*)*monoui;
 
-       if (unwindinfo->unwindInfo.CountOfCodes + 1 >= MONO_MAX_UNWIND_CODES)
+       g_assert (unwindinfo != NULL);
+
+       if (unwindinfo->CountOfCodes + 1 >= MONO_MAX_UNWIND_CODES)
                g_error ("Larger allocation needed for the unwind information.");
 
-       codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->unwindInfo.CountOfCodes += 2);
-       unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
-       unwindcode->FrameOffset = 0; /*Assuming no frame pointer offset for mono*/
-       unwindcode++;
-       unwindcode->UnwindOp = 3; /*UWOP_SET_FPREG*/
-       unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
-       unwindcode->OpInfo = reg;
-       
-       unwindinfo->unwindInfo.FrameRegister = reg;
+       codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
+       unwindcode = &unwindinfo->UnwindCode[codeindex];
+       unwindcode->UnwindOp = UWOP_SET_FPREG;
+       unwindcode->CodeOffset = (guchar)unwind_op->when;
 
-       if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
+       g_assert(unwind_op->val % 16 == 0);
+       unwindinfo->FrameRegister = unwind_op->reg;
+       unwindinfo->FrameOffset = unwind_op->val / 16;
+
+       if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
                g_error ("Adding unwind info in wrong order.");
-       
-       unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
+
+       unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
 }
 
 void
-mono_arch_unwindinfo_add_alloc_stack (gpointer* monoui, gpointer codebegin, gpointer nextip, guint size )
+mono_arch_unwindinfo_add_alloc_stack (PUNWIND_INFO unwindinfo, MonoUnwindOp *unwind_op)
 {
-       PMonoUnwindInfo unwindinfo;
        PUNWIND_CODE unwindcode;
        guchar codeindex;
        guchar codesneeded;
-       if (!*monoui)
-               mono_arch_unwindinfo_create (monoui);
-       
-       unwindinfo = (MonoUnwindInfo*)*monoui;
+       guint size;
+
+       g_assert (unwindinfo != NULL);
+
+       size = unwind_op->val;
 
        if (size < 0x8)
                g_error ("Stack allocation must be equal to or greater than 0x8.");
-       
+
        if (size <= 0x80)
                codesneeded = 1;
        else if (size <= 0x7FFF8)
                codesneeded = 2;
        else
                codesneeded = 3;
-       
-       if (unwindinfo->unwindInfo.CountOfCodes + codesneeded > MONO_MAX_UNWIND_CODES)
+
+       if (unwindinfo->CountOfCodes + codesneeded > MONO_MAX_UNWIND_CODES)
                g_error ("Larger allocation needed for the unwind information.");
 
-       codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->unwindInfo.CountOfCodes += codesneeded);
-       unwindcode = &unwindinfo->unwindInfo.UnwindCode[codeindex];
+       codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->CountOfCodes += codesneeded);
+       unwindcode = &unwindinfo->UnwindCode[codeindex];
+
+       unwindcode->CodeOffset = (guchar)unwind_op->when;
 
        if (codesneeded == 1) {
-               /*The size of the allocation is 
+               /*The size of the allocation is
                  (the number in the OpInfo member) times 8 plus 8*/
+               unwindcode->UnwindOp = UWOP_ALLOC_SMALL;
                unwindcode->OpInfo = (size - 8)/8;
-               unwindcode->UnwindOp = 2; /*UWOP_ALLOC_SMALL*/
        }
        else {
                if (codesneeded == 3) {
                        /*the unscaled size of the allocation is recorded
-                         in the next two slots in little-endian format*/
-                       *((unsigned int*)(&unwindcode->FrameOffset)) = size;
-                       unwindcode += 2;
+                         in the next two slots in little-endian format.
+                         NOTE, unwind codes are allocated from end to begining of list so
+                         unwind code will have right execution order. List is sorted on CodeOffset
+                         using descending sort order.*/
+                       unwindcode->UnwindOp = UWOP_ALLOC_LARGE;
                        unwindcode->OpInfo = 1;
+                       *((unsigned int*)(&(unwindcode + 1)->FrameOffset)) = size;
                }
                else {
                        /*the size of the allocation divided by 8
-                         is recorded in the next slot*/
-                       unwindcode->FrameOffset = size/8; 
-                       unwindcode++;   
+                         is recorded in the next slot.
+                         NOTE, unwind codes are allocated from end to begining of list so
+                         unwind code will have right execution order. List is sorted on CodeOffset
+                         using descending sort order.*/
+                       unwindcode->UnwindOp = UWOP_ALLOC_LARGE;
                        unwindcode->OpInfo = 0;
-                       
+                       (unwindcode + 1)->FrameOffset = (gushort)(size/8);
                }
-               unwindcode->UnwindOp = 1; /*UWOP_ALLOC_LARGE*/
        }
 
-       unwindcode->CodeOffset = (((guchar*)nextip)-((guchar*)codebegin));
-
-       if (unwindinfo->unwindInfo.SizeOfProlog >= unwindcode->CodeOffset)
+       if (unwindinfo->SizeOfProlog >= unwindcode->CodeOffset)
                g_error ("Adding unwind info in wrong order.");
-       
-       unwindinfo->unwindInfo.SizeOfProlog = unwindcode->CodeOffset;
+
+       unwindinfo->SizeOfProlog = unwindcode->CodeOffset;
 }
 
-guint
-mono_arch_unwindinfo_get_size (gpointer monoui)
+static gboolean g_dyn_func_table_inited;
+
+// Dynamic function table used when registering unwind info for OS unwind support.
+static GList *g_dynamic_function_table_begin;
+static GList *g_dynamic_function_table_end;
+
+// SRW lock (lightweight read/writer lock) protecting dynamic function table.
+static SRWLOCK g_dynamic_function_table_lock = SRWLOCK_INIT;
+
+// Module handle used when explicit loading ntdll.
+static HMODULE g_ntdll;
+
+// If Win8 or Win2012Server or later, use growable function tables instead
+// of callbacks. Callback solution will still be fallback on older systems.
+static RtlAddGrowableFunctionTablePtr g_rtl_add_growable_function_table;
+static RtlGrowFunctionTablePtr g_rtl_grow_function_table;
+static RtlDeleteGrowableFunctionTablePtr g_rtl_delete_growable_function_table;
+
+// When using function table callback solution an out of proc module is needed by
+// debuggers in order to read unwind info from debug target.
+#ifdef _MSC_VER
+#define MONO_DAC_MODULE L"mono-2.0-dac-sgen.dll"
+#else
+#define MONO_DAC_MODULE L"mono-2.0-sgen.dll"
+#endif
+
+#define MONO_DAC_MODULE_MAX_PATH 1024
+
+static void
+mono_arch_unwindinfo_init_table_no_lock (void)
 {
-       PMonoUnwindInfo unwindinfo;
-       if (!monoui)
-               return 0;
-       
-       unwindinfo = (MonoUnwindInfo*)monoui;
-       return (8 + sizeof (MonoUnwindInfo)) - 
-               (sizeof (UNWIND_CODE) * (MONO_MAX_UNWIND_CODES - unwindinfo->unwindInfo.CountOfCodes));
+       if (g_dyn_func_table_inited == FALSE) {
+               g_assert_checked (g_dynamic_function_table_begin == NULL);
+               g_assert_checked (g_dynamic_function_table_end == NULL);
+               g_assert_checked (g_rtl_add_growable_function_table == NULL);
+               g_assert_checked (g_rtl_grow_function_table == NULL);
+               g_assert_checked (g_rtl_delete_growable_function_table == NULL);
+               g_assert_checked (g_ntdll == NULL);
+
+               // Load functions available on Win8/Win2012Server or later. If running on earlier
+               // systems the below GetProceAddress will fail, this is expected behavior.
+               if (GetModuleHandleEx (0, TEXT("ntdll.dll"), &g_ntdll) == TRUE) {
+                       g_rtl_add_growable_function_table = (RtlAddGrowableFunctionTablePtr)GetProcAddress (g_ntdll, "RtlAddGrowableFunctionTable");
+                       g_rtl_grow_function_table = (RtlGrowFunctionTablePtr)GetProcAddress (g_ntdll, "RtlGrowFunctionTable");
+                       g_rtl_delete_growable_function_table = (RtlDeleteGrowableFunctionTablePtr)GetProcAddress (g_ntdll, "RtlDeleteGrowableFunctionTable");
+               }
+
+               g_dyn_func_table_inited = TRUE;
+       }
+}
+
+void
+mono_arch_unwindinfo_init_table (void)
+{
+       if (g_dyn_func_table_inited == FALSE) {
+
+               AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
+
+               mono_arch_unwindinfo_init_table_no_lock ();
+
+               ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
+       }
+}
+
+static void
+mono_arch_unwindinfo_terminate_table_no_lock (void)
+{
+       if (g_dyn_func_table_inited == TRUE) {
+               if (g_dynamic_function_table_begin != NULL) {
+                       // Free all list elements.
+                       for (GList *l = g_dynamic_function_table_begin; l; l = l->next) {
+                               if (l->data) {
+                                       g_free (l->data);
+                                       l->data = NULL;
+                               }
+                       }
+
+                       //Free the list.
+                       g_list_free (g_dynamic_function_table_begin);
+                       g_dynamic_function_table_begin = NULL;
+                       g_dynamic_function_table_end = NULL;
+               }
+
+               g_rtl_delete_growable_function_table = NULL;
+               g_rtl_grow_function_table = NULL;
+               g_rtl_add_growable_function_table = NULL;
+
+               if (g_ntdll != NULL) {
+                       FreeLibrary (g_ntdll);
+                       g_ntdll = NULL;
+               }
+
+               g_dyn_func_table_inited = FALSE;
+       }
+}
+
+void
+mono_arch_unwindinfo_terminate_table (void)
+{
+       if (g_dyn_func_table_inited == TRUE) {
+
+               AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
+
+               mono_arch_unwindinfo_terminate_table_no_lock ();
+
+               ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
+       }
+}
+
+static GList *
+mono_arch_unwindinfo_fast_find_range_in_table_no_lock_ex (gsize begin_range, gsize end_range, gboolean *continue_search)
+{
+       GList *found_entry = NULL;
+
+       // Fast path, look at boundaries.
+       if (g_dynamic_function_table_begin != NULL) {
+               DynamicFunctionTableEntry *first_entry = g_dynamic_function_table_begin->data;
+               DynamicFunctionTableEntry *last_entry = (g_dynamic_function_table_end != NULL ) ? g_dynamic_function_table_end->data : first_entry;
+
+               // Sorted in descending order based on begin_range, check first item, that is the entry with highest range.
+               if (first_entry != NULL && first_entry->begin_range <= begin_range && first_entry->end_range >= end_range) {
+                               // Entry belongs to first entry in list.
+                               found_entry = g_dynamic_function_table_begin;
+                               *continue_search = FALSE;
+               } else {
+                       if (first_entry != NULL && first_entry->begin_range >= begin_range) {
+                               if (last_entry != NULL && last_entry->begin_range <= begin_range) {
+                                       // Entry has a range that could exist in table, continue search.
+                                       *continue_search = TRUE;
+                               }
+                       }
+               }
+       }
+
+       return found_entry;
+}
+
+static inline DynamicFunctionTableEntry *
+mono_arch_unwindinfo_fast_find_range_in_table_no_lock (gsize begin_range, gsize end_range, gboolean *continue_search)
+{
+       GList *found_entry = mono_arch_unwindinfo_fast_find_range_in_table_no_lock_ex (begin_range, end_range, continue_search);
+       return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
+}
+
+static GList *
+mono_arch_unwindinfo_find_range_in_table_no_lock_ex (const gpointer code_block, gsize block_size)
+{
+       GList *found_entry = NULL;
+       gboolean continue_search = FALSE;
+
+       gsize begin_range = (gsize)code_block;
+       gsize end_range = begin_range + block_size;
+
+       // Fast path, check table boundaries.
+       found_entry = mono_arch_unwindinfo_fast_find_range_in_table_no_lock_ex (begin_range, end_range, &continue_search);
+       if (found_entry || continue_search == FALSE)
+               return found_entry;
+
+       // Scan table for an entry including range.
+       for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
+               DynamicFunctionTableEntry *current_entry = (DynamicFunctionTableEntry *)node->data;
+               g_assert_checked (current_entry != NULL);
+
+               // Do we have a match?
+               if (current_entry->begin_range == begin_range && current_entry->end_range == end_range) {
+                       found_entry = node;
+                       break;
+               }
+       }
+
+       return found_entry;
+}
+
+static inline DynamicFunctionTableEntry *
+mono_arch_unwindinfo_find_range_in_table_no_lock (const gpointer code_block, gsize block_size)
+{
+       GList *found_entry = mono_arch_unwindinfo_find_range_in_table_no_lock_ex (code_block, block_size);
+       return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
+}
+
+static GList *
+mono_arch_unwindinfo_find_pc_in_table_no_lock_ex (const gpointer pc)
+{
+       GList *found_entry = NULL;
+       gboolean continue_search = FALSE;
+
+       gsize begin_range = (gsize)pc;
+       gsize end_range = begin_range;
+
+       // Fast path, check table boundaries.
+       found_entry = mono_arch_unwindinfo_fast_find_range_in_table_no_lock_ex (begin_range, begin_range, &continue_search);
+       if (found_entry || continue_search == FALSE)
+               return found_entry;
+
+       // Scan table for a entry including range.
+       for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
+               DynamicFunctionTableEntry *current_entry = (DynamicFunctionTableEntry *)node->data;
+               g_assert_checked (current_entry != NULL);
+
+               // Do we have a match?
+               if (current_entry->begin_range <= begin_range && current_entry->end_range >= end_range) {
+                       found_entry = node;
+                       break;
+               }
+       }
+
+       return found_entry;
+}
+
+static inline DynamicFunctionTableEntry *
+mono_arch_unwindinfo_find_pc_in_table_no_lock (const gpointer pc)
+{
+       GList *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock_ex (pc);
+       return (found_entry != NULL) ? (DynamicFunctionTableEntry *)found_entry->data : NULL;
+}
+
+#ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
+static void
+mono_arch_unwindinfo_validate_table_no_lock (void)
+{
+       // Validation method checking that table is sorted as expected and don't include overlapped regions.
+       // Method will assert on failure to explicitly indicate what check failed.
+       if (g_dynamic_function_table_begin != NULL) {
+               g_assert_checked (g_dynamic_function_table_end != NULL);
+
+               DynamicFunctionTableEntry *prevoious_entry = NULL;
+               DynamicFunctionTableEntry *current_entry = NULL;
+               for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
+                       current_entry = (DynamicFunctionTableEntry *)node->data;
+
+                       g_assert_checked (current_entry != NULL);
+                       g_assert_checked (current_entry->end_range > current_entry->begin_range);
+
+                       if (prevoious_entry != NULL) {
+                               // List should be sorted in descending order on begin_range.
+                               g_assert_checked (prevoious_entry->begin_range > current_entry->begin_range);
+
+                               // Check for overlapped regions.
+                               g_assert_checked (prevoious_entry->begin_range >= current_entry->end_range);
+                       }
+
+                       prevoious_entry = current_entry;
+               }
+       }
+}
+
+#else
+
+static inline void
+mono_arch_unwindinfo_validate_table_no_lock (void)
+{
+       ;
+}
+#endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
+
+// Forward declare.
+static PRUNTIME_FUNCTION MONO_GET_RUNTIME_FUNCTION_CALLBACK (DWORD64 ControlPc, IN PVOID Context);
+
+DynamicFunctionTableEntry *
+mono_arch_unwindinfo_insert_range_in_table (const gpointer code_block, gsize block_size)
+{
+       DynamicFunctionTableEntry *new_entry = NULL;
+
+       gsize begin_range = (gsize)code_block;
+       gsize end_range = begin_range + block_size;
+
+       AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
+       mono_arch_unwindinfo_init_table_no_lock ();
+       new_entry = mono_arch_unwindinfo_find_range_in_table_no_lock (code_block, block_size);
+       if (new_entry == NULL) {
+               // Allocate new entry.
+               new_entry = g_new0 (DynamicFunctionTableEntry, 1);
+               if (new_entry != NULL) {
+
+                       // Pre-allocate RUNTIME_FUNCTION array, assume average method size of
+                       // MONO_UNWIND_INFO_RT_FUNC_SIZE bytes.
+                       InitializeSRWLock (&new_entry->lock);
+                       new_entry->handle = NULL;
+                       new_entry->begin_range = begin_range;
+                       new_entry->end_range = end_range;
+                       new_entry->rt_funcs_max_count = (block_size / MONO_UNWIND_INFO_RT_FUNC_SIZE) + 1;
+                       new_entry->rt_funcs_current_count = 0;
+                       new_entry->rt_funcs = g_new0 (RUNTIME_FUNCTION, new_entry->rt_funcs_max_count);
+
+                       if (new_entry->rt_funcs != NULL) {
+                               // Check insert on boundaries. List is sorted descending on begin_range.
+                               if (g_dynamic_function_table_begin == NULL) {
+                                       g_dynamic_function_table_begin = g_list_append (g_dynamic_function_table_begin, new_entry);
+                                       g_dynamic_function_table_end = g_dynamic_function_table_begin;
+                               } else if (((DynamicFunctionTableEntry *)(g_dynamic_function_table_begin->data))->begin_range < begin_range) {
+                                       // Insert at the head.
+                                       g_dynamic_function_table_begin = g_list_prepend (g_dynamic_function_table_begin, new_entry);
+                               } else if (((DynamicFunctionTableEntry *)(g_dynamic_function_table_end->data))->begin_range > begin_range) {
+                                       // Insert at tail.
+                                       g_list_append (g_dynamic_function_table_end, new_entry);
+                                       g_dynamic_function_table_end = g_dynamic_function_table_end->next;
+                               } else {
+                                       //Search and insert at correct position.
+                                       for (GList *node = g_dynamic_function_table_begin; node; node = node->next) {
+                                               DynamicFunctionTableEntry * current_entry = (DynamicFunctionTableEntry *)node->data;
+                                               g_assert_checked (current_entry != NULL);
+
+                                               if (current_entry->begin_range < new_entry->begin_range) {
+                                                       g_dynamic_function_table_begin = g_list_insert_before (g_dynamic_function_table_begin, node, new_entry);
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               // Register dynamic function table entry with OS.
+                               if (g_rtl_add_growable_function_table != NULL) {
+                                       // Allocate new growable handle table for entry.
+                                       g_assert_checked (new_entry->handle == NULL);
+                                       DWORD result = g_rtl_add_growable_function_table (&new_entry->handle,
+                                                                               new_entry->rt_funcs, new_entry->rt_funcs_current_count,
+                                                                               new_entry->rt_funcs_max_count, new_entry->begin_range, new_entry->end_range);
+                                       g_assert (!result);
+                               } else {
+                                       WCHAR buffer [MONO_DAC_MODULE_MAX_PATH] = { 0 };
+                                       WCHAR *path = buffer;
+
+                                       // DAC module should be in the same directory as the
+                                       // main executable.
+                                       GetModuleFileNameW (NULL, buffer, G_N_ELEMENTS (buffer));
+                                       path = wcsrchr (buffer, TEXT('\\'));
+                                       if (path != NULL) {
+                                               path++;
+                                               *path = TEXT('\0');
+                                       }
+
+                                       wcscat_s (buffer, G_N_ELEMENTS (buffer), MONO_DAC_MODULE);
+                                       path = buffer;
+
+                                       // Register function table callback + out of proc module.
+                                       new_entry->handle = (PVOID)((DWORD64)(new_entry->begin_range) | 3);
+                                       BOOLEAN result = RtlInstallFunctionTableCallback ((DWORD64)(new_entry->handle),
+                                                                               (DWORD64)(new_entry->begin_range), (DWORD)(new_entry->end_range - new_entry->begin_range),
+                                                                               MONO_GET_RUNTIME_FUNCTION_CALLBACK, new_entry, path);
+                                       g_assert(result);
+                               }
+
+                               // Only included in checked builds. Validates the structure of table after insert.
+                               mono_arch_unwindinfo_validate_table_no_lock ();
+
+                       } else {
+                               g_free (new_entry);
+                               new_entry = NULL;
+                       }
+               }
+       }
+       ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
+
+       return new_entry;
+}
+
+static void
+mono_arch_unwindinfo_remove_range_in_table_no_lock (GList *entry)
+{
+       if (entry != NULL) {
+               if (entry == g_dynamic_function_table_end)
+                       g_dynamic_function_table_end = entry->prev;
+
+               g_dynamic_function_table_begin = g_list_remove_link(g_dynamic_function_table_begin, entry);
+               DynamicFunctionTableEntry *removed_entry = (DynamicFunctionTableEntry *)entry->data;
+
+               g_assert_checked (removed_entry != NULL);
+               g_assert_checked (removed_entry->rt_funcs != NULL);
+
+               // Remove function table from OS.
+               if (removed_entry->handle != NULL) {
+                       if (g_rtl_delete_growable_function_table != NULL) {
+                               g_rtl_delete_growable_function_table (removed_entry->handle);
+                       } else {
+                               RtlDeleteFunctionTable ((PRUNTIME_FUNCTION)removed_entry->handle);
+                       }
+               }
+
+               g_free (removed_entry->rt_funcs);
+               g_free (removed_entry);
+
+               g_list_free_1 (entry);
+       }
+
+       // Only included in checked builds. Validates the structure of table after remove.
+       mono_arch_unwindinfo_validate_table_no_lock ();
+}
+
+void
+mono_arch_unwindinfo_remove_pc_range_in_table (const gpointer code)
+{
+       AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
+
+       GList *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock_ex (code);
+
+       g_assert_checked (found_entry != NULL || ((DynamicFunctionTableEntry *)found_entry->data)->begin_range == (gsize)code);
+       mono_arch_unwindinfo_remove_range_in_table_no_lock (found_entry);
+
+       ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
+}
+
+void
+mono_arch_unwindinfo_remove_range_in_table (const gpointer code_block, gsize block_size)
+{
+       AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
+
+       GList *found_entry = mono_arch_unwindinfo_find_range_in_table_no_lock_ex (code_block, block_size);
+
+       g_assert_checked (found_entry != NULL || ((DynamicFunctionTableEntry *)found_entry->data)->begin_range == (gsize)code_block);
+       mono_arch_unwindinfo_remove_range_in_table_no_lock (found_entry);
+
+       ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
+}
+
+PRUNTIME_FUNCTION
+mono_arch_unwindinfo_find_rt_func_in_table (const gpointer code, gsize code_size)
+{
+       PRUNTIME_FUNCTION found_rt_func = NULL;
+
+       gsize begin_range = (gsize)code;
+       gsize end_range = begin_range + code_size;
+
+       AcquireSRWLockShared (&g_dynamic_function_table_lock);
+
+       DynamicFunctionTableEntry *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock (code);
+
+       if (found_entry != NULL) {
+
+               AcquireSRWLockShared (&found_entry->lock);
+
+               g_assert_checked (found_entry->begin_range <= begin_range);
+               g_assert_checked (found_entry->end_range >= begin_range && found_entry->end_range >= end_range);
+               g_assert_checked (found_entry->rt_funcs != NULL);
+
+               for (int i = 0; i < found_entry->rt_funcs_current_count; ++i) {
+                       PRUNTIME_FUNCTION current_rt_func = (PRUNTIME_FUNCTION)(&found_entry->rt_funcs [i]);
+
+                       // Is this our RT function entry?
+                       if (found_entry->begin_range + current_rt_func->BeginAddress <= begin_range &&
+                               found_entry->begin_range + current_rt_func->EndAddress >= end_range) {
+                               found_rt_func = current_rt_func;
+                               break;
+                       }
+               }
+
+               ReleaseSRWLockShared (&found_entry->lock);
+       }
+
+       ReleaseSRWLockShared (&g_dynamic_function_table_lock);
+
+       return found_rt_func;
+}
+
+inline PRUNTIME_FUNCTION
+mono_arch_unwindinfo_find_pc_rt_func_in_table (const gpointer pc)
+{
+       return mono_arch_unwindinfo_find_rt_func_in_table (pc, 0);
+}
+
+#ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
+static void
+mono_arch_unwindinfo_validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
+{
+       // Validation method checking that runtime function table is sorted as expected and don't include overlapped regions.
+       // Method will assert on failure to explicitly indicate what check failed.
+       g_assert_checked (entry != NULL);
+       g_assert_checked (entry->rt_funcs_max_count >= entry->rt_funcs_current_count);
+       g_assert_checked (entry->rt_funcs != NULL);
+
+       PRUNTIME_FUNCTION current_rt_func = NULL;
+       PRUNTIME_FUNCTION previous_rt_func = NULL;
+       for (int i = 0; i < entry->rt_funcs_current_count; ++i) {
+               current_rt_func = &(entry->rt_funcs [i]);
+
+               g_assert_checked (current_rt_func->BeginAddress < current_rt_func->EndAddress);
+               g_assert_checked (current_rt_func->EndAddress <= current_rt_func->UnwindData);
+
+               if (previous_rt_func != NULL) {
+                       // List should be sorted in ascending order based on BeginAddress.
+                       g_assert_checked (previous_rt_func->BeginAddress < current_rt_func->BeginAddress);
+
+                       // Check for overlapped regions.
+                       g_assert_checked (previous_rt_func->EndAddress <= current_rt_func->BeginAddress);
+               }
+
+               previous_rt_func = current_rt_func;
+       }
+}
+
+#else
+
+static inline void
+mono_arch_unwindinfo_validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
+{
+       ;
+}
+#endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
+
+PRUNTIME_FUNCTION
+mono_arch_unwindinfo_insert_rt_func_in_table (const gpointer code, gsize code_size)
+{
+       PRUNTIME_FUNCTION new_rt_func = NULL;
+
+       gsize begin_range = (gsize)code;
+       gsize end_range = begin_range + code_size;
+
+       AcquireSRWLockShared (&g_dynamic_function_table_lock);
+
+       DynamicFunctionTableEntry *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock (code);
+
+       if (found_entry != NULL) {
+
+               AcquireSRWLockExclusive (&found_entry->lock);
+
+               g_assert_checked (found_entry->begin_range <= begin_range);
+               g_assert_checked (found_entry->end_range >= begin_range && found_entry->end_range >= end_range);
+               g_assert_checked (found_entry->rt_funcs != NULL);
+               g_assert_checked ((guchar*)code - found_entry->begin_range >= 0);
+
+               gsize code_offset = (gsize)code - found_entry->begin_range;
+               gsize entry_count = found_entry->rt_funcs_current_count;
+               gsize max_entry_count = found_entry->rt_funcs_max_count;
+               PRUNTIME_FUNCTION current_rt_funcs = found_entry->rt_funcs;
+
+               RUNTIME_FUNCTION new_rt_func_data;
+               new_rt_func_data.BeginAddress = code_offset;
+               new_rt_func_data.EndAddress = code_offset + code_size;
+
+               gsize aligned_unwind_data = ALIGN_TO(end_range, sizeof (mgreg_t));
+               new_rt_func_data.UnwindData = aligned_unwind_data - found_entry->begin_range;
+
+               g_assert_checked (new_rt_func_data.UnwindData == ALIGN_TO(new_rt_func_data.EndAddress, sizeof (mgreg_t)));
+
+               PRUNTIME_FUNCTION new_rt_funcs = NULL;
+
+               // List needs to be sorted in ascending order based on BeginAddress (Windows requirement if list
+               // going to be directly reused in OS func tables. Check if we can append to end of existing table without realloc.
+               if (entry_count == 0 || (entry_count < max_entry_count) && (current_rt_funcs [entry_count - 1].BeginAddress) < code_offset) {
+                       new_rt_func = &(current_rt_funcs [entry_count]);
+                       *new_rt_func = new_rt_func_data;
+                       entry_count++;
+               } else {
+                       // No easy way out, need to realloc, grow to double size (or current max, if to small).
+                       max_entry_count = entry_count * 2 > max_entry_count ? entry_count * 2 : max_entry_count;
+                       new_rt_funcs = g_new0 (RUNTIME_FUNCTION, max_entry_count);
+
+                       if (new_rt_funcs != NULL) {
+                               gsize from_index = 0;
+                               gsize to_index = 0;
+
+                               // Copy from old table into new table. Make sure new rt func gets inserted
+                               // into correct location based on sort order.
+                               for (; from_index < entry_count; ++from_index) {
+                                       if (new_rt_func == NULL && current_rt_funcs [from_index].BeginAddress > new_rt_func_data.BeginAddress) {
+                                               new_rt_func = &(new_rt_funcs [to_index++]);
+                                               *new_rt_func = new_rt_func_data;
+                                       }
+
+                                       if (current_rt_funcs [from_index].UnwindData != 0)
+                                               new_rt_funcs[to_index++] = current_rt_funcs [from_index];
+                               }
+
+                               // If we didn't insert by now, put it last in the list.
+                               if (new_rt_func == NULL) {
+                                       new_rt_func = &(new_rt_funcs [to_index]);
+                                       *new_rt_func = new_rt_func_data;
+                               }
+                       }
+
+                       entry_count++;
+               }
+
+               // Update the stats for current entry.
+               found_entry->rt_funcs_current_count = entry_count;
+               found_entry->rt_funcs_max_count = max_entry_count;
+
+               if (new_rt_funcs == NULL && g_rtl_grow_function_table != NULL) {
+                       // No new table just report increase in use.
+                       g_assert_checked (found_entry->handle != NULL);
+                       g_rtl_grow_function_table (found_entry->handle, found_entry->rt_funcs_current_count);
+               } else if (new_rt_funcs != NULL && g_rtl_add_growable_function_table != NULL) {
+                       // New table, delete old table and rt funcs, and register a new one.
+                       g_assert_checked (g_rtl_delete_growable_function_table != NULL);
+                       g_rtl_delete_growable_function_table (found_entry->handle);
+                       found_entry->handle = NULL;
+                       g_free (found_entry->rt_funcs);
+                       found_entry->rt_funcs = new_rt_funcs;
+                       DWORD result = g_rtl_add_growable_function_table (&found_entry->handle,
+                                                               found_entry->rt_funcs, found_entry->rt_funcs_current_count,
+                                                               found_entry->rt_funcs_max_count, found_entry->begin_range, found_entry->end_range);
+                       g_assert (!result);
+               } else if (new_rt_funcs != NULL && g_rtl_add_growable_function_table == NULL) {
+                       // No table registered with OS, callback solution in use. Switch tables.
+                       g_free (found_entry->rt_funcs);
+                       found_entry->rt_funcs = new_rt_funcs;
+               } else if (new_rt_funcs == NULL && g_rtl_grow_function_table == NULL) {
+                       // No table registered with OS, callback solution in use, nothing to do.
+               } else {
+                       g_assert_not_reached ();
+               }
+
+               // Only included in checked builds. Validates the structure of table after insert.
+               mono_arch_unwindinfo_validate_rt_funcs_in_table_no_lock (found_entry);
+
+               ReleaseSRWLockExclusive (&found_entry->lock);
+       }
+
+       ReleaseSRWLockShared (&g_dynamic_function_table_lock);
+
+       return new_rt_func;
 }
 
 static PRUNTIME_FUNCTION
 MONO_GET_RUNTIME_FUNCTION_CALLBACK ( DWORD64 ControlPc, IN PVOID Context )
 {
-       MonoJitInfo *ji;
-       guint64 pos;
-       PMonoUnwindInfo targetinfo;
-       MonoDomain *domain = mono_domain_get ();
+       return mono_arch_unwindinfo_find_pc_rt_func_in_table ((gpointer)ControlPc);
+}
+
+static void
+mono_arch_unwindinfo_initialize_unwind_info_internal_ex (GSList *unwind_ops, PUNWIND_INFO unwindinfo)
+{
+       if (unwind_ops != NULL && unwindinfo != NULL) {
+               MonoUnwindOp *unwind_op_data;
+               gboolean sp_alloced = FALSE;
+               gboolean fp_alloced = FALSE;
+
+               // Replay collected unwind info and setup Windows format.
+               for (GSList *l = unwind_ops; l; l = l->next) {
+                       unwind_op_data = (MonoUnwindOp *)l->data;
+                       switch (unwind_op_data->op) {
+                               case DW_CFA_offset : {
+                                       // Pushes should go before SP/FP allocation to be compliant with Windows x64 ABI.
+                                       // TODO: DW_CFA_offset can also be used to move saved regs into frame.
+                                       if (unwind_op_data->reg != AMD64_RIP && sp_alloced == FALSE && fp_alloced == FALSE)
+                                               mono_arch_unwindinfo_add_push_nonvol (unwindinfo, unwind_op_data);
+                                       break;
+                               }
+                               case DW_CFA_mono_sp_alloc_info_win64 : {
+                                       mono_arch_unwindinfo_add_alloc_stack (unwindinfo, unwind_op_data);
+                                       sp_alloced = TRUE;
+                                       break;
+                               }
+                               case DW_CFA_mono_fp_alloc_info_win64 : {
+                                       mono_arch_unwindinfo_add_set_fpreg (unwindinfo, unwind_op_data);
+                                       fp_alloced = TRUE;
+                                       break;
+                               }
+                               default :
+                                       break;
+                       }
+               }
+       }
+}
 
-       ji = mini_jit_info_table_find (domain, (char*)ControlPc, NULL);
-       if (!ji)
-               return 0;
+static PUNWIND_INFO
+mono_arch_unwindinfo_initialize_unwind_info_internal (GSList *unwind_ops)
+{
+       PUNWIND_INFO unwindinfo;
 
-       pos = (guint64)(((char*)ji->code_start) + ji->code_size);
-       
-       targetinfo = (PMonoUnwindInfo)ALIGN_TO (pos, 8);
+       mono_arch_unwindinfo_create (&unwindinfo);
+       mono_arch_unwindinfo_initialize_unwind_info_internal_ex (unwind_ops, unwindinfo);
+
+       return unwindinfo;
+}
 
-       targetinfo->runtimeFunction.BeginAddress = ((DWORD64)ji->code_start) - ((DWORD64)Context);
-       targetinfo->runtimeFunction.EndAddress = pos - ((DWORD64)Context);
-       targetinfo->runtimeFunction.UnwindData = ((DWORD64)&targetinfo->unwindInfo) - ((DWORD64)Context);
+guchar
+mono_arch_unwindinfo_get_code_count (GSList *unwind_ops)
+{
+       UNWIND_INFO unwindinfo = {0};
+       mono_arch_unwindinfo_initialize_unwind_info_internal_ex (unwind_ops, &unwindinfo);
+       return unwindinfo.CountOfCodes;
+}
 
-       return &targetinfo->runtimeFunction;
+guint
+mono_arch_unwindinfo_init_method_unwind_info (gpointer cfg)
+{
+       MonoCompile * current_cfg = (MonoCompile *)cfg;
+       g_assert (current_cfg->arch.unwindinfo == NULL);
+       current_cfg->arch.unwindinfo = mono_arch_unwindinfo_initialize_unwind_info_internal (current_cfg->unwind_ops);
+       return mono_arch_unwindinfo_get_size (((PUNWIND_INFO)(current_cfg->arch.unwindinfo))->CountOfCodes);
 }
 
 void
-mono_arch_unwindinfo_install_unwind_info (gpointer* monoui, gpointer code, guint code_size)
+mono_arch_unwindinfo_install_method_unwind_info (gpointer *monoui, gpointer code, guint code_size)
 {
-       PMonoUnwindInfo unwindinfo, targetinfo;
+       PUNWIND_INFO unwindinfo, targetinfo;
        guchar codecount;
        guint64 targetlocation;
        if (!*monoui)
                return;
 
-       unwindinfo = (MonoUnwindInfo*)*monoui;
+       unwindinfo = (PUNWIND_INFO)*monoui;
        targetlocation = (guint64)&(((guchar*)code)[code_size]);
-       targetinfo = (PMonoUnwindInfo) ALIGN_TO(targetlocation, 8);
+       targetinfo = (PUNWIND_INFO) ALIGN_TO(targetlocation, sizeof (mgreg_t));
 
-       unwindinfo->runtimeFunction.EndAddress = code_size;
-       unwindinfo->runtimeFunction.UnwindData = ((guchar*)&targetinfo->unwindInfo) - ((guchar*)code);
-       
-       memcpy (targetinfo, unwindinfo, sizeof (MonoUnwindInfo) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES));
-       
-       codecount = unwindinfo->unwindInfo.CountOfCodes;
+       memcpy (targetinfo, unwindinfo, sizeof (UNWIND_INFO) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES));
+
+       codecount = unwindinfo->CountOfCodes;
+       if (codecount) {
+               memcpy (&targetinfo->UnwindCode [0], &unwindinfo->UnwindCode [MONO_MAX_UNWIND_CODES - codecount],
+                       sizeof (UNWIND_CODE) * codecount);
+       }
+
+#ifdef ENABLE_CHECKED_BUILD_UNWINDINFO
        if (codecount) {
-               memcpy (&targetinfo->unwindInfo.UnwindCode[0], &unwindinfo->unwindInfo.UnwindCode[MONO_MAX_UNWIND_CODES-codecount], 
-                       sizeof (UNWIND_CODE) * unwindinfo->unwindInfo.CountOfCodes);
+               // Validate the order of unwind op codes in checked builds. Offset should be in descending order.
+               // In first iteration previous == current, this is intended to handle UWOP_ALLOC_LARGE as first item.
+               int previous = 0;
+               for (int current = 0; current < codecount; current++) {
+                       g_assert_checked (targetinfo->UnwindCode [previous].CodeOffset >= targetinfo->UnwindCode [current].CodeOffset);
+                       previous = current;
+                       if (targetinfo->UnwindCode [current].UnwindOp == UWOP_ALLOC_LARGE) {
+                               if (targetinfo->UnwindCode [current].OpInfo == 0) {
+                                       current++;
+                               } else {
+                                       current += 2;
+                               }
+                       }
+               }
        }
+#endif /* ENABLE_CHECKED_BUILD_UNWINDINFO */
 
        g_free (unwindinfo);
        *monoui = 0;
+
+       // Register unwind info in table.
+       mono_arch_unwindinfo_insert_rt_func_in_table (code, code_size);
+}
+
+void
+mono_arch_unwindinfo_install_tramp_unwind_info (GSList *unwind_ops, gpointer code, guint code_size)
+{
+       PUNWIND_INFO unwindinfo = mono_arch_unwindinfo_initialize_unwind_info_internal (unwind_ops);
+       if (unwindinfo != NULL) {
+               mono_arch_unwindinfo_install_method_unwind_info (&unwindinfo, code, code_size);
+       }
 }
 
 void
 mono_arch_code_chunk_new (void *chunk, int size)
 {
-       BOOLEAN success = RtlInstallFunctionTableCallback (((DWORD64)chunk) | 0x3, (DWORD64)chunk, size, MONO_GET_RUNTIME_FUNCTION_CALLBACK, chunk, NULL);
-       g_assert (success);
+       mono_arch_unwindinfo_insert_range_in_table (chunk, size);
 }
 
 void mono_arch_code_chunk_destroy (void *chunk)
 {
-       BOOLEAN success = RtlDeleteFunctionTable ((PRUNTIME_FUNCTION)((DWORD64)chunk | 0x03));
-       g_assert (success);
+       mono_arch_unwindinfo_remove_pc_range_in_table (chunk);
 }
-
-#endif /* defined(TARGET_WIN32) !defined(DISABLE_JIT) */
+#endif /* MONO_ARCH_HAVE_UNWIND_TABLE */
 
 #if MONO_SUPPORT_TASKLETS && !defined(DISABLE_JIT)
 MonoContinuationRestore
index 1269d5d5c7c122af0ba55d290199ef934422fa52..62a3c0863bfcd824affa9e74e63b457ce7406726 100644 (file)
@@ -18,6 +18,7 @@
 #include "mini.h"
 #include <string.h>
 #include <math.h>
+#include <assert.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -4540,10 +4541,16 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                /* Copy arguments on the stack to our argument area */
                                for (i = 0; i < call->stack_usage; i += sizeof(mgreg_t)) {
                                        amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, i, sizeof(mgreg_t));
-                                       amd64_mov_membase_reg (code, AMD64_RBP, 16 + i, AMD64_RAX, sizeof(mgreg_t));
+                                       amd64_mov_membase_reg (code, AMD64_RBP, ARGS_OFFSET + i, AMD64_RAX, sizeof(mgreg_t));
                                }
 
+#ifdef TARGET_WIN32
+                               amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+                               amd64_pop_reg (code, AMD64_RBP);
+                               mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#else
                                amd64_leave (code);
+#endif
                        }
 
                        offset = code - cfg->native_code;
@@ -6476,6 +6483,16 @@ mono_arch_register_lowlevel_calls (void)
 {
        /* The signature doesn't matter */
        mono_register_jit_icall (mono_amd64_throw_exception, "mono_amd64_throw_exception", mono_create_icall_signature ("void"), TRUE);
+
+#if defined(TARGET_WIN32) || defined(HOST_WIN32)
+#if _MSC_VER
+       extern void __chkstk(void);
+       mono_register_jit_icall_full (__chkstk, "mono_chkstk_win64", NULL, TRUE, FALSE, "__chkstk");
+#else
+       extern void ___chkstk_ms(void);
+       mono_register_jit_icall_full (___chkstk_ms, "mono_chkstk_win64", NULL, TRUE, FALSE, "___chkstk_ms");
+#endif
+#endif
 }
 
 void
@@ -6543,6 +6560,41 @@ get_max_epilog_size (MonoCompile *cfg)
     } \
 } while (0)
 
+#ifdef TARGET_WIN32
+guint8 *
+mono_arch_emit_prolog_setup_sp_win64(MonoCompile *cfg, guint8 *code, int alloc_size, int *cfa_offset_input)
+{
+       int cfa_offset = *cfa_offset_input;
+
+       /* Allocate windows stack frame using stack probing method */
+       if (alloc_size) {
+
+               if (alloc_size >= 0x1000) {
+                       amd64_mov_reg_imm (code, AMD64_RAX, alloc_size);
+                       code = emit_call_body (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_chkstk_win64");
+               }
+
+               amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
+               if (cfg->arch.omit_fp) {
+                       cfa_offset += alloc_size;
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+                       async_exc_point (code);
+               }
+
+               // NOTE, in a standard win64 prolog the alloc unwind info is always emitted, but since mono
+               // uses a frame pointer with negative offsets and a standard win64 prolog assumes positive offsets, we can't
+               // emit sp alloc unwind metadata since the native OS unwinder will incorrectly restore sp. Excluding the alloc
+               // metadata on the other hand won't give the OS the information so it can just restore the frame pointer to sp and
+               // that will retrieve the expected results.
+               if (cfg->arch.omit_fp)
+                       mono_emit_unwind_op_sp_alloc (cfg, code, alloc_size);
+       }
+
+       *cfa_offset_input = cfa_offset;
+       return code;
+}
+#endif /* TARGET_WIN32 */
+
 guint8 *
 mono_arch_emit_prolog (MonoCompile *cfg)
 {
@@ -6573,8 +6625,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        /* 
         * The prolog consists of the following parts:
         * FP present:
-        * - push rbp, mov rbp, rsp
-        * - save callee saved regs using pushes
+        * - push rbp
+        * - mov rbp, rsp
+        * - save callee saved regs using moves
         * - allocate frame
         * - save rgctx if needed
         * - save lmf if needed
@@ -6599,18 +6652,13 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                mono_emit_unwind_op_offset (cfg, code, AMD64_RBP, - cfa_offset);
                async_exc_point (code);
-#ifdef TARGET_WIN32
-               mono_arch_unwindinfo_add_push_nonvol (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
-#endif
                /* These are handled automatically by the stack marking code */
                mini_gc_set_slot_type_from_cfa (cfg, -cfa_offset, SLOT_NOREF);
-               
+
                amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
                mono_emit_unwind_op_def_cfa_reg (cfg, code, AMD64_RBP);
+               mono_emit_unwind_op_fp_alloc (cfg, code, AMD64_RBP, 0);
                async_exc_point (code);
-#ifdef TARGET_WIN32
-               mono_arch_unwindinfo_add_set_fpreg (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
-#endif
        }
 
        /* The param area is always at offset 0 from sp */
@@ -6647,9 +6695,12 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        cfg->arch.stack_alloc_size = alloc_size;
 
        /* Allocate stack frame */
+#ifdef TARGET_WIN32
+       code = mono_arch_emit_prolog_setup_sp_win64(cfg, code, alloc_size, &cfa_offset);
+#else
        if (alloc_size) {
                /* See mono_emit_stack_alloc */
-#if defined(TARGET_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
+#if defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
                guint32 remaining_size = alloc_size;
                /*FIXME handle unbounded code expansion, we should use a loop in case of more than X interactions*/
                guint32 required_code_size = ((remaining_size / 0x1000) + 1) * 11; /*11 is the max size of amd64_alu_reg_imm + amd64_test_membase_reg*/
@@ -6669,10 +6720,6 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                        }
                        async_exc_point (code);
-#ifdef TARGET_WIN32
-                       if (cfg->arch.omit_fp) 
-                               mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, 0x1000);
-#endif
 
                        amd64_test_membase_reg (code, AMD64_RSP, 0, AMD64_RSP);
                        remaining_size -= 0x1000;
@@ -6684,10 +6731,6 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
                                async_exc_point (code);
                        }
-#ifdef TARGET_WIN32
-                       if (cfg->arch.omit_fp) 
-                               mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, remaining_size);
-#endif
                }
 #else
                amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
@@ -6698,6 +6741,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                }
 #endif
        }
+#endif
 
        /* Stack alignment check */
 #if 0
@@ -7138,8 +7182,14 @@ mono_arch_emit_epilog (MonoCompile *cfg)
                        amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, cfg->arch.stack_alloc_size);
                }
        } else {
+#ifdef TARGET_WIN32
+               amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+               amd64_pop_reg (code, AMD64_RBP);
+               mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#else
                amd64_leave (code);
                mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#endif
        }
        mono_emit_unwind_op_def_cfa (cfg, code, AMD64_RSP, 8);
        async_exc_point (code);
@@ -7600,7 +7650,7 @@ get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, guint32 par
        unwind_ops = mono_arch_get_cie_program ();
 
        if (has_target) {
-               start = code = (guint8 *)mono_global_codeman_reserve (64);
+               start = code = (guint8 *)mono_global_codeman_reserve (64 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
                /* Replace the this argument with the target */
                amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
@@ -7608,8 +7658,9 @@ get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, guint32 par
                amd64_jump_membase (code, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
 
                g_assert ((code - start) < 64);
+               g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
        } else {
-               start = code = (guint8 *)mono_global_codeman_reserve (64);
+               start = code = (guint8 *)mono_global_codeman_reserve (64 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
                if (param_count == 0) {
                        amd64_jump_membase (code, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
@@ -7630,6 +7681,7 @@ get_delegate_invoke_impl (MonoTrampInfo **info, gboolean has_target, guint32 par
                        amd64_jump_membase (code, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
                }
                g_assert ((code - start) < 64);
+               g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
        }
 
        mono_arch_flush_icache (start, code - start);
@@ -7670,7 +7722,7 @@ get_delegate_virtual_invoke_impl (MonoTrampInfo **info, gboolean load_imt_reg, i
        if (offset / (int)sizeof (gpointer) > MAX_VIRTUAL_DELEGATE_OFFSET)
                return NULL;
 
-       start = code = (guint8 *)mono_global_codeman_reserve (size);
+       start = code = (guint8 *)mono_global_codeman_reserve (size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
@@ -7891,9 +7943,9 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTC
                size += item->chunk_size;
        }
        if (fail_tramp)
-               code = (guint8 *)mono_method_alloc_generic_virtual_trampoline (domain, size);
+               code = (guint8 *)mono_method_alloc_generic_virtual_trampoline (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
        else
-               code = (guint8 *)mono_domain_code_reserve (domain, size);
+               code = (guint8 *)mono_domain_code_reserve (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
        start = code;
 
        unwind_ops = mono_arch_get_cie_program ();
@@ -7984,6 +8036,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTC
        if (!fail_tramp)
                mono_stats.imt_trampolines_size += code - start;
        g_assert (code - start <= size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL);
 
index 134b977c616eb7eb0d4fce628d0a55c49ad1997f..bc4614444a8226953b84bb6635d10eb3e9c3d00e 100644 (file)
@@ -47,6 +47,36 @@ void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler);
 
 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep);
 
+typedef struct {
+       SRWLOCK lock;
+       PVOID handle;
+       gsize begin_range;
+       gsize end_range;
+       PRUNTIME_FUNCTION rt_funcs;
+       DWORD rt_funcs_current_count;
+       DWORD rt_funcs_max_count;
+} DynamicFunctionTableEntry;
+
+#define MONO_UNWIND_INFO_RT_FUNC_SIZE 128
+
+// On Win8/Win2012Server and later we can use dynamic growable function tables
+// instead of RtlInstallFunctionTableCallback. This gives us the benefit to
+// include all needed unwind upon registration.
+typedef DWORD (NTAPI* RtlAddGrowableFunctionTablePtr)(
+    _Out_ PVOID * DynamicTable,
+    _In_reads_(MaximumEntryCount) PRUNTIME_FUNCTION FunctionTable,
+    _In_ DWORD EntryCount,
+    _In_ DWORD MaximumEntryCount,
+    _In_ ULONG_PTR RangeBase,
+    _In_ ULONG_PTR RangeEnd);
+
+typedef VOID (NTAPI* RtlGrowFunctionTablePtr)(
+    _Inout_ PVOID DynamicTable,
+    _In_ DWORD NewEntryCount);
+
+typedef VOID (NTAPI* RtlDeleteGrowableFunctionTablePtr)(
+    _In_ PVOID DynamicTable);
+
 #endif /* HOST_WIN32 */
 
 #ifdef sun    // Solaris x86
@@ -178,7 +208,7 @@ typedef struct MonoCompileArch {
        gint32 async_point_count;
        gpointer vret_addr_loc;
 #ifdef HOST_WIN32
-       gpointer        unwindinfo;
+       gpointer unwindinfo;
 #endif
        gpointer seq_point_info_var;
        gpointer ss_trigger_page_var;
@@ -478,17 +508,105 @@ mono_amd64_handler_block_trampoline_helper (void);
 
 #if defined(TARGET_WIN32) && !defined(DISABLE_JIT)
 
-void mono_arch_unwindinfo_add_push_nonvol (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg );
-void mono_arch_unwindinfo_add_set_fpreg (gpointer* monoui, gpointer codebegin, gpointer nextip, guchar reg );
-void mono_arch_unwindinfo_add_alloc_stack (gpointer* monoui, gpointer codebegin, gpointer nextip, guint size );
-guint mono_arch_unwindinfo_get_size (gpointer monoui);
-void mono_arch_unwindinfo_install_unwind_info (gpointer* monoui, gpointer code, guint code_size);
-
+#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
 #define MONO_ARCH_HAVE_UNWIND_TABLE 1
+#define MONO_ARCH_HAVE_CODE_CHUNK_TRACKING 1
+
+#ifdef ENABLE_CHECKED_BUILD
+#define ENABLE_CHECKED_BUILD_UNWINDINFO
+#endif
+
+#define MONO_MAX_UNWIND_CODES 22
+
+typedef enum _UNWIND_OP_CODES {
+    UWOP_PUSH_NONVOL = 0, /* info == register number */
+    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
+    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
+    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
+    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
+    UWOP_SAVE_XMM128,     /* info == XMM reg number, offset in next slot */
+    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
+    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
+} UNWIND_CODE_OPS;
+
+typedef union _UNWIND_CODE {
+    struct {
+        guchar CodeOffset;
+        guchar UnwindOp : 4;
+        guchar OpInfo   : 4;
+    };
+    gushort FrameOffset;
+} UNWIND_CODE, *PUNWIND_CODE;
+
+typedef struct _UNWIND_INFO {
+       guchar Version       : 3;
+       guchar Flags         : 5;
+       guchar SizeOfProlog;
+       guchar CountOfCodes;
+       guchar FrameRegister : 4;
+       guchar FrameOffset   : 4;
+       UNWIND_CODE UnwindCode[MONO_MAX_UNWIND_CODES];
+/*     UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
+ *     union {
+ *             OPTIONAL ULONG ExceptionHandler;
+ *             OPTIONAL ULONG FunctionEntry;
+ *     };
+ *     OPTIONAL ULONG ExceptionData[]; */
+} UNWIND_INFO, *PUNWIND_INFO;
+
+//typedef struct
+//{
+//     UNWIND_INFO unwindInfo;
+//} MonoUnwindInfo, *PMonoUnwindInfo;
+
+inline guint
+mono_arch_unwindinfo_get_size (guchar code_count)
+{
+       // Returned size will be used as the allocated size for unwind data trailing the memory used by compiled method.
+       // Windows x64 ABI have some requirements on the data written into this memory. Both the RUNTIME_FUNCTION
+       // and UNWIND_INFO struct needs to be DWORD aligned and the number of elements in unwind codes array
+       // should have an even number of entries, while the count stored in UNWIND_INFO struct should hold the real number
+       // of unwind codes. Adding extra bytes to the total size will make sure we can properly align the RUNTIME_FUNCTION
+       // struct. Since our UNWIND_INFO follows RUNTIME_FUNCTION struct in memory, it will automatically be DWORD aligned
+       // as well. Also make sure to allocate room for a padding UNWIND_CODE, if needed.
+       return (sizeof (mgreg_t) + sizeof (UNWIND_INFO)) -
+               (sizeof (UNWIND_CODE) * ((MONO_MAX_UNWIND_CODES - ((code_count + 1) & ~1))));
+}
+
+guchar mono_arch_unwindinfo_get_code_count (GSList *unwind_ops);
+guint mono_arch_unwindinfo_init_method_unwind_info (gpointer cfg);
+void mono_arch_unwindinfo_install_method_unwind_info (gpointer *monoui, gpointer code, guint code_size);
+void mono_arch_unwindinfo_install_tramp_unwind_info (GSList *unwind_ops, gpointer code, guint code_size);
 
 void mono_arch_code_chunk_new (void *chunk, int size);
 void mono_arch_code_chunk_destroy (void *chunk);
-#define MONO_ARCH_HAVE_CODE_CHUNK_TRACKING 1
+
+#endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
+#endif /* defined(TARGET_WIN32) && !defined(DISABLE_JIT) */
+
+#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
+// Allocate additional size for max 3 unwind ops (push + fp or sp small|large) + unwind info struct trailing code buffer.
+#define MONO_TRAMPOLINE_UNWINDINFO_SIZE(max_code_count) (mono_arch_unwindinfo_get_size (max_code_count))
+#define MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE (MONO_TRAMPOLINE_UNWINDINFO_SIZE(3))
+
+inline gboolean
+mono_arch_unwindinfo_validate_size (GSList *unwind_ops, guint max_size)
+{
+       guint current_size = mono_arch_unwindinfo_get_size (mono_arch_unwindinfo_get_code_count (unwind_ops));
+       return current_size <= max_size;
+}
+
+#else
+
+#define MONO_TRAMPOLINE_UNWINDINFO_SIZE(max_code_count) 0
+#define MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE 0
+
+inline gboolean
+mono_arch_unwindinfo_validate_unwindinfo_size (GSList *unwind_ops, guint max_size)
+{
+       return TRUE;
+}
 #endif
 
 CallInfo* mono_arch_get_call_info (MonoMemPool *mp, MonoMethodSignature *sig);
index 2ebf1c7873ab04004be1be89d89ce522209c00fe..22d9a5cd227a889fe2192431f7357b12f972e822 100644 (file)
@@ -505,6 +505,10 @@ mono_tramp_info_register (MonoTrampInfo *info, MonoDomain *domain)
        mono_save_trampoline_xdebug_info (info);
        mono_lldb_save_trampoline_info (info);
 
+#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
+       mono_arch_unwindinfo_install_tramp_unwind_info (info->unwind_ops, info->code, info->code_size);
+#endif
+
        /* Only register trampolines that have unwind infos */
        if (mono_get_root_domain () && copy->uw_info)
                register_trampoline_jit_info (domain, copy);
index 3d14e9cde783e8bf6f3ae6e51a068a0f789f9c03..41100fa3b35be60f47127f19ff3bff3792956bdf 100644 (file)
  */
 #define DW_CFA_mono_advance_loc DW_CFA_lo_user
 
+/*
+ * Mono extension, Windows x64 unwind ABI needs some more details around sp alloc size and fp offset.
+ */
+#if defined(TARGET_WIN32) && defined(TARGET_AMD64)
+#define DW_CFA_mono_sp_alloc_info_win64 (DW_CFA_lo_user + 1)
+#define DW_CFA_mono_fp_alloc_info_win64 (DW_CFA_lo_user + 2)
+#endif
+
 /* Represents one unwind instruction */
 typedef struct {
        guint8 op; /* One of DW_CFA_... */
@@ -107,6 +115,14 @@ typedef struct {
  */
 #define mono_emit_unwind_op_mark_loc(cfg,ip,n) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_advance_loc, 0, (n))
 
+#if defined(TARGET_WIN32) && defined(TARGET_AMD64)
+#define mono_emit_unwind_op_sp_alloc(cfg,ip,size) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_sp_alloc_info_win64, 0, (size))
+#define mono_emit_unwind_op_fp_alloc(cfg,ip,reg,size) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_fp_alloc_info_win64, (reg), (size))
+#else
+#define mono_emit_unwind_op_sp_alloc(cfg,ip,size)
+#define mono_emit_unwind_op_fp_alloc(cfg,ip,reg,size)
+#endif
+
 /* Similar macros usable when a cfg is not available, like for trampolines */
 #define mono_add_unwind_op_def_cfa(op_list,code,buf,reg,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa, (reg), (offset))); } while (0)
 #define mono_add_unwind_op_def_cfa_reg(op_list,code,buf,reg) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa_register, (reg), (0))); } while (0)
@@ -114,6 +130,14 @@ typedef struct {
 #define mono_add_unwind_op_same_value(op_list,code,buf,reg) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_same_value, (reg), 0)); } while (0)
 #define mono_add_unwind_op_offset(op_list,code,buf,reg,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_offset, (reg), (offset))); } while (0)
 
+#if defined(TARGET_WIN32) && defined(TARGET_AMD64)
+#define mono_add_unwind_op_sp_alloc(op_list,code,buf,size) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_mono_sp_alloc_info_win64, 0, (size))); } while (0)
+#define mono_add_unwind_op_fp_alloc(op_list,code,buf,reg,size) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_mono_fp_alloc_info_win64, (reg), (size))); } while (0)
+#else
+#define mono_add_unwind_op_sp_alloc(op_list,code,buf,size)
+#define mono_add_unwind_op_fp_alloc(op_list,code,buf,reg,size)
+#endif
+
 #define mono_free_unwind_info(op_list) do { GSList *l; for (l = op_list; l; l = l->next) g_free (l->data); g_slist_free (op_list); op_list = NULL; } while (0)
 
 /* Pointer Encoding in the .eh_frame */
diff --git a/mono/mini/mini-windows-dlldac.c b/mono/mini/mini-windows-dlldac.c
new file mode 100644 (file)
index 0000000..1654fc6
--- /dev/null
@@ -0,0 +1,81 @@
+#include <config.h>
+
+#ifdef HOST_WIN32
+#include <winsock2.h>
+#include <windows.h>
+#include <winnt.h>
+
+#if defined(TARGET_AMD64) && !defined(DISABLE_JIT)
+#include "mono/mini/mini.h"
+#include "mono/mini/mini-amd64.h"
+#include "mono/utils/mono-publib.h"
+
+typedef enum _FUNCTION_TABLE_TYPE {
+    RF_SORTED,
+    RF_UNSORTED,
+    RF_CALLBACK
+} FUNCTION_TABLE_TYPE;
+
+typedef struct _DYNAMIC_FUNCTION_TABLE {
+    LIST_ENTRY Links;
+    PRUNTIME_FUNCTION FunctionTable;
+    LARGE_INTEGER TimeStamp;
+    ULONG64 MinimumAddress;
+    ULONG64 MaximumAddress;
+    ULONG64 BaseAddress;
+    PGET_RUNTIME_FUNCTION_CALLBACK Callback;
+    PVOID Context;
+    PWSTR OutOfProcessCallbackDll;
+    FUNCTION_TABLE_TYPE Type;
+    ULONG EntryCount;
+} DYNAMIC_FUNCTION_TABLE, *PDYNAMIC_FUNCTION_TABLE;
+
+typedef BOOL (ReadMemoryFunction)(PVOID user_context, LPCVOID base_address, PVOID buffer, SIZE_T size, SIZE_T *read);
+BOOL read_memory(PVOID user_context, LPCVOID base_address, PVOID buffer, SIZE_T size, SIZE_T* read)
+{
+    return ReadProcessMemory((HANDLE)user_context, base_address, buffer, size, read);
+}
+
+MONO_API_EXPORT DWORD OutOfProcessFunctionTableCallbackEx (ReadMemoryFunction read_memory, PVOID user_context, PVOID table_address, PDWORD entries, PRUNTIME_FUNCTION *functions)
+{
+       DYNAMIC_FUNCTION_TABLE func_table = { 0 };
+       DynamicFunctionTableEntry func_table_entry = { 0 };
+       PRUNTIME_FUNCTION rt_funcs = NULL;
+       size_t reads = 0;
+       DWORD result = 0xC0000001;
+
+       if (read_memory (user_context, table_address, &func_table, sizeof (func_table), &reads)) {
+               if (func_table.Context != NULL && read_memory (user_context, func_table.Context, &func_table_entry, sizeof (func_table_entry), &reads)) {
+                       if (func_table_entry.rt_funcs_current_count != 0) {
+                               rt_funcs = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, func_table_entry.rt_funcs_current_count * sizeof (RUNTIME_FUNCTION));
+                               if (rt_funcs) {
+                                       if (read_memory (user_context, func_table_entry.rt_funcs, rt_funcs, func_table_entry.rt_funcs_current_count * sizeof (RUNTIME_FUNCTION), &reads)) {
+                                               *entries = func_table_entry.rt_funcs_current_count;
+                                               *functions = rt_funcs;
+                                               result = 0x00000000;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return result;
+}
+
+MONO_API_EXPORT DWORD OutOfProcessFunctionTableCallback (HANDLE process, PVOID table_address, PDWORD entries, PRUNTIME_FUNCTION *functions)
+{
+       return OutOfProcessFunctionTableCallbackEx (&read_memory, process, table_address, entries, functions);
+}
+#endif /* defined(TARGET_AMD64) && !defined(DISABLE_JIT) */
+
+#ifdef _MSC_VER
+BOOL APIENTRY DllMain (HMODULE module_handle, DWORD reason, LPVOID reserved)
+{
+       return TRUE;
+}
+#endif
+
+#else
+
+MONO_EMPTY_SOURCE_FILE (mini_windows_dlldac);
+#endif /* HOST_WIN32 */
index 807cd5dd470cf7a2c230ede0b523020e1131d380..21dd9c8f7ea21323fda7a86424318f7037c027b6 100644 (file)
@@ -2273,7 +2273,7 @@ mono_codegen (MonoCompile *cfg)
        /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */
 
 #ifdef MONO_ARCH_HAVE_UNWIND_TABLE
-       unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo);
+       unwindlen = mono_arch_unwindinfo_init_method_unwind_info (cfg);
 #endif
 
        if (cfg->method->dynamic) {
@@ -2394,7 +2394,7 @@ mono_codegen (MonoCompile *cfg)
        mono_debug_close_method (cfg);
 
 #ifdef MONO_ARCH_HAVE_UNWIND_TABLE
-       mono_arch_unwindinfo_install_unwind_info (&cfg->arch.unwindinfo, cfg->native_code, cfg->code_len);
+       mono_arch_unwindinfo_install_method_unwind_info (&cfg->arch.unwindinfo, cfg->native_code, cfg->code_len);
 #endif
 }
 
index 0c986f14e7b8b7655ad2f16abd636e19197b0963..6497094328f27edbc433ca6d198a116c81f5e775 100644 (file)
@@ -162,7 +162,7 @@ mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
        int reg_area_size;
 
        buf_len = 2048;
-       buf = code = mono_global_codeman_reserve (buf_len);
+       buf = code = mono_global_codeman_reserve (buf_len + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
 
        /*
         * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
@@ -208,6 +208,7 @@ mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
 
        /* unwind markers 3/3 */
        mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
+       mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
 
        /* setup the frame */
        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
@@ -444,10 +445,17 @@ mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
                mono_amd64_patch (br_ret [i], code);
 
        /* Exit code path */
+#if TARGET_WIN32
+       amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+       amd64_pop_reg (code, AMD64_RBP);
+       mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
+#else
        amd64_leave (code);
+#endif
        amd64_ret (code);
 
        g_assert ((code - buf) < buf_len);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
 
        if (info)
                *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
index b143645fb7d83c9d70d04ac4e9f6d2f9d81b9ded..0a93c0e69e69b6300abab3ab163abbc8cb3c5e02 100644 (file)
@@ -4,6 +4,7 @@
  * Authors:
  *   Dietmar Maurer (dietmar@ximian.com)
  *   Zoltan Varga (vargaz@gmail.com)
+ *   Johan Lorensson (lateralusx.github@gmail.com)
  *
  * (C) 2001 Ximian, Inc.
  * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
@@ -54,7 +55,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
 
        this_reg = mono_arch_get_this_arg_reg (NULL);
 
-       start = code = (guint8 *)mono_domain_code_reserve (domain, size);
+       start = code = (guint8 *)mono_domain_code_reserve (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
@@ -63,6 +64,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
        amd64_mov_reg_imm (code, AMD64_RAX, addr);
        amd64_jump_reg (code, AMD64_RAX);
        g_assert ((code - start) < size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        mono_arch_flush_icache (start, code - start);
        mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m);
@@ -96,13 +98,14 @@ mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericCo
                buf_len = 30;
 #endif
 
-       start = code = (guint8 *)mono_domain_code_reserve (domain, buf_len);
+       start = code = (guint8 *)mono_domain_code_reserve (domain, buf_len + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
        amd64_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, mrgctx);
        amd64_jump_code (code, addr);
        g_assert ((code - start) < buf_len);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        mono_arch_flush_icache (start, code - start);
        mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
@@ -237,7 +240,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf
        else
                has_caller = TRUE;
 
-       code = buf = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
+       code = buf = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
 
        /* Compute stack frame size and offsets */
        offset = 0;
@@ -306,6 +309,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf
        orig_rsp_to_rbp_offset -= sizeof(mgreg_t);
        amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
        mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
+       mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
 
        /* Compute the trampoline address from the return address */
@@ -505,7 +509,13 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf
         * We have an exception we want to throw in the caller's frame, so pop
         * the trampoline frame and throw from the caller.
         */
+#if TARGET_WIN32
+       amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+       amd64_pop_reg (code, AMD64_RBP);
+       mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
+#else
        amd64_leave (code);
+#endif
        /* We are in the parent frame, the exception is in rax */
        /*
         * EH is initialized after trampolines, so get the address of the variable
@@ -537,7 +547,13 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf
                amd64_movsd_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * sizeof(mgreg_t)));
 
        /* Restore stack */
+#if TARGET_WIN32
+       amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+       amd64_pop_reg (code, AMD64_RBP);
+       mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
+#else
        amd64_leave (code);
+#endif
        cfa_offset -= sizeof (mgreg_t);
        mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
 
@@ -551,6 +567,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf
        }
 
        g_assert ((code - buf) <= kMaxCodeSize);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
 
        mono_arch_flush_icache (buf, code - buf);
        mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
@@ -642,7 +659,7 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info
 
        tramp_size = 64 + 8 * depth;
 
-       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size);
+       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
@@ -706,6 +723,7 @@ mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info
        mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
 
        g_assert (code - buf <= tramp_size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        char *name = mono_get_rgctx_fetch_trampoline_name (slot);
        *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
@@ -725,7 +743,7 @@ mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboo
        g_assert (aot);
        tramp_size = 64;
 
-       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size);
+       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
@@ -742,6 +760,7 @@ mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboo
        mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
 
        g_assert (code - buf <= tramp_size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        if (info)
                *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
@@ -779,7 +798,7 @@ mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
        MonoJumpInfo *ji = NULL;
        GSList *unwind_ops;
 
-       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size);
+       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        unwind_ops = mono_arch_get_cie_program ();
 
@@ -834,6 +853,7 @@ mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
        mono_arch_flush_icache (buf, code - buf);
        mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
        g_assert (code - buf <= tramp_size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
 
        *info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops);
 
@@ -887,7 +907,7 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo
        GSList *unwind_ops = NULL;
        MonoJumpInfo *ji = NULL;
 
-       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size);
+       code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
 
        framesize = 0;
 #ifdef TARGET_WIN32
@@ -913,6 +933,7 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo
 
        amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
        mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
+       mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
 
        gregs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs);
@@ -955,7 +976,13 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo
        amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, gregs_offset + (AMD64_RIP * sizeof (mgreg_t)), sizeof (mgreg_t));
        amd64_mov_membase_reg (code, AMD64_RBP, sizeof (mgreg_t), AMD64_R11, sizeof (mgreg_t));
 
+#if TARGET_WIN32
+       amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+       amd64_pop_reg (code, AMD64_RBP);
+       mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
+#else
        amd64_leave (code);
+#endif
        cfa_offset -= sizeof (mgreg_t);
        mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
        amd64_ret (code);
@@ -963,6 +990,7 @@ mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gbo
        mono_arch_flush_icache (code, code - buf);
        mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
        g_assert (code - buf <= tramp_size);
+       g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
 
        const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
        *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
@@ -989,7 +1017,7 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
        static int arg_regs[] = {AMD64_ARG_REG1, AMD64_ARG_REG2, AMD64_ARG_REG3, AMD64_ARG_REG4, AMD64_R8, AMD64_R9};
        int i, offset = 0;
 
-       start = code = (guint8 *) mono_global_codeman_reserve (256);
+       start = code = (guint8 *) mono_global_codeman_reserve (256 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
 
        /* save MethodArguments* onto stack */
        amd64_push_reg (code, AMD64_ARG_REG2);
index 6d60f79e7701cc5b697f276356f68f68c0fde65c..960447171e690e113129845451173abf39cbcadb 100644 (file)
@@ -449,6 +449,13 @@ mono_unwind_ops_encode_full (GSList *unwind_ops, guint32 *out_len, gboolean enab
                        g_assert (op->val == 0);
                        *p ++ = op->op;
                        break;
+#if defined(TARGET_WIN32) && defined(TARGET_AMD64)
+               case DW_CFA_mono_sp_alloc_info_win64:
+               case DW_CFA_mono_fp_alloc_info_win64:
+                       // Drop Windows specific unwind op's. These op's are currently
+                       // only used when registering unwind info with Windows OS unwinder.
+                       break;
+#endif
                default:
                        g_assert_not_reached ();
                        break;
diff --git a/msvc/libmonodac.vcxproj b/msvc/libmonodac.vcxproj
new file mode 100644 (file)
index 0000000..b8d2520
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}</ProjectGuid>\r
+    <RootNamespace>libmonodac</RootNamespace>\r
+    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <UseOfMfc>false</UseOfMfc>\r
+    <CharacterSet>Unicode</CharacterSet>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <UseOfMfc>false</UseOfMfc>\r
+    <CharacterSet>Unicode</CharacterSet>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="mono.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="mono.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup>\r
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">mono-2.0-dac$(MONO_TARGET_SUFFIX)</TargetName>\r
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">mono-2.0-dac$(MONO_TARGET_SUFFIX)</TargetName>\r
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\</OutDir>\r
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(MONO_BUILD_DIR_PREFIX)$(Platform)\bin\$(Configuration)\</OutDir>\r
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\</IntDir>\r
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(MONO_BUILD_DIR_PREFIX)$(Platform)\obj\$(ProjectName)$(MONO_TARGET_SUFFIX)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <LinkIncremental>true</LinkIncremental>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <PreBuildEvent>\r
+      <Command>\r
+      </Command>\r
+    </PreBuildEvent>\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <AdditionalOptions>/D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions)</AdditionalOptions>\r
+      <Optimization>Disabled</Optimization>\r
+      <InlineFunctionExpansion>Default</InlineFunctionExpansion>\r
+      <IntrinsicFunctions>true</IntrinsicFunctions>\r
+      <AdditionalIncludeDirectories>$(MONO_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <MinimalRebuild>true</MinimalRebuild>\r
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+      <PrecompiledHeaderOutputFile>\r
+      </PrecompiledHeaderOutputFile>\r
+      <BrowseInformation>true</BrowseInformation>\r
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
+      <CompileAs>CompileAsC</CompileAs>\r
+      <DisableSpecificWarnings>4996;4018;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <ResourceCompile>\r
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <Culture>0x0409</Culture>\r
+    </ResourceCompile>\r
+    <ProjectReference>\r
+      <LinkLibraryDependencies>true</LinkLibraryDependencies>\r
+    </ProjectReference>\r
+    <Link>\r
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+      <ModuleDefinitionFile>\r
+      </ModuleDefinitionFile>\r
+      <GenerateDebugInformation>true</GenerateDebugInformation>\r
+      <ImportLibrary>$(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+    <PostBuildEvent>\r
+      <Command>\r
+      </Command>\r
+    </PostBuildEvent>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <PreBuildEvent>\r
+      <Command>\r
+      </Command>\r
+    </PreBuildEvent>\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <AdditionalOptions>/D /NODEFAULTLIB:LIBCD" " %(AdditionalOptions)</AdditionalOptions>\r
+      <Optimization>MinSpace</Optimization>\r
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>\r
+      <IntrinsicFunctions>true</IntrinsicFunctions>\r
+      <AdditionalIncludeDirectories>$(MONO_DIR);$(LIBGC_CPPFLAGS_INCLUDE);$(GLIB_CFLAGS_INCLUDE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <StringPooling>true</StringPooling>\r
+      <FunctionLevelLinking>true</FunctionLevelLinking>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+      <PrecompiledHeaderOutputFile>\r
+      </PrecompiledHeaderOutputFile>\r
+      <CompileAs>CompileAsC</CompileAs>\r
+    </ClCompile>\r
+    <ResourceCompile>\r
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <Culture>0x0409</Culture>\r
+    </ResourceCompile>\r
+    <ProjectReference>\r
+      <LinkLibraryDependencies>true</LinkLibraryDependencies>\r
+    </ProjectReference>\r
+    <Link>\r
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+      <ModuleDefinitionFile>\r
+      </ModuleDefinitionFile>\r
+      <ImportLibrary>$(MONO_BUILD_DIR_PREFIX)$(Platform)\lib\$(Configuration)\$(TargetName).lib</ImportLibrary>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+      <GenerateDebugInformation>true</GenerateDebugInformation>\r
+    </Link>\r
+    <PostBuildEvent>\r
+      <Command>\r
+      </Command>\r
+    </PostBuildEvent>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ProjectReference Include="libmono.vcxproj">\r
+      <Project>{cb0d9e92-293c-439c-9ac7-c5f59b6e0771}</Project>\r
+    </ProjectReference>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\mono\mini\mini-windows-dlldac.c" />\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>
\ No newline at end of file
diff --git a/msvc/libmonodac.vcxproj.filters b/msvc/libmonodac.vcxproj.filters
new file mode 100644 (file)
index 0000000..998b4c7
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup>\r
+    <Filter Include="Header Files">\r
+      <UniqueIdentifier>{bdc9f80b-3045-49d2-bb7b-510450371395}</UniqueIdentifier>\r
+    </Filter>\r
+    <Filter Include="Resource Files">\r
+      <UniqueIdentifier>{f7700495-afaa-4d16-9aac-79d54d10de23}</UniqueIdentifier>\r
+    </Filter>\r
+    <Filter Include="Source Files">\r
+      <UniqueIdentifier>{5370c3c4-b6ec-4f8a-8b21-ce4e782720a6}</UniqueIdentifier>\r
+    </Filter>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\mono\mini\mini-windows-dlldac.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
+  </ItemGroup>\r
+</Project>
\ No newline at end of file
index 9a8a64ddfd483c8783ce94f727adf73c4d1312f4..193213c809b05a5c69ade222b03689eba06ad0a9 100644 (file)
@@ -210,6 +210,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mono-nunit-test", "mono-nun
                {92AE7622-5F58-4234-9A26-9EC71876B3F4} = {92AE7622-5F58-4234-9A26-9EC71876B3F4}\r
        EndProjectSection\r
 EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmonodac", "libmonodac.vcxproj", "{DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}"\r
+EndProject\r
 Global\r
        GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
                Debug|Win32 = Debug|Win32\r
@@ -432,6 +434,12 @@ Global
                {0046B994-40A8-4C64-AC9D-429DC9177B54}.Release|Win32.Build.0 = Release|Win32\r
                {0046B994-40A8-4C64-AC9D-429DC9177B54}.Release|x64.ActiveCfg = Release|x64\r
                {0046B994-40A8-4C64-AC9D-429DC9177B54}.Release|x64.Build.0 = Release|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Debug|Win32.ActiveCfg = Debug|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Debug|x64.ActiveCfg = Debug|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Debug|x64.Build.0 = Debug|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Release|Win32.ActiveCfg = Release|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Release|x64.ActiveCfg = Release|x64\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53}.Release|x64.Build.0 = Release|x64\r
        EndGlobalSection\r
        GlobalSection(SolutionProperties) = preSolution\r
                HideSolutionNode = FALSE\r
@@ -468,11 +476,12 @@ Global
                {6C64262B-077B-4E12-AF91-9409ECCB75F6} = {BACF489E-EAEB-42BF-9E0A-C54D7CF455B4}\r
                {7BECCFA0-28A0-4995-9856-558560F720E6} = {A0068765-334B-414C-8E21-8376CD2EC9F6}\r
                {0046B994-40A8-4C64-AC9D-429DC9177B54} = {A0068765-334B-414C-8E21-8376CD2EC9F6}\r
+               {DC50997D-8A0D-4EB6-849B-9D7FBC39CE53} = {DE3617B4-17A8-4E5F-A00F-BA43D956881F}\r
        EndGlobalSection\r
        GlobalSection(ExtensibilityGlobals) = postSolution\r
-               AMDCaPersistentConfig = Debug|Win32\r
-               AMDCaPersistentStartup = mono\r
                AMDCaProjectFile = C:\Users\Owner\Development\monogit\mono\msvc\CodeAnalyst\mono.caw\r
+               AMDCaPersistentStartup = mono\r
+               AMDCaPersistentConfig = Debug|Win32\r
        EndGlobalSection\r
        GlobalSection(DPCodeReviewSolutionGUID) = preSolution\r
                DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000}\r
index 73f67534019a237795922f6b7b855ce5e4c8a259..37a232b6a53a1715de5a96064b544663f18252ad 100644 (file)
 #error "Mono requires Windows Vista or later"
 #endif /* _WIN32_WINNT < 0x0600 */
 
+#ifndef HAVE_WINAPI_FAMILY_SUPPORT
+
+#define HAVE_WINAPI_FAMILY_SUPPORT
+
+/* WIN API Family support */
+#include <winapifamily.h>
+
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 1
+       #define HAVE_UWP_WINAPI_SUPPORT 0
+#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
+       #define HAVE_UWP_WINAPI_SUPPORT 1
+#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
+       #error Unsupported WINAPI family
+#endif
+#else
+       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
+       #define HAVE_UWP_WINAPI_SUPPORT 0
+#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
+       #error Unsupported WINAPI family
+#endif
+#endif
+
+#endif
+
 /*
  * Features that are not required in the Windows port
  */
 #define HAVE_COMPLEX_H 1
 
 /* Define to 1 if you have the `system' function. */
+#if HAVE_WINAPI_FAMILY_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
 #define HAVE_SYSTEM 1
+#endif
+
 
 /* Have /dev/random */
 #define HAVE_CRYPT_RNG 1
 
 /* Version number of package */
 #define VERSION "#MONO_VERSION#"
-
-#ifndef HAVE_WINAPI_FAMILY_SUPPORT
-
-#define HAVE_WINAPI_FAMILY_SUPPORT
-
-/* WIN API Family support */
-#include <winapifamily.h>
-
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 1
-       #define HAVE_UWP_WINAPI_SUPPORT 0
-#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
-       #define HAVE_UWP_WINAPI_SUPPORT 1
-#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
-       #error Unsupported WINAPI family
-#endif
-#else
-       #define HAVE_CLASSIC_WINAPI_SUPPORT 0
-       #define HAVE_UWP_WINAPI_SUPPORT 0
-#ifndef HAVE_EXTERN_DEFINED_WINAPI_SUPPORT
-       #error Unsupported WINAPI family
-#endif
-#endif
-
-#endif
 #endif