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