Merge from the pain train
[official-gcc.git] / gcc / config / rs6000 / linux-unwind.h
blob1acf738123e1a26d10841ec1495d5aa26f8cf02b
1 /* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
11 In addition to the permissions in the GNU General Public License,
12 the Free Software Foundation gives you unlimited permission to link
13 the compiled version of this file with other programs, and to
14 distribute those programs without any restriction coming from the
15 use of this file. (The General Public License restrictions do
16 apply in other respects; for example, they cover modification of
17 the file, and distribution when not linked into another program.)
19 GCC is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
22 License for more details.
24 You should have received a copy of the GNU General Public License
25 along with GCC; see the file COPYING. If not, write to the
26 Free Software Foundation, 59 Temple Place - Suite 330, Boston,
27 MA 02111-1307, USA. */
29 /* This file defines our own versions of various kernel and user
30 structs, so that system headers are not needed, which otherwise
31 can make bootstrapping a new toolchain difficult. Do not use
32 these structs elsewhere; Many fields are missing, particularly
33 from the end of the structures. */
35 struct gcc_vregs
37 __attribute__ ((vector_size (16))) int vr[32];
38 #ifdef __powerpc64__
39 unsigned int pad1[3];
40 unsigned int vscr;
41 unsigned int vsave;
42 unsigned int pad2[3];
43 #else
44 unsigned int vsave;
45 unsigned int pad[2];
46 unsigned int vscr;
47 #endif
50 struct gcc_regs
52 unsigned long gpr[32];
53 unsigned long nip;
54 unsigned long msr;
55 unsigned long orig_gpr3;
56 unsigned long ctr;
57 unsigned long link;
58 unsigned long xer;
59 unsigned long ccr;
60 unsigned long softe;
61 unsigned long trap;
62 unsigned long dar;
63 unsigned long dsisr;
64 unsigned long result;
65 unsigned long pad1[4];
66 double fpr[32];
67 unsigned int pad2;
68 unsigned int fpscr;
69 #ifdef __powerpc64__
70 struct gcc_vregs *vp;
71 #else
72 unsigned int pad3[2];
73 #endif
74 struct gcc_vregs vregs;
77 struct gcc_ucontext
79 #ifdef __powerpc64__
80 unsigned long pad[28];
81 #else
82 unsigned long pad[12];
83 #endif
84 struct gcc_regs *regs;
85 struct gcc_regs rsave;
88 #ifdef __powerpc64__
90 enum { SIGNAL_FRAMESIZE = 128 };
92 /* If the current unwind info (FS) does not contain explicit info
93 saving R2, then we have to do a minor amount of code reading to
94 figure out if it was saved. The big problem here is that the
95 code that does the save/restore is generated by the linker, so
96 we have no good way to determine at compile time what to do. */
98 #define MD_FROB_UPDATE_CONTEXT frob_update_context
100 static void
101 frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
103 if (fs->regs.reg[2].how == REG_UNSAVED)
105 unsigned int *insn
106 = (unsigned int *) _Unwind_GetGR (context, LINK_REGISTER_REGNUM);
107 if (*insn == 0xE8410028)
108 _Unwind_SetGRPtr (context, 2, context->cfa + 40);
112 /* If PC is at a sigreturn trampoline, return a pointer to the
113 regs. Otherwise return NULL. */
115 static struct gcc_regs *
116 get_regs (struct _Unwind_Context *context)
118 const unsigned char *pc = context->ra;
120 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
121 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
122 if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
123 || *(unsigned int *) (pc + 8) != 0x44000002)
124 return NULL;
125 if (*(unsigned int *) (pc + 4) == 0x38000077)
127 struct sigframe {
128 char gap[SIGNAL_FRAMESIZE];
129 unsigned long pad[7];
130 struct gcc_regs *regs;
131 } *frame = (struct sigframe *) context->cfa;
132 return frame->regs;
134 else if (*(unsigned int *) (pc + 4) == 0x380000AC)
136 /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
137 because pc isn't pointing into the stack. Can be removed when
138 no one is running 2.4.19 or 2.4.20, the first two ppc64
139 kernels released. */
140 struct rt_sigframe_24 {
141 int tramp[6];
142 void *pinfo;
143 struct gcc_ucontext *puc;
144 } *frame24 = (struct rt_sigframe_24 *) pc;
146 /* Test for magic value in *puc of vdso. */
147 if ((long) frame24->puc != -21 * 8)
148 return frame24->puc->regs;
149 else
151 /* This works for 2.4.21 and later kernels. */
152 struct rt_sigframe {
153 char gap[SIGNAL_FRAMESIZE];
154 struct gcc_ucontext uc;
155 unsigned long pad[2];
156 int tramp[6];
157 void *pinfo;
158 struct gcc_ucontext *puc;
159 } *frame = (struct rt_sigframe *) context->cfa;
160 return frame->uc.regs;
163 return NULL;
166 #else /* !__powerpc64__ */
168 enum { SIGNAL_FRAMESIZE = 64 };
170 static struct gcc_regs *
171 get_regs (struct _Unwind_Context *context)
173 const unsigned char *pc = context->ra;
175 /* li r0, 0x7777; sc (sigreturn old) */
176 /* li r0, 0x0077; sc (sigreturn new) */
177 /* li r0, 0x6666; sc (rt_sigreturn old) */
178 /* li r0, 0x00AC; sc (rt_sigreturn new) */
179 if (*(unsigned int *) (pc + 4) != 0x44000002)
180 return NULL;
181 if (*(unsigned int *) (pc + 0) == 0x38007777
182 || *(unsigned int *) (pc + 0) == 0x38000077)
184 struct sigframe {
185 char gap[SIGNAL_FRAMESIZE];
186 unsigned long pad[7];
187 struct gcc_regs *regs;
188 } *frame = (struct sigframe *) context->cfa;
189 return frame->regs;
191 else if (*(unsigned int *) (pc + 0) == 0x38006666
192 || *(unsigned int *) (pc + 0) == 0x380000AC)
194 struct rt_sigframe {
195 char gap[SIGNAL_FRAMESIZE + 16];
196 char siginfo[128];
197 struct gcc_ucontext uc;
198 } *frame = (struct rt_sigframe *) context->cfa;
199 return frame->uc.regs;
201 return NULL;
203 #endif
205 /* Find an entry in the process auxiliary vector. The canonical way to
206 test for VMX is to look at AT_HWCAP. */
208 static long
209 ppc_linux_aux_vector (long which)
211 /* __libc_stack_end holds the original stack passed to a process. */
212 extern long *__libc_stack_end;
213 long argc;
214 char **argv;
215 char **envp;
216 struct auxv
218 long a_type;
219 long a_val;
220 } *auxp;
222 /* The Linux kernel puts argc first on the stack. */
223 argc = __libc_stack_end[0];
224 /* Followed by argv, NULL terminated. */
225 argv = (char **) __libc_stack_end + 1;
226 /* Followed by environment string pointers, NULL terminated. */
227 envp = argv + argc + 1;
228 while (*envp++)
229 continue;
230 /* Followed by the aux vector, zero terminated. */
231 for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
232 if (auxp->a_type == which)
233 return auxp->a_val;
234 return 0;
237 /* Do code reading to identify a signal frame, and set the frame
238 state data appropriately. See unwind-dw2.c for the structs. */
240 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
242 static _Unwind_Reason_Code
243 ppc_fallback_frame_state (struct _Unwind_Context *context,
244 _Unwind_FrameState *fs)
246 static long hwcap = 0;
247 struct gcc_regs *regs = get_regs (context);
248 long new_cfa;
249 int i;
251 if (regs == NULL)
252 return _URC_END_OF_STACK;
254 new_cfa = regs->gpr[STACK_POINTER_REGNUM];
255 fs->cfa_how = CFA_REG_OFFSET;
256 fs->cfa_reg = STACK_POINTER_REGNUM;
257 fs->cfa_offset = new_cfa - (long) context->cfa;
259 for (i = 0; i < 32; i++)
260 if (i != STACK_POINTER_REGNUM)
262 fs->regs.reg[i].how = REG_SAVED_OFFSET;
263 fs->regs.reg[i].loc.offset = (long) &regs->gpr[i] - new_cfa;
266 fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
267 fs->regs.reg[CR2_REGNO].loc.offset = (long) &regs->ccr - new_cfa;
269 fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
270 fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) &regs->link - new_cfa;
272 fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
273 fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) &regs->nip - new_cfa;
274 fs->retaddr_column = ARG_POINTER_REGNUM;
276 if (hwcap == 0)
278 hwcap = ppc_linux_aux_vector (16);
279 /* These will already be set if we found AT_HWCAP. A non-zero
280 value stops us looking again if for some reason we couldn't
281 find AT_HWCAP. */
282 #ifdef __powerpc64__
283 hwcap |= 0xc0000000;
284 #else
285 hwcap |= 0x80000000;
286 #endif
289 /* If we have a FPU... */
290 if (hwcap & 0x08000000)
291 for (i = 0; i < 32; i++)
293 fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
294 fs->regs.reg[i + 32].loc.offset = (long) &regs->fpr[i] - new_cfa;
297 /* If we have a VMX unit... */
298 if (hwcap & 0x10000000)
300 struct gcc_vregs *vregs;
301 #ifdef __powerpc64__
302 vregs = regs->vp;
303 #else
304 vregs = &regs->vregs;
305 #endif
306 if (regs->msr & (1 << 25))
308 for (i = 0; i < 32; i++)
310 fs->regs.reg[i + FIRST_ALTIVEC_REGNO].how = REG_SAVED_OFFSET;
311 fs->regs.reg[i + FIRST_ALTIVEC_REGNO].loc.offset
312 = (long) &vregs[i] - new_cfa;
315 fs->regs.reg[VSCR_REGNO].how = REG_SAVED_OFFSET;
316 fs->regs.reg[VSCR_REGNO].loc.offset = (long) &vregs->vscr - new_cfa;
319 fs->regs.reg[VRSAVE_REGNO].how = REG_SAVED_OFFSET;
320 fs->regs.reg[VRSAVE_REGNO].loc.offset = (long) &vregs->vsave - new_cfa;
323 return _URC_NO_REASON;