diff -r 000000000000 -r a41df078684a kernel/eka/compsupp/symaehabi/unwinder.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/compsupp/symaehabi/unwinder.c Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1218 @@ +/* unwinder.c + * + * Copyright 2002-2005 ARM Limited. All rights reserved. + * + * Your rights to use this code are set out in the accompanying licence + * text file LICENCE.txt (ARM contract number LEC-ELA-00080 v1.0). + */ + +/* Portions copyright Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). */ + +/* + * RCS $Revision: 92986 $ + * Checkin $Date: 2005-10-13 15:56:12 +0100 (Thu, 13 Oct 2005) $ + * Revising $Author: achapman $ + */ + +/* Language-independent unwinder implementation */ + +/* This source file is compiled automatically by ARM's make system into + * multiple object files. The source regions constituting object file + * xxx.o are delimited by ifdef xxx_c / endif directives. + * + * The source regions currently marked are: + * unwinder_c + * unwind_activity_c + */ + +#ifndef __EPOC32__ +#include +#include +#else +#include +#endif +/* Environment: */ +#include "unwind_env.h" +/* Language-independent unwinder declarations: */ +#include "unwinder.h" + +#ifdef __EPOC32__ +/* Symbian specific support */ +#include "symbian_support.h" +#endif + +/* Define UNWIND_ACTIVITY_DIAGNOSTICS for printed information from _Unwind_Activity */ +/* Define VRS_DIAGNOSTICS for printed diagnostics about VRS operations */ + +#if defined(VRS_DIAGNOSTICS) || defined(UNWIND_ACTIVITY_DIAGNOSTICS) +#ifndef __EPOC32__ +extern int printf(const char *, ...); +#endif +#endif + +#ifdef SUPPORT_NESTED_EXCEPTIONS +extern _Unwind_Control_Block *AllocSavedUCB(); +extern void FreeSavedUCB(_Unwind_Control_Block *context); +#endif + +#ifdef unwinder_c + +/* ========================= ========================= */ +/* ========================= Virtual register set ========================= */ +/* ========================= ========================= */ + +/* The approach taken by this implementation is to use the real machine + * registers to hold all but the values of core (integer) + * registers. Consequently the implementation must use only the core + * registers except when manipulating the virtual register set. Non-core + * registers are saved only on first use, so the single implementation can + * cope with execution on processors which lack certain registers. The + * registers as they were at the start of the propagation must be preserved + * over phase 1 so that the machine state is correct at the start of phase + * 2. This requires a copy to be taken (which can be stack allocated). During + * a stack unwind (phase 1 or phase 2), the "current" virtual register set is + * implemented as core register values held in a data structure, and non-core + * register values held in the registers themselves. To ensure that all + * original register values are available at the beginning of phase 2, the + * core registers are saved in a second structure at the start of phase 1 and + * the non-core registers are demand-saved into another part of the data + * structure that holds the current core registers during the phase 1 stack + * unwind. + */ +/* Extent to which the access routines are implemented: + * _Unwind_VRS_Get and _Unwind_VRS_Set implement only access to the core registers. + * _Unwind_VRS_Pop implements only popping of core and vfp registers. + * There is no support here for the Intel WMMX registers, but space is nevertheless + * reserved in the virtual register set structure to indicate whether demand-saving + * of those registers is required (as they are unsupported, it never is). The space + * costs nothing as it is required for alignment. + * The level of supported functionality is compliant with the requirements of the + * Exceptions ABI. + */ + +typedef unsigned char bool; +struct core_s { uint32_t r[16]; }; /* core integer regs */ +struct vfp_s { uint64_t d[32]; }; /* VFP registers saved in FSTMD format */ + +/* Phase 1 virtual register set includes demand-save areas */ +/* The phase 2 virtual register set must be a prefix of the phase 1 set */ +typedef struct phase1_virtual_register_set_s { + /* demand_save flag == 1 means save the registers in the demand-save area */ + bool demand_save_vfp_low; + bool demand_save_vfp_high; + bool demand_save_wmmxd; + bool demand_save_wmmxc; + struct core_s core; /* current core registers */ + struct vfp_s vfp; /* demand-saved vfp registers */ +} phase1_virtual_register_set; + +/* Phase 2 virtual register set has no demand-save areas */ +/* The phase 2 virtual register set must be a prefix of the phase 1 set */ +/* The assembly fragments for _Unwind_RaiseException and _Unwind_Resume create + * a phase2_virtual_register_set_s by hand so be careful. + */ +typedef struct phase2_virtual_register_set_s { + /* demand_save flag == 1 means save the registers in the demand-save area */ + /* Always 0 in phase 2 */ + bool demand_save_vfp_low; + bool demand_save_vfp_high; + bool demand_save_wmmxd; + bool demand_save_wmmxc; + struct core_s core; /* current core registers */ +} phase2_virtual_register_set; + +/* -- Helper macros for the embedded assembly */ + +#if defined(__TARGET_ARCH_5T) || defined(__TARGET_ARCH_5TXM) || \ + defined(__TARGET_ARCH_5TE) || defined(__TARGET_ARCH_6) || \ + defined(__TARGET_ARCH_6T2) || defined(__TARGET_ARCH_7_A) /* || ... */ + #define ARCH_5T_OR_LATER 1 +#else + #define ARCH_5T_OR_LATER 0 +#endif + +#if defined(__APCS_INTERWORK) && !ARCH_5T_OR_LATER + #define OLD_STYLE_INTERWORKING 1 +#else + #define OLD_STYLE_INTERWORKING 0 +#endif + +#if defined(__TARGET_ARCH_4T) || defined(__TARGET_ARCH_4TXM) || ARCH_5T_OR_LATER + #define HAVE_BX 1 +#else + #define HAVE_BX 0 +#endif + +#if defined(__TARGET_ARCH_THUMBNAIL) + #define THUMBNAIL 1 +#else + #define THUMBNAIL 0 +#endif + +#if HAVE_BX + #define RET_LR bx lr +#else + #define RET_LR mov pc,lr +#endif + +/* ----- Routines: ----- */ + +/* ----- Helper routines, private ----- */ + +/* R_ARM_PREL31 is a place-relative 31-bit signed relocation. The + * routine takes the address of a location that was relocated by + * R_ARM_PREL31, and returns an absolute address. + */ +static FORCEINLINE uint32_t __ARM_resolve_prel31(void *p) +{ + return (uint32_t)((((*(int32_t *)p) << 1) >> 1) + (int32_t)p); +} + +/* ----- Helper routines, private but external ----- */ + +/* Note '%0' refers to local label '0' */ +#if defined(__thumb) +#define MAYBE_SWITCH_TO_ARM_STATE SWITCH_TO_ARM_STATE +#define MAYBE_CODE16 code16 +#else +#define MAYBE_SWITCH_TO_ARM_STATE /* nothing */ +#define MAYBE_CODE16 /* nothing */ +#endif +__asm void __ARM_Unwind_VRS_VFPpreserve_low(void *vfpp) +{ +vfp_d0 CN 0; + /* Preserve the low vfp registers in the passed memory */ +#if defined(__thumb) + macro; + SWITCH_TO_ARM_STATE; +1 + align 4; +2 + assert (%2 - %1) = 0; + bx pc; + nop; + code32; + mend; +#endif + + MAYBE_SWITCH_TO_ARM_STATE; + stc p11,vfp_d0,[r0],{0x20}; /* 0xec800b20 FSTMIAD r0,{d0-d15} */ + RET_LR; + MAYBE_CODE16; +} + +__asm void __ARM_Unwind_VRS_VFPpreserve_high(void *vfpp) +{ +vfp_d16 CN 0; /* =16 when used with stcl */ + /* Preserve the high vfp registers in the passed memory */ + MAYBE_SWITCH_TO_ARM_STATE; + stcl p11,vfp_d16,[r0],{0x20}; /* 0xecc00b20 FSTMIAD r0,{d16-d31} */ + RET_LR; + MAYBE_CODE16; +} + +__asm void __ARM_Unwind_VRS_VFPrestore_low(void *vfpp) +{ + /* Restore the low vfp registers from the passed memory */ +vfp_d0 CN 0; + MAYBE_SWITCH_TO_ARM_STATE; + ldc p11,vfp_d0,[r0],{0x20}; /* 0xec900b20 FLDMIAD r0,{d0-d15} */ + RET_LR; + MAYBE_CODE16; +} + +__asm void __ARM_Unwind_VRS_VFPrestore_high(void *vfpp) +{ + /* Restore the high vfp registers from the passed memory */ +vfp_d16 CN 0; /* =16 when used with ldcl */ + MAYBE_SWITCH_TO_ARM_STATE; + ldcl p11,vfp_d16,[r0],{0x20}; /* 0xecd00b20 FLDMIAD r0,{d16-d31} */ + RET_LR; + MAYBE_CODE16; +} + + +__asm NORETURNDECL void __ARM_Unwind_VRS_corerestore(void *corep) +{ + /* We rely here on corep pointing to a location in the stack, + * as we briefly assign it to sp. This allows us to safely do + * ldmia's which restore sp (if we use a different base register, + * the updated sp may be used by the handler of any data abort + * that occurs during the ldmia, and the stack gets overwritten). + * By hypothesis this is preserve8 but the load of sp means the + * assembler can't infer that. + */ +#if THUMBNAIL + preserve8; + mov.w r13, r0; + ldmia.w r13!,{r0-r12}; + ldr.w r14, [r13, #4] /* lr */ + ldr.w r12, [r13, #4*2] /* pc */ + ldr.w r13, [r13, #0] /* sp */ + bx r12 + +#else + preserve8; + MAYBE_SWITCH_TO_ARM_STATE; +#if OLD_STYLE_INTERWORKING + mov r13, r0; + ldmia r13!,{r0-r12}; + ldr r12,[r13, #4*2]; /* pc */ + ldmia r13,{r13-r14}; + bx r12; +#else + + #if __ARMCC_VERSION < 300000 + mov r13, r0; + ldmia r13,{r0-r15}; + #else + mov r14, r0; + ldmia r14!, {r0-r12}; + ldr r13, [r14], #4; + ldmia r14, {r14,r15}; + #endif + +#endif + MAYBE_CODE16; +#endif +} + + +/* ----- Development support ----- */ + +#ifdef VRS_DIAGNOSTICS +static void debug_print_vrs_vfp(uint32_t base, uint64_t *lp) +{ + int c = 0; + int i; + for (i = 0; i < 16; i++) { + printf("D%-2d 0x%16.16llx ", i + base, *lp); + lp++; + if (c++ == 1) { + c = 0; + printf("\n"); + } + } +} + + +static void debug_print_vrs(_Unwind_Context *context) +{ + phase1_virtual_register_set *vrsp = (phase1_virtual_register_set *)context; + int i; + int c; + printf("------------------------------------------------------------------------\n"); + c = 0; + for (i = 0; i < 16; i++) { + printf("r%-2d 0x%8.8x ", i, vrsp->core.r[i]); + if (c++ == 3) { + c = 0; + printf("\n"); + } + } + + printf("-----\n"); + if (vrsp->demand_save_vfp_low == 1) + printf("VFP low registers not saved\n"); + else + debug_print_vrs_vfp(0, &vrsp->vfp.d[0]); + printf("-----\n"); + if (vrsp->demand_save_vfp_high == 1) + printf("VFP high registers not saved\n"); + else + debug_print_vrs_vfp(16, &vrsp->vfp.d[16]); + printf("------------------------------------------------------------------------\n"); +} +#endif + + +/* ----- Public routines ----- */ + +EXPORT_C _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) +{ + phase1_virtual_register_set *vrsp = (phase1_virtual_register_set *)context; + switch (regclass) { + case _UVRSC_CORE: + { + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + vrsp->core.r[regno] = *(uint32_t *)valuep; + return _UVRSR_OK; + } + case _UVRSC_VFP: + case _UVRSC_WMMXD: + case _UVRSC_WMMXC: + return _UVRSR_NOT_IMPLEMENTED; + default: + break; + } + return _UVRSR_FAILED; +} + + +EXPORT_C _Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) +{ + phase1_virtual_register_set *vrsp = (phase1_virtual_register_set *)context; + switch (regclass) { + case _UVRSC_CORE: + { + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + *(uint32_t *)valuep = vrsp->core.r[regno]; + return _UVRSR_OK; + } + case _UVRSC_VFP: + case _UVRSC_WMMXD: + case _UVRSC_WMMXC: + return _UVRSR_NOT_IMPLEMENTED; + default: + break; + } + return _UVRSR_FAILED; +} + + +#define R_SP 13 + +EXPORT_C _Unwind_VRS_Result _Unwind_VRS_Pop(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t descriminator, + _Unwind_VRS_DataRepresentation representation) +{ + phase1_virtual_register_set *vrsp = (phase1_virtual_register_set *)context; + switch (regclass) { + case _UVRSC_CORE: + { + /* If SP is included in the mask, the loaded value is used in preference to + * the writeback value, but only on completion of the loading. + */ + uint32_t mask, *vsp, *rp, sp_loaded; + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + vsp = (uint32_t *)vrsp->core.r[R_SP]; + rp = (uint32_t *)&vrsp->core; + mask = descriminator & 0xffff; + sp_loaded = mask & (1 << R_SP); + while (mask != 0) { + if (mask & 1) { +#ifdef VRS_DIAGNOSTICS + printf("VRS Pop r%d\n", rp - &vrsp->core.r[0]); +#endif + *rp = *vsp++; + } + rp++; + mask >>= 1; + } + if (!sp_loaded) + vrsp->core.r[R_SP] = (uint32_t)vsp; + return _UVRSR_OK; + } + case _UVRSC_VFP: + { + uint32_t start = descriminator >> 16; + uint32_t count = descriminator & 0xffff; + bool some_low = start < 16; + bool some_high = start + count > 16; + if ((representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) || + (representation == _UVRSD_VFPX && some_high) || + (representation == _UVRSD_DOUBLE && start + count > 32)) + return _UVRSR_FAILED; + if (some_low && vrsp->demand_save_vfp_low == 1) { /* Demand-save over phase 1 */ + vrsp->demand_save_vfp_low = 0; + __ARM_Unwind_VRS_VFPpreserve_low(&vrsp->vfp.d[0]); + } + if (some_high && vrsp->demand_save_vfp_high == 1) { /* Demand-save over phase 1 */ + vrsp->demand_save_vfp_high = 0; + __ARM_Unwind_VRS_VFPpreserve_high(&vrsp->vfp.d[16]); + } + /* Now recover from the stack into the real machine registers. + * Note for _UVRSD_VFPX we assume FSTMX standard format 1. + * Do this by saving the current VFP registers to a memory area, + * moving the in-memory values into that area, and + * restoring from the whole area. + * Must be careful as the 64-bit values saved by FSTMX might be + * only 32-bit aligned. + */ + { + struct unaligned_vfp_reg_s { uint32_t w1; uint32_t w2; }; + struct unaligned_vfp_reg_s *vsp; + struct vfp_s temp_vfp; + if (some_low) + __ARM_Unwind_VRS_VFPpreserve_low(&temp_vfp.d[0]); + if (some_high) + __ARM_Unwind_VRS_VFPpreserve_high(&temp_vfp.d[16]); + vsp = (struct unaligned_vfp_reg_s *)vrsp->core.r[R_SP]; + while (count--) { + struct unaligned_vfp_reg_s *v = + (struct unaligned_vfp_reg_s *)&temp_vfp.d[start++]; + *v = *vsp++; +#ifdef VRS_DIAGNOSTICS + printf("VRS Pop D%d = 0x%llx\n", start - 1, temp_vfp.d[start - 1]); +#endif + } + vrsp->core.r[R_SP] = (uint32_t)((uint32_t *)vsp + + (representation == _UVRSD_VFPX ? + 1 : /* +1 to skip the format word */ + 0)); + if (some_low) + __ARM_Unwind_VRS_VFPrestore_low(&temp_vfp.d[0]); + if (some_high) + __ARM_Unwind_VRS_VFPrestore_high(&temp_vfp.d[16]); + } + return _UVRSR_OK; + } + case _UVRSC_WMMXD: + case _UVRSC_WMMXC: + return _UVRSR_NOT_IMPLEMENTED; + default: + break; + } + return _UVRSR_FAILED; +} + + + +/* ========================= ========================= */ +/* ========================= The unwinder ========================= */ +/* ========================= ========================= */ + + +/* This implementation uses the UCB unwinder_cache as follows: + * reserved1 is documented in the EABI as requiring initialisation to 0. + * It is used to manage nested simultaneous propagation. If the value is 0, + * the UCB is participating in no propagations. If the value is 1, the UCB + * is participating in one propagation. Otherwise the value is a pointer to + * a structure holding saved UCB state from the next propagation out. + * The structure used is simply a mallocated UCB. + * reserved2 is used to preserve the call-site address over calls to a + * personality routine and cleanup. + * reserved3 is used to cache the PR address. + * reserved4 is used by the Symbian implementation to cache the ROM exeception + * search table + * reserved5 is used by the symbian implementation to cache the + * TExceptionDescriptor for the executable of the 'current' frame + */ + +#define NESTED_CONTEXT unwinder_cache.reserved1 +#define SAVED_CALLSITE_ADDR unwinder_cache.reserved2 +#define PR_ADDR unwinder_cache.reserved3 + +/* Index table entry: */ + +#ifndef __EPOC32__ // Symbian OS defines this in symbian_support.h +typedef struct __EIT_entry { + uint32_t fnoffset; /* Place-relative */ + uint32_t content; +} __EIT_entry; +#endif + +/* Private defines etc: */ + +static const uint32_t EXIDX_CANTUNWIND = 1; +static const uint32_t uint32_highbit = 0x80000000; + +/* ARM C++ personality routines: */ + +typedef _Unwind_Reason_Code (*personality_routine)(_Unwind_State, + _Unwind_Control_Block *, + _Unwind_Context *); + +WEAKDECL _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(_Unwind_State state, _Unwind_Control_Block *, + _Unwind_Context *context); +IMPORT_C WEAKDECL _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *, + _Unwind_Context *context); +IMPORT_C WEAKDECL _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(_Unwind_State state, _Unwind_Control_Block *, + _Unwind_Context *context); + + +/* Various image symbols: */ + +struct ExceptionTableInfo { + uint32_t EIT_base; + uint32_t EIT_limit; +}; + +#ifndef __EPOC32__ +/* We define __ARM_ETInfo to allow access to some linker-generated + names that are not legal C identifiers. __ARM_ETInfo is extern only + because of scope limitations of the embedded assembler */ +extern const struct ExceptionTableInfo __ARM_ETInfo; +#define EIT_base \ + ((const __EIT_entry *)(__ARM_ETInfo.EIT_base + (const char *)&__ARM_ETInfo)) +#define EIT_limit \ + ((const __EIT_entry *)(__ARM_ETInfo.EIT_limit + (const char *)&__ARM_ETInfo)) + +#endif + + +/* ----- Index table processing ----- */ + +/* find_and_expand_eit_entry is a support function used in both phases to set + * ucb.pr_cache and internal cache. + * Call with a pointer to the ucb and the return address to look up. + * + * The table is contained in the half-open interval + * [EIT_base, EIT_limit) and is an ordered array of __EIT_entrys. + * Perform a binary search via C library routine bsearch. + * The table contains only function start addresses (encoded as offsets), so + * we need to special-case the end table entry in the comparison function, + * which we do by assuming the function it describes extends to end of memory. + * This causes us problems indirectly in that we would like to fault as + * many attempts as possible to look up an invalid return address. There are + * several ways an invalid return address can be obtained from a broken + * program, such as someone corrupting the stack or broken unwind instructions + * recovered the wrong value. It is plausible that many bad return addresses + * will be either small integers or will point into the heap or stack, hence + * it's desirable to get the length of that final function roughly right. + * Here we make no attempt to do it. Code exclusively for use in toolchains + * which define a suitable limit symbol could make use of that symbol. + * Alternatively (QoI) a smart linker could augment the index table with a + * dummy EXIDX_CANTUNWIND entry pointing just past the last real function. + */ + +#ifndef __EPOC32__ +static int EIT_comparator(const void *ck, const void *ce) +{ + uint32_t return_address = *(const uint32_t *)ck; + const __EIT_entry *eitp = (const __EIT_entry *)ce; + const __EIT_entry *next_eitp = eitp + 1; + uint32_t next_fn; + if (next_eitp != EIT_limit) + next_fn = __ARM_resolve_prel31((void *)&next_eitp->fnoffset); + else + next_fn = 0xffffffffU; + if (return_address < __ARM_resolve_prel31((void *)&eitp->fnoffset)) return -1; + if (return_address >= next_fn) return 1; + return 0; +} +#endif + + +static _Unwind_Reason_Code find_and_expand_eit_entry_V2(_Unwind_Control_Block *ucbp, + uint32_t return_address) +{ + /* Search the index table for an entry containing the specified return + * address. Subtract the 2 from the return address, as the index table + * contains function start addresses (a trailing noreturn BL would + * appear to return to the first address of the next function (perhaps + * +1 if Thumb); a leading BL would appear to return to function start + * + instruction size (perhaps +1 if Thumb)). + */ + +#ifndef __EPOC32__ + const __EIT_entry *base = EIT_base; + size_t nelems = EIT_limit - EIT_base; + __EIT_entry *eitp; + + return_address -= 2; + + eitp = (__EIT_entry *) bsearch(&return_address, base, nelems, + sizeof(__EIT_entry), EIT_comparator); +#else + const __EIT_entry *base = EIT_base(ucbp); + size_t nelems = EIT_limit(ucbp) - base; + __EIT_entry *eitp; + + return_address -= 2; + + // This must succeed on SymbianOS or else an error will have occured already. + eitp = SearchEITV2(return_address, base, nelems); +#endif + + if (eitp == NULL) { + /* The return address we have was not found in the EIT. + * This breaks the scan and we have to indicate failure. + */ + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_UNWINDER_LOOKUPFAILED); + return _URC_FAILURE; + } + + /* Cache the function offset */ + + ucbp->pr_cache.fnstart = __ARM_resolve_prel31((void *)&eitp->fnoffset); + + /* Can this frame be unwound at all? */ + + if (eitp->content == EXIDX_CANTUNWIND) { + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_NOUNWIND); + return _URC_FAILURE; + } + + /* 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 content field is a 31-bit place-relative offset to an _Unwind_EHT_Entry structure */ + ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)__ARM_resolve_prel31((void *)&eitp->content); + ucbp->pr_cache.additional = 0; + } + + /* Discover the personality routine address */ + + if (*(uint32_t *)(ucbp->pr_cache.ehtp) & uint32_highbit) { + /* It is immediate data - compute matching pr */ + uint32_t idx = ((*(uint32_t *)(ucbp->pr_cache.ehtp)) >> 24) & 0xf; + if (idx == 0) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr0; + else if (idx == 1) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr1; + else if (idx == 2) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr2; + else { /* Failed */ + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_TABLECORRUPT); + return _URC_FAILURE; + } + } else { + /* It's a place-relative offset to pr */ + ucbp->PR_ADDR = __ARM_resolve_prel31((void *)(ucbp->pr_cache.ehtp)); + } + return _URC_OK; +} + +static _Unwind_Reason_Code find_and_expand_eit_entry_V1(_Unwind_Control_Block *ucbp, + uint32_t return_address) +{ + /* Search the index table for an entry containing the specified return + * address. The EIT contains function offsets relative to the base of the + * execute region so adjust the return address accordingly. + */ + +#ifndef __EPOC32__ + uint32_t return_address_offset = ADDR_TO_ER_RO_OFFSET(return_address, ucbp); + const __EIT_entry *base = EIT_base; + size_t nelems = EIT_limit - EIT_base; + + const __EIT_entry *eitp = + (const __EIT_entry *) bsearch(&return_address_offset, base, nelems, + sizeof(__EIT_entry), EIT_comparator); + if (eitp == NULL) { + /* The return address we have was not found in the EIT. + * This breaks the scan and we have to indicate failure. + */ + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_UNWINDER_LOOKUPFAILED); + return _URC_FAILURE; + } +#else + /* Shouldn't we subtract 2 from here just like in the V2 lookup? + */ + uint32_t return_address_offset = ADDR_TO_ER_RO_OFFSET(return_address, ucbp); + const __EIT_entry *base = EIT_base(ucbp); + size_t nelems = EIT_limit(ucbp) - base; + + // This must succeed or else an error will have occured already. + const __EIT_entry *eitp = SearchEITV1(return_address_offset, base, nelems); + +#endif + + + /* Cache the function offset */ + + ucbp->pr_cache.fnstart = ER_RO_OFFSET_TO_ADDR(eitp->fnoffset, ucbp); + + /* Can this frame be unwound at all? */ + + if (eitp->content == EXIDX_CANTUNWIND) { + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_NOUNWIND); + return _URC_FAILURE; + } + + /* 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 content field is a segment relative offset to an _Unwind_EHT_Entry structure */ + ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)ER_RO_OFFSET_TO_ADDR(eitp->content, ucbp); + ucbp->pr_cache.additional = 0; + } + + /* Discover the personality routine address */ + + if (*(uint32_t *)(ucbp->pr_cache.ehtp) & uint32_highbit) { + /* It is immediate data - compute matching pr */ + uint32_t idx = ((*(uint32_t *)(ucbp->pr_cache.ehtp)) >> 24) & 0xf; + + if (idx == 0) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr0; + else if (idx == 1) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr1; + else if (idx == 2) ucbp->PR_ADDR = (uint32_t)&__aeabi_unwind_cpp_pr2; + else { /* Failed */ + ucbp->PR_ADDR = NULL; + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_TABLECORRUPT); + return _URC_FAILURE; + } + } else { + /* Execute region offset to PR */ + ucbp->PR_ADDR = ER_RO_OFFSET_TO_ADDR(*(uint32_t *)(ucbp->pr_cache.ehtp), ucbp); + + } + return _URC_OK; +} + +static _Unwind_Reason_Code find_and_expand_eit_entry(_Unwind_Control_Block *ucbp, + uint32_t return_address) +{ + ValidateExceptionDescriptor(return_address, ucbp); + if (EHABI_V2(ucbp)) + return find_and_expand_eit_entry_V2(ucbp, return_address); + else + return find_and_expand_eit_entry_V1(ucbp, return_address); +} + + +/* ----- Unwinding: ----- */ + +/* Fwd decl */ +static NORETURNDECL void unwind_next_frame(_Unwind_Control_Block *ucbp, phase2_virtual_register_set *vrsp); + +/* Helper fn: If the demand_save flag in a phase1_virtual_register_set was + * zeroed, the registers were demand-saved. This function restores from + * the save area. +*/ +static FORCEINLINE void restore_non_core_regs(phase1_virtual_register_set *vrsp) +{ + if (vrsp->demand_save_vfp_low == 0) + __ARM_Unwind_VRS_VFPrestore_low(&vrsp->vfp.d[0]); + if (vrsp->demand_save_vfp_high == 0) + __ARM_Unwind_VRS_VFPrestore_high(&vrsp->vfp.d[16]); +} + +/* _Unwind_RaiseException is the external entry point to begin unwinding */ +__asm _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Control_Block *ucbp) +{ + extern __ARM_Unwind_RaiseException; + +#if THUMBNAIL + + /* Create a phase2_virtual_register_set on the stack */ + /* Save the core registers, carefully writing the original sp value */ + /* Note we account for the pc but do not actually write it's value here */ + str.w r14,[sp, #-8]!; + add.w r14, r13, #8; + str.w r14,[sp, #-4]! /* pushed 3 words => 3 words */ + stmfd.w sp!,{r0-r12}; /* pushed 13 words => 16 words */ + /* Write zeroes for the demand_save bytes so no saving occurs in phase 2 */ + mov.w r1,#0; + str.w r1,[sp,#-4]!; /* pushed 1 word => 17 words */ + mov.w r1,sp; + sub.w sp,sp,#4; /* preserve 8 byte alignment => 18 words */ + + /* Now pass to C (with r0 still valid) to do the real work. + * r0 = ucbp, r1 = phase2_virtual_register_set. + * If we get control back, pop the stack and return preserving r0. + */ + + /* on arch 5T and later the linker will fix 'bl' => 'blx' as + needed */ + bl.w __ARM_Unwind_RaiseException; + ldr.w r14,[sp,#16*4]; + add.w sp,sp,#18*4; + bx lr; + +#else + + MAYBE_SWITCH_TO_ARM_STATE; + + /* Create a phase2_virtual_register_set on the stack */ + /* Save the core registers, carefully writing the original sp value */ + #if __ARMCC_VERSION < 300000 + stmfd sp!,{r13-r15}; /* pushed 3 words => 3 words */ + #else + stmdb r13, {r14,r15}; + str r13, [r13,#-3*4]; + sub r13, r13, #3*4; + #endif + stmfd sp!,{r0-r12}; /* pushed 13 words => 16 words */ + /* Write zeroes for the demand_save bytes so no saving occurs in phase 2 */ + mov r1,#0; + str r1,[sp,#-4]!; /* pushed 1 word => 17 words */ + mov r1,sp; + sub sp,sp,#4; /* preserve 8 byte alignment => 18 words */ + + /* Now pass to C (with r0 still valid) to do the real work. + * r0 = ucbp, r1 = phase2_virtual_register_set. + * If we get control back, pop the stack and return preserving r0. + */ + +#if OLD_STYLE_INTERWORKING + ldr r2,Unwind_RaiseException_Offset; + add r2,r2,pc; + mov lr,pc; +Offset_Base + bx r2; +#else + /* on arch 5T and later the linker will fix 'bl' => 'blx' as + needed */ + bl __ARM_Unwind_RaiseException; +#endif + ldr r14,[sp,#16*4]; + add sp,sp,#18*4; + RET_LR; +#if OLD_STYLE_INTERWORKING +Unwind_RaiseException_Offset dcd __ARM_Unwind_RaiseException - Offset_Base; +#endif + MAYBE_CODE16; + +#endif + +#ifndef __EPOC32__ + /* Alternate symbol names for difficult symbols. + * It is possible no functions included in the image require + * a handler table. Therefore make only a weak reference to + * the handler table base symbol, which may be absent. + */ + align 4 + extern |.ARM.exidx$$Base|; + extern |.ARM.exidx$$Limit|; + extern |.ARM.extab$$Base| WEAKASMDECL; + export __ARM_ETInfo; + /* these are offsets for /ropi */ +__ARM_ETInfo /* layout must match struct ExceptionTableInfo */ +eit_base dcd |.ARM.exidx$$Base| - __ARM_ETInfo; /* index table base */ +eit_limit dcd |.ARM.exidx$$Limit| - __ARM_ETInfo; /* index table limit */ +#endif +} + + +/* __ARM_Unwind_RaiseException performs phase 1 unwinding */ + +_Unwind_Reason_Code __ARM_Unwind_RaiseException(_Unwind_Control_Block *ucbp, + phase2_virtual_register_set *entry_VRSp) +{ + phase1_virtual_register_set phase1_VRS; + + /* Is this a nested simultaneous propagation? + * (see comments with _Unwind_Complete) + */ + if (ucbp->NESTED_CONTEXT == 0) { + /* No - this is only propagation */ + ucbp->NESTED_CONTEXT = 1; + } else { +#ifdef SUPPORT_NESTED_EXCEPTIONS + /* Yes - cache the state elsewhere and restore it when the propagation ends */ + /* This representation wastes space and uses malloc; do better? + * On the other hand will it ever be used in practice? + */ + _Unwind_Control_Block *saved_ucbp = AllocSavedUCB(); + if (ucbp == NULL) { + DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_UNWINDER, _UAACT_ENDING, _UAARG_ENDING_UNWINDER_BUFFERFAILED); + return _URC_FAILURE; + } + saved_ucbp->unwinder_cache = ucbp->unwinder_cache; + saved_ucbp->barrier_cache = ucbp->barrier_cache; + saved_ucbp->cleanup_cache = ucbp->cleanup_cache; + ucbp->NESTED_CONTEXT = (uint32_t)saved_ucbp; +#else + abort(); +#endif + } + + /* entry_VRSp contains the core registers as they were when + * _Unwind_RaiseException was called. Copy the call-site address to r15 + * then copy all the registers to phase1_VRS for the phase 1 stack scan. + */ + + entry_VRSp->core.r[15] = entry_VRSp->core.r[14]; + phase1_VRS.core = entry_VRSp->core; + + /* For phase 1 only ensure non-core registers are saved before use. + * If WMMX registers are supported, initialise their flags here and + * take appropriate action elsewhere. + */ + + phase1_VRS.demand_save_vfp_low = 1; + phase1_VRS.demand_save_vfp_high = 1; +#ifdef __EPOC32__ + /* Set up Symbian specific caches in the _Unwind_Control_Block's + unwinder_cache. + */ + InitialiseSymbianSpecificUnwinderCache(phase1_VRS.core.r[15], ucbp); +#endif + + + /* Now perform a virtual unwind until a propagation barrier is met, or + * until something goes wrong. If something does go wrong, we ought (I + * suppose) to restore registers we may have destroyed. + */ + + while (1) { + + _Unwind_Reason_Code pr_result; + + /* Search the index table for the required entry. Cache the index table + * pointer, and obtain and cache the addresses of the "real" __EHT_Header + * word and the personality routine. + */ + + if (find_and_expand_eit_entry(ucbp, phase1_VRS.core.r[15]) != _URC_OK) { + restore_non_core_regs(&phase1_VRS); + /* Debugger bottleneck fn called during lookup */ + return _URC_FAILURE; + } + + /* Call the pr to decide what to do */ + + pr_result = ((personality_routine)ucbp->PR_ADDR)(_US_VIRTUAL_UNWIND_FRAME, + ucbp, + (_Unwind_Context *)&phase1_VRS); + + if (pr_result == _URC_HANDLER_FOUND) break; + if (pr_result == _URC_CONTINUE_UNWIND) continue; + + /* If we get here some sort of failure has occurred in the + * pr and probably the pr returned _URC_FAILURE + */ + restore_non_core_regs(&phase1_VRS); + return _URC_FAILURE; + } + + /* Propagation barrier located... restore entry register state of non-core regs */ + + restore_non_core_regs(&phase1_VRS); + + /* Initiate real unwinding */ + unwind_next_frame(ucbp, entry_VRSp); + /* Unreached, but keep compiler quiet: */ + return _URC_FAILURE; +} + + +/* unwind_next_frame performs phase 2 unwinding */ + +static NORETURNDECL void unwind_next_frame(_Unwind_Control_Block *ucbp, phase2_virtual_register_set *vrsp) +{ + while (1) { + + _Unwind_Reason_Code pr_result; + + /* Search the index table for the required entry. Cache the index table + * pointer, and obtain and cache the addresses of the "real" __EHT_Header + * word and the personality routine. + */ + + if (find_and_expand_eit_entry(ucbp, vrsp->core.r[15]) != _URC_OK) + abort(); + + /* Save the call-site address and call the pr to do whatever it + * wants to do on this new frame. + */ + + ucbp->SAVED_CALLSITE_ADDR = vrsp->core.r[15]; + pr_result = ((personality_routine)ucbp->PR_ADDR)(_US_UNWIND_FRAME_STARTING, ucbp, + (_Unwind_Context *)vrsp); + + if (pr_result == _URC_INSTALL_CONTEXT) { + /* Upload the registers */ + __ARM_Unwind_VRS_corerestore(&vrsp->core); + } else if (pr_result == _URC_CONTINUE_UNWIND) + continue; + else + abort(); + } +} + + +/* _Unwind_Resume is the external entry point called after a cleanup + * to resume unwinding. It tail-calls a helper function, + * __ARM_Unwind_Resume, which never returns. + */ +__asm NORETURNDECL void _Unwind_Resume(_Unwind_Control_Block *ucbp) +{ + extern __ARM_Unwind_Resume; + +#if THUMBNAIL + + /* Create a phase2_virtual_register_set on the stack */ + /* Save the core registers, carefully writing the original sp value */ + /* Note we account for the pc but do not actually write it's value here */ + str.w r14,[sp, #-8]!; + add.w r14, r13, #8; + str.w r14,[sp, #-4]! /* pushed 3 words => 3 words */ + stmfd.w sp!,{r0-r12}; /* pushed 13 words => 16 words */ + /* Write zeroes for the demand_save bytes so no saving occurs in phase 2 */ + mov.w r1,#0; + str.w r1,[sp,#-4]!; /* pushed 1 word => 17 words */ + mov.w r1,sp; + sub.w sp,sp,#4; /* preserve 8 byte alignment => 18 words */ + + /* Now pass to C (with r0 still valid) to do the real work. + * r0 = ucbp, r1 = phase2_virtual_register_set. + * This call never returns. + */ + + mov pc,r2 + +#else + + MAYBE_SWITCH_TO_ARM_STATE; + + /* Create a phase2_virtual_register_set on the stack */ + /* Save the core registers, carefully writing the original sp value */ + + #if __ARMCC_VERSION < 300000 + stmfd sp!,{r13-r15}; /* pushed 3 words => 3 words */ + #else + stmdb r13, {r14,r15}; + str r13, [r13,#-3*4]; + sub r13, r13, #3*4; + #endif + + stmfd sp!,{r0-r12}; /* pushed 13 words => 16 words */ + /* Write zeroes for the demand_save bytes so no saving occurs in phase 2 */ + mov r1,#0; + str r1,[sp,#-4]!; /* pushed 1 word => 17 words */ + mov r1,sp; + sub sp,sp,#4; /* preserve 8 byte alignment => 18 words */ + + /* Now pass to C (with r0 still valid) to do the real work. + * r0 = ucbp, r1 = phase2_virtual_register_set. + * This call never returns. + */ + +#ifdef __APCS_INTERWORK + ldr r2,Unwind_Resume_Offset; + add r2,r2,pc; + bx r2; +Unwind_Resume_Offset dcd __ARM_Unwind_Resume - .; +#else + b __ARM_Unwind_Resume; +#endif + MAYBE_CODE16; + +#endif +} + + +/* Helper function for _Unwind_Resume */ + +NORETURNDECL void __ARM_Unwind_Resume(_Unwind_Control_Block *ucbp, + phase2_virtual_register_set *entry_VRSp) +{ + _Unwind_Reason_Code pr_result; + + /* Recover saved state */ + + entry_VRSp->core.r[15] = ucbp->SAVED_CALLSITE_ADDR; + + /* Call the cached PR and dispatch */ + + pr_result = ((personality_routine)ucbp->PR_ADDR)(_US_UNWIND_FRAME_RESUME, ucbp, + (_Unwind_Context *)entry_VRSp); + + if (pr_result == _URC_INSTALL_CONTEXT) { + /* Upload the registers */ + __ARM_Unwind_VRS_corerestore(&entry_VRSp->core); + } else if (pr_result == _URC_CONTINUE_UNWIND) + unwind_next_frame(ucbp, entry_VRSp); + else + abort(); +} + + +/* _Unwind_Complete is called at the end of a propagation. + * If we support multiple simultaneous propagations, restore the cached state + * of the previous propagation here. + */ + +void _Unwind_Complete(_Unwind_Control_Block *ucbp) +{ + _Unwind_Control_Block *context = (_Unwind_Control_Block *)ucbp->NESTED_CONTEXT; + if ((uint32_t)context == 0) abort(); /* should be impossible */ + if ((uint32_t)context == 1) { + /* This was the only ongoing propagation of this object */ + ucbp->NESTED_CONTEXT--; + return; + } +#ifdef SUPPORT_NESTED_EXCEPTIONS + /* Otherwise we copy the state back from the cache structure pointed to + * by ucbp->NESTED_CONTEXT. + */ + /* This first one updates ucbp->NESTED_CONTEXT */ + ucbp->unwinder_cache = context->unwinder_cache; + ucbp->barrier_cache = context->barrier_cache; + ucbp->cleanup_cache = context->cleanup_cache; + FreeSavedUCB(context); +#else + abort(); +#endif +} + +/* _Unwind_DeleteException can be used to invoke the exception_cleanup + * function after catching a foreign exception. + */ + +void _Unwind_DeleteException(_Unwind_Control_Block *ucbp) +{ + if (ucbp->exception_cleanup != NULL) + (ucbp->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, ucbp); +} + +#endif /* unwinder_c */ +#ifdef unwind_activity_c + +/* Runtime debug "bottleneck function": */ +/* (not in the current Exceptions EABI document) */ + +void _Unwind_Activity(_Unwind_Control_Block *ucbp, uint32_t reason, uint32_t arg) +{ +#ifdef UNWIND_ACTIVITY_DIAGNOSTICS + uint32_t who = reason >> 24; + uint32_t activity = reason & 0xffffff; + printf("_Unwind_Activity: UCB=0x%8.8x Reason=(", (uint32_t)ucbp); + switch (who) { + case _UASUBSYS_UNWINDER: + printf("unw,"); + if (activity >= 0x80) + printf("%x) Arg=0x%8.8x\n", activity, arg); + break; + case _UASUBSYS_CPP: + printf("C++,"); + if (activity >= 0x80) { + if (activity == _UAACT_CPP_TYPEINFO) + printf("typeinfo) Typeinfo=0x%8.8x\n", arg); + else + printf("%x) Arg=0x%8.8x\n", activity, arg); + } + break; + default: + printf("???,"); + if (activity >= 0x80) + printf("%x) Arg=0x%8.8x\n", activity, arg); + break; + } + if (activity < 0x80) { + switch (activity) { + case _UAACT_STARTING: + printf("starting) Typeinfo=0x%8.8x\n", arg); + break; + case _UAACT_ENDING: + printf("ending) Cause=%d\n", arg); + break; + case _UAACT_BARRIERFOUND: + printf("barrierfound) Pad=0x%8.8x\n", arg); + break; + case _UAACT_PADENTRY: + printf("padentry) Pad=0x%8.8x\n", arg); + break; + default: + printf("%x) Arg=0x%8.8x\n", activity, arg); + break; + } + } +#endif +} + +#endif /* unwind_activity_c */