kernel/eka/compsupp/symaehabi/cppsemantics.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 09:24:54 +0200
changeset 9 96e5fb8b040d
permissions -rw-r--r--
Revision: 200951 Kit: 200951

/* The C++ exceptions runtime support
 *
 * 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: 92950 $
 * Checkin $Date: 2005-10-12 11:08:47 +0100 (Wed, 12 Oct 2005) $
 * Revising $Author: achapman $
 */

/* This source file is compiled automatically by ARM's make system into
 * multiple object files. The source regions constituting object file
 * xxx.o are delimited by ifdef xxx_c / endif directives.
 *
 * The source regions currently marked are:
 * arm_exceptions_globs_c
 * arm_exceptions_mem_c
 * arm_exceptions_uncaught_c
 * arm_exceptions_terminate_c
 * arm_exceptions_setterminate_c
 * arm_exceptions_unexpected_c
 * arm_exceptions_setunexpected_c
 * arm_exceptions_support_c
 * arm_exceptions_callterm_c
 * arm_exceptions_callunex_c
 * arm_exceptions_currenttype_c
 * arm_exceptions_alloc_c
 * arm_exceptions_free_c
 * arm_exceptions_throw_c
 * arm_exceptions_rethrow_c
 * arm_exceptions_foreign_c
 * arm_exceptions_cleanup_c
 * arm_exceptions_getexceptionptr_c
 * arm_exceptions_begincatch_c
 * arm_exceptions_endcatch_c
 * arm_exceptions_bad_typeid_c
 * arm_exceptions_bad_cast_c
 */

#include <string.h>

// Environment:
#include "unwind_env.h"
// Language-independent unwinder declarations:
#include "unwinder.h"

#ifdef __EPOC32__
/* Symbian specific support */
#include "symbian_support.h"
#endif

#include <new>

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

/* By default, none of these routines are unwindable: */
#pragma noexceptions_unwind

/* For brevity: */

typedef _Unwind_Control_Block UCB;

using std::terminate_handler;
using std::unexpected_handler;
using std::terminate;
using std::unexpected;
using std::type_info;

/* Redeclare these interface routines as weak, so using them does not
 * pull in the unwind library. We only want the unwind library if
 * someone throws (or raises an exception from some other language).
 */
WEAKDECL NORETURNDECL void _Unwind_Resume(UCB *);
WEAKDECL void _Unwind_Complete(UCB *);

/* Diagnostics:
 * Define DEBUG to get extra interfaces which assist debugging this functionality.
 * Define CPP_DIAGNOSTICS for printed diagnostics.
 */
#ifdef DEBUG
#define CPP_DIAGNOSTICS
#endif

#ifdef CPP_DIAGNOSTICS
#ifndef __EPOC32__
extern "C" int printf(const char *, ...);
#endif
#endif

/* --------- "Exceptions_class" string for our implementation: --------- */

#define EXCEPTIONS_CLASS_SIZE 8
#define ARMCPP_EXCEPTIONS_CLASS "ARM\0C++\0"


/* --------- Exception control object: --------- */

// Type __cxa_exception is the combined C++ housekeeping (LEO) and UCB.
// It will be followed by the user exception object, hence must ensure
// the latter is aligned on an 8 byte boundary.

#ifndef __EPOC32__
struct __cxa_exception {
  const type_info *exceptionType;       // RTTI object describing the type of the exception
  void *(*exceptionDestructor)(void *); // Destructor for the exception object (may be NULL)
  unexpected_handler unexpectedHandler; // Handler in force after evaluating throw expr
  terminate_handler terminateHandler;   // Handler in force after evaluating throw expr
  __cxa_exception *nextCaughtException; // Chain of "currently caught" c++ exception objects
  uint32_t handlerCount;                // Count of how many handlers this EO is "caught" in
  __cxa_exception *nextPropagatingException; // Chain of objects saved over cleanup
  uint32_t propagationCount;            // Count of live propagations (throws) of this EO
  UCB ucb;                              // Forces alignment of next item to 8-byte boundary
};
#endif


/* --------- Control "globals": --------- */

// We do this by putting all the thread-specific "globals" into a single
// area of store, which we allocate space for dynamically.
// We don't define a constructor for this; see comments with __cxa_get_globals.

#ifndef __EPOC32__
typedef void (*handler)(void);

struct __cxa_eh_globals {
  uint32_t uncaughtExceptions;               // counter
  unexpected_handler unexpectedHandler;      // per-thread handler
  terminate_handler terminateHandler;        // per-thread handler
  bool implementation_ever_called_terminate; // true if it ever did
  handler call_hook;     // transient field to tell terminate/unexpected which hook to call
  __cxa_exception *caughtExceptions;         // chain of "caught" exceptions
  __cxa_exception *propagatingExceptions;    // chain of "propagating" (in cleanup) exceptions
  void *emergency_buffer;                    // emergency buffer for when rest of heap full
};
#endif


/* ---------- Entry points: ---------- */

/* There is a little type-delicacy required here as __cxa_throw takes a
 * function pointer. Setting aside the problem of not being able to form
 * a pointer to a destructor in C++, if we simply say extern "C" here
 * then the function pointer will also have C linkage and will be a
 * pointer to a C function. This causes problems when __cxa_throw is
 * defined (unless we repeat the extern "C" at the definition site) because
 * the fnptr in the definition gets C++ linkage, hence that __cxa_throw has
 * a different signature to the declared one, and so the function we wanted
 * doesn't get defined at all.
 * Maybe it should just take a void * but this seems more honest.
 */

typedef void *(*cppdtorptr)(void *);

