diff -r 000000000000 -r a41df078684a kernel/eka/compsupp/symaehabi/cppsemantics.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/compsupp/symaehabi/cppsemantics.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1402 @@ +/* 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 + +// Environment: +#include "unwind_env.h" +// Language-independent unwinder declarations: +#include "unwinder.h" + +#ifdef __EPOC32__ +/* Symbian specific support */ +#include "symbian_support.h" +#endif + +#include + +/* 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 */