OSDN Git Service

2004-07-19 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / darwin-fallback.c
1 /* Fallback frame-state unwinder for Darwin.
2    Copyright (C) 2004 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 by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    In addition to the permissions in the GNU General Public License, the
12    Free Software Foundation gives you unlimited permission to link the
13    compiled version of this file into combinations with other programs,
14    and to distribute those combinations without any restriction coming
15    from the use of this file.  (The General Public License restrictions
16    do apply in other respects; for example, they cover modification of
17    the file, and distribution when not linked into a combined
18    executable.)
19
20    GCC is distributed in the hope that it will be useful, but WITHOUT
21    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
23    License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with GCC; see the file COPYING.  If not, write to the Free
27    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
28    02111-1307, USA.  */
29
30 #include "tconfig.h"
31 #include "tsystem.h"
32 #include "coretypes.h"
33 #include "tm.h"
34 #include "dwarf2.h"
35 #include "unwind.h"
36 #include "unwind-dw2.h"
37 #include <stdint.h>
38 #include <stdbool.h>
39 #include <signal.h>
40 #include <ucontext.h>
41
42 typedef unsigned long reg_unit;
43
44 /* Place in GPRS the parameters to the first 'sc' instruction that would
45    have been executed if we were returning from this CONTEXT, or
46    return false if an unexpected instruction is encountered.  */
47
48 static bool
49 interpret_libc (reg_unit gprs[32], struct _Unwind_Context *context)
50 {
51   uint32_t *pc = (uint32_t *)_Unwind_GetIP (context);
52   uint32_t cr;
53   reg_unit lr = (reg_unit) pc;
54   reg_unit ctr = 0;
55   uint32_t *invalid_address = NULL;
56
57   int i;
58
59   for (i = 0; i < 13; i++)
60     gprs[i] = 1;
61   gprs[1] = _Unwind_GetCFA (context);
62   for (; i < 32; i++)
63     gprs[i] = _Unwind_GetGR (context, i);
64   cr = _Unwind_GetGR (context, CR2_REGNO);
65
66   /* For each supported Libc, we have to track the code flow
67      all the way back into the kernel.
68   
69      This code is believed to support all released Libc/Libsystem builds since
70      Jaguar 6C115, including all the security updates.  To be precise,
71
72      Libc       Libsystem       Build(s)
73      262~1      60~37           6C115
74      262~1      60.2~4          6D52
75      262~1      61~3            6F21-6F22
76      262~1      63~24           6G30-6G37
77      262~1      63~32           6I34-6I35
78      262~1      63~64           6L29-6L60
79      262.4.1~1  63~84           6L123-6R172
80      
81      320~1      71~101          7B85-7D28
82      320~1      71~266          7F54-7F56
83      320~1      71~288          7F112
84      320~1      71~289          7F113
85      320.1.3~1  71.1.1~29       7H60-7H105
86      320.1.3~1  71.1.1~30       7H110-7H113
87      320.1.3~1  71.1.1~31       7H114
88      
89      That's a big table!  It would be insane to try to keep track of
90      every little detail, so we just read the code itself and do what
91      it would do.
92   */
93
94   for (;;)
95     {
96       uint32_t ins = *pc++;
97       
98       if ((ins & 0xFC000003) == 0x48000000)  /* b instruction */
99         {
100           pc += ((((int32_t) ins & 0x3FFFFFC) ^ 0x2000000) - 0x2000004) / 4;
101           continue;
102         }
103       if ((ins & 0xFC600000) == 0x2C000000)  /* cmpwi */
104         {
105           int32_t val1 = (int16_t) ins;
106           int32_t val2 = gprs[ins >> 16 & 0x1F];
107           /* Only beq and bne instructions are supported, so we only
108              need to set the EQ bit.  */
109           uint32_t mask = 0xF << ((ins >> 21 & 0x1C) ^ 0x1C);
110           if (val1 == val2)
111             cr |= mask;
112           else
113             cr &= ~mask;
114           continue;
115         }
116       if ((ins & 0xFEC38003) == 0x40820000)  /* forwards beq/bne */
117         {
118           if ((cr >> ((ins >> 16 & 0x1F) ^ 0x1F) & 1) == (ins >> 24 & 1))
119             pc += (ins & 0x7FFC) / 4 - 1;
120           continue;
121         }
122       if ((ins & 0xFC0007FF) == 0x7C000378) /* or, including mr */
123         {
124           gprs [ins >> 16 & 0x1F] = (gprs [ins >> 11 & 0x1F] 
125                                      | gprs [ins >> 21 & 0x1F]);
126           continue;
127         }
128       if (ins >> 26 == 0x0E)  /* addi, including li */
129         {
130           reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F];
131           gprs [ins >> 21 & 0x1F] = src + (int16_t) ins;
132           continue;
133         }
134       if (ins >> 26 == 0x0F)  /* addis, including lis */
135         {
136           reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F];
137           gprs [ins >> 21 & 0x1F] = src + ((int16_t) ins << 16);
138           continue;
139         }
140       if (ins >> 26 == 0x20)  /* lwz */
141         {
142           reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F];
143           uint32_t *p = (uint32_t *)(src + (int16_t) ins);
144           if (p == invalid_address)
145             return false;
146           gprs [ins >> 21 & 0x1F] = *p;
147           continue;
148         }
149       if (ins >> 26 == 0x21)  /* lwzu */
150         {
151           uint32_t *p = (uint32_t *)(gprs [ins >> 16 & 0x1F] += (int16_t) ins);
152           if (p == invalid_address)
153             return false;
154           gprs [ins >> 21 & 0x1F] = *p;
155           continue;
156         }
157       if (ins >> 26 == 0x24)  /* stw */
158         /* What we hope this is doing is '--in_sigtramp'.  We don't want
159            to actually store to memory, so just make a note of the
160            address and refuse to load from it.  */
161         {
162           reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F];
163           uint32_t *p = (uint32_t *)(src + (int16_t) ins);
164           if (p == NULL || invalid_address != NULL)
165             return false;
166           invalid_address = p;
167           continue;
168         }
169       if (ins >> 26 == 0x2E) /* lmw */
170         {
171           reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F];
172           uint32_t *p = (uint32_t *)(src + (int16_t) ins);
173           int i;
174
175           for (i = (ins >> 21 & 0x1F); i < 32; i++)
176             {
177               if (p == invalid_address)
178                 return false;
179               gprs[i] = *p++;
180             }
181           continue;
182         }
183       if ((ins & 0xFC1FFFFF) == 0x7c0803a6)  /* mtlr */
184         {
185           lr = gprs [ins >> 21 & 0x1F];
186           continue;
187         }
188       if ((ins & 0xFC1FFFFF) == 0x7c0802a6)  /* mflr */
189         {
190           gprs [ins >> 21 & 0x1F] = lr;
191           continue;
192         }
193       if ((ins & 0xFC1FFFFF) == 0x7c0903a6)  /* mtctr */
194         {
195           ctr = gprs [ins >> 21 & 0x1F];
196           continue;
197         }
198       /* The PowerPC User's Manual says that bit 11 of the mtcrf
199          instruction is reserved and should be set to zero, but it
200          looks like the Darwin assembler doesn't do that... */
201       if ((ins & 0xFC000FFF) == 0x7c000120) /* mtcrf */
202         {
203           int i;
204           uint32_t mask = 0;
205           for (i = 0; i < 8; i++)
206             mask |= ((-(ins >> (12 + i) & 1)) & 0xF) << 4 * i;
207           cr = (cr & ~mask) | (gprs [ins >> 21 & 0x1F] & mask);
208           continue;
209         }
210       if (ins == 0x429f0005)  /* bcl- 20,4*cr7+so,.+4, loads pc into LR */
211         {
212           lr = (reg_unit) pc;
213           continue;
214         }
215       if (ins == 0x4e800420) /* bctr */
216         {
217           pc = (uint32_t *) ctr;
218           continue;
219         }
220       if (ins == 0x44000002) /* sc */
221         return true;
222
223       return false;
224     }
225 }
226
227 /* These defines are from the kernel's bsd/dev/ppc/unix_signal.c.  */
228 #define UC_TRAD                 1
229 #define UC_TRAD_VEC             6
230 #define UC_TRAD64               20
231 #define UC_TRAD64_VEC           25
232 #define UC_FLAVOR               30
233 #define UC_FLAVOR_VEC           35
234 #define UC_FLAVOR64             40
235 #define UC_FLAVOR64_VEC         45
236 #define UC_DUAL                 50
237 #define UC_DUAL_VEC             55
238
239 /* These are based on /usr/include/ppc/ucontext.h and
240    /usr/include/mach/ppc/thread_status.h, but rewritten to be more
241    convenient, to compile on Jaguar, and to work around Radar 3712064
242    on Panther, which is that the 'es' field of 'struct mcontext64' has
243    the wrong type (doh!).  */
244
245 struct gcc_mcontext64 {
246   uint64_t dar;
247   uint32_t dsisr;
248   uint32_t exception;
249   uint32_t padding1[4];
250   uint64_t srr0;
251   uint64_t srr1;
252   uint32_t gpr[32][2];
253   uint32_t cr;
254   uint32_t xer[2];  /* These are arrays because the original structure has them misaligned.  */
255   uint32_t lr[2];
256   uint32_t ctr[2];
257   uint32_t vrsave;
258   ppc_float_state_t fs;
259   ppc_vector_state_t vs;
260 };
261
262 #define UC_FLAVOR_SIZE \
263   (sizeof (struct mcontext) - sizeof (ppc_vector_state_t))
264
265 #define UC_FLAVOR_VEC_SIZE (sizeof (struct mcontext))
266
267 #define UC_FLAVOR64_SIZE \
268   (sizeof (struct gcc_mcontext64) - sizeof (ppc_vector_state_t))
269
270 #define UC_FLAVOR64_VEC_SIZE (sizeof (struct gcc_mcontext64))
271
272 /* Given GPRS as input to a 'sc' instruction, and OLD_CFA, update FS
273    to represent the execution of a signal return; or, if not a signal
274    return, return false.  */
275
276 static bool
277 handle_syscall (_Unwind_FrameState *fs, const reg_unit gprs[32],
278                 _Unwind_Ptr old_cfa)
279 {
280   struct ucontext *uctx;
281   bool is_64, is_vector;
282   ppc_float_state_t *float_state;
283   ppc_vector_state_t *vector_state;
284   _Unwind_Ptr new_cfa;
285   int i;
286   static _Unwind_Ptr return_addr;
287   
288   /* Yay!  We're in a Libc that we understand, and it's made a
289      system call.  It'll be one of two kinds: either a Jaguar-style
290      SYS_sigreturn, or a Panther-style 'syscall' call with 184, which 
291      is also SYS_sigreturn.  */
292   
293   if (gprs[0] == 0x67 /* SYS_SIGRETURN */)
294     {
295       uctx = (struct ucontext *) gprs[3];
296       is_vector = (uctx->uc_mcsize == UC_FLAVOR64_VEC_SIZE
297                    || uctx->uc_mcsize == UC_FLAVOR_VEC_SIZE);
298       is_64 = (uctx->uc_mcsize == UC_FLAVOR64_VEC_SIZE
299                || uctx->uc_mcsize == UC_FLAVOR64_SIZE);
300     }
301   else if (gprs[0] == 0 && gprs[3] == 184)
302     {
303       int ctxstyle = gprs[5];
304       uctx = (struct ucontext *) gprs[4];
305       is_vector = (ctxstyle == UC_FLAVOR_VEC || ctxstyle == UC_FLAVOR64_VEC
306                    || ctxstyle == UC_TRAD_VEC || ctxstyle == UC_TRAD64_VEC);
307       is_64 = (ctxstyle == UC_FLAVOR64_VEC || ctxstyle == UC_TRAD64_VEC
308                || ctxstyle == UC_FLAVOR64 || ctxstyle == UC_TRAD64);
309     }
310   else
311     return false;
312
313 #define set_offset(r, addr)                                     \
314   (fs->regs.reg[r].how = REG_SAVED_OFFSET,                      \
315    fs->regs.reg[r].loc.offset = (_Unwind_Ptr)(addr) - new_cfa)
316
317   /* Restore even the registers that are not call-saved, since they
318      might be being used in the prologue to save other registers,
319      for instance GPR0 is sometimes used to save LR.  */
320
321   /* Handle the GPRs, and produce the information needed to do the rest.  */
322   if (is_64)
323     {
324       /* The context is 64-bit, but it doesn't carry any extra information
325          for us because only the low 32 bits of the registers are
326          call-saved.  */
327       struct gcc_mcontext64 *m64 = (struct gcc_mcontext64 *)uctx->uc_mcontext;
328       int i;
329
330       float_state = &m64->fs;
331       vector_state = &m64->vs;
332
333       new_cfa = m64->gpr[1][1];
334       
335       set_offset (CR2_REGNO, &m64->cr);
336       for (i = 0; i < 32; i++)
337         set_offset (i, m64->gpr[i] + 1);
338       set_offset (XER_REGNO, m64->xer + 1);
339       set_offset (LINK_REGISTER_REGNUM, m64->lr + 1);
340       set_offset (COUNT_REGISTER_REGNUM, m64->ctr + 1);
341       if (is_vector)
342         set_offset (VRSAVE_REGNO, &m64->vrsave);
343       
344       /* Sometimes, srr0 points to the instruction that caused the exception,
345          and sometimes to the next instruction to be executed; we want
346          the latter.  */
347       if (m64->exception == 3 || m64->exception == 4
348           || m64->exception == 6
349           || (m64->exception == 7 && !(m64->srr1 & 0x10000)))
350         return_addr = m64->srr0 + 4;
351       else
352         return_addr = m64->srr0;
353     }
354   else
355     {
356       struct mcontext *m = uctx->uc_mcontext;
357       int i;
358
359       float_state = &m->fs;
360       vector_state = &m->vs;
361       
362       new_cfa = m->ss.r1;
363
364       set_offset (CR2_REGNO, &m->ss.cr);
365       for (i = 0; i < 32; i++)
366         set_offset (i, &m->ss.r0 + i);
367       set_offset (XER_REGNO, &m->ss.xer);
368       set_offset (LINK_REGISTER_REGNUM, &m->ss.lr);
369       set_offset (COUNT_REGISTER_REGNUM, &m->ss.ctr);
370
371       if (is_vector)
372         set_offset (VRSAVE_REGNO, &m->ss.vrsave);
373
374       /* Sometimes, srr0 points to the instruction that caused the exception,
375          and sometimes to the next instruction to be executed; we want
376          the latter.  */
377       if (m->es.exception == 3 || m->es.exception == 4
378           || m->es.exception == 6
379           || (m->es.exception == 7 && !(m->ss.srr1 & 0x10000)))
380         return_addr = m->ss.srr0 + 4;
381       else
382         return_addr = m->ss.srr0;
383     }
384
385   fs->cfa_how = CFA_REG_OFFSET;
386   fs->cfa_reg = STACK_POINTER_REGNUM;
387   fs->cfa_offset = new_cfa - old_cfa;;
388   
389   /* The choice of column for the return address is somewhat tricky.
390      Fortunately, the actual choice is private to this file, and
391      the space it's reserved from is the GCC register space, not the
392      DWARF2 numbering.  So any free element of the right size is an OK
393      choice.  Thus: */
394   fs->retaddr_column = ARG_POINTER_REGNUM;
395   /* FIXME: this should really be done using a DWARF2 location expression,
396      not using a static variable.  In fact, this entire file should
397      be implemented in DWARF2 expressions.  */
398   set_offset (ARG_POINTER_REGNUM, &return_addr);
399
400   for (i = 0; i < 32; i++)
401     set_offset (32 + i, float_state->fpregs + i);
402   set_offset (SPEFSCR_REGNO, &float_state->fpscr);
403   
404   if (is_vector)
405     {
406       for (i = 0; i < 32; i++)
407         set_offset (FIRST_ALTIVEC_REGNO + i, vector_state->save_vr + i);
408       set_offset (VSCR_REGNO, vector_state->save_vscr);
409     }
410
411   return true;
412 }
413
414 /* This is also prototyped in rs6000/darwin.h, inside the
415    MD_FALLBACK_FRAME_STATE_FOR macro.  */
416 extern bool _Unwind_fallback_frame_state_for (struct _Unwind_Context *context,
417                                               _Unwind_FrameState *fs);
418
419 /* Implement the MD_FALLBACK_FRAME_STATE_FOR macro,
420    returning true iff the frame was a sigreturn() frame that we
421    can understand.  */
422
423 bool
424 _Unwind_fallback_frame_state_for (struct _Unwind_Context *context,
425                                   _Unwind_FrameState *fs)
426 {
427   reg_unit gprs[32];
428
429   if (!interpret_libc (gprs, context))
430     return false;
431   return handle_syscall (fs, gprs, _Unwind_GetCFA (context));
432 }