X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fexceptions-x86.c;h=732614236cae0d1ddb0dcb0a2057b2cfecd1d4fb;hb=9c7daac28fa91e15a0728d6eeffd8b1a2c038acf;hp=c63f05cdc1541cad420d28cdc43eb2a497f0bd51;hpb=5bbfa8860b090e465a3aa45edeb9c94481ef1a22;p=mono.git diff --git a/mono/mini/exceptions-x86.c b/mono/mini/exceptions-x86.c index c63f05cdc15..732614236ca 100644 --- a/mono/mini/exceptions-x86.c +++ b/mono/mini/exceptions-x86.c @@ -20,11 +20,14 @@ #include #include #include +#include #include "mini.h" #include "mini-x86.h" +#include "tasklets.h" +#include "debug-mini.h" -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 static void (*restore_stack) (void *); static MonoW32ExceptionHandler fpe_handler; @@ -34,7 +37,7 @@ static MonoW32ExceptionHandler segv_handler; static LPTOP_LEVEL_EXCEPTION_FILTER old_handler; #define W32_SEH_HANDLE_EX(_ex) \ - if (_ex##_handler) _ex##_handler((int)sctx) + if (_ex##_handler) _ex##_handler(0, er, sctx) /* * mono_win32_get_handle_stackoverflow (void): @@ -100,21 +103,22 @@ mono_win32_get_handle_stackoverflow (void) static void win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx) { - SYSTEM_INFO si; - DWORD page_size; + SYSTEM_INFO si; + DWORD page_size; MonoDomain *domain = mono_domain_get (); - MonoJitInfo *ji, rji; + MonoJitInfo rji; MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); MonoLMF *lmf = jit_tls->lmf; MonoContext initial_ctx; MonoContext ctx; guint32 free_stack = 0; + StackFrameInfo frame; /* 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); + GetSystemInfo(&si); page_size = si.dwPageSize; /* Let's walk the stack to recover @@ -129,19 +133,19 @@ win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx) do { MonoContext new_ctx; - ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, NULL, &lmf, NULL, NULL); - if (!ji) { + mono_arch_find_jit_info_ext (domain, jit_tls, &rji, &ctx, &new_ctx, &lmf, &frame); + if (!frame.ji) { g_warning ("Exception inside function without unwind info"); g_assert_not_reached (); } - if (ji != (gpointer)-1) { + if (frame.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); + } while (free_stack < 64 * 1024 && frame.ji != (gpointer) -1); /* convert into sigcontext to be used in mono_arch_handle_exception */ mono_arch_monoctx_to_sigctx (&ctx, sctx); @@ -213,6 +217,8 @@ LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep) ctx->Edi = sctx->edi; ctx->Eip = sctx->eip; + g_free (sctx); + return res; } @@ -247,7 +253,7 @@ void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler) } } -#endif /* PLATFORM_WIN32 */ +#endif /* TARGET_WIN32 */ /* * mono_arch_get_restore_context: @@ -264,7 +270,7 @@ mono_arch_get_restore_context (void) return start; /* restore_contect (MonoContext *ctx) */ - /* we do not restore X86_EAX, X86_EDX */ + /* we do not restore X86_EDX */ start = code = mono_global_codeman_reserve (128); @@ -283,6 +289,8 @@ mono_arch_get_restore_context (void) 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 (MonoContext, ebp), 4); + /* restore EAX */ + x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoContext, eax), 4); /* jump to the saved IP */ x86_jump_reg (code, X86_EDX); @@ -331,9 +339,19 @@ mono_arch_get_call_filter (void) 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); + /* align stack and save ESP */ + x86_mov_reg_reg (code, X86_EDX, X86_ESP, 4); + x86_alu_reg_imm (code, X86_AND, X86_ESP, -MONO_ARCH_FRAME_ALIGNMENT); + g_assert (MONO_ARCH_FRAME_ALIGNMENT >= 8); + x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 8); + x86_push_reg (code, X86_EDX); + /* call the handler */ x86_call_reg (code, X86_ECX); + /* restore ESP */ + x86_pop_reg (code, X86_ESP); + /* restore EBP */ x86_pop_reg (code, X86_EBP); @@ -348,10 +366,14 @@ mono_arch_get_call_filter (void) return start; } +/* + * mono_x86_throw_exception: + * + * C function called from the throw trampolines. + */ static void -throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx, - unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc, - unsigned long eip, unsigned long esp, gboolean rethrow) +mono_x86_throw_exception (mgreg_t *regs, MonoObject *exc, + mgreg_t eip, gboolean rethrow) { static void (*restore_context) (MonoContext *); MonoContext ctx; @@ -359,65 +381,168 @@ throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsign if (!restore_context) restore_context = mono_arch_get_restore_context (); - /* Pop argument and return address */ - ctx.esp = esp + (2 * sizeof (gpointer)); + ctx.esp = regs [X86_ESP]; 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)) { - /* - * The debugger wants us to stop on the `throw' instruction. - * By the time we get here, it already inserted a breakpoint on - * eip - 5 (which is the address of the call). - */ - 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.eip -= 1; + ctx.ebp = regs [X86_EBP]; + ctx.edi = regs [X86_EDI]; + ctx.esi = regs [X86_ESI]; + ctx.ebx = regs [X86_EBX]; + ctx.edx = regs [X86_EDX]; + ctx.ecx = regs [X86_ECX]; + ctx.eax = regs [X86_EAX]; + +#ifdef __APPLE__ + /* The OSX ABI specifies 16 byte alignment at call sites */ + g_assert ((ctx.esp % MONO_ARCH_FRAME_ALIGNMENT) == 0); +#endif if (mono_object_isinst (exc, mono_defaults.exception_class)) { MonoException *mono_ex = (MonoException*)exc; if (!rethrow) mono_ex->stack_trace = NULL; } + + if (mono_debug_using_mono_debugger ()) { + guint8 buf [16], *code; + + mono_breakpoint_clean_code (NULL, (gpointer)eip, 8, buf, sizeof (buf)); + code = buf + 8; + + if (buf [3] == 0xe8) { + MonoContext ctx_cp = ctx; + ctx_cp.eip = eip - 5; + + if (mono_debugger_handle_exception (&ctx_cp, exc)) { + restore_context (&ctx_cp); + g_assert_not_reached (); + } + } + } + + /* adjust eip so that it point into the call instruction */ + ctx.eip -= 1; + mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE); + restore_context (&ctx); g_assert_not_reached (); } +static void +mono_x86_throw_corlib_exception (mgreg_t *regs, guint32 ex_token_index, + mgreg_t eip, gint32 pc_offset) +{ + guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index; + MonoException *ex; + + ex = mono_exception_from_token (mono_defaults.exception_class->image, ex_token); + + eip -= pc_offset; + + mono_x86_throw_exception (regs, (MonoObject*)ex, eip, FALSE); +} + +/* + * get_throw_exception: + * + * Generate a call to mono_x86_throw_exception/ + * mono_x86_throw_corlib_exception. + * If LLVM is true, generate code which assumes the caller is LLVM generated code, + * which doesn't push the arguments. + */ static guint8* -get_throw_exception (gboolean rethrow) +get_throw_exception (const char *name, gboolean rethrow, gboolean llvm, gboolean corlib) { guint8 *start, *code; + GSList *unwind_ops = NULL; + int i, stack_size, stack_offset, arg_offsets [5], regs_offset; - start = code = mono_global_codeman_reserve (64); + start = code = mono_global_codeman_reserve (128); - x86_push_reg (code, X86_ESP); - x86_push_membase (code, X86_ESP, 4); /* IP */ - x86_push_membase (code, X86_ESP, 12); /* exception */ - x86_push_reg (code, X86_EBP); - x86_push_reg (code, X86_EDI); - x86_push_reg (code, X86_ESI); - x86_push_reg (code, X86_EBX); - x86_push_reg (code, X86_EDX); - x86_push_reg (code, X86_ECX); - x86_push_reg (code, X86_EAX); - x86_call_code (code, throw_exception); - /* we should never reach this breakpoint */ + stack_size = 128; + + /* + * On apple, the stack is misaligned by the pushing of the return address. + */ + if (!llvm && corlib) + /* On OSX, we don't generate alignment code to save space */ + stack_size += 4; + else + stack_size += MONO_ARCH_FRAME_ALIGNMENT - 4; + + /* + * The stack looks like this: + * (only if corlib is TRUE) + * / + * <- esp (unaligned on apple) + */ + + mono_add_unwind_op_def_cfa (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_ESP, 4); + mono_add_unwind_op_offset (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_NREG, -4); + + /* Alloc frame */ + x86_alu_reg_imm (code, X86_SUB, X86_ESP, stack_size); + mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 4); + + arg_offsets [0] = 0; + arg_offsets [1] = 4; + arg_offsets [2] = 8; + arg_offsets [3] = 12; + regs_offset = 16; + + /* Save registers */ + for (i = 0; i < X86_NREG; ++i) + if (i != X86_ESP) + x86_mov_membase_reg (code, X86_ESP, regs_offset + (i * 4), i, 4); + /* Calculate the offset between the current sp and the sp of the caller */ + if (llvm) { + /* LLVM doesn't push the arguments */ + stack_offset = stack_size + 4; + } else { + if (corlib) { + /* Two arguments */ + stack_offset = stack_size + 4 + 8; +#ifdef __APPLE__ + /* We don't generate stack alignment code on osx to save space */ +#endif + } else { + /* One argument */ + stack_offset = stack_size + 4 + 4; +#ifdef __APPLE__ + /* Pop the alignment added by OP_THROW too */ + stack_offset += MONO_ARCH_FRAME_ALIGNMENT - 4; +#endif + } + } + /* Save ESP */ + x86_lea_membase (code, X86_EAX, X86_ESP, stack_offset); + x86_mov_membase_reg (code, X86_ESP, regs_offset + (X86_ESP * 4), X86_EAX, 4); + + /* Set arg1 == regs */ + x86_lea_membase (code, X86_EAX, X86_ESP, regs_offset); + x86_mov_membase_reg (code, X86_ESP, arg_offsets [0], X86_EAX, 4); + /* Set arg2 == exc */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 4, 4); + x86_mov_membase_reg (code, X86_ESP, arg_offsets [1], X86_EAX, 4); + /* Set arg3 == eip */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size, 4); + x86_mov_membase_reg (code, X86_ESP, arg_offsets [2], X86_EAX, 4); + if (corlib) { + /* Set arg4 == offset */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 8, 4); + x86_mov_membase_reg (code, X86_ESP, arg_offsets [3], X86_EAX, 4); + } else { + /* Set arg4 == rethrow */ + x86_mov_membase_imm (code, X86_ESP, arg_offsets [3], rethrow, 4); + } + /* Make the call */ + x86_call_code (code, corlib ? (gpointer)mono_x86_throw_corlib_exception : (gpointer)mono_x86_throw_exception); x86_breakpoint (code); - g_assert ((code - start) < 64); + g_assert ((code - start) < 128); + + mono_save_trampoline_xdebug_info (corlib ? "llvm_throw_corlib_exception_trampoline" : "llvm_throw_exception_trampoline", start, code - start, unwind_ops); return start; } @@ -437,73 +562,13 @@ get_throw_exception (gboolean rethrow) gpointer mono_arch_get_throw_exception (void) { - static guint8 *start; - static int inited = 0; - - if (inited) - return start; - - start = get_throw_exception (FALSE); - - inited = 1; - - return start; + return get_throw_exception ("throw_exception_trampoline", FALSE, FALSE, FALSE); } gpointer mono_arch_get_rethrow_exception (void) { - static guint8 *start; - static int inited = 0; - - if (inited) - return start; - - start = get_throw_exception (TRUE); - - inited = 1; - - return start; -} - -/** - * mono_arch_get_throw_exception_by_name: - * - * Returns a function pointer which can be used to raise - * corlib exceptions. The returned function has the following - * signature: void (*func) (gpointer ip, char *exc_name); - * For example to raise an arithmetic exception you can use: - * - * x86_push_imm (code, "ArithmeticException"); - * x86_push_imm (code, ) - * x86_jump_code (code, arch_get_throw_exception_by_name ()); - * - */ -gpointer -mono_arch_get_throw_exception_by_name (void) -{ - static guint8* start; - static int inited = 0; - guint8 *code; - - if (inited) - return start; - - inited = 1; - code = start = mono_global_codeman_reserve (32); - - x86_push_membase (code, X86_ESP, 4); /* exception name */ - x86_push_imm (code, "System"); - x86_push_imm (code, mono_defaults.exception_class->image); - x86_call_code (code, mono_exception_from_name); - x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12); - /* save the newly create object (overwrite exception name)*/ - x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4); - x86_jump_code (code, mono_arch_get_throw_exception ()); - - g_assert ((code - start) < 32); - - return start; + return get_throw_exception ("rethrow_exception_trampoline", TRUE, FALSE, FALSE); } /** @@ -519,135 +584,136 @@ mono_arch_get_throw_exception_by_name (void) gpointer mono_arch_get_throw_corlib_exception (void) { - static guint8* start; - static int inited = 0; - guint8 *code; + return get_throw_exception ("throw_corlib_exception_trampoline", FALSE, FALSE, TRUE); +} - if (inited) - return start; +void +mono_arch_exceptions_init (void) +{ + guint8 *tramp; - 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 ()); + /* LLVM needs different throw trampolines */ + tramp = get_throw_exception ("llvm_throw_exception_trampoline", FALSE, TRUE, FALSE); - g_assert ((code - start) < 64); + mono_register_jit_icall (tramp, "mono_arch_llvm_throw_exception", NULL, TRUE); - return start; + tramp = get_throw_exception ("llvm_throw_corlib_exception_trampoline", FALSE, TRUE, TRUE); + + mono_register_jit_icall (tramp, "mono_arch_llvm_throw_corlib_exception", NULL, TRUE); } -/* mono_arch_find_jit_info: +/* + * mono_arch_find_jit_info_ext: * - * This function is used to gather information from @ctx. It return the - * MonoJitInfo of the corresponding function, unwinds one stack frame and - * stores the resulting context into @new_ctx. It also stores a string - * describing the stack location into @trace (if not NULL), and modifies - * the @lmf if necessary. @native_offset return the IP offset from the - * start of the function or -1 if that info is not available. + * See exceptions-amd64.c for docs. */ -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) +gboolean +mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, + MonoJitInfo *ji, MonoContext *ctx, + MonoContext *new_ctx, MonoLMF **lmf, + StackFrameInfo *frame) { - MonoJitInfo *ji; gpointer ip = MONO_CONTEXT_GET_IP (ctx); - /* Avoid costly table lookup during stack overflow */ - if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size))) - ji = prev_ji; - else - ji = mono_jit_info_table_find (domain, ip); + memset (frame, 0, sizeof (StackFrameInfo)); + frame->ji = ji; + frame->managed = FALSE; - if (managed) - *managed = FALSE; + *new_ctx = *ctx; if (ji != NULL) { - int offset; - - *new_ctx = *ctx; - - if (managed) - if (!ji->method->wrapper_type) - *managed = TRUE; - - /* - * Some managed methods like pinvoke wrappers might have save_lmf set. - * In this case, register save/restore code is not generated by the - * JIT, so we have to restore callee saved registers from the lmf. - */ - if (ji->method->save_lmf) { - /* - * We only need to do this if the exception was raised in managed - * code, since otherwise the lmf was already popped of the stack. - */ - if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { - 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->ebx = *((int *)ctx->ebp + offset); - offset--; - } - if (ji->used_regs & X86_EDI_MASK) { - new_ctx->edi = *((int *)ctx->ebp + offset); - offset--; - } - if (ji->used_regs & X86_ESI_MASK) { - new_ctx->esi = *((int *)ctx->ebp + offset); - } - } + gssize regs [MONO_MAX_IREGS + 1]; + guint8 *cfa; + guint32 unwind_info_len; + guint8 *unwind_info; + + frame->type = FRAME_TYPE_MANAGED; + + if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) + frame->managed = TRUE; + + if (ji->from_aot) + unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len); + else + unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len); + + regs [X86_EAX] = new_ctx->eax; + regs [X86_EBX] = new_ctx->ebx; + regs [X86_ECX] = new_ctx->ecx; + regs [X86_EDX] = new_ctx->edx; + regs [X86_ESP] = new_ctx->esp; + regs [X86_EBP] = new_ctx->ebp; + regs [X86_ESI] = new_ctx->esi; + regs [X86_EDI] = new_ctx->edi; + regs [X86_NREG] = new_ctx->eip; + + mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, + (guint8*)ji->code_start + ji->code_size, + ip, regs, MONO_MAX_IREGS + 1, &cfa); + + new_ctx->eax = regs [X86_EAX]; + new_ctx->ebx = regs [X86_EBX]; + new_ctx->ecx = regs [X86_ECX]; + new_ctx->edx = regs [X86_EDX]; + new_ctx->esp = regs [X86_ESP]; + new_ctx->ebp = regs [X86_EBP]; + new_ctx->esi = regs [X86_ESI]; + new_ctx->edi = regs [X86_EDI]; + new_ctx->eip = regs [X86_NREG]; + + /* The CFA becomes the new SP value */ + new_ctx->esp = (gssize)cfa; + + /* Adjust IP */ + new_ctx->eip --; if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { /* remove any unused lmf */ - *lmf = (*lmf)->previous_lmf; + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); } - /* Pop EBP and the return address */ - 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); - /* Pop arguments off the stack */ + /* + * FIXME: LLVM doesn't push these, we can't use ji->from_llvm as it describes + * the caller. + */ +#ifndef ENABLE_LLVM { MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1); 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; } +#endif - return ji; + return TRUE; } else if (*lmf) { - - *new_ctx = *ctx; - if (!(*lmf)->method) - return (gpointer)-1; + 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); - if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) { + g_assert (ext->debugger_invoke); + + memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); + + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); + + frame->type = FRAME_TYPE_DEBUGGER_INVOKE; + + return TRUE; + } + + if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) { } else { - memset (res, 0, sizeof (MonoJitInfo)); - res->method = (*lmf)->method; + if (!((guint32)((*lmf)->previous_lmf) & 1)) + /* Top LMF entry */ + return FALSE; + /* Trampoline lmf frame */ + frame->method = (*lmf)->method; } new_ctx->esi = (*lmf)->esi; @@ -655,16 +721,39 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf 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; + frame->ji = ji; + frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; + + /* 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 */ +#ifndef ENABLE_LLVM + 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; + } +#endif + } + 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)(((gsize)(*lmf)->previous_lmf) & ~3); - return ji ? ji : res; + return TRUE; } - return NULL; + return FALSE; } #ifdef __sun @@ -758,9 +847,157 @@ mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only) mono_arch_sigctx_to_monoctx (sigctx, &mctx); + if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) + return TRUE; + mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only); mono_arch_monoctx_to_sigctx (&mctx, sigctx); 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); + + if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) { + if (stack_ovf) + prepare_for_guard_pages (&mctx); + restore_context (&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 = mini_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx), NULL); + 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 = mini_jit_info_table_find (mono_domain_get (), (gpointer)sp [0], NULL); + 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 +} + +#if MONO_SUPPORT_TASKLETS +MonoContinuationRestore +mono_tasklets_arch_restore (void) +{ + static guint8* saved = NULL; + guint8 *code, *start; + + if (saved) + return (MonoContinuationRestore)saved; + code = start = mono_global_codeman_reserve (48); + /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */ + /* put cont in edx */ + x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4, 4); + /* setup the copy of the stack */ + x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, stack_used_size), 4); + x86_shift_reg_imm (code, X86_SHR, X86_ECX, 2); + x86_cld (code); + x86_mov_reg_membase (code, X86_ESI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, saved_stack), 4); + x86_mov_reg_membase (code, X86_EDI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_sp), 4); + x86_prefix (code, X86_REP_PREFIX); + x86_movsl (code); + + /* now restore the registers from the LMF */ + x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, lmf), 4); + x86_mov_reg_membase (code, X86_EBX, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebx), 4); + x86_mov_reg_membase (code, X86_EBP, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebp), 4); + x86_mov_reg_membase (code, X86_ESI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, esi), 4); + x86_mov_reg_membase (code, X86_EDI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, edi), 4); + + /* restore the lmf chain */ + /*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4); + x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/ + + /* state in eax, so it's setup as the return value */ + x86_mov_reg_membase (code, X86_EAX, X86_ESP, 8, 4); + x86_jump_membase (code, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_ip)); + g_assert ((code - start) <= 48); + saved = start; + return (MonoContinuationRestore)saved; +} +#endif +