diff options
Diffstat (limited to 'debuggerd/arm/pr-support.c')
-rw-r--r-- | debuggerd/arm/pr-support.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/debuggerd/arm/pr-support.c b/debuggerd/arm/pr-support.c new file mode 100644 index 00000000..358d9b77 --- /dev/null +++ b/debuggerd/arm/pr-support.c @@ -0,0 +1,345 @@ +/* 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; +} |