#include <mono/metadata/exception.h>
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-debug-debugger.h>
+#include <mono/utils/mono-mmap.h>
#include "mini.h"
#include "mini-x86.h"
#ifdef PLATFORM_WIN32
+static void (*restore_stack) (void *);
+
static MonoW32ExceptionHandler fpe_handler;
static MonoW32ExceptionHandler ill_handler;
static MonoW32ExceptionHandler segv_handler;
#define W32_SEH_HANDLE_EX(_ex) \
if (_ex##_handler) _ex##_handler((int)sctx)
+/*
+ * mono_win32_get_handle_stackoverflow (void):
+ *
+ * Returns a pointer to a method which restores the current context stack
+ * and calls handle_exceptions, when done restores the original stack.
+ */
+static gpointer
+mono_win32_get_handle_stackoverflow (void)
+{
+ static guint8 *start = NULL;
+ guint8 *code;
+
+ if (start)
+ return start;
+
+ /* restore_contect (void *sigctx) */
+ start = code = mono_global_codeman_reserve (128);
+
+ /* load context into ebx */
+ x86_mov_reg_membase (code, X86_EBX, X86_ESP, 4, 4);
+
+ /* move current stack into edi for later restore */
+ x86_mov_reg_reg (code, X86_EDI, X86_ESP, 4);
+
+ /* use the new freed stack from sigcontext */
+ x86_mov_reg_membase (code, X86_ESP, X86_EBX, G_STRUCT_OFFSET (struct sigcontext, esp), 4);
+
+ /* get the current domain */
+ x86_call_code (code, mono_domain_get);
+
+ /* get stack overflow exception from domain object */
+ x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoDomain, stack_overflow_ex), 4);
+
+ /* call mono_arch_handle_exception (sctx, stack_overflow_exception_obj, FALSE) */
+ x86_push_imm (code, 0);
+ x86_push_reg (code, X86_EAX);
+ x86_push_reg (code, X86_EBX);
+ x86_call_code (code, mono_arch_handle_exception);
+
+ /* restore the SEH handler stack */
+ x86_mov_reg_reg (code, X86_ESP, X86_EDI, 4);
+
+ /* return */
+ x86_ret (code);
+
+ return start;
+}
+
+/* Special hack to workaround the fact that the
+ * when the SEH handler is called the stack is
+ * to small to recover.
+ *
+ * Stack walking part of this method is from mono_handle_exception
+ *
+ * The idea is simple;
+ * - walk the stack to free some space (64k)
+ * - set esp to new stack location
+ * - call mono_arch_handle_exception with stack overflow exception
+ * - set esp to SEH handlers stack
+ * - done
+ */
+static void
+win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx)
+{
+ SYSTEM_INFO si;
+ DWORD page_size;
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitInfo *ji, rji;
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ MonoLMF *lmf = jit_tls->lmf;
+ MonoContext initial_ctx;
+ MonoContext ctx;
+ guint32 free_stack = 0;
+
+ /* convert sigcontext to MonoContext (due to reuse of stack walking helpers */
+ mono_arch_sigctx_to_monoctx (sctx, &ctx);
+
+ /* get our os page size */
+ GetSystemInfo(&si);
+ page_size = si.dwPageSize;
+
+ /* Let's walk the stack to recover
+ * the needed stack space (if possible)
+ */
+ memset (&rji, 0, sizeof (rji));
+
+ initial_ctx = ctx;
+ free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
+
+ /* try to free 64kb from our stack */
+ do {
+ MonoContext new_ctx;
+
+ ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, &lmf, NULL);
+ if (!ji) {
+ g_warning ("Exception inside function without unwind info");
+ g_assert_not_reached ();
+ }
+
+ if (ji != (gpointer)-1) {
+ free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
+ }
+
+ /* todo: we should call abort if ji is -1 */
+ ctx = new_ctx;
+ } while (free_stack < 64 * 1024 && ji != (gpointer) -1);
+
+ /* convert into sigcontext to be used in mono_arch_handle_exception */
+ mono_arch_monoctx_to_sigctx (&ctx, sctx);
+
+ /* todo: install new stack-guard page */
+
+ /* use the new stack and call mono_arch_handle_exception () */
+ restore_stack (sctx);
+}
+
/*
* Unhandled Exception Filter
* Top-level per-process exception handler.
sctx->eip = ctx->Eip;
switch (er->ExceptionCode) {
+ case EXCEPTION_STACK_OVERFLOW:
+ win32_handle_stack_overflow (ep, sctx);
+ break;
case EXCEPTION_ACCESS_VIOLATION:
W32_SEH_HANDLE_EX(segv);
break;
ctx->Edi = sctx->edi;
ctx->Eip = sctx->eip;
+ g_free (sctx);
+
return res;
}
void win32_seh_init()
{
+ /* install restore stack helper */
+ if (!restore_stack)
+ restore_stack = mono_win32_get_handle_stackoverflow ();
+
old_handler = SetUnhandledExceptionFilter(seh_handler);
}
inited = 1;
code = start = mono_global_codeman_reserve (64);
- x86_push_membase (code, X86_ESP, 4); /* token */
+ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); /* token */
+ x86_alu_reg_imm (code, X86_ADD, X86_EAX, MONO_TOKEN_TYPE_DEF);
+ x86_push_reg (code, X86_EAX);
x86_push_imm (code, mono_defaults.exception_class->image);
x86_call_code (code, mono_exception_from_token);
x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
*/
MonoJitInfo *
mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
- MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
- gboolean *managed)
+ MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
{
MonoJitInfo *ji;
gpointer ip = MONO_CONTEXT_GET_IP (ctx);
if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
/* remove any unused lmf */
- *lmf = (*lmf)->previous_lmf;
+ *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
}
/* Pop EBP and the return address */
- new_ctx->esp = ctx->SC_EBP + (2 * sizeof (gpointer));
+ new_ctx->esp = ctx->ebp + (2 * sizeof (gpointer));
/* we substract 1, so that the IP points into the call instruction */
new_ctx->eip = *((int *)ctx->ebp + 1) - 1;
new_ctx->ebp = *((int *)ctx->ebp);
*new_ctx = *ctx;
- if (!(*lmf)->method)
- return (gpointer)-1;
-
if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
} else {
+ if (!(*lmf)->method)
+ /* Top LMF entry */
+ return (gpointer)-1;
+ /* Trampoline lmf frame */
memset (res, 0, sizeof (MonoJitInfo));
res->method = (*lmf)->method;
}
new_ctx->ebx = (*lmf)->ebx;
new_ctx->ebp = (*lmf)->ebp;
new_ctx->eip = (*lmf)->eip;
- /* the lmf is always stored on the stack, so the following
- * expression points to a stack location which can be used as ESP */
- new_ctx->esp = (unsigned long)&((*lmf)->eip);
- *lmf = (*lmf)->previous_lmf;
+ /* Check if we are in a trampoline LMF frame */
+ if ((guint32)((*lmf)->previous_lmf) & 1) {
+ /* lmf->esp is set by the trampoline code */
+ new_ctx->esp = (*lmf)->esp;
+
+ /* Pop arguments off the stack */
+ /* FIXME: Handle the delegate case too ((*lmf)->method == NULL) */
+ /* FIXME: Handle the IMT/vtable case too */
+ if ((*lmf)->method && (*lmf)->method != MONO_FAKE_IMT_METHOD && (*lmf)->method != MONO_FAKE_VTABLE_METHOD) {
+ MonoMethod *method = (*lmf)->method;
+ MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (method)->param_count + 1);
+
+ guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (method), mono_method_signature (method)->param_count, arg_info);
+ new_ctx->esp += stack_to_pop;
+ }
+ }
+ else
+ /* the lmf is always stored on the stack, so the following
+ * expression points to a stack location which can be used as ESP */
+ new_ctx->esp = (unsigned long)&((*lmf)->eip);
+
+ *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
return ji ? ji : res;
}
return NULL;
}
+#ifdef __sun
+#define REG_EAX EAX
+#define REG_EBX EBX
+#define REG_ECX ECX
+#define REG_EDX EDX
+#define REG_EBP EBP
+#define REG_ESP ESP
+#define REG_ESI ESI
+#define REG_EDI EDI
+#define REG_EIP EIP
+#endif
+
void
mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
{
+#ifdef MONO_ARCH_USE_SIGACTION
ucontext_t *ctx = (ucontext_t*)sigctx;
-#ifdef MONO_ARCH_USE_SIGACTION
- mctx->eax = ctx->uc_mcontext.gregs [REG_EAX];
- mctx->ebx = ctx->uc_mcontext.gregs [REG_EBX];
- mctx->ecx = ctx->uc_mcontext.gregs [REG_ECX];
- mctx->edx = ctx->uc_mcontext.gregs [REG_EDX];
- mctx->ebp = ctx->uc_mcontext.gregs [REG_EBP];
- mctx->esp = ctx->uc_mcontext.gregs [REG_ESP];
- mctx->esi = ctx->uc_mcontext.gregs [REG_ESI];
- mctx->edi = ctx->uc_mcontext.gregs [REG_EDI];
- mctx->eip = ctx->uc_mcontext.gregs [REG_EIP];
-#else
- memcpy (mctx, sigctx, sizeof (MonoContext));
+ mctx->eax = UCONTEXT_REG_EAX (ctx);
+ mctx->ebx = UCONTEXT_REG_EBX (ctx);
+ mctx->ecx = UCONTEXT_REG_ECX (ctx);
+ mctx->edx = UCONTEXT_REG_EDX (ctx);
+ mctx->ebp = UCONTEXT_REG_EBP (ctx);
+ mctx->esp = UCONTEXT_REG_ESP (ctx);
+ mctx->esi = UCONTEXT_REG_ESI (ctx);
+ mctx->edi = UCONTEXT_REG_EDI (ctx);
+ mctx->eip = UCONTEXT_REG_EIP (ctx);
+#else
+ struct sigcontext *ctx = (struct sigcontext *)sigctx;
+
+ mctx->eax = ctx->SC_EAX;
+ mctx->ebx = ctx->SC_EBX;
+ mctx->ecx = ctx->SC_ECX;
+ mctx->edx = ctx->SC_EDX;
+ mctx->ebp = ctx->SC_EBP;
+ mctx->esp = ctx->SC_ESP;
+ mctx->esi = ctx->SC_ESI;
+ mctx->edi = ctx->SC_EDI;
+ mctx->eip = ctx->SC_EIP;
#endif
}
void
mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
{
+#ifdef MONO_ARCH_USE_SIGACTION
ucontext_t *ctx = (ucontext_t*)sigctx;
-#ifdef MONO_ARCH_USE_SIGACTION
- ctx->uc_mcontext.gregs [REG_EAX] = mctx->eax;
- ctx->uc_mcontext.gregs [REG_EBX] = mctx->ebx;
- ctx->uc_mcontext.gregs [REG_ECX] = mctx->ecx;
- ctx->uc_mcontext.gregs [REG_EDX] = mctx->edx;
- ctx->uc_mcontext.gregs [REG_EBP] = mctx->ebp;
- ctx->uc_mcontext.gregs [REG_ESP] = mctx->esp;
- ctx->uc_mcontext.gregs [REG_ESI] = mctx->esi;
- ctx->uc_mcontext.gregs [REG_EDI] = mctx->edi;
- ctx->uc_mcontext.gregs [REG_EIP] = mctx->eip;
+ UCONTEXT_REG_EAX (ctx) = mctx->eax;
+ UCONTEXT_REG_EBX (ctx) = mctx->ebx;
+ UCONTEXT_REG_ECX (ctx) = mctx->ecx;
+ UCONTEXT_REG_EDX (ctx) = mctx->edx;
+ UCONTEXT_REG_EBP (ctx) = mctx->ebp;
+ UCONTEXT_REG_ESP (ctx) = mctx->esp;
+ UCONTEXT_REG_ESI (ctx) = mctx->esi;
+ UCONTEXT_REG_EDI (ctx) = mctx->edi;
+ UCONTEXT_REG_EIP (ctx) = mctx->eip;
#else
- memcpy (ctx, mctx, sizeof (MonoContext));
+ struct sigcontext *ctx = (struct sigcontext *)sigctx;
+
+ ctx->SC_EAX = mctx->eax;
+ ctx->SC_EBX = mctx->ebx;
+ ctx->SC_ECX = mctx->ecx;
+ ctx->SC_EDX = mctx->edx;
+ ctx->SC_EBP = mctx->ebp;
+ ctx->SC_ESP = mctx->esp;
+ ctx->SC_ESI = mctx->esi;
+ ctx->SC_EDI = mctx->edi;
+ ctx->SC_EIP = mctx->eip;
#endif
}
{
#ifdef MONO_ARCH_USE_SIGACTION
ucontext_t *ctx = (ucontext_t*)sigctx;
- return (gpointer)ctx->uc_mcontext.gregs [REG_EIP];
+ return (gpointer)UCONTEXT_REG_EIP (ctx);
#else
struct sigcontext *ctx = sigctx;
return (gpointer)ctx->SC_EIP;
return TRUE;
}
+
+static void
+restore_soft_guard_pages (void)
+{
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ if (jit_tls->stack_ovf_guard_base)
+ mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
+}
+
+/*
+ * this function modifies mctx so that when it is restored, it
+ * won't execcute starting at mctx.eip, but in a function that
+ * will restore the protection on the soft-guard pages and return back to
+ * continue at mctx.eip.
+ */
+static void
+prepare_for_guard_pages (MonoContext *mctx)
+{
+ gpointer *sp;
+ sp = (gpointer)(mctx->esp);
+ sp -= 1;
+ /* the resturn addr */
+ sp [0] = (gpointer)(mctx->eip);
+ mctx->eip = (unsigned long)restore_soft_guard_pages;
+ mctx->esp = (unsigned long)sp;
+}
+
+static void
+altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
+{
+ void (*restore_context) (MonoContext *);
+ MonoContext mctx;
+
+ restore_context = mono_arch_get_restore_context ();
+ mono_arch_sigctx_to_monoctx (sigctx, &mctx);
+ mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
+ if (stack_ovf)
+ prepare_for_guard_pages (&mctx);
+ restore_context (&mctx);
+}
+
+void
+mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
+{
+#ifdef MONO_ARCH_USE_SIGACTION
+ MonoException *exc = NULL;
+ ucontext_t *ctx = (ucontext_t*)sigctx;
+ MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx));
+ gpointer *sp;
+ int frame_size;
+
+ /* if we didn't find a managed method for the ip address and it matches the fault
+ * address, we assume we followed a broken pointer during an indirect call, so
+ * we try the lookup again with the return address pushed on the stack
+ */
+ if (!ji && fault_addr == (gpointer)UCONTEXT_REG_EIP (ctx)) {
+ glong *sp = (gpointer)UCONTEXT_REG_ESP (ctx);
+ ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)sp [0]);
+ if (ji)
+ UCONTEXT_REG_EIP (ctx) = sp [0];
+ }
+ if (stack_ovf)
+ exc = mono_domain_get ()->stack_overflow_ex;
+ if (!ji)
+ mono_handle_native_sigsegv (SIGSEGV, sigctx);
+ /* setup a call frame on the real stack so that control is returned there
+ * and exception handling can continue.
+ * If this was a stack overflow the caller already ensured the stack pages
+ * needed have been unprotected.
+ * The frame looks like:
+ * ucontext struct
+ * test_only arg
+ * exception arg
+ * ctx arg
+ * return ip
+ */
+ frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4;
+ frame_size += 15;
+ frame_size &= ~15;
+ sp = (gpointer)(UCONTEXT_REG_ESP (ctx) & ~15);
+ sp = (gpointer)((char*)sp - frame_size);
+ /* the incoming arguments are aligned to 16 bytes boundaries, so the return address IP
+ * goes at sp [-1]
+ */
+ sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx);
+ sp [0] = sp + 4;
+ sp [1] = exc;
+ sp [2] = (gpointer)stack_ovf;
+ /* may need to adjust pointers in the new struct copy, depending on the OS */
+ memcpy (sp + 4, ctx, sizeof (ucontext_t));
+ /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
+ UCONTEXT_REG_EIP (ctx) = (unsigned long)altstack_handle_and_restore;
+ UCONTEXT_REG_ESP (ctx) = (unsigned long)(sp - 1);
+#endif
+}
+