sl@0: /* The C++ exceptions runtime support sl@0: * sl@0: * Copyright 2002-2003 ARM Limited. sl@0: */ sl@0: /* sl@0: Licence sl@0: sl@0: 1. Subject to the provisions of clause 2, ARM hereby grants to LICENSEE a sl@0: perpetual, non-exclusive, nontransferable, royalty free, worldwide licence sl@0: to use this Example Implementation of Exception Handling solely for the sl@0: purpose of developing, having developed, manufacturing, having sl@0: manufactured, offering to sell, selling, supplying or otherwise sl@0: distributing products which comply with the Exception Handling ABI for the sl@0: ARM Architecture specification. All other rights are reserved to ARM or its sl@0: licensors. sl@0: sl@0: 2. THIS EXAMPLE IMPLEMENTATION OF EXCEPTION HANDLING IS PROVIDED "AS IS" sl@0: WITH NO WARRANTIES EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED sl@0: TO ANY WARRANTY OF SATISFACTORY QUALITY, MERCHANTABILITY, NONINFRINGEMENT sl@0: OR FITNESS FOR A PARTICULAR PURPOSE. sl@0: */ sl@0: /* sl@0: * RCS $Revision: 1.29.2.1 $ sl@0: * Checkin $Date: 2004/01/20 15:11:16 $ sl@0: * Revising $Author: achapman $ sl@0: */ sl@0: sl@0: /* This source file is compiled automatically by ARM's make system into sl@0: * multiple object files. The source regions constituting object file sl@0: * xxx.o are delimited by ifdef xxx_c / endif directives. sl@0: * sl@0: * The source regions currently marked are: sl@0: * arm_exceptions_globs_c sl@0: * arm_exceptions_mem_c sl@0: * arm_exceptions_uncaught_c sl@0: * arm_exceptions_terminate_c sl@0: * arm_exceptions_setterminate_c sl@0: * arm_exceptions_unexpected_c sl@0: * arm_exceptions_setunexpected_c sl@0: * arm_exceptions_support_c sl@0: * arm_exceptions_callterm_c sl@0: * arm_exceptions_callunex_c sl@0: * arm_exceptions_currenttype_c sl@0: * arm_exceptions_alloc_c sl@0: * arm_exceptions_free_c sl@0: * arm_exceptions_throw_c sl@0: * arm_exceptions_rethrow_c sl@0: * arm_exceptions_foreign_c sl@0: * arm_exceptions_cleanup_c sl@0: * arm_exceptions_begincatch_c sl@0: * arm_exceptions_endcatch_c sl@0: * arm_exceptions_bad_typeid_c sl@0: * arm_exceptions_bad_cast_c sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: // Environment: sl@0: #include "unwind_env.h" sl@0: // Language-independent unwinder declarations: sl@0: #include "unwinder.h" sl@0: sl@0: /* By default, none of these routines are unwindable: */ sl@0: #pragma noexceptions_unwind sl@0: sl@0: /* For brevity: */ sl@0: sl@0: typedef _Unwind_Control_Block UCB; sl@0: sl@0: using std::terminate_handler; sl@0: using std::unexpected_handler; sl@0: using std::terminate; sl@0: using std::unexpected; sl@0: using std::type_info; sl@0: sl@0: /* Redeclare these interface routines as weak, so using them does not sl@0: * pull in the unwind library. We only want the unwind library if sl@0: * someone throws (or raises an exception from some other language). sl@0: */ sl@0: WEAKDECL NORETURNDECL void _Unwind_Resume(UCB *); sl@0: WEAKDECL void _Unwind_Complete(UCB *); sl@0: sl@0: /* Diagnostics: sl@0: * Define DEBUG to get extra interfaces which assist debugging this functionality. sl@0: * Define PRINTED_DIAGNOSTICS for printed diagnostics. sl@0: */ sl@0: #ifdef DEBUG sl@0: #define PRINTED_DIAGNOSTICS sl@0: #endif sl@0: sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: extern "C" int printf(const char *, ...); sl@0: #endif sl@0: sl@0: /* --------- "Exceptions_class" string for our implementation: --------- */ sl@0: sl@0: #define EXCEPTIONS_CLASS_SIZE 8 sl@0: #define ARMCPP_EXCEPTIONS_CLASS "ARM\0C++\0" sl@0: sl@0: sl@0: /* --------- Exception control object: --------- */ sl@0: sl@0: // Type __cxa_exception is the combined C++ housekeeping (LEO) and UCB. sl@0: // It will be followed by the user exception object, hence must ensure sl@0: // the latter is aligned on an 8 byte boundary. sl@0: sl@0: struct __cxa_exception { sl@0: const type_info *exceptionType; // RTTI object describing the type of the exception sl@0: void *(*exceptionDestructor)(void *); // Destructor for the exception object (may be NULL) sl@0: unexpected_handler unexpectedHandler; // Handler in force after evaluating throw expr sl@0: terminate_handler terminateHandler; // Handler in force after evaluating throw expr sl@0: __cxa_exception *nextCaughtException; // Chain of "currently caught" c++ exception objects sl@0: uint32_t handlerCount; // Count of how many handlers this EO is "caught" in sl@0: __cxa_exception *nextPropagatingException; // Chain of objects saved over cleanup sl@0: uint32_t propagationCount; // Count of live propagations (throws) of this EO sl@0: UCB ucb; // Forces alignment of next item to 8-byte boundary sl@0: }; sl@0: sl@0: sl@0: /* --------- Control "globals": --------- */ sl@0: sl@0: // We do this by putting all the thread-specific "globals" into a single sl@0: // area of store, which we allocate space for dynamically. sl@0: // We don't define a constructor for this; see comments with __cxa_get_globals. sl@0: sl@0: typedef void (*handler)(void); sl@0: sl@0: struct __cxa_eh_globals { sl@0: uint32_t uncaughtExceptions; // counter sl@0: unexpected_handler unexpectedHandler; // per-thread handler sl@0: terminate_handler terminateHandler; // per-thread handler sl@0: bool implementation_ever_called_terminate; // true if it ever did sl@0: handler call_hook; // transient field to tell terminate/unexpected which hook to call sl@0: __cxa_exception *caughtExceptions; // chain of "caught" exceptions sl@0: __cxa_exception *propagatingExceptions; // chain of "propagating" (in cleanup) exceptions sl@0: void *emergency_buffer; // emergency buffer for when rest of heap full sl@0: }; sl@0: sl@0: sl@0: /* ---------- Entry points: ---------- */ sl@0: sl@0: /* There is a little type-delicacy required here as __cxa_throw takes a sl@0: * function pointer. Setting aside the problem of not being able to form sl@0: * a pointer to a destructor in C++, if we simply say extern "C" here sl@0: * then the function pointer will also have C linkage and will be a sl@0: * pointer to a C function. This causes problems when __cxa_throw is sl@0: * defined (unless we repeat the extern "C" at the definition site) because sl@0: * the fnptr in the definition gets C++ linkage, hence that __cxa_throw has sl@0: * a different signature to the declared one, and so the function we wanted sl@0: * doesn't get defined at all. sl@0: * Maybe it should just take a void * but this seems more honest. sl@0: */ sl@0: sl@0: typedef void *(*cppdtorptr)(void *); sl@0: sl@0: extern "C" { sl@0: sl@0: // Protocol routines called directly from application code sl@0: sl@0: void *__cxa_allocate_exception(size_t size); sl@0: void __cxa_free_exception(void *); sl@0: WEAKDECL void __cxa_throw(void *, const type_info *, cppdtorptr); sl@0: void __cxa_rethrow(void); sl@0: void *__cxa_begin_catch(UCB *); sl@0: void __cxa_end_catch(void); sl@0: void __cxa_end_cleanup(void); sl@0: const type_info *__cxa_current_exception_type(void); sl@0: sl@0: // Protocol routines usually called only by the personality routine(s). sl@0: sl@0: void __cxa_call_terminate(UCB *); sl@0: void __cxa_call_unexpected(UCB *); sl@0: bool __cxa_begin_cleanup(UCB *); sl@0: bool __cxa_type_match(UCB *, const std::type_info *, void **); sl@0: sl@0: // Auxilliary routines sl@0: sl@0: __cxa_eh_globals *__cxa_get_globals(void); sl@0: void __cxa_bad_typeid(void); sl@0: void __cxa_bad_cast(void); sl@0: sl@0: // Emergency memory buffer management routines sl@0: sl@0: void *__ARM_exceptions_buffer_init(void); sl@0: void *__ARM_exceptions_buffer_allocate(void *, size_t); sl@0: void *__ARM_exceptions_buffer_free(void *, void *); sl@0: } sl@0: sl@0: sl@0: // Support routines sl@0: sl@0: #define NAMES __ARM sl@0: namespace NAMES { sl@0: void default_unexpected_handler(void); sl@0: void call_terminate_handler(UCB *); sl@0: void eh_catch_semantics(UCB *); sl@0: bool is_foreign_exception(UCB *); sl@0: bool same_exceptions_class(const void *, const void *); sl@0: __cxa_exception *get_foreign_intermediary(__cxa_exception *, UCB *); sl@0: } sl@0: sl@0: // Macro: convert ucb pointer to __cxa_exception pointer sl@0: sl@0: #define ucbp_to_ep(UCB_P) ((__cxa_exception *)((char *)(UCB_P) - offsetof(__cxa_exception, ucb))) sl@0: sl@0: sl@0: #ifdef arm_exceptions_globs_c sl@0: sl@0: /* --------- Allocating and retrieving "globals": --------- */ sl@0: sl@0: // The exception-handling globals should be allocated per-thread. sl@0: // This is done here assuming the existance of a zero-initialised void* sl@0: // pointer location obtainable by the macro EH_GLOBALS. sl@0: sl@0: // Default terminate handler: sl@0: sl@0: static void __default_terminate_handler(void) { sl@0: abort(); sl@0: } sl@0: sl@0: // If std::unexpected() is in the image, include a default handler for it: sl@0: namespace NAMES { WEAKDECL void default_unexpected_handler(void); } sl@0: sl@0: // If this symbol is present, allocate an emergency buffer. sl@0: // As we aren't allowed static data, make it a function sl@0: extern "C" WEAKDECL void __ARM_exceptions_buffer_required(void); sl@0: sl@0: sl@0: // __cxa_eh_globals returns the per-thread memory. There are several complications, sl@0: // all of which relate to not touching the exceptions system while trying to sl@0: // initialise it: sl@0: // 1) We can't obtain memory by calling new or nothrow new as both of these use sl@0: // exceptions internally, so we must use malloc sl@0: // 2) We choose not to initialise the memory via placement new and a constructor, sl@0: // since placement new is declared with an empty function exception specification, sl@0: // which causes more of the exceptions system to always be pulled in. sl@0: // 3) We can't call terminate, as terminate looks in the memory we are trying to sl@0: // allocate. sl@0: sl@0: __cxa_eh_globals *__cxa_get_globals(void) sl@0: { sl@0: __cxa_eh_globals *this_thread_globals = (__cxa_eh_globals *)(EH_GLOBALS); sl@0: if (this_thread_globals == NULL) { sl@0: sl@0: // First call sl@0: // Obtain some memory: this is thread-safe provided malloc is. sl@0: this_thread_globals = (__cxa_eh_globals *)malloc(sizeof(__cxa_eh_globals)); sl@0: if (this_thread_globals == NULL) abort(); // NOT terminate(), which calls this fn sl@0: sl@0: // Save the pointer in the specially-provided location sl@0: EH_GLOBALS = this_thread_globals; sl@0: sl@0: // Finally initialise the memory by hand sl@0: this_thread_globals->uncaughtExceptions = 0; sl@0: this_thread_globals->unexpectedHandler = NAMES::default_unexpected_handler; sl@0: this_thread_globals->terminateHandler = __default_terminate_handler; sl@0: this_thread_globals->implementation_ever_called_terminate = false; sl@0: this_thread_globals->call_hook = NULL; sl@0: this_thread_globals->caughtExceptions = NULL; sl@0: this_thread_globals->propagatingExceptions = NULL; sl@0: if (&__ARM_exceptions_buffer_required == NULL) sl@0: this_thread_globals->emergency_buffer = NULL; sl@0: else sl@0: this_thread_globals->emergency_buffer = __ARM_exceptions_buffer_init(); sl@0: } sl@0: sl@0: return this_thread_globals; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_globs_c */ sl@0: #ifdef arm_exceptions_mem_c sl@0: sl@0: /* --------- Emergency memory: --------- */ sl@0: sl@0: // It is possible to reserve memory for throwing bad_alloc when the heap sl@0: // is otherwise full. The ARM implementation provides hooks to do this. sl@0: // The default implementation reserves just enough space for a bad_alloc sl@0: // object, so if memory is later exhausted bad_alloc can still be thrown. sl@0: // Note there is no guarantee or requirement that the exception being sl@0: // thrown is actually bad_alloc. sl@0: sl@0: // A usage flag and enough space for a bad_alloc exception control object sl@0: sl@0: struct emergency_eco { sl@0: __cxa_exception ep; sl@0: std::bad_alloc b; sl@0: }; sl@0: sl@0: struct emergency_buffer { sl@0: bool inuse; sl@0: struct emergency_eco eco; sl@0: }; sl@0: sl@0: // Initialiser sl@0: void* __ARM_exceptions_buffer_init(void) sl@0: { sl@0: emergency_buffer *buffer = (emergency_buffer *)malloc(sizeof(emergency_buffer)); sl@0: if (buffer == NULL) return NULL; sl@0: buffer->inuse = false; sl@0: return buffer; sl@0: } sl@0: sl@0: // Allocator sl@0: void *__ARM_exceptions_buffer_allocate(void *buffer, size_t size) sl@0: { sl@0: emergency_buffer *b = (emergency_buffer *)buffer; sl@0: if (size > sizeof(emergency_eco) || b == NULL || b->inuse) return NULL; sl@0: b->inuse = true; sl@0: return &b->eco; sl@0: } sl@0: sl@0: // Deallocator: Must return non-NULL if and only if it recognises sl@0: // and releases the supplied object sl@0: void *__ARM_exceptions_buffer_free(void *buffer, void *addr) sl@0: { sl@0: emergency_buffer *b = (emergency_buffer *)buffer; sl@0: if (b == NULL || addr != &b->eco) return NULL; sl@0: b->inuse = false; sl@0: return b; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_mem_c */ sl@0: #ifdef arm_exceptions_uncaught_c sl@0: sl@0: /* ---- uncaught_exception() ---- */ sl@0: sl@0: /* The EDG (and I think our) interpretation is that if the implementation sl@0: * ever called terminate(), uncaught_exception() should return true. sl@0: */ sl@0: sl@0: bool std::uncaught_exception(void) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: return g->implementation_ever_called_terminate || g->uncaughtExceptions; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_uncaught_c */ sl@0: #ifdef arm_exceptions_terminate_c sl@0: sl@0: /* ---- terminate() etc ---- */ sl@0: sl@0: /* The behaviour of terminate() must differ between calls by the sl@0: * implementation and calls by the application. This is achieved by having the sl@0: * implementation set call_hook immediately before the call to terminate(). sl@0: * The hook called by terminate() should terminate the program without sl@0: * returning to the caller. There is no requirement for terminate() itself to sl@0: * intercept throws. sl@0: */ sl@0: sl@0: void std::terminate(void) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: sl@0: if (g->call_hook != NULL) { sl@0: // Clear then call hook fn we were passed sl@0: handler call_hook = g->call_hook; sl@0: g->call_hook = NULL; sl@0: call_hook(); sl@0: } else { sl@0: // Call global hook fn sl@0: g->terminateHandler(); sl@0: } sl@0: // If hook fn returns: sl@0: abort(); sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_terminate_c */ sl@0: #ifdef arm_exceptions_setterminate_c sl@0: sl@0: terminate_handler std::set_terminate(terminate_handler h) throw() sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: terminate_handler old = g->terminateHandler; sl@0: g->terminateHandler = h; sl@0: return old; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_setterminate_c */ sl@0: #ifdef arm_exceptions_unexpected_c sl@0: sl@0: /* ---- unexpected() etc ---- */ sl@0: /* Comments as per terminate() */ sl@0: sl@0: void NAMES::default_unexpected_handler(void) { sl@0: terminate(); sl@0: } sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void std::unexpected(void) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: sl@0: if (g->call_hook != NULL) { sl@0: // Clear then call hook fn we were passed sl@0: handler call_hook = g->call_hook; sl@0: g->call_hook = NULL; sl@0: call_hook(); sl@0: } else { sl@0: // Call global hook fn sl@0: g->unexpectedHandler(); sl@0: } sl@0: sl@0: // If hook fn returns: sl@0: abort(); sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_unexpected_c */ sl@0: #ifdef arm_exceptions_setunexpected_c sl@0: sl@0: unexpected_handler std::set_unexpected(unexpected_handler h) throw() sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: unexpected_handler old = g->unexpectedHandler; sl@0: g->unexpectedHandler = h; sl@0: return old; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_setunexpected_c */ sl@0: #ifdef arm_exceptions_support_c sl@0: sl@0: /* ---------- Helper functions: ---------- */ sl@0: sl@0: /* Two routines to determine whether two exceptions objects share a layout. sl@0: * This is determined by checking whether the UCB exception_class members sl@0: * are identical. sl@0: * In principle we could use memcmp to perform this check (the code is sl@0: * given below) but the check is quite frequent and so that is costly. sl@0: * Therefore for efficiency we make use of the fact that the UCB is sl@0: * word aligned, that the exception_class member is consequently sl@0: * word aligned within it, and that we know the size of the member. sl@0: * We take care elsewhere to only ever call the routines with pointers sl@0: * to word-aligned addresses. sl@0: */ sl@0: sl@0: #if 0 sl@0: sl@0: // Straightforward versions sl@0: sl@0: bool NAMES::same_exceptions_class(const void *ec1, const void *ec2) sl@0: { sl@0: return memcmp(ec1, ec2, EXCEPTIONS_CLASS_SIZE) == 0; // identical sl@0: } sl@0: sl@0: // One of our exception objects, or not? sl@0: sl@0: bool NAMES::is_foreign_exception(UCB *ucbp) sl@0: { sl@0: return !NAMES::same_exceptions_class(&ucbp->exception_class, ARMCPP_EXCEPTIONS_CLASS); sl@0: } sl@0: sl@0: #else sl@0: sl@0: // Faster versions sl@0: sl@0: bool NAMES::same_exceptions_class(const void *ec1, const void *ec2) sl@0: { sl@0: uint32_t *ip1 = (uint32_t *)ec1; sl@0: uint32_t *ip2 = (uint32_t *)ec2; sl@0: return ip1[0] == ip2[0] && ip1[1] == ip2[1]; sl@0: } sl@0: sl@0: // One of our exception objects, or not? sl@0: sl@0: bool NAMES::is_foreign_exception(UCB *ucbp) sl@0: { sl@0: // Need a word-aligned copy of the string sl@0: static const union { sl@0: const char s[EXCEPTIONS_CLASS_SIZE+1]; int dummy; sl@0: } is_foreign_exception_static = {ARMCPP_EXCEPTIONS_CLASS}; sl@0: return !NAMES::same_exceptions_class(&ucbp->exception_class, &is_foreign_exception_static.s); sl@0: } sl@0: sl@0: #endif sl@0: sl@0: sl@0: #endif /* arm_exceptions_support_c */ sl@0: #ifdef arm_exceptions_callterm_c sl@0: sl@0: /* When the implementation wants to call terminate(), do the following: sl@0: * Mark the object as "caught" so it can be rethrown. sl@0: * Set the hook function for terminate() to call; sl@0: * Mark the fact that terminate() has been called by the implementation; sl@0: * We have to be careful - the implementation might encounter an error while sl@0: * unwinding a foreign exception, and also it is possible this might be sl@0: * called after failing to obtain a ucb. sl@0: */ sl@0: sl@0: void NAMES::call_terminate_handler(UCB *ucbp) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: sl@0: if (ucbp == NULL) { sl@0: // Call global hook sl@0: g->call_hook = g->terminateHandler; sl@0: } else { sl@0: // Extract the hook to call sl@0: if (NAMES::is_foreign_exception(ucbp)) { sl@0: // Someone else's sl@0: g->call_hook = g->terminateHandler; // best we can do under the circumstances sl@0: } else { sl@0: // One of ours sl@0: __cxa_exception *ep = ucbp_to_ep(ucbp); sl@0: g->call_hook = ep->terminateHandler; // the one in force at the point of throw sl@0: } sl@0: } sl@0: sl@0: g->implementation_ever_called_terminate = true; sl@0: terminate(); sl@0: // never returns sl@0: } sl@0: sl@0: sl@0: void __cxa_call_terminate(UCB *ucbp) sl@0: { sl@0: if (ucbp != NULL) // Record entry to (implicit) handler sl@0: __cxa_begin_catch(ucbp); sl@0: sl@0: NAMES::call_terminate_handler(ucbp); sl@0: // never returns sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_callterm_c */ sl@0: #ifdef arm_exceptions_callunex_c sl@0: sl@0: /* When the implementation wants to call unexpected(), do the following: sl@0: * Mark the object as "caught" so it can be rethrown. sl@0: * Set the hook function for unexpected() to call; sl@0: * Call unexpected and trap any throw to make sure it is acceptable. sl@0: * We have to be careful - the implementation might encounter an error while sl@0: * unwinding a foreign exception. sl@0: */ sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_call_unexpected(UCB *ucbp) sl@0: { sl@0: sl@0: // Extract data we will need from the barrier cache before sl@0: // anyone has a chance to overwrite it sl@0: sl@0: uint32_t rtti_count = ucbp->barrier_cache.bitpattern[1]; sl@0: uint32_t base = ucbp->barrier_cache.bitpattern[2]; sl@0: uint32_t stride = ucbp->barrier_cache.bitpattern[3]; sl@0: uint32_t rtti_offset_array_addr = ucbp->barrier_cache.bitpattern[4]; sl@0: sl@0: // Also get the globals here and the eop sl@0: sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep = ucbp_to_ep(ucbp); sl@0: sl@0: #ifdef ARM_EXCEPTIONS_ENABLED sl@0: try { sl@0: #endif sl@0: sl@0: // Record entry to (implicit) handler sl@0: sl@0: __cxa_begin_catch(ucbp); sl@0: sl@0: // Now extract the hook to call sl@0: sl@0: if (NAMES::is_foreign_exception(ucbp)) { sl@0: // Someone else's sl@0: g->call_hook = g->unexpectedHandler; // best we can do under the circumstances sl@0: } else { sl@0: // One of ours sl@0: g->call_hook = ep->unexpectedHandler; // the one in force at the point of throw sl@0: } sl@0: unexpected(); // never returns normally, but might throw something sl@0: sl@0: #ifdef ARM_EXCEPTIONS_ENABLED sl@0: } catch (...) { sl@0: sl@0: // Unexpected() threw. This requires some delicacy. sl@0: // There are 2 possibilities: sl@0: // i) rethrow of the same object sl@0: // ii) throw of a new object sl@0: // Unexpected() is an implicit handler, and we manually called sl@0: // __cxa_begin_catch on the ingoing object. We need to call sl@0: // __cxa_end_catch on that object and, if the object is no longer sl@0: // being handled (possible in case ii), this will cause its destruction. sl@0: // The wrinkle is that in case ii the object is not on top of the catch sl@0: // stack because we just caught something else. sl@0: sl@0: // Get hold of what was thrown (which we just caught). sl@0: sl@0: __cxa_exception *epnew = g->caughtExceptions; sl@0: sl@0: // Call __cxa_end_catch on the original object, taking care with the catch chain sl@0: sl@0: if (epnew == ep) { sl@0: // rethrow - easy & safe - object is at top of chain and handlercount > 1 sl@0: __cxa_end_catch(); sl@0: } else { sl@0: // not rethrow - unchain the top (new) object, clean up the next one, sl@0: // and put the top object back sl@0: sl@0: // unchain sl@0: g->caughtExceptions = epnew->nextCaughtException; sl@0: // assert g->caughtExceptions == ep now sl@0: // Decrement its handlercount (this might call a dtor if the count goes to 0, sl@0: // and the dtor might throw - if it does, just give up) sl@0: try { sl@0: __cxa_end_catch(); sl@0: } catch(...) { sl@0: terminate(); sl@0: } sl@0: // Chain back in sl@0: epnew->nextCaughtException = g->caughtExceptions; sl@0: g->caughtExceptions = epnew; sl@0: } sl@0: sl@0: // See whether what was thrown is permitted, and in passing sl@0: // see if std::bad_exception is permitted sl@0: sl@0: bool bad_exception_permitted = false; sl@0: uint32_t i; sl@0: for (i = 0; i < rtti_count; i++) { sl@0: void *matched_object; sl@0: const type_info *fnspec = (const type_info *)(*(uint32_t *)rtti_offset_array_addr + base); sl@0: if (__cxa_type_match(&(epnew->ucb), fnspec, &matched_object)) { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_call_unexpected: fnspec matched\n"); sl@0: #endif sl@0: throw; // got a match - propagate it sl@0: } sl@0: if (&typeid(std::bad_exception) == fnspec) sl@0: bad_exception_permitted = true; sl@0: rtti_offset_array_addr += stride; sl@0: } sl@0: sl@0: // There was no match... sl@0: if (bad_exception_permitted) throw std::bad_exception(); // transmute sl@0: sl@0: // Otherwise call epnew's terminate handler sl@0: NAMES::call_terminate_handler(&epnew->ucb); sl@0: } sl@0: #endif sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_callunex_c */ sl@0: #ifdef arm_exceptions_currenttype_c sl@0: sl@0: /* Yield the type of the currently handled exception, or null if none or the sl@0: * object is foreign. sl@0: */ sl@0: sl@0: const type_info *__cxa_current_exception_type(void) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep = g->caughtExceptions; sl@0: if (ep == NULL || NAMES::is_foreign_exception(&ep->ucb)) return NULL; sl@0: return ep->exceptionType; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_currenttype_c */ sl@0: #ifdef arm_exceptions_alloc_c sl@0: sl@0: /* Allocate store for controlling an exception propagation */ sl@0: sl@0: void *__cxa_allocate_exception(size_t size) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: sl@0: // Allocate store for a __cxa_exception header and the EO. sl@0: // Allocated store should be thread-safe and persistent, and must do sl@0: // something sensible if the allocation fails sl@0: sl@0: size_t total_size = size + sizeof(__cxa_exception); sl@0: __cxa_exception *ep = (__cxa_exception *)malloc(total_size); sl@0: if (ep == NULL) { sl@0: // Try the emergency memory pool sl@0: ep = (__cxa_exception *)__ARM_exceptions_buffer_allocate(g->emergency_buffer, total_size); sl@0: if (ep == NULL) NAMES::call_terminate_handler(NULL); sl@0: } sl@0: sl@0: UCB *ucbp = &ep->ucb; sl@0: sl@0: // Initialise the UCB sl@0: sl@0: memcpy(ucbp->exception_class, ARMCPP_EXCEPTIONS_CLASS, EXCEPTIONS_CLASS_SIZE); sl@0: ucbp->exception_cleanup = NULL; /* initialise properly before throwing */ sl@0: ucbp->unwinder_cache.reserved1 = 0; /* required to do this */ sl@0: sl@0: // Initialise parts of the LEO, in case copy-construction of the EO results sl@0: // in a need to call terminate (via __cxa_call_terminate) sl@0: sl@0: ep->handlerCount = 0; // Not in any handlers sl@0: ep->nextCaughtException = NULL; // Not in any handlers sl@0: ep->nextPropagatingException = NULL; // Not saved over cleanup sl@0: ep->propagationCount = 0; // Not propagating sl@0: ep->terminateHandler = g->terminateHandler; // Cache current terminate handler sl@0: ep->unexpectedHandler = g->unexpectedHandler; // Cache current unexpected handler sl@0: sl@0: // Return pointer to the EO sl@0: sl@0: return ep + 1; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_alloc_c */ sl@0: #ifdef arm_exceptions_free_c sl@0: sl@0: /* Free store allocated by __cxa_allocate_exception */ sl@0: sl@0: void __cxa_free_exception(void *eop) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: char *ep = (char *)eop - sizeof(__cxa_exception); sl@0: if (__ARM_exceptions_buffer_free(g->emergency_buffer, ep)) return; sl@0: free(ep); sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_free_c */ sl@0: #ifdef arm_exceptions_throw_c sl@0: sl@0: /* This routine is called when a foreign runtime catches one of our exception sl@0: * objects and then exits its catch by a means other than rethrow. sl@0: * We should clean it up as if we had caught it ourselves. sl@0: */ sl@0: sl@0: static void external_exception_termination(_Unwind_Reason_Code c, UCB *ucbp) sl@0: { sl@0: NAMES::eh_catch_semantics(ucbp); sl@0: __cxa_end_catch(); sl@0: } sl@0: sl@0: sl@0: /* Initiate a throw */ sl@0: sl@0: #pragma push sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_throw(void *eop, const type_info *t, cppdtorptr d) sl@0: { sl@0: __cxa_exception *ep = (__cxa_exception *)((char *)eop - sizeof(__cxa_exception)); sl@0: UCB *ucbp = &ep->ucb; sl@0: sl@0: // Initialise the remaining LEO and UCB fields not done by __cxa_allocate_exception sl@0: sl@0: ucbp->exception_cleanup = external_exception_termination; sl@0: ep->exceptionType = t; sl@0: ep->exceptionDestructor = d; sl@0: ep->propagationCount = 1; // Propagating by 1 throw sl@0: sl@0: // Increment the uncaught C++ exceptions count sl@0: sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: g->uncaughtExceptions++; sl@0: sl@0: // Tell debugger what's happening sl@0: sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_STARTING, t); sl@0: sl@0: // Initiate unwinding - if we get control back, call C++ routine terminate() sl@0: sl@0: _Unwind_RaiseException(ucbp); sl@0: sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_throw: throw failed\n"); sl@0: #endif sl@0: sl@0: __cxa_call_terminate(ucbp); sl@0: } sl@0: sl@0: #pragma pop sl@0: sl@0: /* ----- Type matching: ----- */ sl@0: sl@0: /* This is located here so that (in ARM's implementation) it is only retained in sl@0: * an image if the application itself throws. sl@0: */ sl@0: sl@0: /* Type matching functions. sl@0: * C++ DR126 says the matching rules for fnspecs are intended to be the same as sl@0: * those for catch: sl@0: * "A function is said to allow an exception of type E if its exception-specification sl@0: * contains a type T for which a handler of type T would be a match (15.3 except.handle) sl@0: * for an exception of type E." sl@0: * Thus we have a single type matching rule. sl@0: */ sl@0: sl@0: /* Helper macros: */ sl@0: sl@0: #define CV_quals_of_pointee(P) (((const abi::__pbase_type_info *)(P))->__flags & \ sl@0: (abi::__pbase_type_info::__const_mask | \ sl@0: abi::__pbase_type_info::__volatile_mask)) sl@0: sl@0: #define is_const(QUALS) (((QUALS) & abi::__pbase_type_info::__const_mask) != 0) sl@0: sl@0: #define any_qualifier_missing(TEST_QUALS, REF_QUALS) ((~(TEST_QUALS) & (REF_QUALS)) != 0) sl@0: sl@0: /* A routine is required for derived class to base class conversion. sl@0: * This is obtained via a macro definition DERIVED_TO_BASE_CONVERSION sl@0: * in unwind_env.h. sl@0: */ sl@0: sl@0: /* External entry point: sl@0: * Type check the c++ rtti object for compatibility against the type of sl@0: * the object containing the ucb. Return a pointer to the matched object sl@0: * (possibly a non-leftmost baseclass of the exception object) sl@0: */ sl@0: bool __cxa_type_match(UCB *ucbp, const type_info *match_type, void **matched_objectpp) sl@0: { sl@0: if (NAMES::is_foreign_exception(ucbp)) sl@0: return false; sl@0: sl@0: __cxa_exception *ep = ucbp_to_ep(ucbp); sl@0: const type_info *throw_type = ep->exceptionType; sl@0: bool previous_qualifiers_include_const = true; // for pointer qualification conversion sl@0: unsigned int pointer_depth = 0; sl@0: void *original_objectp = ep + 1; sl@0: void *current_objectp = original_objectp; sl@0: sl@0: for (;;) { sl@0: sl@0: // Match if identical sl@0: sl@0: if (throw_type == match_type) { sl@0: *matched_objectpp = original_objectp; sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: success (exact match after any ptrs)\n"); sl@0: #endif sl@0: return true; sl@0: } sl@0: sl@0: // Fail if one is a pointer and the other isn't sl@0: sl@0: const type_info *type_throw_type = &typeid(*throw_type); sl@0: const type_info *type_match_type = &typeid(*match_type); sl@0: sl@0: if ((type_throw_type == &typeid(abi::__pointer_type_info) || sl@0: type_match_type == &typeid(abi::__pointer_type_info)) && sl@0: type_throw_type != type_match_type) { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: failed (mixed ptr/non-ptr)\n"); sl@0: #endif sl@0: return false; sl@0: } sl@0: sl@0: // Both are pointers or neither is sl@0: if (type_throw_type == &typeid(abi::__pointer_type_info)) { sl@0: // Both are pointers sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: throwing a ptr\n"); sl@0: #endif sl@0: pointer_depth++; sl@0: // Check match_type is at least as CV-qualified as throw_type sl@0: unsigned int match_quals = CV_quals_of_pointee(match_type); sl@0: unsigned int throw_quals = CV_quals_of_pointee(throw_type); sl@0: if (any_qualifier_missing(match_quals, throw_quals)) { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: failed (missing qualifiers)\n"); sl@0: #endif sl@0: return false; sl@0: } sl@0: // If the match type has additional qualifiers not found in the sl@0: // throw type, any previous qualifiers must have included const sl@0: if (any_qualifier_missing(throw_quals, match_quals) && sl@0: !previous_qualifiers_include_const) { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: failed (not all qualifiers have const)\n"); sl@0: #endif sl@0: return false; sl@0: } sl@0: if (!is_const(match_quals)) sl@0: previous_qualifiers_include_const = false; sl@0: throw_type = ((const abi::__pbase_type_info *)throw_type)->__pointee; sl@0: match_type = ((const abi::__pbase_type_info *)match_type)->__pointee; sl@0: if (current_objectp != NULL) sl@0: current_objectp = *(void **)current_objectp; sl@0: continue; sl@0: } sl@0: sl@0: // Neither is a pointer now but qualification conversion has been done. sl@0: // See if pointer conversion on the original was possible. sl@0: // T* will match void* sl@0: sl@0: if (pointer_depth == 1 && match_type == &typeid(void)) { sl@0: *matched_objectpp = original_objectp; sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: success(conversion to void *)\n"); sl@0: #endif sl@0: return true; sl@0: } sl@0: sl@0: // Else if we have 2 class types, a derived class is matched by a sl@0: // non-ambiguous public base class (perhaps not a leftmost one). sl@0: // __si_class_type_info and __vmi_class_type_info are classes with bases. sl@0: sl@0: void *matched_base_p; sl@0: sl@0: if (pointer_depth < 2 && sl@0: (type_throw_type == &typeid(abi::__si_class_type_info) || sl@0: type_throw_type == &typeid(abi::__vmi_class_type_info))) { sl@0: if (DERIVED_TO_BASE_CONVERSION(current_objectp, &matched_base_p, sl@0: throw_type, match_type)) { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: success (matched base 0x%x of 0x%x%s, thrown object 0x%x)\n", sl@0: matched_base_p, current_objectp, sl@0: pointer_depth == 0 ? "" : " via ptr", sl@0: original_objectp); sl@0: #endif sl@0: *matched_objectpp = pointer_depth == 0 ? matched_base_p : original_objectp; sl@0: return true; sl@0: } else { sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: failed (derived to base failed)\n"); sl@0: #endif sl@0: return false; sl@0: } sl@0: } sl@0: sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_type_match: failed (types simply differ)\n"); sl@0: #endif sl@0: return false; sl@0: } /* for */ sl@0: } sl@0: sl@0: sl@0: /* For debugging purposes: */ sl@0: #ifdef DEBUG sl@0: extern "C" bool debug__cxa_type_match(void *objptr, sl@0: const type_info *throw_type, sl@0: const type_info *catch_type, sl@0: void **matched_objectpp) sl@0: { sl@0: /* Create enough of an exception object that the type-matcher can run, then sl@0: * check the type. Objptr is expected to be the result of a call to sl@0: * __cxa_allocate_exception, which has then been copy-constructed. sl@0: */ sl@0: __cxa_exception *e = ((__cxa_exception *)objptr) - 1; sl@0: e->exceptionType = throw_type; sl@0: return __cxa_type_match(&e->ucb, catch_type, matched_objectpp); sl@0: } sl@0: #endif sl@0: sl@0: sl@0: #endif /* arm_exceptions_throw_c */ sl@0: #ifdef arm_exceptions_rethrow_c sl@0: sl@0: /* Redeclare _Unwind_RaiseException as weak (if WEAKDECL is defined sl@0: * appropriately) so the use from __cxa_rethrow does not on its own sl@0: * force the unwind library to be loaded. sl@0: */ sl@0: sl@0: extern "C" WEAKDECL _Unwind_Reason_Code _Unwind_RaiseException(UCB *ucbp); sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_rethrow(void) sl@0: { sl@0: // Recover the exception object - it is the most recent caught exception object sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep = g->caughtExceptions; sl@0: bool foreign; sl@0: sl@0: // Must call terminate here if no such exception sl@0: if (ep == NULL) NAMES::call_terminate_handler(NULL); sl@0: sl@0: UCB *ucbp = &ep->ucb; sl@0: sl@0: // Mark the object as being propagated by throw, preventing multiple sl@0: // propagation and also permitting __cxa_end_catch to do the right sl@0: // thing when it is called from the handler's cleanup. sl@0: sl@0: ep->propagationCount++; sl@0: sl@0: // Now reraise, taking care with foreign exceptions sl@0: sl@0: foreign = NAMES::is_foreign_exception(ucbp); sl@0: if (foreign) { sl@0: // Indirect through the intermediate object to the foreign ucb sl@0: ucbp = (UCB *)ep->exceptionType; sl@0: } else { sl@0: // Increment the uncaught C++ exceptions count sl@0: g->uncaughtExceptions++; sl@0: } sl@0: sl@0: // Tell debugger what's happening sl@0: sl@0: DEBUGGER_BOTTLENECK(ucbp, _UASUBSYS_CPP, _UAACT_STARTING, foreign ? NULL : ep->exceptionType); sl@0: sl@0: // Initiate unwinding - if we get control back, call C++ routine terminate() sl@0: sl@0: _Unwind_RaiseException(ucbp); sl@0: sl@0: #ifdef PRINTED_DIAGNOSTICS sl@0: printf("__cxa_rethrow: throw failed\n"); sl@0: #endif sl@0: sl@0: __cxa_call_terminate(ucbp); sl@0: } sl@0: sl@0: #endif /* arm_exceptions_rethrow_c */ sl@0: #ifdef arm_exceptions_foreign_c sl@0: sl@0: /* During catch and cleanup, foreign exception objects are dealt with using sl@0: * an intermediate __cxa_exception block in the appropriate exceptions sl@0: * chain. This block has the same exception_class as the real foreign sl@0: * ucb, and points to the real ucb via the intermediate block's exceptionType sl@0: * field. This helper function checks whether it has been passed such an sl@0: * intermediate block and sets one up if not. Only call it when the UCB sl@0: * is known to belong to a foreign exception. sl@0: */ sl@0: sl@0: __cxa_exception *NAMES::get_foreign_intermediary(__cxa_exception *head_ep, UCB *ucbp) sl@0: { sl@0: if (head_ep != NULL) { sl@0: UCB *head_ucbp = &head_ep->ucb; sl@0: if (NAMES::same_exceptions_class(&head_ucbp->exception_class, &ucbp->exception_class) && sl@0: (UCB *)head_ep->exceptionType == ucbp) sl@0: return head_ep; sl@0: } sl@0: sl@0: // Create an intermediate block. Only initialise as much as necessary sl@0: __cxa_exception *ep = ((__cxa_exception *)__cxa_allocate_exception(0)) - 1; sl@0: UCB *new_ucbp = &ep->ucb; sl@0: memcpy(new_ucbp->exception_class, ucbp->exception_class, EXCEPTIONS_CLASS_SIZE); sl@0: ep->propagationCount = 0; // Not propagating sl@0: ep->handlerCount = 0; // Not handled sl@0: ep->nextCaughtException = NULL; // Not in chain sl@0: ep->exceptionType = (const type_info *)ucbp; // The foreign UCB sl@0: return ep; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_foreign_c */ sl@0: #ifdef arm_exceptions_cleanup_c sl@0: sl@0: bool __cxa_begin_cleanup(UCB *ucbp) sl@0: { sl@0: // Indicate that a cleanup is about to start. sl@0: // Save the exception pointer over the cleanup for recovery later, using a chain. sl@0: // If we allowed the exception to be rethrown in a cleanup, then sl@0: // the object might appear multiple times at the head of this chain, sl@0: // and the propagationCount could be used to track this - at this point, sl@0: // the object is logically in the chain propagationCount-1 times, and sl@0: // physically 0 or 1 times. Thus if propagationCount == 1 we should insert sl@0: // it physically. A similar rule is used for physical removal in sl@0: //__cxa_end_cleanup. sl@0: // Foreign exceptions are handled via an intermediate __cxa_exception object sl@0: // in a similar way as __cxa_begin_catch. sl@0: sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep; sl@0: sl@0: if (NAMES::is_foreign_exception(ucbp)) { sl@0: ep = NAMES::get_foreign_intermediary(g->propagatingExceptions, ucbp); sl@0: ep->propagationCount++; // Indicate one (or one additional) propagation sl@0: } else { sl@0: ep = ucbp_to_ep(ucbp); sl@0: } sl@0: sl@0: if (ep->propagationCount == 1) { sl@0: // Insert into chain sl@0: ep->nextPropagatingException = g->propagatingExceptions; sl@0: g->propagatingExceptions = ep; sl@0: } sl@0: sl@0: return true; sl@0: } sl@0: sl@0: sl@0: // Helper function for __cxa_end_cleanup sl@0: sl@0: extern "C" UCB * __ARM_cxa_end_cleanup(void) sl@0: { sl@0: // Recover and return the currently propagating exception (from the sl@0: // head of the propagatingExceptions chain). sl@0: // propagationCount at this moment is a logical count of how many times the sl@0: // item is in the chain so physically unchain it when this count is 1. sl@0: // Foreign exceptions use an intermediary. sl@0: sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep = g->propagatingExceptions; sl@0: sl@0: if (ep == NULL) terminate(); sl@0: sl@0: UCB *ucbp = &ep->ucb; sl@0: if (NAMES::is_foreign_exception(ucbp)) { sl@0: // Get the foreign ucb sl@0: ucbp = (UCB *)ep->exceptionType; sl@0: if (ep->propagationCount == 1) { sl@0: // Free the intermediate ucb (see description in __cxa_begin_catch) sl@0: void *eop = (void *)(ep + 1); sl@0: g->propagatingExceptions = ep->nextPropagatingException; sl@0: __cxa_free_exception(eop); sl@0: } else { sl@0: ep->propagationCount--; sl@0: } sl@0: } else { sl@0: // Not foreign sl@0: if (ep->propagationCount == 1) { // logically in chain once - so unchain sl@0: g->propagatingExceptions = ep->nextPropagatingException; sl@0: } sl@0: } sl@0: return ucbp; sl@0: } sl@0: sl@0: // __cxa_end_cleanup is called at the end of a cleanup fragment. sl@0: // It must do the C++ housekeeping, then call _Unwind_Resume, but it must sl@0: // damage no significant registers in the process. sl@0: sl@0: __asm void __cxa_end_cleanup(void) { sl@0: extern __ARM_cxa_end_cleanup; sl@0: extern _Unwind_Resume WEAKASMDECL; sl@0: sl@0: #ifdef __thumb sl@0: preserve8; // This is preserve8 (ARM assembler heuristics are inadequate) sl@0: push {r1-r7}; sl@0: mov r2, r8; sl@0: mov r3, r9; sl@0: mov r4, r10; sl@0: mov r5, r11; sl@0: push {r1-r5}; sl@0: bl __ARM_cxa_end_cleanup; // returns UCB address in r0 sl@0: pop {r1-r5}; sl@0: mov r8, r2; sl@0: mov r9, r3; sl@0: mov r10, r4; sl@0: mov r11, r5; sl@0: pop {r1-r7}; sl@0: bl _Unwind_Resume; // won't return sl@0: #else sl@0: stmfd r13!, {r1-r12} sl@0: bl __ARM_cxa_end_cleanup; // returns UCB address in r0 sl@0: ldmia r13!, {r1-r12}; sl@0: b _Unwind_Resume; // won't return sl@0: #endif sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_cleanup_c */ sl@0: #ifdef arm_exceptions_catchsemantics_c sl@0: sl@0: /* Update date structures as if catching an object. sl@0: * Call this from __cxa_begin_catch when actually catching an object, sl@0: * and from external_exception_termination when called by a foreign runtime sl@0: * after one of our objects was caught. sl@0: */ sl@0: sl@0: void NAMES::eh_catch_semantics(UCB *ucbp) sl@0: { sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep; sl@0: sl@0: if (NAMES::is_foreign_exception(ucbp)) { sl@0: // Foreign exception. Get the associated intermediary block or sl@0: // make one if there isn't one already. sl@0: // In the case of a rethrow, the foreign object may already be on sl@0: // the handled exceptions chain (it will be first). sl@0: ep = NAMES::get_foreign_intermediary(g->caughtExceptions, ucbp); sl@0: } else { sl@0: // Not foreign sl@0: ep = ucbp_to_ep(ucbp); sl@0: // Decrement the propagation count sl@0: ep->propagationCount--; sl@0: // Decrement the total uncaught C++ exceptions count sl@0: g->uncaughtExceptions--; sl@0: } sl@0: sl@0: // Common code for our EO's, and foreign ones where we work on the intermediate EO sl@0: sl@0: // Increment the handler count for this exception object sl@0: ep->handlerCount++; sl@0: sl@0: // Push the ep onto the "handled exceptions" chain if it is not already there. sl@0: // (If catching a rethrow, it may already be there) sl@0: sl@0: if (ep->nextCaughtException == NULL) { sl@0: ep->nextCaughtException = g->caughtExceptions; sl@0: g->caughtExceptions = ep; sl@0: } sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_catchsemantics_c */ sl@0: #ifdef arm_exceptions_begincatch_c sl@0: sl@0: void *__cxa_begin_catch(UCB *ucbp) sl@0: { sl@0: void *match = (void *)ucbp->barrier_cache.bitpattern[0]; // The matched object, if any sl@0: sl@0: // Update the data structures sl@0: sl@0: NAMES::eh_catch_semantics(ucbp); sl@0: sl@0: // Tell the unwinder the exception propagation has finished, sl@0: // and return the object pointer sl@0: sl@0: _Unwind_Complete(ucbp); sl@0: return match; sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_begincatch_c */ sl@0: #ifdef arm_exceptions_endcatch_c sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_end_catch(void) sl@0: { sl@0: // Recover the exception object - it is the most recent caught exception object sl@0: __cxa_eh_globals *g = __cxa_get_globals(); sl@0: __cxa_exception *ep = g->caughtExceptions; sl@0: sl@0: if (ep == NULL) terminate(); sl@0: sl@0: // Rethrow in progress? sl@0: sl@0: bool object_being_rethrown = ep->propagationCount != 0; sl@0: sl@0: // Decrement the handler count for this exception object sl@0: ep->handlerCount--; sl@0: sl@0: // Unstack the object if it is no longer being handled anywhere. sl@0: // Destroy and free the object if it is no longer alive - sl@0: // it is dead if its handler count becomes 0, unless it is sl@0: // about to be rethrown. sl@0: // If the dtor throws, allow its exception to propagate. sl@0: // Do different things if it is a foreign exception object. sl@0: sl@0: if (ep->handlerCount == 0) { sl@0: void *eop = (void *)(ep + 1); sl@0: UCB *ucbp = &ep->ucb; sl@0: bool foreign = NAMES::is_foreign_exception(ucbp); sl@0: sl@0: // Unstack it from the caught exceptions stack - it is guaranteed to be top item. sl@0: g->caughtExceptions = ep->nextCaughtException; sl@0: sl@0: if (foreign) { sl@0: // Get the foreign ucb and free the intermediate ucb (see description in __cxa_begin_catch) sl@0: ucbp = (UCB *)ep->exceptionType; sl@0: __cxa_free_exception(eop); sl@0: } else { sl@0: ep->nextCaughtException = NULL; // So __cxa_begin_catch knows it isn't in the chain sl@0: } sl@0: sl@0: // Now destroy the exception object if it's no longer needed sl@0: if (!object_being_rethrown) { sl@0: if (foreign) { sl@0: sl@0: // Notify the foreign language, if it so requested sl@0: if (ucbp->exception_cleanup != NULL) sl@0: (ucbp->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, ucbp); sl@0: sl@0: } else { sl@0: sl@0: // One of our objects: do C++-specific semantics sl@0: sl@0: if (ep->exceptionDestructor != NULL) { sl@0: // Run the dtor. If it throws, free the memory anyway and sl@0: // propagate the new exception. sl@0: #ifdef ARM_EXCEPTIONS_ENABLED sl@0: try { sl@0: (ep->exceptionDestructor)(eop); sl@0: } catch(...) { sl@0: // Free the memory and reraise sl@0: __cxa_free_exception(eop); sl@0: throw; sl@0: } sl@0: #else sl@0: (ep->exceptionDestructor)(eop); sl@0: #endif sl@0: } sl@0: // Dtor (if there was one) didn't throw. Free the memory. sl@0: __cxa_free_exception(eop); sl@0: } // !foreign sl@0: } // !object_being_rethrown sl@0: } // ep->handlerCount == 0 sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_endcatch_c */ sl@0: #ifdef arm_exceptions_bad_typeid_c sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_bad_typeid(void) sl@0: { sl@0: throw std::bad_typeid(); sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_bad_typeid_c */ sl@0: #ifdef arm_exceptions_bad_cast_c sl@0: sl@0: #pragma exceptions_unwind sl@0: sl@0: void __cxa_bad_cast(void) sl@0: { sl@0: throw std::bad_cast(); sl@0: } sl@0: sl@0: sl@0: #endif /* arm_exceptions_bad_cast_c */