(root)/
gcc-13.2.0/
libgcc/
config/
pa/
linux-unwind.h
       1  /* DWARF2 EH unwinding support for PA Linux.
       2     Copyright (C) 2004-2023 Free Software Foundation, Inc.
       3  
       4  This file is part of GCC.
       5  
       6  GCC is free software; you can redistribute it and/or modify
       7  it under the terms of the GNU General Public License as published by
       8  the Free Software Foundation; either version 3, or (at your option)
       9  any later version.
      10  
      11  GCC is distributed in the hope that it will be useful,
      12  but WITHOUT ANY WARRANTY; without even the implied warranty of
      13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14  GNU General Public License for more details.
      15  
      16  Under Section 7 of GPL version 3, you are granted additional
      17  permissions described in the GCC Runtime Library Exception, version
      18  3.1, as published by the Free Software Foundation.
      19  
      20  You should have received a copy of the GNU General Public License and
      21  a copy of the GCC Runtime Library Exception along with this program;
      22  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      23  <http://www.gnu.org/licenses/>.  */
      24  
      25  
      26  /* Do code reading to identify a signal frame, and set the frame
      27     state data appropriately.  See unwind-dw2.c for the structs.  */
      28  
      29  /* Don't use this if inhibit_libc is set.
      30     The build for this target will fail trying to include missing headers. */
      31  #ifndef inhibit_libc
      32  #include <signal.h>
      33  #include <sys/ucontext.h>
      34  
      35  /* Return TRUE if read access to *P is allowed.  */
      36  
      37  static inline long
      38  pa32_read_access_ok (void *p)
      39  {
      40    long ret;
      41  
      42    __asm__ ("proberi (%1),3,%0" : "=r" (ret) : "r" (p) :);
      43    return ret;
      44  }
      45  
      46  /* Unfortunately, because of various bugs and changes to the kernel,
      47     we have several cases to deal with.
      48  
      49     In 2.4, the signal trampoline is 4 words, and (CONTEXT)->ra should
      50     point directly at the beginning of the trampoline and struct rt_sigframe.
      51  
      52     In <= 2.6.5-rc2-pa3, the signal trampoline is 9 words, and 
      53     (CONTEXT)->ra points at the 4th word in the trampoline structure.  This 
      54     is wrong, it should point at the 5th word.  This is fixed in 2.6.5-rc2-pa4.
      55  
      56     To detect these cases, we first take (CONTEXT)->ra, align it to 64-bytes
      57     to get the beginning of the signal frame, and then check offsets 0, 4
      58     and 5 to see if we found the beginning of the trampoline.  This will
      59     tell us how to locate the sigcontext structure.
      60  
      61     Note that with a 2.4 64-bit kernel, the signal context is not properly
      62     passed back to userspace so the unwind will not work correctly.
      63  
      64     There is also a bug in various glibc versions.  The (CONTEXT)->ra
      65     for the outermost frame is not marked as undefined, so we need to
      66     check whether read access is allowed for all the accesses used in
      67     searching for the signal trampoline.  */
      68  
      69  #define MD_FALLBACK_FRAME_STATE_FOR pa32_fallback_frame_state
      70  
      71  static _Unwind_Reason_Code
      72  pa32_fallback_frame_state (struct _Unwind_Context *context,
      73  			   _Unwind_FrameState *fs)
      74  {
      75    unsigned long sp = (unsigned long)context->ra & ~63;
      76    unsigned int *pc = (unsigned int *)sp;
      77    unsigned long off;
      78    _Unwind_Ptr new_cfa;
      79    int i;
      80    struct sigcontext *sc;
      81    struct rt_sigframe {
      82      siginfo_t info;
      83      ucontext_t uc;
      84    } *frame;
      85  
      86    /* rt_sigreturn trampoline:
      87       3419000x ldi 0, %r25 or ldi 1, %r25   (x = 0 or 2)
      88       3414015a ldi __NR_rt_sigreturn, %r20
      89       e4008200 be,l 0x100(%sr2, %r0), %sr0, %r31
      90       08000240 nop  */
      91  
      92    if (pa32_read_access_ok (pc)
      93        && (pc[0] == 0x34190000 || pc[0] == 0x34190002))
      94      off = 4*4;
      95    else if (pa32_read_access_ok (&pc[4])
      96  	   && (pc[4] == 0x34190000 || pc[4] == 0x34190002))
      97      {
      98        pc += 4;
      99        off = 10 * 4;
     100      }
     101    else if (pa32_read_access_ok (&pc[5])
     102  	   && (pc[5] == 0x34190000 || pc[5] == 0x34190002))
     103      {
     104        pc += 5;
     105        off = 10 * 4;
     106      }
     107    else
     108      {
     109        /* We may have to unwind through an alternate signal stack.
     110  	 We assume that the alignment of the alternate signal stack
     111  	 is BIGGEST_ALIGNMENT (i.e., that it has been allocated using
     112  	 malloc).  As a result, we can't distinguish trampolines
     113  	 used prior to 2.6.5-rc2-pa4.  However after 2.6.5-rc2-pa4,
     114  	 the return address of a signal trampoline will be on an odd
     115  	 word boundary and we can then determine the frame offset.  */
     116        sp = (unsigned long)context->ra;
     117        pc = (unsigned int *)sp;
     118        if ((sp & 4)
     119  	  && pa32_read_access_ok (pc)
     120  	  && (pc[0] == 0x34190000 || pc[0] == 0x34190002))
     121  	off = 5 * 4;
     122        else
     123  	return _URC_END_OF_STACK;
     124      }
     125  
     126    if (!pa32_read_access_ok (&pc[3])
     127        || pc[1] != 0x3414015a
     128        || pc[2] != 0xe4008200
     129        || pc[3] != 0x08000240)
     130      return _URC_END_OF_STACK;
     131  
     132    frame = (struct rt_sigframe *)(sp + off);
     133    sc = (struct sigcontext *)&frame->uc.uc_mcontext;
     134  
     135    new_cfa = sc->sc_gr[30];
     136    fs->regs.cfa_how = CFA_REG_OFFSET;
     137    fs->regs.cfa_reg = 30;
     138    fs->regs.cfa_offset = new_cfa - (long) context->cfa;
     139    for (i = 1; i <= 31; i++)
     140      {
     141        fs->regs.how[i] = REG_SAVED_OFFSET;
     142        fs->regs.reg[i].loc.offset = (long)&sc->sc_gr[i] - new_cfa;
     143      }
     144    for (i = 4; i <= 31; i++)
     145      {
     146        /* FP regs have left and right halves */
     147        fs->regs.how[2*i+24] = REG_SAVED_OFFSET;
     148        fs->regs.reg[2*i+24].loc.offset
     149  	= (long)&sc->sc_fr[i] - new_cfa;
     150        fs->regs.how[2*i+24+1] = REG_SAVED_OFFSET;
     151        fs->regs.reg[2*i+24+1].loc.offset
     152  	= (long)&sc->sc_fr[i] + 4 - new_cfa;
     153      }
     154    fs->regs.how[88] = REG_SAVED_OFFSET;
     155    fs->regs.reg[88].loc.offset = (long) &sc->sc_sar - new_cfa;
     156    fs->regs.how[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__]
     157      = REG_SAVED_OFFSET;
     158    fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset
     159      = (long) &sc->sc_iaoq[0] - new_cfa;
     160    fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__;
     161    fs->signal_frame = 1;
     162    return _URC_NO_REASON;
     163  }
     164  #endif /* inhibit_libc */