OSDN Git Service

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