diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2010-12-13 11:31:04 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2010-12-13 11:31:04 -0800 |
commit | 5599029c8dacd2c3dc348ac903b35dfa76a93bf0 (patch) | |
tree | 1cdfacde62a9b4b364dfac02adab78720005234a /debuggerd/arm/unwind.c | |
parent | 127fd070f038825fc05624e2e3632c47999ffb19 (diff) | |
parent | 144773f9e1371a8d4b848c3136b931d5d0687b38 (diff) | |
download | system_core-5599029c8dacd2c3dc348ac903b35dfa76a93bf0.zip system_core-5599029c8dacd2c3dc348ac903b35dfa76a93bf0.tar.gz system_core-5599029c8dacd2c3dc348ac903b35dfa76a93bf0.tar.bz2 |
Manual merge of 144773f9
Change-Id: I0ac3f88ac85bbc604fdc151768da20e86318fb46
Diffstat (limited to 'debuggerd/arm/unwind.c')
-rw-r--r-- | debuggerd/arm/unwind.c | 668 |
1 files changed, 668 insertions, 0 deletions
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); +} |