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