X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fexceptions-arm.c;h=575654b67e3ca63724d4f5f70b7201fd356a9277;hb=d0771cfe0d6f833be7bdcc4f19369f38dba89a6a;hp=44e067108a4b80042ecc131389286ec0184fcd9b;hpb=e75cb4420541302d195f3ae207e1283291bc2b25;p=mono.git diff --git a/mono/mini/exceptions-arm.c b/mono/mini/exceptions-arm.c index 44e067108a4..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,87 +34,7 @@ #include "mini.h" #include "mini-arm.h" - -static gboolean arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only); - -/* - -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. - */ - -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; - -#define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do { \ - int reg; \ - 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: @@ -113,27 +43,46 @@ typedef struct my_ucontext { * The first argument in r0 is the pointer to the context. */ gpointer -mono_arch_get_restore_context (void) +mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *code; - static guint8 start [128]; - static int inited = 0; - - if (inited) - return start; - inited = 1; - - code = start; - 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)); - /* jump to the saved IP */ - ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1); + guint8 *start; + int ctx_reg; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; + + start = code = mono_global_codeman_reserve (128); + + /* + * 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); + /* never reached */ ARM_DBRK (code); - g_assert ((code - start) < sizeof(start)); + g_assert ((code - start) < 128); + mono_arch_flush_icache (start, code - start); + + if (info) + *info = mono_tramp_info_create (g_strdup_printf ("restore_context"), start, code - start, ji, unwind_ops); + return start; } @@ -145,26 +94,26 @@ mono_arch_get_restore_context (void) * @exc object in this case). */ gpointer -mono_arch_get_call_filter (void) +mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { - static guint8 start [320]; - static int inited = 0; guint8 *code; - int alloc_size, pos, i; - - if (inited) - return start; + guint8* start; + int ctx_reg; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; - inited = 1; /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */ - code = start; + start = code = mono_global_codeman_reserve (320); /* save all the regs on the stack */ ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP); 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); @@ -173,120 +122,168 @@ mono_arch_get_call_filter (void) /* epilog */ ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC))); - g_assert ((code - start) < sizeof(start)); + g_assert ((code - start) < 320); + mono_arch_flush_icache (start, code - start); + + if (info) + *info = mono_tramp_info_create (g_strdup_printf ("call_filter"), start, code - start, ji, unwind_ops); + return start; } -static void -throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs) +void +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_arch_get_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_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, 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) +{ + 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 * signature: void (*func) (MonoException *exc); or - * void (*func) (char *exc_name); + * void (*func) (guint32 ex_token, guint8* ip); * */ static gpointer -mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name, gboolean rethrow) +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; - int alloc_size, pos, i; + MonoJumpInfo *ji = NULL; + GSList *unwind_ops = NULL; + int cfa_offset; + + code = start = mono_global_codeman_reserve (size); - code = start; + 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); - if (by_name) { - /* r0 has the name of the exception: get the object */ - ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R0); - code = mono_arm_emit_load_imm (code, ARMREG_R0, GPOINTER_TO_UINT (mono_defaults.corlib)); - code = mono_arm_emit_load_imm (code, ARMREG_R1, GPOINTER_TO_UINT ("System")); - code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (mono_exception_from_name)); - ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); - ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP); - } + 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_name) - ARM_LDR_IMM (code, ARMREG_R1, ARMREG_SP, 9 * 4); /* pos on the stack were lr was saved */ - else + 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); - - code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (throw_exception)); + /* fp regs */ + ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_SP, 8); + ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, 0); + + if (aot) { + 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 (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); /* we should never reach this breakpoint */ ARM_DBRK (code); g_assert ((code - start) < size); mono_arch_flush_icache (start, 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 (void) -{ - static guint8 start [132]; - static int inited = 0; + if (info) + *info = mono_tramp_info_create (g_strdup_printf (tramp_name), start, code - start, ji, unwind_ops); - if (inited) - return start; - mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, TRUE); - inited = 1; return start; } + /** * arch_get_throw_exception: * @@ -300,224 +297,346 @@ mono_arch_get_rethrow_exception (void) * */ gpointer -mono_arch_get_throw_exception (void) +mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot) { - static guint8 start [132]; - static int inited = 0; + return get_throw_trampoline (132, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot); +} - if (inited) - return start; - mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, FALSE); - inited = 1; - 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); } /** - * arch_get_throw_exception_by_name: + * 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) (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 ()); - * + * 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. + * On ARM, the ip is passed instead of an offset. */ gpointer -mono_arch_get_throw_exception_by_name (void) +mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot) { - static guint8 start [168]; - static int inited = 0; - - if (inited) - return start; - mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE, FALSE); - inited = 1; - return start; + 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, char **trace, MonoLMF **lmf, - int *native_offset, 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; - } + for (i = 0; i < 16; ++i) + regs [i] = new_ctx->regs [i]; + + 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); - if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { + 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 (!(*lmf)->method) - return (gpointer)-1; + 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; + } - if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) { + frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; + + if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) { + frame->ji = ji; } else { - memset (res, 0, sizeof (MonoJitInfo)); - res->method = (*lmf)->method; + if (!(*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; + + /* we substract 1, so that the IP points into the call instruction */ + new_ctx->pc--; - *lmf = (*lmf)->previous_lmf; + *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); - return ji ? ji : res; + 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 = my_uc->sig_ctx.arm_pc; - mctx->ebp = my_uc->sig_ctx.arm_sp; - memcpy (&mctx->regs, &my_uc->sig_ctx.arm_r4, 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 *); - my_uc->sig_ctx.arm_pc = mctx->eip; - my_uc->sig_ctx.arm_sp = mctx->ebp; - memcpy (&my_uc->sig_ctx.arm_r4, &mctx->regs, sizeof (gulong) * 8); + if (!restore_context) + restore_context = mono_get_restore_context (); + + 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; - return (void*)my_uc->sig_ctx.arm_pc; + 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); +}