| /* DWARF2 EH unwinding support for S/390 Linux. |
| Copyright (C) 2004-2019 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. */ |
| |
| #define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state |
| |
| static _Unwind_Reason_Code |
| s390_fallback_frame_state (struct _Unwind_Context *context, |
| _Unwind_FrameState *fs) |
| { |
| static const unsigned char dwarf_to_fpr_map[16] = |
| { 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15 }; |
| unsigned char *pc = context->ra; |
| long new_cfa; |
| int i; |
| |
| typedef struct |
| { |
| unsigned long psw_mask; |
| unsigned long psw_addr; |
| unsigned long gprs[16]; |
| unsigned int acrs[16]; |
| unsigned int fpc; |
| unsigned int __pad; |
| double fprs[16]; |
| } __attribute__ ((__aligned__ (8))) sigregs_; |
| |
| sigregs_ *regs; |
| int *signo; |
| |
| /* svc $__NR_sigreturn or svc $__NR_rt_sigreturn */ |
| if (pc[0] != 0x0a || (pc[1] != 119 && pc[1] != 173)) |
| return _URC_END_OF_STACK; |
| |
| /* Legacy frames: |
| old signal mask (8 bytes) |
| pointer to sigregs (8 bytes) - points always to next location |
| sigregs |
| retcode |
| This frame layout was used on kernels < 2.6.9 for non-RT frames, |
| and on kernels < 2.4.13 for RT frames as well. Note that we need |
| to look at RA to detect this layout -- this means that if you use |
| sa_restorer to install a different signal restorer on a legacy |
| kernel, unwinding from signal frames will not work. */ |
| if (context->ra == context->cfa + 16 + sizeof (sigregs_)) |
| { |
| regs = (sigregs_ *)(context->cfa + 16); |
| signo = NULL; |
| } |
| |
| /* New-style RT frame: |
| retcode + alignment (8 bytes) |
| siginfo (128 bytes) |
| ucontext (contains sigregs) */ |
| else if (pc[1] == 173 /* __NR_rt_sigreturn */) |
| { |
| struct ucontext_ |
| { |
| unsigned long uc_flags; |
| struct ucontext_ *uc_link; |
| unsigned long uc_stack[3]; |
| sigregs_ uc_mcontext; |
| } *uc = context->cfa + 8 + 128; |
| |
| regs = &uc->uc_mcontext; |
| signo = context->cfa + sizeof(long); |
| } |
| |
| /* New-style non-RT frame: |
| old signal mask (8 bytes) |
| pointer to sigregs (followed by signal number) */ |
| else |
| { |
| regs = *(sigregs_ **)(context->cfa + 8); |
| signo = (int *)(regs + 1); |
| } |
| |
| new_cfa = regs->gprs[15] + 16*sizeof(long) + 32; |
| fs->regs.cfa_how = CFA_REG_OFFSET; |
| fs->regs.cfa_reg = 15; |
| fs->regs.cfa_offset = |
| new_cfa - (long) context->cfa + 16*sizeof(long) + 32; |
| |
| for (i = 0; i < 16; i++) |
| { |
| fs->regs.reg[i].how = REG_SAVED_OFFSET; |
| fs->regs.reg[i].loc.offset = |
| (long)®s->gprs[i] - new_cfa; |
| } |
| for (i = 0; i < 16; i++) |
| { |
| fs->regs.reg[16+i].how = REG_SAVED_OFFSET; |
| fs->regs.reg[16+i].loc.offset = |
| (long)®s->fprs[dwarf_to_fpr_map[i]] - new_cfa; |
| } |
| |
| /* Load return addr from PSW into dummy register 32. */ |
| |
| fs->regs.reg[32].how = REG_SAVED_OFFSET; |
| fs->regs.reg[32].loc.offset = (long)®s->psw_addr - new_cfa; |
| fs->retaddr_column = 32; |
| /* SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr |
| after the faulting instruction rather than before it. |
| Don't set FS->signal_frame in that case. */ |
| if (!signo || (*signo != 4 && *signo != 5 && *signo != 8)) |
| fs->signal_frame = 1; |
| |
| return _URC_NO_REASON; |
| } |