OSDN Git Service

Add NIOS2 support. Code from SourceyG++.
[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, 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 int *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 (pc[0] != 0x38210000 + SIGNAL_FRAMESIZE || pc[2] != 0x44000002)
99     return NULL;
100   if (pc[1] == 0x38000077)
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     }
109   else if (pc[1] == 0x380000AC)
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.  */
115       const struct rt_sigframe_24 {
116         int tramp[6];
117         void *pinfo;
118         struct gcc_ucontext *puc;
119       } *frame24 = (const struct rt_sigframe_24 *) context->ra;
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 {
148   const unsigned int *pc = context->ra;
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)  */
154   if (pc[1] != 0x44000002)
155     return NULL;
156   if (pc[0] == 0x38007777 || pc[0] == 0x38000077)
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     }
165   else if (pc[0] == 0x38006666 || pc[0] == 0x380000AC)
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) &regs->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) &regs->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) &regs->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) &regs->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) &regs->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 = &regs->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) &regs->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 }