extern "C" {

  // Protocol routines called directly from application code

  IMPORT_C void *__cxa_allocate_exception(size_t size);
  IMPORT_C void __cxa_free_exception(void *);
  WEAKDECL void __cxa_throw(void *, const type_info *, cppdtorptr);
  IMPORT_C void __cxa_rethrow(void);
  IMPORT_C void *__cxa_get_exception_ptr(UCB *);
  void *__cxa_begin_catch(UCB *);
  IMPORT_C void __cxa_end_catch(void);
  IMPORT_C void __cxa_end_cleanup(void);
  IMPORT_C const type_info *__cxa_current_exception_type(void);

  // Protocol routines usually called only by the personality routine(s).

  IMPORT_C void __cxa_call_terminate(UCB *);
  IMPORT_C void __cxa_call_unexpected(UCB *);
  IMPORT_C bool __cxa_begin_cleanup(UCB *);
  typedef enum {
    ctm_failed = 0,
    ctm_succeeded = 1,
    ctm_succeeded_with_ptr_to_base = 2
  } __cxa_type_match_result;
  IMPORT_C __cxa_type_match_result __cxa_type_match(UCB *, const std::type_info *,
                                                    bool is_reference_type, void **);

  // Auxilliary routines

  __cxa_eh_globals *__cxa_get_globals(void);
  IMPORT_C void __cxa_bad_typeid(void);
  IMPORT_C void __cxa_bad_cast(void);

  // Emergency memory buffer management routines

  void *__ARM_exceptions_buffer_init(void);
  void *__ARM_exceptions_buffer_allocate(void *, size_t);
  void *__ARM_exceptions_buffer_free(void *, void *);
}


// Support routines

#define NAMES __ARM
namespace NAMES {
  void default_unexpected_handler(void);
  void call_terminate_handler(UCB *);
  void eh_catch_semantics(UCB *);
  bool is_foreign_exception(UCB *);
  bool same_exceptions_class(const void *, const void *);
  __cxa_exception *get_foreign_intermediary(__cxa_exception *, UCB *);
}

// Macro: convert ucb pointer to __cxa_exception pointer

#define ucbp_to_ep(UCB_P) ((__cxa_exception *)((char *)(UCB_P) - offsetof(__cxa_exception, ucb)))


#ifdef arm_exceptions_globs_c

/* --------- Allocating and retrieving "globals": --------- */

// The exception-handling globals should be allocated per-thread.
// This is done here assuming the existance of a zero-initialised void*
// pointer location obtainable by the macro EH_GLOBALS.

// Default terminate handler:

#ifndef __EPOC32__
static void __default_terminate_handler(void) {
  abort();
}
#endif

// If std::unexpected() is in the image, include a default handler for it:
namespace NAMES { WEAKDECL void default_unexpected_handler(void); }

// ARM's toolchain documentation states that if symbol
// __ARM_exceptions_buffer_required is present we should allocate
// an emergency buffer.
// As we aren't allowed static data in ARM library builds, reference the
// symbol by declaring it a function. This causes an additional problem when
// ARM libraries are built position-independent, namely that an absent
// function doesn't compare address-equal to NULL. So we have to get the
// "addresses" from two different compilation units and compare those.
// This is a known defect, to be fixed in the compiler.
extern "C" WEAKDECL void __ARM_exceptions_buffer_required(void);
extern void (*(__ARM_exceptions_buffer_required_address(void)))(void);

// The address comparison function only needs to be used when we are building 
// position-independent.  In other cases, comparing the address to NULL is more
// efficient.
#if 0
#  define ARM_EXCEPTIONS_BUFFER_NOT_REQUIRED() (&__ARM_exceptions_buffer_required != __ARM_exceptions_buffer_required_address())
#else
#  define ARM_EXCEPTIONS_BUFFER_NOT_REQUIRED() (&__ARM_exceptions_buffer_required == NULL)
#endif

// __cxa_eh_globals returns the per-thread memory. There are several complications,
// all of which relate to not touching the exceptions system while trying to
// initialise it:
// 1) We can't obtain memory by calling new or nothrow new as both of these use
//    exceptions internally, so we must use malloc
// 2) We choose not to initialise the memory via placement new and a constructor,
//    since placement new is declared with an empty function exception specification,
//    which causes more of the exceptions system to always be pulled in.
// 3) We can't call terminate, as terminate looks in the memory we are trying to
//    allocate.

EXPORT_C __cxa_eh_globals *__cxa_get_globals(void)
{
  __cxa_eh_globals *this_thread_globals = (__cxa_eh_globals *)(EH_GLOBALS);

#ifndef __EPOC32__
  /* The Symbian implementation allocates the required space on the threads stack
     at thread creation and sets up thread local storage to point to the globals
     which are also initialised
  */
  if (this_thread_globals == NULL) {

    // First call
    // Obtain some memory: this is thread-safe provided malloc is.
    this_thread_globals = (__cxa_eh_globals *)malloc(sizeof(__cxa_eh_globals));
    if (this_thread_globals == NULL) abort(); // NOT terminate(), which calls this fn

    // Save the pointer in the specially-provided location
    EH_GLOBALS = this_thread_globals;

    // Finally initialise the memory by hand
    this_thread_globals->uncaughtExceptions = 0;
    this_thread_globals->unexpectedHandler = NAMES::default_unexpected_handler;
    this_thread_globals->terminateHandler = __default_terminate_handler;
    this_thread_globals->implementation_ever_called_terminate = false;
    this_thread_globals->call_hook = NULL;
    this_thread_globals->caughtExceptions = NULL;
    this_thread_globals->propagatingExceptions = NULL;
    if (ARM_EXCEPTIONS_BUFFER_NOT_REQUIRED())
      this_thread_globals->emergency_buffer = NULL;
    else
      this_thread_globals->emergency_buffer = __ARM_exceptions_buffer_init();
  }
#endif
  return this_thread_globals;
}


#endif /* arm_exceptions_globs_c */
#ifdef arm_exceptions_mem_c

/* --------- Emergency memory: --------- */

// It is possible to reserve memory for throwing bad_alloc when the heap
// is otherwise full. The ARM implementation provides hooks to do this.
// The default implementation reserves just enough space for a bad_alloc
// object, so if memory is later exhausted bad_alloc can still be thrown.
// Note there is no guarantee or requirement that the exception being
// thrown is actually bad_alloc.

