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