1 /* DWARF2 EH unwinding support for SPARC Solaris.
2 Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
4 This file is part of GCC.
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)
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.
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.
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/>. */
25 /* Do code reading to identify a signal frame, and set the frame
26 state data appropriately. See unwind-dw2.c for the structs. */
29 #include <sys/frame.h>
30 #include <sys/stack.h>
32 #if defined(__arch64__)
34 #define IS_SIGHANDLER sparc64_is_sighandler
37 sparc64_is_sighandler (unsigned int *pc
, unsigned int *savpc
, int *nframes
)
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)
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
64 <sigacthandler+24>: sra %i0, 0, %l1
67 <sigacthandler+28>: ldx [ %o2 + 0xf68 ], %g5
69 <sigacthandler+28>: ldx [ %o2 + 0xe50 ], %g5
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
79 && pc
[-5] == 0x892c7003
80 && pc
[-4] == 0x90100011
81 && pc
[-3] == 0xe0590005
82 && pc
[-2] == 0x9fc40000
83 && pc
[-1] == 0x9410001a
84 && pc
[ 0] == 0x80a46008))
86 /* We need to move up one frame:
88 <signal handler> <-- context->cfa
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 */
106 && pc
[-4] == 0x90100018
107 && pc
[-3] == 0x92100019
108 && pc
[-2] == 0x9fc6c000
109 && pc
[-1] == 0x9410001a
110 && pc
[ 0] == 0x81c7e008
111 && pc
[ 1] == 0x81e80000)
113 if (/* Solaris 8 /usr/lib/sparcv9/libthread.so.1
114 ------------------------------------------
115 Before patch 108827-08:
116 <sigacthandler+1760>: st %g4, [ %i1 + 0x1c ]
118 Since patch 108827-08:
119 <sigacthandler+1816>: st %l0, [ %i4 + 0x10 ] */
120 savpc
[-1] == 0xc826601c
121 || savpc
[-1] == 0xe0272010)
123 /* We need to move up three frames:
125 <signal handler> <-- context->cfa
132 else /* Solaris 8 /usr/lib/lwp/sparcv9/libthread.so.1, Solaris 9+
133 ---------------------------------------------------------- */
135 /* We need to move up three frames:
137 <signal handler> <-- context->cfa
151 #define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
153 #define MD_FROB_UPDATE_CONTEXT sparc64_frob_update_context
156 sparc64_frob_update_context (struct _Unwind_Context
*context
,
157 _Unwind_FrameState
*fs
)
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
)
170 context
->cfa
-= STACK_BIAS
;
172 for (i
= 0; i
< DWARF_FRAME_REGISTERS
+ 1; ++i
)
173 if (fs
->regs
.reg
[i
].how
== REG_SAVED_OFFSET
)
174 _Unwind_SetGRPtr (context
, i
,
175 _Unwind_GetGRPtr (context
, i
) - STACK_BIAS
);
181 #define IS_SIGHANDLER sparc_is_sighandler
184 sparc_is_sighandler (unsigned int *pc
, unsigned int * savpc
, int *nframes
)
186 if (/* Solaris 8, 9 - single-threaded
187 -------------------------------
188 The pattern changes slightly in different versions of the operating
189 system, so we skip the comparison against pc[-6].
191 <sigacthandler+16>: add %o1, %o7, %o3
192 <sigacthandler+20>: mov %i1, %o1
194 <sigacthandler+24>: ld [ %o3 + <offset> ], %o2
196 <sigacthandler+28>: sll %i0, 2, %o0
197 <sigacthandler+32>: ld [ %o0 + %o2 ], %l0
198 <sigacthandler+36>: mov %i0, %o0
199 <sigacthandler+40>: call %l0
200 <sigacthandler+44>: mov %i2, %o2
201 <sigacthandler+48>: cmp %i0, 8 <--- PC */
203 && pc
[-7] == 0x92100019
205 && pc
[-5] == 0x912e2002
206 && pc
[-4] == 0xe002000a
207 && pc
[-3] == 0x90100018
208 && pc
[-2] == 0x9fc40000
209 && pc
[-1] == 0x9410001a
210 && pc
[ 0] == 0x80a62008)
212 /* Need to move up one frame:
214 <signal handler> <-- context->cfa
222 if (/* Solaris 8 - multi-threaded
223 ---------------------------
224 <__libthread_segvhdlr+212>: clr %o2
225 <__libthread_segvhdlr+216>: ld [ %fp + -28 ], %l0
226 <__libthread_segvhdlr+220>: mov %i4, %o0
227 <__libthread_segvhdlr+224>: mov %i1, %o1
228 <__libthread_segvhdlr+228>: call %l0
229 <__libthread_segvhdlr+232>: mov %i2, %o2
230 <__libthread_segvhdlr+236>: ret <--- PC
231 <__libthread_segvhdlr+240>: restore
232 <__libthread_segvhdlr+244>: cmp %o1, 0 */
234 && pc
[-5] == 0xe007bfe4
235 && pc
[-4] == 0x9010001c
236 && pc
[-3] == 0x92100019
237 && pc
[-2] == 0x9fc40000
238 && pc
[-1] == 0x9410001a
239 && pc
[ 0] == 0x81c7e008
240 && pc
[ 1] == 0x81e80000
241 && pc
[ 2] == 0x80a26000)
243 /* Need to move up one frame:
245 <signal handler> <-- context->cfa
253 if(/* Solaris 8+ - multi-threaded
254 ----------------------------
255 <__sighndlr>: save %sp, -96, %sp
256 <__sighndlr+4>: mov %i0, %o0
257 <__sighndlr+8>: mov %i1, %o1
258 <__sighndlr+12>: call %i3
259 <__sighndlr+16>: mov %i2, %o2
260 <__sighndlr+20>: ret <--- PC
261 <__sighndlr+24>: restore */
263 && pc
[-4] == 0x90100018
264 && pc
[-3] == 0x92100019
265 && pc
[-2] == 0x9fc6c000
266 && pc
[-1] == 0x9410001a
267 && pc
[ 0] == 0x81c7e008
268 && pc
[ 1] == 0x81e80000)
270 if (/* Solaris 8 /usr/lib/libthread.so.1
271 ----------------------------------
272 <sigacthandler+1796>: mov %i0, %o0 */
273 savpc
[-1] == 0x90100018)
275 /* We need to move up two frames:
277 <signal handler> <-- context->cfa
284 else /* Solaris 8 /usr/lib/lwp/libthread.so.1, Solaris 9+
285 -------------------------------------------------- */
287 /* We need to move up three frames:
289 <signal handler> <-- context->cfa
303 #define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
307 static _Unwind_Reason_Code
308 MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context
*context
,
309 _Unwind_FrameState
*fs
)
311 void *pc
= context
->ra
;
312 struct frame
*fp
= (struct frame
*) context
->cfa
;
314 void *this_cfa
= context
->cfa
;
316 void *ra_location
, *shifted_ra_location
;
320 /* Deal with frame-less function from which a signal was raised. */
321 if (_Unwind_IsSignalFrame (context
))
323 /* The CFA is by definition unmodified in this case. */
324 fs
->regs
.cfa_how
= CFA_REG_OFFSET
;
325 fs
->regs
.cfa_reg
= __builtin_dwarf_sp_column ();
326 fs
->regs
.cfa_offset
= 0;
328 /* This is the canonical RA column. */
329 fs
->retaddr_column
= 15;
331 return _URC_NO_REASON
;
334 if (IS_SIGHANDLER (pc
, (unsigned int *)fp
->fr_savpc
, &nframes
))
336 struct handler_args
{
342 /* context->cfa points into the frame after the saved frame pointer and
343 saved pc (struct frame).
345 The ucontext_t structure is in the kernel frame after a struct
346 frame. Since the frame sizes vary even within OS releases, we
347 need to walk the stack to get there. */
349 for (i
= 0; i
< nframes
; i
++)
350 fp
= (struct frame
*) ((char *)fp
->fr_savfp
+ STACK_BIAS
);
352 handler_args
= (struct handler_args
*) fp
;
353 ucp
= &handler_args
->ucontext
;
354 mctx
= &ucp
->uc_mcontext
;
357 /* Exit if the pattern at the return address does not match the
358 previous three patterns. */
360 return _URC_END_OF_STACK
;
362 new_cfa
= mctx
->gregs
[REG_SP
];
363 /* The frame address is %sp + STACK_BIAS in 64-bit mode. */
364 new_cfa
+= STACK_BIAS
;
366 fs
->regs
.cfa_how
= CFA_REG_OFFSET
;
367 fs
->regs
.cfa_reg
= __builtin_dwarf_sp_column ();
368 fs
->regs
.cfa_offset
= new_cfa
- (long) this_cfa
;
370 /* Restore global and out registers (in this order) from the
371 ucontext_t structure, uc_mcontext.gregs field. */
372 for (i
= 1; i
< 16; i
++)
374 /* We never restore %sp as everything is purely CFA-based. */
375 if ((unsigned int) i
== __builtin_dwarf_sp_column ())
378 /* First the global registers and then the out registers. */
379 fs
->regs
.reg
[i
].how
= REG_SAVED_OFFSET
;
380 fs
->regs
.reg
[i
].loc
.offset
= (long)&mctx
->gregs
[REG_Y
+ i
] - new_cfa
;
383 /* Just above the stack pointer there are 16 extended words in which
384 the register window (in and local registers) was saved. */
385 for (i
= 0; i
< 16; i
++)
387 fs
->regs
.reg
[i
+ 16].how
= REG_SAVED_OFFSET
;
388 fs
->regs
.reg
[i
+ 16].loc
.offset
= i
*sizeof(long);
391 /* Check whether we need to restore FPU registers. */
392 if (mctx
->fpregs
.fpu_qcnt
)
394 for (i
= 0; i
< 32; i
++)
396 fs
->regs
.reg
[i
+ 32].how
= REG_SAVED_OFFSET
;
397 fs
->regs
.reg
[i
+ 32].loc
.offset
398 = (long)&mctx
->fpregs
.fpu_fr
.fpu_regs
[i
] - new_cfa
;
402 /* For 64-bit, fpu_fr.fpu_dregs contains 32 instead of 16 doubles. */
403 for (i
= 32; i
< 64; i
++)
405 if (i
> 32 && (i
& 1))
408 fs
->regs
.reg
[i
+ 32].how
= REG_SAVED_OFFSET
;
409 fs
->regs
.reg
[i
+ 32].loc
.offset
410 = (long)&mctx
->fpregs
.fpu_fr
.fpu_dregs
[i
/2] - new_cfa
;
415 /* State the rules to find the kernel's code "return address", which is
416 the address of the active instruction when the signal was caught.
417 On the SPARC, since RETURN_ADDR_OFFSET (essentially 8) is defined, we
418 need to preventively subtract it from the purported return address. */
419 ra_location
= &mctx
->gregs
[REG_PC
];
420 shifted_ra_location
= &mctx
->gregs
[REG_Y
];
421 *(void **)shifted_ra_location
= *(void **)ra_location
- 8;
422 fs
->retaddr_column
= 0;
423 fs
->regs
.reg
[0].how
= REG_SAVED_OFFSET
;
424 fs
->regs
.reg
[0].loc
.offset
= (long)shifted_ra_location
- new_cfa
;
425 fs
->signal_frame
= 1;
427 return _URC_NO_REASON
;