// A usage flag and enough space for a bad_alloc exception control object
#ifndef __EPOC32__
struct emergency_eco {
  __cxa_exception ep;
  std::bad_alloc b;
};

struct emergency_buffer {
  bool inuse;
  struct emergency_eco eco;
};

#endif

#ifndef __EPOC32__
// The SymbianOS implementation allocates this space at thread creation

// Initialiser
void* __ARM_exceptions_buffer_init(void)
{
  emergency_buffer *buffer = (emergency_buffer *)malloc(sizeof(emergency_buffer));
  if (buffer == NULL) return NULL;
  buffer->inuse = false;
  return buffer;
}
#endif

// Allocator
void *__ARM_exceptions_buffer_allocate(void *buffer, size_t size)
{
  emergency_buffer *b = (emergency_buffer *)buffer;
  if (size > sizeof(emergency_eco) || b == NULL || b->inuse) return NULL;
  b->inuse = true;
  return &b->eco;
}

// Deallocator: Must return non-NULL if and only if it recognises
// and releases the supplied object
void *__ARM_exceptions_buffer_free(void *buffer, void *addr)
{
  emergency_buffer *b = (emergency_buffer *)buffer;
  if (b == NULL || addr != &b->eco) return NULL;
  b->inuse = false;
  return b;
}

#  if 0
// Hook activation support - see comments earlier
extern "C" WEAKDECL void __ARM_exceptions_buffer_required(void);
void (*(__ARM_exceptions_buffer_required_address(void)))(void)
{
  return &__ARM_exceptions_buffer_required;
}
#  endif


#endif /* arm_exceptions_mem_c */
#ifdef arm_exceptions_uncaught_c

/* ---- uncaught_exception() ---- */

/* The EDG (and I think our) interpretation is that if the implementation
 * ever called terminate(), uncaught_exception() should return true.
 */
#if __ARMCC_VERSION < 220000
bool std::uncaught_exception(void)
#else
EXPORT_C bool std::uncaught_exception(void)
#endif
{
   __cxa_eh_globals *g = __cxa_get_globals();
   return g->implementation_ever_called_terminate || g->uncaughtExceptions;
}


#endif /* arm_exceptions_uncaught_c */
#ifdef arm_exceptions_terminate_c

/* ---- terminate() etc ---- */

/* The behaviour of terminate() must differ between calls by the
 * implementation and calls by the application. This is achieved by having the
 * implementation set call_hook immediately before the call to terminate().
 * The hook called by terminate() should terminate the program without
 * returning to the caller. There is no requirement for terminate() itself to
 * intercept throws.
 */

EXPORT_C void std::terminate(void)
{
  __cxa_eh_globals *g = __cxa_get_globals();

  if (g->call_hook != NULL) {
    // Clear then call hook fn we were passed
    handler call_hook = g->call_hook;
    g->call_hook = NULL;
    call_hook();
  } else {
    // Call global hook fn
    g->terminateHandler();
  }
  // If hook fn returns:
  abort();
}


#endif /* arm_exceptions_terminate_c */
#ifdef arm_exceptions_setterminate_c

EXPORT_C terminate_handler std::set_terminate(terminate_handler h) throw()
{
  __cxa_eh_globals *g = __cxa_get_globals();
  terminate_handler old = g->terminateHandler;
  g->terminateHandler = h;
  return old;
}


#endif /* arm_exceptions_setterminate_c */
#ifdef arm_exceptions_unexpected_c

/* ---- unexpected() etc ---- */
/* Comments as per terminate() */

void NAMES::default_unexpected_handler(void) {
  terminate();
}

#pragma exceptions_unwind

EXPORT_C void std::unexpected(void)
{
  __cxa_eh_globals *g = __cxa_get_globals();

  if (g->call_hook != NULL) {
    // Clear then call hook fn we were passed
    handler call_hook = g->call_hook;
    g->call_hook = NULL;
    call_hook();
  } else {
    // Call global hook fn
    g->unexpectedHandler();
  }

  // If hook fn returns:
  abort();
}


#endif /* arm_exceptions_unexpected_c */
#ifdef arm_exceptions_setunexpected_c

EXPORT_C unexpected_handler std::set_unexpected(unexpected_handler h) throw()
{
  __cxa_eh_globals *g = __cxa_get_globals();
  unexpected_handler old = g->unexpectedHandler;
  g->unexpectedHandler = h;
  return old;
}


#endif /* arm_exceptions_setunexpected_c */
#ifdef arm_exceptions_support_c

/* ---------- Helper functions: ---------- */

/* Two routines to determine whether two exceptions objects share a layout.
 * This is determined by checking whether the UCB exception_class members
 * are identical.
 * In principle we could use memcmp to perform this check (the code is
 * given below) but the check is quite frequent and so that is costly.
 * Therefore for efficiency we make use of the fact that the UCB is
 * word aligned, that the exception_class member is consequently
 * word aligned within it, and that we know the size of the member.
 * We take care elsewhere to only ever call the routines with pointers
 * to word-aligned addresses.
 */

#if 0

// Straightforward versions

bool NAMES::same_exceptions_class(const void *ec1, const void *ec2)
{
  return memcmp(ec1, ec2, EXCEPTIONS_CLASS_SIZE) == 0; // identical
}

// One of our exception objects, or not?

bool NAMES::is_foreign_exception(UCB *ucbp)
{
  return !NAMES::same_exceptions_class(&ucbp->exception_class, ARMCPP_EXCEPTIONS_CLASS);
}

#else

// Faster versions

bool NAMES::same_exceptions_class(const void *ec1, const void *ec2)
{
  uint32_t *ip1 = (uint32_t *)ec1;
  uint32_t *ip2 = (uint32_t *)ec2;
  return ip1[0] == ip2[0] && ip1[1] == ip2[1];
}

// One of our exception objects, or not?

