]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgcc/config/sparc/sol2-unwind.h
Update copyright years.
[thirdparty/gcc.git] / libgcc / config / sparc / sol2-unwind.h
CommitLineData
cb7d60a6 1/* DWARF2 EH unwinding support for SPARC Solaris.
7adcbafe 2 Copyright (C) 2009-2022 Free Software Foundation, Inc.
cb7d60a6
JR
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 3, or (at your option)
9any later version.
10
11GCC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
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>
ad56a54c
RO
29#include <sys/frame.h>
30#include <sys/stack.h>
cb7d60a6 31
8d2c862d 32#ifdef __arch64__
cb7d60a6 33
ad56a54c 34#define IS_SIGHANDLER sparc64_is_sighandler
cb7d60a6 35
ad56a54c 36static int
bf4db96c 37sparc64_is_sighandler (unsigned int *pc, void *cfa, int *nframes)
cb7d60a6 38{
ad56a54c
RO
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)
cb7d60a6 55 {
bf4db96c
EB
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
0f952eb4 65 if (cuh_pattern == 0x92100019)
d9f069ab
RO
66 /* This matches the call_user_handler pattern in Solaris 11
67 libc.so.1:
68
69 <call_user_handler+864>: mov %i1, %o1
e54b1a92 70 <call_user_handler+868>: call __sighndlr */
bf4db96c 71 *nframes = 3;
0f952eb4 72
ad56a54c 73 return 1;
cb7d60a6
JR
74 }
75
ad56a54c 76 return 0;
cb7d60a6
JR
77}
78
ad56a54c
RO
79#define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
80
cb7d60a6
JR
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
4fcb5d87 89 CFA register, we must avoid counting the stack bias twice. */
cb7d60a6
JR
90 if (fs->regs.cfa_reg == __builtin_dwarf_sp_column ()
91 && fs->regs.cfa_how == CFA_REG_OFFSET
4fcb5d87 92 && fs->regs.cfa_offset != 0)
b11b0904
EB
93 {
94 long i;
95
96 context->cfa -= STACK_BIAS;
97
53d68b9f 98 for (i = 0; i < __LIBGCC_DWARF_FRAME_REGISTERS__ + 1; ++i)
b11b0904
EB
99 if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
100 _Unwind_SetGRPtr (context, i,
101 _Unwind_GetGRPtr (context, i) - STACK_BIAS);
102 }
cb7d60a6
JR
103}
104
105#else
106
ad56a54c
RO
107#define IS_SIGHANDLER sparc_is_sighandler
108
109static int
bf4db96c 110sparc_is_sighandler (unsigned int *pc, void *cfa, int *nframes)
ad56a54c 111{
ad56a54c
RO
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 {
bf4db96c
EB
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
0f952eb4 138 if (cuh_pattern == 0x92100019)
d9f069ab
RO
139 /* This matches the call_user_handler pattern in Solaris 11
140 libc.so.1:
141
142 <call_user_handler+876>: mov %i1, %o1
e54b1a92 143 <call_user_handler+880>: call __sighndlr */
bf4db96c 144 *nframes = 3;
0f952eb4 145
ad56a54c
RO
146 return 1;
147 }
148
149 return 0;
150}
151
cb7d60a6
JR
152#define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
153
ad56a54c
RO
154#endif
155
cb7d60a6 156static _Unwind_Reason_Code
ad56a54c
RO
157MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context *context,
158 _Unwind_FrameState *fs)
cb7d60a6
JR
159{
160 void *pc = context->ra;
161 void *this_cfa = context->cfa;
4fcb5d87 162 int nframes = 0;
ad56a54c
RO
163 long new_cfa;
164 void *ra_location, *shifted_ra_location;
165 mcontext_t *mctx;
cb7d60a6
JR
166 int i;
167
cb7d60a6
JR
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
4fcb5d87 182 /* Do some pattern matching at the return address. */
bf4db96c 183 if (IS_SIGHANDLER (pc, this_cfa, &nframes))
cb7d60a6 184 {
4fcb5d87 185 struct frame *fp = (struct frame *) this_cfa;
ad56a54c
RO
186 struct handler_args {
187 struct frame frwin;
188 ucontext_t ucontext;
189 } *handler_args;
190 ucontext_t *ucp;
191
4fcb5d87 192 /* this_cfa points into the frame after the saved frame pointer and
ad56a54c
RO
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. */
ad56a54c
RO
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;
cb7d60a6 204 }
cb7d60a6
JR
205 else
206 return _URC_END_OF_STACK;
207
ad56a54c 208 /* The frame address is %sp + STACK_BIAS in 64-bit mode. */
4fcb5d87 209 new_cfa = mctx->gregs[REG_SP] + STACK_BIAS;
ad56a54c 210
cb7d60a6
JR
211 fs->regs.cfa_how = CFA_REG_OFFSET;
212 fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
4fcb5d87 213 fs->regs.cfa_offset = new_cfa - (long) this_cfa + STACK_BIAS;
cb7d60a6
JR
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
ad56a54c 223 /* First the global registers and then the out registers. */
cb7d60a6 224 fs->regs.reg[i].how = REG_SAVED_OFFSET;
ad56a54c 225 fs->regs.reg[i].loc.offset = (long)&mctx->gregs[REG_Y + i] - new_cfa;
cb7d60a6
JR
226 }
227
ad56a54c
RO
228 /* Just above the stack pointer there are 16 extended words in which
229 the register window (in and local registers) was saved. */
cb7d60a6
JR
230 for (i = 0; i < 16; i++)
231 {
232 fs->regs.reg[i + 16].how = REG_SAVED_OFFSET;
4fcb5d87 233 fs->regs.reg[i + 16].loc.offset = i * sizeof(long);
cb7d60a6
JR
234 }
235
4a8f6a63 236 /* Check whether we need to restore FPU registers. */
ad56a54c 237 if (mctx->fpregs.fpu_qcnt)
cb7d60a6
JR
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
ad56a54c
RO
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;
cb7d60a6 256 }
ad56a54c 257#endif
cb7d60a6
JR
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. */
ad56a54c
RO
264 ra_location = &mctx->gregs[REG_PC];
265 shifted_ra_location = &mctx->gregs[REG_Y];
cb7d60a6
JR
266 *(void **)shifted_ra_location = *(void **)ra_location - 8;
267 fs->retaddr_column = 0;
268 fs->regs.reg[0].how = REG_SAVED_OFFSET;
ad56a54c 269 fs->regs.reg[0].loc.offset = (long)shifted_ra_location - new_cfa;
0ba045df
EB
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;
cb7d60a6
JR
276
277 return _URC_NO_REASON;
ad56a54c 278}