X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fmini%2Fexceptions-arm.c;h=575654b67e3ca63724d4f5f70b7201fd356a9277;hb=d0771cfe0d6f833be7bdcc4f19369f38dba89a6a;hp=619f5b796172a65d4e319e42a1c1e68a58a29982;hpb=3276c5beecb49334e883c78a7bcbeface1c25ba3;p=mono.git diff --git a/mono/mini/exceptions-arm.c b/mono/mini/exceptions-arm.c index 619f5b79617..575654b67e3 100644 --- a/mono/mini/exceptions-arm.c +++ b/mono/mini/exceptions-arm.c @@ -12,9 +12,19 @@ #include #include #include + +#ifndef MONO_CROSS_COMPILE +#ifdef HAVE_ASM_SIGCONTEXT_H +#include +#endif /* def HAVE_ASM_SIGCONTEXT_H */ +#endif + +#ifdef HAVE_UCONTEXT_H #include +#endif /* def HAVE_UCONTEXT_H */ #include +#include #include #include #include @@ -24,88 +34,7 @@ #include "mini.h" #include "mini-arm.h" - -/* - -struct sigcontext { - unsigned long trap_no; - unsigned long error_code; - unsigned long oldmask; - unsigned long arm_r0; - unsigned long arm_r1; - unsigned long arm_r2; - unsigned long arm_r3; - unsigned long arm_r4; - unsigned long arm_r5; - unsigned long arm_r6; - unsigned long arm_r7; - unsigned long arm_r8; - unsigned long arm_r9; - unsigned long arm_r10; - unsigned long arm_fp; - unsigned long arm_ip; - unsigned long arm_sp; - unsigned long arm_lr; - unsigned long arm_pc; - unsigned long arm_cpsr; - unsigned long fault_address; -}; - -gregs below is this struct -struct user_regs { - unsigned long int uregs[18]; -}; - -the companion user_fpregs has just 8 double registers -(it's valid for FPA mode, will need changes for VFP) - -typedef struct { - gregset_t gregs; - fpregset_t fpregs; -} mcontext_t; - -typedef struct ucontext { - unsigned long int uc_flags; - struct ucontext *uc_link; - __sigset_t uc_sigmask; - stack_t uc_stack; - mcontext_t uc_mcontext; - long int uc_filler[5]; -} ucontext_t; - -*/ - -/* - * So, it turns out that the ucontext struct defined by libc is incorrect. - * We define our own version here and use it instead. - */ - -#if __APPLE__ -#define my_ucontext ucontext_t -#else -typedef struct my_ucontext { - unsigned long uc_flags; - struct my_ucontext *uc_link; - struct { - void *p; - int flags; - size_t size; - } sstack_data; - struct sigcontext sig_ctx; - /* some 2.6.x kernel has fp data here after a few other fields - * we don't use them for now... - */ -} my_ucontext; -#endif - -#define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do { \ - ARM_LDR_IMM (code, ip_reg, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip)); \ - ARM_ADD_REG_IMM8 (code, tmp_reg, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs)); \ - ARM_LDMIA (code, tmp_reg, MONO_ARM_REGSAVE_MASK); \ - } while (0) - -/* nothing to do */ -#define setup_context(ctx) +#include "mono/utils/mono-sigcontext.h" /* * arch_get_restore_context: @@ -114,22 +43,36 @@ typedef struct my_ucontext { * The first argument in r0 is the pointer to the context. */ gpointer -mono_arch_get_restore_context_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *code; guint8 *start; - - *ji = NULL; + int ctx_reg; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; start = code = mono_global_codeman_reserve (128); - restore_regs_from_context (ARMREG_R0, ARMREG_R1, ARMREG_R2); - /* restore also the stack pointer, FIXME: handle sp != fp */ - ARM_LDR_IMM (code, ARMREG_SP, ARMREG_R0, G_STRUCT_OFFSET (MonoContext, ebp)); - ARM_LDR_IMM (code, ARMREG_FP, ARMREG_R0, G_STRUCT_OFFSET (MonoContext, esp)); + /* + * Move things to their proper place so we can restore all the registers with + * one instruction. + */ + + ctx_reg = ARMREG_R0; + +#if defined(ARM_FPU_VFP) + ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, fregs)); + ARM_FLDMD (code, ARM_VFP_D0, 16, ARMREG_IP); +#endif + + /* move pc to PC */ + ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, pc)); + ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_PC * sizeof (mgreg_t))); + + /* restore everything */ + ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs)); + ARM_LDM (code, ARMREG_IP, 0xffff); - /* jump to the saved IP */ - ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1); /* never reached */ ARM_DBRK (code); @@ -137,7 +80,8 @@ mono_arch_get_restore_context_full (guint32 *code_size, MonoJumpInfo **ji, gbool mono_arch_flush_icache (start, code - start); - *code_size = code - start; + if (info) + *info = mono_tramp_info_create (g_strdup_printf ("restore_context"), start, code - start, ji, unwind_ops); return start; } @@ -150,12 +94,13 @@ mono_arch_get_restore_context_full (guint32 *code_size, MonoJumpInfo **ji, gbool * @exc object in this case). */ gpointer -mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { guint8 *code; guint8* start; - - *ji = NULL; + int ctx_reg; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */ start = code = mono_global_codeman_reserve (320); @@ -165,7 +110,10 @@ mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean ARM_PUSH (code, MONO_ARM_REGSAVE_MASK); /* restore all the regs from ctx (in r0), but not sp, the stack pointer */ - restore_regs_from_context (ARMREG_R0, ARMREG_IP, ARMREG_LR); + ctx_reg = ARMREG_R0; + ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, pc)); + ARM_ADD_REG_IMM8 (code, ARMREG_LR, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs) + (MONO_ARM_FIRST_SAVED_REG * sizeof (mgreg_t))); + ARM_LDM (code, ARMREG_LR, MONO_ARM_REGSAVE_MASK); /* call handler at eip (r1) and set the first arg with the exception (r2) */ ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2); ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); @@ -178,52 +126,71 @@ mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean mono_arch_flush_icache (start, code - start); - *code_size = code - start; + if (info) + *info = mono_tramp_info_create (g_strdup_printf ("call_filter"), start, code - start, ji, unwind_ops); return start; } void -mono_arm_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs) +mono_arm_throw_exception (MonoObject *exc, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs) { static void (*restore_context) (MonoContext *); MonoContext ctx; - gboolean rethrow = eip & 1; + gboolean rethrow = pc & 1; if (!restore_context) restore_context = mono_get_restore_context (); - eip &= ~1; /* clear the optional rethrow bit */ + pc &= ~1; /* clear the optional rethrow bit */ /* adjust eip so that it point into the call instruction */ - eip -= 4; - - setup_context (&ctx); + pc -= 4; /*printf ("stack in throw: %p\n", esp);*/ - MONO_CONTEXT_SET_BP (&ctx, esp); - MONO_CONTEXT_SET_SP (&ctx, esp); - MONO_CONTEXT_SET_IP (&ctx, eip); - memcpy (&ctx.regs, int_regs, sizeof (gulong) * 8); - /* memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS); */ + MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]); + MONO_CONTEXT_SET_SP (&ctx, sp); + MONO_CONTEXT_SET_IP (&ctx, pc); + memcpy (((guint8*)&ctx.regs) + (ARMREG_R4 * sizeof (mgreg_t)), int_regs, 8 * sizeof (mgreg_t)); + memcpy (&ctx.fregs, fp_regs, sizeof (double) * 16); if (mono_object_isinst (exc, mono_defaults.exception_class)) { MonoException *mono_ex = (MonoException*)exc; if (!rethrow) mono_ex->stack_trace = NULL; } - mono_handle_exception (&ctx, exc, (gpointer)(eip + 4), FALSE); + mono_handle_exception (&ctx, exc); restore_context (&ctx); g_assert_not_reached (); } void -mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs) +mono_arm_throw_exception_by_token (guint32 type_token, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs) +{ + /* Clear thumb bit */ + pc &= ~1; + + mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), pc, sp, int_regs, fp_regs); +} + +void +mono_arm_resume_unwind (guint32 dummy1, mgreg_t pc, mgreg_t sp, mgreg_t *int_regs, gdouble *fp_regs) { - mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), eip, esp, int_regs, fp_regs); + MonoContext ctx; + + pc &= ~1; /* clear the optional rethrow bit */ + /* adjust eip so that it point into the call instruction */ + pc -= 4; + + MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]); + MONO_CONTEXT_SET_SP (&ctx, sp); + MONO_CONTEXT_SET_IP (&ctx, pc); + memcpy (((guint8*)&ctx.regs) + (ARMREG_R4 * sizeof (mgreg_t)), int_regs, 8 * sizeof (mgreg_t)); + + mono_resume_unwind (&ctx); } /** - * arch_get_throw_exception_generic: + * get_throw_trampoline: * * Returns a function pointer which can be used to raise * exceptions. The returned function has the following @@ -232,45 +199,77 @@ mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsign * */ static gpointer -mono_arch_get_throw_exception_generic (int size, int by_token, gboolean rethrow, guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot) { guint8 *start; guint8 *code; - - *ji = NULL; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; + int cfa_offset; code = start = mono_global_codeman_reserve (size); + mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, 0); + /* save all the regs on the stack */ ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP); ARM_PUSH (code, MONO_ARM_REGSAVE_MASK); + cfa_offset = MONO_ARM_NUM_SAVED_REGS * sizeof (mgreg_t); + mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, cfa_offset); + mono_add_unwind_op_offset (unwind_ops, code, start, ARMREG_LR, - sizeof (mgreg_t)); + + /* Save fp regs */ + ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (double) * 16); + cfa_offset += sizeof (double) * 16; + mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, cfa_offset); +#if defined(ARM_FPU_VFP) + ARM_FSTMD (code, ARM_VFP_D0, 16, ARMREG_SP); +#endif + + /* Param area */ + ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8); + cfa_offset += 8; + mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, cfa_offset); + /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */ /* caller sp */ - ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, 10 * 4); /* 10 saved regs */ + ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, cfa_offset); /* exc is already in place in r0 */ - if (by_token) { + if (corlib) { /* The caller ip is already in R1 */ + if (llvm) + /* Negate the ip adjustment done in mono_arm_throw_exception */ + ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4); } else { ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */ } - /* FIXME: pointer to the saved fp regs */ - /*pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS; - ppc_addi (code, ppc_r7, ppc_sp, pos);*/ - /* pointer to the saved int regs */ - ARM_MOV_REG_REG (code, ARMREG_R3, ARMREG_SP); /* the pushed regs */ - /* we encode rethrow in the ip, so we avoid args on the stack */ + /* int regs */ + ARM_ADD_REG_IMM8 (code, ARMREG_R3, ARMREG_SP, (cfa_offset - (MONO_ARM_NUM_SAVED_REGS * sizeof (mgreg_t)))); + /* we encode rethrow in the ip */ ARM_ORR_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, rethrow); + /* fp regs */ + ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_SP, 8); + ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, 0); if (aot) { - *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, by_token ? "mono_arm_throw_exception_by_token" : "mono_arm_throw_exception"); + const char *icall_name; + + if (resume_unwind) + icall_name = "mono_arm_resume_unwind"; + else if (corlib) + icall_name = "mono_arm_throw_exception_by_token"; + else + icall_name = "mono_arm_throw_exception"; + + ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name); ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); ARM_B (code, 0); *(gpointer*)(gpointer)code = NULL; code += 4; ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP); } else { - code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (by_token ? (gpointer)mono_arm_throw_exception_by_token : (gpointer)mono_arm_throw_exception)); + code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (resume_unwind ? (gpointer)mono_arm_resume_unwind : (corlib ? (gpointer)mono_arm_throw_exception_by_token : (gpointer)mono_arm_throw_exception))); } ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP); @@ -279,25 +278,12 @@ mono_arch_get_throw_exception_generic (int size, int by_token, gboolean rethrow, g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); - *code_size = code - start; + if (info) + *info = mono_tramp_info_create (g_strdup_printf (tramp_name), start, code - start, ji, unwind_ops); return start; } -/** - * mono_arch_get_rethrow_exception: - * - * Returns a function pointer which can be used to rethrow - * exceptions. The returned function has the following - * signature: void (*func) (MonoException *exc); - * - */ -gpointer -mono_arch_get_rethrow_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) -{ - return mono_arch_get_throw_exception_generic (132, FALSE, TRUE, code_size, ji, aot); -} - /** * arch_get_throw_exception: * @@ -311,27 +297,23 @@ mono_arch_get_rethrow_exception_full (guint32 *code_size, MonoJumpInfo **ji, gbo * */ gpointer -mono_arch_get_throw_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot) { - return mono_arch_get_throw_exception_generic (132, FALSE, FALSE, code_size, ji, aot); + return get_throw_trampoline (132, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot); } -gpointer -mono_arch_get_throw_exception_by_name_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) -{ - guint8* start; - guint8 *code; - - *ji = NULL; - - start = code = mono_global_codeman_reserve (64); - - /* Not used on ARM */ - ARM_DBRK (code); - - *code_size = code - start; - - return start; +/** + * mono_arch_get_rethrow_exception: + * + * Returns a function pointer which can be used to rethrow + * exceptions. The returned function has the following + * signature: void (*func) (MonoException *exc); + * + */ +gpointer +mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot) +{ + return get_throw_trampoline (132, FALSE, TRUE, FALSE, FALSE, "rethrow_exception", info, aot); } /** @@ -346,192 +328,315 @@ mono_arch_get_throw_exception_by_name_full (guint32 *code_size, MonoJumpInfo **j * On ARM, the ip is passed instead of an offset. */ gpointer -mono_arch_get_throw_corlib_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) +mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot) { - return mono_arch_get_throw_exception_generic (168, TRUE, FALSE, code_size, ji, aot); + return get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "throw_corlib_exception", info, aot); } -/* mono_arch_find_jit_info: +GSList* +mono_arm_get_exception_trampolines (gboolean aot) +{ + MonoTrampInfo *info; + GSList *tramps = NULL; + + /* LLVM uses the normal trampolines, but with a different name */ + get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", &info, aot); + tramps = g_slist_prepend (tramps, info); + + get_throw_trampoline (168, TRUE, FALSE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", &info, aot); + tramps = g_slist_prepend (tramps, info); + + get_throw_trampoline (168, FALSE, FALSE, FALSE, TRUE, "llvm_resume_unwind_trampoline", &info, aot); + tramps = g_slist_prepend (tramps, info); + + return tramps; +} + +void +mono_arch_exceptions_init (void) +{ + guint8 *tramp; + GSList *tramps, *l; + + if (mono_aot_only) { + tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline"); + mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE); + tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline"); + mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE); + tramp = mono_aot_get_trampoline ("llvm_resume_unwind_trampoline"); + mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE); + } else { + tramps = mono_arm_get_exception_trampolines (FALSE); + for (l = tramps; l; l = l->next) { + MonoTrampInfo *info = l->data; + + mono_register_jit_icall (info->code, g_strdup (info->name), NULL, TRUE); + mono_save_trampoline_xdebug_info (info); + mono_tramp_info_free (info); + } + g_slist_free (tramps); + } +} + +/* + * mono_arch_find_jit_info: * - * 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, MonoLMF **lmf, gboolean *managed) +gboolean +mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, + MonoJitInfo *ji, MonoContext *ctx, + MonoContext *new_ctx, MonoLMF **lmf, + mgreg_t **save_locations, + 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; - if (managed) - *managed = FALSE; + *new_ctx = *ctx; if (ji != NULL) { - int offset; + int i; + gssize regs [MONO_MAX_IREGS + 1]; + guint8 *cfa; + guint32 unwind_info_len; + guint8 *unwind_info; - *new_ctx = *ctx; + frame->type = FRAME_TYPE_MANAGED; - if (managed) - if (!ji->method->wrapper_type) - *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); - /* - * 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)) { - memcpy (&new_ctx->regs [0], &(*lmf)->iregs [4], sizeof (gulong) * MONO_SAVED_GREGS); - } - new_ctx->esp = (*lmf)->iregs [12]; - new_ctx->eip = (*lmf)->iregs [13]; - new_ctx->ebp = new_ctx->esp; - } else { - int i; - char* sp; - offset = ji->used_regs >> 16; - offset <<= 2; - /* the saved regs are at sp + offset */ - /* restore caller saved registers */ - sp = (char*)ctx->ebp; - sp += offset; - for (i = 4; i < 16; ++i) { - if (ji->used_regs & (1 << i)) { - new_ctx->regs [i - 4] = *(gulong*)sp; - sp += sizeof (gulong); - } - } - /* IP and LR */ - new_ctx->esp = *(gulong*)sp; - sp += sizeof (gulong); - new_ctx->eip = *(gulong*)sp; - sp += sizeof (gulong); - new_ctx->ebp = new_ctx->esp; - /* Needed by get_exception_catch_class () */ - new_ctx->regs [ARMREG_R11] = new_ctx->esp; - } + for (i = 0; i < 16; ++i) + regs [i] = new_ctx->regs [i]; - if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { + mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, + (guint8*)ji->code_start + ji->code_size, + ip, regs, MONO_MAX_IREGS, + save_locations, MONO_MAX_IREGS, &cfa); + + for (i = 0; i < 16; ++i) + new_ctx->regs [i] = regs [i]; + new_ctx->pc = regs [ARMREG_LR]; + new_ctx->regs [ARMREG_SP] = (gsize)cfa; + + if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->sp)) { /* remove any unused lmf */ - *lmf = (*lmf)->previous_lmf; + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); } + /* Clear thumb bit */ + new_ctx->pc &= ~1; + /* we substract 1, so that the IP points into the call instruction */ - new_ctx->eip--; + new_ctx->pc--; - return ji; + return TRUE; } else if (*lmf) { - - *new_ctx = *ctx; - if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) { + if (((gsize)(*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)); + + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); + + frame->type = FRAME_TYPE_DEBUGGER_INVOKE; + + return TRUE; + } + + frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; + + if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) { + frame->ji = ji; } else { if (!(*lmf)->method) - return (gpointer)-1; - memset (res, 0, sizeof (MonoJitInfo)); - res->method = (*lmf)->method; + return FALSE; + frame->method = (*lmf)->method; } - memcpy (&new_ctx->regs [0], &(*lmf)->iregs [4], sizeof (gulong) * MONO_SAVED_GREGS); - new_ctx->esp = (*lmf)->iregs [12]; - new_ctx->eip = (*lmf)->iregs [13]; - new_ctx->ebp = new_ctx->esp; + /* + * The LMF is saved at the start of the method using: + * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP) + * ARM_PUSH (code, 0x5ff0); + * So it stores the register state as it existed at the caller. We need to + * produce the register state which existed at the time of the call which + * transitioned to native call, so we save the sp/fp/ip in the LMF. + */ + memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (mgreg_t) * 13); + new_ctx->pc = (*lmf)->ip; + new_ctx->regs [ARMREG_SP] = (*lmf)->sp; + new_ctx->regs [ARMREG_FP] = (*lmf)->fp; + + /* Clear thumb bit */ + new_ctx->pc &= ~1; - *lmf = (*lmf)->previous_lmf; + /* we substract 1, so that the IP points into the call instruction */ + new_ctx->pc--; - return ji ? ji : res; + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); + + return TRUE; } - return NULL; + return FALSE; } +#if MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX void mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx) { -#if BROKEN_LINUX - struct ucontext *uc = sigctx; - - mctx->eip = uc->uc_mcontext.gregs [ARMREG_PC]; - mctx->ebp = uc->uc_mcontext.gregs [ARMREG_SP]; - memcpy (&mctx->regs, &uc->uc_mcontext.gregs [ARMREG_R4], sizeof (gulong) * 8); - /* memcpy (&mctx->fregs, &uc->uc_mcontext.uc_regs->fpregs.fpregs [14], sizeof (double) * MONO_SAVED_FREGS);*/ -#else - my_ucontext *my_uc = sigctx; - - mctx->eip = UCONTEXT_REG_PC (my_uc); - mctx->ebp = UCONTEXT_REG_SP (my_uc); - memcpy (&mctx->regs, &UCONTEXT_REG_R4 (my_uc), sizeof (gulong) * 8); -#endif + mono_sigctx_to_monoctx (sigctx, mctx); } void mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx) { -#if BROKEN_LINUX - struct ucontext *uc = ctx; + mono_monoctx_to_sigctx (mctx, ctx); +} +#endif /* MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX */ - uc->uc_mcontext.gregs [ARMREG_PC] = mctx->eip; - uc->uc_mcontext.gregs [ARMREG_SP] = mctx->ebp; - memcpy (&uc->uc_mcontext.gregs [ARMREG_R4], &mctx->regs, sizeof (gulong) * 8); - /* memcpy (&uc->uc_mcontext.uc_regs->fpregs.fpregs [14], &mctx->fregs, sizeof (double) * MONO_SAVED_FREGS);*/ -#else - my_ucontext *my_uc = ctx; +/* + * handle_exception: + * + * Called by resuming from a signal handler. + */ +static void +handle_signal_exception (gpointer obj) +{ + MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); + MonoContext ctx; + static void (*restore_context) (MonoContext *); + + if (!restore_context) + restore_context = mono_get_restore_context (); - UCONTEXT_REG_PC (my_uc) = mctx->eip; - UCONTEXT_REG_SP (my_uc) = mctx->ebp; - memcpy (&UCONTEXT_REG_R4 (my_uc), &mctx->regs, sizeof (gulong) * 8); + memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext)); + + mono_handle_exception (&ctx, obj); + + restore_context (&ctx); +} + +/* + * This works around a gcc 4.5 bug: + * https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/721531 + */ +#if defined(__GNUC__) +__attribute__((noinline)) #endif +static gpointer +get_handle_signal_exception_addr (void) +{ + return handle_signal_exception; } /* * This is the function called from the signal handler */ gboolean -mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only) +mono_arch_handle_exception (void *ctx, gpointer obj) { +#if defined(MONO_CROSS_COMPILE) || !defined(MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX) + g_assert_not_reached (); +#elif defined(MONO_ARCH_USE_SIGACTION) + arm_ucontext *sigctx = ctx; + /* + * Handling the exception in the signal handler is problematic, since the original + * signal is disabled, and we could run arbitrary code though the debugger. So + * resume into the normal stack and do most work there if possible. + */ + MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); + guint64 sp = UCONTEXT_REG_SP (sigctx); + + /* Pass the ctx parameter in TLS */ + mono_arch_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); + /* The others in registers */ + UCONTEXT_REG_R0 (sigctx) = (gsize)obj; + + /* Allocate a stack frame */ + sp -= 16; + UCONTEXT_REG_SP (sigctx) = sp; + + UCONTEXT_REG_PC (sigctx) = (gsize)get_handle_signal_exception_addr (); +#ifdef UCONTEXT_REG_CPSR + if ((gsize)UCONTEXT_REG_PC (sigctx) & 1) + /* Transition to thumb */ + UCONTEXT_REG_CPSR (sigctx) |= (1 << 5); + else + /* Transition to ARM */ + UCONTEXT_REG_CPSR (sigctx) &= ~(1 << 5); +#endif + + return TRUE; +#else MonoContext mctx; gboolean result; mono_arch_sigctx_to_monoctx (ctx, &mctx); - result = mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only); + result = mono_handle_exception (&mctx, obj); /* restore the context so that returning from the signal handler will invoke * the catch clause */ mono_arch_monoctx_to_sigctx (&mctx, ctx); return result; +#endif } gpointer mono_arch_ip_from_context (void *sigctx) { -#if BROKEN_LINUX - struct ucontext *uc = sigctx; - return (gpointer)uc->uc_mcontext.gregs [ARMREG_PC]; +#ifdef MONO_CROSS_COMPILE + g_assert_not_reached (); +#elif defined(__native_client__) + g_assert_not_reached (); #else - my_ucontext *my_uc = sigctx; + arm_ucontext *my_uc = sigctx; return (void*) UCONTEXT_REG_PC (my_uc); #endif } -gboolean -mono_arch_has_unwind_info (gconstpointer addr) +void +mono_arch_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data) { - return FALSE; + mgreg_t sp = (mgreg_t)MONO_CONTEXT_GET_SP (ctx); + + // FIXME: + g_assert (!user_data); + + /* Allocate a stack frame */ + sp -= 16; + MONO_CONTEXT_SET_SP (ctx, sp); + MONO_CONTEXT_SET_IP (ctx, async_cb); + + // FIXME: thumb/arm } +/* + * mono_arch_setup_resume_sighandler_ctx: + * + * Setup CTX so execution continues at FUNC. + */ +void +mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func) +{ + MONO_CONTEXT_SET_IP (ctx,func); + if ((mgreg_t)MONO_CONTEXT_GET_IP (ctx) & 1) + /* Transition to thumb */ + ctx->cpsr |= (1 << 5); + else + /* Transition to ARM */ + ctx->cpsr &= ~(1 << 5); +}