bool NAMES::is_foreign_exception(UCB *ucbp)
{
  // Need a word-aligned copy of the string
  static const union {
    const char s[EXCEPTIONS_CLASS_SIZE+1]; int dummy;
  } is_foreign_exception_static = {ARMCPP_EXCEPTIONS_CLASS};
  return !NAMES::same_exceptions_class(&ucbp->exception_class, &is_foreign_exception_static.s);
}

#endif


#endif /* arm_exceptions_support_c */
#ifdef arm_exceptions_callterm_c

/* When the implementation wants to call terminate(), do the following:
 * Mark the object as "caught" so it can be rethrown.
 * Set the hook function for terminate() to call;
 * Mark the fact that terminate() has been called by the implementation;
 * We have to be careful - the implementation might encounter an error while
 * unwinding a foreign exception, and also it is possible this might be
 * called after failing to obtain a ucb.
 */

void NAMES::call_terminate_handler(UCB *ucbp)
{
  __cxa_eh_globals *g = __cxa_get_globals();

  if (ucbp == NULL) {
    // Call global hook
    g->call_hook = g->terminateHandler;
  } else {
    // Extract the hook to call
    if (NAMES::is_foreign_exception(ucbp)) {
      // Someone else's
      g->call_hook = g->terminateHandler;  // best we can do under the circumstances
    } else {
      // One of ours
      __cxa_exception *ep = ucbp_to_ep(ucbp);
      g->call_hook = ep->terminateHandler; // the one in force at the point of throw
    }
  }

  g->implementation_ever_called_terminate = true;
  terminate();
  // never returns
}


EXPORT_C void __cxa_call_terminate(UCB *ucbp)
{
  if (ucbp != NULL) // Record entry to (implicit) handler
    __cxa_begin_catch(ucbp);

  NAMES::call_terminate_handler(ucbp);
  // never returns
}


#endif /* arm_exceptions_callterm_c */
#ifdef arm_exceptions_callunex_c

/* When the implementation wants to call unexpected(), do the following:
 * Mark the object as "caught" so it can be rethrown.
 * Set the hook function for unexpected() to call;
 * Call unexpected and trap any throw to make sure it is acceptable.
 * We have to be careful - the implementation might encounter an error while
 * unwinding a foreign exception.
 */

#pragma exceptions_unwind

EXPORT_C void __cxa_call_unexpected(UCB *ucbp)
{

  // Extract data we will need from the barrier cache before
  // anyone has a chance to overwrite it

  uint32_t rtti_count = ucbp->barrier_cache.bitpattern[BARRIER_FNSPECCOUNT];
  uint32_t base = ucbp->barrier_cache.bitpattern[BARRIER_FNSPECBASE];
  uint32_t stride = ucbp->barrier_cache.bitpattern[BARRIER_FNSPECSTRIDE];
  uint32_t rtti_offset_array_addr = ucbp->barrier_cache.bitpattern[BARRIER_FNSPECARRAY];

  // Also get the globals here and the eop

  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep = ucbp_to_ep(ucbp);

#ifdef ARM_EXCEPTIONS_ENABLED
  try {
#endif

    // Record entry to (implicit) handler

    __cxa_begin_catch(ucbp);

    // Now extract the hook to call

    if (NAMES::is_foreign_exception(ucbp)) {
      // Someone else's
      g->call_hook = g->unexpectedHandler;  // best we can do under the circumstances
    } else {
      // One of ours
      g->call_hook = ep->unexpectedHandler; // the one in force at the point of throw
    }
    unexpected();  // never returns normally, but might throw something

#ifdef ARM_EXCEPTIONS_ENABLED
  } catch (...) {

    // Unexpected() threw. This requires some delicacy.
    // There are 2 possibilities:
    // i) rethrow of the same object
    // ii) throw of a new object
    // Unexpected() is an implicit handler, and we manually called
    // __cxa_begin_catch on the ingoing object. We need to call
    // __cxa_end_catch on that object and, if the object is no longer
    // being handled (possible in case ii), this will cause its destruction.
    // The wrinkle is that in case ii the object is not on top of the catch
    // stack because we just caught something else.

    // Get hold of what was thrown (which we just caught).

    __cxa_exception *epnew = g->caughtExceptions;

    // Call __cxa_end_catch on the original object, taking care with the catch chain

    if (epnew == ep) {
      // rethrow - easy & safe - object is at top of chain and handlercount > 1
      __cxa_end_catch();
    } else {
      // not rethrow - unchain the top (new) object, clean up the next one,
      // and put the top object back

      // unchain
      g->caughtExceptions = epnew->nextCaughtException;
      // assert g->caughtExceptions == ep now
      // Decrement its handlercount (this might call a dtor if the count goes to 0,
      // and the dtor might throw - if it does, just give up)
      try {
	__cxa_end_catch();
      } catch(...) {
	terminate();
      }
      // Chain back in
      epnew->nextCaughtException = g->caughtExceptions;
      g->caughtExceptions = epnew;
    }

    // See whether what was thrown is permitted, and in passing
    // see if std::bad_exception is permitted

    bool bad_exception_permitted = false;
    uint32_t i;
    for (i = 0; i < rtti_count; i++) {
      void *matched_object;
      type_info *fnspec;
      if (EHABI_V2(ucbp))
	fnspec = (type_info *)__ARM_resolve_target2((void *)rtti_offset_array_addr);
      else
	fnspec = (type_info *)(*(uint32_t *)rtti_offset_array_addr + base);
      if (__cxa_type_match(&(epnew->ucb), fnspec, false, &matched_object)) {
#ifdef CPP_DIAGNOSTICS
	printf("__cxa_call_unexpected: fnspec matched\n");
#endif
	throw; // got a match - propagate it
      }
      if (typeid(std::bad_exception) == *fnspec)
	bad_exception_permitted = true;
      rtti_offset_array_addr += stride;
    }

    // There was no match...
    if (bad_exception_permitted) throw std::bad_exception(); // transmute

    // Otherwise call epnew's terminate handler
    NAMES::call_terminate_handler(&epnew->ucb);
  }
#endif
}


