|  | /* DWARF2 EH unwinding support for S/390 Linux. | 
|  | Copyright (C) 2004-2020 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; | 
|  | } |