]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/rs6000/darwin-fallback.c
Index: gcc/ChangeLog
[thirdparty/gcc.git] / gcc / config / rs6000 / darwin-fallback.c
CommitLineData
f8a57be8
GK
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
42typedef 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
48static bool
49interpret_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
245struct 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
276static bool
277handle_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. */
416extern 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
423bool
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}