#endif /* arm_exceptions_callunex_c */
#ifdef arm_exceptions_currenttype_c

/* Yield the type of the currently handled exception, or null if none or the
 * object is foreign.
 */

EXPORT_C const type_info *__cxa_current_exception_type(void)
{
  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep = g->caughtExceptions;
  if (ep == NULL || NAMES::is_foreign_exception(&ep->ucb)) return NULL;
  return ep->exceptionType;
}


#endif /* arm_exceptions_currenttype_c */
#ifdef arm_exceptions_alloc_c

/* Allocate store for controlling an exception propagation */

EXPORT_C void *__cxa_allocate_exception(size_t size)
{
  __cxa_eh_globals *g = __cxa_get_globals();

  // Allocate store for a __cxa_exception header and the EO.
  // Allocated store should be thread-safe and persistent, and must do
  // something sensible if the allocation fails

  size_t total_size = size + sizeof(__cxa_exception);
  // coverity[alloc_fn]
  __cxa_exception *ep = (__cxa_exception *)malloc(total_size);
  if (ep == NULL) {
    // Try the emergency memory pool
    SYMBIAN_EH_SUPPORT_PRINTF("Trying emergency buffer: size %d\n", total_size);
    ep = (__cxa_exception *)__ARM_exceptions_buffer_allocate(g->emergency_buffer, total_size);
        
    if (ep == NULL) {
      SYMBIAN_EH_SUPPORT_PRINTF("Emergency buffer allocation failed. Terminating\n");
      NAMES::call_terminate_handler(NULL);
    }
  }

  UCB *ucbp = &ep->ucb;

  // Initialise the UCB

  memcpy(ucbp->exception_class, ARMCPP_EXCEPTIONS_CLASS, EXCEPTIONS_CLASS_SIZE);
  ucbp->exception_cleanup = NULL; /* initialise properly before throwing */
  ucbp->unwinder_cache.reserved1 = 0; /* required to do this */

  // Initialise parts of the LEO, in case copy-construction of the EO results
  // in a need to call terminate (via __cxa_call_terminate)

  ep->handlerCount = 0;                         // Not in any handlers
  ep->nextCaughtException = NULL;               // Not in any handlers
  ep->nextPropagatingException = NULL;          // Not saved over cleanup
  ep->propagationCount = 0;                     // Not propagating
  ep->terminateHandler = g->terminateHandler;   // Cache current terminate handler
  ep->unexpectedHandler = g->unexpectedHandler; // Cache current unexpected handler

  // Return pointer to the EO
  // coverity[memory_leak]
  return ep + 1;
}


#endif /* arm_exceptions_alloc_c */
#ifdef arm_exceptions_free_c

/* Free store allocated by __cxa_allocate_exception */

EXPORT_C void __cxa_free_exception(void *eop)
{
  __cxa_eh_globals *g = __cxa_get_globals();
  char *ep = (char *)eop - sizeof(__cxa_exception);
  if (__ARM_exceptions_buffer_free(g->emergency_buffer, ep)) return;
  free(ep);
}


#endif /* arm_exceptions_free_c */
#ifdef arm_exceptions_throw_c

/* This routine is called when a foreign runtime catches one of our exception
 * objects and then exits its catch by a means other than rethrow.
 * We should clean it up as if we had caught it ourselves.
 */

static void external_exception_termination(_Unwind_Reason_Code c, UCB *ucbp)
{
  NAMES::eh_catch_semantics(ucbp);
  __cxa_end_catch();
}


/* Initiate a throw */

#pragma push
#pragma exceptions_unwind

EXPORT_C void __cxa_throw(void *eop, const type_info *t, cppdtorptr d)
{
  __cxa_exception *ep = (__cxa_exception *)((char *)eop - sizeof(__cxa_exception));
  UCB *ucbp = &ep->ucb;

  // Initialise the remaining LEO and UCB fields not done by __cxa_allocate_exception

  ucbp->exception_cleanup = external_exception_termination;
  ep->exceptionType = t;
  ep->exceptionDestructor = d;
  ep->propagationCount = 1;      // Propagating by 1 throw

  // Increment the uncaught C++ exceptions count

  __cxa_eh_globals *g = __cxa_get_globals();
  g->uncaughtExceptions++;

  // Tell debugger what's happening

  DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_STARTING, t);

  // Initiate unwinding - if we get control back, call C++ routine terminate()

  _Unwind_RaiseException(ucbp);

#ifdef CPP_DIAGNOSTICS
  printf("__cxa_throw: throw failed\n");
#endif

  __cxa_call_terminate(ucbp);
}

#pragma pop

/* ----- Type matching: ----- */

/* This is located here so that (in ARM's implementation) it is only retained in
 * an image if the application itself throws.
 */

/* Type matching functions.
 * C++ DR126 says the matching rules for fnspecs are intended to be the same as
 * those for catch:
 * "A function is said to allow an exception of type E if its exception-specification
 * contains a type T for which a handler of type T would be a match (15.3 except.handle)
 * for an exception of type E."
 * Thus we have a single type matching rule.
 */

/* Helper macros: */

#define CV_quals_of_pointee(P) (((const abi::__pbase_type_info *)(P))->__flags & \
		                (abi::__pbase_type_info::__const_mask | \
		                 abi::__pbase_type_info::__volatile_mask))

#define is_const(QUALS) (((QUALS) & abi::__pbase_type_info::__const_mask) != 0)

#define any_qualifier_missing(TEST_QUALS, REF_QUALS) ((~(TEST_QUALS) & (REF_QUALS)) != 0)

/* A routine is required for derived class to base class conversion.
 * This is obtained via a macro definition DERIVED_TO_BASE_CONVERSION
 * in unwind_env.h.
 */

/* External entry point:
 * Type check the c++ rtti object for compatibility against the type of
 * the object containing the ucb. Return a pointer to the matched object
 * (possibly a non-leftmost baseclass of the exception object)
 */
