| /* ARM EABI compliant unwinding routines | |
| Copyright (C) 2004, 2005 Free Software Foundation, Inc. | |
| Contributed by Paul Brook | |
| This file 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 combine | |
| executable.) | |
| This file 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 this program; see the file COPYING. If not, write to | |
| the Free Software Foundation, 51 Franklin Street, Fifth Floor, | |
| Boston, MA 02110-1301, USA. */ | |
| /**************************************************************************** | |
| * The functions here are derived from gcc/config/arm/pr-support.c from the | |
| * 4.3.x release. The main changes here involve the use of ptrace to retrieve | |
| * memory/processor states from a remote process. | |
| ****************************************************************************/ | |
| #include <sys/types.h> | |
| #include <unwind.h> | |
| #include "utility.h" | |
| /* We add a prototype for abort here to avoid creating a dependency on | |
| target headers. */ | |
| extern void abort (void); | |
| /* Derived from _Unwind_VRS_Pop to use ptrace */ | |
| extern _Unwind_VRS_Result | |
| unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, | |
| _Unwind_VRS_RegClass regclass, | |
| _uw discriminator, | |
| _Unwind_VRS_DataRepresentation representation, | |
| pid_t pid); | |
| typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ | |
| /* Misc constants. */ | |
| #define R_IP 12 | |
| #define R_SP 13 | |
| #define R_LR 14 | |
| #define R_PC 15 | |
| #define uint32_highbit (((_uw) 1) << 31) | |
| void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); | |
| /* Unwind descriptors. */ | |
| typedef struct | |
| { | |
| _uw16 length; | |
| _uw16 offset; | |
| } EHT16; | |
| typedef struct | |
| { | |
| _uw length; | |
| _uw offset; | |
| } EHT32; | |
| /* Personality routine helper functions. */ | |
| #define CODE_FINISH (0xb0) | |
| /* Derived from next_unwind_byte to use ptrace */ | |
| /* Return the next byte of unwinding information, or CODE_FINISH if there is | |
| no data remaining. */ | |
| static inline _uw8 | |
| next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid) | |
| { | |
| _uw8 b; | |
| if (uws->bytes_left == 0) | |
| { | |
| /* Load another word */ | |
| if (uws->words_left == 0) | |
| return CODE_FINISH; /* Nothing left. */ | |
| uws->words_left--; | |
| uws->data = get_remote_word(pid, uws->next); | |
| uws->next++; | |
| uws->bytes_left = 3; | |
| } | |
| else | |
| uws->bytes_left--; | |
| /* Extract the most significant byte. */ | |
| b = (uws->data >> 24) & 0xff; | |
| uws->data <<= 8; | |
| return b; | |
| } | |
| /* Execute the unwinding instructions described by UWS. */ | |
| _Unwind_Reason_Code | |
| unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, | |
| pid_t pid) | |
| { | |
| _uw op; | |
| int set_pc; | |
| _uw reg; | |
| set_pc = 0; | |
| for (;;) | |
| { | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| if (op == CODE_FINISH) | |
| { | |
| /* If we haven't already set pc then copy it from lr. */ | |
| if (!set_pc) | |
| { | |
| _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, | |
| ®); | |
| _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, | |
| ®); | |
| set_pc = 1; | |
| } | |
| /* Drop out of the loop. */ | |
| break; | |
| } | |
| if ((op & 0x80) == 0) | |
| { | |
| /* vsp = vsp +- (imm6 << 2 + 4). */ | |
| _uw offset; | |
| offset = ((op & 0x3f) << 2) + 4; | |
| _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
| if (op & 0x40) | |
| reg -= offset; | |
| else | |
| reg += offset; | |
| _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
| continue; | |
| } | |
| if ((op & 0xf0) == 0x80) | |
| { | |
| op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid); | |
| if (op == 0x8000) | |
| { | |
| /* Refuse to unwind. */ | |
| return _URC_FAILURE; | |
| } | |
| /* Pop r4-r15 under mask. */ | |
| op = (op << 4) & 0xfff0; | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| if (op & (1 << R_PC)) | |
| set_pc = 1; | |
| continue; | |
| } | |
| if ((op & 0xf0) == 0x90) | |
| { | |
| op &= 0xf; | |
| if (op == 13 || op == 15) | |
| /* Reserved. */ | |
| return _URC_FAILURE; | |
| /* vsp = r[nnnn]. */ | |
| _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); | |
| _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); | |
| continue; | |
| } | |
| if ((op & 0xf0) == 0xa0) | |
| { | |
| /* Pop r4-r[4+nnn], [lr]. */ | |
| _uw mask; | |
| mask = (0xff0 >> (7 - (op & 7))) & 0xff0; | |
| if (op & 8) | |
| mask |= (1 << R_LR); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if ((op & 0xf0) == 0xb0) | |
| { | |
| /* op == 0xb0 already handled. */ | |
| if (op == 0xb1) | |
| { | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| if (op == 0 || ((op & 0xf0) != 0)) | |
| /* Spare. */ | |
| return _URC_FAILURE; | |
| /* Pop r0-r4 under mask. */ | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, | |
| _UVRSD_UINT32, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if (op == 0xb2) | |
| { | |
| /* vsp = vsp + 0x204 + (uleb128 << 2). */ | |
| int shift; | |
| _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
| ®); | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| shift = 2; | |
| while (op & 0x80) | |
| { | |
| reg += ((op & 0x7f) << shift); | |
| shift += 7; | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| } | |
| reg += ((op & 0x7f) << shift) + 0x204; | |
| _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, | |
| ®); | |
| continue; | |
| } | |
| if (op == 0xb3) | |
| { | |
| /* Pop VFP registers with fldmx. */ | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if ((op & 0xfc) == 0xb4) | |
| { | |
| /* Pop FPA E[4]-E[4+nn]. */ | |
| op = 0x40000 | ((op & 3) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| /* op & 0xf8 == 0xb8. */ | |
| /* Pop VFP D[8]-D[8+nnn] with fldmx. */ | |
| op = 0x80000 | ((op & 7) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if ((op & 0xf0) == 0xc0) | |
| { | |
| if (op == 0xc6) | |
| { | |
| /* Pop iWMMXt D registers. */ | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, | |
| _UVRSD_UINT64, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if (op == 0xc7) | |
| { | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| if (op == 0 || (op & 0xf0) != 0) | |
| /* Spare. */ | |
| return _URC_FAILURE; | |
| /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op, | |
| _UVRSD_UINT32, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if ((op & 0xf8) == 0xc0) | |
| { | |
| /* Pop iWMMXt wR[10]-wR[10+nnn]. */ | |
| op = 0xa0000 | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, | |
| _UVRSD_UINT64, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| if (op == 0xc8) | |
| { | |
| #ifndef __VFP_FP__ | |
| /* Pop FPA registers. */ | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| #else | |
| /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, | |
| _UVRSD_DOUBLE, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| #endif | |
| } | |
| if (op == 0xc9) | |
| { | |
| /* Pop VFP registers with fldmd. */ | |
| op = next_unwind_byte_with_ptrace (uws, pid); | |
| op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, | |
| _UVRSD_DOUBLE, pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| /* Spare. */ | |
| return _URC_FAILURE; | |
| } | |
| if ((op & 0xf8) == 0xd0) | |
| { | |
| /* Pop VFP D[8]-D[8+nnn] with fldmd. */ | |
| op = 0x80000 | ((op & 7) + 1); | |
| if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE, | |
| pid) | |
| != _UVRSR_OK) | |
| return _URC_FAILURE; | |
| continue; | |
| } | |
| /* Spare. */ | |
| return _URC_FAILURE; | |
| } | |
| return _URC_OK; | |
| } |