sl@0: /* unwinder.c sl@0: * sl@0: * Copyright 2002-2003 ARM Limited. sl@0: */ sl@0: /* sl@0: Licence sl@0: sl@0: 1. Subject to the provisions of clause 2, ARM hereby grants to LICENSEE a sl@0: perpetual, non-exclusive, nontransferable, royalty free, worldwide licence sl@0: to use this Example Implementation of Exception Handling solely for the sl@0: purpose of developing, having developed, manufacturing, having sl@0: manufactured, offering to sell, selling, supplying or otherwise sl@0: distributing products which comply with the Exception Handling ABI for the sl@0: ARM Architecture specification. All other rights are reserved to ARM or its sl@0: licensors. sl@0: sl@0: 2. THIS EXAMPLE IMPLEMENTATION OF EXCEPTION HANDLING IS PROVIDED "AS IS" sl@0: WITH NO WARRANTIES EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED sl@0: TO ANY WARRANTY OF SATISFACTORY QUALITY, MERCHANTABILITY, NONINFRINGEMENT sl@0: OR FITNESS FOR A PARTICULAR PURPOSE. sl@0: */ sl@0: /* sl@0: * RCS $Revision: 1.16 $ sl@0: * Checkin $Date: 2003/10/23 13:57:39 $ sl@0: * Revising $Author: agrant $ 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: #include sl@0: #include sl@0: /* Environment: */ sl@0: #include "unwind_env.h" sl@0: /* Language-independent unwinder declarations: */ sl@0: #include "unwinder.h" 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: extern int printf(const char *, ...); sl@0: #endif sl@0: 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, vfp and fpa 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 vfp[16+1]; }; /* VFP registers saved in FSTMX format */ sl@0: /* Extra 2 words for the format word + unused */ sl@0: struct fpa_reg { uint32_t word[3]; }; sl@0: struct fpa_s { struct fpa_reg fpa[8]; }; /* FPA registers saved in SFM 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; sl@0: bool demand_save_fpa; 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: struct fpa_s fpa; /* demand-saved fpa 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; sl@0: bool demand_save_fpa; 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) || defined(__TARGET_ARCH_5TE) || \ sl@0: defined(__TARGET_ARCH_6) /* || ... */ 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 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 but external ----- */ sl@0: /* Note '%0' refers to local label '0' */ sl@0: sl@0: __asm void __ARM_Unwind_VRS_VFPpreserve(void *vfpp) sl@0: { sl@0: /* Preserve the vfp registers in the passed memory */ sl@0: #ifdef __thumb sl@0: #define MAYBE_SWITCH_TO_ARM_STATE SWITCH_TO_ARM_STATE sl@0: #define MAYBE_CODE16 code16 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: #else sl@0: #define MAYBE_SWITCH_TO_ARM_STATE /* nothing */ sl@0: #define MAYBE_CODE16 /* nothing */ sl@0: #endif sl@0: sl@0: vfp_d0 CN 0; sl@0: MAYBE_SWITCH_TO_ARM_STATE; sl@0: stc p11,vfp_d0,[r0],{0x21}; /* 0xec800b21 FSTMIAX r0,{d0-d15} */ sl@0: RET_LR; sl@0: MAYBE_CODE16; sl@0: } sl@0: sl@0: __asm void __ARM_Unwind_VRS_VFPrestore(void *vfpp) sl@0: { sl@0: /* Restore the 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],{0x21}; /* 0xec900b21 FLDMIAX r0,{d0-d15} */ sl@0: RET_LR; sl@0: MAYBE_CODE16; sl@0: } sl@0: sl@0: __asm void __ARM_Unwind_VRS_FPApreserve(void *vfpp) sl@0: { sl@0: /* Preserve the fpa registers in the passed memory */ sl@0: fpa_f0 CN 0; sl@0: fpa_f4 CN 0; sl@0: MAYBE_SWITCH_TO_ARM_STATE; sl@0: stc p2, fpa_f0, [r0]; /* 0xed800200 SFM f0,4,[r0,#0] */ sl@0: stc p2, fpa_f4, [r0, #48]; /* 0xed80420c SFM f4,4,[r0,#0x30] */ sl@0: RET_LR; sl@0: MAYBE_CODE16; sl@0: } sl@0: sl@0: __asm void __ARM_Unwind_VRS_FPArestore(void *vfpp) sl@0: { sl@0: /* Restore the fpa registers from the passed memory */ sl@0: fpa_f0 CN 0; sl@0: fpa_f4 CN 0; sl@0: MAYBE_SWITCH_TO_ARM_STATE; sl@0: ldc p2, fpa_f0, [r0]; /* 0xed900200 LFM f0,4,[r0,#0] */ sl@0: ldc p2, fpa_f4, [r0, #48]; /* 0xed90020c LFM f4,4,[r0,#0x30] */ sl@0: RET_LR; sl@0: MAYBE_CODE16; sl@0: } sl@0: sl@0: __asm NORETURNDECL void __ARM_Unwind_VRS_corerestore(void *corep) sl@0: { 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: preserve8; sl@0: MAYBE_SWITCH_TO_ARM_STATE; sl@0: #if OLD_STYLE_INTERWORKING sl@0: mov r14, r0; sl@0: ldmia r14!,{r0-r12}; sl@0: ldr r12,[r14, #4*2]; /* pc */ sl@0: ldmia r14,{r13-r14}; sl@0: bx r12; sl@0: #else sl@0: ldmia r0,{r0-r15}; sl@0: #endif sl@0: MAYBE_CODE16; 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(struct vfp_s *vfpp) sl@0: { sl@0: uint64_t *lp = (uint64_t *)vfpp; 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, *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: static void debug_print_vrs_fpa(struct fpa_s *fpap) sl@0: { sl@0: uint32_t *lp = (uint32_t *)fpap; sl@0: int c = 0; sl@0: int i; sl@0: for (i = 0; i < 8; i++) { sl@0: printf("F%-2d 0x%8.8x%8.8x%8.8x ", i, *lp, *(lp+1), *(lp+2)); sl@0: lp+=3; sl@0: if (c++ == 1) { sl@0: c = 0; sl@0: printf("\n"); 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 == 1) sl@0: printf("VFP is not saved\n"); sl@0: else sl@0: debug_print_vrs_vfp(&vrsp->vfp); sl@0: printf("-----\n"); sl@0: if (vrsp->demand_save_fpa == 1) sl@0: printf("FPA is not saved\n"); sl@0: else sl@0: debug_print_vrs_fpa(&vrsp->fpa); sl@0: printf("------------------------------------------------------------------------\n"); sl@0: } sl@0: #endif sl@0: sl@0: sl@0: /* ----- Public routines ----- */ sl@0: sl@0: _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_FPA: 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: _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_FPA: 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: _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: if (representation != _UVRSD_VFPX || start + count > 16) sl@0: return _UVRSR_FAILED; sl@0: if (vrsp->demand_save_vfp == 1) { /* Demand-save over phase 1 */ sl@0: vrsp->demand_save_vfp = 0; sl@0: __ARM_Unwind_VRS_VFPpreserve(&vrsp->vfp); sl@0: } sl@0: /* Now recover from the stack into the real machine registers. sl@0: * Note 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 over that area, and sl@0: * restoring from the whole area. sl@0: */ sl@0: { sl@0: struct vfp_s temp_vfp; sl@0: uint64_t *vsp; sl@0: __ARM_Unwind_VRS_VFPpreserve(&temp_vfp); sl@0: vsp = (uint64_t *)vrsp->core.r[R_SP]; sl@0: while (count--) { sl@0: #ifdef VRS_DIAGNOSTICS sl@0: printf("VRS Pop D%d = 0x%llx\n", start, *vsp); sl@0: #endif sl@0: temp_vfp.vfp[start++] = *vsp++; sl@0: } sl@0: vrsp->core.r[R_SP] = (uint32_t)((uint32_t *)vsp + 1); /* +1 to skip the format word */ sl@0: __ARM_Unwind_VRS_VFPrestore(&temp_vfp); sl@0: } sl@0: return _UVRSR_OK; sl@0: } sl@0: case _UVRSC_FPA: sl@0: { sl@0: uint32_t start = descriminator >> 16; sl@0: uint32_t count = descriminator & 0xffff; sl@0: if (representation != _UVRSD_FPAX || start > 7 || count > 4) sl@0: return _UVRSR_FAILED; sl@0: if (vrsp->demand_save_fpa == 1) { /* Demand-save over phase 1 */ sl@0: vrsp->demand_save_fpa = 0; sl@0: __ARM_Unwind_VRS_FPApreserve(&vrsp->fpa); sl@0: } sl@0: /* Now recover from the stack into the real machine registers. sl@0: * Do this by saving the current FPA registers to a memory area, sl@0: * moving the in-memory values over that area, and sl@0: * restoring from the whole area. sl@0: * Unlike VFP, here the range is allowed to wrap round. sl@0: */ sl@0: { sl@0: struct fpa_s temp_fpa; sl@0: struct fpa_reg *vsp; sl@0: __ARM_Unwind_VRS_FPApreserve(&temp_fpa); sl@0: vsp = (struct fpa_reg *)vrsp->core.r[R_SP]; sl@0: while (count--) { sl@0: #ifdef VRS_DIAGNOSTICS sl@0: printf("VRS Pop F%d = 0x%-8.8x%-8.8x%-8.8x\n", start, *(uint32_t *)vsp, sl@0: *((uint32_t *)vsp + 1), *((uint32_t *)vsp + 2)); sl@0: #endif sl@0: temp_fpa.fpa[start++] = *vsp++; sl@0: start &= 7; sl@0: } sl@0: vrsp->core.r[R_SP] = (uint32_t)vsp; sl@0: __ARM_Unwind_VRS_FPArestore(&temp_fpa); 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 not used. sl@0: * reserved5 is not used. 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: typedef struct __EIT_entry { sl@0: uint32_t fnoffset; /* Relative to base of execution region */ sl@0: uint32_t content; sl@0: } __EIT_entry; sl@0: 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: WEAKDECL _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *, sl@0: _Unwind_Context *context); sl@0: 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: /* 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: sl@0: /* ----- Address manipulation: ----- */ sl@0: sl@0: /* The following helper function is never called and is present simply sl@0: * for ease of packaging. The constant word within is used by sl@0: * ER_RO_offset_to_addr to compute the RO segment base. sl@0: * The zero word named W is relocated relative to the base B of the sl@0: * segment which includes it, hence B is recoverable at runtime by sl@0: * computing &W - W. sl@0: */ sl@0: sl@0: extern const uint32_t __ARM_unwind_ROSegBase_SelfOffset; sl@0: sl@0: __asm void __ARM_unwind_basehelper(void) sl@0: { sl@0: export __ARM_unwind_ROSegBase_SelfOffset; sl@0: R_ARM_ROSEGREL32 EQU 39 sl@0: __ARM_unwind_ROSegBase_SelfOffset; sl@0: dcd 0; sl@0: __RELOC R_ARM_ROSEGREL32,__ARM_unwind_ROSegBase_SelfOffset; sl@0: } sl@0: sl@0: #define ER_RO_SegBase ((uint32_t)&__ARM_unwind_ROSegBase_SelfOffset - \ sl@0: __ARM_unwind_ROSegBase_SelfOffset) sl@0: sl@0: /* And now functions used to convert between segment-relative offsets sl@0: * and absolute addresses. sl@0: */ sl@0: sl@0: static __inline uint32_t addr_to_ER_RO_offset(uint32_t addr) sl@0: { sl@0: return addr - ER_RO_SegBase; sl@0: } sl@0: sl@0: static __inline uint32_t ER_RO_offset_to_addr(uint32_t offset) sl@0: { sl@0: extern const uint32_t __ARM_unwind_ROSegBase_SelfOffset; sl@0: return offset + ER_RO_SegBase; sl@0: } 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: static int EIT_comparator(const void *ck, const void *ce) sl@0: { sl@0: uint32_t return_address_offset = *(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 = next_eitp->fnoffset; sl@0: else sl@0: next_fn = addr_to_ER_RO_offset(0); /* address 0 is 'just past' the end of memory */ sl@0: if (return_address_offset < eitp->fnoffset) return -1; sl@0: if (return_address_offset >= next_fn) return 1; sl@0: return 0; sl@0: } 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: /* 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: uint32_t return_address_offset = addr_to_ER_RO_offset(return_address); 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: 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 = ER_RO_offset_to_addr(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 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); 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: /* Execute region offset to PR */ sl@0: ucbp->PR_ADDR = ER_RO_offset_to_addr(*(uint32_t *)(ucbp->pr_cache.ehtp)); sl@0: } sl@0: return _URC_OK; sl@0: } sl@0: 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 void restore_non_core_regs(phase1_virtual_register_set *vrsp) sl@0: { sl@0: if (vrsp->demand_save_vfp == 0) sl@0: __ARM_Unwind_VRS_VFPrestore(&vrsp->vfp); sl@0: if (vrsp->demand_save_fpa == 0) sl@0: __ARM_Unwind_VRS_FPArestore(&vrsp->fpa); sl@0: } sl@0: sl@0: /* _Unwind_RaiseException is the external entry point to begin unwinding */ sl@0: sl@0: __asm _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Control_Block *ucbp) sl@0: { sl@0: extern __ARM_Unwind_RaiseException; 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: stmfd sp!,{r13-r15}; /* pushed 3 words => 3 words */ 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: /* 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: 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: } 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: /* 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 = sl@0: (_Unwind_Control_Block *)malloc(sizeof(_Unwind_Control_Block)); 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: } 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 = 1; sl@0: phase1_VRS.demand_save_fpa = 1; 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: 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: stmfd sp!,{r13-r15}; /* pushed 3 words => 3 words */ 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: 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: /* 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: free(context); 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 */