EXPORT_C __cxa_type_match_result __cxa_type_match(UCB *ucbp, const type_info *match_type,
                                                  bool is_reference_type, void **matched_objectpp)
{
  if (NAMES::is_foreign_exception(ucbp))
    return ctm_failed;

  __cxa_exception *ep = ucbp_to_ep(ucbp);
  const type_info *throw_type = ep->exceptionType;
  bool previous_qualifiers_include_const = true; // for pointer qualification conversion
  unsigned int pointer_depth = 0;
  void *original_objectp = ep + 1;
  void *current_objectp = original_objectp;

  for (;;) {

    // Match if identical

    if (*throw_type == *match_type) {
      *matched_objectpp = original_objectp;
#ifdef CPP_DIAGNOSTICS
      printf("__cxa_type_match: success\n");
#endif
      return ctm_succeeded;
    }

    // Fail if one is a pointer and the other isn't

    const type_info &type_throw_type = typeid(*throw_type);
    const type_info &type_match_type = typeid(*match_type);

    if ((type_throw_type == typeid(abi::__pointer_type_info) ||
	 type_match_type == typeid(abi::__pointer_type_info)) &&
	type_throw_type != type_match_type) {
#ifdef CPP_DIAGNOSTICS
      printf("__cxa_type_match: failed (mixed ptr/non-ptr)\n");
#endif
      return ctm_failed;
    }

    // Both are pointers or neither is
    if (type_throw_type == typeid(abi::__pointer_type_info)) {
      // Both are pointers
#ifdef CPP_DIAGNOSTICS
      printf("__cxa_type_match: throwing a ptr\n");
#endif
      pointer_depth++;
      // Check match_type is at least as CV-qualified as throw_type
      unsigned int match_quals = CV_quals_of_pointee(match_type);
      unsigned int throw_quals = CV_quals_of_pointee(throw_type);
      if (any_qualifier_missing(match_quals, throw_quals)) {
#ifdef CPP_DIAGNOSTICS
	printf("__cxa_type_match: failed (missing qualifiers)\n");
#endif
	return ctm_failed;
      }
      // If the match type has additional qualifiers not found in the
      // throw type, any previous qualifiers must have included const
      if (any_qualifier_missing(throw_quals, match_quals) &&
	  !previous_qualifiers_include_const) {
#ifdef CPP_DIAGNOSTICS
	printf("__cxa_type_match: failed (not all qualifiers have const)\n");
#endif
	return ctm_failed;
      }
      if (!is_const(match_quals))
	previous_qualifiers_include_const = false;
      throw_type = ((const abi::__pbase_type_info *)throw_type)->__pointee;
      match_type = ((const abi::__pbase_type_info *)match_type)->__pointee;
      if (current_objectp != NULL)
        current_objectp = *(void **)current_objectp;
      continue;
    }

    // Neither is a pointer now but qualification conversion has been done.
    // See if pointer conversion on the original was possible.
    // T* will match void*

    if (pointer_depth == 1 && *match_type == typeid(void)) {
      if (is_reference_type) {
#ifdef CPP_DIAGNOSTICS
        printf("__cxa_type_match: failed (void *&)\n");
#endif
        return ctm_failed;
      } else {
        *matched_objectpp = original_objectp;
#ifdef CPP_DIAGNOSTICS
        printf("__cxa_type_match: success (conversion to void *)\n");
#endif
        return ctm_succeeded;
      }
    }

    // Else if we have 2 (different) class types, a derived class is matched by a
    // non-ambiguous public base class (perhaps not a leftmost one) and a
    // pointer to a derived class is matched by a non-reference pointer to
    // non-ambiguous public base class (perhaps not a leftmost one).
    // __si_class_type_info and __vmi_class_type_info are classes with bases.

    void *matched_base_p;

    if ((pointer_depth == 0 || (pointer_depth == 1 && !is_reference_type)) &&
	(type_throw_type == typeid(abi::__si_class_type_info) ||
	 type_throw_type == typeid(abi::__vmi_class_type_info))) {
      if (DERIVED_TO_BASE_CONVERSION(current_objectp, &matched_base_p,
				     throw_type, match_type)) {
#ifdef CPP_DIAGNOSTICS
	printf("__cxa_type_match: success (matched base 0x%x of 0x%x%s, thrown object 0x%x)\n",
	       matched_base_p, current_objectp,
	       pointer_depth == 0 ? "" : " via ptr",
	       original_objectp);
#endif
        *matched_objectpp = matched_base_p;
        return pointer_depth == 0 ? ctm_succeeded : ctm_succeeded_with_ptr_to_base;
      } else {
#ifdef CPP_DIAGNOSTICS
	printf("__cxa_type_match: failed (derived to base failed or ref to base pointer)\n");
#endif
	return ctm_failed;
      }
    }

#ifdef CPP_DIAGNOSTICS
    printf("__cxa_type_match: failed (types simply differ)\n");
#endif
    return ctm_failed;
  } /* for */
}


/* For debugging purposes: */
#ifdef DEBUG
extern "C" bool debug__cxa_type_match(void *objptr,
				      const type_info *throw_type,
				      const type_info *catch_type,
				      void **matched_objectpp)
{
  /* Create enough of an exception object that the type-matcher can run, then
   * check the type. Objptr is expected to be the result of a call to
   * __cxa_allocate_exception, which has then been copy-constructed.
   */
  __cxa_exception *e = ((__cxa_exception *)objptr) - 1;
  e->exceptionType = throw_type;
  return __cxa_type_match(&e->ucb, catch_type, false, matched_objectpp);
}
#endif


#endif /* arm_exceptions_throw_c */
#ifdef arm_exceptions_rethrow_c

/* Redeclare _Unwind_RaiseException as weak (if WEAKDECL is defined
 * appropriately) so the use from __cxa_rethrow does not on its own
 * force the unwind library to be loaded.
 */

extern "C" WEAKDECL _Unwind_Reason_Code _Unwind_RaiseException(UCB *ucbp);

