3 * exception support for ARM64
5 * Copyright 2013 Xamarin Inc
7 * Based on exceptions-arm.c:
10 * Dietmar Maurer (dietmar@ximian.com)
11 * Paolo Molaro (lupus@ximian.com)
13 * (C) 2001 Ximian, Inc.
14 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #include "mini-runtime.h"
19 #include "aot-runtime.h"
21 #include <mono/arch/arm64/arm64-codegen.h>
22 #include <mono/metadata/abi-details.h>
27 mono_arch_get_restore_context (MonoTrampInfo
**info
, gboolean aot
)
30 MonoJumpInfo
*ji
= NULL
;
31 GSList
*unwind_ops
= NULL
;
36 code
= start
= mono_global_codeman_reserve (size
);
38 arm_movx (code
, ARMREG_IP0
, ARMREG_R0
);
42 arm_ldrx (code
, ARMREG_IP1
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, has_fregs
));
44 arm_cbzx (code
, ARMREG_IP1
, 0);
45 for (i
= 0; i
< 32; ++i
)
46 arm_ldrfpx (code
, i
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, fregs
) + (i
* sizeof (MonoContextSimdReg
)));
47 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
49 // FIXME: Restore less registers
50 // FIXME: fp should be restored later
51 code
= mono_arm_emit_load_regarray (code
, 0xffffffff & ~(1 << ctx_reg
) & ~(1 << ARMREG_SP
), ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
52 /* ip0/ip1 doesn't need to be restored */
54 arm_ldrx (code
, ARMREG_IP1
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, pc
));
56 arm_ldrx (code
, ARMREG_IP0
, ctx_reg
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_SP
* 8));
57 /* Restore sp, ctx is no longer valid */
58 arm_movspx (code
, ARMREG_SP
, ARMREG_IP0
);
60 arm_brx (code
, ARMREG_IP1
);
64 g_assert ((code
- start
) < size
);
65 mono_arch_flush_icache (start
, code
- start
);
66 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
69 *info
= mono_tramp_info_create ("restore_context", start
, code
- start
, ji
, unwind_ops
);
75 mono_arch_get_call_filter (MonoTrampInfo
**info
, gboolean aot
)
79 int i
, size
, offset
, gregs_offset
, fregs_offset
, ctx_offset
, num_fregs
, frame_size
;
80 MonoJumpInfo
*ji
= NULL
;
81 GSList
*unwind_ops
= NULL
;
85 start
= code
= mono_global_codeman_reserve (size
);
87 /* Compute stack frame size and offsets */
92 gregs_offset
= offset
;
96 fregs_offset
= offset
;
97 offset
+= num_fregs
* 8;
100 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
103 * We are being called from C code, ctx is in r0, the address to call is in r1.
104 * We need to save state, restore ctx, make the call, then restore the previous state,
105 * returning the value returned by the call.
109 arm_stpx_pre (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, -frame_size
);
110 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
113 arm_strx (code
, ARMREG_R0
, ARMREG_FP
, ctx_offset
);
115 code
= mono_arm_emit_store_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
| (1 << ARMREG_FP
), ARMREG_FP
, gregs_offset
);
117 for (i
= 0; i
< num_fregs
; ++i
)
118 arm_strfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
120 /* Load regs from ctx */
121 code
= mono_arm_emit_load_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
123 arm_ldrx (code
, ARMREG_IP0
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, has_fregs
));
125 arm_cbzx (code
, ARMREG_IP0
, 0);
126 for (i
= 0; i
< num_fregs
; ++i
)
127 arm_ldrfpx (code
, ARMREG_D8
+ i
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, fregs
) + ((i
+ 8) * sizeof (MonoContextSimdReg
)));
128 mono_arm_patch (labels
[0], code
, MONO_R_ARM64_CBZ
);
130 arm_ldrx (code
, ARMREG_FP
, ARMREG_R0
, MONO_STRUCT_OFFSET (MonoContext
, regs
) + (ARMREG_FP
* 8));
133 arm_blrx (code
, ARMREG_R1
);
134 /* For filters, the result is in R0 */
137 arm_ldrx (code
, ARMREG_FP
, ARMREG_SP
, gregs_offset
+ (ARMREG_FP
* 8));
139 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, ctx_offset
);
140 /* Save registers back to ctx */
141 /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */
142 code
= mono_arm_emit_store_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_IP0
, MONO_STRUCT_OFFSET (MonoContext
, regs
));
145 code
= mono_arm_emit_load_regarray (code
, MONO_ARCH_CALLEE_SAVED_REGS
, ARMREG_FP
, gregs_offset
);
147 for (i
= 0; i
< num_fregs
; ++i
)
148 arm_ldrfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
150 code
= mono_arm_emit_destroy_frame (code
, frame_size
, (1 << ARMREG_IP0
));
151 arm_retx (code
, ARMREG_LR
);
153 g_assert ((code
- start
) < size
);
154 mono_arch_flush_icache (start
, code
- start
);
155 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
158 *info
= mono_tramp_info_create ("call_filter", start
, code
- start
, ji
, unwind_ops
);
164 get_throw_trampoline (int size
, gboolean corlib
, gboolean rethrow
, gboolean llvm
, gboolean resume_unwind
, const char *tramp_name
, MonoTrampInfo
**info
, gboolean aot
, gboolean preserve_ips
)
166 guint8
*start
, *code
;
167 MonoJumpInfo
*ji
= NULL
;
168 GSList
*unwind_ops
= NULL
;
169 int i
, offset
, gregs_offset
, fregs_offset
, frame_size
, num_fregs
;
171 code
= start
= mono_global_codeman_reserve (size
);
173 /* We are being called by JITted code, the exception object/type token is in R0 */
175 /* Compute stack frame size and offsets */
180 gregs_offset
= offset
;
184 fregs_offset
= offset
;
185 offset
+= num_fregs
* 8;
186 frame_size
= ALIGN_TO (offset
, MONO_ARCH_FRAME_ALIGNMENT
);
189 arm_stpx_pre (code
, ARMREG_FP
, ARMREG_LR
, ARMREG_SP
, -frame_size
);
190 arm_movspx (code
, ARMREG_FP
, ARMREG_SP
);
193 code
= mono_arm_emit_store_regarray (code
, 0xffffffff, ARMREG_FP
, gregs_offset
);
195 /* The real LR is in R1 */
196 arm_strx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
198 arm_ldrx (code
, ARMREG_IP0
, ARMREG_FP
, 0);
199 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, gregs_offset
+ (ARMREG_FP
* 8));
200 arm_addx_imm (code
, ARMREG_IP0
, ARMREG_FP
, frame_size
);
201 arm_strx (code
, ARMREG_IP0
, ARMREG_FP
, gregs_offset
+ (ARMREG_SP
* 8));
203 for (i
= 0; i
< num_fregs
; ++i
)
204 arm_strfpx (code
, ARMREG_D8
+ i
, ARMREG_FP
, fregs_offset
+ (i
* 8));
206 /* Call the C trampoline function */
207 /* Arg1 = exception object/type token */
208 arm_movx (code
, ARMREG_R0
, ARMREG_R0
);
209 /* Arg2 = caller ip */
212 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, gregs_offset
+ (ARMREG_LR
* 8));
214 arm_movx (code
, ARMREG_R1
, ARMREG_R1
);
216 arm_ldrx (code
, ARMREG_R1
, ARMREG_FP
, 8);
219 arm_addx_imm (code
, ARMREG_R2
, ARMREG_FP
, gregs_offset
);
221 arm_addx_imm (code
, ARMREG_R3
, ARMREG_FP
, fregs_offset
);
223 arm_movzx (code
, ARMREG_R4
, corlib
? 1 : 0, 0);
224 /* Arg 6 = rethrow */
225 arm_movzx (code
, ARMREG_R5
, rethrow
? 1 : 0, 0);
226 if (!resume_unwind
) {
227 /* Arg 7 = preserve_ips */
228 arm_movzx (code
, ARMREG_R6
, preserve_ips
? 1 : 0, 0);
231 /* Call the function */
233 MonoJitICallId icall_id
;
236 icall_id
= MONO_JIT_ICALL_mono_arm_resume_unwind
;
238 icall_id
= MONO_JIT_ICALL_mono_arm_throw_exception
;
240 code
= mono_arm_emit_aotconst (&ji
, code
, start
, ARMREG_LR
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, GUINT_TO_POINTER (icall_id
));
245 icall_func
= (gpointer
)mono_arm_resume_unwind
;
247 icall_func
= (gpointer
)mono_arm_throw_exception
;
249 code
= mono_arm_emit_imm64 (code
, ARMREG_LR
, (guint64
)icall_func
);
251 arm_blrx (code
, ARMREG_LR
);
252 /* This shouldn't return */
255 g_assert ((code
- start
) < size
);
256 mono_arch_flush_icache (start
, code
- start
);
257 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
, NULL
));
260 *info
= mono_tramp_info_create (tramp_name
, start
, code
- start
, ji
, unwind_ops
);
266 mono_arch_get_throw_exception (MonoTrampInfo
**info
, gboolean aot
)
268 return get_throw_trampoline (256, FALSE
, FALSE
, FALSE
, FALSE
, "throw_exception", info
, aot
, FALSE
);
272 mono_arch_get_rethrow_exception (MonoTrampInfo
**info
, gboolean aot
)
274 return get_throw_trampoline (256, FALSE
, TRUE
, FALSE
, FALSE
, "rethrow_exception", info
, aot
, FALSE
);
278 mono_arch_get_rethrow_preserve_exception (MonoTrampInfo
**info
, gboolean aot
)
280 return get_throw_trampoline (256, FALSE
, TRUE
, FALSE
, FALSE
, "rethrow_preserve_exception", info
, aot
, TRUE
);
284 mono_arch_get_throw_corlib_exception (MonoTrampInfo
**info
, gboolean aot
)
286 return get_throw_trampoline (256, TRUE
, FALSE
, FALSE
, FALSE
, "throw_corlib_exception", info
, aot
, FALSE
);
290 mono_arm_get_exception_trampolines (gboolean aot
)
293 GSList
*tramps
= NULL
;
297 /* LLVM uses the normal trampolines, but with a different name */
298 get_throw_trampoline (256, TRUE
, FALSE
, FALSE
, FALSE
, "llvm_throw_corlib_exception_trampoline", &info
, aot
, FALSE
);
299 info
->jit_icall_info
= &mono_get_jit_icall_info ()->mono_llvm_throw_corlib_exception_trampoline
;
300 tramps
= g_slist_prepend (tramps
, info
);
302 get_throw_trampoline (256, TRUE
, FALSE
, TRUE
, FALSE
, "llvm_throw_corlib_exception_abs_trampoline", &info
, aot
, FALSE
);
303 info
->jit_icall_info
= &mono_get_jit_icall_info ()->mono_llvm_throw_corlib_exception_abs_trampoline
;
304 tramps
= g_slist_prepend (tramps
, info
);
306 get_throw_trampoline (256, FALSE
, FALSE
, FALSE
, TRUE
, "llvm_resume_unwind_trampoline", &info
, aot
, FALSE
);
307 info
->jit_icall_info
= &mono_get_jit_icall_info ()->mono_llvm_resume_unwind_trampoline
;
308 tramps
= g_slist_prepend (tramps
, info
);
316 mono_arm_get_exception_trampolines (gboolean aot
)
318 g_assert_not_reached ();
322 #endif /* DISABLE_JIT */
325 mono_arch_exceptions_init (void)
334 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
335 mono_register_jit_icall_info (&mono_get_jit_icall_info ()->mono_llvm_throw_corlib_exception_trampoline
, tramp
, "llvm_throw_corlib_exception_trampoline", NULL
, TRUE
, NULL
);
337 tramp
= mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
338 mono_register_jit_icall_info (&mono_get_jit_icall_info ()->mono_llvm_throw_corlib_exception_abs_trampoline
, tramp
, "llvm_throw_corlib_exception_abs_trampoline", NULL
, TRUE
, NULL
);
340 tramp
= mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
341 mono_register_jit_icall_info (&mono_get_jit_icall_info ()->mono_llvm_resume_unwind_trampoline
, tramp
, "llvm_resume_unwind_trampoline", NULL
, TRUE
, NULL
);
344 tramps
= mono_arm_get_exception_trampolines (FALSE
);
345 for (l
= tramps
; l
; l
= l
->next
) {
346 MonoTrampInfo
*info
= (MonoTrampInfo
*)l
->data
;
347 mono_register_jit_icall_info (info
->jit_icall_info
, info
->code
, g_strdup (info
->name
), NULL
, TRUE
, NULL
);
348 mono_tramp_info_register (info
, NULL
);
350 g_slist_free (tramps
);
355 * mono_arm_throw_exception:
357 * This function is called by the exception trampolines.
358 * FP_REGS points to the 8 callee saved fp regs.
361 mono_arm_throw_exception (gpointer arg
, host_mgreg_t pc
, host_mgreg_t
*int_regs
, gdouble
*fp_regs
, gboolean corlib
, gboolean rethrow
, gboolean preserve_ips
)
365 MonoObject
*exc
= NULL
;
366 guint32 ex_token_index
, ex_token
;
369 exc
= (MonoObject
*)arg
;
371 ex_token_index
= (guint64
)arg
;
372 ex_token
= MONO_TOKEN_TYPE_DEF
| ex_token_index
;
373 exc
= (MonoObject
*)mono_exception_from_token (mono_defaults
.corlib
, ex_token
);
376 /* Adjust pc so it points into the call instruction */
379 /* Initialize a ctx based on the arguments */
380 memset (&ctx
, 0, sizeof (MonoContext
));
381 memcpy (&(ctx
.regs
[0]), int_regs
, sizeof (host_mgreg_t
) * 32);
382 for (int i
= 0; i
< 8; i
++)
383 *((gdouble
*)&ctx
.fregs
[ARMREG_D8
+ i
]) = fp_regs
[i
];
387 if (mono_object_isinst_checked (exc
, mono_defaults
.exception_class
, error
)) {
388 MonoException
*mono_ex
= (MonoException
*)exc
;
390 mono_ex
->stack_trace
= NULL
;
391 mono_ex
->trace_ips
= NULL
;
392 } else if (preserve_ips
) {
393 mono_ex
->caught_in_unmanaged
= TRUE
;
396 mono_error_assert_ok (error
);
398 mono_handle_exception (&ctx
, exc
);
400 mono_restore_context (&ctx
);
404 mono_arm_resume_unwind (gpointer arg
, host_mgreg_t pc
, host_mgreg_t
*int_regs
, gdouble
*fp_regs
, gboolean corlib
, gboolean rethrow
)
408 /* Adjust pc so it points into the call instruction */
411 /* Initialize a ctx based on the arguments */
412 memset (&ctx
, 0, sizeof (MonoContext
));
413 memcpy (&(ctx
.regs
[0]), int_regs
, sizeof (host_mgreg_t
) * 32);
414 for (int i
= 0; i
< 8; i
++)
415 *((gdouble
*)&ctx
.fregs
[ARMREG_D8
+ i
]) = fp_regs
[i
];
419 mono_resume_unwind (&ctx
);
423 * mono_arch_unwind_frame:
425 * See exceptions-amd64.c for docs;
428 mono_arch_unwind_frame (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
,
429 MonoJitInfo
*ji
, MonoContext
*ctx
,
430 MonoContext
*new_ctx
, MonoLMF
**lmf
,
431 host_mgreg_t
**save_locations
,
432 StackFrameInfo
*frame
)
434 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
436 memset (frame
, 0, sizeof (StackFrameInfo
));
442 host_mgreg_t regs
[MONO_MAX_IREGS
+ 8 + 1];
444 guint32 unwind_info_len
;
448 if (ji
->is_trampoline
)
449 frame
->type
= FRAME_TYPE_TRAMPOLINE
;
451 frame
->type
= FRAME_TYPE_MANAGED
;
453 unwind_info
= mono_jinfo_get_unwind_info (ji
, &unwind_info_len
);
455 memcpy (regs
, &new_ctx
->regs
, sizeof (host_mgreg_t
) * 32);
456 /* v8..v15 are callee saved */
457 for (int i
= 0; i
< 8; i
++)
458 (regs
+ MONO_MAX_IREGS
) [i
] = *((host_mgreg_t
*)&new_ctx
->fregs
[8 + i
]);
460 gboolean success
= mono_unwind_frame (unwind_info
, unwind_info_len
, (guint8
*)ji
->code_start
,
461 (guint8
*)ji
->code_start
+ ji
->code_size
,
462 (guint8
*)ip
, NULL
, regs
, MONO_MAX_IREGS
+ 8,
463 save_locations
, MONO_MAX_IREGS
, (guint8
**)&cfa
);
468 memcpy (&new_ctx
->regs
, regs
, sizeof (host_mgreg_t
) * 32);
469 for (int i
= 0; i
< 8; i
++)
470 *((host_mgreg_t
*)&new_ctx
->fregs
[8 + i
]) = (regs
+ MONO_MAX_IREGS
) [i
];
472 new_ctx
->pc
= regs
[ARMREG_LR
];
473 new_ctx
->regs
[ARMREG_SP
] = (host_mgreg_t
)(gsize
)cfa
;
475 if (*lmf
&& (*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
] && (MONO_CONTEXT_GET_SP (ctx
) >= (gpointer
)(*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
])) {
476 /* remove any unused lmf */
477 *lmf
= (MonoLMF
*)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
480 /* we substract 1, so that the IP points into the call instruction */
485 g_assert ((((guint64
)(*lmf
)->previous_lmf
) & 2) == 0);
487 frame
->type
= FRAME_TYPE_MANAGED_TO_NATIVE
;
489 ji
= mini_jit_info_table_find (domain
, (gpointer
)(*lmf
)->pc
, NULL
);
493 g_assert (MONO_ARCH_LMF_REGS
== ((0x3ff << 19) | (1 << ARMREG_FP
) | (1 << ARMREG_SP
)));
494 memcpy (&new_ctx
->regs
[ARMREG_R19
], &(*lmf
)->gregs
[0], sizeof (host_mgreg_t
) * 10);
495 new_ctx
->regs
[ARMREG_FP
] = (*lmf
)->gregs
[MONO_ARCH_LMF_REG_FP
];
496 new_ctx
->regs
[ARMREG_SP
] = (*lmf
)->gregs
[MONO_ARCH_LMF_REG_SP
];
497 new_ctx
->pc
= (*lmf
)->pc
;
499 /* we substract 1, so that the IP points into the call instruction */
502 *lmf
= (MonoLMF
*)(((gsize
)(*lmf
)->previous_lmf
) & ~3);
513 * Called by resuming from a signal handler.
516 handle_signal_exception (gpointer obj
)
518 MonoJitTlsData
*jit_tls
= mono_tls_get_jit_tls ();
521 memcpy (&ctx
, &jit_tls
->ex_ctx
, sizeof (MonoContext
));
523 mono_handle_exception (&ctx
, (MonoObject
*)obj
);
525 mono_restore_context (&ctx
);
529 * This is the function called from the signal handler
532 mono_arch_handle_exception (void *ctx
, gpointer obj
)
534 #if defined(MONO_CROSS_COMPILE)
535 g_assert_not_reached ();
537 MonoJitTlsData
*jit_tls
;
541 * Resume into the normal stack and handle the exception there.
543 jit_tls
= mono_tls_get_jit_tls ();
545 /* Pass the ctx parameter in TLS */
546 mono_sigctx_to_monoctx (sigctx
, &jit_tls
->ex_ctx
);
547 /* The others in registers */
548 UCONTEXT_REG_R0 (sigctx
) = (gsize
)obj
;
550 UCONTEXT_REG_PC (sigctx
) = (gsize
)handle_signal_exception
;
551 UCONTEXT_REG_SP (sigctx
) = UCONTEXT_REG_SP (sigctx
) - MONO_ARCH_REDZONE_SIZE
;
558 mono_arch_ip_from_context (void *sigctx
)
560 #ifdef MONO_CROSS_COMPILE
561 g_assert_not_reached ();
564 return (gpointer
)UCONTEXT_REG_PC (sigctx
);
569 mono_arch_setup_async_callback (MonoContext
*ctx
, void (*async_cb
)(void *fun
), gpointer user_data
)
571 host_mgreg_t sp
= (host_mgreg_t
)MONO_CONTEXT_GET_SP (ctx
);
574 g_assert (!user_data
);
576 /* Allocate a stack frame */
578 MONO_CONTEXT_SET_SP (ctx
, sp
);
580 mono_arch_setup_resume_sighandler_ctx (ctx
, (gpointer
)async_cb
);
584 * mono_arch_setup_resume_sighandler_ctx:
586 * Setup CTX so execution continues at FUNC.
589 mono_arch_setup_resume_sighandler_ctx (MonoContext
*ctx
, gpointer func
)
591 MONO_CONTEXT_SET_IP (ctx
,func
);
595 mono_arch_undo_ip_adjustment (MonoContext
*ctx
)
601 mono_arch_do_ip_adjustment (MonoContext
*ctx
)