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