diff options
author | Bruce Beare <bruce.j.beare@intel.com> | 2010-10-13 14:21:30 -0700 |
---|---|---|
committer | Bruce Beare <bruce.j.beare@intel.com> | 2010-11-29 11:03:48 -0800 |
commit | 849249064cae9c1bb23b0204b5d35b832567801e (patch) | |
tree | 46945159307ad0c7252aab2f986ae310c261ee0a /debuggerd/arm | |
parent | 1a15cb7e904a562b0120039816fce5b8e6d5b147 (diff) | |
download | system_core-849249064cae9c1bb23b0204b5d35b832567801e.zip system_core-849249064cae9c1bb23b0204b5d35b832567801e.tar.gz system_core-849249064cae9c1bb23b0204b5d35b832567801e.tar.bz2 |
debuggerd: Reorganize to facilitate IA commit
Change-Id: I22dd02daf617672fc0a8ba7e396017c57f17e26e
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
Diffstat (limited to 'debuggerd/arm')
-rw-r--r-- | debuggerd/arm/crashglue.S | 30 | ||||
-rw-r--r-- | debuggerd/arm/machine.c | 229 | ||||
-rw-r--r-- | debuggerd/arm/pr-support.c | 345 | ||||
-rw-r--r-- | debuggerd/arm/unwind.c | 668 |
4 files changed, 1272 insertions, 0 deletions
diff --git a/debuggerd/arm/crashglue.S b/debuggerd/arm/crashglue.S new file mode 100644 index 0000000..eb9d0e3 --- /dev/null +++ b/debuggerd/arm/crashglue.S @@ -0,0 +1,30 @@ +.globl crash1 +.type crash1, %function +.globl crashnostack +.type crashnostack, %function + +crash1: + ldr r0, =0xa5a50000 + ldr r1, =0xa5a50001 + ldr r2, =0xa5a50002 + ldr r3, =0xa5a50003 + ldr r4, =0xa5a50004 + ldr r5, =0xa5a50005 + ldr r6, =0xa5a50006 + ldr r7, =0xa5a50007 + ldr r8, =0xa5a50008 + ldr r9, =0xa5a50009 + ldr r10, =0xa5a50010 + ldr r11, =0xa5a50011 + ldr r12, =0xa5a50012 + + mov lr, #0 + ldr lr, [lr] + b . + + +crashnostack: + mov sp, #0 + mov r0, #0 + ldr r0, [r0] + b . diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c new file mode 100644 index 0000000..ccd0baf --- /dev/null +++ b/debuggerd/arm/machine.c @@ -0,0 +1,229 @@ +/* system/debuggerd/debuggerd.c +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <pthread.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> + +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <sys/exec_elf.h> +#include <sys/stat.h> + +#include <cutils/sockets.h> +#include <cutils/properties.h> + +#include <linux/input.h> + +#include "utility.h" + +#ifdef WITH_VFP +#ifdef WITH_VFP_D32 +#define NUM_VFP_REGS 32 +#else +#define NUM_VFP_REGS 16 +#endif +#endif + +/* Main entry point to get the backtrace from the crashing process */ +extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], + int *frame0_pc_sane, + bool at_fault); + +void dump_stack_and_code(int tfd, int pid, mapinfo *map, + int unwind_depth, unsigned int sp_list[], + bool at_fault) +{ + unsigned int sp, pc, p, end, data; + struct pt_regs r; + int sp_depth; + bool only_in_tombstone = !at_fault; + char code_buffer[80]; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; + sp = r.ARM_sp; + pc = r.ARM_pc; + + _LOG(tfd, only_in_tombstone, "\ncode around pc:\n"); + + end = p = pc & ~3; + p -= 32; + end += 32; + + /* Dump the code around PC as: + * addr contents + * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c + * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 + */ + while (p <= end) { + int i; + + sprintf(code_buffer, "%08x ", p); + for (i = 0; i < 4; i++) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + sprintf(code_buffer + strlen(code_buffer), "%08x ", data); + p += 4; + } + _LOG(tfd, only_in_tombstone, "%s\n", code_buffer); + } + + if ((unsigned) r.ARM_lr != pc) { + _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); + + end = p = r.ARM_lr & ~3; + p -= 32; + end += 32; + + /* Dump the code around LR as: + * addr contents + * 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c + * 00008d44 f7ff18a0 490ced94 68035860 d0012b00 + */ + while (p <= end) { + int i; + + sprintf(code_buffer, "%08x ", p); + for (i = 0; i < 4; i++) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + sprintf(code_buffer + strlen(code_buffer), "%08x ", data); + p += 4; + } + _LOG(tfd, only_in_tombstone, "%s\n", code_buffer); + } + } + + p = sp - 64; + p &= ~3; + if (unwind_depth != 0) { + if (unwind_depth < STACK_CONTENT_DEPTH) { + end = sp_list[unwind_depth-1]; + } + else { + end = sp_list[STACK_CONTENT_DEPTH-1]; + } + } + else { + end = sp | 0x000000ff; + end += 0xff; + } + + _LOG(tfd, only_in_tombstone, "\nstack:\n"); + + /* If the crash is due to PC == 0, there will be two frames that + * have identical SP value. + */ + if (sp_list[0] == sp_list[1]) { + sp_depth = 1; + } + else { + sp_depth = 0; + } + + while (p <= end) { + char *prompt; + char level[16]; + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + if (p == sp_list[sp_depth]) { + sprintf(level, "#%02d", sp_depth++); + prompt = level; + } + else { + prompt = " "; + } + + /* Print the stack content in the log for the first 3 frames. For the + * rest only print them in the tombstone file. + */ + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + "%s %08x %08x %s\n", prompt, p, data, + map_to_name(map, data, "")); + p += 4; + } + /* print another 64-byte of stack data after the last frame */ + + end = p+64; + while (p <= end) { + data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + _LOG(tfd, (sp_depth > 2) || only_in_tombstone, + " %08x %08x %s\n", p, data, + map_to_name(map, data, "")); + p += 4; + } +} + +void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, + bool at_fault) +{ + struct pt_regs r; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); + return; + } + + if (unwound_level == 0) { + _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, + map_to_name(map, r.ARM_pc, "<unknown>")); + } + _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, + map_to_name(map, r.ARM_lr, "<unknown>")); +} + +void dump_registers(int tfd, int pid, bool at_fault) +{ + struct pt_regs r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); + _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); + _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", + r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); + _LOG(tfd, only_in_tombstone, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); + +#ifdef WITH_VFP + struct user_vfp vfp_regs; + int i; + + if(ptrace(PTRACE_GETVFPREGS, pid, 0, &vfp_regs)) { + _LOG(tfd, only_in_tombstone, + "cannot get registers: %s\n", strerror(errno)); + return; + } + + for (i = 0; i < NUM_VFP_REGS; i += 2) { + _LOG(tfd, only_in_tombstone, + " d%-2d %016llx d%-2d %016llx\n", + i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); + } + _LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr); +#endif +} diff --git a/debuggerd/arm/pr-support.c b/debuggerd/arm/pr-support.c new file mode 100644 index 0000000..358d9b7 --- /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; +} diff --git a/debuggerd/arm/unwind.c b/debuggerd/arm/unwind.c new file mode 100644 index 0000000..d191310 --- /dev/null +++ b/debuggerd/arm/unwind.c @@ -0,0 +1,668 @@ +/* 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/unwind-arm.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 <cutils/logd.h> +#include <sys/ptrace.h> +#include <unwind.h> +#include "utility.h" + +#include "symbol_table.h" + +typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ + +void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp); +bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp, + const type_info *rttip, + bool is_reference, + void **matched_object); + +/* Misc constants. */ +#define R_IP 12 +#define R_SP 13 +#define R_LR 14 +#define R_PC 15 + +#define EXIDX_CANTUNWIND 1 +#define uint32_highbit (((_uw) 1) << 31) + +#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1) +#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2) +#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3) +#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4) + +struct core_regs +{ + _uw r[16]; +}; + +/* We use normal integer types here to avoid the compiler generating + coprocessor instructions. */ +struct vfp_regs +{ + _uw64 d[16]; + _uw pad; +}; + +struct vfpv3_regs +{ + /* Always populated via VSTM, so no need for the "pad" field from + vfp_regs (which is used to store the format word for FSTMX). */ + _uw64 d[16]; +}; + +struct fpa_reg +{ + _uw w[3]; +}; + +struct fpa_regs +{ + struct fpa_reg f[8]; +}; + +struct wmmxd_regs +{ + _uw64 wd[16]; +}; + +struct wmmxc_regs +{ + _uw wc[4]; +}; + +/* Unwind descriptors. */ + +typedef struct +{ + _uw16 length; + _uw16 offset; +} EHT16; + +typedef struct +{ + _uw length; + _uw offset; +} EHT32; + +/* The ABI specifies that the unwind routines may only use core registers, + except when actually manipulating coprocessor state. This allows + us to write one implementation that works on all platforms by + demand-saving coprocessor registers. + + During unwinding we hold the coprocessor state in the actual hardware + registers and allocate demand-save areas for use during phase1 + unwinding. */ + +typedef struct +{ + /* The first fields must be the same as a phase2_vrs. */ + _uw demand_save_flags; + struct core_regs core; + _uw prev_sp; /* Only valid during forced unwinding. */ + struct vfp_regs vfp; + struct vfpv3_regs vfp_regs_16_to_31; + struct fpa_regs fpa; + struct wmmxd_regs wmmxd; + struct wmmxc_regs wmmxc; +} phase1_vrs; + +/* This must match the structure created by the assembly wrappers. */ +typedef struct +{ + _uw demand_save_flags; + struct core_regs core; +} phase2_vrs; + + +/* An exception index table entry. */ + +typedef struct __EIT_entry +{ + _uw fnoffset; + _uw content; +} __EIT_entry; + +/* Derived version to use ptrace */ +typedef _Unwind_Reason_Code (*personality_routine_with_ptrace) + (_Unwind_State, + _Unwind_Control_Block *, + _Unwind_Context *, + pid_t); + +/* Derived version to use ptrace */ +/* ABI defined personality routines. */ +static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); +static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State, + _Unwind_Control_Block *, _Unwind_Context *, pid_t); + +/* Execute the unwinding instructions described by UWS. */ +extern _Unwind_Reason_Code +unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, + pid_t pid); + +/* Derived version to use ptrace. Only handles core registers. Disregards + * FP and others. + */ +/* ABI defined function to pop registers off the stack. */ + +_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + _uw discriminator, + _Unwind_VRS_DataRepresentation representation, + pid_t pid) +{ + phase1_vrs *vrs = (phase1_vrs *) context; + + switch (regclass) + { + case _UVRSC_CORE: + { + _uw *ptr; + _uw mask; + int i; + + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + + mask = discriminator & 0xffff; + ptr = (_uw *) vrs->core.r[R_SP]; + /* Pop the requested registers. */ + for (i = 0; i < 16; i++) + { + if (mask & (1 << i)) { + vrs->core.r[i] = get_remote_word(pid, ptr); + ptr++; + } + } + /* Writeback the stack pointer value if it wasn't restored. */ + if ((mask & (1 << R_SP)) == 0) + vrs->core.r[R_SP] = (_uw) ptr; + } + return _UVRSR_OK; + + default: + return _UVRSR_FAILED; + } +} + +/* Core unwinding functions. */ + +/* Calculate the address encoded by a 31-bit self-relative offset at address + P. */ +static inline _uw +selfrel_offset31 (const _uw *p, pid_t pid) +{ + _uw offset = get_remote_word(pid, (void*)p); + + //offset = *p; + /* Sign extend to 32 bits. */ + if (offset & (1 << 30)) + offset |= 1u << 31; + else + offset &= ~(1u << 31); + + return offset + (_uw) p; +} + + +/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains + NREC entries. */ + +static const __EIT_entry * +search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address, + pid_t pid) +{ + _uw next_fn; + _uw this_fn; + int n, left, right; + + if (nrec == 0) + return (__EIT_entry *) 0; + + left = 0; + right = nrec - 1; + + while (1) + { + n = (left + right) / 2; + this_fn = selfrel_offset31 (&table[n].fnoffset, pid); + if (n != nrec - 1) + next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1; + else + next_fn = (_uw)0 - 1; + + if (return_address < this_fn) + { + if (n == left) + return (__EIT_entry *) 0; + right = n - 1; + } + else if (return_address <= next_fn) + return &table[n]; + else + left = n + 1; + } +} + +/* Find the exception index table eintry for the given address. */ +static const __EIT_entry* +get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp = NULL; + int nrec; + mapinfo *mi; + + /* The return address is the address of the instruction following the + call instruction (plus one in thumb mode). If this was the last + instruction in the function the address will lie in the following + function. Subtract 2 from the address so that it points within the call + instruction itself. */ + if (return_address >= 2) + return_address -= 2; + + for (mi = map; mi != NULL; mi = mi->next) { + if (return_address >= mi->start && return_address <= mi->end) break; + } + + if (mi) { + if (containing_map) *containing_map = mi; + eitp = (__EIT_entry *) mi->exidx_start; + nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry); + eitp = search_EIT_table (eitp, nrec, return_address, pid); + } + return eitp; +} + +/* Find the exception index table eintry for the given address. + Fill in the relevant fields of the UCB. + Returns _URC_FAILURE if an error occurred, _URC_OK on success. */ + +static _Unwind_Reason_Code +get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, + mapinfo *map, mapinfo **containing_map) +{ + const __EIT_entry *eitp; + + eitp = get_eitp(return_address, pid, map, containing_map); + + if (!eitp) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid); + + _uw eitp_content = get_remote_word(pid, (void *)&eitp->content); + + /* Can this frame be unwound at all? */ + if (eitp_content == EXIDX_CANTUNWIND) + { + UCB_PR_ADDR (ucbp) = 0; + return _URC_END_OF_STACK; + } + + /* Obtain the address of the "real" __EHT_Header word. */ + + if (eitp_content & uint32_highbit) + { + /* It is immediate data. */ + ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content; + ucbp->pr_cache.additional = 1; + } + else + { + /* The low 31 bits of the content field are a self-relative + offset to an _Unwind_EHT_Entry structure. */ + ucbp->pr_cache.ehtp = + (_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid); + ucbp->pr_cache.additional = 0; + } + + /* Discover the personality routine address. */ + if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31)) + { + /* One of the predefined standard routines. */ + _uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf; + if (idx == 0) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace; + else if (idx == 1) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace; + else if (idx == 2) + UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace; + else + { /* Failed */ + UCB_PR_ADDR (ucbp) = 0; + return _URC_FAILURE; + } + } + else + { + /* Execute region offset to PR */ + UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid); + /* Since we are unwinding the stack from a different process, it is + * impossible to execute the personality routine in debuggerd. Punt here. + */ + return _URC_FAILURE; + } + return _URC_OK; +} + +/* Print out the current call level, pc, and module name in the crash log */ +static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, + int tfd, + int stack_level, + mapinfo *map, + unsigned int sp_list[], + bool at_fault) +{ + _uw pc; + _uw rel_pc; + phase2_vrs *vrs = (phase2_vrs*) context; + const mapinfo *mi; + bool only_in_tombstone = !at_fault; + const struct symbol* sym = 0; + + if (stack_level < STACK_CONTENT_DEPTH) { + sp_list[stack_level] = vrs->core.r[R_SP]; + } + pc = vrs->core.r[R_PC]; + + // Top level frame + if (stack_level == 0) { + pc &= ~1; + } + // For deeper framers, rollback pc by one instruction + else { + pc = vrs->core.r[R_PC]; + /* Thumb mode - need to check whether the bl(x) has long offset or not. + * Examples: + * + * arm blx in the middle of thumb: + * 187ae: 2300 movs r3, #0 + * 187b0: f7fe ee1c blx 173ec + * 187b4: 2c00 cmp r4, #0 + * + * arm bl in the middle of thumb: + * 187d8: 1c20 adds r0, r4, #0 + * 187da: f136 fd15 bl 14f208 + * 187de: 2800 cmp r0, #0 + * + * pure thumb: + * 18894: 189b adds r3, r3, r2 + * 18896: 4798 blx r3 + * 18898: b001 add sp, #4 + */ + if (pc & 1) { + _uw prev_word; + pc = (pc & ~1); + prev_word = get_remote_word(pid, (char *) pc-4); + // Long offset + if ((prev_word & 0xf0000000) == 0xf0000000 && + (prev_word & 0x0000e000) == 0x0000e000) { + pc -= 4; + } + else { + pc -= 2; + } + } + else { + pc -= 4; + } + } + + /* We used to print the absolute PC in the back trace, and mask out the top + * 3 bits to guesstimate the offset in the .so file. This is not working for + * non-prelinked libraries since the starting offset may not be aligned on + * 1MB boundaries, and the library may be larger than 1MB. So for .so + * addresses we print the relative offset in back trace. + */ + rel_pc = pc; + mi = pc_to_mapinfo(map, pc, &rel_pc); + + /* See if we can determine what symbol this stack frame resides in */ + if (mi != 0 && mi->symbols != 0) { + sym = symbol_table_lookup(mi->symbols, rel_pc); + } + + if (sym) { + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s (%s)\n", stack_level, rel_pc, + mi ? mi->name : "", sym->name); + } else { + _LOG(tfd, only_in_tombstone, + " #%02d pc %08x %s\n", stack_level, rel_pc, + mi ? mi->name : ""); + } + + return _URC_NO_REASON; +} + +/* Derived from __gnu_Unwind_Backtrace to use ptrace */ +/* Perform stack backtrace through unwind data. Return the level of stack it + * unwinds. + */ +int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, + unsigned int sp_list[], int *frame0_pc_sane, + bool at_fault) +{ + phase1_vrs saved_vrs; + _Unwind_Reason_Code code = _URC_OK; + struct pt_regs r; + int i; + int stack_level = 0; + + _Unwind_Control_Block ucb; + _Unwind_Control_Block *ucbp = &ucb; + + if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; + + for (i = 0; i < 16; i++) { + saved_vrs.core.r[i] = r.uregs[i]; + /* + _LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]); + */ + } + + /* Set demand-save flags. */ + saved_vrs.demand_save_flags = ~(_uw) 0; + + /* + * If the app crashes because of calling the weeds, we cannot pass the PC + * to the usual unwinding code as the EXIDX mapping will fail. + * Instead, we simply print out the 0 as the top frame, and resume the + * unwinding process with the value stored in LR. + */ + if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { + *frame0_pc_sane = 0; + log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault); + saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR]; + stack_level++; + } + + do { + mapinfo *this_map = NULL; + /* Find the entry for this routine. */ + if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map) + != _URC_OK) { + /* Uncomment the code below to study why the unwinder failed */ +#if 0 + /* Shed more debugging info for stack unwinder improvement */ + if (this_map) { + _LOG(tfd, 1, + "Relative PC=%#x from %s not contained in EXIDX\n", + saved_vrs.core.r[R_PC] - this_map->start, this_map->name); + } + _LOG(tfd, 1, "PC=%#x SP=%#x\n", + saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]); +#endif + code = _URC_FAILURE; + break; + } + + /* The dwarf unwinder assumes the context structure holds things + like the function and LSDA pointers. The ARM implementation + caches these in the exception header (UCB). To avoid + rewriting everything we make the virtual IP register point at + the UCB. */ + _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp); + + /* Call log function. */ + if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, + map, sp_list, at_fault) != _URC_NO_REASON) { + code = _URC_FAILURE; + break; + } + stack_level++; + + /* Call the pr to decide what to do. */ + code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))( + _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp, + (void *) &saved_vrs, pid); + /* + * In theory the unwinding process will stop when the end of stack is + * reached or there is no unwinding information for the code address. + * To add another level of guarantee that the unwinding process + * will terminate we will stop it when the STACK_CONTENT_DEPTH is reached. + */ + } while (code != _URC_END_OF_STACK && code != _URC_FAILURE && + stack_level < STACK_CONTENT_DEPTH); + return stack_level; +} + + +/* Derived version to use ptrace */ +/* Common implementation for ARM ABI defined personality routines. + ID is the index of the personality routine, other arguments are as defined + by __aeabi_unwind_cpp_pr{0,1,2}. */ + +static _Unwind_Reason_Code +unwind_pr_common_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + int id, + pid_t pid) +{ + __gnu_unwind_state uws; + _uw *data; + int phase2_call_unexpected_after_unwind = 0; + + state &= _US_ACTION_MASK; + + data = (_uw *) ucbp->pr_cache.ehtp; + uws.data = get_remote_word(pid, data); + data++; + uws.next = data; + if (id == 0) + { + uws.data <<= 8; + uws.words_left = 0; + uws.bytes_left = 3; + } + else + { + uws.words_left = (uws.data >> 16) & 0xff; + uws.data <<= 16; + uws.bytes_left = 2; + data += uws.words_left; + } + + /* Restore the saved pointer. */ + if (state == _US_UNWIND_FRAME_RESUME) + data = (_uw *) ucbp->cleanup_cache.bitpattern[0]; + + if ((ucbp->pr_cache.additional & 1) == 0) + { + /* Process descriptors. */ + while (get_remote_word(pid, data)) { + /********************************************************************** + * The original code here seems to deal with exceptions that are not + * applicable in our toolchain, thus there is no way to test it for now. + * Instead of leaving it here and causing potential instability in + * debuggerd, we'd better punt here and leave the stack unwound. + * In the future when we discover cases where the stack should be unwound + * further but is not, we can revisit the code here. + **********************************************************************/ + return _URC_FAILURE; + } + /* Finished processing this descriptor. */ + } + + if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK) + return _URC_FAILURE; + + if (phase2_call_unexpected_after_unwind) + { + /* Enter __cxa_unexpected as if called from the call site. */ + _Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC)); + _Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected); + return _URC_INSTALL_CONTEXT; + } + + return _URC_CONTINUE_UNWIND; +} + + +/* ABI defined personality routine entry points. */ + +static _Unwind_Reason_Code +unwind_cpp_pr0_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr1_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid); +} + +static _Unwind_Reason_Code +unwind_cpp_pr2_with_ptrace (_Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context, + pid_t pid) +{ + return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid); +} |