0
|
1 /* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
|
|
2 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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 it
|
|
7 under the terms of the GNU General Public License as published
|
|
8 by the Free Software Foundation; either version 3, or (at your
|
|
9 option) any later version.
|
|
10
|
|
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.
|
|
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 #define R_LR 65
|
|
26 #define R_CR2 70
|
|
27 #define R_VR0 77
|
|
28 #define R_VRSAVE 109
|
|
29 #define R_VSCR 110
|
|
30
|
|
31 struct gcc_vregs
|
|
32 {
|
|
33 __attribute__ ((vector_size (16))) int vr[32];
|
|
34 #ifdef __powerpc64__
|
|
35 unsigned int pad1[3];
|
|
36 unsigned int vscr;
|
|
37 unsigned int vsave;
|
|
38 unsigned int pad2[3];
|
|
39 #else
|
|
40 unsigned int vsave;
|
|
41 unsigned int pad[2];
|
|
42 unsigned int vscr;
|
|
43 #endif
|
|
44 };
|
|
45
|
|
46 struct gcc_regs
|
|
47 {
|
|
48 unsigned long gpr[32];
|
|
49 unsigned long nip;
|
|
50 unsigned long msr;
|
|
51 unsigned long orig_gpr3;
|
|
52 unsigned long ctr;
|
|
53 unsigned long link;
|
|
54 unsigned long xer;
|
|
55 unsigned long ccr;
|
|
56 unsigned long softe;
|
|
57 unsigned long trap;
|
|
58 unsigned long dar;
|
|
59 unsigned long dsisr;
|
|
60 unsigned long result;
|
|
61 unsigned long pad1[4];
|
|
62 double fpr[32];
|
|
63 unsigned int pad2;
|
|
64 unsigned int fpscr;
|
|
65 #ifdef __powerpc64__
|
|
66 struct gcc_vregs *vp;
|
|
67 #else
|
|
68 unsigned int pad3[2];
|
|
69 #endif
|
|
70 struct gcc_vregs vregs;
|
|
71 };
|
|
72
|
|
73 struct gcc_ucontext
|
|
74 {
|
|
75 #ifdef __powerpc64__
|
|
76 unsigned long pad[28];
|
|
77 #else
|
|
78 unsigned long pad[12];
|
|
79 #endif
|
|
80 struct gcc_regs *regs;
|
|
81 struct gcc_regs rsave;
|
|
82 };
|
|
83
|
|
84 #ifdef __powerpc64__
|
|
85
|
|
86 enum { SIGNAL_FRAMESIZE = 128 };
|
|
87
|
|
88 /* If PC is at a sigreturn trampoline, return a pointer to the
|
|
89 regs. Otherwise return NULL. */
|
|
90
|
|
91 static struct gcc_regs *
|
|
92 get_regs (struct _Unwind_Context *context)
|
|
93 {
|
|
94 const unsigned char *pc = context->ra;
|
|
95
|
|
96 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
|
|
97 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
|
|
98 if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
|
|
99 || *(unsigned int *) (pc + 8) != 0x44000002)
|
|
100 return NULL;
|
|
101 if (*(unsigned int *) (pc + 4) == 0x38000077)
|
|
102 {
|
|
103 struct sigframe {
|
|
104 char gap[SIGNAL_FRAMESIZE];
|
|
105 unsigned long pad[7];
|
|
106 struct gcc_regs *regs;
|
|
107 } *frame = (struct sigframe *) context->cfa;
|
|
108 return frame->regs;
|
|
109 }
|
|
110 else if (*(unsigned int *) (pc + 4) == 0x380000AC)
|
|
111 {
|
|
112 /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
|
|
113 because pc isn't pointing into the stack. Can be removed when
|
|
114 no one is running 2.4.19 or 2.4.20, the first two ppc64
|
|
115 kernels released. */
|
|
116 struct rt_sigframe_24 {
|
|
117 int tramp[6];
|
|
118 void *pinfo;
|
|
119 struct gcc_ucontext *puc;
|
|
120 } *frame24 = (struct rt_sigframe_24 *) pc;
|
|
121
|
|
122 /* Test for magic value in *puc of vdso. */
|
|
123 if ((long) frame24->puc != -21 * 8)
|
|
124 return frame24->puc->regs;
|
|
125 else
|
|
126 {
|
|
127 /* This works for 2.4.21 and later kernels. */
|
|
128 struct rt_sigframe {
|
|
129 char gap[SIGNAL_FRAMESIZE];
|
|
130 struct gcc_ucontext uc;
|
|
131 unsigned long pad[2];
|
|
132 int tramp[6];
|
|
133 void *pinfo;
|
|
134 struct gcc_ucontext *puc;
|
|
135 } *frame = (struct rt_sigframe *) context->cfa;
|
|
136 return frame->uc.regs;
|
|
137 }
|
|
138 }
|
|
139 return NULL;
|
|
140 }
|
|
141
|
|
142 #else /* !__powerpc64__ */
|
|
143
|
|
144 enum { SIGNAL_FRAMESIZE = 64 };
|
|
145
|
|
146 static struct gcc_regs *
|
|
147 get_regs (struct _Unwind_Context *context)
|
|
148 {
|
|
149 const unsigned char *pc = context->ra;
|
|
150
|
|
151 /* li r0, 0x7777; sc (sigreturn old) */
|
|
152 /* li r0, 0x0077; sc (sigreturn new) */
|
|
153 /* li r0, 0x6666; sc (rt_sigreturn old) */
|
|
154 /* li r0, 0x00AC; sc (rt_sigreturn new) */
|
|
155 if (*(const unsigned int *) (pc + 4) != 0x44000002)
|
|
156 return NULL;
|
|
157 if (*(const unsigned int *) (pc + 0) == 0x38007777
|
|
158 || *(const unsigned int *) (pc + 0) == 0x38000077)
|
|
159 {
|
|
160 struct sigframe {
|
|
161 char gap[SIGNAL_FRAMESIZE];
|
|
162 unsigned long pad[7];
|
|
163 struct gcc_regs *regs;
|
|
164 } *frame = (struct sigframe *) context->cfa;
|
|
165 return frame->regs;
|
|
166 }
|
|
167 else if (*(const unsigned int *) (pc + 0) == 0x38006666
|
|
168 || *(const unsigned int *) (pc + 0) == 0x380000AC)
|
|
169 {
|
|
170 struct rt_sigframe {
|
|
171 char gap[SIGNAL_FRAMESIZE + 16];
|
|
172 char siginfo[128];
|
|
173 struct gcc_ucontext uc;
|
|
174 } *frame = (struct rt_sigframe *) context->cfa;
|
|
175 return frame->uc.regs;
|
|
176 }
|
|
177 return NULL;
|
|
178 }
|
|
179 #endif
|
|
180
|
|
181 /* Find an entry in the process auxiliary vector. The canonical way to
|
|
182 test for VMX is to look at AT_HWCAP. */
|
|
183
|
|
184 static long
|
|
185 ppc_linux_aux_vector (long which)
|
|
186 {
|
|
187 /* __libc_stack_end holds the original stack passed to a process. */
|
|
188 extern long *__libc_stack_end;
|
|
189 long argc;
|
|
190 char **argv;
|
|
191 char **envp;
|
|
192 struct auxv
|
|
193 {
|
|
194 long a_type;
|
|
195 long a_val;
|
|
196 } *auxp;
|
|
197
|
|
198 /* The Linux kernel puts argc first on the stack. */
|
|
199 argc = __libc_stack_end[0];
|
|
200 /* Followed by argv, NULL terminated. */
|
|
201 argv = (char **) __libc_stack_end + 1;
|
|
202 /* Followed by environment string pointers, NULL terminated. */
|
|
203 envp = argv + argc + 1;
|
|
204 while (*envp++)
|
|
205 continue;
|
|
206 /* Followed by the aux vector, zero terminated. */
|
|
207 for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
|
|
208 if (auxp->a_type == which)
|
|
209 return auxp->a_val;
|
|
210 return 0;
|
|
211 }
|
|
212
|
|
213 /* Do code reading to identify a signal frame, and set the frame
|
|
214 state data appropriately. See unwind-dw2.c for the structs. */
|
|
215
|
|
216 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
|
|
217
|
|
218 static _Unwind_Reason_Code
|
|
219 ppc_fallback_frame_state (struct _Unwind_Context *context,
|
|
220 _Unwind_FrameState *fs)
|
|
221 {
|
|
222 static long hwcap = 0;
|
|
223 struct gcc_regs *regs = get_regs (context);
|
|
224 long new_cfa;
|
|
225 int i;
|
|
226
|
|
227 if (regs == NULL)
|
|
228 return _URC_END_OF_STACK;
|
|
229
|
|
230 new_cfa = regs->gpr[STACK_POINTER_REGNUM];
|
|
231 fs->regs.cfa_how = CFA_REG_OFFSET;
|
|
232 fs->regs.cfa_reg = STACK_POINTER_REGNUM;
|
|
233 fs->regs.cfa_offset = new_cfa - (long) context->cfa;
|
|
234
|
|
235 for (i = 0; i < 32; i++)
|
|
236 if (i != STACK_POINTER_REGNUM)
|
|
237 {
|
|
238 fs->regs.reg[i].how = REG_SAVED_OFFSET;
|
|
239 fs->regs.reg[i].loc.offset = (long) ®s->gpr[i] - new_cfa;
|
|
240 }
|
|
241
|
|
242 fs->regs.reg[R_CR2].how = REG_SAVED_OFFSET;
|
|
243 /* CR? regs are always 32-bit and PPC is big-endian, so in 64-bit
|
|
244 libgcc loc.offset needs to point to the low 32 bits of regs->ccr. */
|
|
245 fs->regs.reg[R_CR2].loc.offset = (long) ®s->ccr - new_cfa
|
|
246 + sizeof (long) - 4;
|
|
247
|
|
248 fs->regs.reg[R_LR].how = REG_SAVED_OFFSET;
|
|
249 fs->regs.reg[R_LR].loc.offset = (long) ®s->link - new_cfa;
|
|
250
|
|
251 fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
|
|
252 fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) ®s->nip - new_cfa;
|
|
253 fs->retaddr_column = ARG_POINTER_REGNUM;
|
|
254 fs->signal_frame = 1;
|
|
255
|
|
256 if (hwcap == 0)
|
|
257 {
|
|
258 hwcap = ppc_linux_aux_vector (16);
|
|
259 /* These will already be set if we found AT_HWCAP. A nonzero
|
|
260 value stops us looking again if for some reason we couldn't
|
|
261 find AT_HWCAP. */
|
|
262 #ifdef __powerpc64__
|
|
263 hwcap |= 0xc0000000;
|
|
264 #else
|
|
265 hwcap |= 0x80000000;
|
|
266 #endif
|
|
267 }
|
|
268
|
|
269 /* If we have a FPU... */
|
|
270 if (hwcap & 0x08000000)
|
|
271 for (i = 0; i < 32; i++)
|
|
272 {
|
|
273 fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
|
|
274 fs->regs.reg[i + 32].loc.offset = (long) ®s->fpr[i] - new_cfa;
|
|
275 }
|
|
276
|
|
277 /* If we have a VMX unit... */
|
|
278 if (hwcap & 0x10000000)
|
|
279 {
|
|
280 struct gcc_vregs *vregs;
|
|
281 #ifdef __powerpc64__
|
|
282 vregs = regs->vp;
|
|
283 #else
|
|
284 vregs = ®s->vregs;
|
|
285 #endif
|
|
286 if (regs->msr & (1 << 25))
|
|
287 {
|
|
288 for (i = 0; i < 32; i++)
|
|
289 {
|
|
290 fs->regs.reg[i + R_VR0].how = REG_SAVED_OFFSET;
|
|
291 fs->regs.reg[i + R_VR0].loc.offset
|
|
292 = (long) &vregs->vr[i] - new_cfa;
|
|
293 }
|
|
294
|
|
295 fs->regs.reg[R_VSCR].how = REG_SAVED_OFFSET;
|
|
296 fs->regs.reg[R_VSCR].loc.offset = (long) &vregs->vscr - new_cfa;
|
|
297 }
|
|
298
|
|
299 fs->regs.reg[R_VRSAVE].how = REG_SAVED_OFFSET;
|
|
300 fs->regs.reg[R_VRSAVE].loc.offset = (long) &vregs->vsave - new_cfa;
|
|
301 }
|
|
302
|
|
303 /* If we have SPE register high-parts... we check at compile-time to
|
|
304 avoid expanding the code for all other PowerPC. */
|
|
305 #ifdef __SPE__
|
|
306 for (i = 0; i < 32; i++)
|
|
307 {
|
|
308 fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].how = REG_SAVED_OFFSET;
|
|
309 fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].loc.offset
|
|
310 = (long) ®s->vregs - new_cfa + 4 * i;
|
|
311 }
|
|
312 #endif
|
|
313
|
|
314 return _URC_NO_REASON;
|
|
315 }
|
|
316
|
|
317 #define MD_FROB_UPDATE_CONTEXT frob_update_context
|
|
318
|
|
319 static void
|
|
320 frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs ATTRIBUTE_UNUSED)
|
|
321 {
|
|
322 const unsigned int *pc = (const unsigned int *) context->ra;
|
|
323
|
|
324 /* Fix up for 2.6.12 - 2.6.16 Linux kernels that have vDSO, but don't
|
|
325 have S flag in it. */
|
|
326 #ifdef __powerpc64__
|
|
327 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
|
|
328 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
|
|
329 if (pc[0] == 0x38210000 + SIGNAL_FRAMESIZE
|
|
330 && (pc[1] == 0x38000077 || pc[1] == 0x380000AC)
|
|
331 && pc[2] == 0x44000002)
|
|
332 _Unwind_SetSignalFrame (context, 1);
|
|
333 #else
|
|
334 /* li r0, 0x7777; sc (sigreturn old) */
|
|
335 /* li r0, 0x0077; sc (sigreturn new) */
|
|
336 /* li r0, 0x6666; sc (rt_sigreturn old) */
|
|
337 /* li r0, 0x00AC; sc (rt_sigreturn new) */
|
|
338 if ((pc[0] == 0x38007777 || pc[0] == 0x38000077
|
|
339 || pc[0] == 0x38006666 || pc[0] == 0x380000AC)
|
|
340 && pc[1] == 0x44000002)
|
|
341 _Unwind_SetSignalFrame (context, 1);
|
|
342 #endif
|
|
343
|
|
344 #ifdef __powerpc64__
|
|
345 if (fs->regs.reg[2].how == REG_UNSAVED)
|
|
346 {
|
|
347 /* If the current unwind info (FS) does not contain explicit info
|
|
348 saving R2, then we have to do a minor amount of code reading to
|
|
349 figure out if it was saved. The big problem here is that the
|
|
350 code that does the save/restore is generated by the linker, so
|
|
351 we have no good way to determine at compile time what to do. */
|
|
352 unsigned int *insn
|
|
353 = (unsigned int *) _Unwind_GetGR (context, R_LR);
|
|
354 if (insn && *insn == 0xE8410028)
|
|
355 _Unwind_SetGRPtr (context, 2, context->cfa + 40);
|
|
356 }
|
|
357 #endif
|
|
358 }
|