2 * exceptions-ppc.c: exception support for PowerPC
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Andreas Faerber <andreas.faerber@web.de>
9 * (C) 2001 Ximian, Inc.
10 * (C) 2007-2008 Andreas Faerber
20 #include <mono/arch/ppc/ppc-codegen.h>
21 #include <mono/metadata/appdomain.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/metadata/exception.h>
26 #include <mono/metadata/mono-debug.h>
34 int sc_onstack; // sigstack state to restore
35 int sc_mask; // signal mask to restore
37 int sc_psw; // processor status word
38 int sc_sp; // stack pointer if sc_regs == NULL
39 void *sc_regs; // (kernel private) saved state
44 sigset_t uc_sigmask; // signal mask used by this context
45 stack_t uc_stack; // stack used by this context
46 struct ucontext *uc_link; // pointer to resuming context
47 size_t uc_mcsize; // size of the machine context passed in
48 mcontext_t uc_mcontext; // machine specific context
51 typedef struct ppc_exception_state {
52 unsigned long dar; // Fault registers for coredump
54 unsigned long exception;// number of powerpc exception taken
55 unsigned long pad0; // align to 16 bytes
57 unsigned long pad1[4]; // space in PCB "just in case"
58 } ppc_exception_state_t;
60 typedef struct ppc_vector_state {
61 unsigned long save_vr[32][4];
62 unsigned long save_vscr[4];
63 unsigned int save_pad5[4];
64 unsigned int save_vrvalid; // VRs that have been saved
65 unsigned int save_pad6[7];
68 typedef struct ppc_float_state {
71 unsigned int fpscr_pad; // fpscr is 64 bits, 32 bits of rubbish
72 unsigned int fpscr; // floating point status register
75 typedef struct ppc_thread_state {
76 unsigned int srr0; // Instruction address register (PC)
77 unsigned int srr1; // Machine state register (supervisor)
83 unsigned int cr; // Condition register
84 unsigned int xer; // User's integer exception register
85 unsigned int lr; // Link register
86 unsigned int ctr; // Count register
87 unsigned int mq; // MQ register (601 only)
89 unsigned int vrsave; // Vector Save Register
93 ppc_exception_state_t es;
94 ppc_thread_state_t ss;
96 ppc_vector_state_t vs;
99 typedef struct mcontext * mcontext_t;
101 Linux/PPC instead has:
103 unsigned long _unused[4];
105 unsigned long handler;
106 unsigned long oldmask;
107 struct pt_regs *regs;
110 unsigned long gpr[32];
113 unsigned long orig_gpr3; // Used for restarting system calls
118 unsigned long mq; // 601 only (not used at present)
119 // Used on APUS to hold IPL value.
120 unsigned long trap; // Reason for being here
121 // N.B. for critical exceptions on 4xx, the dar and dsisr
122 // fields are overloaded to hold srr0 and srr1.
123 unsigned long dar; // Fault registers
124 unsigned long dsisr; // on 4xx/Book-E used for ESR
125 unsigned long result; // Result of a system call
128 elf_gregset_t mc_gregs;
129 elf_fpregset_t mc_fregs;
130 unsigned long mc_pad[2];
131 elf_vrregset_t mc_vregs __attribute__((__aligned__(16)));
135 unsigned long uc_flags;
136 struct ucontext *uc_link;
139 struct mcontext *uc_regs; // points to uc_mcontext field
141 // glibc has 1024-bit signal masks, ours are 64-bit
144 struct mcontext uc_mcontext;
147 #define ELF_NGREG 48 // includes nip, msr, lr, etc.
148 #define ELF_NFPREG 33 // includes fpscr
151 typedef unsigned long elf_greg_t;
152 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
154 // Floating point registers
155 typedef double elf_fpreg_t;
156 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
162 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do { \
164 ppc_load_reg (code, ip_reg, G_STRUCT_OFFSET (MonoContext, sc_ir), ctx_reg); \
165 ppc_load_multiple_regs (code, ppc_r13, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs)); \
166 for (reg = 0; reg < MONO_SAVED_FREGS; ++reg) { \
167 ppc_lfd (code, (14 + reg), \
168 G_STRUCT_OFFSET(MonoContext, fregs) + reg * sizeof (gdouble), ctx_reg); \
173 #define setup_context(ctx)
175 #ifdef __mono_ppc64__
177 ppc_create_ftnptr (gpointer addr)
181 desc = mono_global_codeman_reserve (3 * sizeof (gpointer));
190 #define ppc_create_ftnptr(a) a
194 * arch_get_restore_context:
196 * Returns a pointer to a method which restores a previously saved sigcontext.
197 * The first argument in r3 is the pointer to the context.
200 mono_arch_get_restore_context (void)
202 static guint8 *ftnptr = NULL;
206 int size = MONO_PPC_32_64_CASE (128, 172);
211 code = start = mono_global_codeman_reserve (size);
212 restore_regs_from_context (ppc_r3, ppc_r4, ppc_r5);
213 /* restore also the stack pointer */
214 ppc_load_reg (code, ppc_sp, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3);
216 /* jump to the saved IP */
217 ppc_mtctr (code, ppc_r4);
218 ppc_bcctr (code, PPC_BR_ALWAYS, 0);
222 g_assert ((code - start) < size);
223 mono_arch_flush_icache (start, code - start);
224 ftnptr = ppc_create_ftnptr (start);
229 * mono_arch_get_call_filter:
231 * Returns a pointer to a method which calls an exception filter. We
232 * also use this function to call finally handlers (we pass NULL as
233 * @exc object in this case).
236 mono_arch_get_call_filter (void)
238 static guint8 *ftnptr = NULL;
242 int alloc_size, pos, i;
243 int size = MONO_PPC_32_64_CASE (320, 500);
248 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
249 code = start = mono_global_codeman_reserve (size);
251 /* save all the regs on the stack */
253 for (i = 31; i >= 14; --i) {
254 pos += sizeof (gdouble);
255 ppc_stfd (code, i, -pos, ppc_sp);
257 pos += sizeof (gulong) * MONO_SAVED_GREGS;
258 ppc_store_multiple_regs (code, ppc_r13, ppc_sp, -pos);
260 ppc_mflr (code, ppc_r0);
261 ppc_store_reg (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
263 alloc_size = PPC_MINIMAL_STACK_SIZE + pos + 64;
264 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
265 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
266 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
268 /* allocate stack frame and set link from sp in ctx */
269 g_assert ((alloc_size & (MONO_ARCH_FRAME_ALIGNMENT-1)) == 0);
270 ppc_load_reg (code, ppc_r0, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3);
271 ppc_load_reg_indexed (code, ppc_r0, ppc_r0, ppc_r0);
272 ppc_store_reg_update (code, ppc_r0, -alloc_size, ppc_sp);
274 /* restore all the regs from ctx (in r3), but not r1, the stack pointer */
275 restore_regs_from_context (ppc_r3, ppc_r6, ppc_r7);
276 /* call handler at eip (r4) and set the first arg with the exception (r5) */
277 ppc_mtctr (code, ppc_r4);
278 ppc_mr (code, ppc_r3, ppc_r5);
279 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
282 ppc_load_reg (code, ppc_r0, alloc_size + PPC_RET_ADDR_OFFSET, ppc_sp);
283 ppc_mtlr (code, ppc_r0);
284 ppc_addic (code, ppc_sp, ppc_sp, alloc_size);
286 /* restore all the regs from the stack */
288 for (i = 31; i >= 14; --i) {
289 pos += sizeof (double);
290 ppc_lfd (code, i, -pos, ppc_sp);
292 pos += sizeof (gulong) * MONO_SAVED_GREGS;
293 ppc_load_multiple_regs (code, ppc_r13, ppc_sp, -pos);
297 g_assert ((code - start) < size);
298 mono_arch_flush_icache (start, code - start);
299 ftnptr = ppc_create_ftnptr (start);
304 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs, gboolean rethrow)
306 static void (*restore_context) (MonoContext *);
309 if (!restore_context)
310 restore_context = mono_arch_get_restore_context ();
312 /* adjust eip so that it point into the call instruction */
315 setup_context (&ctx);
317 /*printf ("stack in throw: %p\n", esp);*/
318 MONO_CONTEXT_SET_BP (&ctx, esp);
319 MONO_CONTEXT_SET_IP (&ctx, eip);
320 memcpy (&ctx.regs, int_regs, sizeof (gulong) * MONO_SAVED_GREGS);
321 memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS);
323 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
324 MonoException *mono_ex = (MonoException*)exc;
326 mono_ex->stack_trace = NULL;
328 mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
329 restore_context (&ctx);
331 g_assert_not_reached ();
335 * arch_get_throw_exception_generic:
337 * Returns a function pointer which can be used to raise
338 * exceptions. The returned function has the following
339 * signature: void (*func) (MonoException *exc); or
340 * void (*func) (char *exc_name);
344 mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name, gboolean rethrow)
347 int alloc_size, pos, i;
351 /* save all the regs on the stack */
353 for (i = 31; i >= 14; --i) {
354 pos += sizeof (gdouble);
355 ppc_stfd (code, i, -pos, ppc_sp);
357 pos += sizeof (gulong) * MONO_SAVED_GREGS;
358 ppc_store_multiple_regs (code, ppc_r13, ppc_sp, -pos);
360 ppc_mflr (code, ppc_r0);
361 ppc_store_reg (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
363 /* The 64 bytes here are for outgoing arguments and a bit of
364 spare. We don't use it all, but it doesn't hurt. */
365 alloc_size = PPC_MINIMAL_STACK_SIZE + pos + 64;
366 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
367 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
368 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
370 g_assert ((alloc_size & (MONO_ARCH_FRAME_ALIGNMENT-1)) == 0);
371 ppc_store_reg_update (code, ppc_sp, -alloc_size, ppc_sp);
375 ppc_mr (code, ppc_r5, ppc_r3);
376 ppc_load (code, ppc_r3, (gulong)mono_defaults.corlib);
377 ppc_load (code, ppc_r4, "System");
378 ppc_load_func (code, ppc_r0, mono_exception_from_name);
379 ppc_mtctr (code, ppc_r0);
380 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
383 /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
385 ppc_load_reg (code, ppc_r5, 0, ppc_sp);
386 /* exc is already in place in r3 */
388 ppc_load_reg (code, ppc_r4, PPC_RET_ADDR_OFFSET, ppc_r5);
390 ppc_mr (code, ppc_r4, ppc_r0); /* caller ip */
391 /* pointer to the saved fp regs */
392 pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
393 ppc_addi (code, ppc_r7, ppc_sp, pos);
394 /* pointer to the saved int regs */
395 pos -= sizeof (gulong) * MONO_SAVED_GREGS;
396 ppc_addi (code, ppc_r6, ppc_sp, pos);
397 ppc_li (code, ppc_r8, rethrow);
399 ppc_load_func (code, ppc_r0, throw_exception);
400 ppc_mtctr (code, ppc_r0);
401 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
402 /* we should never reach this breakpoint */
404 g_assert ((code - start) < size);
405 mono_arch_flush_icache (start, code - start);
406 return ppc_create_ftnptr (start);
410 * mono_arch_get_rethrow_exception:
412 * Returns a function pointer which can be used to rethrow
413 * exceptions. The returned function has the following
414 * signature: void (*func) (MonoException *exc);
418 mono_arch_get_rethrow_exception (void)
420 static guint8 *start = NULL;
421 static int inited = 0;
424 int size = MONO_PPC_32_64_CASE (132, 224);
428 code = mono_global_codeman_reserve (size);
429 start = mono_arch_get_throw_exception_generic (code, size, FALSE, TRUE);
434 * arch_get_throw_exception:
436 * Returns a function pointer which can be used to raise
437 * exceptions. The returned function has the following
438 * signature: void (*func) (MonoException *exc);
439 * For example to raise an arithmetic exception you can use:
441 * x86_push_imm (code, mono_get_exception_arithmetic ());
442 * x86_call_code (code, arch_get_throw_exception ());
446 mono_arch_get_throw_exception (void)
448 static guint8 *start = NULL;
449 static int inited = 0;
452 int size = MONO_PPC_32_64_CASE (132, 224);
456 code = mono_global_codeman_reserve (size);
457 start = mono_arch_get_throw_exception_generic (code, size, FALSE, FALSE);
463 * arch_get_throw_exception_by_name:
465 * Returns a function pointer which can be used to raise
466 * corlib exceptions. The returned function has the following
467 * signature: void (*func) (char *exc_name);
468 * For example to raise an arithmetic exception you can use:
470 * x86_push_imm (code, "ArithmeticException");
471 * x86_call_code (code, arch_get_throw_exception_by_name ());
475 mono_arch_get_throw_exception_by_name (void)
477 static guint8 *start = NULL;
478 static int inited = 0;
481 int size = MONO_PPC_32_64_CASE (168, 292);
485 code = mono_global_codeman_reserve (size);
486 start = mono_arch_get_throw_exception_generic (code, size, TRUE, FALSE);
491 /* mono_arch_find_jit_info:
493 * This function is used to gather information from @ctx. It return the
494 * MonoJitInfo of the corresponding function, unwinds one stack frame and
495 * stores the resulting context into @new_ctx. It also stores a string
496 * describing the stack location into @trace (if not NULL), and modifies
497 * the @lmf if necessary. @native_offset return the IP offset from the
498 * start of the function or -1 if that info is not available.
501 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
502 MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
505 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
506 MonoPPCStackFrame *sframe;
508 /* Avoid costly table lookup during stack overflow */
509 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
512 ji = mono_jit_info_table_find (domain, ip);
522 setup_context (new_ctx);
524 if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->ebp)) {
525 /* remove any unused lmf */
526 *lmf = (*lmf)->previous_lmf;
529 address = (char *)ip - (char *)ji->code_start;
532 if (!ji->method->wrapper_type)
535 sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx);
536 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
537 if (ji->method->save_lmf) {
538 memcpy (&new_ctx->fregs, (char*)sframe->sp - sizeof (double) * MONO_SAVED_FREGS, sizeof (double) * MONO_SAVED_FREGS);
539 memcpy (&new_ctx->regs, (char*)sframe->sp - sizeof (double) * MONO_SAVED_FREGS - sizeof (gulong) * MONO_SAVED_GREGS, sizeof (gulong) * MONO_SAVED_GREGS);
540 } else if (ji->used_regs) {
541 /* keep updated with emit_prolog in mini-ppc.c */
543 /* FIXME handle floating point args
544 for (i = 31; i >= 14; --i) {
545 if (ji->used_fregs & (1 << i)) {
546 offset += sizeof (double);
547 new_ctx->fregs [i - 14] = *(gulong*)((char*)sframe->sp - offset);
550 for (i = 31; i >= 13; --i) {
551 if (ji->used_regs & (1 << i)) {
552 offset += sizeof (gulong);
553 new_ctx->regs [i - 13] = *(gulong*)((char*)sframe->sp - offset);
557 /* the calling IP is in the parent frame */
558 sframe = (MonoPPCStackFrame*)sframe->sp;
559 /* we substract 4, so that the IP points into the call instruction */
560 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr - 4);
566 setup_context (new_ctx);
568 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
573 memset (res, 0, sizeof (MonoJitInfo));
574 res->method = (*lmf)->method;
577 /*sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx);
578 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
579 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr);*/
580 MONO_CONTEXT_SET_BP (new_ctx, (*lmf)->ebp);
581 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip);
582 memcpy (&new_ctx->regs, (*lmf)->iregs, sizeof (gulong) * MONO_SAVED_GREGS);
583 memcpy (&new_ctx->fregs, (*lmf)->fregs, sizeof (double) * MONO_SAVED_FREGS);
585 /* FIXME: what about trampoline LMF frames? see exceptions-x86.c */
587 *lmf = (*lmf)->previous_lmf;
589 return ji ? ji : res;
596 * This is the function called from the signal handler
599 mono_arch_sigctx_to_monoctx (void *ctx, MonoContext *mctx)
601 os_ucontext *uc = ctx;
603 mctx->sc_ir = UCONTEXT_REG_NIP(uc);
604 mctx->sc_sp = UCONTEXT_REG_Rn(uc, 1);
605 memcpy (&mctx->regs, &UCONTEXT_REG_Rn(uc, 13), sizeof (gulong) * MONO_SAVED_GREGS);
606 memcpy (&mctx->fregs, &UCONTEXT_REG_FPRn(uc, 14), sizeof (double) * MONO_SAVED_FREGS);
610 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx)
612 os_ucontext *uc = ctx;
614 UCONTEXT_REG_NIP(uc) = mctx->sc_ir;
615 UCONTEXT_REG_Rn(uc, 1) = mctx->sc_sp;
616 memcpy (&UCONTEXT_REG_Rn(uc, 13), &mctx->regs, sizeof (gulong) * MONO_SAVED_GREGS);
617 memcpy (&UCONTEXT_REG_FPRn(uc, 14), &mctx->fregs, sizeof (double) * MONO_SAVED_FREGS);
621 mono_arch_ip_from_context (void *sigctx)
623 os_ucontext *uc = sigctx;
624 return (gpointer)UCONTEXT_REG_NIP(uc);
628 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean test_only)
630 void (*restore_context) (MonoContext *);
633 restore_context = mono_arch_get_restore_context ();
634 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
635 mono_handle_exception (&mctx, obj, (gpointer)mctx.sc_ir, test_only);
636 restore_context (&mctx);
640 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
642 #ifdef MONO_ARCH_USE_SIGACTION
643 os_ucontext *uc = (ucontext_t*)sigctx;
644 os_ucontext *uc_copy;
645 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context (sigctx));
651 /* we don't do much now, but we can warn the user with a useful message */
652 fprintf (stderr, "Stack overflow: IP: %p, SP: %p\n", mono_arch_ip_from_context (sigctx), (gpointer)UCONTEXT_REG_Rn(uc, 1));
653 if (ji && ji->method)
654 method = mono_method_full_name (ji->method, TRUE);
656 method = "Unmanaged";
657 fprintf (stderr, "At %s\n", method);
661 mono_handle_native_sigsegv (SIGSEGV, sigctx);
662 /* setup a call frame on the real stack so that control is returned there
663 * and exception handling can continue.
664 * The frame looks like:
667 * 224 is the size of the red zone
669 frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 16 + 224;
672 sp = (gpointer)(UCONTEXT_REG_Rn(uc, 1) & ~15);
673 sp = (gpointer)((char*)sp - frame_size);
674 /* may need to adjust pointers in the new struct copy, depending on the OS */
675 uc_copy = (ucontext_t*)(sp + 16);
676 memcpy (uc_copy, uc, sizeof (os_ucontext));
677 #if defined(__linux__) && !defined(__mono_ppc64__)
678 uc_copy->uc_mcontext.uc_regs = (gpointer)((char*)uc_copy + ((char*)uc->uc_mcontext.uc_regs - (char*)uc));
680 g_assert (mono_arch_ip_from_context (uc) == mono_arch_ip_from_context (uc_copy));
681 /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
682 UCONTEXT_REG_LNK(uc) = UCONTEXT_REG_NIP(uc);
683 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
685 MonoPPCFunctionDescriptor *handler_ftnptr = (MonoPPCFunctionDescriptor*)altstack_handle_and_restore;
687 UCONTEXT_REG_NIP(uc) = (gulong)handler_ftnptr->code;
688 UCONTEXT_REG_Rn(uc, 2) = (gulong)handler_ftnptr->toc;
691 UCONTEXT_REG_NIP(uc) = (unsigned long)altstack_handle_and_restore;
693 UCONTEXT_REG_Rn(uc, 1) = (unsigned long)sp;
694 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG) = (unsigned long)(sp + 16);
695 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG + 1) = 0;
696 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG + 2) = 0;
701 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
706 mono_arch_sigctx_to_monoctx (ctx, &mctx);
708 result = mono_handle_exception (&mctx, obj, (gpointer)mctx.sc_ir, test_only);
709 /* restore the context so that returning from the signal handler will invoke
712 mono_arch_monoctx_to_sigctx (&mctx, ctx);
717 mono_arch_has_unwind_info (gconstpointer addr)