2 * exceptions-arm.c: exception support for ARM
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
8 * (C) 2001 Ximian, Inc.
15 #ifdef HAVE_ASM_SIGCONTEXT_H
16 #include <asm/sigcontext.h>
17 #endif /* def HAVE_ASM_SIGCONTEXT_H */
18 #ifdef HAVE_UCONTEXT_H
20 #endif /* def HAVE_UCONTEXT_H */
22 #include <mono/arch/arm/arm-codegen.h>
23 #include <mono/metadata/appdomain.h>
24 #include <mono/metadata/tabledefs.h>
25 #include <mono/metadata/threads.h>
26 #include <mono/metadata/debug-helpers.h>
27 #include <mono/metadata/exception.h>
28 #include <mono/metadata/mono-debug.h>
32 #include "mono/utils/mono-sigcontext.h"
35 * arch_get_restore_context:
37 * Returns a pointer to a method which restores a previously saved sigcontext.
38 * The first argument in r0 is the pointer to the context.
41 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
46 MonoJumpInfo *ji = NULL;
47 GSList *unwind_ops = NULL;
49 start = code = mono_global_codeman_reserve (128);
52 * Move things to their proper place so we can restore all the registers with
59 ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
60 ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_PC * 4));
62 ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, esp));
63 ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 4));
65 /* restore everything */
66 ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs));
67 ARM_LDM (code, ARMREG_IP, 0xffff);
72 g_assert ((code - start) < 128);
74 mono_arch_flush_icache (start, code - start);
77 *info = mono_tramp_info_create (g_strdup_printf ("restore_context"), start, code - start, ji, unwind_ops);
83 * arch_get_call_filter:
85 * Returns a pointer to a method which calls an exception filter. We
86 * also use this function to call finally handlers (we pass NULL as
87 * @exc object in this case).
90 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
95 MonoJumpInfo *ji = NULL;
96 GSList *unwind_ops = NULL;
98 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
99 start = code = mono_global_codeman_reserve (320);
101 /* save all the regs on the stack */
102 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
103 ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
105 /* restore all the regs from ctx (in r0), but not sp, the stack pointer */
107 ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
108 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs) + (4 * 4));
109 ARM_LDM (code, ARMREG_LR, MONO_ARM_REGSAVE_MASK);
110 /* call handler at eip (r1) and set the first arg with the exception (r2) */
111 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2);
112 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
113 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
116 ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
118 g_assert ((code - start) < 320);
120 mono_arch_flush_icache (start, code - start);
123 *info = mono_tramp_info_create (g_strdup_printf ("call_filter"), start, code - start, ji, unwind_ops);
129 mono_arm_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
131 static void (*restore_context) (MonoContext *);
133 gboolean rethrow = eip & 1;
135 if (!restore_context)
136 restore_context = mono_get_restore_context ();
138 eip &= ~1; /* clear the optional rethrow bit */
139 /* adjust eip so that it point into the call instruction */
142 /*printf ("stack in throw: %p\n", esp);*/
143 MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]);
144 MONO_CONTEXT_SET_SP (&ctx, esp);
145 MONO_CONTEXT_SET_IP (&ctx, eip);
146 memcpy (((guint8*)&ctx.regs) + (4 * 4), int_regs, sizeof (gulong) * 8);
147 /* memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS); */
149 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
150 MonoException *mono_ex = (MonoException*)exc;
152 mono_ex->stack_trace = NULL;
154 mono_handle_exception (&ctx, exc, (gpointer)(eip + 4), FALSE);
155 restore_context (&ctx);
156 g_assert_not_reached ();
160 mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
162 mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), eip, esp, int_regs, fp_regs);
166 mono_arm_resume_unwind (guint32 dummy1, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
170 eip &= ~1; /* clear the optional rethrow bit */
171 /* adjust eip so that it point into the call instruction */
174 MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]);
175 MONO_CONTEXT_SET_SP (&ctx, esp);
176 MONO_CONTEXT_SET_IP (&ctx, eip);
177 memcpy (((guint8*)&ctx.regs) + (4 * 4), int_regs, sizeof (gulong) * 8);
179 mono_resume_unwind (&ctx);
183 * get_throw_trampoline:
185 * Returns a function pointer which can be used to raise
186 * exceptions. The returned function has the following
187 * signature: void (*func) (MonoException *exc); or
188 * void (*func) (guint32 ex_token, guint8* ip);
192 get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot)
196 MonoJumpInfo *ji = NULL;
197 GSList *unwind_ops = NULL;
199 code = start = mono_global_codeman_reserve (size);
201 mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, 0);
203 /* save all the regs on the stack */
204 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
205 ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
207 mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, 10 * 4);
208 mono_add_unwind_op_offset (unwind_ops, code, start, ARMREG_LR, -4);
210 /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
212 ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, 10 * 4); /* 10 saved regs */
213 /* exc is already in place in r0 */
215 /* The caller ip is already in R1 */
217 /* Negate the ip adjustment done in mono_arm_throw_exception */
218 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
220 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
222 /* FIXME: pointer to the saved fp regs */
223 /*pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
224 ppc_addi (code, ppc_r7, ppc_sp, pos);*/
225 /* pointer to the saved int regs */
226 ARM_MOV_REG_REG (code, ARMREG_R3, ARMREG_SP); /* the pushed regs */
227 /* we encode rethrow in the ip, so we avoid args on the stack */
228 ARM_ORR_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, rethrow);
231 ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, corlib ? "mono_arm_throw_exception_by_token" : "mono_arm_throw_exception");
232 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
234 *(gpointer*)(gpointer)code = NULL;
236 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
238 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)));
240 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
241 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
242 /* we should never reach this breakpoint */
244 g_assert ((code - start) < size);
245 mono_arch_flush_icache (start, code - start);
248 *info = mono_tramp_info_create (g_strdup_printf (tramp_name), start, code - start, ji, unwind_ops);
254 * arch_get_throw_exception:
256 * Returns a function pointer which can be used to raise
257 * exceptions. The returned function has the following
258 * signature: void (*func) (MonoException *exc);
259 * For example to raise an arithmetic exception you can use:
261 * x86_push_imm (code, mono_get_exception_arithmetic ());
262 * x86_call_code (code, arch_get_throw_exception ());
266 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
268 return get_throw_trampoline (132, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot);
272 * mono_arch_get_rethrow_exception:
274 * Returns a function pointer which can be used to rethrow
275 * exceptions. The returned function has the following
276 * signature: void (*func) (MonoException *exc);
280 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
282 return get_throw_trampoline (132, FALSE, TRUE, FALSE, FALSE, "rethrow_exception", info, aot);
286 * mono_arch_get_throw_corlib_exception:
288 * Returns a function pointer which can be used to raise
289 * corlib exceptions. The returned function has the following
290 * signature: void (*func) (guint32 ex_token, guint32 offset);
291 * Here, offset is the offset which needs to be substracted from the caller IP
292 * to get the IP of the throw. Passing the offset has the advantage that it
293 * needs no relocations in the caller.
294 * On ARM, the ip is passed instead of an offset.
297 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
299 return get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "throw_corlib_exception", info, aot);
303 mono_arch_exceptions_init (void)
309 /* LLVM uses the normal trampolines, but with a different name */
310 tramp = get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", NULL, FALSE);
311 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE);
313 tramp = get_throw_trampoline (168, TRUE, FALSE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", NULL, FALSE);
314 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE);
316 tramp = get_throw_trampoline (168, FALSE, FALSE, FALSE, TRUE, "llvm_resume_unwind_trampoline", NULL, FALSE);
317 mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE);
322 * mono_arch_find_jit_info:
324 * See exceptions-amd64.c for docs;
327 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
328 MonoJitInfo *ji, MonoContext *ctx,
329 MonoContext *new_ctx, MonoLMF **lmf,
330 StackFrameInfo *frame)
332 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
334 memset (frame, 0, sizeof (StackFrameInfo));
336 frame->managed = FALSE;
342 gssize regs [MONO_MAX_IREGS + 1];
344 guint32 unwind_info_len;
347 frame->type = FRAME_TYPE_MANAGED;
349 if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
350 frame->managed = TRUE;
353 unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
355 unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
357 for (i = 0; i < 16; ++i)
358 regs [i] = new_ctx->regs [i];
359 regs [ARMREG_SP] = new_ctx->esp;
361 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start,
362 (guint8*)ji->code_start + ji->code_size,
363 ip, regs, MONO_MAX_IREGS, &cfa);
365 for (i = 0; i < 16; ++i)
366 new_ctx->regs [i] = regs [i];
367 new_ctx->eip = regs [ARMREG_LR];
368 new_ctx->esp = (gsize)cfa;
370 if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->esp)) {
371 /* remove any unused lmf */
372 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
375 /* we substract 1, so that the IP points into the call instruction */
381 if (((gsize)(*lmf)->previous_lmf) & 2) {
383 * This LMF entry is created by the soft debug code to mark transitions to
384 * managed code done during invokes.
386 MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
388 g_assert (ext->debugger_invoke);
390 memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
392 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
394 frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
399 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
401 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
406 frame->method = (*lmf)->method;
410 * The LMF is saved at the start of the method using:
411 * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP)
412 * ARM_PUSH (code, 0x5ff0);
413 * So it stores the register state as it existed at the caller.
415 memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (gulong) * 13);
417 new_ctx->regs [ARMREG_LR] = (*lmf)->iregs [ARMREG_LR - 1];
418 new_ctx->esp = (*lmf)->iregs [ARMREG_IP];
419 new_ctx->eip = new_ctx->regs [ARMREG_LR];
421 /* we substract 1, so that the IP points into the call instruction */
424 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
433 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
436 g_assert_not_reached ();
438 arm_ucontext *my_uc = sigctx;
440 mctx->eip = UCONTEXT_REG_PC (my_uc);
441 mctx->esp = UCONTEXT_REG_SP (my_uc);
442 memcpy (&mctx->regs, &UCONTEXT_REG_R0 (my_uc), sizeof (gulong) * 16);
447 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx)
450 g_assert_not_reached ();
452 arm_ucontext *my_uc = ctx;
454 UCONTEXT_REG_PC (my_uc) = mctx->eip;
455 UCONTEXT_REG_SP (my_uc) = mctx->regs [ARMREG_FP];
456 /* The upper registers are not guaranteed to be valid */
457 memcpy (&UCONTEXT_REG_R0 (my_uc), &mctx->regs, sizeof (gulong) * 12);
464 * Called by resuming from a signal handler.
467 handle_signal_exception (gpointer obj, gboolean test_only)
469 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
471 static void (*restore_context) (MonoContext *);
473 if (!restore_context)
474 restore_context = mono_get_restore_context ();
476 memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
478 mono_handle_exception (&ctx, obj, MONO_CONTEXT_GET_IP (&ctx), test_only);
480 restore_context (&ctx);
484 * This is the function called from the signal handler
487 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
489 #if defined(MONO_CROSS_COMPILE)
490 g_assert_not_reached ();
491 #elif defined(MONO_ARCH_USE_SIGACTION)
492 arm_ucontext *sigctx = ctx;
494 * Handling the exception in the signal handler is problematic, since the original
495 * signal is disabled, and we could run arbitrary code though the debugger. So
496 * resume into the normal stack and do most work there if possible.
498 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
499 guint64 sp = UCONTEXT_REG_SP (sigctx);
501 /* Pass the ctx parameter in TLS */
502 mono_arch_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx);
503 /* The others in registers */
504 UCONTEXT_REG_R0 (sigctx) = (gsize)obj;
505 UCONTEXT_REG_R1 (sigctx) = test_only;
507 /* Allocate a stack frame */
509 UCONTEXT_REG_SP (sigctx) = sp;
511 UCONTEXT_REG_PC (sigctx) = (gsize)handle_signal_exception;
512 #ifdef UCONTEXT_REG_CPSR
513 if ((gsize)UCONTEXT_REG_PC (sigctx) & 1)
514 /* Transition to thumb */
515 UCONTEXT_REG_CPSR (sigctx) |= (1 << 5);
523 mono_arch_sigctx_to_monoctx (ctx, &mctx);
525 result = mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
526 /* restore the context so that returning from the signal handler will invoke
529 mono_arch_monoctx_to_sigctx (&mctx, ctx);
535 mono_arch_ip_from_context (void *sigctx)
538 g_assert_not_reached ();
540 arm_ucontext *my_uc = sigctx;
541 return (void*) UCONTEXT_REG_PC (my_uc);
546 mono_arch_has_unwind_info (gconstpointer addr)