#pragma exceptions_unwind

EXPORT_C void __cxa_rethrow(void)
{
  // Recover the exception object - it is the most recent caught exception object
  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep = g->caughtExceptions;
  bool foreign;

  // Must call terminate here if no such exception
  if (ep == NULL) NAMES::call_terminate_handler(NULL);

  UCB *ucbp = &ep->ucb;

  // Mark the object as being propagated by throw, preventing multiple
  // propagation and also permitting __cxa_end_catch to do the right
  // thing when it is called from the handler's cleanup.

  ep->propagationCount++;

  // Now reraise, taking care with foreign exceptions

  foreign = NAMES::is_foreign_exception(ucbp);
  if (foreign) {
    // Indirect through the intermediate object to the foreign ucb
    ucbp = (UCB *)ep->exceptionType;
  } else {
    // Increment the uncaught C++ exceptions count
    g->uncaughtExceptions++;
  }

  // Tell debugger what's happening

  DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_STARTING, foreign ? NULL : ep->exceptionType);

  // Initiate unwinding - if we get control back, call C++ routine terminate()

  _Unwind_RaiseException(ucbp);

#ifdef CPP_DIAGNOSTICS
  printf("__cxa_rethrow: throw failed\n");
#endif

  __cxa_call_terminate(ucbp);
}

#endif /* arm_exceptions_rethrow_c */
#ifdef arm_exceptions_foreign_c

/* During catch and cleanup, foreign exception objects are dealt with using
 * an intermediate __cxa_exception block in the appropriate exceptions
 * chain. This block has the same exception_class as the real foreign
 * ucb, and points to the real ucb via the intermediate block's exceptionType
 * field. This helper function checks whether it has been passed such an
 * intermediate block and sets one up if not. Only call it when the UCB
 * is known to belong to a foreign exception.
 */

__cxa_exception *NAMES::get_foreign_intermediary(__cxa_exception *head_ep, UCB *ucbp)
{
  if (head_ep != NULL) {
    UCB *head_ucbp = &head_ep->ucb;
    if (NAMES::same_exceptions_class(&head_ucbp->exception_class, &ucbp->exception_class) &&
	(UCB *)head_ep->exceptionType == ucbp)
      return head_ep;
  }

  // Create an intermediate block. Only initialise as much as necessary
  __cxa_exception *ep = ((__cxa_exception *)__cxa_allocate_exception(0)) - 1;
  UCB *new_ucbp = &ep->ucb;
  memcpy(new_ucbp->exception_class, ucbp->exception_class, EXCEPTIONS_CLASS_SIZE);
  ep->propagationCount = 0;                     // Not propagating
  ep->handlerCount = 0;                         // Not handled
  ep->nextCaughtException = NULL;               // Not in chain
  ep->exceptionType = (const type_info *)ucbp;  // The foreign UCB
  return ep;
}


#endif /* arm_exceptions_foreign_c */
#ifdef arm_exceptions_cleanup_c

EXPORT_C bool __cxa_begin_cleanup(UCB *ucbp)
{
  // Indicate that a cleanup is about to start.
  // Save the exception pointer over the cleanup for recovery later, using a chain.
  // If we allowed the exception to be rethrown in a cleanup, then
  // the object might appear multiple times at the head of this chain,
  // and the propagationCount could be used to track this - at this point,
  // the object is logically in the chain propagationCount-1 times, and
  // physically 0 or 1 times. Thus if propagationCount == 1 we should insert
  // it physically. A similar rule is used for physical removal in
  //__cxa_end_cleanup.
  // Foreign exceptions are handled via an intermediate __cxa_exception object
  // in a similar way as __cxa_begin_catch.

  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep;

  if (NAMES::is_foreign_exception(ucbp)) {
	// coverity[alloc_fn] coverity[var_assign]
    ep = NAMES::get_foreign_intermediary(g->propagatingExceptions, ucbp);
    ep->propagationCount++;  // Indicate one (or one additional) propagation
  } else {
    ep = ucbp_to_ep(ucbp);
  }

  if (ep->propagationCount == 1) {
    // Insert into chain
    ep->nextPropagatingException = g->propagatingExceptions;
    g->propagatingExceptions = ep;
  }
  // coverity[leaked_storage]
  return true;
}


// Helper function for __cxa_end_cleanup

extern "C" UCB * __ARM_cxa_end_cleanup(void)
{
  // Recover and return the currently propagating exception (from the
  // head of the propagatingExceptions chain).
  // propagationCount at this moment is a logical count of how many times the
  // item is in the chain so physically unchain it when this count is 1.
  // Foreign exceptions use an intermediary.

  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep = g->propagatingExceptions;

  if (ep == NULL) terminate();

  UCB *ucbp = &ep->ucb;
  if (NAMES::is_foreign_exception(ucbp)) {
    // Get the foreign ucb
    ucbp = (UCB *)ep->exceptionType;
    if (ep->propagationCount == 1) {
      // Free the intermediate ucb (see description in __cxa_begin_catch)
      void *eop = (void *)(ep + 1);
      g->propagatingExceptions = ep->nextPropagatingException;
      __cxa_free_exception(eop);
    } else {
      ep->propagationCount--;
    }
  } else {
    // Not foreign
    if (ep->propagationCount == 1) { // logically in chain once - so unchain
      g->propagatingExceptions = ep->nextPropagatingException;
    }
  }
  return ucbp;
}

// __cxa_end_cleanup is called at the end of a cleanup fragment.
// It must do the C++ housekeeping, then call _Unwind_Resume, but it must
// damage no significant registers in the process.

