2 * exceptions-ppc.c: exception support for PowerPC
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
8 * (C) 2001 Ximian, Inc.
18 #include <mono/arch/ppc/ppc-codegen.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/threads.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/mono-debug.h>
32 int sc_onstack; // sigstack state to restore
33 int sc_mask; // signal mask to restore
35 int sc_psw; // processor status word
36 int sc_sp; // stack pointer if sc_regs == NULL
37 void *sc_regs; // (kernel private) saved state
42 sigset_t uc_sigmask; // signal mask used by this context
43 stack_t uc_stack; // stack used by this context
44 struct ucontext *uc_link; // pointer to resuming context
45 size_t uc_mcsize; // size of the machine context passed in
46 mcontext_t uc_mcontext; // machine specific context
49 typedef struct ppc_exception_state {
50 unsigned long dar; // Fault registers for coredump
52 unsigned long exception;// number of powerpc exception taken
53 unsigned long pad0; // align to 16 bytes
55 unsigned long pad1[4]; // space in PCB "just in case"
56 } ppc_exception_state_t;
58 typedef struct ppc_vector_state {
59 unsigned long save_vr[32][4];
60 unsigned long save_vscr[4];
61 unsigned int save_pad5[4];
62 unsigned int save_vrvalid; // VRs that have been saved
63 unsigned int save_pad6[7];
66 typedef struct ppc_float_state {
69 unsigned int fpscr_pad; // fpscr is 64 bits, 32 bits of rubbish
70 unsigned int fpscr; // floating point status register
73 typedef struct ppc_thread_state {
74 unsigned int srr0; // Instruction address register (PC)
75 unsigned int srr1; // Machine state register (supervisor)
81 unsigned int cr; // Condition register
82 unsigned int xer; // User's integer exception register
83 unsigned int lr; // Link register
84 unsigned int ctr; // Count register
85 unsigned int mq; // MQ register (601 only)
87 unsigned int vrsave; // Vector Save Register
91 ppc_exception_state_t es;
92 ppc_thread_state_t ss;
94 ppc_vector_state_t vs;
97 typedef struct mcontext * mcontext_t;
99 Linux/PPC instead has:
101 unsigned long _unused[4];
103 unsigned long handler;
104 unsigned long oldmask;
105 struct pt_regs *regs;
108 unsigned long gpr[32];
111 unsigned long orig_gpr3; // Used for restarting system calls
116 unsigned long mq; // 601 only (not used at present)
117 // Used on APUS to hold IPL value.
118 unsigned long trap; // Reason for being here
119 // N.B. for critical exceptions on 4xx, the dar and dsisr
120 // fields are overloaded to hold srr0 and srr1.
121 unsigned long dar; // Fault registers
122 unsigned long dsisr; // on 4xx/Book-E used for ESR
123 unsigned long result; // Result of a system call
126 elf_gregset_t mc_gregs;
127 elf_fpregset_t mc_fregs;
128 unsigned long mc_pad[2];
129 elf_vrregset_t mc_vregs __attribute__((__aligned__(16)));
133 unsigned long uc_flags;
134 struct ucontext *uc_link;
137 struct mcontext *uc_regs; // points to uc_mcontext field
139 // glibc has 1024-bit signal masks, ours are 64-bit
142 struct mcontext uc_mcontext;
145 #define ELF_NGREG 48 // includes nip, msr, lr, etc.
146 #define ELF_NFPREG 33 // includes fpscr
149 typedef unsigned long elf_greg_t;
150 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
152 // Floating point registers
153 typedef double elf_fpreg_t;
154 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
160 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do { \
162 ppc_lwz (code, ip_reg, G_STRUCT_OFFSET (MonoContext, sc_ir), ctx_reg); \
163 ppc_lmw (code, ppc_r13, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs)); \
164 for (reg = 0; reg < MONO_SAVED_FREGS; ++reg) { \
165 ppc_lfd (code, (14 + reg), G_STRUCT_OFFSET(MonoLMF, fregs) + reg * sizeof (gdouble), ctx_reg); \
170 #define setup_context(ctx)
173 * arch_get_restore_context:
175 * Returns a pointer to a method which restores a previously saved sigcontext.
176 * The first argument in r3 is the pointer to the context.
179 mono_arch_get_restore_context (void)
182 static guint8 *start = NULL;
187 code = start = mono_global_codeman_reserve (128);
188 restore_regs_from_context (ppc_r3, ppc_r4, ppc_r5);
189 /* restore also the stack pointer */
190 ppc_lwz (code, ppc_sp, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3);
192 /* jump to the saved IP */
193 ppc_mtctr (code, ppc_r4);
194 ppc_bcctr (code, PPC_BR_ALWAYS, 0);
198 g_assert ((code - start) < 128);
199 mono_arch_flush_icache (start, code - start);
204 * mono_arch_get_call_filter:
206 * Returns a pointer to a method which calls an exception filter. We
207 * also use this function to call finally handlers (we pass NULL as
208 * @exc object in this case).
211 mono_arch_get_call_filter (void)
213 static guint8 *start = NULL;
215 int alloc_size, pos, i;
220 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
221 code = start = mono_global_codeman_reserve (320);
223 /* save all the regs on the stack */
225 for (i = 31; i >= 14; --i) {
226 pos += sizeof (gdouble);
227 ppc_stfd (code, i, -pos, ppc_sp);
229 pos += sizeof (gulong) * MONO_SAVED_GREGS;
230 ppc_stmw (code, ppc_r13, ppc_sp, -pos);
232 ppc_mflr (code, ppc_r0);
233 ppc_stw (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
235 alloc_size = PPC_MINIMAL_STACK_SIZE + pos + 64;
236 // align to PPC_STACK_ALIGNMENT bytes
237 alloc_size += PPC_STACK_ALIGNMENT - 1;
238 alloc_size &= ~(PPC_STACK_ALIGNMENT - 1);
240 /* allocate stack frame and set link from sp in ctx */
241 g_assert ((alloc_size & (PPC_STACK_ALIGNMENT-1)) == 0);
242 ppc_lwz (code, ppc_r0, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3);
243 ppc_lwzx (code, ppc_r0, ppc_r0, ppc_r0);
244 ppc_stwu (code, ppc_r0, -alloc_size, ppc_sp);
246 /* restore all the regs from ctx (in r3), but not r1, the stack pointer */
247 restore_regs_from_context (ppc_r3, ppc_r6, ppc_r7);
248 /* call handler at eip (r4) and set the first arg with the exception (r5) */
249 ppc_mtctr (code, ppc_r4);
250 ppc_mr (code, ppc_r3, ppc_r5);
251 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
254 ppc_lwz (code, ppc_r0, alloc_size + PPC_RET_ADDR_OFFSET, ppc_sp);
255 ppc_mtlr (code, ppc_r0);
256 ppc_addic (code, ppc_sp, ppc_sp, alloc_size);
258 /* restore all the regs from the stack */
260 for (i = 31; i >= 14; --i) {
261 pos += sizeof (double);
262 ppc_lfd (code, i, -pos, ppc_sp);
264 pos += sizeof (gulong) * MONO_SAVED_GREGS;
265 ppc_lmw (code, ppc_r13, ppc_sp, -pos);
269 g_assert ((code - start) < 320);
270 mono_arch_flush_icache (start, code - start);
275 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs, gboolean rethrow)
277 static void (*restore_context) (MonoContext *);
280 if (!restore_context)
281 restore_context = mono_arch_get_restore_context ();
283 /* adjust eip so that it point into the call instruction */
286 setup_context (&ctx);
288 /*printf ("stack in throw: %p\n", esp);*/
289 MONO_CONTEXT_SET_BP (&ctx, esp);
290 MONO_CONTEXT_SET_IP (&ctx, eip);
291 memcpy (&ctx.regs, int_regs, sizeof (gulong) * MONO_SAVED_GREGS);
292 memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS);
294 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
295 MonoException *mono_ex = (MonoException*)exc;
297 mono_ex->stack_trace = NULL;
299 mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
300 restore_context (&ctx);
302 g_assert_not_reached ();
306 * arch_get_throw_exception_generic:
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); or
311 * void (*func) (char *exc_name);
315 mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name, gboolean rethrow)
318 int alloc_size, pos, i;
322 /* save all the regs on the stack */
324 for (i = 31; i >= 14; --i) {
325 pos += sizeof (gdouble);
326 ppc_stfd (code, i, -pos, ppc_sp);
328 pos += sizeof (gulong) * MONO_SAVED_GREGS;
329 ppc_stmw (code, ppc_r13, ppc_sp, -pos);
331 ppc_mflr (code, ppc_r0);
332 ppc_stw (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
334 alloc_size = PPC_MINIMAL_STACK_SIZE + pos + 64;
335 // align to PPC_STACK_ALIGNMENT bytes
336 alloc_size += PPC_STACK_ALIGNMENT - 1;
337 alloc_size &= ~(PPC_STACK_ALIGNMENT - 1);
339 g_assert ((alloc_size & (PPC_STACK_ALIGNMENT-1)) == 0);
340 ppc_stwu (code, ppc_sp, -alloc_size, ppc_sp);
344 ppc_mr (code, ppc_r5, ppc_r3);
345 ppc_load (code, ppc_r3, (guint32)mono_defaults.corlib);
346 ppc_load (code, ppc_r4, "System");
347 ppc_load (code, ppc_r0, mono_exception_from_name);
348 ppc_mtctr (code, ppc_r0);
349 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
352 /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
354 ppc_lwz (code, ppc_r5, 0, ppc_sp);
355 /* exc is already in place in r3 */
357 ppc_lwz (code, ppc_r4, PPC_RET_ADDR_OFFSET, ppc_r5);
359 ppc_mr (code, ppc_r4, ppc_r0); /* caller ip */
360 /* pointer to the saved fp regs */
361 pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
362 ppc_addi (code, ppc_r7, ppc_sp, pos);
363 /* pointer to the saved int regs */
364 pos -= sizeof (gulong) * MONO_SAVED_GREGS;
365 ppc_addi (code, ppc_r6, ppc_sp, pos);
366 ppc_li (code, ppc_r8, rethrow);
368 ppc_load (code, ppc_r0, throw_exception);
369 ppc_mtctr (code, ppc_r0);
370 ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
371 /* we should never reach this breakpoint */
373 g_assert ((code - start) < size);
374 mono_arch_flush_icache (start, code - start);
379 * mono_arch_get_rethrow_exception:
381 * Returns a function pointer which can be used to rethrow
382 * exceptions. The returned function has the following
383 * signature: void (*func) (MonoException *exc);
387 mono_arch_get_rethrow_exception (void)
389 static guint8 *start = NULL;
390 static int inited = 0;
394 start = mono_global_codeman_reserve (132);
395 mono_arch_get_throw_exception_generic (start, 132, FALSE, TRUE);
400 * arch_get_throw_exception:
402 * Returns a function pointer which can be used to raise
403 * exceptions. The returned function has the following
404 * signature: void (*func) (MonoException *exc);
405 * For example to raise an arithmetic exception you can use:
407 * x86_push_imm (code, mono_get_exception_arithmetic ());
408 * x86_call_code (code, arch_get_throw_exception ());
412 mono_arch_get_throw_exception (void)
414 static guint8 *start = NULL;
415 static int inited = 0;
419 start = mono_global_codeman_reserve (132);
420 mono_arch_get_throw_exception_generic (start, 132, FALSE, FALSE);
426 * arch_get_throw_exception_by_name:
428 * Returns a function pointer which can be used to raise
429 * corlib exceptions. The returned function has the following
430 * signature: void (*func) (char *exc_name);
431 * For example to raise an arithmetic exception you can use:
433 * x86_push_imm (code, "ArithmeticException");
434 * x86_call_code (code, arch_get_throw_exception_by_name ());
438 mono_arch_get_throw_exception_by_name (void)
440 static guint8 *start = NULL;
441 static int inited = 0;
445 start = mono_global_codeman_reserve (168);
446 mono_arch_get_throw_exception_generic (start, 168, TRUE, FALSE);
452 glist_to_array (GList *list, MonoClass *eclass)
454 MonoDomain *domain = mono_domain_get ();
461 len = g_list_length (list);
462 res = mono_array_new (domain, eclass, len);
464 for (i = 0; list; list = list->next, i++)
465 mono_array_set (res, gpointer, i, list->data);
470 /* mono_arch_find_jit_info:
472 * This function is used to gather information from @ctx. It return the
473 * MonoJitInfo of the corresponding function, unwinds one stack frame and
474 * stores the resulting context into @new_ctx. It also stores a string
475 * describing the stack location into @trace (if not NULL), and modifies
476 * the @lmf if necessary. @native_offset return the IP offset from the
477 * start of the function or -1 if that info is not available.
480 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
481 MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
484 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
485 MonoPPCStackFrame *sframe;
487 /* Avoid costly table lookup during stack overflow */
488 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
491 ji = mono_jit_info_table_find (domain, ip);
501 setup_context (new_ctx);
503 if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->ebp)) {
504 /* remove any unused lmf */
505 *lmf = (*lmf)->previous_lmf;
508 address = (char *)ip - (char *)ji->code_start;
511 if (!ji->method->wrapper_type)
514 sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx);
515 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
516 if (ji->method->save_lmf) {
517 memcpy (&new_ctx->fregs, (char*)sframe->sp - sizeof (double) * MONO_SAVED_FREGS, sizeof (double) * MONO_SAVED_FREGS);
518 memcpy (&new_ctx->regs, (char*)sframe->sp - sizeof (double) * MONO_SAVED_FREGS - sizeof (gulong) * MONO_SAVED_GREGS, sizeof (gulong) * MONO_SAVED_GREGS);
519 } else if (ji->used_regs) {
520 /* keep updated with emit_prolog in mini-ppc.c */
522 /* FIXME handle floating point args
523 for (i = 31; i >= 14; --i) {
524 if (ji->used_fregs & (1 << i)) {
525 offset += sizeof (double);
526 new_ctx->fregs [i - 14] = *(gulong*)((char*)sframe->sp - offset);
529 for (i = 31; i >= 13; --i) {
530 if (ji->used_regs & (1 << i)) {
531 offset += sizeof (gulong);
532 new_ctx->regs [i - 13] = *(gulong*)((char*)sframe->sp - offset);
536 /* the calling IP is in the parent frame */
537 sframe = (MonoPPCStackFrame*)sframe->sp;
538 /* we substract 4, so that the IP points into the call instruction */
539 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr - 4);
545 setup_context (new_ctx);
547 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
552 memset (res, 0, sizeof (MonoJitInfo));
553 res->method = (*lmf)->method;
556 /*sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx);
557 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
558 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr);*/
559 MONO_CONTEXT_SET_BP (new_ctx, (*lmf)->ebp);
560 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip);
561 memcpy (&new_ctx->regs, (*lmf)->iregs, sizeof (gulong) * MONO_SAVED_GREGS);
562 memcpy (&new_ctx->fregs, (*lmf)->fregs, sizeof (double) * MONO_SAVED_FREGS);
564 /* FIXME: what about trampoline LMF frames? see exceptions-x86.c */
566 *lmf = (*lmf)->previous_lmf;
568 return ji ? ji : res;
575 * This is the function called from the signal handler
578 mono_arch_sigctx_to_monoctx (void *ctx, MonoContext *mctx)
580 os_ucontext *uc = ctx;
582 mctx->sc_ir = UCONTEXT_REG_NIP(uc);
583 mctx->sc_sp = UCONTEXT_REG_Rn(uc, 1);
584 memcpy (&mctx->regs, &UCONTEXT_REG_Rn(uc, 13), sizeof (gulong) * MONO_SAVED_GREGS);
585 memcpy (&mctx->fregs, &UCONTEXT_REG_FPRn(uc, 14), sizeof (double) * MONO_SAVED_FREGS);
589 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx)
591 os_ucontext *uc = ctx;
593 UCONTEXT_REG_NIP(uc) = mctx->sc_ir;
594 UCONTEXT_REG_Rn(uc, 1) = mctx->sc_sp;
595 memcpy (&UCONTEXT_REG_Rn(uc, 13), &mctx->regs, sizeof (gulong) * MONO_SAVED_GREGS);
596 memcpy (&UCONTEXT_REG_FPRn(uc, 14), &mctx->fregs, sizeof (double) * MONO_SAVED_FREGS);
600 mono_arch_ip_from_context (void *sigctx)
602 os_ucontext *uc = sigctx;
603 return (gpointer)UCONTEXT_REG_NIP(uc);
607 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean test_only)
609 void (*restore_context) (MonoContext *);
612 restore_context = mono_arch_get_restore_context ();
613 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
614 mono_handle_exception (&mctx, obj, (gpointer)mctx.sc_ir, test_only);
615 restore_context (&mctx);
619 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
621 #ifdef MONO_ARCH_USE_SIGACTION
622 os_ucontext *uc = (ucontext_t*)sigctx;
623 os_ucontext *uc_copy;
624 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context (sigctx));
630 /* we don't do much now, but we can warn the user with a useful message */
631 fprintf (stderr, "Stack overflow: IP: %p, SP: %p\n", mono_arch_ip_from_context (sigctx), (gpointer)UCONTEXT_REG_Rn(uc, 1));
632 if (ji && ji->method)
633 method = mono_method_full_name (ji->method, TRUE);
635 method = "Unmanaged";
636 fprintf (stderr, "At %s\n", method);
640 mono_handle_native_sigsegv (SIGSEGV, sigctx);
641 /* setup a call frame on the real stack so that control is returned there
642 * and exception handling can continue.
643 * The frame looks like:
646 * 224 is the size of the red zone
648 frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 16 + 224;
651 sp = (gpointer)(UCONTEXT_REG_Rn(uc, 1) & ~15);
652 sp = (gpointer)((char*)sp - frame_size);
653 /* may need to adjust pointers in the new struct copy, depending on the OS */
654 uc_copy = (ucontext_t*)(sp + 16);
655 memcpy (uc_copy, uc, sizeof (os_ucontext));
657 uc_copy->uc_mcontext.uc_regs = (gpointer)((char*)uc_copy + ((char*)uc->uc_mcontext.uc_regs - (char*)uc));
659 g_assert (mono_arch_ip_from_context (uc) == mono_arch_ip_from_context (uc_copy));
660 /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
661 UCONTEXT_REG_LNK(uc) = UCONTEXT_REG_NIP(uc);
662 UCONTEXT_REG_NIP(uc) = (unsigned long)altstack_handle_and_restore;
663 UCONTEXT_REG_Rn(uc, 1) = (unsigned long)sp;
664 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG) = (unsigned long)(sp + 16);
665 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG + 1) = 0;
666 UCONTEXT_REG_Rn(uc, PPC_FIRST_ARG_REG + 2) = 0;
671 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
676 mono_arch_sigctx_to_monoctx (ctx, &mctx);
678 result = mono_handle_exception (&mctx, obj, (gpointer)mctx.sc_ir, test_only);
679 /* restore the context so that returning from the signal handler will invoke
682 mono_arch_monoctx_to_sigctx (&mctx, ctx);
687 mono_arch_has_unwind_info (gconstpointer addr)