#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 "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, NULL, &lmf, NULL, 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;
void win32_seh_init()
{
+ /* install restore stack helper */
+ if (!restore_stack)
+ restore_stack = mono_win32_get_handle_stackoverflow ();
+
old_handler = SetUnhandledExceptionFilter(seh_handler);
}
if (start)
return start;
- /* restore_contect (struct sigcontext *ctx) */
+ /* restore_contect (MonoContext *ctx) */
/* we do not restore X86_EAX, X86_EDX */
- start = code = g_malloc (1024);
+ start = code = mono_global_codeman_reserve (128);
/* load ctx */
x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
/* get return address, stored in EDX */
- x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
+ x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (MonoContext, eip), 4);
/* restore EBX */
- x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
+ x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
/* restore EDI */
- x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+ x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
/* restore ESI */
- x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
+ x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
/* restore ESP */
- x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
+ x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (MonoContext, esp), 4);
/* restore EBP */
- x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+ x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
/* jump to the saved IP */
x86_jump_reg (code, X86_EDX);
gpointer
mono_arch_get_call_filter (void)
{
- static guint8 start [64];
+ static guint8* start;
static int inited = 0;
guint8 *code;
return start;
inited = 1;
- /* call_filter (struct sigcontext *ctx, unsigned long eip) */
- code = start;
+ /* call_filter (MonoContext *ctx, unsigned long eip) */
+ start = code = mono_global_codeman_reserve (64);
x86_push_reg (code, X86_EBP);
x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
x86_push_reg (code, X86_EBP);
/* set new EBP */
- x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
+ x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
/* restore registers used by global register allocation (EBX & ESI) */
- x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
- x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
- x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
+ x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
+ x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
+ x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
/* call the handler */
x86_call_reg (code, X86_ECX);
unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
unsigned long eip, unsigned long esp, gboolean rethrow)
{
- static void (*restore_context) (struct sigcontext *);
- struct sigcontext ctx;
+ static void (*restore_context) (MonoContext *);
+ MonoContext ctx;
if (!restore_context)
restore_context = mono_arch_get_restore_context ();
/* Pop argument and return address */
- ctx.SC_ESP = esp + (2 * sizeof (gpointer));
- ctx.SC_EIP = eip;
- ctx.SC_EBP = ebp;
- ctx.SC_EDI = edi;
- ctx.SC_ESI = esi;
- ctx.SC_EBX = ebx;
- ctx.SC_EDX = edx;
- ctx.SC_ECX = ecx;
- ctx.SC_EAX = eax;
+ ctx.esp = esp + (2 * sizeof (gpointer));
+ ctx.eip = eip;
+ ctx.ebp = ebp;
+ ctx.edi = edi;
+ ctx.esi = esi;
+ ctx.ebx = ebx;
+ ctx.edx = edx;
+ ctx.ecx = ecx;
+ ctx.eax = eax;
if (mono_debugger_throw_exception ((gpointer)(eip - 5), (gpointer)esp, exc)) {
/*
* By the time we get here, it already inserted a breakpoint on
* eip - 5 (which is the address of the call).
*/
- ctx.SC_EIP = eip - 5;
- ctx.SC_ESP = esp + sizeof (gpointer);
+ ctx.eip = eip - 5;
+ ctx.esp = esp + sizeof (gpointer);
restore_context (&ctx);
g_assert_not_reached ();
}
/* adjust eip so that it point into the call instruction */
- ctx.SC_EIP -= 1;
+ ctx.eip -= 1;
if (mono_object_isinst (exc, mono_defaults.exception_class)) {
MonoException *mono_ex = (MonoException*)exc;
{
guint8 *start, *code;
- start = code = g_malloc (64);
+ start = code = mono_global_codeman_reserve (64);
x86_push_reg (code, X86_ESP);
x86_push_membase (code, X86_ESP, 4); /* IP */
*
* Returns a function pointer which can be used to raise
* corlib exceptions. The returned function has the following
- * signature: void (*func) (char *exc_name);
+ * signature: void (*func) (gpointer ip, char *exc_name);
* For example to raise an arithmetic exception you can use:
*
* x86_push_imm (code, "ArithmeticException");
- * x86_call_code (code, arch_get_throw_exception_by_name ());
+ * x86_push_imm (code, <IP>)
+ * x86_jump_code (code, arch_get_throw_exception_by_name ());
*
*/
gpointer
mono_arch_get_throw_exception_by_name (void)
{
- static guint8 start [32];
+ static guint8* start;
static int inited = 0;
guint8 *code;
return start;
inited = 1;
- code = start;
+ code = start = mono_global_codeman_reserve (32);
x86_push_membase (code, X86_ESP, 4); /* exception name */
x86_push_imm (code, "System");
return start;
}
+/**
+ * mono_arch_get_throw_corlib_exception:
+ *
+ * Returns a function pointer which can be used to raise
+ * corlib exceptions. The returned function has the following
+ * signature: void (*func) (guint32 ex_token, guint32 offset);
+ * Here, offset is the offset which needs to be substracted from the caller IP
+ * to get the IP of the throw. Passing the offset has the advantage that it
+ * needs no relocations in the caller.
+ */
+gpointer
+mono_arch_get_throw_corlib_exception (void)
+{
+ static guint8* start;
+ static int inited = 0;
+ guint8 *code;
+
+ if (inited)
+ return start;
+
+ inited = 1;
+ code = start = mono_global_codeman_reserve (64);
+
+ x86_push_membase (code, X86_ESP, 4); /* token */
+ 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);
+ /* Compute caller ip */
+ x86_pop_reg (code, X86_ECX);
+ /* Pop token */
+ x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
+ x86_pop_reg (code, X86_EDX);
+ x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
+ /* Push exception object */
+ x86_push_reg (code, X86_EAX);
+ /* Push throw IP */
+ x86_push_reg (code, X86_ECX);
+ x86_jump_code (code, mono_arch_get_throw_exception ());
+
+ g_assert ((code - start) < 64);
+
+ return start;
+}
+
/* mono_arch_find_jit_info:
*
* This function is used to gather information from @ctx. It return the
* code, since otherwise the lmf was already popped of the stack.
*/
if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
- new_ctx->SC_ESI = (*lmf)->esi;
- new_ctx->SC_EDI = (*lmf)->edi;
- new_ctx->SC_EBX = (*lmf)->ebx;
+ new_ctx->esi = (*lmf)->esi;
+ new_ctx->edi = (*lmf)->edi;
+ new_ctx->ebx = (*lmf)->ebx;
}
}
else {
offset = -1;
/* restore caller saved registers */
if (ji->used_regs & X86_EBX_MASK) {
- new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
+ new_ctx->ebx = *((int *)ctx->ebp + offset);
offset--;
}
if (ji->used_regs & X86_EDI_MASK) {
- new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
+ new_ctx->edi = *((int *)ctx->ebp + offset);
offset--;
}
if (ji->used_regs & X86_ESI_MASK) {
- new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
+ new_ctx->esi = *((int *)ctx->ebp + offset);
}
}
}
/* Pop EBP and the return address */
- new_ctx->SC_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->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
- new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
+ new_ctx->eip = *((int *)ctx->ebp + 1) - 1;
+ new_ctx->ebp = *((int *)ctx->ebp);
/* Pop arguments off the stack */
{
- MonoJitArgumentInfo *arg_info = alloca (sizeof (MonoJitArgumentInfo) * (ji->method->signature->param_count + 1));
+ MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
- guint32 stack_to_pop = mono_arch_get_argument_info (ji->method->signature, ji->method->signature->param_count, arg_info);
- new_ctx->SC_ESP += stack_to_pop;
+ guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
+ new_ctx->esp += stack_to_pop;
}
- *res = *ji;
- return res;
-#ifdef MONO_USE_EXC_TABLES
- } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
- *res = *ji;
- return res;
-#endif
+ return ji;
} else if (*lmf) {
*new_ctx = *ctx;
return (gpointer)-1;
if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
- *res = *ji;
} else {
memset (res, 0, sizeof (MonoJitInfo));
res->method = (*lmf)->method;
}
- new_ctx->SC_ESI = (*lmf)->esi;
- new_ctx->SC_EDI = (*lmf)->edi;
- new_ctx->SC_EBX = (*lmf)->ebx;
- new_ctx->SC_EBP = (*lmf)->ebp;
- new_ctx->SC_EIP = (*lmf)->eip;
+ new_ctx->esi = (*lmf)->esi;
+ new_ctx->edi = (*lmf)->edi;
+ 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->SC_ESP = (unsigned long)&((*lmf)->eip);
+ new_ctx->esp = (unsigned long)&((*lmf)->eip);
*lmf = (*lmf)->previous_lmf;
- return res;
-
+ return ji ? ji : res;
}
return NULL;
}
-/**
- * mono_arch_handle_exception:
- *
- * @ctx: saved processor state
- * @obj: the exception object
- */
-gboolean
-mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
+#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)
{
- return mono_handle_exception (sigctx, obj, mono_arch_ip_from_context (sigctx), test_only);
+#ifdef MONO_ARCH_USE_SIGACTION
+ ucontext_t *ctx = (ucontext_t*)sigctx;
+
+ 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;
+
+ 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
+ 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
+}
+
gpointer
mono_arch_ip_from_context (void *sigctx)
{
+#ifdef MONO_ARCH_USE_SIGACTION
+ ucontext_t *ctx = (ucontext_t*)sigctx;
+ return (gpointer)UCONTEXT_REG_EIP (ctx);
+#else
struct sigcontext *ctx = sigctx;
return (gpointer)ctx->SC_EIP;
+#endif
}
+gboolean
+mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
+{
+ MonoContext mctx;
+
+ mono_arch_sigctx_to_monoctx (sigctx, &mctx);
+
+ mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
+
+ mono_arch_monoctx_to_sigctx (&mctx, sigctx);
+
+ return TRUE;
+}