kernel/eka/compsupp/symaehabi/unwind_pr.c
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

/* 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 <cstdlib>
/* 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 */