X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fexceptions-x86.c;h=f2d5eee568f9facc2bdf54308bd57c3fb9008ae0;hb=c2d511e567dd2d535056dc79b745f88b4fb64afa;hp=c63f05cdc1541cad420d28cdc43eb2a497f0bd51;hpb=fc4b07f20f9e79fe99d4b520bb5ff8b5e80b10f6;p=mono.git diff --git a/mono/mini/exceptions-x86.c b/mono/mini/exceptions-x86.c index c63f05cdc15..f2d5eee568f 100644 --- a/mono/mini/exceptions-x86.c +++ b/mono/mini/exceptions-x86.c @@ -20,9 +20,12 @@ #include #include #include +#include #include "mini.h" #include "mini-x86.h" +#include "tasklets.h" +#include "debug-mini.h" #ifdef PLATFORM_WIN32 static void (*restore_stack) (void *); @@ -129,7 +132,7 @@ 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); + 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 (); @@ -213,6 +216,8 @@ LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep) ctx->Edi = sctx->edi; ctx->Eip = sctx->eip; + g_free (sctx); + return res; } @@ -331,9 +336,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); @@ -370,26 +385,32 @@ throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsign 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; - 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); @@ -482,26 +503,15 @@ mono_arch_get_rethrow_exception (void) gpointer mono_arch_get_throw_exception_by_name (void) { - static guint8* start; - static int inited = 0; + guint8* start; guint8 *code; - if (inited) - return start; - - inited = 1; - code = start = mono_global_codeman_reserve (32); + start = code = 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 ()); + /* Not used */ + x86_breakpoint (code); - g_assert ((code - start) < 32); + mono_arch_flush_icache (start, code - start); return start; } @@ -529,7 +539,9 @@ mono_arch_get_throw_corlib_exception (void) 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); @@ -561,8 +573,7 @@ mono_arch_get_throw_corlib_exception (void) */ 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); @@ -576,58 +587,58 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf if (managed) *managed = FALSE; - if (ji != NULL) { - int offset; + *new_ctx = *ctx; - *new_ctx = *ctx; + if (ji != NULL) { + gssize regs [MONO_MAX_IREGS + 1]; + guint8 *cfa; + guint32 unwind_info_len; + guint8 *unwind_info; 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); - } - } + 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)(((guint32)(*lmf)->previous_lmf) & ~1); } - /* 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 */ { MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1); @@ -641,12 +652,13 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf *new_ctx = *ctx; - if (!(*lmf)->method) - return (gpointer)-1; - if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) { } else { - memset (res, 0, sizeof (MonoJitInfo)); + if (!((guint32)((*lmf)->previous_lmf) & 1)) + /* Top LMF entry */ + return (gpointer)-1; + /* Trampoline lmf frame */ + memset (res, 0, MONO_SIZEOF_JIT_INFO); res->method = (*lmf)->method; } @@ -655,11 +667,29 @@ 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; + /* 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; } @@ -758,9 +788,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 = 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 +} + +#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 +