diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-10-21 12:14:56 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2011-10-22 16:43:00 -0700 |
commit | 13e715b491e876865e752a3a69dd6f347049a488 (patch) | |
tree | 1f4c2193ecf40157b01fed7b9ee7d04d7b963dfd /debuggerd/arm | |
parent | 10484a068412613aaf3924f63a0b2f61400c7d1e (diff) | |
download | system_core-13e715b491e876865e752a3a69dd6f347049a488.zip system_core-13e715b491e876865e752a3a69dd6f347049a488.tar.gz system_core-13e715b491e876865e752a3a69dd6f347049a488.tar.bz2 |
Use libcorkscrew in debuggerd.
Change-Id: I5e3645a39d96c808f87075b49111d0262a19a0c8
Diffstat (limited to 'debuggerd/arm')
-rw-r--r-- | debuggerd/arm/machine.c | 332 | ||||
-rw-r--r-- | debuggerd/arm/pr-support.c | 345 | ||||
-rw-r--r-- | debuggerd/arm/unwind.c | 667 |
3 files changed, 48 insertions, 1296 deletions
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 5055444..891b1ef 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -34,10 +34,11 @@ #include <linux/input.h> #include <linux/user.h> -#include "utility.h" +#include "../utility.h" +#include "../machine.h" /* enable to dump memory pointed to by every register */ -#define DUMP_MEM_FOR_ALL_REGS 1 +#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 #ifdef WITH_VFP #ifdef WITH_VFP_D32 @@ -47,172 +48,22 @@ #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); - /* - * If this isn't clearly a null pointer dereference, dump the - * /proc/maps entries near the fault address. - * - * This only makes sense to do on the thread that crashed. + * If configured to do so, dump memory around *all* registers + * for the crashing thread. */ -static void show_nearby_maps(int tfd, int pid, mapinfo *map) -{ - siginfo_t si; - - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) { - _LOG(tfd, false, "cannot get siginfo for %d: %s\n", - pid, strerror(errno)); - return; - } - if (!signal_has_address(si.si_signo)) +static void dump_memory_and_code(int tfd, pid_t tid, bool at_fault) { + struct pt_regs regs; + if(ptrace(PTRACE_GETREGS, tid, 0, ®s)) { return; - - uintptr_t addr = (uintptr_t) si.si_addr; - addr &= ~0xfff; /* round to 4K page boundary */ - if (addr == 0) /* null-pointer deref */ - return; - - _LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr); - - /* - * Search for a match, or for a hole where the match would be. The list - * is backward from the file content, so it starts at high addresses. - */ - bool found = false; - mapinfo *next = NULL; - mapinfo *prev = NULL; - while (map != NULL) { - if (addr >= map->start && addr < map->end) { - found = true; - next = map->next; - break; - } else if (addr >= map->end) { - /* map would be between "prev" and this entry */ - next = map; - map = NULL; - break; - } - - prev = map; - map = map->next; - } - - /* - * Show "next" then "match" then "prev" so that the addresses appear in - * ascending order (like /proc/pid/maps). - */ - if (next != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name); - } else { - _LOG(tfd, false, "(no map below)\n"); - } - if (map != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name); - } else { - _LOG(tfd, false, "(no map for address)\n"); - } - if (prev != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name); - } else { - _LOG(tfd, false, "(no map above)\n"); - } -} - -/* - * Dumps a few bytes of memory, starting a bit before and ending a bit - * after the specified address. - */ -static void dump_memory(int tfd, int pid, uintptr_t addr, - bool only_in_tombstone) -{ - char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ - char ascii_buffer[32]; /* actual 16 + 1 == 17 */ - uintptr_t p, end; - - p = addr & ~3; - p -= 32; - if (p > addr) { - /* catch underflow */ - p = 0; - } - end = p + 80; - /* catch overflow; 'end - p' has to be multiples of 16 */ - while (end < p) - end -= 16; - - /* Dump the code around PC as: - * addr contents ascii - * 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q - * 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... - */ - while (p < end) { - char* asc_out = ascii_buffer; - - sprintf(code_buffer, "%08x ", p); - - int i; - for (i = 0; i < 4; i++) { - /* - * If we see (data == -1 && errno != 0), we know that the ptrace - * call failed, probably because we're dumping memory in an - * unmapped or inaccessible page. I don't know if there's - * value in making that explicit in the output -- it likely - * just complicates parsing and clarifies nothing for the - * enlightened reader. - */ - long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); - - int j; - for (j = 0; j < 4; j++) { - /* - * Our isprint() allows high-ASCII characters that display - * differently (often badly) in different viewers, so we - * just use a simpler test. - */ - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } - p += 4; - } - *asc_out = '\0'; - _LOG(tfd, only_in_tombstone, "%s %s\n", code_buffer, ascii_buffer); } -} - -void dump_stack_and_code(int tfd, int pid, mapinfo *map, - int unwind_depth, unsigned int sp_list[], - bool at_fault) -{ - struct pt_regs r; - int sp_depth; - bool only_in_tombstone = !at_fault; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; - - if (DUMP_MEM_FOR_ALL_REGS && at_fault) { - /* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - * - * TODO: remove duplicates. - */ - static const char REG_NAMES[] = "R0R1R2R3R4R5R6R7R8R9SLFPIPSPLRPC"; + if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - int reg; - for (reg = 0; reg < 16; reg++) { + for (int reg = 0; reg < 14; reg++) { /* this may not be a valid way to access, but it'll do for now */ - uintptr_t addr = r.uregs[reg]; + uintptr_t addr = regs.uregs[reg]; /* * Don't bother if it looks like a small int or ~= null, or if @@ -222,152 +73,65 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, continue; } - _LOG(tfd, only_in_tombstone, "\nmem near %.2s:\n", - ®_NAMES[reg*2]); - dump_memory(tfd, pid, addr, false); - } - } else { - unsigned int pc, lr; - pc = r.ARM_pc; - lr = r.ARM_lr; - - _LOG(tfd, only_in_tombstone, "\ncode around pc:\n"); - dump_memory(tfd, pid, (uintptr_t) pc, only_in_tombstone); - - if (lr != pc) { - _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); - dump_memory(tfd, pid, (uintptr_t) lr, only_in_tombstone); - } - } - - if (at_fault) { - show_nearby_maps(tfd, pid, map); - } - - unsigned int p, end; - unsigned int sp = r.ARM_sp; - - p = sp - 64; - if (p > sp) - p = 0; - p &= ~3; - if (unwind_depth != 0) { - if (unwind_depth < STACK_CONTENT_DEPTH) { - end = sp_list[unwind_depth-1]; + _LOG(tfd, false, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(tfd, tid, addr, at_fault); } - else { - end = sp_list[STACK_CONTENT_DEPTH-1]; - } - } - else { - end = p + 256; - /* 'end - p' has to be multiples of 4 */ - if (end < p) - end = ~7; - } - - _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]; - long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - if (p == sp_list[sp_depth]) { - sprintf(level, "#%02d", sp_depth++); - prompt = level; - } - else { - prompt = " "; - } + _LOG(tfd, !at_fault, "\ncode around pc:\n"); + dump_memory(tfd, tid, (uintptr_t)regs.ARM_pc, at_fault); - /* 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; + if (regs.ARM_pc != regs.ARM_lr) { + _LOG(tfd, !at_fault, "\ncode around lr:\n"); + dump_memory(tfd, tid, (uintptr_t)regs.ARM_lr, at_fault); } - /* print another 64-byte of stack data after the last frame */ - - end = p+64; - /* 'end - p' has to be multiples of 4 */ - if (end < p) - end = ~7; - - while (p <= end) { - long 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) +void dump_registers(ptrace_context_t* context __attribute((unused)), + int tfd, pid_t tid, 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)); + if(ptrace(PTRACE_GETREGS, tid, 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); + _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + (uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3); + _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + (uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7); + _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x sl %08x fp %08x\n", + (uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp); + _LOG(tfd, only_in_tombstone, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + (uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr, + (uint32_t)r.ARM_pc, (uint32_t)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)); + if(ptrace(PTRACE_GETVFPREGS, tid, 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, " 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); + _LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr); #endif } + +void dump_thread(ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) { + dump_registers(context, tfd, tid, at_fault); + + dump_backtrace_and_stack(context, tfd, tid, at_fault); + + if (at_fault) { + dump_memory_and_code(tfd, tid, at_fault); + dump_nearby_maps(context, tfd, tid); + } +} diff --git a/debuggerd/arm/pr-support.c b/debuggerd/arm/pr-support.c deleted file mode 100644 index 358d9b7..0000000 --- a/debuggerd/arm/pr-support.c +++ /dev/null @@ -1,345 +0,0 @@ -/* 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 deleted file mode 100644 index d9600b7..0000000 --- a/debuggerd/arm/unwind.c +++ /dev/null @@ -1,667 +0,0 @@ -/* 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. - */ - 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); -} |