]>
Commit | Line | Data |
---|---|---|
cb7d60a6 | 1 | /* DWARF2 EH unwinding support for SPARC Solaris. |
7adcbafe | 2 | Copyright (C) 2009-2022 Free Software Foundation, Inc. |
cb7d60a6 JR |
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> | |
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 | 36 | static int |
bf4db96c | 37 | sparc64_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 | ||
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 | |
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 | ||
109 | static int | |
bf4db96c | 110 | sparc_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 | 156 | static _Unwind_Reason_Code |
ad56a54c RO |
157 | MD_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 | } |