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
35struct 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
50struct 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
77struct 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
90enum { SIGNAL_FRAMESIZE = 128 };
91
92/* If PC is at a sigreturn trampoline, return a pointer to the
93   regs.  Otherwise return NULL.  */
94
95static struct gcc_regs *
96get_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
148enum { SIGNAL_FRAMESIZE = 64 };
149
150static struct gcc_regs *
151get_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
188static long
189ppc_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
222static _Unwind_Reason_Code
223ppc_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
309static void
310frob_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}
349