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.
16 #ifndef MONO_CROSS_COMPILE
17 #ifdef PLATFORM_ANDROID
18 #include <asm/sigcontext.h>
19 #endif /* def PLATFORM_ANDROID */
22 #ifdef HAVE_UCONTEXT_H
24 #endif /* def HAVE_UCONTEXT_H */
26 #include <mono/arch/arm/arm-codegen.h>
27 #include <mono/arch/arm/arm-vfp-codegen.h>
28 #include <mono/metadata/abi-details.h>
29 #include <mono/metadata/appdomain.h>
30 #include <mono/metadata/tabledefs.h>
31 #include <mono/metadata/threads.h>
32 #include <mono/metadata/debug-helpers.h>
33 #include <mono/metadata/exception.h>
34 #include <mono/metadata/mono-debug.h>
38 #include "mono/utils/mono-sigcontext.h"
39 #include "mono/utils/mono-compiler.h"
42 * arch_get_restore_context:
44 * Returns a pointer to a method which restores a previously saved sigcontext.
45 * The first argument in r0 is the pointer to the context.
48 mono_arch_get_restore_context (MonoTrampInfo
**info
, gboolean aot
)
53 MonoJumpInfo
*ji
= NULL
;
54 GSList
*unwind_ops
= NULL
;
56 start
= code
= mono_global_codeman_reserve (128);
59 * Move things to their proper place so we can restore all the registers with
65 if (!mono_arch_is_soft_float ()) {
66 ARM_ADD_REG_IMM8 (code
, ARMREG_IP
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, fregs
));
67 ARM_FLDMD (code
, ARM_VFP_D0
, 16, ARMREG_IP
);
71 ARM_LDR_IMM (code
, ARMREG_IP
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, pc
));
72 ARM_STR_IMM (code
, ARMREG_IP
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_PC
* sizeof (mgreg_t
)));
74 /* restore everything */
75 ARM_ADD_REG_IMM8 (code
, ARMREG_IP
, ctx_reg
, MONO_STRUCT_OFFSET(MonoContext
, regs
));
76 ARM_LDM (code
, ARMREG_IP
, 0xffff);
81 g_assert ((code
- start
) < 128);
83 mono_arch_flush_icache (start
, code
- start
);
86 *info
= mono_tramp_info_create ("restore_context", start
, code
- start
, ji
, unwind_ops
);
92 * arch_get_call_filter:
94 * Returns a pointer to a method which calls an exception filter. We
95 * also use this function to call finally handlers (we pass NULL as
96 * @exc object in this case).
99 mono_arch_get_call_filter (MonoTrampInfo
**info
, gboolean aot
)
104 MonoJumpInfo
*ji
= NULL
;
105 GSList
*unwind_ops
= NULL
;
107 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
108 start
= code
= mono_global_codeman_reserve (320);
110 /* save all the regs on the stack */
111 ARM_MOV_REG_REG (code
, ARMREG_IP
, ARMREG_SP
);
112 ARM_PUSH (code
, MONO_ARM_REGSAVE_MASK
);
114 ARM_SUB_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, 8);
116 /* restore all the regs from ctx (in r0), but not sp, the stack pointer */
118 ARM_LDR_IMM (code
, ARMREG_IP
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, pc
));
119 ARM_ADD_REG_IMM8 (code
, ARMREG_LR
, ctx_reg
, MONO_STRUCT_OFFSET(MonoContext
, regs
) + (MONO_ARM_FIRST_SAVED_REG
* sizeof (mgreg_t
)));
120 ARM_LDM (code
, ARMREG_LR
, MONO_ARM_REGSAVE_MASK
);
121 /* call handler at eip (r1) and set the first arg with the exception (r2) */
122 ARM_MOV_REG_REG (code
, ARMREG_R0
, ARMREG_R2
);
123 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
124 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_R1
);
126 ARM_ADD_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, 8);
129 ARM_POP_NWB (code
, 0xff0 | ((1 << ARMREG_SP
) | (1 << ARMREG_PC
)));
131 g_assert ((code
- start
) < 320);
133 mono_arch_flush_icache (start
, code
- start
);
136 *info
= mono_tramp_info_create ("call_filter", start
, code
- start
, ji
, unwind_ops
);
142 mono_arm_throw_exception (MonoObject
*exc
, mgreg_t pc
, mgreg_t sp
, mgreg_t
*int_regs
, gdouble
*fp_regs
)
146 gboolean rethrow
= sp
& 1;
148 sp
&= ~1; /* clear the optional rethrow bit */
149 pc
&= ~1; /* clear the thumb bit */
150 /* adjust eip so that it point into the call instruction */
153 /*printf ("stack in throw: %p\n", esp);*/
154 MONO_CONTEXT_SET_BP (&ctx
, int_regs
[ARMREG_FP
- 4]);
155 MONO_CONTEXT_SET_SP (&ctx
, sp
);
156 MONO_CONTEXT_SET_IP (&ctx
, pc
);
157 memcpy (((guint8
*)&ctx
.regs
) + (ARMREG_R4
* sizeof (mgreg_t
)), int_regs
, 8 * sizeof (mgreg_t
));
158 memcpy (&ctx
.fregs
, fp_regs
, sizeof (double) * 16);
160 if (mono_object_isinst_checked (exc
, mono_defaults
.exception_class
, &error
)) {
161 MonoException
*mono_ex
= (MonoException
*)exc
;
163 mono_ex
->stack_trace
= NULL
;
164 mono_ex
->trace_ips
= NULL
;
167 mono_error_assert_ok (&error
);
168 mono_handle_exception (&ctx
, exc
);
169 mono_restore_context (&ctx
);
170 g_assert_not_reached ();
174 mono_arm_throw_exception_by_token (guint32 ex_token_index
, mgreg_t pc
, mgreg_t sp
, mgreg_t
*int_regs
, gdouble
*fp_regs
)
176 guint32 ex_token
= MONO_TOKEN_TYPE_DEF
| ex_token_index
;
177 /* Clear thumb bit */
180 mono_arm_throw_exception ((MonoObject
*)mono_exception_from_token (mono_defaults
.corlib
, ex_token
), pc
, sp
, int_regs
, fp_regs
);
184 mono_arm_resume_unwind (guint32 dummy1
, mgreg_t pc
, mgreg_t sp
, mgreg_t
*int_regs
, gdouble
*fp_regs
)
188 pc
&= ~1; /* clear the optional rethrow bit */
189 /* adjust eip so that it point into the call instruction */
192 MONO_CONTEXT_SET_BP (&ctx
, int_regs
[ARMREG_FP
- 4]);
193 MONO_CONTEXT_SET_SP (&ctx
, sp
);
194 MONO_CONTEXT_SET_IP (&ctx
, pc
);
195 memcpy (((guint8
*)&ctx
.regs
) + (ARMREG_R4
* sizeof (mgreg_t
)), int_regs
, 8 * sizeof (mgreg_t
));
197 mono_resume_unwind (&ctx
);
201 * get_throw_trampoline:
203 * Returns a function pointer which can be used to raise
204 * exceptions. The returned function has the following
205 * signature: void (*func) (MonoException *exc); or
206 * void (*func) (guint32 ex_token, guint8* ip);
210 get_throw_trampoline (int size
, gboolean corlib
, gboolean rethrow
, gboolean llvm
, gboolean resume_unwind
, const char *tramp_name
, MonoTrampInfo
**info
, gboolean aot
)
214 MonoJumpInfo
*ji
= NULL
;
215 GSList
*unwind_ops
= NULL
;
218 code
= start
= mono_global_codeman_reserve (size
);
220 mono_add_unwind_op_def_cfa (unwind_ops
, code
, start
, ARMREG_SP
, 0);
222 /* save all the regs on the stack */
223 ARM_MOV_REG_REG (code
, ARMREG_IP
, ARMREG_SP
);
224 ARM_PUSH (code
, MONO_ARM_REGSAVE_MASK
);
226 cfa_offset
= MONO_ARM_NUM_SAVED_REGS
* sizeof (mgreg_t
);
227 mono_add_unwind_op_def_cfa (unwind_ops
, code
, start
, ARMREG_SP
, cfa_offset
);
228 mono_add_unwind_op_offset (unwind_ops
, code
, start
, ARMREG_LR
, - sizeof (mgreg_t
));
231 if (!mono_arch_is_soft_float ()) {
232 ARM_SUB_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, sizeof (double) * 16);
233 cfa_offset
+= sizeof (double) * 16;
234 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, cfa_offset
);
235 ARM_FSTMD (code
, ARM_VFP_D0
, 16, ARMREG_SP
);
239 ARM_SUB_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, 8);
241 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, start
, cfa_offset
);
243 /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
245 ARM_ADD_REG_IMM8 (code
, ARMREG_R2
, ARMREG_SP
, cfa_offset
);
246 /* we encode rethrow in sp */
248 g_assert (!resume_unwind
);
250 ARM_ORR_REG_IMM8 (code
, ARMREG_R2
, ARMREG_R2
, rethrow
);
252 /* exc is already in place in r0 */
254 /* The caller ip is already in R1 */
257 * The address passed by llvm might point to before the call,
258 * thus outside the eh range recorded by llvm. Use the return
260 * FIXME: Do this on more platforms.
262 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_LR
); /* caller ip */
265 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_LR
); /* caller ip */
268 ARM_ADD_REG_IMM8 (code
, ARMREG_R3
, ARMREG_SP
, (cfa_offset
- (MONO_ARM_NUM_SAVED_REGS
* sizeof (mgreg_t
))));
270 ARM_ADD_REG_IMM8 (code
, ARMREG_LR
, ARMREG_SP
, 8);
271 ARM_STR_IMM (code
, ARMREG_LR
, ARMREG_SP
, 0);
274 const char *icall_name
;
277 icall_name
= "mono_arm_resume_unwind";
279 icall_name
= "mono_arm_throw_exception_by_token";
281 icall_name
= "mono_arm_throw_exception";
283 ji
= mono_patch_info_list_prepend (ji
, code
- start
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, icall_name
);
284 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
286 *(gpointer
*)(gpointer
)code
= NULL
;
288 ARM_LDR_REG_REG (code
, ARMREG_IP
, ARMREG_PC
, ARMREG_IP
);
290 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
)));
292 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
293 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_IP
);
294 /* we should never reach this breakpoint */
296 g_assert ((code
- start
) < size
);
297 mono_arch_flush_icache (start
, code
- start
);
300 *info
= mono_tramp_info_create (tramp_name
, start
, code
- start
, ji
, unwind_ops
);
306 * arch_get_throw_exception:
308 * Returns a function pointer which can be used to raise
309 * exceptions. The returned function has the following
310 * signature: void (*func) (MonoException *exc);
311 * For example to raise an arithmetic exception you can use:
313 * x86_push_imm (code, mono_get_exception_arithmetic ());
314 * x86_call_code (code, arch_get_throw_exception ());
318 mono_arch_get_throw_exception (MonoTrampInfo
**info
, gboolean aot
)
320 return get_throw_trampoline (132, FALSE
, FALSE
, FALSE
, FALSE
, "throw_exception", info
, aot
);
324 * mono_arch_get_rethrow_exception:
326 * Returns a function pointer which can be used to rethrow
327 * exceptions. The returned function has the following
328 * signature: void (*func) (MonoException *exc);
332 mono_arch_get_rethrow_exception (MonoTrampInfo
**info
, gboolean aot
)
334 return get_throw_trampoline (132, FALSE
, TRUE
, FALSE
, FALSE
, "rethrow_exception", info
, aot
);
338 * mono_arch_get_throw_corlib_exception:
340 * Returns a function pointer which can be used to raise
341 * corlib exceptions. The returned function has the following
342 * signature: void (*func) (guint32 ex_token, guint32 offset);
343 * Here, offset is the offset which needs to be substracted from the caller IP
344 * to get the IP of the throw. Passing the offset has the advantage that it
345 * needs no relocations in the caller.
346 * On ARM, the ip is passed instead of an offset.
349 mono_arch_get_throw_corlib_exception (MonoTrampInfo
**info
, gboolean aot
)
351 return get_throw_trampoline (168, TRUE
, FALSE
, FALSE
, FALSE
, "throw_corlib_exception", info
, aot
);
355 mono_arm_get_exception_trampolines (gboolean aot
)
358 GSList
*tramps
= NULL
;
360 /* LLVM uses the normal trampolines, but with a different name */
361 get_throw_trampoline (168, TRUE
, FALSE
, FALSE
, FALSE
, "llvm_throw_corlib_exception_trampoline", &info
, aot
);
362 tramps
= g_slist_prepend (tramps
, info
);
364 get_throw_trampoline (168, TRUE
, FALSE
, TRUE
, FALSE
, "llvm_throw_corlib_exception_abs_trampoline", &info
, aot
);
365 tramps
= g_slist_prepend (tramps
, info
);
367 get_throw_trampoline (168, FALSE
, FALSE
, FALSE
, TRUE
, "llvm_resume_unwind_trampoline", &info
, aot
);
368 tramps
= g_slist_prepend (tramps
, info
);
374 mono_arch_exceptions_init (void)
380 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
381 mono_register_jit_icall (tramp
, "llvm_throw_corlib_exception_trampoline", NULL
, TRUE
);
382 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
383 mono_register_jit_icall (tramp
, "llvm_throw_corlib_exception_abs_trampoline", NULL
, TRUE
);
384 tramp
= mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
385 mono_register_jit_icall (tramp
, "llvm_resume_unwind_trampoline", NULL
, TRUE
);
387 tramps
= mono_arm_get_exception_trampolines (FALSE
);
388 for (l
= tramps
; l
; l
= l
->next
) {
389 MonoTrampInfo
*info
= l
->data
;
391 mono_register_jit_icall (info
->code
, g_strdup (info
->name
), NULL
, TRUE
);
392 mono_tramp_info_register (info
, NULL
);
394 g_slist_free (tramps
);
399 * mono_arch_unwind_frame:
401 * See exceptions-amd64.c for docs;
404 mono_arch_unwind_frame (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
,
405 MonoJitInfo
*ji
, MonoContext
*ctx
,
406 MonoContext
*new_ctx
, MonoLMF
**lmf
,
407 mgreg_t
**save_locations
,
408 StackFrameInfo
*frame
)
410 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
412 memset (frame
, 0, sizeof (StackFrameInfo
));
419 mono_unwind_reg_t regs
[MONO_MAX_IREGS
+ 1 + 8];
421 guint32 unwind_info_len
;
424 if (ji
->is_trampoline
)
425 frame
->type
= FRAME_TYPE_TRAMPOLINE
;
427 frame
->type
= FRAME_TYPE_MANAGED
;
429 unwind_info
= mono_jinfo_get_unwind_info (ji
, &unwind_info_len
);
432 printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip);
433 mono_print_unwind_info (unwind_info, unwind_info_len);
436 for (i
= 0; i
< 16; ++i
)
437 regs
[i
] = new_ctx
->regs
[i
];
439 /* On IOS, d8..d15 are callee saved. They are mapped to 8..15 in unwind.c */
440 for (i
= 0; i
< 8; ++i
)
441 regs
[MONO_MAX_IREGS
+ i
] = *(guint64
*)&(new_ctx
->fregs
[8 + i
]);
444 mono_unwind_frame (unwind_info
, unwind_info_len
, ji
->code_start
,
445 (guint8
*)ji
->code_start
+ ji
->code_size
,
446 ip
, NULL
, regs
, MONO_MAX_IREGS
+ 8,
447 save_locations
, MONO_MAX_IREGS
, &cfa
);
449 for (i
= 0; i
< 16; ++i
)
450 new_ctx
->regs
[i
] = regs
[i
];
451 new_ctx
->pc
= regs
[ARMREG_LR
];
452 new_ctx
->regs
[ARMREG_SP
] = (gsize
)cfa
;
454 for (i
= 0; i
< 8; ++i
)
455 new_ctx
->fregs
[8 + i
] = *(double*)&(regs
[MONO_MAX_IREGS
+ i
]);
458 /* Clear thumb bit */
461 /* we substract 1, so that the IP points into the call instruction */
467 if (((gsize
)(*lmf
)->previous_lmf
) & 2) {
469 * This LMF entry is created by the soft debug code to mark transitions to
470 * managed code done during invokes.
472 MonoLMFExt
*ext
= (MonoLMFExt
*)(*lmf
);
474 g_assert (ext
->debugger_invoke
);
476 memcpy (new_ctx
, &ext
->ctx
, sizeof (MonoContext
));
478 *lmf
= (gpointer
)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
480 frame
->type
= FRAME_TYPE_DEBUGGER_INVOKE
;
485 frame
->type
= FRAME_TYPE_MANAGED_TO_NATIVE
;
487 if ((ji
= mini_jit_info_table_find (domain
, (gpointer
)(*lmf
)->ip
, NULL
))) {
492 frame
->method
= (*lmf
)->method
;
496 * The LMF is saved at the start of the method using:
497 * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP)
498 * ARM_PUSH (code, 0x5ff0);
499 * So it stores the register state as it existed at the caller. We need to
500 * produce the register state which existed at the time of the call which
501 * transitioned to native call, so we save the sp/fp/ip in the LMF.
503 memcpy (&new_ctx
->regs
[0], &(*lmf
)->iregs
[0], sizeof (mgreg_t
) * 13);
504 new_ctx
->pc
= (*lmf
)->ip
;
505 new_ctx
->regs
[ARMREG_SP
] = (*lmf
)->sp
;
506 new_ctx
->regs
[ARMREG_FP
] = (*lmf
)->fp
;
508 /* Clear thumb bit */
511 /* we substract 1, so that the IP points into the call instruction */
514 *lmf
= (gpointer
)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
525 * Called by resuming from a signal handler.
528 handle_signal_exception (gpointer obj
)
530 MonoJitTlsData
*jit_tls
= mono_native_tls_get_value (mono_jit_tls_id
);
533 memcpy (&ctx
, &jit_tls
->ex_ctx
, sizeof (MonoContext
));
535 mono_handle_exception (&ctx
, obj
);
537 mono_restore_context (&ctx
);
541 * This works around a gcc 4.5 bug:
542 * https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/721531
544 static MONO_NEVER_INLINE gpointer
545 get_handle_signal_exception_addr (void)
547 return handle_signal_exception
;
551 * This is the function called from the signal handler
554 mono_arch_handle_exception (void *ctx
, gpointer obj
)
556 #if defined(MONO_CROSS_COMPILE) || !defined(MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX)
557 g_assert_not_reached ();
558 #elif defined(MONO_ARCH_USE_SIGACTION)
559 arm_ucontext
*sigctx
= ctx
;
561 * Handling the exception in the signal handler is problematic, since the original
562 * signal is disabled, and we could run arbitrary code though the debugger. So
563 * resume into the normal stack and do most work there if possible.
565 MonoJitTlsData
*jit_tls
= mono_native_tls_get_value (mono_jit_tls_id
);
566 guint64 sp
= UCONTEXT_REG_SP (sigctx
);
568 /* Pass the ctx parameter in TLS */
569 mono_sigctx_to_monoctx (sigctx
, &jit_tls
->ex_ctx
);
570 /* The others in registers */
571 UCONTEXT_REG_R0 (sigctx
) = (gsize
)obj
;
573 /* Allocate a stack frame */
575 UCONTEXT_REG_SP (sigctx
) = sp
;
577 UCONTEXT_REG_PC (sigctx
) = (gsize
)get_handle_signal_exception_addr ();
578 #ifdef UCONTEXT_REG_CPSR
579 if ((gsize
)UCONTEXT_REG_PC (sigctx
) & 1)
580 /* Transition to thumb */
581 UCONTEXT_REG_CPSR (sigctx
) |= (1 << 5);
583 /* Transition to ARM */
584 UCONTEXT_REG_CPSR (sigctx
) &= ~(1 << 5);
592 mono_sigctx_to_monoctx (ctx
, &mctx
);
594 result
= mono_handle_exception (&mctx
, obj
);
595 /* restore the context so that returning from the signal handler will invoke
598 mono_monoctx_to_sigctx (&mctx
, ctx
);
604 mono_arch_ip_from_context (void *sigctx
)
606 #ifdef MONO_CROSS_COMPILE
607 g_assert_not_reached ();
609 arm_ucontext
*my_uc
= sigctx
;
610 return (void*) UCONTEXT_REG_PC (my_uc
);
615 mono_arch_setup_async_callback (MonoContext
*ctx
, void (*async_cb
)(void *fun
), gpointer user_data
)
617 mgreg_t sp
= (mgreg_t
)MONO_CONTEXT_GET_SP (ctx
);
620 g_assert (!user_data
);
622 /* Allocate a stack frame */
624 MONO_CONTEXT_SET_SP (ctx
, sp
);
626 mono_arch_setup_resume_sighandler_ctx (ctx
, async_cb
);
630 * mono_arch_setup_resume_sighandler_ctx:
632 * Setup CTX so execution continues at FUNC.
635 mono_arch_setup_resume_sighandler_ctx (MonoContext
*ctx
, gpointer func
)
637 MONO_CONTEXT_SET_IP (ctx
,func
);
638 if ((mgreg_t
)MONO_CONTEXT_GET_IP (ctx
) & 1)
639 /* Transition to thumb */
640 ctx
->cpsr
|= (1 << 5);
642 /* Transition to ARM */
643 ctx
->cpsr
&= ~(1 << 5);