Mercurial > hg > CbC > CbC_gcc
comparison gcc/config/rs6000/linux-unwind.h @ 0:a06113de4d67
first commit
author | kent <kent@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 17 Jul 2009 14:47:48 +0900 |
parents | |
children | 77e2b8dfacca |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:a06113de4d67 |
---|---|
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 } |