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