darwin-fallback.c revision 169689
1218887Sdim/* Fallback frame-state unwinder for Darwin. 2218887Sdim Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3218887Sdim 4218887Sdim This file is part of GCC. 5218887Sdim 6218887Sdim GCC is free software; you can redistribute it and/or modify it 7218887Sdim under the terms of the GNU General Public License as published by 8218887Sdim the Free Software Foundation; either version 2, or (at your option) 9218887Sdim any later version. 10221345Sdim 11218887Sdim In addition to the permissions in the GNU General Public License, the 12218887Sdim Free Software Foundation gives you unlimited permission to link the 13218887Sdim compiled version of this file into combinations with other programs, 14218887Sdim and to distribute those combinations without any restriction coming 15218887Sdim from the use of this file. (The General Public License restrictions 16249423Sdim do apply in other respects; for example, they cover modification of 17218887Sdim the file, and distribution when not linked into a combined 18263508Sdim executable.) 19218887Sdim 20218887Sdim GCC is distributed in the hope that it will be useful, but WITHOUT 21218887Sdim ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 22218887Sdim or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 23218887Sdim License for more details. 24218887Sdim 25243830Sdim You should have received a copy of the GNU General Public License 26263508Sdim along with GCC; see the file COPYING. If not, write to the Free 27263508Sdim Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 28239462Sdim 02110-1301, USA. */ 29218887Sdim 30218887Sdim#include "tconfig.h" 31218887Sdim#include "tsystem.h" 32218887Sdim#include "coretypes.h" 33263508Sdim#include "tm.h" 34263508Sdim#include "dwarf2.h" 35263508Sdim#include "unwind.h" 36263508Sdim#include "unwind-dw2.h" 37263508Sdim#include <stdint.h> 38263508Sdim#include <stdbool.h> 39263508Sdim#include <sys/types.h> 40263508Sdim#include <signal.h> 41263508Sdim 42263508Sdimtypedef unsigned long reg_unit; 43263508Sdim 44263508Sdim/* Place in GPRS the parameters to the first 'sc' instruction that would 45263508Sdim have been executed if we were returning from this CONTEXT, or 46263508Sdim return false if an unexpected instruction is encountered. */ 47263508Sdim 48263508Sdimstatic bool 49263508Sdiminterpret_libc (reg_unit gprs[32], struct _Unwind_Context *context) 50263508Sdim{ 51263508Sdim uint32_t *pc = (uint32_t *)_Unwind_GetIP (context); 52263508Sdim uint32_t cr; 53263508Sdim reg_unit lr = (reg_unit) pc; 54263508Sdim reg_unit ctr = 0; 55263508Sdim uint32_t *invalid_address = NULL; 56263508Sdim 57263508Sdim int i; 58263508Sdim 59263508Sdim for (i = 0; i < 13; i++) 60263508Sdim gprs[i] = 1; 61263508Sdim gprs[1] = _Unwind_GetCFA (context); 62263508Sdim for (; i < 32; i++) 63263508Sdim gprs[i] = _Unwind_GetGR (context, i); 64263508Sdim cr = _Unwind_GetGR (context, CR2_REGNO); 65263508Sdim 66263508Sdim /* For each supported Libc, we have to track the code flow 67249423Sdim all the way back into the kernel. 68249423Sdim 69249423Sdim This code is believed to support all released Libc/Libsystem builds since 70249423Sdim Jaguar 6C115, including all the security updates. To be precise, 71249423Sdim 72249423Sdim Libc Libsystem Build(s) 73249423Sdim 262~1 60~37 6C115 74218887Sdim 262~1 60.2~4 6D52 75218887Sdim 262~1 61~3 6F21-6F22 76218887Sdim 262~1 63~24 6G30-6G37 77218887Sdim 262~1 63~32 6I34-6I35 78226633Sdim 262~1 63~64 6L29-6L60 79218887Sdim 262.4.1~1 63~84 6L123-6R172 80249423Sdim 81249423Sdim 320~1 71~101 7B85-7D28 82249423Sdim 320~1 71~266 7F54-7F56 83249423Sdim 320~1 71~288 7F112 84249423Sdim 320~1 71~289 7F113 85249423Sdim 320.1.3~1 71.1.1~29 7H60-7H105 86263508Sdim 320.1.3~1 71.1.1~30 7H110-7H113 87239462Sdim 320.1.3~1 71.1.1~31 7H114 88239462Sdim 89239462Sdim That's a big table! It would be insane to try to keep track of 90239462Sdim every little detail, so we just read the code itself and do what 91239462Sdim it would do. 92239462Sdim */ 93218887Sdim 94218887Sdim for (;;) 95263508Sdim { 96263508Sdim uint32_t ins = *pc++; 97263508Sdim 98263508Sdim if ((ins & 0xFC000003) == 0x48000000) /* b instruction */ 99218887Sdim { 100251662Sdim pc += ((((int32_t) ins & 0x3FFFFFC) ^ 0x2000000) - 0x2000004) / 4; 101251662Sdim continue; 102226633Sdim } 103249423Sdim if ((ins & 0xFC600000) == 0x2C000000) /* cmpwi */ 104249423Sdim { 105263508Sdim int32_t val1 = (int16_t) ins; 106263508Sdim int32_t val2 = gprs[ins >> 16 & 0x1F]; 107263508Sdim /* Only beq and bne instructions are supported, so we only 108218887Sdim need to set the EQ bit. */ 109249423Sdim uint32_t mask = 0xF << ((ins >> 21 & 0x1C) ^ 0x1C); 110249423Sdim if (val1 == val2) 111249423Sdim cr |= mask; 112249423Sdim else 113263508Sdim cr &= ~mask; 114263508Sdim continue; 115263508Sdim } 116249423Sdim if ((ins & 0xFEC38003) == 0x40820000) /* forwards beq/bne */ 117239462Sdim { 118239462Sdim if ((cr >> ((ins >> 16 & 0x1F) ^ 0x1F) & 1) == (ins >> 24 & 1)) 119239462Sdim pc += (ins & 0x7FFC) / 4 - 1; 120218887Sdim continue; 121263508Sdim } 122218887Sdim if ((ins & 0xFC0007FF) == 0x7C000378) /* or, including mr */ 123218887Sdim { 124218887Sdim gprs [ins >> 16 & 0x1F] = (gprs [ins >> 11 & 0x1F] 125251662Sdim | gprs [ins >> 21 & 0x1F]); 126251662Sdim continue; 127263508Sdim } 128263508Sdim if (ins >> 26 == 0x0E) /* addi, including li */ 129263508Sdim { 130218887Sdim reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F]; 131218887Sdim gprs [ins >> 21 & 0x1F] = src + (int16_t) ins; 132239462Sdim continue; 133218887Sdim } 134218887Sdim if (ins >> 26 == 0x0F) /* addis, including lis */ 135218887Sdim { 136218887Sdim reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F]; 137263508Sdim gprs [ins >> 21 & 0x1F] = src + ((int16_t) ins << 16); 138239462Sdim continue; 139249423Sdim } 140218887Sdim if (ins >> 26 == 0x20) /* lwz */ 141243830Sdim { 142218887Sdim reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F]; 143239462Sdim uint32_t *p = (uint32_t *)(src + (int16_t) ins); 144239462Sdim if (p == invalid_address) 145249423Sdim return false; 146218887Sdim gprs [ins >> 21 & 0x1F] = *p; 147239462Sdim continue; 148218887Sdim } 149243830Sdim if (ins >> 26 == 0x21) /* lwzu */ 150239462Sdim { 151218887Sdim uint32_t *p = (uint32_t *)(gprs [ins >> 16 & 0x1F] += (int16_t) ins); 152218887Sdim if (p == invalid_address) 153218887Sdim return false; 154239462Sdim gprs [ins >> 21 & 0x1F] = *p; 155239462Sdim continue; 156218887Sdim } 157239462Sdim if (ins >> 26 == 0x24) /* stw */ 158239462Sdim /* What we hope this is doing is '--in_sigtramp'. We don't want 159239462Sdim to actually store to memory, so just make a note of the 160218887Sdim address and refuse to load from it. */ 161263508Sdim { 162263508Sdim reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F]; 163263508Sdim uint32_t *p = (uint32_t *)(src + (int16_t) ins); 164218887Sdim if (p == NULL || invalid_address != NULL) 165263508Sdim return false; 166249423Sdim invalid_address = p; 167243830Sdim continue; 168218887Sdim } 169218887Sdim if (ins >> 26 == 0x2E) /* lmw */ 170249423Sdim { 171249423Sdim reg_unit src = (ins >> 16 & 0x1F) == 0 ? 0 : gprs [ins >> 16 & 0x1F]; 172263508Sdim uint32_t *p = (uint32_t *)(src + (int16_t) ins); 173218887Sdim int i; 174218887Sdim 175263508Sdim for (i = (ins >> 21 & 0x1F); i < 32; i++) 176218887Sdim { 177263508Sdim if (p == invalid_address) 178218887Sdim return false; 179263508Sdim gprs[i] = *p++; 180263508Sdim } 181263508Sdim continue; 182263508Sdim } 183263508Sdim if ((ins & 0xFC1FFFFF) == 0x7c0803a6) /* mtlr */ 184263508Sdim { 185263508Sdim lr = gprs [ins >> 21 & 0x1F]; 186218887Sdim continue; 187218887Sdim } 188226633Sdim if ((ins & 0xFC1FFFFF) == 0x7c0802a6) /* mflr */ 189218887Sdim { 190218887Sdim gprs [ins >> 21 & 0x1F] = lr; 191226633Sdim continue; 192263508Sdim } 193263508Sdim if ((ins & 0xFC1FFFFF) == 0x7c0903a6) /* mtctr */ 194263508Sdim { 195263508Sdim ctr = gprs [ins >> 21 & 0x1F]; 196263508Sdim continue; 197263508Sdim } 198226633Sdim /* The PowerPC User's Manual says that bit 11 of the mtcrf 199226633Sdim instruction is reserved and should be set to zero, but it 200263508Sdim looks like the Darwin assembler doesn't do that... */ 201218887Sdim if ((ins & 0xFC000FFF) == 0x7c000120) /* mtcrf */ 202226633Sdim { 203218887Sdim int i; 204226633Sdim uint32_t mask = 0; 205263508Sdim for (i = 0; i < 8; i++) 206263508Sdim mask |= ((-(ins >> (12 + i) & 1)) & 0xF) << 4 * i; 207263508Sdim cr = (cr & ~mask) | (gprs [ins >> 21 & 0x1F] & mask); 208263508Sdim continue; 209263508Sdim } 210263508Sdim if (ins == 0x429f0005) /* bcl- 20,4*cr7+so,.+4, loads pc into LR */ 211263508Sdim { 212263508Sdim lr = (reg_unit) pc; 213218887Sdim continue; 214218887Sdim } 215218887Sdim if (ins == 0x4e800420) /* bctr */ 216218887Sdim { 217263508Sdim pc = (uint32_t *) ctr; 218263508Sdim continue; 219263508Sdim } 220263508Sdim if (ins == 0x44000002) /* sc */ 221263508Sdim return true; 222263508Sdim 223218887Sdim return false; 224263508Sdim } 225263508Sdim} 226263508Sdim 227263508Sdim/* We used to include <ucontext.h> and <mach/thread_status.h>, 228263508Sdim but they change so much between different Darwin system versions 229263508Sdim that it's much easier to just write the structures involved here 230263508Sdim directly. */ 231263508Sdim 232263508Sdim/* These defines are from the kernel's bsd/dev/ppc/unix_signal.c. */ 233263508Sdim#define UC_TRAD 1 234263508Sdim#define UC_TRAD_VEC 6 235218887Sdim#define UC_TRAD64 20 236263508Sdim#define UC_TRAD64_VEC 25 237263508Sdim#define UC_FLAVOR 30 238263508Sdim#define UC_FLAVOR_VEC 35 239263508Sdim#define UC_FLAVOR64 40 240218887Sdim#define UC_FLAVOR64_VEC 45 241263508Sdim#define UC_DUAL 50 242263508Sdim#define UC_DUAL_VEC 55 243263508Sdim 244263508Sdimstruct gcc_ucontext 245218887Sdim{ 246218887Sdim int onstack; 247234353Sdim sigset_t sigmask; 248218887Sdim void * stack_sp; 249218887Sdim size_t stack_sz; 250263508Sdim int stack_flags; 251263508Sdim struct gcc_ucontext *link; 252263508Sdim size_t mcsize; 253218887Sdim struct gcc_mcontext32 *mcontext; 254263508Sdim}; 255263508Sdim 256263508Sdimstruct gcc_float_vector_state 257263508Sdim{ 258263508Sdim double fpregs[32]; 259263508Sdim uint32_t fpscr_pad; 260263508Sdim uint32_t fpscr; 261263508Sdim uint32_t save_vr[32][4]; 262263508Sdim uint32_t save_vscr[4]; 263263508Sdim}; 264263508Sdim 265218887Sdimstruct gcc_mcontext32 { 266218887Sdim uint32_t dar; 267218887Sdim uint32_t dsisr; 268218887Sdim uint32_t exception; 269218887Sdim uint32_t padding1[5]; 270218887Sdim uint32_t srr0; 271226633Sdim uint32_t srr1; 272218887Sdim uint32_t gpr[32]; 273218887Sdim uint32_t cr; 274239462Sdim uint32_t xer; 275239462Sdim uint32_t lr; 276239462Sdim uint32_t ctr; 277218887Sdim uint32_t mq; 278218887Sdim uint32_t vrsave; 279218887Sdim struct gcc_float_vector_state fvs; 280218887Sdim}; 281218887Sdim 282218887Sdim/* These are based on /usr/include/ppc/ucontext.h and 283218887Sdim /usr/include/mach/ppc/thread_status.h, but rewritten to be more 284218887Sdim convenient, to compile on Jaguar, and to work around Radar 3712064 285239462Sdim on Panther, which is that the 'es' field of 'struct mcontext64' has 286239462Sdim the wrong type (doh!). */ 287239462Sdim 288239462Sdimstruct gcc_mcontext64 { 289239462Sdim uint64_t dar; 290239462Sdim uint32_t dsisr; 291239462Sdim uint32_t exception; 292239462Sdim uint32_t padding1[4]; 293239462Sdim uint64_t srr0; 294218887Sdim uint64_t srr1; 295218887Sdim uint32_t gpr[32][2]; 296218887Sdim uint32_t cr; 297218887Sdim uint32_t xer[2]; /* These are arrays because the original structure has them misaligned. */ 298218887Sdim uint32_t lr[2]; 299263508Sdim uint32_t ctr[2]; 300263508Sdim uint32_t vrsave; 301263508Sdim struct gcc_float_vector_state fvs; 302263508Sdim}; 303263508Sdim 304218887Sdim#define UC_FLAVOR_SIZE \ 305218887Sdim (sizeof (struct gcc_mcontext32) - 33*16) 306239462Sdim 307218887Sdim#define UC_FLAVOR_VEC_SIZE (sizeof (struct gcc_mcontext32)) 308263508Sdim 309263508Sdim#define UC_FLAVOR64_SIZE \ 310263508Sdim (sizeof (struct gcc_mcontext64) - 33*16) 311263508Sdim 312263508Sdim#define UC_FLAVOR64_VEC_SIZE (sizeof (struct gcc_mcontext64)) 313263508Sdim 314218887Sdim/* Given GPRS as input to a 'sc' instruction, and OLD_CFA, update FS 315263508Sdim to represent the execution of a signal return; or, if not a signal 316263508Sdim return, return false. */ 317263508Sdim 318218887Sdimstatic bool 319218887Sdimhandle_syscall (_Unwind_FrameState *fs, const reg_unit gprs[32], 320218887Sdim _Unwind_Ptr old_cfa) 321218887Sdim{ 322218887Sdim struct gcc_ucontext *uctx; 323218887Sdim bool is_64, is_vector; 324218887Sdim struct gcc_float_vector_state * float_vector_state; 325218887Sdim _Unwind_Ptr new_cfa; 326218887Sdim int i; 327218887Sdim static _Unwind_Ptr return_addr; 328218887Sdim 329218887Sdim /* Yay! We're in a Libc that we understand, and it's made a 330218887Sdim system call. It'll be one of two kinds: either a Jaguar-style 331218887Sdim SYS_sigreturn, or a Panther-style 'syscall' call with 184, which 332218887Sdim is also SYS_sigreturn. */ 333218887Sdim 334218887Sdim if (gprs[0] == 0x67 /* SYS_SIGRETURN */) 335218887Sdim { 336218887Sdim uctx = (struct gcc_ucontext *) gprs[3]; 337218887Sdim is_vector = (uctx->mcsize == UC_FLAVOR64_VEC_SIZE 338218887Sdim || uctx->mcsize == UC_FLAVOR_VEC_SIZE); 339218887Sdim is_64 = (uctx->mcsize == UC_FLAVOR64_VEC_SIZE 340218887Sdim || uctx->mcsize == UC_FLAVOR64_SIZE); 341218887Sdim } 342218887Sdim else if (gprs[0] == 0 && gprs[3] == 184) 343218887Sdim { 344243830Sdim int ctxstyle = gprs[5]; 345218887Sdim uctx = (struct gcc_ucontext *) gprs[4]; 346218887Sdim is_vector = (ctxstyle == UC_FLAVOR_VEC || ctxstyle == UC_FLAVOR64_VEC 347239462Sdim || ctxstyle == UC_TRAD_VEC || ctxstyle == UC_TRAD64_VEC); 348239462Sdim is_64 = (ctxstyle == UC_FLAVOR64_VEC || ctxstyle == UC_TRAD64_VEC 349263508Sdim || ctxstyle == UC_FLAVOR64 || ctxstyle == UC_TRAD64); 350263508Sdim } 351251662Sdim else 352263508Sdim return false; 353263508Sdim 354263508Sdim#define set_offset(r, addr) \ 355263508Sdim (fs->regs.reg[r].how = REG_SAVED_OFFSET, \ 356263508Sdim fs->regs.reg[r].loc.offset = (_Unwind_Ptr)(addr) - new_cfa) 357263508Sdim 358263508Sdim /* Restore even the registers that are not call-saved, since they 359263508Sdim might be being used in the prologue to save other registers, 360263508Sdim for instance GPR0 is sometimes used to save LR. */ 361239462Sdim 362218887Sdim /* Handle the GPRs, and produce the information needed to do the rest. */ 363263508Sdim if (is_64) 364251662Sdim { 365251662Sdim /* The context is 64-bit, but it doesn't carry any extra information 366251662Sdim for us because only the low 32 bits of the registers are 367251662Sdim call-saved. */ 368218887Sdim struct gcc_mcontext64 *m64 = (struct gcc_mcontext64 *)uctx->mcontext; 369251662Sdim int i; 370218887Sdim 371218887Sdim float_vector_state = &m64->fvs; 372218887Sdim 373218887Sdim new_cfa = m64->gpr[1][1]; 374218887Sdim 375218887Sdim set_offset (CR2_REGNO, &m64->cr); 376218887Sdim for (i = 0; i < 32; i++) 377218887Sdim set_offset (i, m64->gpr[i] + 1); 378218887Sdim set_offset (XER_REGNO, m64->xer + 1); 379218887Sdim set_offset (LINK_REGISTER_REGNUM, m64->lr + 1); 380218887Sdim set_offset (COUNT_REGISTER_REGNUM, m64->ctr + 1); 381218887Sdim if (is_vector) 382218887Sdim set_offset (VRSAVE_REGNO, &m64->vrsave); 383218887Sdim 384218887Sdim /* Sometimes, srr0 points to the instruction that caused the exception, 385218887Sdim and sometimes to the next instruction to be executed; we want 386218887Sdim the latter. */ 387218887Sdim if (m64->exception == 3 || m64->exception == 4 388218887Sdim || m64->exception == 6 389218887Sdim || (m64->exception == 7 && !(m64->srr1 & 0x10000))) 390218887Sdim return_addr = m64->srr0 + 4; 391218887Sdim else 392218887Sdim return_addr = m64->srr0; 393218887Sdim } 394263508Sdim else 395263508Sdim { 396263508Sdim struct gcc_mcontext32 *m = uctx->mcontext; 397263508Sdim int i; 398263508Sdim 399243830Sdim float_vector_state = &m->fvs; 400263508Sdim 401263508Sdim new_cfa = m->gpr[1]; 402263508Sdim 403239462Sdim set_offset (CR2_REGNO, &m->cr); 404239462Sdim for (i = 0; i < 32; i++) 405263508Sdim set_offset (i, m->gpr + i); 406263508Sdim set_offset (XER_REGNO, &m->xer); 407263508Sdim set_offset (LINK_REGISTER_REGNUM, &m->lr); 408263508Sdim set_offset (COUNT_REGISTER_REGNUM, &m->ctr); 409263508Sdim 410263508Sdim if (is_vector) 411263508Sdim set_offset (VRSAVE_REGNO, &m->vrsave); 412263508Sdim 413263508Sdim /* Sometimes, srr0 points to the instruction that caused the exception, 414263508Sdim and sometimes to the next instruction to be executed; we want 415263508Sdim the latter. */ 416263508Sdim if (m->exception == 3 || m->exception == 4 417263508Sdim || m->exception == 6 418263508Sdim || (m->exception == 7 && !(m->srr1 & 0x10000))) 419263508Sdim return_addr = m->srr0 + 4; 420218887Sdim else 421218887Sdim return_addr = m->srr0; 422218887Sdim } 423218887Sdim 424239462Sdim fs->cfa_how = CFA_REG_OFFSET; 425249423Sdim fs->cfa_reg = STACK_POINTER_REGNUM; 426239462Sdim fs->cfa_offset = new_cfa - old_cfa;; 427239462Sdim 428239462Sdim /* The choice of column for the return address is somewhat tricky. 429249423Sdim Fortunately, the actual choice is private to this file, and 430239462Sdim the space it's reserved from is the GCC register space, not the 431239462Sdim DWARF2 numbering. So any free element of the right size is an OK 432239462Sdim choice. Thus: */ 433239462Sdim fs->retaddr_column = ARG_POINTER_REGNUM; 434239462Sdim /* FIXME: this should really be done using a DWARF2 location expression, 435239462Sdim not using a static variable. In fact, this entire file should 436249423Sdim be implemented in DWARF2 expressions. */ 437249423Sdim set_offset (ARG_POINTER_REGNUM, &return_addr); 438239462Sdim 439239462Sdim for (i = 0; i < 32; i++) 440239462Sdim set_offset (32 + i, float_vector_state->fpregs + i); 441239462Sdim set_offset (SPEFSCR_REGNO, &float_vector_state->fpscr); 442239462Sdim 443239462Sdim if (is_vector) 444218887Sdim { 445218887Sdim for (i = 0; i < 32; i++) 446218887Sdim set_offset (FIRST_ALTIVEC_REGNO + i, float_vector_state->save_vr + i); 447218887Sdim set_offset (VSCR_REGNO, float_vector_state->save_vscr); 448218887Sdim } 449218887Sdim 450239462Sdim return true; 451249423Sdim} 452239462Sdim 453249423Sdim/* This is also prototyped in rs6000/darwin.h, inside the 454239462Sdim MD_FALLBACK_FRAME_STATE_FOR macro. */ 455263508Sdimextern bool _Unwind_fallback_frame_state_for (struct _Unwind_Context *context, 456263508Sdim _Unwind_FrameState *fs); 457263508Sdim 458263508Sdim/* Implement the MD_FALLBACK_FRAME_STATE_FOR macro, 459263508Sdim returning true iff the frame was a sigreturn() frame that we 460263508Sdim can understand. */ 461263508Sdim 462263508Sdimbool 463263508Sdim_Unwind_fallback_frame_state_for (struct _Unwind_Context *context, 464263508Sdim _Unwind_FrameState *fs) 465239462Sdim{ 466239462Sdim reg_unit gprs[32]; 467239462Sdim 468239462Sdim if (!interpret_libc (gprs, context)) 469239462Sdim return false; 470239462Sdim return handle_syscall (fs, gprs, _Unwind_GetCFA (context)); 471239462Sdim} 472239462Sdim