| /* DWARF2 EH unwinding support for MIPS IRIX 6. |
| Copyright (C) 2011 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 3, 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. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* Do code reading to identify a signal frame, and set the frame |
| state data appropriately. See unwind-dw2.c for the structs. */ |
| |
| /* This code was developed-for and only tested-in limited ABI |
| configurations. Characterize that. */ |
| |
| #if defined (_ABIN32) || defined (_ABI64) |
| #define SUPPORTED_ABI 1 |
| #else |
| #define SUPPORTED_ABI 0 |
| #endif |
| |
| #include <signal.h> |
| |
| #define MD_FALLBACK_FRAME_STATE_FOR mips_fallback_frame_state |
| |
| /* Look at the code around RA to see if it matches a sighandler caller with a |
| sigcontext_t * argument (SA_SIGINFO cleared). Return that pointer argument |
| if it does match, or 0 otherwise. */ |
| |
| static sigcontext_t * |
| sigcontext_for (unsigned int *ra, void *cfa) |
| { |
| /* IRIX 6.5, mono-threaded application. We're lucky enough to be able |
| to expect a short very sighandler specific sequence around. |
| |
| <_sigtramp+124>: li v0,1088 (SYS_sigreturn) |
| <_sigtramp+128>: syscall */ |
| |
| if ( ra[6] == 0x24020440 |
| && ra[7] == 0x0000000c) |
| return (sigcontext_t *)(cfa + 0x30); |
| |
| /* IRIX 6.5 variants, multi-threaded application, pthreads. Nothing really |
| sighandler specific handy, so match a fairly long constant sequence. */ |
| |
| #if _MIPS_SIM == _ABIN32 |
| /* |
| <sig_fixup_mask+40>: sd s0,0(sp) |
| <sig_fixup_mask+44>: sll ra,a0,0x2 |
| <sig_fixup_mask+48>: addiu t9,t9,-28584/-28456/-28448 |
| <sig_fixup_mask+52>: lw s0,3804(at) |
| <sig_fixup_mask+56>: addu t9,t9,ra |
| <sig_fixup_mask+60>: lw t9,0(t9) |
| <sig_fixup_mask+64>: ld at,3696(at) |
| <sig_fixup_mask+68>: ld s2,88(s0) |
| <sig_fixup_mask+72>: jalr t9 |
| <sig_fixup_mask+76>: sd at,88(s0) */ |
| if ( ra[-10] == 0xffb00000 |
| && ra[ -9] == 0x0004f880 |
| && (ra[-8] == 0x27399058 |
| || ra[-8] == 0x273990d8 |
| || ra[-8] == 0x273990e0) |
| && ra[ -7] == 0x8c300edc |
| && ra[ -6] == 0x033fc821 |
| && ra[ -5] == 0x8f390000 |
| && ra[ -4] == 0xdc210e70 |
| && ra[ -3] == 0xde120058 |
| && ra[ -2] == 0x0320f809 |
| && ra[ -1] == 0xfe010058) |
| |
| #elif _MIPS_SIM == _ABI64 |
| /* |
| <sig_fixup_mask+44>: sd s0,0(sp) |
| <sig_fixup_mask+48>: daddu t9,t9,ra |
| <sig_fixup_mask+52>: dsll ra,a0,0x3 |
| <sig_fixup_mask+56>: ld s0,3880(at) |
| <sig_fixup_mask+60>: daddu t9,t9,ra |
| <sig_fixup_mask+64>: ld t9,0(t9) |
| <sig_fixup_mask+68>: ld at,3696(at) |
| <sig_fixup_mask+72>: ld s2,152(s0) |
| <sig_fixup_mask+76>: jalr t9 |
| <sig_fixup_mask+80>: sd at,152(s0) */ |
| if ( ra[-10] == 0xffb00000 |
| && ra[ -9] == 0x033fc82d |
| && ra[ -8] == 0x0004f8f8 |
| && ra[ -7] == 0xdc300f28 |
| && ra[ -6] == 0x033fc82d |
| && ra[ -5] == 0xdf390000 |
| && ra[ -4] == 0xdc210e70 |
| && ra[ -3] == 0xde120098 |
| && ra[ -2] == 0x0320f809 |
| && ra[ -1] == 0xfe010098) |
| #endif |
| return (sigcontext_t *)(cfa + 0x60); |
| |
| return 0; |
| } |
| |
| #define SIGCTX_GREG_ADDR(REGNO,SIGCTX) \ |
| ((void *) &(SIGCTX)->sc_regs[REGNO]) |
| |
| #define SIGCTX_FPREG_ADDR(REGNO,SIGCTX) \ |
| ((void *) &(SIGCTX)->sc_fpregs[REGNO]) |
| |
| static _Unwind_Reason_Code |
| mips_fallback_frame_state (struct _Unwind_Context *context, |
| _Unwind_FrameState *fs) |
| { |
| /* Return address and CFA of the frame we're attempting to unwind through, |
| possibly a signal handler. */ |
| void *ctx_ra = (void *)context->ra; |
| void *ctx_cfa = (void *)context->cfa; |
| |
| /* CFA of the intermediate abstract kernel frame between the interrupted |
| code and the signal handler, if we're indeed unwinding through a signal |
| handler. */ |
| void *k_cfa; |
| |
| /* Pointer to the sigcontext_t structure pushed by the kernel when we're |
| unwinding through a signal handler setup with SA_SIGINFO cleared. */ |
| sigcontext_t *sigctx; |
| int i; |
| |
| if (! SUPPORTED_ABI) |
| return _URC_END_OF_STACK; |
| |
| sigctx = sigcontext_for (ctx_ra, ctx_cfa); |
| |
| if (sigctx == 0) |
| return _URC_END_OF_STACK; |
| |
| /* The abstract kernel frame's CFA is extactly the stack pointer |
| value at the interruption point. */ |
| k_cfa = *(void **)SIGCTX_GREG_ADDR (CTX_SP, sigctx); |
| |
| /* State the rules to compute the CFA we have the value of: use the |
| previous CFA and offset by the difference between the two. See |
| uw_update_context_1 for the supporting details. */ |
| fs->regs.cfa_how = CFA_REG_OFFSET; |
| fs->regs.cfa_reg = __builtin_dwarf_sp_column (); |
| fs->regs.cfa_offset = k_cfa - ctx_cfa; |
| |
| /* Fill the internal frame_state structure with information stating where |
| each register of interest can be found from the CFA. */ |
| for (i = 0; i <= 31; i ++) |
| { |
| fs->regs.reg[i].how = REG_SAVED_OFFSET; |
| fs->regs.reg[i].loc.offset = SIGCTX_GREG_ADDR (i, sigctx) - k_cfa; |
| } |
| |
| for (i = 0; i <= 31; i ++) |
| { |
| fs->regs.reg[32+i].how = REG_SAVED_OFFSET; |
| fs->regs.reg[32+i].loc.offset = SIGCTX_FPREG_ADDR (i, sigctx) - k_cfa; |
| } |
| |
| /* State the rules to find the kernel's code "return address", which is the |
| address of the active instruction when the signal was caught. */ |
| fs->retaddr_column = DWARF_FRAME_RETURN_COLUMN; |
| fs->regs.reg[fs->retaddr_column].how = REG_SAVED_OFFSET; |
| fs->regs.reg[fs->retaddr_column].loc.offset = (void *)&sigctx->sc_pc - k_cfa; |
| fs->signal_frame = 1; |
| |
| return _URC_NO_REASON; |
| } |