sl@0: /* unwind_pr.c - ARM-defined model personality routines 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.20 $ sl@0: * Checkin $Date: 2003/11/10 17:13:37 $ sl@0: * Revising $Author: achapman $ sl@0: */ sl@0: 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 PR_DIAGNOSTICS for printed diagnostics from the personality routine */ sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: extern int printf(const char *, ...); sl@0: #endif sl@0: sl@0: sl@0: /* Forward decl: */ sl@0: extern _Unwind_Reason_Code __ARM_unwind_cpp_prcommon(_Unwind_State state, sl@0: _Unwind_Control_Block *ucbp, sl@0: _Unwind_Context *context, sl@0: uint32_t idx); sl@0: sl@0: /* Personality routines - external entry points. sl@0: * pr0: short unwind description, 16 bit EHT offsets. sl@0: * pr1: long unwind description, 16 bit EHT offsets. sl@0: * pr2: long unwind description, 32 bit EHT offsets. sl@0: */ sl@0: sl@0: #ifdef pr0_c sl@0: _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(_Unwind_State state, sl@0: _Unwind_Control_Block *ucbp, sl@0: _Unwind_Context *context) { sl@0: return __ARM_unwind_cpp_prcommon(state, ucbp, context, 0); sl@0: } sl@0: #endif sl@0: sl@0: #ifdef pr1_c sl@0: _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(_Unwind_State state, sl@0: _Unwind_Control_Block *ucbp, sl@0: _Unwind_Context *context) { sl@0: return __ARM_unwind_cpp_prcommon(state, ucbp, context, 1); sl@0: } sl@0: #endif sl@0: sl@0: #ifdef pr2_c sl@0: _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(_Unwind_State state, sl@0: _Unwind_Control_Block *ucbp, sl@0: _Unwind_Context *context) { sl@0: return __ARM_unwind_cpp_prcommon(state, ucbp, context, 2); sl@0: } sl@0: #endif sl@0: sl@0: /* The rest of the file deals with the common routine */ sl@0: sl@0: #ifdef prcommon_c sl@0: sl@0: /* C++ exceptions ABI required here: sl@0: * Declare protocol routines called by the personality routine. sl@0: * These are weak references so that referencing them here is sl@0: * insufficient to pull them into the image - they will only be sl@0: * included if application code uses a __cxa routine. sl@0: */ sl@0: sl@0: typedef unsigned char bool; sl@0: static const bool false = 0; sl@0: static const bool true = !false; sl@0: sl@0: typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ sl@0: sl@0: WEAKDECL void __cxa_call_unexpected(_Unwind_Control_Block *ucbp); sl@0: WEAKDECL bool __cxa_begin_cleanup(_Unwind_Control_Block *ucbp); sl@0: WEAKDECL bool __cxa_type_match(_Unwind_Control_Block *ucbp, sl@0: const type_info *rttip, sl@0: void **matched_object); 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_cpp_ROSegBase_SelfOffset; sl@0: sl@0: __asm void __ARM_unwind_cpp_basehelper(void) sl@0: { sl@0: export __ARM_unwind_cpp_ROSegBase_SelfOffset; sl@0: R_ARM_ROSEGREL32 EQU 39 sl@0: __ARM_unwind_cpp_ROSegBase_SelfOffset; sl@0: dcd 0; sl@0: __RELOC R_ARM_ROSEGREL32,__ARM_unwind_cpp_ROSegBase_SelfOffset; sl@0: } sl@0: sl@0: #define ER_RO_SegBase ((uint32_t)&__ARM_unwind_cpp_ROSegBase_SelfOffset - \ sl@0: __ARM_unwind_cpp_ROSegBase_SelfOffset) sl@0: sl@0: /* And now the function used to convert segment-relative offsets sl@0: * to absolute addresses. sl@0: */ sl@0: sl@0: static __inline uint32_t ER_RO_offset_to_addr(uint32_t offset) sl@0: { sl@0: return offset + ER_RO_SegBase; sl@0: } sl@0: sl@0: sl@0: /* --------- VRS manipulation: --------- */ sl@0: sl@0: #define R_SP 13 sl@0: #define R_LR 14 sl@0: #define R_PC 15 sl@0: sl@0: static __inline uint32_t core_get(_Unwind_Context *context, uint32_t regno) sl@0: { sl@0: uint32_t val; sl@0: /* This call is required to never fail if given a valid regno */ sl@0: _Unwind_VRS_Get(context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val); sl@0: return val; sl@0: } sl@0: sl@0: static __inline void core_set(_Unwind_Context *context, uint32_t regno, uint32_t newval) sl@0: { sl@0: /* This call is required to never fail if given a valid regno */ sl@0: _Unwind_VRS_Set(context, _UVRSC_CORE, regno, _UVRSD_UINT32, &newval); sl@0: } sl@0: sl@0: static __inline uint32_t count_to_mask(uint32_t count) { sl@0: return (1 << count) - 1; sl@0: } sl@0: sl@0: /* --------- Support for unwind instruction stream: --------- */ sl@0: sl@0: #define CODE_FINISH (0xb0) sl@0: sl@0: typedef struct uwdata { sl@0: uint32_t unwind_word; /* current word of unwind description */ sl@0: uint32_t *unwind_word_pointer; /* ptr to next word */ sl@0: uint8_t unwind_word_bytes_remaining; /* count of bytes left in current word */ sl@0: uint8_t unwind_words_remaining; /* count of words left, at ptr onwards */ sl@0: } uwdata; sl@0: sl@0: static __inline uint8_t next_unwind_byte(uwdata *u) { sl@0: uint8_t ub; sl@0: if (u->unwind_word_bytes_remaining == 0) { /* Load another word */ sl@0: if (u->unwind_words_remaining == 0) return CODE_FINISH; /* nothing left - yield NOP */ sl@0: u->unwind_words_remaining--; sl@0: u->unwind_word = *(u->unwind_word_pointer++); sl@0: u->unwind_word_bytes_remaining = 4; sl@0: } sl@0: sl@0: u->unwind_word_bytes_remaining--; sl@0: ub = (u->unwind_word & 0xff000000) >> 24; sl@0: u->unwind_word <<= 8; sl@0: return ub; sl@0: } sl@0: sl@0: sl@0: /* --------- Personality routines: --------- */ sl@0: sl@0: /* The C++ Standard is silent on what is supposed to happen if an internal sl@0: * inconsistency occurs during unwinding. In our design, we return to the sl@0: * caller with _URC_FAILURE. During phase 1 this causes a return from the sl@0: * language-independent unwinder to its caller (__cxa_throw or __cxa_rethrow) sl@0: * which will then call terminate(). If an error occurs during phase 2, the sl@0: * caller will call abort(). sl@0: */ sl@0: sl@0: /* Types to assist with reading EHT's */ sl@0: sl@0: typedef struct { sl@0: uint16_t length; sl@0: uint16_t offset; sl@0: } EHT16; sl@0: sl@0: typedef struct { sl@0: uint32_t length; sl@0: uint32_t offset; sl@0: } EHT32; sl@0: sl@0: typedef uint32_t landingpad_t; sl@0: sl@0: typedef struct { sl@0: landingpad_t landingpad; sl@0: } EHT_cleanup_tail; sl@0: sl@0: typedef struct { sl@0: landingpad_t landingpad; sl@0: uint32_t rtti_offset; sl@0: } EHT_catch_tail; sl@0: sl@0: typedef struct { sl@0: uint32_t rtti_count; /* table count (possibly 0) */ sl@0: uint32_t (rtti_offsets[1]); /* variable length table, possibly followed by landing pad */ sl@0: } EHT_fnspec_tail; sl@0: sl@0: sl@0: /* Macros: */ sl@0: sl@0: /* Barrier cache: */ sl@0: /* Requirement imposed by C++ semantics module - match object in slot 0: */ sl@0: #define BARRIER_HANDLEROBJECT (0) sl@0: /* Requirement imposed by C++ semantics module - function exception spec info */ sl@0: #define BARRIER_FNSPECCOUNT (1) sl@0: #define BARRIER_FNSPECBASE (2) sl@0: #define BARRIER_FNSPECSTRIDE (3) sl@0: #define BARRIER_FNSPECARRAY (4) sl@0: /* Private use for us between phase 1 & 2: */ sl@0: #define BARRIER_EHTP (1) sl@0: sl@0: #define SAVE_PROPAGATION_BARRIER(UCB_PTR,VSP,EHTP,HANDLEROBJECT) \ sl@0: (UCB_PTR)->barrier_cache.sp = (VSP); \ sl@0: (UCB_PTR)->barrier_cache.bitpattern[BARRIER_EHTP] = (uint32_t)(EHTP); \ sl@0: (UCB_PTR)->barrier_cache.bitpattern[BARRIER_HANDLEROBJECT] = (uint32_t)(HANDLEROBJECT); sl@0: sl@0: #define CHECK_FOR_PROPAGATION_BARRIER(UCB_PTR,VSP,EHTP) \ sl@0: ((UCB_PTR)->barrier_cache.sp == (VSP) && \ sl@0: (UCB_PTR)->barrier_cache.bitpattern[BARRIER_EHTP] == (uint32_t)(EHTP)) sl@0: sl@0: sl@0: /* Cleanup cache: We only use one field */ sl@0: #define CLEANUP_EHTP (0) sl@0: sl@0: sl@0: /* Special catch rtti values */ sl@0: #define CATCH_ALL (0xffffffff) sl@0: #define CATCH_ALL_AND_TERMINATE (0xfffffffe) sl@0: sl@0: sl@0: /* Common personality routine: receives pr index as an argument. sl@0: * sl@0: * Note this implementation contains no explicit check against attempting to sl@0: * unwind off the top of the stack. Instead it relies (in cooperation with sl@0: * the language-independent unwinder) on there being a propagation barrier sl@0: * somewhere on the stack, perhaps the caller to main being not sl@0: * unwindable. An alternative would be to check for the stack pointer sl@0: * addressing a stack limit symbol. sl@0: */ sl@0: sl@0: _Unwind_Reason_Code __ARM_unwind_cpp_prcommon(_Unwind_State state, sl@0: _Unwind_Control_Block *ucbp, sl@0: _Unwind_Context *context, sl@0: uint32_t idx) sl@0: { sl@0: _Unwind_EHT_Header *eht_startp; /* EHT start pointer */ sl@0: uint8_t *ehtp; /* EHT pointer, incremented as required */ sl@0: /* Flag for fnspec violations in which the frame should be unwound before calling unexpected() */ sl@0: bool phase2_call_unexpected_after_unwind; sl@0: /* Flag for whether we have loaded r15 (pc) with a return address while executing sl@0: * unwind instructions. sl@0: * Set this on any write to r15 while executing the unwind instructions. sl@0: */ sl@0: bool wrote_pc = false; sl@0: /* Flag for whether we loaded r15 from r14 while executing the unwind instructions */ sl@0: bool wrote_pc_from_lr = false; sl@0: uwdata ud; sl@0: sl@0: /* Mark all as well and extract the EHT pointer */ sl@0: sl@0: eht_startp = ucbp->pr_cache.ehtp; sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR entered: state=%d, r15=0x%x, fnstart=0x%x\n", sl@0: state, core_get(context, R_PC), ucbp->pr_cache.fnstart); sl@0: #endif sl@0: sl@0: /* What are we supposed to do? */ sl@0: sl@0: if (state != _US_VIRTUAL_UNWIND_FRAME && sl@0: state != _US_UNWIND_FRAME_STARTING && sl@0: state != _US_UNWIND_FRAME_RESUME) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_UNSPECIFIED); sl@0: return _URC_FAILURE; sl@0: } sl@0: sl@0: phase2_call_unexpected_after_unwind = false; sl@0: sl@0: /* Traverse the current EHT, if there is one. sl@0: * The required behaviours are: sl@0: * _US_VIRTUAL_UNWIND_FRAME: search for a propagation barrier in this frame. sl@0: * otherwise look for the propagation barrier we found in phase 1, sl@0: * performing cleanups on the way. In this case if state will be one of: sl@0: * _US_UNWIND_FRAME_STARTING first time with this frame sl@0: * _US_UNWIND_FRAME_RESUME not first time, we are part-way through the EHT. sl@0: */ sl@0: sl@0: if ((ucbp->pr_cache.additional & 1) == 0) { /* EHT inline in index table? */ sl@0: /* No: thus there is a real EHT */ sl@0: sl@0: if (state == _US_UNWIND_FRAME_RESUME) { sl@0: /* Recover saved pointer to next EHT entry */ sl@0: ehtp = (uint8_t *)ucbp->cleanup_cache.bitpattern[CLEANUP_EHTP]; sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR EHT recovered pointer 0x%x\n", (int)ehtp); sl@0: #endif sl@0: } else { sl@0: /* Point at the first EHT entry. sl@0: * For pr0, the unwind description is entirely within the header word. sl@0: * For pr1 & pr2, an unwind description extension word count is sl@0: * held in bits 16-23 of the header word. sl@0: */ sl@0: uint32_t unwind_extension_word_count = (idx == 0 ? 0 : ((*eht_startp) >> 16) & 0xff); sl@0: ehtp = (uint8_t *)(eht_startp + 1 + unwind_extension_word_count); sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR EHT first entry at 0x%x\n", (int)ehtp); sl@0: #endif sl@0: } sl@0: sl@0: /* scan ... */ sl@0: sl@0: while (1) { sl@0: sl@0: /* Extract 32 bit length and offset */ sl@0: uint32_t length; sl@0: uint32_t offset; sl@0: if (idx == 2) { sl@0: /* 32 bit offsets */ sl@0: length = ((EHT32 *)ehtp)->length; sl@0: if (length == 0) break; /* end of table */ sl@0: offset = ((EHT32 *)ehtp)->offset; sl@0: ehtp += sizeof(EHT32); sl@0: } else { sl@0: /* 16 bit offsets */ sl@0: length = ((EHT16 *)ehtp)->length; sl@0: if (length == 0) break; /* end of table */ sl@0: offset = ((EHT16 *)ehtp)->offset; sl@0: ehtp += sizeof(EHT16); sl@0: } sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got entry at 0x%x code=%d, length=0x%x, offset=0x%x\n", sl@0: (int)(ehtp-4), ((offset & 1) << 1) | (length & 1), sl@0: length & ~1, offset & ~1); sl@0: #endif sl@0: sl@0: /* Dispatch on the kind of entry */ sl@0: switch (((offset & 1) << 1) | (length & 1)) { sl@0: case 0: /* cleanup */ sl@0: if (state == _US_VIRTUAL_UNWIND_FRAME) { sl@0: /* Not a propagation barrier - skip */ sl@0: } else { sl@0: /* Phase 2: call the cleanup if the return address is in range */ sl@0: uint32_t padaddress; sl@0: uint32_t rangestartaddr = ucbp->pr_cache.fnstart + offset; sl@0: uint32_t rtn_addr = core_get(context, R_PC); sl@0: if (rangestartaddr <= rtn_addr && rtn_addr < rangestartaddr + length) { sl@0: /* It is in range. */ sl@0: landingpad_t landingpad = ((EHT_cleanup_tail *)ehtp)->landingpad; sl@0: ehtp += sizeof(EHT_cleanup_tail); sl@0: /* Dump state into the ECO so we resume correctly after the cleanup. */ sl@0: /* We simply save the address of the next EHT entry. */ sl@0: ucbp->cleanup_cache.bitpattern[CLEANUP_EHTP] = (uint32_t)ehtp; sl@0: if (!__cxa_begin_cleanup(ucbp)) { sl@0: /* Should be impossible, using ARM's library */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_UNSPECIFIED); sl@0: return _URC_FAILURE; sl@0: } sl@0: /* Set up the VRS to enter the landing pad. */ sl@0: padaddress = ER_RO_offset_to_addr(landingpad); sl@0: core_set(context, R_PC, padaddress); sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got cleanup in range, cleanup addr=0x%x\n", core_get(context, R_PC)); sl@0: printf("PR Saving EHT pointer 0x%x\n", (int)ehtp); sl@0: #endif sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_PADENTRY, padaddress); sl@0: /* Exit requesting upload the VRS to the real machine. */ sl@0: return _URC_INSTALL_CONTEXT; sl@0: } sl@0: } sl@0: /* Phase 1, or phase 2 and not in range */ sl@0: ehtp += sizeof(EHT_cleanup_tail); sl@0: break; sl@0: case 1: /* catch */ sl@0: { sl@0: if (state == _US_VIRTUAL_UNWIND_FRAME) { sl@0: /* In range, and with a matching type? */ sl@0: uint32_t rangestartaddr = ucbp->pr_cache.fnstart + offset; sl@0: uint32_t rtn_addr = core_get(context, R_PC); sl@0: void *matched_object; sl@0: length -= 1; /* length had low bit set - clear it */ sl@0: if (rangestartaddr <= rtn_addr && rtn_addr < rangestartaddr + length) { sl@0: /* In range */ sl@0: uint32_t rtti_val = ((EHT_catch_tail *)ehtp)->rtti_offset; sl@0: if (rtti_val == CATCH_ALL_AND_TERMINATE) { sl@0: /* Always matches and causes propagation failure in phase 1 */ sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got CATCH_ALL_AND_TERMINATE in phase 1\n"); sl@0: #endif sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_NOUNWIND); sl@0: return _URC_FAILURE; sl@0: } sl@0: if ((rtti_val == CATCH_ALL && ((matched_object = ucbp + 1),1)) || sl@0: __cxa_type_match(ucbp, sl@0: (type_info *)(rtti_val = ER_RO_offset_to_addr(rtti_val)), sl@0: &matched_object)) { sl@0: /* In range and matches. sl@0: * Record the propagation barrier details for ease of detection in phase 2. sl@0: * We save a pointer to the middle of the handler entry - sl@0: * this is fine, so long as we are consistent about it. sl@0: */ sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got barrier in phase 1\n"); sl@0: printf("PR Matched object address 0x%8.8x\n", matched_object); sl@0: #endif sl@0: SAVE_PROPAGATION_BARRIER(ucbp, core_get(context, R_SP), ehtp, matched_object); sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_BARRIERFOUND, sl@0: ER_RO_offset_to_addr(((EHT_catch_tail *)ehtp)->landingpad)); sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_CPP_TYPEINFO, rtti_val); sl@0: return _URC_HANDLER_FOUND; sl@0: } sl@0: } sl@0: /* Not in range or no type match - fall thru to carry on scanning the table */ sl@0: } else { sl@0: /* Else this is phase 2: have we encountered the saved barrier? */ sl@0: if (CHECK_FOR_PROPAGATION_BARRIER(ucbp, core_get(context, R_SP), ehtp)) { sl@0: /* Yes we have. sl@0: * Set up the VRS to enter the landing pad, sl@0: * and upload the VRS to the real machine. sl@0: */ sl@0: landingpad_t landingpad; sl@0: uint32_t padaddress; sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got catch barrier in phase 2\n"); sl@0: #endif sl@0: landingpad = ((EHT_catch_tail *)ehtp)->landingpad; sl@0: padaddress = ER_RO_offset_to_addr(landingpad); sl@0: core_set(context, R_PC, padaddress); sl@0: core_set(context, 0, (uint32_t)ucbp); sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_PADENTRY, padaddress); sl@0: /* Exit requesting upload the VRS to the real machine. */ sl@0: return _URC_INSTALL_CONTEXT; sl@0: } sl@0: } sl@0: /* Else carry on scanning the table */ sl@0: ehtp += sizeof(EHT_catch_tail); sl@0: break; sl@0: } sl@0: case 2: /* function exception specification (fnspec) */ sl@0: { sl@0: uint32_t counter_word = ((EHT_fnspec_tail *)ehtp)->rtti_count; sl@0: uint32_t rtti_count = counter_word & 0x7fffffff; /* Extract offset count */ sl@0: if (state == _US_VIRTUAL_UNWIND_FRAME) { sl@0: /* Phase 1 */ sl@0: /* In range? Offset had low bit set - clear it */ sl@0: uint32_t rangestartaddr = ucbp->pr_cache.fnstart + offset - 1; sl@0: uint32_t rtn_addr = core_get(context, R_PC); sl@0: if (rangestartaddr <= rtn_addr && rtn_addr < rangestartaddr + length) { sl@0: /* See if any type matches */ sl@0: uint32_t *rttipp = &((EHT_fnspec_tail *)ehtp)->rtti_offsets[0]; sl@0: uint32_t i; sl@0: for (i = 0; i < rtti_count; i++) { sl@0: void *matched_object; sl@0: if (__cxa_type_match(ucbp, sl@0: (type_info *)ER_RO_offset_to_addr(*rttipp), sl@0: &matched_object)) { sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Fnspec matched in phase 1\n"); sl@0: #endif sl@0: break; sl@0: } sl@0: rttipp++; sl@0: } sl@0: sl@0: if (i == rtti_count) { /* NB case rtti_count==0 forces no match [for throw()] */ sl@0: /* No match - fnspec violation is a propagation barrier */ sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got fnspec barrier in phase 1\n"); sl@0: #endif sl@0: SAVE_PROPAGATION_BARRIER(ucbp, core_get(context, R_SP), ehtp, 0); /* save ptr to the count of types */ sl@0: /* Even if this is a fnspec with a landing pad, we always end up in sl@0: * __cxa_call_unexpected so tell the debugger thats where we're going sl@0: */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_BARRIERFOUND, &__cxa_call_unexpected); sl@0: return _URC_HANDLER_FOUND; sl@0: } sl@0: } /* if (in range...) */ sl@0: sl@0: /* Fall out of the 'if' to continue table scanning */ sl@0: sl@0: } else { sl@0: /* Else this is phase 2: have we encountered the saved barrier? */ sl@0: if (CHECK_FOR_PROPAGATION_BARRIER(ucbp, core_get(context, R_SP), ehtp)) { sl@0: /* Yes we have. Fill in the UCB barrier_cache for entry to __cxa_call_unexpected */ sl@0: uint32_t *p = (uint32_t *)ehtp; /* ptr to rtti count */ sl@0: ucbp->barrier_cache.bitpattern[BARRIER_FNSPECCOUNT] = rtti_count; sl@0: ucbp->barrier_cache.bitpattern[BARRIER_FNSPECBASE] = ER_RO_offset_to_addr(0); /* base address */ sl@0: ucbp->barrier_cache.bitpattern[BARRIER_FNSPECSTRIDE] = 4; /* stride */ sl@0: ucbp->barrier_cache.bitpattern[BARRIER_FNSPECARRAY] = (uint32_t)(p + 1); /* address of rtti offset list */ sl@0: sl@0: /* If this is a fnspec with an attached landing pad, we must enter sl@0: * the pad immediately. Otherwise we need to unwind the frame before sl@0: * calling __cxa_call_unexpected() so set a flag to make this happen. sl@0: */ sl@0: if (counter_word == rtti_count) sl@0: phase2_call_unexpected_after_unwind = true; /* no pad, enter later */ sl@0: else { /* pad */ sl@0: landingpad_t landingpad; sl@0: uint32_t padaddress; sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got fnspec barrier in phase 2 (immediate entry)\n"); sl@0: #endif sl@0: ehtp += (sizeof(((EHT_fnspec_tail *)ehtp)->rtti_count) + sl@0: sizeof(uint32_t) * rtti_count); /* point at pad offset */ sl@0: landingpad = *(landingpad_t *)ehtp; sl@0: padaddress = ER_RO_offset_to_addr(landingpad); sl@0: core_set(context, 0, (uint32_t)ucbp); sl@0: core_set(context, R_PC, padaddress); sl@0: /* Even if this is a fnspec with a landing pad, in phase 1 we said we'd sl@0: * end up in __cxa_call_unexpected so show the same thing now sl@0: */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_PADENTRY, &__cxa_call_unexpected); sl@0: return _URC_INSTALL_CONTEXT; sl@0: } sl@0: } /* endif (barrier match) */ sl@0: } /* endif (which phase) */ sl@0: sl@0: /* Advance to the next item, remembering to skip the landing pad if present */ sl@0: ehtp += (sizeof(((EHT_fnspec_tail *)ehtp)->rtti_count) + sl@0: sizeof(uint32_t) * rtti_count + sl@0: (counter_word == rtti_count ? 0 : sizeof(landingpad_t))); sl@0: break; sl@0: } sl@0: case 3: /* unallocated */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_TABLECORRUPT); sl@0: return _URC_FAILURE; sl@0: } /* switch */ sl@0: sl@0: } /* while (1) */ sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Reached end of EHT\n"); sl@0: #endif sl@0: sl@0: } /* if out-of-line EHT */ sl@0: sl@0: sl@0: /* Do a virtual unwind of this frame - load the first unwind bytes then loop. sl@0: * Loop exit is by executing opcode CODE_FINISH. sl@0: */ sl@0: sl@0: ud.unwind_word = *(uint32_t *)eht_startp; /* first word */ sl@0: ud.unwind_word_pointer = (uint32_t *)eht_startp + 1; /* ptr to extension words, if any */ sl@0: if (idx == 0) { /* short description */ sl@0: ud.unwind_words_remaining = 0; /* no further words */ sl@0: ud.unwind_word <<= 8; /* 3 explicit unwind bytes in this word */ sl@0: ud.unwind_word_bytes_remaining = 3; sl@0: } else { /* long description: extension word count in bits 16-23 */ sl@0: ud.unwind_words_remaining = ((ud.unwind_word) >> 16) & 0xff; sl@0: ud.unwind_word <<= 16; /* 2 explicit unwind bytes in this word */ sl@0: ud.unwind_word_bytes_remaining = 2; sl@0: } sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: /* debug_print_vrs(context); */ sl@0: #endif sl@0: sl@0: while (1) { sl@0: uint8_t ub = next_unwind_byte(&ud); sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Unwind byte 0x%x\n", ub); sl@0: #endif sl@0: sl@0: /* decode and execute the current byte ... */ sl@0: sl@0: if (ub == CODE_FINISH) { /* finished unwinding */ sl@0: if (!wrote_pc) { sl@0: uint32_t lr; sl@0: _Unwind_VRS_Get(context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, &lr); sl@0: core_set(context, R_PC, lr); sl@0: wrote_pc_from_lr = true; sl@0: } sl@0: #ifdef PR_DIAGNOSTICS sl@0: { sl@0: uint32_t nextpc; sl@0: _Unwind_VRS_Get(context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, &nextpc); sl@0: printf("PR Next PC is 0x%x\n", nextpc); sl@0: } sl@0: #endif sl@0: break; sl@0: } sl@0: if (ub <= 0x3f) { /* 00nnnnnn: vsp += (nnnnnn << 2) + 4 */ sl@0: uint32_t increment = ((ub & 0x3f) << 2) + 4; sl@0: core_set(context, R_SP, core_get(context, R_SP) + increment); sl@0: continue; sl@0: } sl@0: if (ub <= 0x7f) { /* 01xxxxxx: vsp -= (xxxxxx << 2) + 4 */ sl@0: uint32_t decrement = ((ub & 0x3f) << 2) + 4; sl@0: core_set(context, R_SP, core_get(context, R_SP) - decrement); sl@0: continue; sl@0: } sl@0: if (ub <= 0x8f) { /* 100000000 00000000: refuse, 1000rrrr rrrrrrrr: pop integer regs */ sl@0: uint32_t mask = (ub & 0xf) << 12; sl@0: ub = next_unwind_byte(&ud); sl@0: mask |= ub << 4; sl@0: if (mask == 0) { /* 10000000 00000000 refuse to unwind */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_NOUNWIND); sl@0: return _URC_FAILURE; sl@0: } sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_CORE, mask, _UVRSD_UINT32) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: if (mask & (1 << R_PC)) wrote_pc = true; sl@0: continue; sl@0: } sl@0: if (ub <= 0x9f) { /* 1001nnnn: vsp = r[nnnn] if not 13,15 */ sl@0: uint8_t regno = ub & 0xf; sl@0: if (regno == 13 || regno == R_PC) { /* reserved */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_CPP_BADOPCODE); sl@0: return _URC_FAILURE; sl@0: } sl@0: core_set(context, R_SP, core_get(context, regno)); sl@0: continue; sl@0: } sl@0: if (ub <= 0xaf) { /* 1010xnnn: pop r4-r[4+nnn], +r14 if x */ sl@0: uint32_t mask = count_to_mask((ub & 0x7) + 1) << 4; sl@0: if (ub & 0x8) mask |= (1 << R_LR); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_CORE, mask, _UVRSD_UINT32) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: if (ub <= 0xb7) { sl@0: /* if (ub == 0xb0) is CODE_FINISH, handled earlier */ sl@0: if (ub == 0xb1) { /* 10110001 0000iiii pop integer regs, others reserved */ sl@0: uint32_t mask = next_unwind_byte(&ud); sl@0: if (mask == 0 || mask > 0xf) { /* reserved */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_CPP_BADOPCODE); sl@0: return _URC_FAILURE; sl@0: } sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_CORE, mask, _UVRSD_UINT32) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: if (ub == 0xb2) { /* 10110010 uleb128 : vsp += (uleb128 << 2) + 0x204 */ sl@0: uint32_t u = 0; sl@0: uint32_t n = 0; sl@0: /* decode */ sl@0: while (1) { sl@0: ub = next_unwind_byte(&ud); sl@0: u |= (ub & 0x7f) << n; sl@0: if ((ub & 0x80) == 0) break; sl@0: n += 7; sl@0: } sl@0: core_set(context, R_SP, core_get(context, R_SP) + (u << 2) + 0x204); sl@0: continue; sl@0: } sl@0: if (ub == 0xb3) { /* 10110011: pop vfp */ sl@0: uint32_t discriminator = next_unwind_byte(&ud); sl@0: discriminator = ((discriminator & 0xf0) << 12) | ((discriminator & 0x0f) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_VFP, discriminator, _UVRSD_VFPX) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: { /* 101101nn: pop fpa */ sl@0: uint32_t discriminator = 0x40000 | ((ub & 0x3) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_FPA, discriminator, _UVRSD_FPAX) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: } /* if (ub <= 0xb7) ... */ sl@0: if (ub <= 0xbf) { /* 10111nnn: pop vfp */ sl@0: uint32_t discriminator = 0x80000 | ((ub & 0x7) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_VFP, discriminator, _UVRSD_VFPX) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: if (ub <= 0xc7) { sl@0: if (ub == 0xc7) { /* 11000111: WMMX C regs */ sl@0: uint32_t mask = next_unwind_byte(&ud); sl@0: if (mask == 0 || mask > 0xf) { /* reserved */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_CPP_BADOPCODE); sl@0: return _URC_FAILURE; sl@0: } sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_WMMXC, mask, _UVRSD_UINT32) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } else if (ub == 0xc6) { /* 11000110: WMMX D regs */ sl@0: uint32_t discriminator = next_unwind_byte(&ud); sl@0: discriminator = ((discriminator & 0xf0) << 4) | ((discriminator & 0x0f) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_WMMXD, discriminator, _UVRSD_UINT64) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } else { sl@0: /* 11000nnn (nnn != 6, 7): WMMX D regs */ sl@0: uint32_t discriminator = 0xa00 | ((ub & 0x7) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_WMMXD, discriminator, _UVRSD_UINT64) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: } /* if (ub <= 0xc7) ... */ sl@0: if (ub == 0xc8) { /* 11001000: pop fpa */ sl@0: uint32_t discriminator = next_unwind_byte(&ud); sl@0: discriminator = ((discriminator & 0x70) << 12) | ((discriminator & 0x03) + 1); sl@0: if (_Unwind_VRS_Pop(context, _UVRSC_FPA, discriminator, _UVRSD_FPAX) != _UVRSR_OK) { sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_VRSFAILED); sl@0: return _URC_FAILURE; sl@0: } sl@0: continue; sl@0: } sl@0: /* and in fact everything else is currently reserved or spare */ sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_ENDING, _UAARG_ENDING_CPP_BADOPCODE); sl@0: return _URC_FAILURE; sl@0: } sl@0: sl@0: #ifdef PR_DIAGNOSTICS sl@0: /* debug_print_vrs(context); */ sl@0: #endif sl@0: sl@0: /* The VRS has now been updated to reflect the virtual unwind. sl@0: * If we are dealing with an unmatched fnspec, pop intervening frames sl@0: * and call unexpected(). Else return to our caller with an sl@0: * indication to continue unwinding. sl@0: */ sl@0: sl@0: if (phase2_call_unexpected_after_unwind) { sl@0: /* Set up the VRS to enter __cxa_call_unexpected, sl@0: * and upload the VRS to the real machine. sl@0: * The barrier_cache was initialised earlier. sl@0: */ sl@0: #ifdef PR_DIAGNOSTICS sl@0: printf("PR Got fnspec barrier in phase 2 (unwinding completed)\n"); sl@0: #endif sl@0: core_set(context, 0, (uint32_t)ucbp); sl@0: if (!wrote_pc_from_lr) { sl@0: uint32_t pc; sl@0: /* Move the return address to lr to simulate a call */ sl@0: _Unwind_VRS_Get(context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, &pc); sl@0: core_set(context, R_LR, pc); sl@0: } sl@0: core_set(context, R_PC, (uint32_t)&__cxa_call_unexpected); sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_PADENTRY, &__cxa_call_unexpected); sl@0: return _URC_INSTALL_CONTEXT; sl@0: } sl@0: sl@0: /* Else continue with next frame */ sl@0: return _URC_CONTINUE_UNWIND; sl@0: } sl@0: sl@0: #endif sl@0: /* end ifdef prcommon_c */