EXPORT_C __asm void __cxa_end_cleanup(void) {
  extern __ARM_cxa_end_cleanup;
  extern _Unwind_Resume WEAKASMDECL;

#ifdef __thumb
  preserve8;                   // This is preserve8 (ARM assembler heuristics are inadequate)
  push {r1-r7};
  mov r2, r8;
  mov r3, r9;
  mov r4, r10;
  mov r5, r11;
  push {r1-r5};
  bl __ARM_cxa_end_cleanup;    // returns UCB address in r0
  pop {r1-r5};
  mov r8, r2;
  mov r9, r3;
  mov r10, r4;
  mov r11, r5;
  pop {r1-r7};
  bl _Unwind_Resume;           // won't return
#else
  stmfd r13!, {r1-r12}
  bl __ARM_cxa_end_cleanup;    // returns UCB address in r0
  ldmia r13!, {r1-r12};
  b _Unwind_Resume;            // won't return
#endif
}


#endif /* arm_exceptions_cleanup_c */
#ifdef arm_exceptions_catchsemantics_c

/* Update date structures as if catching an object.
 * Call this from __cxa_begin_catch when actually catching an object,
 * and from external_exception_termination when called by a foreign runtime
 * after one of our objects was caught.
 */

void NAMES::eh_catch_semantics(UCB *ucbp)
{
  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep;

  if (NAMES::is_foreign_exception(ucbp)) {
    // Foreign exception. Get the associated intermediary block or
    // make one if there isn't one already.
    // In the case of a rethrow, the foreign object may already be on
    // the handled exceptions chain (it will be first).
	// coverity[alloc_fn] coverity[var_assign]
    ep = NAMES::get_foreign_intermediary(g->caughtExceptions, ucbp);
  } else {
    // Not foreign
    ep = ucbp_to_ep(ucbp);
    // Decrement the propagation count
    ep->propagationCount--;
    // Decrement the total uncaught C++ exceptions count
    g->uncaughtExceptions--;
  }

  // Common code for our EO's, and foreign ones where we work on the intermediate EO

  // Increment the handler count for this exception object
  ep->handlerCount++;

  // Push the ep onto the "handled exceptions" chain if it is not already there.
  // (If catching a rethrow, it may already be there)

  if (ep->nextCaughtException == NULL) {
    ep->nextCaughtException = g->caughtExceptions;
    g->caughtExceptions = ep;
  }
  // coverity[leaked_storage]
}


#endif /* arm_exceptions_catchsemantics_c */
#ifdef arm_exceptions_getexceptionptr_c

EXPORT_C void *__cxa_get_exception_ptr(UCB *ucbp)
{
  return (void *)ucbp->barrier_cache.bitpattern[BARRIER_HANDLEROBJECT]; // The matched object, if any
}


#endif /* arm_exceptions_getexceptionptr_c */
#ifdef arm_exceptions_begincatch_c

void *__cxa_begin_catch(UCB *ucbp)
{
  void *match = (void *)ucbp->barrier_cache.bitpattern[BARRIER_HANDLEROBJECT]; // The matched object, if any

  // Update the data structures

  NAMES::eh_catch_semantics(ucbp);

  // Tell the unwinder the exception propagation has finished,
  // and return the object pointer

  _Unwind_Complete(ucbp);
  return match;
}


#endif /* arm_exceptions_begincatch_c */
#ifdef arm_exceptions_endcatch_c

#pragma exceptions_unwind

EXPORT_C void __cxa_end_catch(void)
{
  // Recover the exception object - it is the most recent caught exception object
  __cxa_eh_globals *g = __cxa_get_globals();
  __cxa_exception *ep = g->caughtExceptions;

  if (ep == NULL) terminate();

  // Rethrow in progress?

  bool object_being_rethrown = ep->propagationCount != 0;

  // Decrement the handler count for this exception object
  ep->handlerCount--;

  // Unstack the object if it is no longer being handled anywhere.
  // Destroy and free the object if it is no longer alive -
  // it is dead if its handler count becomes 0, unless it is
  // about to be rethrown.
  // If the dtor throws, allow its exception to propagate.
  // Do different things if it is a foreign exception object.

  if (ep->handlerCount == 0) {
    void *eop = (void *)(ep + 1);
    UCB *ucbp = &ep->ucb;
    bool foreign = NAMES::is_foreign_exception(ucbp);

    // Unstack it from the caught exceptions stack - it is guaranteed to be top item.
    g->caughtExceptions = ep->nextCaughtException;

    if (foreign) {
      // Get the foreign ucb and free the intermediate ucb (see description in __cxa_begin_catch)
      ucbp = (UCB *)ep->exceptionType;
      __cxa_free_exception(eop);
    } else {
      ep->nextCaughtException = NULL;  // So __cxa_begin_catch knows it isn't in the chain
    }

    // Now destroy the exception object if it's no longer needed
    if (!object_being_rethrown) {
      if (foreign) {

	// Notify the foreign language, if it so requested
	if (ucbp->exception_cleanup != NULL)
	  (ucbp->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, ucbp);

      } else {

        // One of our objects: do C++-specific semantics

	if (ep->exceptionDestructor != NULL) {
	  // Run the dtor. If it throws, free the memory anyway and
	  // propagate the new exception.
#ifdef ARM_EXCEPTIONS_ENABLED
	  try {
	    (ep->exceptionDestructor)(eop);
	  } catch(...) {
	    // Free the memory and reraise
	    __cxa_free_exception(eop);
	    throw;
	  }
#else
	  (ep->exceptionDestructor)(eop);
#endif
	}
	// Dtor (if there was one) didn't throw. Free the memory.
	__cxa_free_exception(eop);
      }  // !foreign
    }  // !object_being_rethrown
  }  // ep->handlerCount == 0
}


#endif /* arm_exceptions_endcatch_c */
#ifdef arm_exceptions_bad_typeid_c

#pragma exceptions_unwind

EXPORT_C void __cxa_bad_typeid(void)
{
  throw std::bad_typeid();
}


#endif /* arm_exceptions_bad_typeid_c */
#ifdef arm_exceptions_bad_cast_c

#pragma exceptions_unwind

EXPORT_C void __cxa_bad_cast(void)
{
  throw std::bad_cast();
}


#endif /* arm_exceptions_bad_cast_c */