1 /* DWARF2 EH unwinding support for SPARC Solaris.
2    Copyright (C) 2009-2020 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
sparc64_is_sighandler(unsigned int * pc,void * cfa,int * nframes)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
sparc64_frob_update_context(struct _Unwind_Context * context,_Unwind_FrameState * fs)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.reg[i].how == 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
sparc_is_sighandler(unsigned int * pc,void * cfa,int * nframes)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
MD_FALLBACK_FRAME_STATE_FOR(struct _Unwind_Context * context,_Unwind_FrameState * fs)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.reg[i].how = 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.reg[i + 16].how = 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.reg[i + 32].how = 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.reg[i + 32].how = 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.reg[0].how = 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