]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/gcc/config/rs6000/linux-unwind.h
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / gcc / config / rs6000 / linux-unwind.h
1 /* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
2    Copyright (C) 2004, 2005, 2006 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 2, or (at your
9    option) any later version.
10
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.)
18
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.
23
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, 51 Franklin Street, Fifth Floor, Boston,
27    MA 02110-1301, USA.  */
28
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.  */
34
35 struct gcc_vregs
36 {
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
48 };
49
50 struct gcc_regs
51 {
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;
75 };
76
77 struct gcc_ucontext
78 {
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;
86 };
87
88 #ifdef __powerpc64__
89
90 enum { SIGNAL_FRAMESIZE = 128 };
91
92 /* If PC is at a sigreturn trampoline, return a pointer to the
93    regs.  Otherwise return NULL.  */
94
95 static struct gcc_regs *
96 get_regs (struct _Unwind_Context *context)
97 {
98   const unsigned char *pc = context->ra;
99
100   /* addi r1, r1, 128; li r0, 0x0077; sc  (sigreturn) */
101   /* addi r1, r1, 128; li r0, 0x00AC; sc  (rt_sigreturn) */
102   if (*(unsigned int *) (pc + 0) != 0x38210000 + SIGNAL_FRAMESIZE
103       || *(unsigned int *) (pc + 8) != 0x44000002)
104     return NULL;
105   if (*(unsigned int *) (pc + 4) == 0x38000077)
106     {
107       struct sigframe {
108         char gap[SIGNAL_FRAMESIZE];
109         unsigned long pad[7];
110         struct gcc_regs *regs;
111       } *frame = (struct sigframe *) context->cfa;
112       return frame->regs;
113     }
114   else if (*(unsigned int *) (pc + 4) == 0x380000AC)
115     {
116       /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
117          because pc isn't pointing into the stack.  Can be removed when
118          no one is running 2.4.19 or 2.4.20, the first two ppc64
119          kernels released.  */
120       struct rt_sigframe_24 {
121         int tramp[6];
122         void *pinfo;
123         struct gcc_ucontext *puc;
124       } *frame24 = (struct rt_sigframe_24 *) pc;
125
126       /* Test for magic value in *puc of vdso.  */
127       if ((long) frame24->puc != -21 * 8)
128         return frame24->puc->regs;
129       else
130         {
131           /* This works for 2.4.21 and later kernels.  */
132           struct rt_sigframe {
133             char gap[SIGNAL_FRAMESIZE];
134             struct gcc_ucontext uc;
135             unsigned long pad[2];
136             int tramp[6];
137             void *pinfo;
138             struct gcc_ucontext *puc;
139           } *frame = (struct rt_sigframe *) context->cfa;
140           return frame->uc.regs;
141         }
142     }
143   return NULL;
144 }
145
146 #else  /* !__powerpc64__ */
147
148 enum { SIGNAL_FRAMESIZE = 64 };
149
150 static struct gcc_regs *
151 get_regs (struct _Unwind_Context *context)
152 {
153   const unsigned char *pc = context->ra;
154
155   /* li r0, 0x7777; sc  (sigreturn old)  */
156   /* li r0, 0x0077; sc  (sigreturn new)  */
157   /* li r0, 0x6666; sc  (rt_sigreturn old)  */
158   /* li r0, 0x00AC; sc  (rt_sigreturn new)  */
159   if (*(unsigned int *) (pc + 4) != 0x44000002)
160     return NULL;
161   if (*(unsigned int *) (pc + 0) == 0x38007777
162       || *(unsigned int *) (pc + 0) == 0x38000077)
163     {
164       struct sigframe {
165         char gap[SIGNAL_FRAMESIZE];
166         unsigned long pad[7];
167         struct gcc_regs *regs;
168       } *frame = (struct sigframe *) context->cfa;
169       return frame->regs;
170     }
171   else if (*(unsigned int *) (pc + 0) == 0x38006666
172            || *(unsigned int *) (pc + 0) == 0x380000AC)
173     {
174       struct rt_sigframe {
175         char gap[SIGNAL_FRAMESIZE + 16];
176         char siginfo[128];
177         struct gcc_ucontext uc;
178       } *frame = (struct rt_sigframe *) context->cfa;
179       return frame->uc.regs;
180     }
181   return NULL;
182 }
183 #endif
184
185 /* Find an entry in the process auxiliary vector.  The canonical way to
186    test for VMX is to look at AT_HWCAP.  */
187
188 static long
189 ppc_linux_aux_vector (long which)
190 {
191   /* __libc_stack_end holds the original stack passed to a process.  */
192   extern long *__libc_stack_end;
193   long argc;
194   char **argv;
195   char **envp;
196   struct auxv
197   {
198     long a_type;
199     long a_val;
200   } *auxp;
201
202   /* The Linux kernel puts argc first on the stack.  */
203   argc = __libc_stack_end[0];
204   /* Followed by argv, NULL terminated.  */
205   argv = (char **) __libc_stack_end + 1;
206   /* Followed by environment string pointers, NULL terminated. */
207   envp = argv + argc + 1;
208   while (*envp++)
209     continue;
210   /* Followed by the aux vector, zero terminated.  */
211   for (auxp = (struct auxv *) envp; auxp->a_type != 0; ++auxp)
212     if (auxp->a_type == which)
213       return auxp->a_val;
214   return 0;
215 }
216
217 /* Do code reading to identify a signal frame, and set the frame
218    state data appropriately.  See unwind-dw2.c for the structs.  */
219
220 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
221
222 static _Unwind_Reason_Code
223 ppc_fallback_frame_state (struct _Unwind_Context *context,
224                           _Unwind_FrameState *fs)
225 {
226   static long hwcap = 0;
227   struct gcc_regs *regs = get_regs (context);
228   long new_cfa;
229   int i;
230
231   if (regs == NULL)
232     return _URC_END_OF_STACK;
233
234   new_cfa = regs->gpr[STACK_POINTER_REGNUM];
235   fs->cfa_how = CFA_REG_OFFSET;
236   fs->cfa_reg = STACK_POINTER_REGNUM;
237   fs->cfa_offset = new_cfa - (long) context->cfa;
238
239   for (i = 0; i < 32; i++)
240     if (i != STACK_POINTER_REGNUM)
241       {
242         fs->regs.reg[i].how = REG_SAVED_OFFSET;
243         fs->regs.reg[i].loc.offset = (long) &regs->gpr[i] - new_cfa;
244       }
245
246   fs->regs.reg[CR2_REGNO].how = REG_SAVED_OFFSET;
247   fs->regs.reg[CR2_REGNO].loc.offset = (long) &regs->ccr - new_cfa;
248
249   fs->regs.reg[LINK_REGISTER_REGNUM].how = REG_SAVED_OFFSET;
250   fs->regs.reg[LINK_REGISTER_REGNUM].loc.offset = (long) &regs->link - new_cfa;
251
252   fs->regs.reg[ARG_POINTER_REGNUM].how = REG_SAVED_OFFSET;
253   fs->regs.reg[ARG_POINTER_REGNUM].loc.offset = (long) &regs->nip - new_cfa;
254   fs->retaddr_column = ARG_POINTER_REGNUM;
255   fs->signal_frame = 1;
256
257   if (hwcap == 0)
258     {
259       hwcap = ppc_linux_aux_vector (16);
260       /* These will already be set if we found AT_HWCAP.  A nonzero
261          value stops us looking again if for some reason we couldn't
262          find AT_HWCAP.  */
263 #ifdef __powerpc64__
264       hwcap |= 0xc0000000;
265 #else
266       hwcap |= 0x80000000;
267 #endif
268     }
269
270   /* If we have a FPU...  */
271   if (hwcap & 0x08000000)
272     for (i = 0; i < 32; i++)
273       {
274         fs->regs.reg[i + 32].how = REG_SAVED_OFFSET;
275         fs->regs.reg[i + 32].loc.offset = (long) &regs->fpr[i] - new_cfa;
276       }
277
278   /* If we have a VMX unit...  */
279   if (hwcap & 0x10000000)
280     {
281       struct gcc_vregs *vregs;
282 #ifdef __powerpc64__
283       vregs = regs->vp;
284 #else
285       vregs = &regs->vregs;
286 #endif
287       if (regs->msr & (1 << 25))
288         {
289           for (i = 0; i < 32; i++)
290             {
291               fs->regs.reg[i + FIRST_ALTIVEC_REGNO].how = REG_SAVED_OFFSET;
292               fs->regs.reg[i + FIRST_ALTIVEC_REGNO].loc.offset
293                 = (long) &vregs[i] - new_cfa;
294             }
295
296           fs->regs.reg[VSCR_REGNO].how = REG_SAVED_OFFSET;
297           fs->regs.reg[VSCR_REGNO].loc.offset = (long) &vregs->vscr - new_cfa;
298         }
299
300       fs->regs.reg[VRSAVE_REGNO].how = REG_SAVED_OFFSET;
301       fs->regs.reg[VRSAVE_REGNO].loc.offset = (long) &vregs->vsave - new_cfa;
302     }
303
304   return _URC_NO_REASON;
305 }
306
307 #define MD_FROB_UPDATE_CONTEXT frob_update_context
308
309 static void
310 frob_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs ATTRIBUTE_UNUSED)
311 {
312   const unsigned int *pc = (const unsigned int *) context->ra;
313
314   /* Fix up for 2.6.12 - 2.6.16 Linux kernels that have vDSO, but don't
315      have S flag in it.  */
316 #ifdef __powerpc64__
317   /* addi r1, r1, 128; li r0, 0x0077; sc  (sigreturn) */
318   /* addi r1, r1, 128; li r0, 0x00AC; sc  (rt_sigreturn) */
319   if (pc[0] == 0x38210000 + SIGNAL_FRAMESIZE
320       && (pc[1] == 0x38000077 || pc[1] == 0x380000AC)
321       && pc[2] == 0x44000002)
322     _Unwind_SetSignalFrame (context, 1);
323 #else
324   /* li r0, 0x7777; sc  (sigreturn old)  */
325   /* li r0, 0x0077; sc  (sigreturn new)  */
326   /* li r0, 0x6666; sc  (rt_sigreturn old)  */
327   /* li r0, 0x00AC; sc  (rt_sigreturn new)  */
328   if ((pc[0] == 0x38007777 || pc[0] == 0x38000077
329        || pc[0] == 0x38006666 || pc[0] == 0x380000AC)
330       && pc[1] == 0x44000002)
331     _Unwind_SetSignalFrame (context, 1);
332 #endif
333
334 #ifdef __powerpc64__
335   if (fs->regs.reg[2].how == REG_UNSAVED)
336     {
337       /* If the current unwind info (FS) does not contain explicit info
338          saving R2, then we have to do a minor amount of code reading to
339          figure out if it was saved.  The big problem here is that the
340          code that does the save/restore is generated by the linker, so
341          we have no good way to determine at compile time what to do.  */
342       unsigned int *insn
343         = (unsigned int *) _Unwind_GetGR (context, LINK_REGISTER_REGNUM);
344       if (*insn == 0xE8410028)
345         _Unwind_SetGRPtr (context, 2, context->cfa + 40);
346     }
347 #endif
348 }