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.
17 #include <mono/arch/arm/arm-codegen.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/threads.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/mono-debug.h>
28 static gboolean arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only);
33 unsigned long trap_no;
34 unsigned long error_code;
35 unsigned long oldmask;
46 unsigned long arm_r10;
52 unsigned long arm_cpsr;
53 unsigned long fault_address;
56 gregs below is this struct
58 unsigned long int uregs[18];
61 the companion user_fpregs has just 8 double registers
62 (it's valid for FPA mode, will need changes for VFP)
69 typedef struct ucontext {
70 unsigned long int uc_flags;
71 struct ucontext *uc_link;
72 __sigset_t uc_sigmask;
74 mcontext_t uc_mcontext;
75 long int uc_filler[5];
81 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do { \
83 ARM_LDR_IMM (code, ip_reg, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip)); \
84 ARM_ADD_REG_IMM8 (code, tmp_reg, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs)); \
85 ARM_LDMIA (code, tmp_reg, MONO_ARM_REGSAVE_MASK); \
89 #define setup_context(ctx)
92 * arch_get_restore_context:
94 * Returns a pointer to a method which restores a previously saved sigcontext.
95 * The first argument in r0 is the pointer to the context.
98 mono_arch_get_restore_context (void)
101 static guint8 start [128];
102 static int inited = 0;
109 restore_regs_from_context (ARMREG_R0, ARMREG_R1, ARMREG_R2);
110 /* restore also the stack pointer, FIXME: handle sp != fp */
111 ARM_LDR_IMM (code, ARMREG_SP, ARMREG_R0, G_STRUCT_OFFSET (MonoContext, ebp));
112 /* jump to the saved IP */
113 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
117 g_assert ((code - start) < sizeof(start));
118 mono_arch_flush_icache (start, code - start);
123 * arch_get_call_filter:
125 * Returns a pointer to a method which calls an exception filter. We
126 * also use this function to call finally handlers (we pass NULL as
127 * @exc object in this case).
130 mono_arch_get_call_filter (void)
132 static guint8 start [320];
133 static int inited = 0;
135 int alloc_size, pos, i;
141 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
144 /* save all the regs on the stack */
145 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
146 ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
148 /* restore all the regs from ctx (in r0), but not sp, the stack pointer */
149 restore_regs_from_context (ARMREG_R0, ARMREG_IP, ARMREG_LR);
150 /* call handler at eip (r1) and set the first arg with the exception (r2) */
151 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2);
152 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
153 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
156 ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
158 g_assert ((code - start) < sizeof(start));
159 mono_arch_flush_icache (start, code - start);
164 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
166 static void (*restore_context) (MonoContext *);
168 gboolean rethrow = eip & 1;
170 if (!restore_context)
171 restore_context = mono_arch_get_restore_context ();
173 eip &= ~1; /* clear the optional rethrow bit */
174 /* adjust eip so that it point into the call instruction */
177 setup_context (&ctx);
179 /*printf ("stack in throw: %p\n", esp);*/
180 MONO_CONTEXT_SET_BP (&ctx, esp);
181 MONO_CONTEXT_SET_IP (&ctx, eip);
182 memcpy (&ctx.regs, int_regs, sizeof (gulong) * 8);
183 /* memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS); */
185 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
186 MonoException *mono_ex = (MonoException*)exc;
188 mono_ex->stack_trace = NULL;
190 mono_handle_exception (&ctx, exc, (gpointer)(eip + 4), FALSE);
191 restore_context (&ctx);
192 g_assert_not_reached ();
196 * arch_get_throw_exception_generic:
198 * Returns a function pointer which can be used to raise
199 * exceptions. The returned function has the following
200 * signature: void (*func) (MonoException *exc); or
201 * void (*func) (char *exc_name);
205 mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name, gboolean rethrow)
208 int alloc_size, pos, i;
212 /* save all the regs on the stack */
213 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
214 ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
217 /* r0 has the name of the exception: get the object */
218 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R0);
219 code = mono_arm_emit_load_imm (code, ARMREG_R0, GPOINTER_TO_UINT (mono_defaults.corlib));
220 code = mono_arm_emit_load_imm (code, ARMREG_R1, GPOINTER_TO_UINT ("System"));
221 code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (mono_exception_from_name));
222 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
223 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
226 /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
228 ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, 10 * 4); /* 10 saved regs */
229 /* exc is already in place in r0 */
231 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_SP, 9 * 4); /* pos on the stack were lr was saved */
233 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
234 /* FIXME: pointer to the saved fp regs */
235 /*pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
236 ppc_addi (code, ppc_r7, ppc_sp, pos);*/
237 /* pointer to the saved int regs */
238 ARM_MOV_REG_REG (code, ARMREG_R3, ARMREG_SP); /* the pushed regs */
239 /* we encode rethrow in the ip, so we avoid args on the stack */
240 ARM_ORR_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, rethrow);
242 code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (throw_exception));
243 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
244 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
245 /* we should never reach this breakpoint */
247 g_assert ((code - start) < size);
248 mono_arch_flush_icache (start, code - start);
253 * mono_arch_get_rethrow_exception:
255 * Returns a function pointer which can be used to rethrow
256 * exceptions. The returned function has the following
257 * signature: void (*func) (MonoException *exc);
261 mono_arch_get_rethrow_exception (void)
263 static guint8 start [132];
264 static int inited = 0;
268 mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, TRUE);
273 * arch_get_throw_exception:
275 * Returns a function pointer which can be used to raise
276 * exceptions. The returned function has the following
277 * signature: void (*func) (MonoException *exc);
278 * For example to raise an arithmetic exception you can use:
280 * x86_push_imm (code, mono_get_exception_arithmetic ());
281 * x86_call_code (code, arch_get_throw_exception ());
285 mono_arch_get_throw_exception (void)
287 static guint8 start [132];
288 static int inited = 0;
292 mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, FALSE);
298 * arch_get_throw_exception_by_name:
300 * Returns a function pointer which can be used to raise
301 * corlib exceptions. The returned function has the following
302 * signature: void (*func) (char *exc_name);
303 * For example to raise an arithmetic exception you can use:
305 * x86_push_imm (code, "ArithmeticException");
306 * x86_call_code (code, arch_get_throw_exception_by_name ());
310 mono_arch_get_throw_exception_by_name (void)
312 static guint8 start [168];
313 static int inited = 0;
317 mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE, FALSE);
323 glist_to_array (GList *list, MonoClass *eclass)
325 MonoDomain *domain = mono_domain_get ();
332 len = g_list_length (list);
333 res = mono_array_new (domain, eclass, len);
335 for (i = 0; list; list = list->next, i++)
336 mono_array_set (res, gpointer, i, list->data);
341 /* mono_arch_find_jit_info:
343 * This function is used to gather information from @ctx. It return the
344 * MonoJitInfo of the corresponding function, unwinds one stack frame and
345 * stores the resulting context into @new_ctx. It also stores a string
346 * describing the stack location into @trace (if not NULL), and modifies
347 * the @lmf if necessary. @native_offset return the IP offset from the
348 * start of the function or -1 if that info is not available.
351 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji,
352 MonoContext *ctx, MonoContext *new_ctx, char **trace, MonoLMF **lmf,
353 int *native_offset, gboolean *managed)
356 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
358 /* Avoid costly table lookup during stack overflow */
359 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
362 ji = mono_jit_info_table_find (domain, ip);
373 if (!ji->method->wrapper_type)
377 * Some managed methods like pinvoke wrappers might have save_lmf set.
378 * In this case, register save/restore code is not generated by the
379 * JIT, so we have to restore callee saved registers from the lmf.
381 if (ji->method->save_lmf) {
383 * We only need to do this if the exception was raised in managed
384 * code, since otherwise the lmf was already popped of the stack.
386 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
387 memcpy (&new_ctx->regs [0], &(*lmf)->iregs [4], sizeof (gulong) * MONO_SAVED_GREGS);
389 new_ctx->esp = (*lmf)->iregs [13];
390 new_ctx->eip = (*lmf)->iregs [14];
391 new_ctx->ebp = new_ctx->esp;
395 offset = ji->used_regs >> 16;
397 /* the saved regs are at sp + offset */
398 /* restore caller saved registers */
399 sp = (char*)ctx->ebp;
401 for (i = 4; i < 16; ++i) {
402 if (ji->used_regs & (1 << i)) {
403 new_ctx->regs [i - 4] = *(gulong*)sp;
404 sp += sizeof (gulong);
408 new_ctx->esp = *(gulong*)sp;
409 sp += sizeof (gulong);
410 new_ctx->eip = *(gulong*)sp;
411 sp += sizeof (gulong);
412 new_ctx->ebp = new_ctx->esp;
415 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
416 /* remove any unused lmf */
417 *lmf = (*lmf)->previous_lmf;
420 /* we substract 1, so that the IP points into the call instruction */
431 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
433 memset (res, 0, sizeof (MonoJitInfo));
434 res->method = (*lmf)->method;
437 memcpy (&new_ctx->regs [0], &(*lmf)->iregs [4], sizeof (gulong) * MONO_SAVED_GREGS);
438 new_ctx->esp = (*lmf)->iregs [13];
439 new_ctx->eip = (*lmf)->iregs [14];
440 new_ctx->ebp = new_ctx->esp;
442 *lmf = (*lmf)->previous_lmf;
444 return ji ? ji : res;
451 * This is the function called from the signal handler
454 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
456 struct ucontext *uc = ctx;
460 mctx.eip = uc->uc_mcontext.gregs [ARMREG_PC];
461 mctx.ebp = uc->uc_mcontext.gregs [ARMREG_SP];
462 memcpy (&mctx.regs, &uc->uc_mcontext.gregs [ARMREG_R4], sizeof (gulong) * 8);
463 /* memcpy (&mctx.fregs, &uc->uc_mcontext.uc_regs->fpregs.fpregs [14], sizeof (double) * MONO_SAVED_FREGS);*/
465 result = mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
466 /* restore the context so that returning from the signal handler will invoke
469 uc->uc_mcontext.gregs [ARMREG_PC] = mctx.eip;
470 uc->uc_mcontext.gregs [ARMREG_SP] = mctx.ebp;
471 memcpy (&uc->uc_mcontext.gregs [ARMREG_R4], &mctx.regs, sizeof (gulong) * 8);
472 /* memcpy (&uc->uc_mcontext.uc_regs->fpregs.fpregs [14], &mctx.fregs, sizeof (double) * MONO_SAVED_FREGS);*/
477 mono_arch_ip_from_context (void *sigctx)
479 struct ucontext *uc = sigctx;
480 return (gpointer)uc->uc_mcontext.gregs [ARMREG_PC];
484 mono_arch_has_unwind_info (gconstpointer addr)