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