Mercurial > hg > CbC > CbC_gcc
annotate gcc/config/rs6000/linux-unwind.h @ 55:77e2b8dfacca gcc-4.4.5
update it from 4.4.3 to 4.5.0
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 12 Feb 2010 23:39:51 +0900 |
parents | a06113de4d67 |
children |
rev | line source |
---|---|
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 { | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
94 const unsigned int *pc = context->ra; |
0 | 95 |
96 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ | |
97 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
98 if (pc[0] != 0x38210000 + SIGNAL_FRAMESIZE || pc[2] != 0x44000002) |
0 | 99 return NULL; |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
100 if (pc[1] == 0x38000077) |
0 | 101 { |
102 struct sigframe { | |
103 char gap[SIGNAL_FRAMESIZE]; | |
104 unsigned long pad[7]; | |
105 struct gcc_regs *regs; | |
106 } *frame = (struct sigframe *) context->cfa; | |
107 return frame->regs; | |
108 } | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
109 else if (pc[1] == 0x380000AC) |
0 | 110 { |
111 /* This works for 2.4 kernels, but not for 2.6 kernels with vdso | |
112 because pc isn't pointing into the stack. Can be removed when | |
113 no one is running 2.4.19 or 2.4.20, the first two ppc64 | |
114 kernels released. */ | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
115 const struct rt_sigframe_24 { |
0 | 116 int tramp[6]; |
117 void *pinfo; | |
118 struct gcc_ucontext *puc; | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
119 } *frame24 = (const struct rt_sigframe_24 *) context->ra; |
0 | 120 |
121 /* Test for magic value in *puc of vdso. */ | |
122 if ((long) frame24->puc != -21 * 8) | |
123 return frame24->puc->regs; | |
124 else | |
125 { | |
126 /* This works for 2.4.21 and later kernels. */ | |
127 struct rt_sigframe { | |
128 char gap[SIGNAL_FRAMESIZE]; | |
129 struct gcc_ucontext uc; | |
130 unsigned long pad[2]; | |
131 int tramp[6]; | |
132 void *pinfo; | |
133 struct gcc_ucontext *puc; | |
134 } *frame = (struct rt_sigframe *) context->cfa; | |
135 return frame->uc.regs; | |
136 } | |
137 } | |
138 return NULL; | |
139 } | |
140 | |
141 #else /* !__powerpc64__ */ | |
142 | |
143 enum { SIGNAL_FRAMESIZE = 64 }; | |
144 | |
145 static struct gcc_regs * | |
146 get_regs (struct _Unwind_Context *context) | |
147 { | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
148 const unsigned int *pc = context->ra; |
0 | 149 |
150 /* li r0, 0x7777; sc (sigreturn old) */ | |
151 /* li r0, 0x0077; sc (sigreturn new) */ | |
152 /* li r0, 0x6666; sc (rt_sigreturn old) */ | |
153 /* li r0, 0x00AC; sc (rt_sigreturn new) */ | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
154 if (pc[1] != 0x44000002) |
0 | 155 return NULL; |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
156 if (pc[0] == 0x38007777 || pc[0] == 0x38000077) |
0 | 157 { |
158 struct sigframe { | |
159 char gap[SIGNAL_FRAMESIZE]; | |
160 unsigned long pad[7]; | |
161 struct gcc_regs *regs; | |
162 } *frame = (struct sigframe *) context->cfa; | |
163 return frame->regs; | |
164 } | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
0
diff
changeset
|
165 else if (pc[0] == 0x38006666 || pc[0] == 0x380000AC) |
0 | 166 { |
167 struct rt_sigframe { | |
168 char gap[SIGNAL_FRAMESIZE + 16]; | |
169 char siginfo[128]; | |
170 struct gcc_ucontext uc; | |
171 } *frame = (struct rt_sigframe *) context->cfa; | |
172 return frame->uc.regs; | |
173 } | |
174 return NULL; | |
175 } | |
176 #endif | |
177 | |
178 /* Find an entry in the process auxiliary vector. The canonical way to | |
179 test for VMX is to look at AT_HWCAP. */ | |
180 | |
181 static long | |
182 ppc_linux_aux_vector (long which) | |
183 { | |
184 /* __libc_stack_end holds the original stack passed to a process. */ | |
185 extern long *__libc_stack_end; | |
186 long argc; | |
187 char **argv; | |
188 char **envp; | |
189 struct auxv | |
190 { | |
191 long a_type; | |
192 long a_val; | |
193 } *auxp; | |
194 | |
195 /* The Linux kernel puts argc first on the stack. */ | |
196 argc = __libc_stack_end[0]; | |
197 /* Followed by argv, NULL terminated. */ | |
198 argv = (char **) __libc_stack_end + 1; | |
199 /* Followed by environment string pointers, NULL terminated. */ | |
200 envp = argv + argc + 1; | |
201 while (*envp++) | |
202 continue; | |
203 /* Followed by the aux vector, zero terminated. */ | |
204 for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp) | |
205 if (auxp->a_type == which) | |
206 return auxp->a_val; | |
207 return 0; | |
208 } | |
209 | |
210 /* Do code reading to identify a signal frame, and set the frame | |
211 state data appropriately. See unwind-dw2.c for the structs. */ | |
212 | |
213 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state | |
214 | |
215 static _Unwind_Reason_Code | |
216 ppc_fallback_frame_state (struct _Unwind_Context *context, | |
217 _Unwind_FrameState *fs) | |
218 { | |
219 static long hwcap = 0; | |
220 struct gcc_regs *regs = get_regs (context); | |
221 long new_cfa; | |
222 int i; | |
223 | |
224 if (regs == NULL) | |
225 return _URC_END_OF_STACK; | |
226 | |
227 new_cfa = regs->gpr[STACK_POINTER_REGNUM]; | |
228 fs->regs.cfa_how = CFA_REG_OFFSET; | |
229 fs->regs.cfa_reg = STACK_POINTER_REGNUM; | |
230 fs->regs.cfa_offset = new_cfa - (long) context->cfa; | |
231 | |
232 for (i = 0; i < 32; i++) | |
233 if (i != STACK_POINTER_REGNUM) | |
234 { | |
235 fs->regs.reg[i].how = REG_SAVED_OFFSET; | |
236 fs->regs.reg[i].loc.offset = (long) ®s->gpr[i] - new_cfa; | |
237 } | |
238 | |
239 fs->regs.reg[R_CR2].how = REG_SAVED_OFFSET; | |
240 /* CR? regs are always 32-bit and PPC is big-endian, so in 64-bit | |
241 libgcc loc.offset needs to point to the low 32 bits of regs->ccr. */ | |
242 fs->regs.reg[R_CR2].loc.offset = (long) ®s->ccr - new_cfa | |
243 + sizeof (long) - 4; | |
244 | |
245 fs->regs.reg[R_LR].how = REG_SAVED_OFFSET; | |
246 fs->regs.reg[R_LR].loc.offset = (long) ®s->link - new_cfa; | |
247 | |
248 fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET; | |
249 fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) ®s->nip - new_cfa; | |
250 fs->retaddr_column = ARG_POINTER_REGNUM; | |
251 fs->signal_frame = 1; | |
252 | |
253 if (hwcap == 0) | |
254 { | |
255 hwcap = ppc_linux_aux_vector (16); | |
256 /* These will already be set if we found AT_HWCAP. A nonzero | |
257 value stops us looking again if for some reason we couldn't | |
258 find AT_HWCAP. */ | |
259 #ifdef __powerpc64__ | |
260 hwcap |= 0xc0000000; | |
261 #else | |
262 hwcap |= 0x80000000; | |
263 #endif | |
264 } | |
265 | |
266 /* If we have a FPU... */ | |
267 if (hwcap & 0x08000000) | |
268 for (i = 0; i < 32; i++) | |
269 { | |
270 fs->regs.reg[i + 32].how = REG_SAVED_OFFSET; | |
271 fs->regs.reg[i + 32].loc.offset = (long) ®s->fpr[i] - new_cfa; | |
272 } | |
273 | |
274 /* If we have a VMX unit... */ | |
275 if (hwcap & 0x10000000) | |
276 { | |
277 struct gcc_vregs *vregs; | |
278 #ifdef __powerpc64__ | |
279 vregs = regs->vp; | |
280 #else | |
281 vregs = ®s->vregs; | |
282 #endif | |
283 if (regs->msr & (1 << 25)) | |
284 { | |
285 for (i = 0; i < 32; i++) | |
286 { | |
287 fs->regs.reg[i + R_VR0].how = REG_SAVED_OFFSET; | |
288 fs->regs.reg[i + R_VR0].loc.offset | |
289 = (long) &vregs->vr[i] - new_cfa; | |
290 } | |
291 | |
292 fs->regs.reg[R_VSCR].how = REG_SAVED_OFFSET; | |
293 fs->regs.reg[R_VSCR].loc.offset = (long) &vregs->vscr - new_cfa; | |
294 } | |
295 | |
296 fs->regs.reg[R_VRSAVE].how = REG_SAVED_OFFSET; | |
297 fs->regs.reg[R_VRSAVE].loc.offset = (long) &vregs->vsave - new_cfa; | |
298 } | |
299 | |
300 /* If we have SPE register high-parts... we check at compile-time to | |
301 avoid expanding the code for all other PowerPC. */ | |
302 #ifdef __SPE__ | |
303 for (i = 0; i < 32; i++) | |
304 { | |
305 fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].how = REG_SAVED_OFFSET; | |
306 fs->regs.reg[i + FIRST_PSEUDO_REGISTER - 1].loc.offset | |
307 = (long) ®s->vregs - new_cfa + 4 * i; | |
308 } | |
309 #endif | |
310 | |
311 return _URC_NO_REASON; | |
312 } | |
313 | |
314 #define MD_FROB_UPDATE_CONTEXT frob_update_context | |
315 | |
316 static void | |
317 frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs ATTRIBUTE_UNUSED) | |
318 { | |
319 const unsigned int *pc = (const unsigned int *) context->ra; | |
320 | |
321 /* Fix up for 2.6.12 - 2.6.16 Linux kernels that have vDSO, but don't | |
322 have S flag in it. */ | |
323 #ifdef __powerpc64__ | |
324 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */ | |
325 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */ | |
326 if (pc[0] == 0x38210000 + SIGNAL_FRAMESIZE | |
327 && (pc[1] == 0x38000077 || pc[1] == 0x380000AC) | |
328 && pc[2] == 0x44000002) | |
329 _Unwind_SetSignalFrame (context, 1); | |
330 #else | |
331 /* li r0, 0x7777; sc (sigreturn old) */ | |
332 /* li r0, 0x0077; sc (sigreturn new) */ | |
333 /* li r0, 0x6666; sc (rt_sigreturn old) */ | |
334 /* li r0, 0x00AC; sc (rt_sigreturn new) */ | |
335 if ((pc[0] == 0x38007777 || pc[0] == 0x38000077 | |
336 || pc[0] == 0x38006666 || pc[0] == 0x380000AC) | |
337 && pc[1] == 0x44000002) | |
338 _Unwind_SetSignalFrame (context, 1); | |
339 #endif | |
340 | |
341 #ifdef __powerpc64__ | |
342 if (fs->regs.reg[2].how == REG_UNSAVED) | |
343 { | |
344 /* If the current unwind info (FS) does not contain explicit info | |
345 saving R2, then we have to do a minor amount of code reading to | |
346 figure out if it was saved. The big problem here is that the | |
347 code that does the save/restore is generated by the linker, so | |
348 we have no good way to determine at compile time what to do. */ | |
349 unsigned int *insn | |
350 = (unsigned int *) _Unwind_GetGR (context, R_LR); | |
351 if (insn && *insn == 0xE8410028) | |
352 _Unwind_SetGRPtr (context, 2, context->cfa + 40); | |
353 } | |
354 #endif | |
355 } |