1169689Skan/* DWARF2 EH unwinding support for SPARC Solaris.
2169689Skan   Copyright (C) 2009-2022 Free Software Foundation, Inc.
3169689Skan
4169689SkanThis file is part of GCC.
5169689Skan
6169689SkanGCC is free software; you can redistribute it and/or modify
7169689Skanit under the terms of the GNU General Public License as published by
8169689Skanthe Free Software Foundation; either version 3, or (at your option)
9169689Skanany later version.
10169689Skan
11169689SkanGCC is distributed in the hope that it will be useful,
12169689Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of
13169689SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14169689SkanGNU General Public License for more details.
15169689Skan
16Under Section 7 of GPL version 3, you are granted additional
17permissions described in the GCC Runtime Library Exception, version
183.1, as published by the Free Software Foundation.
19
20You should have received a copy of the GNU General Public License and
21a copy of the GCC Runtime Library Exception along with this program;
22see 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
36static int
37sparc64_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
83static void
84sparc64_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
109static int
110sparc_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
156static _Unwind_Reason_Code
157MD_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