xref: /freebsd-src/contrib/libcxxrt/exception.cc (revision db47c4bf212fedd87006f4949bcbacb6c58dd064)
17a984708SDavid Chisnall #include <stdlib.h>
27a984708SDavid Chisnall #include <dlfcn.h>
37a984708SDavid Chisnall #include <stdio.h>
47a984708SDavid Chisnall #include <string.h>
57a984708SDavid Chisnall #include <stdint.h>
67a984708SDavid Chisnall #include <pthread.h>
77a984708SDavid Chisnall #include "typeinfo.h"
87a984708SDavid Chisnall #include "dwarf_eh.h"
97a984708SDavid Chisnall #include "cxxabi.h"
107a984708SDavid Chisnall 
11*db47c4bfSDavid Chisnall #pragma weak pthread_key_create
12*db47c4bfSDavid Chisnall #pragma weak pthread_setspecific
13*db47c4bfSDavid Chisnall #pragma weak pthread_getspecific
14*db47c4bfSDavid Chisnall #pragma weak pthread_once
15*db47c4bfSDavid Chisnall #pragma weak pthread_once
16*db47c4bfSDavid Chisnall #pragma weak pthread_cond_signal
17*db47c4bfSDavid Chisnall #pragma weak pthread_cond_wait
18*db47c4bfSDavid Chisnall #pragma weak pthread_mutex_lock
19*db47c4bfSDavid Chisnall #pragma weak pthread_mutex_unlock
20*db47c4bfSDavid Chisnall 
21*db47c4bfSDavid Chisnall 
227a984708SDavid Chisnall using namespace ABI_NAMESPACE;
237a984708SDavid Chisnall 
247a984708SDavid Chisnall /**
257a984708SDavid Chisnall  * Saves the result of the landing pad that we have found.  For ARM, this is
267a984708SDavid Chisnall  * stored in the generic unwind structure, while on other platforms it is
277a984708SDavid Chisnall  * stored in the C++ exception.
287a984708SDavid Chisnall  */
297a984708SDavid Chisnall static void saveLandingPad(struct _Unwind_Context *context,
307a984708SDavid Chisnall                            struct _Unwind_Exception *ucb,
317a984708SDavid Chisnall                            struct __cxa_exception *ex,
327a984708SDavid Chisnall                            int selector,
337a984708SDavid Chisnall                            dw_eh_ptr_t landingPad)
347a984708SDavid Chisnall {
357a984708SDavid Chisnall #ifdef __arm__
367a984708SDavid Chisnall 	// On ARM, we store the saved exception in the generic part of the structure
377a984708SDavid Chisnall 	ucb->barrier_cache.sp = _Unwind_GetGR(context, 13);
387a984708SDavid Chisnall 	ucb->barrier_cache.bitpattern[1] = (uint32_t)selector;
397a984708SDavid Chisnall 	ucb->barrier_cache.bitpattern[3] = (uint32_t)landingPad;
407a984708SDavid Chisnall #endif
417a984708SDavid Chisnall 	// Cache the results for the phase 2 unwind, if we found a handler
427a984708SDavid Chisnall 	// and this is not a foreign exception.
437a984708SDavid Chisnall 	if (ex)
447a984708SDavid Chisnall 	{
457a984708SDavid Chisnall 		ex->handlerSwitchValue = selector;
467a984708SDavid Chisnall 		ex->catchTemp = landingPad;
477a984708SDavid Chisnall 	}
487a984708SDavid Chisnall }
497a984708SDavid Chisnall 
507a984708SDavid Chisnall /**
517a984708SDavid Chisnall  * Loads the saved landing pad.  Returns 1 on success, 0 on failure.
527a984708SDavid Chisnall  */
537a984708SDavid Chisnall static int loadLandingPad(struct _Unwind_Context *context,
547a984708SDavid Chisnall                           struct _Unwind_Exception *ucb,
557a984708SDavid Chisnall                           struct __cxa_exception *ex,
567a984708SDavid Chisnall                           unsigned long *selector,
577a984708SDavid Chisnall                           dw_eh_ptr_t *landingPad)
587a984708SDavid Chisnall {
597a984708SDavid Chisnall #ifdef __arm__
607a984708SDavid Chisnall 	*selector = ucb->barrier_cache.bitpattern[1];
617a984708SDavid Chisnall 	*landingPad = (dw_eh_ptr_t)ucb->barrier_cache.bitpattern[3];
627a984708SDavid Chisnall 	return 1;
637a984708SDavid Chisnall #else
647a984708SDavid Chisnall 	if (ex)
657a984708SDavid Chisnall 	{
667a984708SDavid Chisnall 		*selector = ex->handlerSwitchValue;
677a984708SDavid Chisnall 		*landingPad = (dw_eh_ptr_t)ex->catchTemp;
687a984708SDavid Chisnall 		return 0;
697a984708SDavid Chisnall 	}
707a984708SDavid Chisnall 	return 0;
717a984708SDavid Chisnall #endif
727a984708SDavid Chisnall }
737a984708SDavid Chisnall 
747a984708SDavid Chisnall static inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex,
757a984708SDavid Chisnall                                                     struct _Unwind_Context *context)
767a984708SDavid Chisnall {
777a984708SDavid Chisnall #ifdef __arm__
787a984708SDavid Chisnall 	if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; }
797a984708SDavid Chisnall #endif
807a984708SDavid Chisnall 	return _URC_CONTINUE_UNWIND;
817a984708SDavid Chisnall }
827a984708SDavid Chisnall 
837a984708SDavid Chisnall 
847a984708SDavid Chisnall extern "C" void __cxa_free_exception(void *thrown_exception);
857a984708SDavid Chisnall extern "C" void __cxa_free_dependent_exception(void *thrown_exception);
867a984708SDavid Chisnall extern "C" void* __dynamic_cast(const void *sub,
877a984708SDavid Chisnall                                 const __class_type_info *src,
887a984708SDavid Chisnall                                 const __class_type_info *dst,
897a984708SDavid Chisnall                                 ptrdiff_t src2dst_offset);
907a984708SDavid Chisnall 
917a984708SDavid Chisnall /**
927a984708SDavid Chisnall  * The type of a handler that has been found.
937a984708SDavid Chisnall  */
947a984708SDavid Chisnall typedef enum
957a984708SDavid Chisnall {
967a984708SDavid Chisnall 	/** No handler. */
977a984708SDavid Chisnall 	handler_none,
987a984708SDavid Chisnall 	/**
997a984708SDavid Chisnall 	 * A cleanup - the exception will propagate through this frame, but code
1007a984708SDavid Chisnall 	 * must be run when this happens.
1017a984708SDavid Chisnall 	 */
1027a984708SDavid Chisnall 	handler_cleanup,
1037a984708SDavid Chisnall 	/**
1047a984708SDavid Chisnall 	 * A catch statement.  The exception will not propagate past this frame
1057a984708SDavid Chisnall 	 * (without an explicit rethrow).
1067a984708SDavid Chisnall 	 */
1077a984708SDavid Chisnall 	handler_catch
1087a984708SDavid Chisnall } handler_type;
1097a984708SDavid Chisnall 
1107a984708SDavid Chisnall /**
1117a984708SDavid Chisnall  * Per-thread info required by the runtime.  We store a single structure
1127a984708SDavid Chisnall  * pointer in thread-local storage, because this tends to be a scarce resource
1137a984708SDavid Chisnall  * and it's impolite to steal all of it and not leave any for the rest of the
1147a984708SDavid Chisnall  * program.
1157a984708SDavid Chisnall  *
1167a984708SDavid Chisnall  * Instances of this structure are allocated lazily - at most one per thread -
1177a984708SDavid Chisnall  * and are destroyed on thread termination.
1187a984708SDavid Chisnall  */
1197a984708SDavid Chisnall struct __cxa_thread_info
1207a984708SDavid Chisnall {
1217a984708SDavid Chisnall 	/** The termination handler for this thread. */
1227a984708SDavid Chisnall 	terminate_handler terminateHandler;
1237a984708SDavid Chisnall 	/** The unexpected exception handler for this thread. */
1247a984708SDavid Chisnall 	unexpected_handler unexpectedHandler;
1257a984708SDavid Chisnall 	/**
1267a984708SDavid Chisnall 	 * The number of emergency buffers held by this thread.  This is 0 in
1277a984708SDavid Chisnall 	 * normal operation - the emergency buffers are only used when malloc()
1287a984708SDavid Chisnall 	 * fails to return memory for allocating an exception.  Threads are not
1297a984708SDavid Chisnall 	 * permitted to hold more than 4 emergency buffers (as per recommendation
1307a984708SDavid Chisnall 	 * in ABI spec [3.3.1]).
1317a984708SDavid Chisnall 	 */
1327a984708SDavid Chisnall 	int emergencyBuffersHeld;
1337a984708SDavid Chisnall 	/**
1347a984708SDavid Chisnall 	 * The exception currently running in a cleanup.
1357a984708SDavid Chisnall 	 */
1367a984708SDavid Chisnall 	_Unwind_Exception *currentCleanup;
1377a984708SDavid Chisnall 	/**
1387a984708SDavid Chisnall 	 * The public part of this structure, accessible from outside of this
1397a984708SDavid Chisnall 	 * module.
1407a984708SDavid Chisnall 	 */
1417a984708SDavid Chisnall 	__cxa_eh_globals globals;
1427a984708SDavid Chisnall };
1437a984708SDavid Chisnall /**
1447a984708SDavid Chisnall  * Dependent exception.  This
1457a984708SDavid Chisnall  */
1467a984708SDavid Chisnall struct __cxa_dependent_exception
1477a984708SDavid Chisnall {
1487a984708SDavid Chisnall #if __LP64__
1497a984708SDavid Chisnall 	void *primaryException;
1507a984708SDavid Chisnall #endif
1517a984708SDavid Chisnall 	std::type_info *exceptionType;
1527a984708SDavid Chisnall 	void (*exceptionDestructor) (void *);
1537a984708SDavid Chisnall 	unexpected_handler unexpectedHandler;
1547a984708SDavid Chisnall 	terminate_handler terminateHandler;
1557a984708SDavid Chisnall 	__cxa_exception *nextException;
1567a984708SDavid Chisnall 	int handlerCount;
1577a984708SDavid Chisnall #ifdef __arm__
1587a984708SDavid Chisnall 	_Unwind_Exception *nextCleanup;
1597a984708SDavid Chisnall 	int cleanupCount;
1607a984708SDavid Chisnall #endif
1617a984708SDavid Chisnall 	int handlerSwitchValue;
1627a984708SDavid Chisnall 	const char *actionRecord;
1637a984708SDavid Chisnall 	const char *languageSpecificData;
1647a984708SDavid Chisnall 	void *catchTemp;
1657a984708SDavid Chisnall 	void *adjustedPtr;
1667a984708SDavid Chisnall #if !__LP64__
1677a984708SDavid Chisnall 	void *primaryException;
1687a984708SDavid Chisnall #endif
1697a984708SDavid Chisnall 	_Unwind_Exception unwindHeader;
1707a984708SDavid Chisnall };
1717a984708SDavid Chisnall 
1727a984708SDavid Chisnall 
1737a984708SDavid Chisnall namespace std
1747a984708SDavid Chisnall {
1757a984708SDavid Chisnall 	void unexpected();
1767a984708SDavid Chisnall 	class exception
1777a984708SDavid Chisnall 	{
1787a984708SDavid Chisnall 		public:
1797a984708SDavid Chisnall 			virtual ~exception() throw();
1807a984708SDavid Chisnall 			virtual const char* what() const throw();
1817a984708SDavid Chisnall 	};
1827a984708SDavid Chisnall 
1837a984708SDavid Chisnall }
1847a984708SDavid Chisnall 
1857a984708SDavid Chisnall extern "C" std::type_info *__cxa_current_exception_type();
1867a984708SDavid Chisnall 
1877a984708SDavid Chisnall /**
1887a984708SDavid Chisnall  * Class of exceptions to distinguish between this and other exception types.
1897a984708SDavid Chisnall  *
1907a984708SDavid Chisnall  * The first four characters are the vendor ID.  Currently, we use GNUC,
1917a984708SDavid Chisnall  * because we aim for ABI-compatibility with the GNU implementation, and
1927a984708SDavid Chisnall  * various checks may test for equality of the class, which is incorrect.
1937a984708SDavid Chisnall  */
1947a984708SDavid Chisnall static const uint64_t exception_class =
1957a984708SDavid Chisnall 	EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\0');
1967a984708SDavid Chisnall /**
1977a984708SDavid Chisnall  * Class used for dependent exceptions.
1987a984708SDavid Chisnall  */
1997a984708SDavid Chisnall static const uint64_t dependent_exception_class =
2007a984708SDavid Chisnall 	EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\x01');
2017a984708SDavid Chisnall /**
2027a984708SDavid Chisnall  * The low four bytes of the exception class, indicating that we conform to the
2037a984708SDavid Chisnall  * Itanium C++ ABI.  This is currently unused, but should be used in the future
2047a984708SDavid Chisnall  * if we change our exception class, to allow this library and libsupc++ to be
2057a984708SDavid Chisnall  * linked to the same executable and both to interoperate.
2067a984708SDavid Chisnall  */
2077a984708SDavid Chisnall static const uint32_t abi_exception_class =
2087a984708SDavid Chisnall 	GENERIC_EXCEPTION_CLASS('C', '+', '+', '\0');
2097a984708SDavid Chisnall 
2107a984708SDavid Chisnall static bool isCXXException(uint64_t cls)
2117a984708SDavid Chisnall {
2127a984708SDavid Chisnall 	return (cls == exception_class) || (cls == dependent_exception_class);
2137a984708SDavid Chisnall }
2147a984708SDavid Chisnall 
2157a984708SDavid Chisnall static bool isDependentException(uint64_t cls)
2167a984708SDavid Chisnall {
2177a984708SDavid Chisnall 	return cls == dependent_exception_class;
2187a984708SDavid Chisnall }
2197a984708SDavid Chisnall 
2207a984708SDavid Chisnall static __cxa_exception *exceptionFromPointer(void *ex)
2217a984708SDavid Chisnall {
2227a984708SDavid Chisnall 	return (__cxa_exception*)((char*)ex -
2237a984708SDavid Chisnall 			offsetof(struct __cxa_exception, unwindHeader));
2247a984708SDavid Chisnall }
2257a984708SDavid Chisnall static __cxa_exception *realExceptionFromException(__cxa_exception *ex)
2267a984708SDavid Chisnall {
2277a984708SDavid Chisnall 	if (!isDependentException(ex->unwindHeader.exception_class)) { return ex; }
2287a984708SDavid Chisnall 	return ((__cxa_exception*)(((__cxa_dependent_exception*)ex)->primaryException))-1;
2297a984708SDavid Chisnall }
2307a984708SDavid Chisnall 
2317a984708SDavid Chisnall 
2327a984708SDavid Chisnall namespace std
2337a984708SDavid Chisnall {
2347a984708SDavid Chisnall 	// Forward declaration of standard library terminate() function used to
2357a984708SDavid Chisnall 	// abort execution.
2367a984708SDavid Chisnall 	void terminate(void);
2377a984708SDavid Chisnall }
2387a984708SDavid Chisnall 
2397a984708SDavid Chisnall using namespace ABI_NAMESPACE;
2407a984708SDavid Chisnall 
2417a984708SDavid Chisnall 
2427a984708SDavid Chisnall 
2437a984708SDavid Chisnall /** The global termination handler. */
2447a984708SDavid Chisnall static terminate_handler terminateHandler = abort;
2457a984708SDavid Chisnall /** The global unexpected exception handler. */
2467a984708SDavid Chisnall static unexpected_handler unexpectedHandler = std::terminate;
2477a984708SDavid Chisnall 
2487a984708SDavid Chisnall /** Key used for thread-local data. */
2497a984708SDavid Chisnall static pthread_key_t eh_key;
2507a984708SDavid Chisnall 
2517a984708SDavid Chisnall 
2527a984708SDavid Chisnall /**
2537a984708SDavid Chisnall  * Cleanup function, allowing foreign exception handlers to correctly destroy
2547a984708SDavid Chisnall  * this exception if they catch it.
2557a984708SDavid Chisnall  */
2567a984708SDavid Chisnall static void exception_cleanup(_Unwind_Reason_Code reason,
2577a984708SDavid Chisnall                               struct _Unwind_Exception *ex)
2587a984708SDavid Chisnall {
2597a984708SDavid Chisnall 	__cxa_free_exception((void*)ex);
2607a984708SDavid Chisnall }
2617a984708SDavid Chisnall static void dependent_exception_cleanup(_Unwind_Reason_Code reason,
2627a984708SDavid Chisnall                               struct _Unwind_Exception *ex)
2637a984708SDavid Chisnall {
2647a984708SDavid Chisnall 
2657a984708SDavid Chisnall 	__cxa_free_dependent_exception((void*)ex);
2667a984708SDavid Chisnall }
2677a984708SDavid Chisnall 
2687a984708SDavid Chisnall /**
2697a984708SDavid Chisnall  * Recursively walk a list of exceptions and delete them all in post-order.
2707a984708SDavid Chisnall  */
2717a984708SDavid Chisnall static void free_exception_list(__cxa_exception *ex)
2727a984708SDavid Chisnall {
2737a984708SDavid Chisnall 	if (0 != ex->nextException)
2747a984708SDavid Chisnall 	{
2757a984708SDavid Chisnall 		free_exception_list(ex->nextException);
2767a984708SDavid Chisnall 	}
2777a984708SDavid Chisnall 	// __cxa_free_exception() expects to be passed the thrown object, which
2787a984708SDavid Chisnall 	// immediately follows the exception, not the exception itself
2797a984708SDavid Chisnall 	__cxa_free_exception(ex+1);
2807a984708SDavid Chisnall }
2817a984708SDavid Chisnall 
2827a984708SDavid Chisnall /**
2837a984708SDavid Chisnall  * Cleanup function called when a thread exists to make certain that all of the
2847a984708SDavid Chisnall  * per-thread data is deleted.
2857a984708SDavid Chisnall  */
2867a984708SDavid Chisnall static void thread_cleanup(void* thread_info)
2877a984708SDavid Chisnall {
2887a984708SDavid Chisnall 	__cxa_thread_info *info = (__cxa_thread_info*)thread_info;
2897a984708SDavid Chisnall 	if (info->globals.caughtExceptions)
2907a984708SDavid Chisnall 	{
2917a984708SDavid Chisnall 		free_exception_list(info->globals.caughtExceptions);
2927a984708SDavid Chisnall 	}
2937a984708SDavid Chisnall 	free(thread_info);
2947a984708SDavid Chisnall }
2957a984708SDavid Chisnall 
2967a984708SDavid Chisnall 
2977a984708SDavid Chisnall /**
2987a984708SDavid Chisnall  * Once control used to protect the key creation.
2997a984708SDavid Chisnall  */
3007a984708SDavid Chisnall static pthread_once_t once_control = PTHREAD_ONCE_INIT;
3017a984708SDavid Chisnall 
3027a984708SDavid Chisnall /**
303*db47c4bfSDavid Chisnall  * We may not be linked against a full pthread implementation.  If we're not,
304*db47c4bfSDavid Chisnall  * then we need to fake the thread-local storage by storing 'thread-local'
305*db47c4bfSDavid Chisnall  * things in a global.
306*db47c4bfSDavid Chisnall  */
307*db47c4bfSDavid Chisnall static bool fakeTLS;
308*db47c4bfSDavid Chisnall /**
309*db47c4bfSDavid Chisnall  * Thread-local storage for a single-threaded program.
310*db47c4bfSDavid Chisnall  */
311*db47c4bfSDavid Chisnall static __cxa_thread_info singleThreadInfo;
312*db47c4bfSDavid Chisnall /**
3137a984708SDavid Chisnall  * Initialise eh_key.
3147a984708SDavid Chisnall  */
3157a984708SDavid Chisnall static void init_key(void)
3167a984708SDavid Chisnall {
317*db47c4bfSDavid Chisnall 	if ((0 == pthread_key_create) ||
318*db47c4bfSDavid Chisnall 	    (0 == pthread_setspecific) ||
319*db47c4bfSDavid Chisnall 	    (0 == pthread_getspecific))
320*db47c4bfSDavid Chisnall 	{
321*db47c4bfSDavid Chisnall 		fakeTLS = true;
322*db47c4bfSDavid Chisnall 		return;
323*db47c4bfSDavid Chisnall 	}
3247a984708SDavid Chisnall 	pthread_key_create(&eh_key, thread_cleanup);
325*db47c4bfSDavid Chisnall 	pthread_setspecific(eh_key, (void*)0x42);
326*db47c4bfSDavid Chisnall 	fakeTLS = (pthread_getspecific(eh_key) != (void*)0x42);
327*db47c4bfSDavid Chisnall 	pthread_setspecific(eh_key, 0);
3287a984708SDavid Chisnall }
3297a984708SDavid Chisnall 
3307a984708SDavid Chisnall /**
3317a984708SDavid Chisnall  * Returns the thread info structure, creating it if it is not already created.
3327a984708SDavid Chisnall  */
3337a984708SDavid Chisnall static __cxa_thread_info *thread_info()
3347a984708SDavid Chisnall {
335*db47c4bfSDavid Chisnall 	if ((0 == pthread_once) || pthread_once(&once_control, init_key))
336*db47c4bfSDavid Chisnall 	{
337*db47c4bfSDavid Chisnall 		fakeTLS = true;
338*db47c4bfSDavid Chisnall 	}
339*db47c4bfSDavid Chisnall 	if (fakeTLS) { return &singleThreadInfo; }
3407a984708SDavid Chisnall 	__cxa_thread_info *info = (__cxa_thread_info*)pthread_getspecific(eh_key);
3417a984708SDavid Chisnall 	if (0 == info)
3427a984708SDavid Chisnall 	{
3437a984708SDavid Chisnall 		info = (__cxa_thread_info*)calloc(1, sizeof(__cxa_thread_info));
3447a984708SDavid Chisnall 		pthread_setspecific(eh_key, info);
3457a984708SDavid Chisnall 	}
3467a984708SDavid Chisnall 	return info;
3477a984708SDavid Chisnall }
3487a984708SDavid Chisnall /**
3497a984708SDavid Chisnall  * Fast version of thread_info().  May fail if thread_info() is not called on
3507a984708SDavid Chisnall  * this thread at least once already.
3517a984708SDavid Chisnall  */
3527a984708SDavid Chisnall static __cxa_thread_info *thread_info_fast()
3537a984708SDavid Chisnall {
354*db47c4bfSDavid Chisnall 	if (fakeTLS) { return &singleThreadInfo; }
3557a984708SDavid Chisnall 	return (__cxa_thread_info*)pthread_getspecific(eh_key);
3567a984708SDavid Chisnall }
3577a984708SDavid Chisnall /**
3587a984708SDavid Chisnall  * ABI function returning the __cxa_eh_globals structure.
3597a984708SDavid Chisnall  */
3607a984708SDavid Chisnall extern "C" __cxa_eh_globals *ABI_NAMESPACE::__cxa_get_globals(void)
3617a984708SDavid Chisnall {
3627a984708SDavid Chisnall 	return &(thread_info()->globals);
3637a984708SDavid Chisnall }
3647a984708SDavid Chisnall /**
3657a984708SDavid Chisnall  * Version of __cxa_get_globals() assuming that __cxa_get_globals() has already
3667a984708SDavid Chisnall  * been called at least once by this thread.
3677a984708SDavid Chisnall  */
3687a984708SDavid Chisnall extern "C" __cxa_eh_globals *ABI_NAMESPACE::__cxa_get_globals_fast(void)
3697a984708SDavid Chisnall {
3707a984708SDavid Chisnall 	return &(thread_info_fast()->globals);
3717a984708SDavid Chisnall }
3727a984708SDavid Chisnall 
3737a984708SDavid Chisnall /**
3747a984708SDavid Chisnall  * An emergency allocation reserved for when malloc fails.  This is treated as
3757a984708SDavid Chisnall  * 16 buffers of 1KB each.
3767a984708SDavid Chisnall  */
3777a984708SDavid Chisnall static char emergency_buffer[16384];
3787a984708SDavid Chisnall /**
3797a984708SDavid Chisnall  * Flag indicating whether each buffer is allocated.
3807a984708SDavid Chisnall  */
3817a984708SDavid Chisnall static bool buffer_allocated[16];
3827a984708SDavid Chisnall /**
3837a984708SDavid Chisnall  * Lock used to protect emergency allocation.
3847a984708SDavid Chisnall  */
3857a984708SDavid Chisnall static pthread_mutex_t emergency_malloc_lock = PTHREAD_MUTEX_INITIALIZER;
3867a984708SDavid Chisnall /**
3877a984708SDavid Chisnall  * Condition variable used to wait when two threads are both trying to use the
3887a984708SDavid Chisnall  * emergency malloc() buffer at once.
3897a984708SDavid Chisnall  */
3907a984708SDavid Chisnall static pthread_cond_t emergency_malloc_wait = PTHREAD_COND_INITIALIZER;
3917a984708SDavid Chisnall 
3927a984708SDavid Chisnall /**
3937a984708SDavid Chisnall  * Allocates size bytes from the emergency allocation mechanism, if possible.
3947a984708SDavid Chisnall  * This function will fail if size is over 1KB or if this thread already has 4
3957a984708SDavid Chisnall  * emergency buffers.  If all emergency buffers are allocated, it will sleep
3967a984708SDavid Chisnall  * until one becomes available.
3977a984708SDavid Chisnall  */
3987a984708SDavid Chisnall static char *emergency_malloc(size_t size)
3997a984708SDavid Chisnall {
4007a984708SDavid Chisnall 	if (size > 1024) { return 0; }
4017a984708SDavid Chisnall 
4027a984708SDavid Chisnall 	__cxa_thread_info *info = thread_info();
4037a984708SDavid Chisnall 	// Only 4 emergency buffers allowed per thread!
4047a984708SDavid Chisnall 	if (info->emergencyBuffersHeld > 3) { return 0; }
4057a984708SDavid Chisnall 
406*db47c4bfSDavid Chisnall 	if (pthread_mutex_lock)
407*db47c4bfSDavid Chisnall 	{
4087a984708SDavid Chisnall 		pthread_mutex_lock(&emergency_malloc_lock);
409*db47c4bfSDavid Chisnall 	}
4107a984708SDavid Chisnall 	int buffer = -1;
4117a984708SDavid Chisnall 	while (buffer < 0)
4127a984708SDavid Chisnall 	{
4137a984708SDavid Chisnall 		// While we were sleeping on the lock, another thread might have free'd
4147a984708SDavid Chisnall 		// enough memory for us to use, so try the allocation again - no point
4157a984708SDavid Chisnall 		// using the emergency buffer if there is some real memory that we can
4167a984708SDavid Chisnall 		// use...
4177a984708SDavid Chisnall 		void *m = calloc(1, size);
4187a984708SDavid Chisnall 		if (0 != m)
4197a984708SDavid Chisnall 		{
420*db47c4bfSDavid Chisnall 			if (pthread_mutex_unlock)
421*db47c4bfSDavid Chisnall 			{
4227a984708SDavid Chisnall 				pthread_mutex_unlock(&emergency_malloc_lock);
423*db47c4bfSDavid Chisnall 			}
4247a984708SDavid Chisnall 			return (char*)m;
4257a984708SDavid Chisnall 		}
4267a984708SDavid Chisnall 		for (int i=0 ; i<16 ; i++)
4277a984708SDavid Chisnall 		{
4287a984708SDavid Chisnall 			if (!buffer_allocated[i])
4297a984708SDavid Chisnall 			{
4307a984708SDavid Chisnall 				buffer = i;
4317a984708SDavid Chisnall 				buffer_allocated[i] = true;
4327a984708SDavid Chisnall 				break;
4337a984708SDavid Chisnall 			}
4347a984708SDavid Chisnall 		}
4357a984708SDavid Chisnall 		// If there still isn't a buffer available, then sleep on the condition
4367a984708SDavid Chisnall 		// variable.  This will be signalled when another thread releases one
4377a984708SDavid Chisnall 		// of the emergency buffers.
4387a984708SDavid Chisnall 		if (buffer < 0)
4397a984708SDavid Chisnall 		{
440*db47c4bfSDavid Chisnall 			// If we don't have pthread_cond_wait, then there is only one
441*db47c4bfSDavid Chisnall 			// thread and it's already used all of the emergency buffers, so we
442*db47c4bfSDavid Chisnall 			// have no alternative but to die.  Calling abort() instead of
443*db47c4bfSDavid Chisnall 			// terminate, because terminate can throw exceptions, which can
444*db47c4bfSDavid Chisnall 			// bring us back here and infinite loop.
445*db47c4bfSDavid Chisnall 			if (!pthread_cond_wait)
446*db47c4bfSDavid Chisnall 			{
447*db47c4bfSDavid Chisnall 				fputs("Terminating while out of memory trying to throw an exception",
448*db47c4bfSDavid Chisnall 				      stderr);
449*db47c4bfSDavid Chisnall 				abort();
450*db47c4bfSDavid Chisnall 			}
4517a984708SDavid Chisnall 			pthread_cond_wait(&emergency_malloc_wait, &emergency_malloc_lock);
4527a984708SDavid Chisnall 		}
4537a984708SDavid Chisnall 	}
454*db47c4bfSDavid Chisnall 	if (pthread_mutex_unlock)
455*db47c4bfSDavid Chisnall 	{
4567a984708SDavid Chisnall 		pthread_mutex_unlock(&emergency_malloc_lock);
457*db47c4bfSDavid Chisnall 	}
4587a984708SDavid Chisnall 	info->emergencyBuffersHeld++;
4597a984708SDavid Chisnall 	return emergency_buffer + (1024 * buffer);
4607a984708SDavid Chisnall }
4617a984708SDavid Chisnall 
4627a984708SDavid Chisnall /**
4637a984708SDavid Chisnall  * Frees a buffer returned by emergency_malloc().
4647a984708SDavid Chisnall  *
4657a984708SDavid Chisnall  * Note: Neither this nor emergency_malloc() is particularly efficient.  This
4667a984708SDavid Chisnall  * should not matter, because neither will be called in normal operation - they
4677a984708SDavid Chisnall  * are only used when the program runs out of memory, which should not happen
4687a984708SDavid Chisnall  * often.
4697a984708SDavid Chisnall  */
4707a984708SDavid Chisnall static void emergency_malloc_free(char *ptr)
4717a984708SDavid Chisnall {
4727a984708SDavid Chisnall 	int buffer = -1;
4737a984708SDavid Chisnall 	// Find the buffer corresponding to this pointer.
4747a984708SDavid Chisnall 	for (int i=0 ; i<16 ; i++)
4757a984708SDavid Chisnall 	{
4767a984708SDavid Chisnall 		if (ptr == (void*)(emergency_buffer + (1024 * i)))
4777a984708SDavid Chisnall 		{
4787a984708SDavid Chisnall 			buffer = i;
4797a984708SDavid Chisnall 			break;
4807a984708SDavid Chisnall 		}
4817a984708SDavid Chisnall 	}
4827a984708SDavid Chisnall 	assert(buffer > 0 &&
4837a984708SDavid Chisnall 	       "Trying to free something that is not an emergency buffer!");
4847a984708SDavid Chisnall 	// emergency_malloc() is expected to return 0-initialized data.  We don't
4857a984708SDavid Chisnall 	// zero the buffer when allocating it, because the static buffers will
4867a984708SDavid Chisnall 	// begin life containing 0 values.
4877a984708SDavid Chisnall 	memset((void*)ptr, 0, 1024);
4887a984708SDavid Chisnall 	// Signal the condition variable to wake up any threads that are blocking
4897a984708SDavid Chisnall 	// waiting for some space in the emergency buffer
490*db47c4bfSDavid Chisnall 	if (pthread_mutex_lock)
491*db47c4bfSDavid Chisnall 	{
4927a984708SDavid Chisnall 		pthread_mutex_lock(&emergency_malloc_lock);
493*db47c4bfSDavid Chisnall 	}
4947a984708SDavid Chisnall 	// In theory, we don't need to do this with the lock held.  In practice,
4957a984708SDavid Chisnall 	// our array of bools will probably be updated using 32-bit or 64-bit
4967a984708SDavid Chisnall 	// memory operations, so this update may clobber adjacent values.
4977a984708SDavid Chisnall 	buffer_allocated[buffer] = false;
498*db47c4bfSDavid Chisnall 	if (pthread_cond_signal && pthread_mutex_unlock)
499*db47c4bfSDavid Chisnall 	{
5007a984708SDavid Chisnall 		pthread_cond_signal(&emergency_malloc_wait);
5017a984708SDavid Chisnall 		pthread_mutex_unlock(&emergency_malloc_lock);
5027a984708SDavid Chisnall 	}
503*db47c4bfSDavid Chisnall }
5047a984708SDavid Chisnall 
5057a984708SDavid Chisnall static char *alloc_or_die(size_t size)
5067a984708SDavid Chisnall {
5077a984708SDavid Chisnall 	char *buffer = (char*)calloc(1, size);
5087a984708SDavid Chisnall 
5097a984708SDavid Chisnall 	// If calloc() doesn't want to give us any memory, try using an emergency
5107a984708SDavid Chisnall 	// buffer.
5117a984708SDavid Chisnall 	if (0 == buffer)
5127a984708SDavid Chisnall 	{
5137a984708SDavid Chisnall 		buffer = emergency_malloc(size);
5147a984708SDavid Chisnall 		// This is only reached if the allocation is greater than 1KB, and
5157a984708SDavid Chisnall 		// anyone throwing objects that big really should know better.
5167a984708SDavid Chisnall 		if (0 == buffer)
5177a984708SDavid Chisnall 		{
5187a984708SDavid Chisnall 			fprintf(stderr, "Out of memory attempting to allocate exception\n");
5197a984708SDavid Chisnall 			std::terminate();
5207a984708SDavid Chisnall 		}
5217a984708SDavid Chisnall 	}
5227a984708SDavid Chisnall 	return buffer;
5237a984708SDavid Chisnall }
5247a984708SDavid Chisnall static void free_exception(char *e)
5257a984708SDavid Chisnall {
5267a984708SDavid Chisnall 	// If this allocation is within the address range of the emergency buffer,
5277a984708SDavid Chisnall 	// don't call free() because it was not allocated with malloc()
5287a984708SDavid Chisnall 	if ((e > emergency_buffer) &&
5297a984708SDavid Chisnall 	    (e < (emergency_buffer + sizeof(emergency_buffer))))
5307a984708SDavid Chisnall 	{
5317a984708SDavid Chisnall 		emergency_malloc_free(e);
5327a984708SDavid Chisnall 	}
5337a984708SDavid Chisnall 	else
5347a984708SDavid Chisnall 	{
5357a984708SDavid Chisnall 		free(e);
5367a984708SDavid Chisnall 	}
5377a984708SDavid Chisnall }
5387a984708SDavid Chisnall 
5397a984708SDavid Chisnall /**
5407a984708SDavid Chisnall  * Allocates an exception structure.  Returns a pointer to the space that can
5417a984708SDavid Chisnall  * be used to store an object of thrown_size bytes.  This function will use an
5427a984708SDavid Chisnall  * emergency buffer if malloc() fails, and may block if there are no such
5437a984708SDavid Chisnall  * buffers available.
5447a984708SDavid Chisnall  */
5457a984708SDavid Chisnall extern "C" void *__cxa_allocate_exception(size_t thrown_size)
5467a984708SDavid Chisnall {
5477a984708SDavid Chisnall 	size_t size = thrown_size + sizeof(__cxa_exception);
5487a984708SDavid Chisnall 	char *buffer = alloc_or_die(size);
5497a984708SDavid Chisnall 	return buffer+sizeof(__cxa_exception);
5507a984708SDavid Chisnall }
5517a984708SDavid Chisnall 
5527a984708SDavid Chisnall extern "C" void *__cxa_allocate_dependent_exception(void)
5537a984708SDavid Chisnall {
5547a984708SDavid Chisnall 	size_t size = sizeof(__cxa_dependent_exception);
5557a984708SDavid Chisnall 	char *buffer = alloc_or_die(size);
5567a984708SDavid Chisnall 	return buffer+sizeof(__cxa_dependent_exception);
5577a984708SDavid Chisnall }
5587a984708SDavid Chisnall 
5597a984708SDavid Chisnall /**
5607a984708SDavid Chisnall  * __cxa_free_exception() is called when an exception was thrown in between
5617a984708SDavid Chisnall  * calling __cxa_allocate_exception() and actually throwing the exception.
5627a984708SDavid Chisnall  * This happens when the object's copy constructor throws an exception.
5637a984708SDavid Chisnall  *
5647a984708SDavid Chisnall  * In this implementation, it is also called by __cxa_end_catch() and during
5657a984708SDavid Chisnall  * thread cleanup.
5667a984708SDavid Chisnall  */
5677a984708SDavid Chisnall extern "C" void __cxa_free_exception(void *thrown_exception)
5687a984708SDavid Chisnall {
5697a984708SDavid Chisnall 	__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1;
5707a984708SDavid Chisnall 	// Free the object that was thrown, calling its destructor
5717a984708SDavid Chisnall 	if (0 != ex->exceptionDestructor)
5727a984708SDavid Chisnall 	{
5737a984708SDavid Chisnall 		try
5747a984708SDavid Chisnall 		{
5757a984708SDavid Chisnall 			ex->exceptionDestructor(thrown_exception);
5767a984708SDavid Chisnall 		}
5777a984708SDavid Chisnall 		catch(...)
5787a984708SDavid Chisnall 		{
5797a984708SDavid Chisnall 			// FIXME: Check that this is really what the spec says to do.
5807a984708SDavid Chisnall 			std::terminate();
5817a984708SDavid Chisnall 		}
5827a984708SDavid Chisnall 	}
5837a984708SDavid Chisnall 
5847a984708SDavid Chisnall 	free_exception((char*)ex);
5857a984708SDavid Chisnall }
5867a984708SDavid Chisnall 
5877a984708SDavid Chisnall static void releaseException(__cxa_exception *exception)
5887a984708SDavid Chisnall {
5897a984708SDavid Chisnall 	if (isDependentException(exception->unwindHeader.exception_class))
5907a984708SDavid Chisnall 	{
5917a984708SDavid Chisnall 		__cxa_free_dependent_exception(exception+1);
5927a984708SDavid Chisnall 		return;
5937a984708SDavid Chisnall 	}
5947a984708SDavid Chisnall 	if (__sync_sub_and_fetch(&exception->referenceCount, 1) == 0)
5957a984708SDavid Chisnall 	{
5967a984708SDavid Chisnall 		// __cxa_free_exception() expects to be passed the thrown object,
5977a984708SDavid Chisnall 		// which immediately follows the exception, not the exception
5987a984708SDavid Chisnall 		// itself
5997a984708SDavid Chisnall 		__cxa_free_exception(exception+1);
6007a984708SDavid Chisnall 	}
6017a984708SDavid Chisnall }
6027a984708SDavid Chisnall 
6037a984708SDavid Chisnall void __cxa_free_dependent_exception(void *thrown_exception)
6047a984708SDavid Chisnall {
6057a984708SDavid Chisnall 	__cxa_dependent_exception *ex = ((__cxa_dependent_exception*)thrown_exception) - 1;
6067a984708SDavid Chisnall 	assert(isDependentException(ex->unwindHeader.exception_class));
6077a984708SDavid Chisnall 	if (ex->primaryException)
6087a984708SDavid Chisnall 	{
6097a984708SDavid Chisnall 		releaseException(realExceptionFromException((__cxa_exception*)ex));
6107a984708SDavid Chisnall 	}
6117a984708SDavid Chisnall 	free_exception((char*)ex);
6127a984708SDavid Chisnall }
6137a984708SDavid Chisnall 
6147a984708SDavid Chisnall /**
6157a984708SDavid Chisnall  * Callback function used with _Unwind_Backtrace().
6167a984708SDavid Chisnall  *
6177a984708SDavid Chisnall  * Prints a stack trace.  Used only for debugging help.
6187a984708SDavid Chisnall  *
6197a984708SDavid Chisnall  * Note: As of FreeBSD 8.1, dladd() still doesn't work properly, so this only
6207a984708SDavid Chisnall  * correctly prints function names from public, relocatable, symbols.
6217a984708SDavid Chisnall  */
6227a984708SDavid Chisnall static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
6237a984708SDavid Chisnall {
6247a984708SDavid Chisnall 	Dl_info myinfo;
6257a984708SDavid Chisnall 	int mylookup =
6267a984708SDavid Chisnall 		dladdr((void*)(uintptr_t)__cxa_current_exception_type, &myinfo);
6277a984708SDavid Chisnall 	void *ip = (void*)_Unwind_GetIP(context);
6287a984708SDavid Chisnall 	Dl_info info;
6297a984708SDavid Chisnall 	if (dladdr(ip, &info) != 0)
6307a984708SDavid Chisnall 	{
6317a984708SDavid Chisnall 		if (mylookup == 0 || strcmp(info.dli_fname, myinfo.dli_fname) != 0)
6327a984708SDavid Chisnall 		{
6337a984708SDavid Chisnall 			printf("%p:%s() in %s\n", ip, info.dli_sname, info.dli_fname);
6347a984708SDavid Chisnall 		}
6357a984708SDavid Chisnall 	}
6367a984708SDavid Chisnall 	return _URC_CONTINUE_UNWIND;
6377a984708SDavid Chisnall }
6387a984708SDavid Chisnall 
6397a984708SDavid Chisnall /**
6407a984708SDavid Chisnall  * Report a failure that occurred when attempting to throw an exception.
6417a984708SDavid Chisnall  *
6427a984708SDavid Chisnall  * If the failure happened by falling off the end of the stack without finding
6437a984708SDavid Chisnall  * a handler, prints a back trace before aborting.
6447a984708SDavid Chisnall  */
6457a984708SDavid Chisnall static void report_failure(_Unwind_Reason_Code err, __cxa_exception *thrown_exception)
6467a984708SDavid Chisnall {
6477a984708SDavid Chisnall 	switch (err)
6487a984708SDavid Chisnall 	{
6497a984708SDavid Chisnall 		default: break;
6507a984708SDavid Chisnall 		case _URC_FATAL_PHASE1_ERROR:
6517a984708SDavid Chisnall 			fprintf(stderr, "Fatal error during phase 1 unwinding\n");
6527a984708SDavid Chisnall 			break;
6537a984708SDavid Chisnall #ifndef __arm__
6547a984708SDavid Chisnall 		case _URC_FATAL_PHASE2_ERROR:
6557a984708SDavid Chisnall 			fprintf(stderr, "Fatal error during phase 2 unwinding\n");
6567a984708SDavid Chisnall 			break;
6577a984708SDavid Chisnall #endif
6587a984708SDavid Chisnall 		case _URC_END_OF_STACK:
6597a984708SDavid Chisnall 			fprintf(stderr, "Terminating due to uncaught exception %p",
6607a984708SDavid Chisnall 					(void*)thrown_exception);
6617a984708SDavid Chisnall 			thrown_exception = realExceptionFromException(thrown_exception);
6627a984708SDavid Chisnall 			static const __class_type_info *e_ti =
6637a984708SDavid Chisnall 				static_cast<const __class_type_info*>(&typeid(std::exception));
6647a984708SDavid Chisnall 			const __class_type_info *throw_ti =
6657a984708SDavid Chisnall 				dynamic_cast<const __class_type_info*>(thrown_exception->exceptionType);
6667a984708SDavid Chisnall 			if (throw_ti)
6677a984708SDavid Chisnall 			{
6687a984708SDavid Chisnall 				std::exception *e =
6697a984708SDavid Chisnall 					(std::exception*)e_ti->cast_to((void*)(thrown_exception+1),
6707a984708SDavid Chisnall 							throw_ti);
6717a984708SDavid Chisnall 				if (e)
6727a984708SDavid Chisnall 				{
6737a984708SDavid Chisnall 					fprintf(stderr, " '%s'", e->what());
6747a984708SDavid Chisnall 				}
6757a984708SDavid Chisnall 			}
6767a984708SDavid Chisnall 
6777a984708SDavid Chisnall 			size_t bufferSize = 128;
6787a984708SDavid Chisnall 			char *demangled = (char*)malloc(bufferSize);
6797a984708SDavid Chisnall 			const char *mangled = thrown_exception->exceptionType->name();
6807a984708SDavid Chisnall 			int status;
6817a984708SDavid Chisnall 			demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status);
6827a984708SDavid Chisnall 			fprintf(stderr, " of type %s\n",
6837a984708SDavid Chisnall 				status == 0 ? (const char*)demangled : mangled);
6847a984708SDavid Chisnall 			if (status == 0) { free(demangled); }
6857a984708SDavid Chisnall 			// Print a back trace if no handler is found.
6867a984708SDavid Chisnall 			// TODO: Make this optional
6877a984708SDavid Chisnall 			_Unwind_Backtrace(trace, 0);
6887a984708SDavid Chisnall 			break;
6897a984708SDavid Chisnall 	}
6907a984708SDavid Chisnall 	std::terminate();
6917a984708SDavid Chisnall }
6927a984708SDavid Chisnall 
6937a984708SDavid Chisnall static void throw_exception(__cxa_exception *ex)
6947a984708SDavid Chisnall {
6957a984708SDavid Chisnall 	__cxa_thread_info *info = thread_info();
6967a984708SDavid Chisnall 	ex->unexpectedHandler = info->unexpectedHandler;
6977a984708SDavid Chisnall 	if (0 == ex->unexpectedHandler)
6987a984708SDavid Chisnall 	{
6997a984708SDavid Chisnall 		ex->unexpectedHandler = unexpectedHandler;
7007a984708SDavid Chisnall 	}
7017a984708SDavid Chisnall 	ex->terminateHandler  = info->terminateHandler;
7027a984708SDavid Chisnall 	if (0 == ex->terminateHandler)
7037a984708SDavid Chisnall 	{
7047a984708SDavid Chisnall 		ex->terminateHandler = terminateHandler;
7057a984708SDavid Chisnall 	}
7067a984708SDavid Chisnall 	info->globals.uncaughtExceptions++;
7077a984708SDavid Chisnall 
7087a984708SDavid Chisnall 	_Unwind_Reason_Code err = _Unwind_RaiseException(&ex->unwindHeader);
7097a984708SDavid Chisnall 	// The _Unwind_RaiseException() function should not return, it should
7107a984708SDavid Chisnall 	// unwind the stack past this function.  If it does return, then something
7117a984708SDavid Chisnall 	// has gone wrong.
7127a984708SDavid Chisnall 	report_failure(err, ex);
7137a984708SDavid Chisnall }
7147a984708SDavid Chisnall 
7157a984708SDavid Chisnall 
7167a984708SDavid Chisnall /**
7177a984708SDavid Chisnall  * ABI function for throwing an exception.  Takes the object to be thrown (the
7187a984708SDavid Chisnall  * pointer returned by __cxa_allocate_exception()), the type info for the
7197a984708SDavid Chisnall  * pointee, and the destructor (if there is one) as arguments.
7207a984708SDavid Chisnall  */
7217a984708SDavid Chisnall extern "C" void __cxa_throw(void *thrown_exception,
7227a984708SDavid Chisnall                             std::type_info *tinfo,
7237a984708SDavid Chisnall                             void(*dest)(void*))
7247a984708SDavid Chisnall {
7257a984708SDavid Chisnall 	__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1;
7267a984708SDavid Chisnall 
7277a984708SDavid Chisnall 	ex->referenceCount = 1;
7287a984708SDavid Chisnall 	ex->exceptionType = tinfo;
7297a984708SDavid Chisnall 
7307a984708SDavid Chisnall 	ex->exceptionDestructor = dest;
7317a984708SDavid Chisnall 
7327a984708SDavid Chisnall 	ex->unwindHeader.exception_class = exception_class;
7337a984708SDavid Chisnall 	ex->unwindHeader.exception_cleanup = exception_cleanup;
7347a984708SDavid Chisnall 
7357a984708SDavid Chisnall 	throw_exception(ex);
7367a984708SDavid Chisnall }
7377a984708SDavid Chisnall 
7387a984708SDavid Chisnall extern "C" void __cxa_rethrow_primary_exception(void* thrown_exception)
7397a984708SDavid Chisnall {
7407a984708SDavid Chisnall 	if (NULL == thrown_exception) { return; }
7417a984708SDavid Chisnall 
7427a984708SDavid Chisnall 	__cxa_exception *original = exceptionFromPointer(thrown_exception);
7437a984708SDavid Chisnall 	__cxa_dependent_exception *ex = ((__cxa_dependent_exception*)__cxa_allocate_dependent_exception())-1;
7447a984708SDavid Chisnall 
7457a984708SDavid Chisnall 	ex->primaryException = thrown_exception;
7467a984708SDavid Chisnall 	__cxa_increment_exception_refcount(thrown_exception);
7477a984708SDavid Chisnall 
7487a984708SDavid Chisnall 	ex->exceptionType = original->exceptionType;
7497a984708SDavid Chisnall 	ex->unwindHeader.exception_class = dependent_exception_class;
7507a984708SDavid Chisnall 	ex->unwindHeader.exception_cleanup = dependent_exception_cleanup;
7517a984708SDavid Chisnall 
7527a984708SDavid Chisnall 	throw_exception((__cxa_exception*)ex);
7537a984708SDavid Chisnall }
7547a984708SDavid Chisnall 
7557a984708SDavid Chisnall extern "C" void *__cxa_current_primary_exception(void)
7567a984708SDavid Chisnall {
7577a984708SDavid Chisnall 	__cxa_eh_globals* globals = __cxa_get_globals();
7587a984708SDavid Chisnall 	__cxa_exception *ex = globals->caughtExceptions;
7597a984708SDavid Chisnall 
7607a984708SDavid Chisnall 	if (0 == ex) { return NULL; }
7617a984708SDavid Chisnall 	ex = realExceptionFromException(ex);
7627a984708SDavid Chisnall 	__sync_fetch_and_add(&ex->referenceCount, 1);
7637a984708SDavid Chisnall 	return ex + 1;
7647a984708SDavid Chisnall }
7657a984708SDavid Chisnall 
7667a984708SDavid Chisnall extern "C" void __cxa_increment_exception_refcount(void* thrown_exception)
7677a984708SDavid Chisnall {
7687a984708SDavid Chisnall 	if (NULL == thrown_exception) { return; }
7697a984708SDavid Chisnall 	__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1;
7707a984708SDavid Chisnall 	if (isDependentException(ex->unwindHeader.exception_class)) { return; }
7717a984708SDavid Chisnall 	__sync_fetch_and_add(&ex->referenceCount, 1);
7727a984708SDavid Chisnall }
7737a984708SDavid Chisnall extern "C" void __cxa_decrement_exception_refcount(void* thrown_exception)
7747a984708SDavid Chisnall {
7757a984708SDavid Chisnall 	if (NULL == thrown_exception) { return; }
7767a984708SDavid Chisnall 	__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1;
7777a984708SDavid Chisnall 	releaseException(ex);
7787a984708SDavid Chisnall }
7797a984708SDavid Chisnall 
7807a984708SDavid Chisnall /**
7817a984708SDavid Chisnall  * ABI function.  Rethrows the current exception.  Does not remove the
7827a984708SDavid Chisnall  * exception from the stack or decrement its handler count - the compiler is
7837a984708SDavid Chisnall  * expected to set the landing pad for this function to the end of the catch
7847a984708SDavid Chisnall  * block, and then call _Unwind_Resume() to continue unwinding once
7857a984708SDavid Chisnall  * __cxa_end_catch() has been called and any cleanup code has been run.
7867a984708SDavid Chisnall  */
7877a984708SDavid Chisnall extern "C" void __cxa_rethrow()
7887a984708SDavid Chisnall {
7897a984708SDavid Chisnall 	__cxa_eh_globals *globals = __cxa_get_globals();
7907a984708SDavid Chisnall 	// Note: We don't remove this from the caught list here, because
7917a984708SDavid Chisnall 	// __cxa_end_catch will be called when we unwind out of the try block.  We
7927a984708SDavid Chisnall 	// could probably make this faster by providing an alternative rethrow
7937a984708SDavid Chisnall 	// function and ensuring that all cleanup code is run before calling it, so
7947a984708SDavid Chisnall 	// we can skip the top stack frame when unwinding.
7957a984708SDavid Chisnall 	__cxa_exception *ex = globals->caughtExceptions;
7967a984708SDavid Chisnall 
7977a984708SDavid Chisnall 	if (0 == ex)
7987a984708SDavid Chisnall 	{
7997a984708SDavid Chisnall 		fprintf(stderr,
8007a984708SDavid Chisnall 		        "Attempting to rethrow an exception that doesn't exist!\n");
8017a984708SDavid Chisnall 		std::terminate();
8027a984708SDavid Chisnall 	}
8037a984708SDavid Chisnall 
8047a984708SDavid Chisnall 	assert(ex->handlerCount > 0 && "Rethrowing uncaught exception!");
8057a984708SDavid Chisnall 
8067a984708SDavid Chisnall 	// ex->handlerCount will be decremented in __cxa_end_catch in enclosing
8077a984708SDavid Chisnall 	// catch block
8087a984708SDavid Chisnall 
8097a984708SDavid Chisnall 	// Make handler count negative. This will tell __cxa_end_catch that
8107a984708SDavid Chisnall 	// exception was rethrown and exception object should not be destroyed
8117a984708SDavid Chisnall 	// when handler count become zero
8127a984708SDavid Chisnall 	ex->handlerCount = -ex->handlerCount;
8137a984708SDavid Chisnall 
8147a984708SDavid Chisnall 	// Continue unwinding the stack with this exception.  This should unwind to
8157a984708SDavid Chisnall 	// the place in the caller where __cxa_end_catch() is called.  The caller
8167a984708SDavid Chisnall 	// will then run cleanup code and bounce the exception back with
8177a984708SDavid Chisnall 	// _Unwind_Resume().
8187a984708SDavid Chisnall 	_Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(&ex->unwindHeader);
8197a984708SDavid Chisnall 	report_failure(err, ex);
8207a984708SDavid Chisnall }
8217a984708SDavid Chisnall 
8227a984708SDavid Chisnall /**
8237a984708SDavid Chisnall  * Returns the type_info object corresponding to the filter.
8247a984708SDavid Chisnall  */
8257a984708SDavid Chisnall static std::type_info *get_type_info_entry(_Unwind_Context *context,
8267a984708SDavid Chisnall                                            dwarf_eh_lsda *lsda,
8277a984708SDavid Chisnall                                            int filter)
8287a984708SDavid Chisnall {
8297a984708SDavid Chisnall 	// Get the address of the record in the table.
8307a984708SDavid Chisnall 	dw_eh_ptr_t record = lsda->type_table -
8317a984708SDavid Chisnall 		dwarf_size_of_fixed_size_field(lsda->type_table_encoding)*filter;
8327a984708SDavid Chisnall 	//record -= 4;
8337a984708SDavid Chisnall 	dw_eh_ptr_t start = record;
8347a984708SDavid Chisnall 	// Read the value, but it's probably an indirect reference...
8357a984708SDavid Chisnall 	int64_t offset = read_value(lsda->type_table_encoding, &record);
8367a984708SDavid Chisnall 
8377a984708SDavid Chisnall 	// (If the entry is 0, don't try to dereference it.  That would be bad.)
8387a984708SDavid Chisnall 	if (offset == 0) { return 0; }
8397a984708SDavid Chisnall 
8407a984708SDavid Chisnall 	// ...so we need to resolve it
8417a984708SDavid Chisnall 	return (std::type_info*)resolve_indirect_value(context,
8427a984708SDavid Chisnall 			lsda->type_table_encoding, offset, start);
8437a984708SDavid Chisnall }
8447a984708SDavid Chisnall 
8457a984708SDavid Chisnall 
8467a984708SDavid Chisnall 
8477a984708SDavid Chisnall /**
8487a984708SDavid Chisnall  * Checks the type signature found in a handler against the type of the thrown
8497a984708SDavid Chisnall  * object.  If ex is 0 then it is assumed to be a foreign exception and only
8507a984708SDavid Chisnall  * matches cleanups.
8517a984708SDavid Chisnall  */
8527a984708SDavid Chisnall static bool check_type_signature(__cxa_exception *ex,
8537a984708SDavid Chisnall                                  const std::type_info *type,
8547a984708SDavid Chisnall                                  void *&adjustedPtr)
8557a984708SDavid Chisnall {
8567a984708SDavid Chisnall 	// TODO: For compatibility with the GNU implementation, we should move this
8577a984708SDavid Chisnall 	// out into a __do_catch() virtual function in std::type_info
8587a984708SDavid Chisnall 	void *exception_ptr = (void*)(ex+1);
8597a984708SDavid Chisnall     const std::type_info *ex_type = ex->exceptionType;
8607a984708SDavid Chisnall 
8617a984708SDavid Chisnall 	const __pointer_type_info *ptr_type =
8627a984708SDavid Chisnall 		dynamic_cast<const __pointer_type_info*>(ex_type);
8637a984708SDavid Chisnall 	if (0 != ptr_type)
8647a984708SDavid Chisnall 	{
8657a984708SDavid Chisnall 		exception_ptr = *(void**)exception_ptr;
8667a984708SDavid Chisnall 	}
8677a984708SDavid Chisnall 	// Always match a catchall, even with a foreign exception
8687a984708SDavid Chisnall 	//
8697a984708SDavid Chisnall 	// Note: A 0 here is a catchall, not a cleanup, so we return true to
8707a984708SDavid Chisnall 	// indicate that we found a catch.
8717a984708SDavid Chisnall 	//
8727a984708SDavid Chisnall 	// TODO: Provide a class for matching against foreign exceptions.  This is
8737a984708SDavid Chisnall 	// already done in libobjc2, allowing C++ exceptions to be boxed as
8747a984708SDavid Chisnall 	// Objective-C objects.  We should do something similar, allowing foreign
8757a984708SDavid Chisnall 	// exceptions to be wrapped in a C++ exception and delivered.
8767a984708SDavid Chisnall 	if (0 == type)
8777a984708SDavid Chisnall 	{
8787a984708SDavid Chisnall 		if (ex)
8797a984708SDavid Chisnall 		{
8807a984708SDavid Chisnall 			adjustedPtr = exception_ptr;
8817a984708SDavid Chisnall 		}
8827a984708SDavid Chisnall 		return true;
8837a984708SDavid Chisnall 	}
8847a984708SDavid Chisnall 
8857a984708SDavid Chisnall 	if (0 == ex) { return false; }
8867a984708SDavid Chisnall 
8877a984708SDavid Chisnall 	const __pointer_type_info *target_ptr_type =
8887a984708SDavid Chisnall 		dynamic_cast<const __pointer_type_info*>(type);
8897a984708SDavid Chisnall 
8907a984708SDavid Chisnall 	if (0 != ptr_type && 0 != target_ptr_type)
8917a984708SDavid Chisnall 	{
8927a984708SDavid Chisnall 		if (ptr_type->__flags & ~target_ptr_type->__flags)
8937a984708SDavid Chisnall 		{
8947a984708SDavid Chisnall 			// Handler pointer is less qualified
8957a984708SDavid Chisnall 			return false;
8967a984708SDavid Chisnall 		}
8977a984708SDavid Chisnall 
8987a984708SDavid Chisnall 		// Special case for void* handler.
8997a984708SDavid Chisnall 		if(*target_ptr_type->__pointee == typeid(void))
9007a984708SDavid Chisnall 		{
9017a984708SDavid Chisnall 			adjustedPtr = exception_ptr;
9027a984708SDavid Chisnall 			return true;
9037a984708SDavid Chisnall 		}
9047a984708SDavid Chisnall 
9057a984708SDavid Chisnall 		ex_type = ptr_type->__pointee;
9067a984708SDavid Chisnall 		type = target_ptr_type->__pointee;
9077a984708SDavid Chisnall 	}
9087a984708SDavid Chisnall 
9097a984708SDavid Chisnall 	// If the types are the same, no casting is needed.
9107a984708SDavid Chisnall 	if (*type == *ex_type)
9117a984708SDavid Chisnall 	{
9127a984708SDavid Chisnall 		adjustedPtr = exception_ptr;
9137a984708SDavid Chisnall 		return true;
9147a984708SDavid Chisnall 	}
9157a984708SDavid Chisnall 
9167a984708SDavid Chisnall 	const __class_type_info *cls_type =
9177a984708SDavid Chisnall 		dynamic_cast<const __class_type_info*>(ex_type);
9187a984708SDavid Chisnall 	const __class_type_info *target_cls_type =
9197a984708SDavid Chisnall 		dynamic_cast<const __class_type_info*>(type);
9207a984708SDavid Chisnall 
9217a984708SDavid Chisnall 	if (0 != cls_type &&
9227a984708SDavid Chisnall 		0 != target_cls_type &&
9237a984708SDavid Chisnall 		cls_type->can_cast_to(target_cls_type))
9247a984708SDavid Chisnall 	{
9257a984708SDavid Chisnall 		adjustedPtr = cls_type->cast_to(exception_ptr, target_cls_type);
9267a984708SDavid Chisnall 		return true;
9277a984708SDavid Chisnall 	}
9287a984708SDavid Chisnall 	return false;
9297a984708SDavid Chisnall }
9307a984708SDavid Chisnall /**
9317a984708SDavid Chisnall  * Checks whether the exception matches the type specifiers in this action
9327a984708SDavid Chisnall  * record.  If the exception only matches cleanups, then this returns false.
9337a984708SDavid Chisnall  * If it matches a catch (including a catchall) then it returns true.
9347a984708SDavid Chisnall  *
9357a984708SDavid Chisnall  * The selector argument is used to return the selector that is passed in the
9367a984708SDavid Chisnall  * second exception register when installing the context.
9377a984708SDavid Chisnall  */
9387a984708SDavid Chisnall static handler_type check_action_record(_Unwind_Context *context,
9397a984708SDavid Chisnall                                         dwarf_eh_lsda *lsda,
9407a984708SDavid Chisnall                                         dw_eh_ptr_t action_record,
9417a984708SDavid Chisnall                                         __cxa_exception *ex,
9427a984708SDavid Chisnall                                         unsigned long *selector,
9437a984708SDavid Chisnall                                         void *&adjustedPtr)
9447a984708SDavid Chisnall {
9457a984708SDavid Chisnall 	if (!action_record) { return handler_cleanup; }
9467a984708SDavid Chisnall 	handler_type found = handler_none;
9477a984708SDavid Chisnall 	while (action_record)
9487a984708SDavid Chisnall 	{
9497a984708SDavid Chisnall 		int filter = read_sleb128(&action_record);
9507a984708SDavid Chisnall 		dw_eh_ptr_t action_record_offset_base = action_record;
9517a984708SDavid Chisnall 		int displacement = read_sleb128(&action_record);
9527a984708SDavid Chisnall 		action_record = displacement ?
9537a984708SDavid Chisnall 			action_record_offset_base + displacement : 0;
9547a984708SDavid Chisnall 		// We only check handler types for C++ exceptions - foreign exceptions
9557a984708SDavid Chisnall 		// are only allowed for cleanup.
9567a984708SDavid Chisnall 		if (filter > 0 && 0 != ex)
9577a984708SDavid Chisnall 		{
9587a984708SDavid Chisnall 			std::type_info *handler_type = get_type_info_entry(context, lsda, filter);
9597a984708SDavid Chisnall 			if (check_type_signature(ex, handler_type, adjustedPtr))
9607a984708SDavid Chisnall 			{
9617a984708SDavid Chisnall 				*selector = filter;
9627a984708SDavid Chisnall 				return handler_catch;
9637a984708SDavid Chisnall 			}
9647a984708SDavid Chisnall 		}
9657a984708SDavid Chisnall 		else if (filter < 0 && 0 != ex)
9667a984708SDavid Chisnall 		{
9677a984708SDavid Chisnall 			bool matched = false;
9687a984708SDavid Chisnall 			*selector = filter;
9697a984708SDavid Chisnall #ifdef __arm__
9707a984708SDavid Chisnall 			filter++;
9717a984708SDavid Chisnall 			std::type_info *handler_type = get_type_info_entry(context, lsda, filter--);
9727a984708SDavid Chisnall 			while (handler_type)
9737a984708SDavid Chisnall 			{
9747a984708SDavid Chisnall 				if (check_type_signature(ex, handler_type, adjustedPtr))
9757a984708SDavid Chisnall 				{
9767a984708SDavid Chisnall 					matched = true;
9777a984708SDavid Chisnall 					break;
9787a984708SDavid Chisnall 				}
9797a984708SDavid Chisnall 				handler_type = get_type_info_entry(context, lsda, filter--);
9807a984708SDavid Chisnall 			}
9817a984708SDavid Chisnall #else
9827a984708SDavid Chisnall 			unsigned char *type_index = ((unsigned char*)lsda->type_table - filter - 1);
9837a984708SDavid Chisnall 			while (*type_index)
9847a984708SDavid Chisnall 			{
9857a984708SDavid Chisnall 				std::type_info *handler_type = get_type_info_entry(context, lsda, *(type_index++));
9867a984708SDavid Chisnall 				// If the exception spec matches a permitted throw type for
9877a984708SDavid Chisnall 				// this function, don't report a handler - we are allowed to
9887a984708SDavid Chisnall 				// propagate this exception out.
9897a984708SDavid Chisnall 				if (check_type_signature(ex, handler_type, adjustedPtr))
9907a984708SDavid Chisnall 				{
9917a984708SDavid Chisnall 					matched = true;
9927a984708SDavid Chisnall 					break;
9937a984708SDavid Chisnall 				}
9947a984708SDavid Chisnall 			}
9957a984708SDavid Chisnall #endif
9967a984708SDavid Chisnall 			if (matched) { continue; }
9977a984708SDavid Chisnall 			// If we don't find an allowed exception spec, we need to install
9987a984708SDavid Chisnall 			// the context for this action.  The landing pad will then call the
9997a984708SDavid Chisnall 			// unexpected exception function.  Treat this as a catch
10007a984708SDavid Chisnall 			return handler_catch;
10017a984708SDavid Chisnall 		}
10027a984708SDavid Chisnall 		else if (filter == 0)
10037a984708SDavid Chisnall 		{
10047a984708SDavid Chisnall 			*selector = filter;
10057a984708SDavid Chisnall 			found = handler_cleanup;
10067a984708SDavid Chisnall 		}
10077a984708SDavid Chisnall 	}
10087a984708SDavid Chisnall 	return found;
10097a984708SDavid Chisnall }
10107a984708SDavid Chisnall 
10117a984708SDavid Chisnall static void pushCleanupException(_Unwind_Exception *exceptionObject,
10127a984708SDavid Chisnall                                  __cxa_exception *ex)
10137a984708SDavid Chisnall {
10147a984708SDavid Chisnall #ifdef __arm__
10157a984708SDavid Chisnall 	__cxa_thread_info *info = thread_info_fast();
10167a984708SDavid Chisnall 	if (ex)
10177a984708SDavid Chisnall 	{
10187a984708SDavid Chisnall 		ex->cleanupCount++;
10197a984708SDavid Chisnall 		if (ex->cleanupCount > 1)
10207a984708SDavid Chisnall 		{
10217a984708SDavid Chisnall 			assert(exceptionObject == info->currentCleanup);
10227a984708SDavid Chisnall 			return;
10237a984708SDavid Chisnall 		}
10247a984708SDavid Chisnall 		ex->nextCleanup = info->currentCleanup;
10257a984708SDavid Chisnall 	}
10267a984708SDavid Chisnall 	info->currentCleanup = exceptionObject;
10277a984708SDavid Chisnall #endif
10287a984708SDavid Chisnall }
10297a984708SDavid Chisnall 
10307a984708SDavid Chisnall /**
10317a984708SDavid Chisnall  * The exception personality function.  This is referenced in the unwinding
10327a984708SDavid Chisnall  * DWARF metadata and is called by the unwind library for each C++ stack frame
10337a984708SDavid Chisnall  * containing catch or cleanup code.
10347a984708SDavid Chisnall  */
10357a984708SDavid Chisnall extern "C"
10367a984708SDavid Chisnall BEGIN_PERSONALITY_FUNCTION(__gxx_personality_v0)
10377a984708SDavid Chisnall 	// This personality function is for version 1 of the ABI.  If you use it
10387a984708SDavid Chisnall 	// with a future version of the ABI, it won't know what to do, so it
10397a984708SDavid Chisnall 	// reports a fatal error and give up before it breaks anything.
10407a984708SDavid Chisnall 	if (1 != version)
10417a984708SDavid Chisnall 	{
10427a984708SDavid Chisnall 		return _URC_FATAL_PHASE1_ERROR;
10437a984708SDavid Chisnall 	}
10447a984708SDavid Chisnall 	__cxa_exception *ex = 0;
10457a984708SDavid Chisnall 	__cxa_exception *realEx = 0;
10467a984708SDavid Chisnall 
10477a984708SDavid Chisnall 	// If this exception is throw by something else then we can't make any
10487a984708SDavid Chisnall 	// assumptions about its layout beyond the fields declared in
10497a984708SDavid Chisnall 	// _Unwind_Exception.
10507a984708SDavid Chisnall 	bool foreignException = !isCXXException(exceptionClass);
10517a984708SDavid Chisnall 
10527a984708SDavid Chisnall 	// If this isn't a foreign exception, then we have a C++ exception structure
10537a984708SDavid Chisnall 	if (!foreignException)
10547a984708SDavid Chisnall 	{
10557a984708SDavid Chisnall 		ex = exceptionFromPointer(exceptionObject);
10567a984708SDavid Chisnall 		realEx = realExceptionFromException(ex);
10577a984708SDavid Chisnall 	}
10587a984708SDavid Chisnall 
10597a984708SDavid Chisnall 	unsigned char *lsda_addr =
10607a984708SDavid Chisnall 		(unsigned char*)_Unwind_GetLanguageSpecificData(context);
10617a984708SDavid Chisnall 
10627a984708SDavid Chisnall 	// No LSDA implies no landing pads - try the next frame
10637a984708SDavid Chisnall 	if (0 == lsda_addr) { return continueUnwinding(exceptionObject, context); }
10647a984708SDavid Chisnall 
10657a984708SDavid Chisnall 	// These two variables define how the exception will be handled.
10667a984708SDavid Chisnall 	dwarf_eh_action action = {0};
10677a984708SDavid Chisnall 	unsigned long selector = 0;
10687a984708SDavid Chisnall 
10697a984708SDavid Chisnall 	// During the search phase, we do a complete lookup.  If we return
10707a984708SDavid Chisnall 	// _URC_HANDLER_FOUND, then the phase 2 unwind will call this function with
10717a984708SDavid Chisnall 	// a _UA_HANDLER_FRAME action, telling us to install the handler frame.  If
10727a984708SDavid Chisnall 	// we return _URC_CONTINUE_UNWIND, we may be called again later with a
10737a984708SDavid Chisnall 	// _UA_CLEANUP_PHASE action for this frame.
10747a984708SDavid Chisnall 	//
10757a984708SDavid Chisnall 	// The point of the two-stage unwind allows us to entirely avoid any stack
10767a984708SDavid Chisnall 	// unwinding if there is no handler.  If there are just cleanups found,
10777a984708SDavid Chisnall 	// then we can just panic call an abort function.
10787a984708SDavid Chisnall 	//
10797a984708SDavid Chisnall 	// Matching a handler is much more expensive than matching a cleanup,
10807a984708SDavid Chisnall 	// because we don't need to bother doing type comparisons (or looking at
10817a984708SDavid Chisnall 	// the type table at all) for a cleanup.  This means that there is no need
10827a984708SDavid Chisnall 	// to cache the result of finding a cleanup, because it's (quite) quick to
10837a984708SDavid Chisnall 	// look it up again from the action table.
10847a984708SDavid Chisnall 	if (actions & _UA_SEARCH_PHASE)
10857a984708SDavid Chisnall 	{
10867a984708SDavid Chisnall 		struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
10877a984708SDavid Chisnall 
10887a984708SDavid Chisnall 		if (!dwarf_eh_find_callsite(context, &lsda, &action))
10897a984708SDavid Chisnall 		{
10907a984708SDavid Chisnall 			// EH range not found. This happens if exception is thrown and not
10917a984708SDavid Chisnall 			// caught inside a cleanup (destructor).  We should call
10927a984708SDavid Chisnall 			// terminate() in this case.  The catchTemp (landing pad) field of
10937a984708SDavid Chisnall 			// exception object will contain null when personality function is
10947a984708SDavid Chisnall 			// called with _UA_HANDLER_FRAME action for phase 2 unwinding.
10957a984708SDavid Chisnall 			return _URC_HANDLER_FOUND;
10967a984708SDavid Chisnall 		}
10977a984708SDavid Chisnall 
10987a984708SDavid Chisnall 		handler_type found_handler = check_action_record(context, &lsda,
10997a984708SDavid Chisnall 				action.action_record, realEx, &selector, ex->adjustedPtr);
11007a984708SDavid Chisnall 		// If there's no action record, we've only found a cleanup, so keep
11017a984708SDavid Chisnall 		// searching for something real
11027a984708SDavid Chisnall 		if (found_handler == handler_catch)
11037a984708SDavid Chisnall 		{
11047a984708SDavid Chisnall 			// Cache the results for the phase 2 unwind, if we found a handler
11057a984708SDavid Chisnall 			// and this is not a foreign exception.
11067a984708SDavid Chisnall 			if (ex)
11077a984708SDavid Chisnall 			{
11087a984708SDavid Chisnall 				saveLandingPad(context, exceptionObject, ex, selector, action.landing_pad);
11097a984708SDavid Chisnall 				ex->languageSpecificData = (const char*)lsda_addr;
11107a984708SDavid Chisnall 				ex->actionRecord = (const char*)action.action_record;
11117a984708SDavid Chisnall 				// ex->adjustedPtr is set when finding the action record.
11127a984708SDavid Chisnall 			}
11137a984708SDavid Chisnall 			return _URC_HANDLER_FOUND;
11147a984708SDavid Chisnall 		}
11157a984708SDavid Chisnall 		return continueUnwinding(exceptionObject, context);
11167a984708SDavid Chisnall 	}
11177a984708SDavid Chisnall 
11187a984708SDavid Chisnall 
11197a984708SDavid Chisnall 	// If this is a foreign exception, we didn't have anywhere to cache the
11207a984708SDavid Chisnall 	// lookup stuff, so we need to do it again.  If this is either a forced
11217a984708SDavid Chisnall 	// unwind, a foreign exception, or a cleanup, then we just install the
11227a984708SDavid Chisnall 	// context for a cleanup.
11237a984708SDavid Chisnall 	if (!(actions & _UA_HANDLER_FRAME))
11247a984708SDavid Chisnall 	{
11257a984708SDavid Chisnall 		// cleanup
11267a984708SDavid Chisnall 		struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
11277a984708SDavid Chisnall 		dwarf_eh_find_callsite(context, &lsda, &action);
11287a984708SDavid Chisnall 		if (0 == action.landing_pad) { return continueUnwinding(exceptionObject, context); }
11297a984708SDavid Chisnall 		handler_type found_handler = check_action_record(context, &lsda,
11307a984708SDavid Chisnall 				action.action_record, realEx, &selector, ex->adjustedPtr);
11317a984708SDavid Chisnall 		// Ignore handlers this time.
11327a984708SDavid Chisnall 		if (found_handler != handler_cleanup) { return continueUnwinding(exceptionObject, context); }
11337a984708SDavid Chisnall 		pushCleanupException(exceptionObject, ex);
11347a984708SDavid Chisnall 	}
11357a984708SDavid Chisnall 	else if (foreignException)
11367a984708SDavid Chisnall 	{
11377a984708SDavid Chisnall 		struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
11387a984708SDavid Chisnall 		dwarf_eh_find_callsite(context, &lsda, &action);
11397a984708SDavid Chisnall 		check_action_record(context, &lsda, action.action_record, realEx,
11407a984708SDavid Chisnall 				&selector, ex->adjustedPtr);
11417a984708SDavid Chisnall 	}
11427a984708SDavid Chisnall 	else if (ex->catchTemp == 0)
11437a984708SDavid Chisnall 	{
11447a984708SDavid Chisnall 		// Uncaught exception in cleanup, calling terminate
11457a984708SDavid Chisnall 		std::terminate();
11467a984708SDavid Chisnall 	}
11477a984708SDavid Chisnall 	else
11487a984708SDavid Chisnall 	{
11497a984708SDavid Chisnall 		// Restore the saved info if we saved some last time.
11507a984708SDavid Chisnall 		loadLandingPad(context, exceptionObject, ex, &selector, &action.landing_pad);
11517a984708SDavid Chisnall 		ex->catchTemp = 0;
11527a984708SDavid Chisnall 		ex->handlerSwitchValue = 0;
11537a984708SDavid Chisnall 	}
11547a984708SDavid Chisnall 
11557a984708SDavid Chisnall 
11567a984708SDavid Chisnall 	_Unwind_SetIP(context, (unsigned long)action.landing_pad);
11577a984708SDavid Chisnall 	_Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
11587a984708SDavid Chisnall 	              (unsigned long)exceptionObject);
11597a984708SDavid Chisnall 	_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), selector);
11607a984708SDavid Chisnall 
11617a984708SDavid Chisnall 	return _URC_INSTALL_CONTEXT;
11627a984708SDavid Chisnall }
11637a984708SDavid Chisnall 
11647a984708SDavid Chisnall /**
11657a984708SDavid Chisnall  * ABI function called when entering a catch statement.  The argument is the
11667a984708SDavid Chisnall  * pointer passed out of the personality function.  This is always the start of
11677a984708SDavid Chisnall  * the _Unwind_Exception object.  The return value for this function is the
11687a984708SDavid Chisnall  * pointer to the caught exception, which is either the adjusted pointer (for
11697a984708SDavid Chisnall  * C++ exceptions) of the unadjusted pointer (for foreign exceptions).
11707a984708SDavid Chisnall  */
11717a984708SDavid Chisnall #if __GNUC__ > 3 && __GNUC_MINOR__ > 2
11727a984708SDavid Chisnall extern "C" void *__cxa_begin_catch(void *e) throw()
11737a984708SDavid Chisnall #else
11747a984708SDavid Chisnall extern "C" void *__cxa_begin_catch(void *e)
11757a984708SDavid Chisnall #endif
11767a984708SDavid Chisnall {
11777a984708SDavid Chisnall 	// Decrement the uncaught exceptions count
11787a984708SDavid Chisnall 	__cxa_eh_globals *globals = __cxa_get_globals();
11797a984708SDavid Chisnall 	globals->uncaughtExceptions--;
11807a984708SDavid Chisnall 	_Unwind_Exception *exceptionObject = (_Unwind_Exception*)e;
11817a984708SDavid Chisnall 
11827a984708SDavid Chisnall 	if (isCXXException(exceptionObject->exception_class))
11837a984708SDavid Chisnall 	{
11847a984708SDavid Chisnall 		__cxa_exception *ex =  exceptionFromPointer(exceptionObject);
11857a984708SDavid Chisnall 
11867a984708SDavid Chisnall 		if (ex->handlerCount == 0)
11877a984708SDavid Chisnall 		{
11887a984708SDavid Chisnall 			// Add this to the front of the list of exceptions being handled
11897a984708SDavid Chisnall 			// and increment its handler count so that it won't be deleted
11907a984708SDavid Chisnall 			// prematurely.
11917a984708SDavid Chisnall 			ex->nextException = globals->caughtExceptions;
11927a984708SDavid Chisnall 			globals->caughtExceptions = ex;
11937a984708SDavid Chisnall 		}
11947a984708SDavid Chisnall 
11957a984708SDavid Chisnall 		if (ex->handlerCount < 0)
11967a984708SDavid Chisnall 		{
11977a984708SDavid Chisnall 			// Rethrown exception is catched before end of catch block.
11987a984708SDavid Chisnall 			// Clear the rethrow flag (make value positive) - we are allowed
11997a984708SDavid Chisnall 			// to delete this exception at the end of the catch block, as long
12007a984708SDavid Chisnall 			// as it isn't thrown again later.
12017a984708SDavid Chisnall 
12027a984708SDavid Chisnall 			// Code pattern:
12037a984708SDavid Chisnall 			//
12047a984708SDavid Chisnall 			// try {
12057a984708SDavid Chisnall 			//     throw x;
12067a984708SDavid Chisnall 			// }
12077a984708SDavid Chisnall 			// catch() {
12087a984708SDavid Chisnall 			//     try {
12097a984708SDavid Chisnall 			//         throw;
12107a984708SDavid Chisnall 			//     }
12117a984708SDavid Chisnall 			//     catch() {
12127a984708SDavid Chisnall 			//         __cxa_begin_catch() <- we are here
12137a984708SDavid Chisnall 			//     }
12147a984708SDavid Chisnall 			// }
12157a984708SDavid Chisnall 			ex->handlerCount = -ex->handlerCount + 1;
12167a984708SDavid Chisnall 		}
12177a984708SDavid Chisnall 		else
12187a984708SDavid Chisnall 		{
12197a984708SDavid Chisnall 			ex->handlerCount++;
12207a984708SDavid Chisnall 		}
12217a984708SDavid Chisnall 
12227a984708SDavid Chisnall 		return ex->adjustedPtr;
12237a984708SDavid Chisnall 	}
12247a984708SDavid Chisnall 	// exceptionObject is the pointer to the _Unwind_Exception within the
12257a984708SDavid Chisnall 	// __cxa_exception.  The throw object is after this
12267a984708SDavid Chisnall 	return ((char*)exceptionObject + sizeof(_Unwind_Exception));
12277a984708SDavid Chisnall }
12287a984708SDavid Chisnall 
12297a984708SDavid Chisnall 
12307a984708SDavid Chisnall 
12317a984708SDavid Chisnall /**
12327a984708SDavid Chisnall  * ABI function called when exiting a catch block.  This will free the current
12337a984708SDavid Chisnall  * exception if it is no longer referenced in other catch blocks.
12347a984708SDavid Chisnall  */
12357a984708SDavid Chisnall extern "C" void __cxa_end_catch()
12367a984708SDavid Chisnall {
12377a984708SDavid Chisnall 	// We can call the fast version here because the slow version is called in
12387a984708SDavid Chisnall 	// __cxa_throw(), which must have been called before we end a catch block
12397a984708SDavid Chisnall 	__cxa_eh_globals *globals = __cxa_get_globals_fast();
12407a984708SDavid Chisnall 	__cxa_exception *ex = globals->caughtExceptions;
12417a984708SDavid Chisnall 
12427a984708SDavid Chisnall 	assert(0 != ex && "Ending catch when no exception is on the stack!");
12437a984708SDavid Chisnall 
12447a984708SDavid Chisnall 	bool deleteException = true;
12457a984708SDavid Chisnall 
12467a984708SDavid Chisnall 	if (ex->handlerCount < 0)
12477a984708SDavid Chisnall 	{
12487a984708SDavid Chisnall 		// exception was rethrown. Exception should not be deleted even if
12497a984708SDavid Chisnall 		// handlerCount become zero.
12507a984708SDavid Chisnall 		// Code pattern:
12517a984708SDavid Chisnall 		// try {
12527a984708SDavid Chisnall 		//     throw x;
12537a984708SDavid Chisnall 		// }
12547a984708SDavid Chisnall 		// catch() {
12557a984708SDavid Chisnall 		//     {
12567a984708SDavid Chisnall 		//         throw;
12577a984708SDavid Chisnall 		//     }
12587a984708SDavid Chisnall 		//     cleanup {
12597a984708SDavid Chisnall 		//         __cxa_end_catch();   <- we are here
12607a984708SDavid Chisnall 		//     }
12617a984708SDavid Chisnall 		// }
12627a984708SDavid Chisnall 		//
12637a984708SDavid Chisnall 
12647a984708SDavid Chisnall 		ex->handlerCount++;
12657a984708SDavid Chisnall 		deleteException = false;
12667a984708SDavid Chisnall 	}
12677a984708SDavid Chisnall 	else
12687a984708SDavid Chisnall 	{
12697a984708SDavid Chisnall 		ex->handlerCount--;
12707a984708SDavid Chisnall 	}
12717a984708SDavid Chisnall 
12727a984708SDavid Chisnall 	if (ex->handlerCount == 0)
12737a984708SDavid Chisnall 	{
12747a984708SDavid Chisnall 		globals->caughtExceptions = ex->nextException;
12757a984708SDavid Chisnall 		if (deleteException)
12767a984708SDavid Chisnall 		{
12777a984708SDavid Chisnall 			releaseException(ex);
12787a984708SDavid Chisnall 		}
12797a984708SDavid Chisnall 	}
12807a984708SDavid Chisnall }
12817a984708SDavid Chisnall 
12827a984708SDavid Chisnall /**
12837a984708SDavid Chisnall  * ABI function.  Returns the type of the current exception.
12847a984708SDavid Chisnall  */
12857a984708SDavid Chisnall extern "C" std::type_info *__cxa_current_exception_type()
12867a984708SDavid Chisnall {
12877a984708SDavid Chisnall 	__cxa_eh_globals *globals = __cxa_get_globals();
12887a984708SDavid Chisnall 	__cxa_exception *ex = globals->caughtExceptions;
12897a984708SDavid Chisnall 	return ex ? ex->exceptionType : 0;
12907a984708SDavid Chisnall }
12917a984708SDavid Chisnall 
12927a984708SDavid Chisnall /**
12937a984708SDavid Chisnall  * ABI function, called when an exception specification is violated.
12947a984708SDavid Chisnall  *
12957a984708SDavid Chisnall  * This function does not return.
12967a984708SDavid Chisnall  */
12977a984708SDavid Chisnall extern "C" void __cxa_call_unexpected(void*exception)
12987a984708SDavid Chisnall {
12997a984708SDavid Chisnall 	_Unwind_Exception *exceptionObject = (_Unwind_Exception*)exception;
13007a984708SDavid Chisnall 	if (exceptionObject->exception_class == exception_class)
13017a984708SDavid Chisnall 	{
13027a984708SDavid Chisnall 		__cxa_exception *ex =  exceptionFromPointer(exceptionObject);
13037a984708SDavid Chisnall 		if (ex->unexpectedHandler)
13047a984708SDavid Chisnall 		{
13057a984708SDavid Chisnall 			ex->unexpectedHandler();
13067a984708SDavid Chisnall 			// Should not be reached.
13077a984708SDavid Chisnall 			abort();
13087a984708SDavid Chisnall 		}
13097a984708SDavid Chisnall 	}
13107a984708SDavid Chisnall 	std::unexpected();
13117a984708SDavid Chisnall 	// Should not be reached.
13127a984708SDavid Chisnall 	abort();
13137a984708SDavid Chisnall }
13147a984708SDavid Chisnall 
13157a984708SDavid Chisnall /**
13167a984708SDavid Chisnall  * ABI function, returns the adjusted pointer to the exception object.
13177a984708SDavid Chisnall  */
13187a984708SDavid Chisnall extern "C" void *__cxa_get_exception_ptr(void *exceptionObject)
13197a984708SDavid Chisnall {
13207a984708SDavid Chisnall 	return exceptionFromPointer(exceptionObject)->adjustedPtr;
13217a984708SDavid Chisnall }
13227a984708SDavid Chisnall 
13237a984708SDavid Chisnall /**
13247a984708SDavid Chisnall  * As an extension, we provide the ability for the unexpected and terminate
13257a984708SDavid Chisnall  * handlers to be thread-local.  We default to the standards-compliant
13267a984708SDavid Chisnall  * behaviour where they are global.
13277a984708SDavid Chisnall  */
13287a984708SDavid Chisnall static bool thread_local_handlers = false;
13297a984708SDavid Chisnall 
13307a984708SDavid Chisnall 
13317a984708SDavid Chisnall namespace pathscale
13327a984708SDavid Chisnall {
13337a984708SDavid Chisnall 	/**
13347a984708SDavid Chisnall 	 * Sets whether unexpected and terminate handlers should be thread-local.
13357a984708SDavid Chisnall 	 */
13367a984708SDavid Chisnall 	void set_use_thread_local_handlers(bool flag) throw()
13377a984708SDavid Chisnall 	{
13387a984708SDavid Chisnall 		thread_local_handlers = flag;
13397a984708SDavid Chisnall 	}
13407a984708SDavid Chisnall 	/**
13417a984708SDavid Chisnall 	 * Sets a thread-local unexpected handler.
13427a984708SDavid Chisnall 	 */
13437a984708SDavid Chisnall 	unexpected_handler set_unexpected(unexpected_handler f) throw()
13447a984708SDavid Chisnall 	{
13457a984708SDavid Chisnall 		static __cxa_thread_info *info = thread_info();
13467a984708SDavid Chisnall 		unexpected_handler old = info->unexpectedHandler;
13477a984708SDavid Chisnall 		info->unexpectedHandler = f;
13487a984708SDavid Chisnall 		return old;
13497a984708SDavid Chisnall 	}
13507a984708SDavid Chisnall 	/**
13517a984708SDavid Chisnall 	 * Sets a thread-local terminate handler.
13527a984708SDavid Chisnall 	 */
13537a984708SDavid Chisnall 	terminate_handler set_terminate(terminate_handler f) throw()
13547a984708SDavid Chisnall 	{
13557a984708SDavid Chisnall 		static __cxa_thread_info *info = thread_info();
13567a984708SDavid Chisnall 		terminate_handler old = info->terminateHandler;
13577a984708SDavid Chisnall 		info->terminateHandler = f;
13587a984708SDavid Chisnall 		return old;
13597a984708SDavid Chisnall 	}
13607a984708SDavid Chisnall }
13617a984708SDavid Chisnall 
13627a984708SDavid Chisnall namespace std
13637a984708SDavid Chisnall {
13647a984708SDavid Chisnall 	/**
13657a984708SDavid Chisnall 	 * Sets the function that will be called when an exception specification is
13667a984708SDavid Chisnall 	 * violated.
13677a984708SDavid Chisnall 	 */
13687a984708SDavid Chisnall 	unexpected_handler set_unexpected(unexpected_handler f) throw()
13697a984708SDavid Chisnall 	{
13707a984708SDavid Chisnall 		if (thread_local_handlers) { return pathscale::set_unexpected(f); }
13717a984708SDavid Chisnall 
13727a984708SDavid Chisnall 		return __sync_lock_test_and_set(&unexpectedHandler, f);
13737a984708SDavid Chisnall 	}
13747a984708SDavid Chisnall 	/**
13757a984708SDavid Chisnall 	 * Sets the function that is called to terminate the program.
13767a984708SDavid Chisnall 	 */
13777a984708SDavid Chisnall 	terminate_handler set_terminate(terminate_handler f) throw()
13787a984708SDavid Chisnall 	{
13797a984708SDavid Chisnall 		if (thread_local_handlers) { return pathscale::set_terminate(f); }
13807a984708SDavid Chisnall 		return __sync_lock_test_and_set(&terminateHandler, f);
13817a984708SDavid Chisnall 	}
13827a984708SDavid Chisnall 	/**
13837a984708SDavid Chisnall 	 * Terminates the program, calling a custom terminate implementation if
13847a984708SDavid Chisnall 	 * required.
13857a984708SDavid Chisnall 	 */
13867a984708SDavid Chisnall 	void terminate()
13877a984708SDavid Chisnall 	{
13887a984708SDavid Chisnall 		static __cxa_thread_info *info = thread_info_fast();
13897a984708SDavid Chisnall 		if (0 != info && 0 != info->terminateHandler)
13907a984708SDavid Chisnall 		{
13917a984708SDavid Chisnall 			info->terminateHandler();
13927a984708SDavid Chisnall 			// Should not be reached - a terminate handler is not expected to
13937a984708SDavid Chisnall 			// return.
13947a984708SDavid Chisnall 			abort();
13957a984708SDavid Chisnall 		}
13967a984708SDavid Chisnall 		terminateHandler();
13977a984708SDavid Chisnall 	}
13987a984708SDavid Chisnall 	/**
13997a984708SDavid Chisnall 	 * Called when an unexpected exception is encountered (i.e. an exception
14007a984708SDavid Chisnall 	 * violates an exception specification).  This calls abort() unless a
14017a984708SDavid Chisnall 	 * custom handler has been set..
14027a984708SDavid Chisnall 	 */
14037a984708SDavid Chisnall 	void unexpected()
14047a984708SDavid Chisnall 	{
14057a984708SDavid Chisnall 		static __cxa_thread_info *info = thread_info_fast();
14067a984708SDavid Chisnall 		if (0 != info && 0 != info->unexpectedHandler)
14077a984708SDavid Chisnall 		{
14087a984708SDavid Chisnall 			info->unexpectedHandler();
14097a984708SDavid Chisnall 			// Should not be reached - a terminate handler is not expected to
14107a984708SDavid Chisnall 			// return.
14117a984708SDavid Chisnall 			abort();
14127a984708SDavid Chisnall 		}
14137a984708SDavid Chisnall 		unexpectedHandler();
14147a984708SDavid Chisnall 	}
14157a984708SDavid Chisnall 	/**
14167a984708SDavid Chisnall 	 * Returns whether there are any exceptions currently being thrown that
14177a984708SDavid Chisnall 	 * have not been caught.  This can occur inside a nested catch statement.
14187a984708SDavid Chisnall 	 */
14197a984708SDavid Chisnall 	bool uncaught_exception() throw()
14207a984708SDavid Chisnall 	{
14217a984708SDavid Chisnall 		__cxa_thread_info *info = thread_info();
14227a984708SDavid Chisnall 		return info->globals.uncaughtExceptions != 0;
14237a984708SDavid Chisnall 	}
14247a984708SDavid Chisnall 	/**
14257a984708SDavid Chisnall 	 * Returns the current unexpected handler.
14267a984708SDavid Chisnall 	 */
14277a984708SDavid Chisnall 	unexpected_handler get_unexpected() throw()
14287a984708SDavid Chisnall 	{
14297a984708SDavid Chisnall 		__cxa_thread_info *info = thread_info();
14307a984708SDavid Chisnall 		if (info->unexpectedHandler)
14317a984708SDavid Chisnall 		{
14327a984708SDavid Chisnall 			return info->unexpectedHandler;
14337a984708SDavid Chisnall 		}
14347a984708SDavid Chisnall 		return unexpectedHandler;
14357a984708SDavid Chisnall 	}
14367a984708SDavid Chisnall 	/**
14377a984708SDavid Chisnall 	 * Returns the current terminate handler.
14387a984708SDavid Chisnall 	 */
14397a984708SDavid Chisnall 	terminate_handler get_terminate() throw()
14407a984708SDavid Chisnall 	{
14417a984708SDavid Chisnall 		__cxa_thread_info *info = thread_info();
14427a984708SDavid Chisnall 		if (info->terminateHandler)
14437a984708SDavid Chisnall 		{
14447a984708SDavid Chisnall 			return info->terminateHandler;
14457a984708SDavid Chisnall 		}
14467a984708SDavid Chisnall 		return terminateHandler;
14477a984708SDavid Chisnall 	}
14487a984708SDavid Chisnall }
14497a984708SDavid Chisnall #ifdef __arm__
14507a984708SDavid Chisnall extern "C" _Unwind_Exception *__cxa_get_cleanup(void)
14517a984708SDavid Chisnall {
14527a984708SDavid Chisnall 	__cxa_thread_info *info = thread_info_fast();
14537a984708SDavid Chisnall 	_Unwind_Exception *exceptionObject = info->currentCleanup;
14547a984708SDavid Chisnall 	if (isCXXException(exceptionObject->exception_class))
14557a984708SDavid Chisnall 	{
14567a984708SDavid Chisnall 		__cxa_exception *ex =  exceptionFromPointer(exceptionObject);
14577a984708SDavid Chisnall 		ex->cleanupCount--;
14587a984708SDavid Chisnall 		if (ex->cleanupCount == 0)
14597a984708SDavid Chisnall 		{
14607a984708SDavid Chisnall 			info->currentCleanup = ex->nextCleanup;
14617a984708SDavid Chisnall 			ex->nextCleanup = 0;
14627a984708SDavid Chisnall 		}
14637a984708SDavid Chisnall 	}
14647a984708SDavid Chisnall 	else
14657a984708SDavid Chisnall 	{
14667a984708SDavid Chisnall 		info->currentCleanup = 0;
14677a984708SDavid Chisnall 	}
14687a984708SDavid Chisnall 	return exceptionObject;
14697a984708SDavid Chisnall }
14707a984708SDavid Chisnall 
14717a984708SDavid Chisnall asm (
14727a984708SDavid Chisnall ".pushsection .text.__cxa_end_cleanup    \n"
14737a984708SDavid Chisnall ".global __cxa_end_cleanup               \n"
14747a984708SDavid Chisnall ".type __cxa_end_cleanup, \"function\"   \n"
14757a984708SDavid Chisnall "__cxa_end_cleanup:                      \n"
14767a984708SDavid Chisnall "	push {r1, r2, r3, r4}                \n"
14777a984708SDavid Chisnall "	bl __cxa_get_cleanup                 \n"
14787a984708SDavid Chisnall "	push {r1, r2, r3, r4}                \n"
14797a984708SDavid Chisnall "	b _Unwind_Resume                     \n"
14807a984708SDavid Chisnall "	bl abort                             \n"
14817a984708SDavid Chisnall ".popsection                             \n"
14827a984708SDavid Chisnall );
14837a984708SDavid Chisnall #endif
1484