655 lines
19 KiB
C
655 lines
19 KiB
C
|
/* 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"
|
||
|
|
||
|
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;
|
||
|
|
||
|
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, (void *) 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);
|
||
|
|
||
|
_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);
|
||
|
}
|