-/*
- * exceptions-amd64.c: exception support for AMD64
+/**
+ * \file
+ * exception support for AMD64
*
* Authors:
* Dietmar Maurer (dietmar@ximian.com)
#include <config.h>
+// Secret password to unlock wcscat_s on mxe, must happen before string.h included
+#ifdef __MINGW32__
+#define MINGW_HAS_SECURE_API 1
+#endif
+
#include <glib.h>
#include <string.h>
#define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
#ifdef TARGET_WIN32
+static void (*restore_stack) (void);
static MonoW32ExceptionHandler fpe_handler;
static MonoW32ExceptionHandler ill_handler;
static MonoW32ExceptionHandler segv_handler;
return EXCEPTION_CONTINUE_SEARCH;
}
+static gpointer
+get_win32_restore_stack (void)
+{
+ static guint8 *start = NULL;
+ guint8 *code;
+
+ if (start)
+ return start;
+
+ /* restore_stack (void) */
+ start = code = mono_global_codeman_reserve (128);
+
+ amd64_push_reg (code, AMD64_RBP);
+ amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
+
+ /* push 32 bytes of stack space for Win64 calling convention */
+ amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 32);
+
+ /* restore guard page */
+ amd64_mov_reg_imm (code, AMD64_R11, _resetstkoflw);
+ amd64_call_reg (code, AMD64_R11);
+
+ /* get jit_tls with context to restore */
+ amd64_mov_reg_imm (code, AMD64_R11, mono_tls_get_jit_tls);
+ amd64_call_reg (code, AMD64_R11);
+
+ /* move jit_tls from return reg to arg reg */
+ amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_RAX, 8);
+
+ /* retrieve pointer to saved context */
+ amd64_alu_reg_imm (code, X86_ADD, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoJitTlsData, stack_restore_ctx));
+
+ /* this call does not return */
+ amd64_mov_reg_imm (code, AMD64_R11, mono_restore_context);
+ amd64_call_reg (code, AMD64_R11);
+
+ g_assert ((code - start) < 128);
+
+ mono_arch_flush_icache (start, code - start);
+ MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
+
+ return start;
+}
+
/*
* Unhandled Exception Filter
* Top-level per-process exception handler.
CONTEXT* ctx;
LONG res;
MonoJitTlsData *jit_tls = mono_tls_get_jit_tls ();
+ MonoDomain* domain = mono_domain_get ();
/* If the thread is not managed by the runtime return early */
if (!jit_tls)
ctx = ep->ContextRecord;
switch (er->ExceptionCode) {
+ case EXCEPTION_STACK_OVERFLOW:
+ if (!mono_aot_only) {
+ if (mono_arch_handle_exception (ctx, domain->stack_overflow_ex)) {
+ /* need to restore stack protection once stack is unwound
+ * restore_stack will restore stack protection and then
+ * resume control to the saved stack_restore_ctx */
+ mono_sigctx_to_monoctx (ctx, &jit_tls->stack_restore_ctx);
+ ctx->Rip = (guint64)restore_stack;
+ }
+ } else {
+ jit_tls->mono_win_chained_exception_needs_run = TRUE;
+ }
+ break;
case EXCEPTION_ACCESS_VIOLATION:
W32_SEH_HANDLE_EX(segv);
break;
void win32_seh_init()
{
+ if (!mono_aot_only)
+ restore_stack = get_win32_restore_stack ();
+
mono_old_win_toplevel_exception_filter = SetUnhandledExceptionFilter(seh_unhandled_exception_filter);
mono_win_vectored_exception_handle = AddVectoredExceptionHandler (1, seh_vectored_exception_handler);
}
amd64_jump_reg (code, AMD64_R11);
mono_arch_flush_icache (start, code - start);
- mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+ MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
if (info)
*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);
g_assert ((code - start) < kMaxCodeSize);
mono_arch_flush_icache (start, code - start);
- mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+ MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
if (info)
*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);
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);
+ MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
if (info)
*info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops);
/**
* mono_arch_get_throw_exception:
- *
- * Returns a function pointer which can be used to raise
+ * \returns a function pointer which can be used to raise
* exceptions. The returned function has the following
* signature: void (*func) (MonoException *exc);
- *
*/
gpointer
mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
guint64 rip;
if (((guint64)(*lmf)->previous_lmf) & 2) {
- /*
- * This LMF entry is created by the soft debug code to mark transitions to
- * managed code done during invokes.
- */
MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
- g_assert (ext->debugger_invoke);
-
- memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+ if (ext->debugger_invoke) {
+ /*
+ * This LMF entry is created by the soft debug code to mark transitions to
+ * managed code done during invokes.
+ */
+ frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+ memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+ } else if (ext->interp_exit) {
+ frame->type = FRAME_TYPE_INTERP_TO_MANAGED;
+ frame->interp_exit_data = ext->interp_exit_data;
+ } else {
+ g_assert_not_reached ();
+ }
*lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7);
- frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
-
return TRUE;
}
/**
* mono_arch_handle_exception:
- *
- * @ctx: saved processor state
- * @obj: the exception object
+ * \param ctx saved processor state
+ * \param obj the exception object
*/
gboolean
mono_arch_handle_exception (void *sigctx, gpointer obj)
}
}
+// Implies defined(TARGET_WIN32)
#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
static void
g_error ("Larger allocation needed for the unwind information.");
codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
- unwindcode = &unwindinfo->UnwindCode[codeindex];
+ unwindcode = &unwindinfo->UnwindCode [codeindex];
unwindcode->UnwindOp = UWOP_PUSH_NONVOL;
unwindcode->CodeOffset = (guchar)unwind_op->when;
unwindcode->OpInfo = unwind_op->reg;
g_error ("Larger allocation needed for the unwind information.");
codeindex = MONO_MAX_UNWIND_CODES - (++unwindinfo->CountOfCodes);
- unwindcode = &unwindinfo->UnwindCode[codeindex];
+ unwindcode = &unwindinfo->UnwindCode [codeindex];
unwindcode->UnwindOp = UWOP_SET_FPREG;
unwindcode->CodeOffset = (guchar)unwind_op->when;
- g_assert(unwind_op->val % 16 == 0);
+ g_assert (unwind_op->val % 16 == 0);
unwindinfo->FrameRegister = unwind_op->reg;
unwindinfo->FrameOffset = unwind_op->val / 16;
g_error ("Larger allocation needed for the unwind information.");
codeindex = MONO_MAX_UNWIND_CODES - (unwindinfo->CountOfCodes += codesneeded);
- unwindcode = &unwindinfo->UnwindCode[codeindex];
+ unwindcode = &unwindinfo->UnwindCode [codeindex];
unwindcode->CodeOffset = (guchar)unwind_op->when;
// 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"
+#define MONO_DAC_MODULE TEXT("mono-2.0-dac-sgen.dll")
#else
-#define MONO_DAC_MODULE L"mono-2.0-sgen.dll"
+#define MONO_DAC_MODULE TEXT("mono-2.0-sgen.dll")
#endif
#define MONO_DAC_MODULE_MAX_PATH 1024
static void
-mono_arch_unwindinfo_init_table_no_lock (void)
+init_table_no_lock (void)
{
if (g_dyn_func_table_inited == FALSE) {
g_assert_checked (g_dynamic_function_table_begin == NULL);
AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
- mono_arch_unwindinfo_init_table_no_lock ();
+ init_table_no_lock ();
ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
}
}
static void
-mono_arch_unwindinfo_terminate_table_no_lock (void)
+terminate_table_no_lock (void)
{
if (g_dyn_func_table_inited == TRUE) {
if (g_dynamic_function_table_begin != NULL) {
AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
- mono_arch_unwindinfo_terminate_table_no_lock ();
+ 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)
+fast_find_range_in_table_no_lock_ex (gsize begin_range, gsize end_range, gboolean *continue_search)
{
GList *found_entry = NULL;
}
static inline DynamicFunctionTableEntry *
-mono_arch_unwindinfo_fast_find_range_in_table_no_lock (gsize begin_range, gsize end_range, gboolean *continue_search)
+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);
+ GList *found_entry = 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)
+find_range_in_table_no_lock_ex (const gpointer code_block, gsize block_size)
{
GList *found_entry = NULL;
gboolean continue_search = FALSE;
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);
+ found_entry = fast_find_range_in_table_no_lock_ex (begin_range, end_range, &continue_search);
if (found_entry || continue_search == FALSE)
return found_entry;
}
static inline DynamicFunctionTableEntry *
-mono_arch_unwindinfo_find_range_in_table_no_lock (const gpointer code_block, gsize block_size)
+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);
+ GList *found_entry = 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)
+find_pc_in_table_no_lock_ex (const gpointer pc)
{
GList *found_entry = NULL;
gboolean continue_search = FALSE;
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);
+ found_entry = fast_find_range_in_table_no_lock_ex (begin_range, begin_range, &continue_search);
if (found_entry || continue_search == FALSE)
return found_entry;
}
static inline DynamicFunctionTableEntry *
-mono_arch_unwindinfo_find_pc_in_table_no_lock (const gpointer pc)
+find_pc_in_table_no_lock (const gpointer pc)
{
- GList *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock_ex (pc);
+ GList *found_entry = 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)
+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.
#else
static inline void
-mono_arch_unwindinfo_validate_table_no_lock (void)
+validate_table_no_lock (void)
{
;
}
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);
+ init_table_no_lock ();
+ new_entry = find_range_in_table_no_lock (code_block, block_size);
if (new_entry == NULL) {
// Allocate new entry.
new_entry = g_new0 (DynamicFunctionTableEntry, 1);
// DAC module should be in the same directory as the
// main executable.
- GetModuleFileNameW (NULL, buffer, G_N_ELEMENTS (buffer));
+ 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);
+ wcscat_s (buffer, G_N_ELEMENTS(buffer), MONO_DAC_MODULE);
path = buffer;
// Register function table callback + out of proc module.
}
// Only included in checked builds. Validates the structure of table after insert.
- mono_arch_unwindinfo_validate_table_no_lock ();
+ validate_table_no_lock ();
} else {
g_free (new_entry);
}
static void
-mono_arch_unwindinfo_remove_range_in_table_no_lock (GList *entry)
+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);
+ 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);
}
// Only included in checked builds. Validates the structure of table after remove.
- mono_arch_unwindinfo_validate_table_no_lock ();
+ validate_table_no_lock ();
}
void
{
AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
- GList *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock_ex (code);
+ GList *found_entry = 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);
+ remove_range_in_table_no_lock (found_entry);
ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
}
{
AcquireSRWLockExclusive (&g_dynamic_function_table_lock);
- GList *found_entry = mono_arch_unwindinfo_find_range_in_table_no_lock_ex (code_block, block_size);
+ GList *found_entry = 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);
+ remove_range_in_table_no_lock (found_entry);
ReleaseSRWLockExclusive (&g_dynamic_function_table_lock);
}
AcquireSRWLockShared (&g_dynamic_function_table_lock);
- DynamicFunctionTableEntry *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock (code);
+ DynamicFunctionTableEntry *found_entry = find_pc_in_table_no_lock (code);
if (found_entry != NULL) {
return found_rt_func;
}
-inline PRUNTIME_FUNCTION
+static 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)
+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.
#else
static inline void
-mono_arch_unwindinfo_validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
+validate_rt_funcs_in_table_no_lock (DynamicFunctionTableEntry *entry)
{
;
}
AcquireSRWLockShared (&g_dynamic_function_table_lock);
- DynamicFunctionTableEntry *found_entry = mono_arch_unwindinfo_find_pc_in_table_no_lock (code);
+ DynamicFunctionTableEntry *found_entry = find_pc_in_table_no_lock (code);
if (found_entry != NULL) {
}
if (current_rt_funcs [from_index].UnwindData != 0)
- new_rt_funcs[to_index++] = current_rt_funcs [from_index];
+ new_rt_funcs [to_index++] = current_rt_funcs [from_index];
}
// If we didn't insert by now, put it last in the list.
}
// Only included in checked builds. Validates the structure of table after insert.
- mono_arch_unwindinfo_validate_rt_funcs_in_table_no_lock (found_entry);
+ validate_rt_funcs_in_table_no_lock (found_entry);
ReleaseSRWLockExclusive (&found_entry->lock);
}
}
static void
-mono_arch_unwindinfo_initialize_unwind_info_internal_ex (GSList *unwind_ops, PUNWIND_INFO unwindinfo)
+initialize_unwind_info_internal_ex (GSList *unwind_ops, PUNWIND_INFO unwindinfo)
{
if (unwind_ops != NULL && unwindinfo != NULL) {
MonoUnwindOp *unwind_op_data;
}
static PUNWIND_INFO
-mono_arch_unwindinfo_initialize_unwind_info_internal (GSList *unwind_ops)
+initialize_unwind_info_internal (GSList *unwind_ops)
{
PUNWIND_INFO unwindinfo;
mono_arch_unwindinfo_create (&unwindinfo);
- mono_arch_unwindinfo_initialize_unwind_info_internal_ex (unwind_ops, unwindinfo);
+ initialize_unwind_info_internal_ex (unwind_ops, unwindinfo);
return unwindinfo;
}
mono_arch_unwindinfo_get_code_count (GSList *unwind_ops)
{
UNWIND_INFO unwindinfo = {0};
- mono_arch_unwindinfo_initialize_unwind_info_internal_ex (unwind_ops, &unwindinfo);
+ initialize_unwind_info_internal_ex (unwind_ops, &unwindinfo);
return unwindinfo.CountOfCodes;
}
{
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);
+ current_cfg->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_tramp_unwind_info (GSList *unwind_ops, gpointer code, guint code_size)
{
- PUNWIND_INFO unwindinfo = mono_arch_unwindinfo_initialize_unwind_info_internal (unwind_ops);
+ PUNWIND_INFO unwindinfo = initialize_unwind_info_internal (unwind_ops);
if (unwindinfo != NULL) {
mono_arch_unwindinfo_install_method_unwind_info (&unwindinfo, code, code_size);
}
g_assert ((code - start) <= kMaxCodeSize);
mono_arch_flush_icache (start, code - start);
- mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+ MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
saved = start;
return (MonoContinuationRestore)saved;