Home | History | Annotate | Line # | Download | only in sparc
      1 /* DWARF2 EH unwinding support for SPARC Solaris.
      2    Copyright (C) 2009-2024 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 /* Do code reading to identify a signal frame, and set the frame
     26    state data appropriately.  See unwind-dw2.c for the structs.  */
     27 
     28 #include <ucontext.h>
     29 #include <sys/frame.h>
     30 #include <sys/stack.h>
     31 
     32 #ifdef __arch64__
     33 
     34 #define IS_SIGHANDLER sparc64_is_sighandler
     35 
     36 static int
     37 sparc64_is_sighandler (unsigned int *pc, void *cfa, int *nframes)
     38 {
     39   if (/* Solaris 8+ - multi-threaded
     40 	----------------------------
     41 	<__sighndlr>:        save  %sp, -176, %sp
     42 	<__sighndlr+4>:      mov  %i0, %o0
     43 	<__sighndlr+8>:      mov  %i1, %o1
     44 	<__sighndlr+12>:     call  %i3
     45 	<__sighndlr+16>:     mov  %i2, %o2
     46 	<__sighndlr+20>:     ret 		<--- PC
     47 	<__sighndlr+24>:     restore  */
     48          pc[-5] == 0x9de3bf50
     49       && pc[-4] == 0x90100018
     50       && pc[-3] == 0x92100019
     51       && pc[-2] == 0x9fc6c000
     52       && pc[-1] == 0x9410001a
     53       && pc[ 0] == 0x81c7e008
     54       && pc[ 1] == 0x81e80000)
     55     {
     56       /* We have observed different calling frames among different
     57 	 versions of the operating system, so that we need to
     58 	 discriminate using the upper frame.  We look for the return
     59 	 address of the caller frame (there is an offset of 15 double
     60 	 words between the frame address and the place where this return
     61 	 address is stored) in order to do some more pattern matching.  */
     62       unsigned int cuh_pattern
     63 	= *(unsigned int *)(*(unsigned long *)(cfa + 15*8) - 4);
     64 
     65       if (cuh_pattern == 0x92100019)
     66 	/* This matches the call_user_handler pattern in Solaris 11
     67 	   libc.so.1:
     68 
     69 	   <call_user_handler+864>:     mov  %i1, %o1
     70 	   <call_user_handler+868>:     call __sighndlr  */
     71 	*nframes = 3;
     72 
     73       return 1;
     74     }
     75 
     76   return 0;
     77 }
     78 
     79 #define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
     80 
     81 #define MD_FROB_UPDATE_CONTEXT sparc64_frob_update_context
     82 
     83 static void
     84 sparc64_frob_update_context (struct _Unwind_Context *context,
     85 			     _Unwind_FrameState *fs)
     86 {
     87   /* The column of %sp contains the old CFA, not the old value of %sp.
     88      The CFA offset already comprises the stack bias so, when %sp is the
     89      CFA register, we must avoid counting the stack bias twice.  */
     90   if (fs->regs.cfa_reg == __builtin_dwarf_sp_column ()
     91       && fs->regs.cfa_how == CFA_REG_OFFSET
     92       && fs->regs.cfa_offset != 0)
     93     {
     94       long i;
     95 
     96       context->cfa -= STACK_BIAS;
     97 
     98       for (i = 0; i < __LIBGCC_DWARF_FRAME_REGISTERS__ + 1; ++i)
     99 	if (fs->regs.how[i] == REG_SAVED_OFFSET)
    100 	  _Unwind_SetGRPtr (context, i,
    101 			    _Unwind_GetGRPtr (context, i) - STACK_BIAS);
    102     }
    103 }
    104 
    105 #else
    106 
    107 #define IS_SIGHANDLER sparc_is_sighandler
    108 
    109 static int
    110 sparc_is_sighandler (unsigned int *pc, void *cfa, int *nframes)
    111 {
    112   if(/* Solaris 8+ - multi-threaded
    113        ----------------------------
    114        <__sighndlr>:	save  %sp, -96, %sp
    115        <__sighndlr+4>:	mov  %i0, %o0
    116        <__sighndlr+8>:	mov  %i1, %o1
    117        <__sighndlr+12>:	call  %i3
    118        <__sighndlr+16>:	mov  %i2, %o2
    119        <__sighndlr+20>:	ret 		<--- PC
    120        <__sighndlr+24>:	restore  */
    121         pc[-5] == 0x9de3bfa0
    122      && pc[-4] == 0x90100018
    123      && pc[-3] == 0x92100019
    124      && pc[-2] == 0x9fc6c000
    125      && pc[-1] == 0x9410001a
    126      && pc[ 0] == 0x81c7e008
    127      && pc[ 1] == 0x81e80000)
    128     {
    129       /* We have observed different calling frames among different
    130 	 versions of the operating system, so that we need to
    131 	 discriminate using the upper frame.  We look for the return
    132 	 address of the caller frame (there is an offset of 15 words
    133 	 between the frame address and the place where this return
    134 	 address is stored) in order to do some more pattern matching.  */
    135       unsigned int cuh_pattern
    136 	= *(unsigned int *)(*(unsigned int *)(cfa + 15*4) - 4);
    137 
    138       if (cuh_pattern == 0x92100019)
    139 	/* This matches the call_user_handler pattern in Solaris 11
    140 	   libc.so.1:
    141 
    142 	   <call_user_handler+876>:     mov  %i1, %o1
    143 	   <call_user_handler+880>:     call __sighndlr  */
    144 	*nframes = 3;
    145 
    146       return 1;
    147     }
    148 
    149   return 0;
    150 }
    151 
    152 #define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
    153 
    154 #endif
    155 
    156 static _Unwind_Reason_Code
    157 MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context *context,
    158 			     _Unwind_FrameState *fs)
    159 {
    160   void *pc = context->ra;
    161   void *this_cfa = context->cfa;
    162   int nframes = 0;
    163   long new_cfa;
    164   void *ra_location, *shifted_ra_location;
    165   mcontext_t *mctx;
    166   int i;
    167 
    168   /* Deal with frame-less function from which a signal was raised.  */
    169   if (_Unwind_IsSignalFrame (context))
    170     {
    171       /* The CFA is by definition unmodified in this case.  */
    172       fs->regs.cfa_how = CFA_REG_OFFSET;
    173       fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
    174       fs->regs.cfa_offset = 0;
    175 
    176       /* This is the canonical RA column.  */
    177       fs->retaddr_column = 15;
    178 
    179       return _URC_NO_REASON;
    180     }
    181 
    182   /* Do some pattern matching at the return address.  */
    183   if (IS_SIGHANDLER (pc, this_cfa, &nframes))
    184     {
    185       struct frame *fp = (struct frame *) this_cfa;
    186       struct handler_args {
    187 	struct frame frwin;
    188 	ucontext_t ucontext;
    189       } *handler_args;
    190       ucontext_t *ucp;
    191 
    192       /* this_cfa points into the frame after the saved frame pointer and
    193          saved pc (struct frame).
    194 
    195          The ucontext_t structure is in the kernel frame after a struct
    196          frame.  Since the frame sizes vary even within OS releases, we
    197          need to walk the stack to get there.  */
    198       for (i = 0; i < nframes; i++)
    199 	fp = (struct frame *) ((char *)fp->fr_savfp + STACK_BIAS);
    200 
    201       handler_args = (struct handler_args *) fp;
    202       ucp = &handler_args->ucontext;
    203       mctx = &ucp->uc_mcontext;
    204     }
    205   else
    206     return _URC_END_OF_STACK;
    207 
    208   /* The frame address is %sp + STACK_BIAS in 64-bit mode.  */
    209   new_cfa = mctx->gregs[REG_SP] + STACK_BIAS;
    210 
    211   fs->regs.cfa_how = CFA_REG_OFFSET;
    212   fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
    213   fs->regs.cfa_offset = new_cfa - (long) this_cfa + STACK_BIAS;
    214 
    215   /* Restore global and out registers (in this order) from the
    216      ucontext_t structure, uc_mcontext.gregs field.  */
    217   for (i = 1; i < 16; i++)
    218     {
    219       /* We never restore %sp as everything is purely CFA-based.  */
    220       if ((unsigned int) i == __builtin_dwarf_sp_column ())
    221 	continue;
    222 
    223       /* First the global registers and then the out registers.  */
    224       fs->regs.how[i] = REG_SAVED_OFFSET;
    225       fs->regs.reg[i].loc.offset = (long)&mctx->gregs[REG_Y + i] - new_cfa;
    226     }
    227 
    228   /* Just above the stack pointer there are 16 extended words in which
    229      the register window (in and local registers) was saved.  */
    230   for (i = 0; i < 16; i++)
    231     {
    232       fs->regs.how[i + 16] = REG_SAVED_OFFSET;
    233       fs->regs.reg[i + 16].loc.offset = i * sizeof(long);
    234     }
    235 
    236   /* Check whether we need to restore FPU registers.  */
    237   if (mctx->fpregs.fpu_qcnt)
    238     {
    239       for (i = 0; i < 32; i++)
    240 	{
    241 	  fs->regs.how[i + 32] = REG_SAVED_OFFSET;
    242 	  fs->regs.reg[i + 32].loc.offset
    243 	    = (long)&mctx->fpregs.fpu_fr.fpu_regs[i] - new_cfa;
    244 	}
    245 
    246 #ifdef __arch64__
    247       /* For 64-bit, fpu_fr.fpu_dregs contains 32 instead of 16 doubles.  */
    248       for (i = 32; i < 64; i++)
    249 	{
    250 	  if (i > 32 && (i & 1))
    251 	    continue;
    252 
    253 	  fs->regs.how[i + 32] = REG_SAVED_OFFSET;
    254 	  fs->regs.reg[i + 32].loc.offset
    255 	    = (long)&mctx->fpregs.fpu_fr.fpu_dregs[i/2] - new_cfa;
    256 	}
    257 #endif
    258     }
    259 
    260   /* State the rules to find the kernel's code "return address", which is
    261      the address of the active instruction when the signal was caught.
    262      On the SPARC, since RETURN_ADDR_OFFSET (essentially 8) is defined, we
    263      need to preventively subtract it from the purported return address.  */
    264   ra_location = &mctx->gregs[REG_PC];
    265   shifted_ra_location = &mctx->gregs[REG_Y];
    266   *(void **)shifted_ra_location = *(void **)ra_location - 8;
    267   fs->retaddr_column = 0;
    268   fs->regs.how[0] = REG_SAVED_OFFSET;
    269   fs->regs.reg[0].loc.offset = (long)shifted_ra_location - new_cfa;
    270 
    271   /* SIGFPE for IEEE-754 exceptions is delivered after the faulting insn
    272      rather than before it, so don't set fs->signal_frame in that case.
    273      We test whether the cexc field of the FSR is zero.  */
    274   if ((mctx->fpregs.fpu_fsr & 0x1f) == 0)
    275     fs->signal_frame = 1;
    276 
    277   return _URC_NO_REASON;
    278 }
    279