/* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux. Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This file defines our own versions of various kernel and user structs, so that system headers are not needed, which otherwise can make bootstrapping a new toolchain difficult. Do not use these structs elsewhere; Many fields are missing, particularly from the end of the structures. */ struct gcc_pt_regs { unsigned long gpr[32]; unsigned long nip; unsigned long msr; unsigned long orig_gpr3; unsigned long ctr; unsigned long link; unsigned long xer; unsigned long ccr; }; struct gcc_sigcontext { unsigned long pad[7]; struct gcc_pt_regs *regs; }; struct gcc_ucontext { #ifdef __powerpc64__ unsigned long pad[21]; #else unsigned long pad[5]; #endif struct gcc_sigcontext uc_mcontext; }; #ifdef __powerpc64__ enum { SIGNAL_FRAMESIZE = 128 }; /* If the current unwind info (FS) does not contain explicit info saving R2, then we have to do a minor amount of code reading to figure out if it was saved. The big problem here is that the code that does the save/restore is generated by the linker, so we have no good way to determine at compile time what to do. */ #define MD_FROB_UPDATE_CONTEXT frob_update_context static void frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) { if (fs->regs.reg[2].how == REG_UNSAVED) { unsigned int *insn = (unsigned int *) _Unwind_GetGR (context, LINK_REGISTER_REGNUM); if (*insn == 0xE8410028) _Unwind_SetGRPtr (context, 2, context->cfa + 40); } } /* If PC is at a sigreturn trampoline, return a pointer to the sigcontext. Otherwise return NULL. */ static struct gcc_sigcontext * get_sigcontext (struct _Unwind_Context *context) { const unsigned char *pc = context->ra; /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ if (*(unsigned int *) (pc+0) != 0x38210000 + SIGNAL_FRAMESIZE || *(unsigned int *) (pc+8) != 0x44000002) return NULL; if (*(unsigned int *) (pc+4) == 0x38000077) { struct sigframe { char gap[SIGNAL_FRAMESIZE]; struct gcc_sigcontext sigctx; } *rt_ = context->cfa; return &rt_->sigctx; } else if (*(unsigned int *) (pc+4) == 0x380000AC) { struct rt_sigframe { int tramp[6]; void *pinfo; struct gcc_ucontext *puc; } *rt_ = (struct rt_sigframe *) pc; return &rt_->puc->uc_mcontext; } return NULL; } #else /* !__powerpc64__ */ enum { SIGNAL_FRAMESIZE = 64 }; static struct gcc_sigcontext * get_sigcontext (struct _Unwind_Context *context) { const unsigned char *pc = context->ra; /* li r0, 0x7777; sc (sigreturn old) */ /* li r0, 0x0077; sc (sigreturn new) */ /* li r0, 0x6666; sc (rt_sigreturn old) */ /* li r0, 0x00AC; sc (rt_sigreturn new) */ if (*(unsigned int *) (pc+4) != 0x44000002) return NULL; if (*(unsigned int *) (pc+0) == 0x38007777 || *(unsigned int *) (pc+0) == 0x38000077) { struct sigframe { char gap[SIGNAL_FRAMESIZE]; struct gcc_sigcontext sigctx; } *rt_ = context->cfa; return &rt_->sigctx; } else if (*(unsigned int *) (pc+0) == 0x38006666 || *(unsigned int *) (pc+0) == 0x380000AC) { struct rt_sigframe { char gap[SIGNAL_FRAMESIZE + 16]; char siginfo[128]; struct gcc_ucontext uc; } *rt_ = context->cfa; return &rt_->uc.uc_mcontext; } return NULL; } #endif /* Do code reading to identify a signal frame, and set the frame state data appropriately. See unwind-dw2.c for the structs. */ #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state static _Unwind_Reason_Code ppc_fallback_frame_state (struct _Unwind_Context *context, _Unwind_FrameState *fs) { struct gcc_sigcontext *sc = get_sigcontext (context); long new_cfa; int i; if (sc == NULL) return _URC_END_OF_STACK; new_cfa = sc->regs->gpr[STACK_POINTER_REGNUM]; fs->cfa_how = CFA_REG_OFFSET; fs->cfa_reg = STACK_POINTER_REGNUM; fs->cfa_offset = new_cfa - (long) context->cfa; for (i = 0; i < 32; i++) if (i != STACK_POINTER_REGNUM) { fs->regs.reg[i].how = REG_SAVED_OFFSET; fs->regs.reg[i].loc.offset = (long)&(sc->regs->gpr[i]) - new_cfa; } fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET; fs->regs.reg[CR2_REGNO].loc.offset = (long)&(sc->regs->ccr) - new_cfa; fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET; fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long)&(sc->regs->link) - new_cfa; fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET; fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long)&(sc->regs->nip) - new_cfa; fs->retaddr_column = ARG_POINTER_REGNUM; return _URC_NO_REASON; }