xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/config/sparc/sol2-unwind.h (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /* DWARF2 EH unwinding support for SPARC Solaris.
2    Copyright (C) 2009-2013 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 9 - single-threaded
40 	----------------------------
41 	The pattern changes slightly in different versions of the
42 	operating system, so we skip the comparison against pc[-6] for
43 	Solaris 9.
44 
45 	<sigacthandler+24>:  sra  %i0, 0, %l1
46 
47 	Solaris 9 5/02:
48 	<sigacthandler+28>:  ldx  [ %o2 + 0xf68 ], %g5
49 	Solaris 9 9/05:
50 	<sigacthandler+28>:  ldx  [ %o2 + 0xe50 ], %g5
51 
52 	<sigacthandler+32>:  sllx  %l1, 3, %g4
53 	<sigacthandler+36>:  mov  %l1, %o0
54 	<sigacthandler+40>:  ldx  [ %g4 + %g5 ], %l0
55 	<sigacthandler+44>:  call  %l0
56 	<sigacthandler+48>:  mov  %i2, %o2
57 	<sigacthandler+52>:  cmp  %l1, 8	<--- PC  */
58       (   pc[-7] == 0xa33e2000
59        /* skip pc[-6] */
60        && pc[-5] == 0x892c7003
61        && pc[-4] == 0x90100011
62        && pc[-3] == 0xe0590005
63        && pc[-2] == 0x9fc40000
64        && pc[-1] == 0x9410001a
65        && pc[ 0] == 0x80a46008))
66     {
67       /* We need to move up one frame:
68 
69 		<signal handler>	<-- context->cfa
70 		sigacthandler
71 		<kernel>
72       */
73       *nframes = 1;
74       return 1;
75     }
76 
77   if (/* Solaris 8+ - multi-threaded
78 	----------------------------
79 	<__sighndlr>:        save  %sp, -176, %sp
80 	<__sighndlr+4>:      mov  %i0, %o0
81 	<__sighndlr+8>:      mov  %i1, %o1
82 	<__sighndlr+12>:     call  %i3
83 	<__sighndlr+16>:     mov  %i2, %o2
84 	<__sighndlr+20>:     ret 		<--- PC
85 	<__sighndlr+24>:     restore  */
86          pc[-5] == 0x9de3bf50
87       && pc[-4] == 0x90100018
88       && pc[-3] == 0x92100019
89       && pc[-2] == 0x9fc6c000
90       && pc[-1] == 0x9410001a
91       && pc[ 0] == 0x81c7e008
92       && pc[ 1] == 0x81e80000)
93     {
94       /* We have observed different calling frames among different
95 	 versions of the operating system, so that we need to
96 	 discriminate using the upper frame.  We look for the return
97 	 address of the caller frame (there is an offset of 15 double
98 	 words between the frame address and the place where this return
99 	 address is stored) in order to do some more pattern matching.  */
100       unsigned int cuh_pattern
101 	= *(unsigned int *)(*(unsigned long *)(cfa + 15*8) - 4);
102 
103       if (cuh_pattern == 0x92100019)
104 	/* This matches the call_user_handler pattern for Solaris 11.
105 	   This is the same setup as for Solaris 9, see below.  */
106 	*nframes = 3;
107 
108       else if (cuh_pattern == 0xd25fa7ef)
109 	{
110 	  /* This matches the call_user_handler pattern for Solaris 10.
111 	     There are 2 cases so we look for the return address of the
112 	     caller's caller frame in order to do more pattern matching.  */
113 	  unsigned long sah_address = *(unsigned long *)(cfa + 176 + 15*8);
114 
115           if (sah_address && *(unsigned int *)(sah_address - 4) == 0x92100019)
116 	    /* This is the same setup as for Solaris 9, see below.  */
117 	    *nframes = 3;
118 	  else
119 	    /* The sigacthandler frame isn't present in the chain.
120 	       We need to move up two frames:
121 
122 		<signal handler>	<-- context->cfa
123 		__sighndlr
124 		call_user_handler frame
125 		<kernel>
126 	    */
127 	    *nframes = 2;
128 	}
129 
130       else if (cuh_pattern == 0x9410001a || cuh_pattern == 0x94100013)
131 	/* This matches the call_user_handler pattern for Solaris 9.
132 	   We need to move up three frames:
133 
134 		<signal handler>	<-- context->cfa
135 		__sighndlr
136 		call_user_handler
137 		sigacthandler
138 		<kernel>
139 	*/
140 	*nframes = 3;
141 
142       return 1;
143     }
144 
145   return 0;
146 }
147 
148 #define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
149 
150 #define MD_FROB_UPDATE_CONTEXT sparc64_frob_update_context
151 
152 static void
153 sparc64_frob_update_context (struct _Unwind_Context *context,
154 			     _Unwind_FrameState *fs)
155 {
156   /* The column of %sp contains the old CFA, not the old value of %sp.
157      The CFA offset already comprises the stack bias so, when %sp is the
158      CFA register, we must avoid counting the stack bias twice.  Do not
159      do that for signal frames as the offset is artificial for them.  */
160   if (fs->regs.cfa_reg == __builtin_dwarf_sp_column ()
161       && fs->regs.cfa_how == CFA_REG_OFFSET
162       && fs->regs.cfa_offset != 0
163       && !fs->signal_frame)
164     {
165       long i;
166 
167       context->cfa -= STACK_BIAS;
168 
169       for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)
170 	if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
171 	  _Unwind_SetGRPtr (context, i,
172 			    _Unwind_GetGRPtr (context, i) - STACK_BIAS);
173     }
174 }
175 
176 #else
177 
178 #define IS_SIGHANDLER sparc_is_sighandler
179 
180 static int
181 sparc_is_sighandler (unsigned int *pc, void *cfa, int *nframes)
182 {
183   if (/* Solaris 9 - single-threaded
184         ----------------------------
185 	The pattern changes slightly in different versions of the operating
186 	system, so we skip the comparison against pc[-6].
187 
188 	<sigacthandler+16>:  add  %o1, %o7, %o3
189 	<sigacthandler+20>:  mov  %i1, %o1
190 
191 	<sigacthandler+24>:  ld  [ %o3 + <offset> ], %o2
192 
193 	<sigacthandler+28>:  sll  %i0, 2, %o0
194 	<sigacthandler+32>:  ld  [ %o0 + %o2 ], %l0
195 	<sigacthandler+36>:  mov  %i0, %o0
196 	<sigacthandler+40>:  call  %l0
197 	<sigacthandler+44>:  mov  %i2, %o2
198 	<sigacthandler+48>:  cmp  %i0, 8	<--- PC  */
199          pc[-8] == 0x9602400f
200       && pc[-7] == 0x92100019
201       /* skip pc[-6] */
202       && pc[-5] == 0x912e2002
203       && pc[-4] == 0xe002000a
204       && pc[-3] == 0x90100018
205       && pc[-2] == 0x9fc40000
206       && pc[-1] == 0x9410001a
207       && pc[ 0] == 0x80a62008)
208     {
209       /* We need to move up one frame:
210 
211 		<signal handler>	<-- context->cfa
212 		sigacthandler
213 		<kernel>
214       */
215       *nframes = 1;
216       return 1;
217     }
218 
219   if(/* Solaris 8+ - multi-threaded
220        ----------------------------
221        <__sighndlr>:	save  %sp, -96, %sp
222        <__sighndlr+4>:	mov  %i0, %o0
223        <__sighndlr+8>:	mov  %i1, %o1
224        <__sighndlr+12>:	call  %i3
225        <__sighndlr+16>:	mov  %i2, %o2
226        <__sighndlr+20>:	ret 		<--- PC
227        <__sighndlr+24>:	restore  */
228         pc[-5] == 0x9de3bfa0
229      && pc[-4] == 0x90100018
230      && pc[-3] == 0x92100019
231      && pc[-2] == 0x9fc6c000
232      && pc[-1] == 0x9410001a
233      && pc[ 0] == 0x81c7e008
234      && pc[ 1] == 0x81e80000)
235     {
236       /* We have observed different calling frames among different
237 	 versions of the operating system, so that we need to
238 	 discriminate using the upper frame.  We look for the return
239 	 address of the caller frame (there is an offset of 15 words
240 	 between the frame address and the place where this return
241 	 address is stored) in order to do some more pattern matching.  */
242       unsigned int cuh_pattern
243 	= *(unsigned int *)(*(unsigned int *)(cfa + 15*4) - 4);
244 
245       if (cuh_pattern == 0x92100019)
246 	/* This matches the call_user_handler pattern for Solaris 11.
247 	   This is the same setup as for Solaris 9, see below.  */
248 	*nframes = 3;
249 
250       else if (cuh_pattern == 0xd407a04c)
251 	{
252 	  /* This matches the call_user_handler pattern for Solaris 10.
253 	     There are 2 cases so we look for the return address of the
254 	     caller's caller frame in order to do more pattern matching.  */
255 	  unsigned int sah_address = *(unsigned int *)(cfa + 96 + 15*4);
256 
257           if (sah_address && *(unsigned int *)(sah_address - 4) == 0x92100019)
258 	    /* This is the same setup as for Solaris 9, see below.  */
259 	    *nframes = 3;
260 	  else
261 	    /* The sigacthandler frame isn't present in the chain.
262 	       We need to move up two frames:
263 
264 		<signal handler>	<-- context->cfa
265 		__sighndlr
266 		call_user_handler frame
267 		<kernel>
268 	    */
269 	    *nframes = 2;
270 	}
271 
272       else if (cuh_pattern == 0x9410001a || cuh_pattern == 0x9410001b)
273 	/* This matches the call_user_handler pattern for Solaris 9.
274 	   We need to move up three frames:
275 
276 		<signal handler>	<-- context->cfa
277 		__sighndlr
278 		call_user_handler
279 		sigacthandler
280 		<kernel>
281 	*/
282 	*nframes = 3;
283 
284       return 1;
285     }
286 
287   return 0;
288 }
289 
290 #define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
291 
292 #endif
293 
294 static _Unwind_Reason_Code
295 MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context *context,
296 			     _Unwind_FrameState *fs)
297 {
298   void *pc = context->ra;
299   struct frame *fp = (struct frame *) context->cfa;
300   int nframes;
301   void *this_cfa = context->cfa;
302   long new_cfa;
303   void *ra_location, *shifted_ra_location;
304   mcontext_t *mctx;
305   int i;
306 
307   /* Deal with frame-less function from which a signal was raised.  */
308   if (_Unwind_IsSignalFrame (context))
309     {
310       /* The CFA is by definition unmodified in this case.  */
311       fs->regs.cfa_how = CFA_REG_OFFSET;
312       fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
313       fs->regs.cfa_offset = 0;
314 
315       /* This is the canonical RA column.  */
316       fs->retaddr_column = 15;
317 
318       return _URC_NO_REASON;
319     }
320 
321   if (IS_SIGHANDLER (pc, this_cfa, &nframes))
322     {
323       struct handler_args {
324 	struct frame frwin;
325 	ucontext_t ucontext;
326       } *handler_args;
327       ucontext_t *ucp;
328 
329       /* context->cfa points into the frame after the saved frame pointer and
330          saved pc (struct frame).
331 
332          The ucontext_t structure is in the kernel frame after a struct
333          frame.  Since the frame sizes vary even within OS releases, we
334          need to walk the stack to get there.  */
335 
336       for (i = 0; i < nframes; i++)
337 	fp = (struct frame *) ((char *)fp->fr_savfp + STACK_BIAS);
338 
339       handler_args = (struct handler_args *) fp;
340       ucp = &handler_args->ucontext;
341       mctx = &ucp->uc_mcontext;
342     }
343 
344   /* Exit if the pattern at the return address does not match the
345      previous three patterns.  */
346   else
347     return _URC_END_OF_STACK;
348 
349   new_cfa = mctx->gregs[REG_SP];
350   /* The frame address is %sp + STACK_BIAS in 64-bit mode.  */
351   new_cfa += STACK_BIAS;
352 
353   fs->regs.cfa_how = CFA_REG_OFFSET;
354   fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
355   fs->regs.cfa_offset = new_cfa - (long) this_cfa;
356 
357   /* Restore global and out registers (in this order) from the
358      ucontext_t structure, uc_mcontext.gregs field.  */
359   for (i = 1; i < 16; i++)
360     {
361       /* We never restore %sp as everything is purely CFA-based.  */
362       if ((unsigned int) i == __builtin_dwarf_sp_column ())
363 	continue;
364 
365       /* First the global registers and then the out registers.  */
366       fs->regs.reg[i].how = REG_SAVED_OFFSET;
367       fs->regs.reg[i].loc.offset = (long)&mctx->gregs[REG_Y + i] - new_cfa;
368     }
369 
370   /* Just above the stack pointer there are 16 extended words in which
371      the register window (in and local registers) was saved.  */
372   for (i = 0; i < 16; i++)
373     {
374       fs->regs.reg[i + 16].how = REG_SAVED_OFFSET;
375       fs->regs.reg[i + 16].loc.offset = i*sizeof(long);
376     }
377 
378   /* Check whether we need to restore FPU registers.  */
379   if (mctx->fpregs.fpu_qcnt)
380     {
381       for (i = 0; i < 32; i++)
382 	{
383 	  fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
384 	  fs->regs.reg[i + 32].loc.offset
385 	    = (long)&mctx->fpregs.fpu_fr.fpu_regs[i] - new_cfa;
386 	}
387 
388 #ifdef __arch64__
389       /* For 64-bit, fpu_fr.fpu_dregs contains 32 instead of 16 doubles.  */
390       for (i = 32; i < 64; i++)
391 	{
392 	  if (i > 32 && (i & 1))
393 	    continue;
394 
395 	  fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
396 	  fs->regs.reg[i + 32].loc.offset
397 	    = (long)&mctx->fpregs.fpu_fr.fpu_dregs[i/2] - new_cfa;
398 	}
399 #endif
400     }
401 
402   /* State the rules to find the kernel's code "return address", which is
403      the address of the active instruction when the signal was caught.
404      On the SPARC, since RETURN_ADDR_OFFSET (essentially 8) is defined, we
405      need to preventively subtract it from the purported return address.  */
406   ra_location = &mctx->gregs[REG_PC];
407   shifted_ra_location = &mctx->gregs[REG_Y];
408   *(void **)shifted_ra_location = *(void **)ra_location - 8;
409   fs->retaddr_column = 0;
410   fs->regs.reg[0].how = REG_SAVED_OFFSET;
411   fs->regs.reg[0].loc.offset = (long)shifted_ra_location - new_cfa;
412   fs->signal_frame = 1;
413 
414   return _URC_NO_REASON;
415 }
416