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