PR target/16201
[official-gcc.git] / gcc / config / rs6000 / linux-unwind.h
blob3e12cc691aecbcc9b934b30419aa771bb684dc7f
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 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston,
19 MA 02111-1307, USA. */
21 /* This file defines our own versions of various kernel and user
22 structs, so that system headers are not needed, which otherwise
23 can make bootstrapping a new toolchain difficult. Do not use
24 these structs elsewhere; Many fields are missing, particularly
25 from the end of the structures. */
27 struct gcc_vregs
29 __attribute__ ((vector_size (16))) int vr[32];
30 #ifdef __powerpc64__
31 unsigned int pad1[3];
32 unsigned int vscr;
33 unsigned int vsave;
34 unsigned int pad2[3];
35 #else
36 unsigned int vsave;
37 unsigned int pad[2];
38 unsigned int vscr;
39 #endif
42 struct gcc_regs
44 unsigned long gpr[32];
45 unsigned long nip;
46 unsigned long msr;
47 unsigned long orig_gpr3;
48 unsigned long ctr;
49 unsigned long link;
50 unsigned long xer;
51 unsigned long ccr;
52 unsigned long softe;
53 unsigned long trap;
54 unsigned long dar;
55 unsigned long dsisr;
56 unsigned long result;
57 unsigned long pad1[4];
58 double fpr[32];
59 unsigned int pad2;
60 unsigned int fpscr;
61 #ifdef __powerpc64__
62 struct gcc_vregs *vp;
63 #else
64 unsigned int pad3[2];
65 #endif
66 struct gcc_vregs vregs;
69 struct gcc_ucontext
71 #ifdef __powerpc64__
72 unsigned long pad[28];
73 #else
74 unsigned long pad[12];
75 #endif
76 struct gcc_regs *regs;
77 struct gcc_regs rsave;
80 #ifdef __powerpc64__
82 enum { SIGNAL_FRAMESIZE = 128 };
84 /* If the current unwind info (FS) does not contain explicit info
85 saving R2, then we have to do a minor amount of code reading to
86 figure out if it was saved. The big problem here is that the
87 code that does the save/restore is generated by the linker, so
88 we have no good way to determine at compile time what to do. */
90 #define MD_FROB_UPDATE_CONTEXT frob_update_context
92 static void
93 frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
95 if (fs->regs.reg[2].how == REG_UNSAVED)
97 unsigned int *insn
98 = (unsigned int *) _Unwind_GetGR (context, LINK_REGISTER_REGNUM);
99 if (*insn == 0xE8410028)
100 _Unwind_SetGRPtr (context, 2, context->cfa + 40);
104 /* If PC is at a sigreturn trampoline, return a pointer to the
105 regs. Otherwise return NULL. */
107 static struct gcc_regs *
108 get_regs (struct _Unwind_Context *context)
110 const unsigned char *pc = context->ra;
112 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
113 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
114 if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
115 || *(unsigned int *) (pc + 8) != 0x44000002)
116 return NULL;
117 if (*(unsigned int *) (pc + 4) == 0x38000077)
119 struct sigframe {
120 char gap[SIGNAL_FRAMESIZE];
121 unsigned long pad[7];
122 struct gcc_regs *regs;
123 } *frame = (struct sigframe *) context->cfa;
124 return frame->regs;
126 else if (*(unsigned int *) (pc + 4) == 0x380000AC)
128 /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
129 because pc isn't pointing into the stack. Can be removed when
130 no one is running 2.4.19 or 2.4.20, the first two ppc64
131 kernels released. */
132 struct rt_sigframe_24 {
133 int tramp[6];
134 void *pinfo;
135 struct gcc_ucontext *puc;
136 } *frame24 = (struct rt_sigframe_24 *) pc;
138 /* Test for magic value in *puc of vdso. */
139 if ((long) frame24->puc != -21 * 8)
140 return frame24->puc->regs;
141 else
143 /* This works for 2.4.21 and later kernels. */
144 struct rt_sigframe {
145 char gap[SIGNAL_FRAMESIZE];
146 struct gcc_ucontext uc;
147 unsigned long pad[2];
148 int tramp[6];
149 void *pinfo;
150 struct gcc_ucontext *puc;
151 } *frame = (struct rt_sigframe *) context->cfa;
152 return frame->uc.regs;
155 return NULL;
158 #else /* !__powerpc64__ */
160 enum { SIGNAL_FRAMESIZE = 64 };
162 static struct gcc_regs *
163 get_regs (struct _Unwind_Context *context)
165 const unsigned char *pc = context->ra;
167 /* li r0, 0x7777; sc (sigreturn old) */
168 /* li r0, 0x0077; sc (sigreturn new) */
169 /* li r0, 0x6666; sc (rt_sigreturn old) */
170 /* li r0, 0x00AC; sc (rt_sigreturn new) */
171 if (*(unsigned int *) (pc + 4) != 0x44000002)
172 return NULL;
173 if (*(unsigned int *) (pc + 0) == 0x38007777
174 || *(unsigned int *) (pc + 0) == 0x38000077)
176 struct sigframe {
177 char gap[SIGNAL_FRAMESIZE];
178 unsigned long pad[7];
179 struct gcc_regs *regs;
180 } *frame = (struct sigframe *) context->cfa;
181 return frame->regs;
183 else if (*(unsigned int *) (pc + 0) == 0x38006666
184 || *(unsigned int *) (pc + 0) == 0x380000AC)
186 struct rt_sigframe {
187 char gap[SIGNAL_FRAMESIZE + 16];
188 char siginfo[128];
189 struct gcc_ucontext uc;
190 } *frame = (struct rt_sigframe *) context->cfa;
191 return frame->uc.regs;
193 return NULL;
195 #endif
197 /* Find an entry in the process auxilliary vector. The canonical way to
198 test for VMX is to look at AT_HWCAP. */
200 static long
201 ppc_linux_aux_vector (long which)
203 /* __libc_stack_end holds the original stack passed to a process. */
204 extern long *__libc_stack_end;
205 long argc;
206 char **argv;
207 char **envp;
208 struct auxv
210 long a_type;
211 long a_val;
212 } *auxp;
214 /* The Linux kernel puts argc first on the stack. */
215 argc = __libc_stack_end[0];
216 /* Followed by argv, NULL terminated. */
217 argv = (char **) __libc_stack_end + 1;
218 /* Followed by environment string pointers, NULL terminated. */
219 envp = argv + argc + 1;
220 while (*envp++)
221 continue;
222 /* Followed by the aux vector, zero terminated. */
223 for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
224 if (auxp->a_type == which)
225 return auxp->a_val;
226 return 0;
229 /* Do code reading to identify a signal frame, and set the frame
230 state data appropriately. See unwind-dw2.c for the structs. */
232 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
234 static _Unwind_Reason_Code
235 ppc_fallback_frame_state (struct _Unwind_Context *context,
236 _Unwind_FrameState *fs)
238 static long hwcap = 0;
239 struct gcc_regs *regs = get_regs (context);
240 long new_cfa;
241 int i;
243 if (regs == NULL)
244 return _URC_END_OF_STACK;
246 new_cfa = regs->gpr[STACK_POINTER_REGNUM];
247 fs->cfa_how = CFA_REG_OFFSET;
248 fs->cfa_reg = STACK_POINTER_REGNUM;
249 fs->cfa_offset = new_cfa - (long) context->cfa;
251 for (i = 0; i < 32; i++)
252 if (i != STACK_POINTER_REGNUM)
254 fs->regs.reg[i].how = REG_SAVED_OFFSET;
255 fs->regs.reg[i].loc.offset = (long) &regs->gpr[i] - new_cfa;
258 fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
259 fs->regs.reg[CR2_REGNO].loc.offset = (long) &regs->ccr - new_cfa;
261 fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
262 fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) &regs->link - new_cfa;
264 fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
265 fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) &regs->nip - new_cfa;
266 fs->retaddr_column = ARG_POINTER_REGNUM;
268 if (hwcap == 0)
270 hwcap = ppc_linux_aux_vector (16);
271 /* These will already be set if we found AT_HWCAP. A non-zero
272 value stops us looking again if for some reason we couldn't
273 find AT_HWCAP. */
274 #ifdef __powerpc64__
275 hwcap |= 0xc0000000;
276 #else
277 hwcap |= 0x80000000;
278 #endif
281 /* If we have a FPU... */
282 if (hwcap & 0x08000000)
283 for (i = 0; i < 32; i++)
285 fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
286 fs->regs.reg[i + 32].loc.offset = (long) &regs->fpr[i] - new_cfa;
289 /* If we have a VMX unit... */
290 if (hwcap & 0x10000000)
292 struct gcc_vregs *vregs;
293 #ifdef __powerpc64__
294 vregs = regs->vp;
295 #else
296 vregs = &regs->vregs;
297 #endif
298 if (regs->msr & (1 << 25))
300 for (i = 0; i < 32; i++)
302 fs->regs.reg[i + FIRST_ALTIVEC_REGNO].how = REG_SAVED_OFFSET;
303 fs->regs.reg[i + FIRST_ALTIVEC_REGNO].loc.offset
304 = (long) &vregs[i] - new_cfa;
307 fs->regs.reg[VSCR_REGNO].how = REG_SAVED_OFFSET;
308 fs->regs.reg[VSCR_REGNO].loc.offset = (long) &vregs->vscr - new_cfa;
311 fs->regs.reg[VRSAVE_REGNO].how = REG_SAVED_OFFSET;
312 fs->regs.reg[VRSAVE_REGNO].loc.offset = (long) &vregs->vsave - new_cfa;
315 return _URC_NO_REASON;