invoke.texi (SPARC options): Add -mflat.
[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 {
168 long i;
169
170 context->cfa -= STACK_BIAS;
171
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);
176 }
177 }
178
179 #else
180
181 #define IS_SIGHANDLER sparc_is_sighandler
182
183 static int
184 sparc_is_sighandler (unsigned int *pc, unsigned int * savpc, int *nframes)
185 {
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].
190
191 <sigacthandler+16>: add %o1, %o7, %o3
192 <sigacthandler+20>: mov %i1, %o1
193
194 <sigacthandler+24>: ld [ %o3 + <offset> ], %o2
195
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 */
202 pc[-8] == 0x9602400f
203 && pc[-7] == 0x92100019
204 /* skip pc[-6] */
205 && pc[-5] == 0x912e2002
206 && pc[-4] == 0xe002000a
207 && pc[-3] == 0x90100018
208 && pc[-2] == 0x9fc40000
209 && pc[-1] == 0x9410001a
210 && pc[ 0] == 0x80a62008)
211 {
212 /* Need to move up one frame:
213
214 <signal handler> <-- context->cfa
215 sigacthandler
216 <kernel>
217 */
218 *nframes = 1;
219 return 1;
220 }
221
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 */
233 pc[-6] == 0x94102000
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)
242 {
243 /* Need to move up one frame:
244
245 <signal handler> <-- context->cfa
246 __libthread_segvhdlr
247 <kernel>
248 */
249 *nframes = 1;
250 return 1;
251 }
252
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 */
262 pc[-5] == 0x9de3bfa0
263 && pc[-4] == 0x90100018
264 && pc[-3] == 0x92100019
265 && pc[-2] == 0x9fc6c000
266 && pc[-1] == 0x9410001a
267 && pc[ 0] == 0x81c7e008
268 && pc[ 1] == 0x81e80000)
269 {
270 if (/* Solaris 8 /usr/lib/libthread.so.1
271 ----------------------------------
272 <sigacthandler+1796>: mov %i0, %o0 */
273 savpc[-1] == 0x90100018)
274 {
275 /* We need to move up two frames:
276
277 <signal handler> <-- context->cfa
278 __sighndlr
279 sigacthandler
280 <kernel>
281 */
282 *nframes = 2;
283 }
284 else /* Solaris 8 /usr/lib/lwp/libthread.so.1, Solaris 9+
285 -------------------------------------------------- */
286 {
287 /* We need to move up three frames:
288
289 <signal handler> <-- context->cfa
290 __sighndlr
291 call_user_handler
292 sigacthandler
293 <kernel>
294 */
295 *nframes = 3;
296 }
297 return 1;
298 }
299
300 return 0;
301 }
302
303 #define MD_FALLBACK_FRAME_STATE_FOR sparc_fallback_frame_state
304
305 #endif
306
307 static _Unwind_Reason_Code
308 MD_FALLBACK_FRAME_STATE_FOR (struct _Unwind_Context *context,
309 _Unwind_FrameState *fs)
310 {
311 void *pc = context->ra;
312 struct frame *fp = (struct frame *) context->cfa;
313 int nframes;
314 void *this_cfa = context->cfa;
315 long new_cfa;
316 void *ra_location, *shifted_ra_location;
317 mcontext_t *mctx;
318 int i;
319
320 /* Deal with frame-less function from which a signal was raised. */
321 if (_Unwind_IsSignalFrame (context))
322 {
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;
327
328 /* This is the canonical RA column. */
329 fs->retaddr_column = 15;
330
331 return _URC_NO_REASON;
332 }
333
334 if (IS_SIGHANDLER (pc, (unsigned int *)fp->fr_savpc, &nframes))
335 {
336 struct handler_args {
337 struct frame frwin;
338 ucontext_t ucontext;
339 } *handler_args;
340 ucontext_t *ucp;
341
342 /* context->cfa points into the frame after the saved frame pointer and
343 saved pc (struct frame).
344
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. */
348
349 for (i = 0; i < nframes; i++)
350 fp = (struct frame *) ((char *)fp->fr_savfp + STACK_BIAS);
351
352 handler_args = (struct handler_args *) fp;
353 ucp = &handler_args->ucontext;
354 mctx = &ucp->uc_mcontext;
355 }
356
357 /* Exit if the pattern at the return address does not match the
358 previous three patterns. */
359 else
360 return _URC_END_OF_STACK;
361
362 new_cfa = mctx->gregs[REG_SP];
363 /* The frame address is %sp + STACK_BIAS in 64-bit mode. */
364 new_cfa += STACK_BIAS;
365
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;
369
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++)
373 {
374 /* We never restore %sp as everything is purely CFA-based. */
375 if ((unsigned int) i == __builtin_dwarf_sp_column ())
376 continue;
377
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;
381 }
382
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++)
386 {
387 fs->regs.reg[i + 16].how = REG_SAVED_OFFSET;
388 fs->regs.reg[i + 16].loc.offset = i*sizeof(long);
389 }
390
391 /* Check whether we need to restore FPU registers. */
392 if (mctx->fpregs.fpu_qcnt)
393 {
394 for (i = 0; i < 32; i++)
395 {
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;
399 }
400
401 #ifdef __arch64__
402 /* For 64-bit, fpu_fr.fpu_dregs contains 32 instead of 16 doubles. */
403 for (i = 32; i < 64; i++)
404 {
405 if (i > 32 && (i & 1))
406 continue;
407
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;
411 }
412 #endif
413 }
414
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;
426
427 return _URC_NO_REASON;
428 }