diff options
author | Marshall Clow <mclow@qualcomm.com> | 2011-08-09 15:09:41 +0000 |
---|---|---|
committer | Marshall Clow <mclow@qualcomm.com> | 2011-08-09 15:09:41 +0000 |
commit | 8769449b610472ceed72f7573a26a652fa76f3db (patch) | |
tree | db9461e6d8fa9f058f040bc00c586ade9736f9e5 /libcxxabi | |
parent | 1b46951f9f31fedacac0edde64ce7dda4f71b653 (diff) | |
download | bcm5719-llvm-8769449b610472ceed72f7573a26a652fa76f3db.tar.gz bcm5719-llvm-8769449b610472ceed72f7573a26a652fa76f3db.zip |
First cut at exception handling; missing dependent exceptions. Next step: tests
llvm-svn: 137118
Diffstat (limited to 'libcxxabi')
-rw-r--r-- | libcxxabi/src/cxa_exception.cpp | 231 |
1 files changed, 224 insertions, 7 deletions
diff --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp index 22a269c6d1d..9feea29e7a1 100644 --- a/libcxxabi/src/cxa_exception.cpp +++ b/libcxxabi/src/cxa_exception.cpp @@ -21,20 +21,49 @@ #include "cxa_exception.hpp" namespace __cxxabiv1 { - +static const uint64_t kOurExceptionClass = 0x434C4E47432B2B00; // CLNGC++\0 +static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1 + // Utility routines -static __cxa_exception *exception_from_object ( void *p ) { +static __cxa_exception *exception_from_thrown_object ( void *p ) throw () { return ((__cxa_exception *) p ) - 1; } -void * object_from_exception ( void *p ) { +static void * thrown_object_from_exception ( void *p ) throw () { return (void *) (((__cxa_exception *) p ) + 1 ); } -static size_t object_size_from_exception_size ( size_t size ) { +static size_t object_size_from_exception_size ( size_t size ) throw () { return size + sizeof (__cxa_exception); } +// Get the exception object from the unwind pointer. +// Relies on the structure layout, where the unwind pointer is right in +// front of the user's exception object +static __cxa_exception * +exception_from_exception_object ( void *ptr ) throw () { + _Unwind_Exception *p = reinterpret_cast<_Unwind_Exception *> ( ptr ); + return exception_from_thrown_object ( p + 1 ); + } + +static void setExceptionClass ( _Unwind_Exception *unwind ) throw () { + unwind->exception_class = kOurExceptionClass; + } + +static void setDependentExceptionClass ( _Unwind_Exception *unwind ) throw () { + unwind->exception_class = kOurDependentExceptionClass; + } + +// Is it one of ours? +static bool isOurExceptionClass ( _Unwind_Exception *unwind ) throw () { + return ( unwind->exception_class == kOurExceptionClass ) || + ( unwind->exception_class == kOurDependentExceptionClass ); + } + +static bool isDependentException ( _Unwind_Exception *unwind ) throw () { + return ( unwind->exception_class & 0xFF ) == 0x01; + } + #include "fallback_malloc.cpp" // Allocate some memory from _somewhere_ @@ -51,7 +80,33 @@ static void do_free ( void *ptr ) throw () { return is_fallback_ptr ( ptr ) ? fallback_free ( ptr ) : std::free ( ptr ); } -// pthread_once_t __globals::flag_ = PTHREAD_ONCE_INIT; +/* Howard says: + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the exceptionDestructor stored in + exc is called, and then the memory for the exception is deallocated. +*/ +static void exception_cleanup_func ( _Unwind_Reason_Code reason, struct _Unwind_Exception* exc ) { + __cxa_exception *exception = exception_from_exception_object ( exc ); + if ( _URC_FOREIGN_EXCEPTION_CAUGHT != reason ) + exception->terminateHandler (); + + void * thrown_object = thrown_object_from_exception ( exception ); + if ( NULL != exception->exceptionDestructor ) + exception->exceptionDestructor ( thrown_object ); + __cxa_free_exception( thrown_object ); + } + +static LIBCXXABI_NORETURN void failed_throw ( __cxa_exception *exception ) throw () { +// Section 2.5.3 says: +// * For purposes of this ABI, several things are considered exception handlers: +// ** A terminate() call due to a throw. +// and +// * Upon entry, Following initialization of the catch parameter, +// a handler must call: +// * void *__cxa_begin_catch ( void *exceptionObject ); + (void) __cxa_begin_catch ( &exception->unwindHeader ); + std::terminate (); + } extern "C" { @@ -66,13 +121,13 @@ void * __cxa_allocate_exception (size_t thrown_size) throw() { if ( NULL == ptr ) std::terminate (); std::memset ( ptr, 0, actual_size ); - return object_from_exception ( ptr ); + return thrown_object_from_exception ( ptr ); } // Free a __cxa_exception object allocated with __cxa_allocate_exception. void __cxa_free_exception (void * thrown_exception) throw() { - do_free ( exception_from_object ( thrown_exception )); + do_free ( exception_from_thrown_object ( thrown_exception )); } @@ -97,6 +152,168 @@ void __cxa_free_dependent_exception (void * dependent_exception) throw() { do_free ( dependent_exception ); } + +// 2.4.3 Throwing the Exception Object +/* +After constructing the exception object with the throw argument value, +the generated code calls the __cxa_throw runtime library routine. This +routine never returns. + +The __cxa_throw routine will do the following: + +* Obtain the __cxa_exception header from the thrown exception object address, +which can be computed as follows: + __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); +* Save the current unexpected_handler and terminate_handler in the __cxa_exception header. +* Save the tinfo and dest arguments in the __cxa_exception header. +* Set the exception_class field in the unwind header. This is a 64-bit value +representing the ASCII string "XXXXC++\0", where "XXXX" is a +vendor-dependent string. That is, for implementations conforming to this +ABI, the low-order 4 bytes of this 64-bit value will be "C++\0". +* Increment the uncaught_exception flag. +* Call _Unwind_RaiseException in the system unwind library, Its argument is the +pointer to the thrown exception, which __cxa_throw itself received as an argument. +__Unwind_RaiseException begins the process of stack unwinding, described +in Section 2.5. In special cases, such as an inability to find a +handler, _Unwind_RaiseException may return. In that case, __cxa_throw +will call terminate, assuming that there was no handler for the +exception. +*/ +LIBCXXABI_NORETURN void +__cxa_throw(void * thrown_exception, std::type_info * tinfo, void (*dest)(void *)) { + __cxa_eh_globals *globals = __cxa_get_globals (); + __cxa_exception *exception = exception_from_thrown_object ( thrown_exception ); + + exception->unexpectedHandler = __cxxabiapple::__cxa_unexpected_handler; + exception->terminateHandler = __cxxabiapple::__cxa_terminate_handler; + exception->exceptionType = tinfo; + exception->exceptionDestructor = dest; + setExceptionClass ( &exception->unwindHeader ); + exception->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. + globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local + + exception->unwindHeader.exception_cleanup = exception_cleanup_func; + _Unwind_RaiseException ( &exception->unwindHeader ); + +// If we get here, some kind of unwinding error has occurred. + failed_throw ( exception ); + } + + +// 2.5.3 Exception Handlers +extern void * __cxa_get_exception_ptr(void * exceptionObject) throw() { + return exception_from_exception_object ( exceptionObject ); + } + + +/* +This routine: +* Increment's the exception's handler count. +* Places the exception on the stack of currently-caught exceptions if it is not + already there, linking the exception to the previous top of the stack. +* Decrements the uncaught_exception count. +* Returns the adjusted pointer to the exception object. +*/ +void * __cxa_begin_catch(void * exceptionObject) throw() { + __cxa_eh_globals *globals = __cxa_get_globals (); + __cxa_exception *exception = exception_from_exception_object ( exceptionObject ); + +// TODO add stuff for dependent exceptions. + +// TODO - should this be atomic? +// Increment the handler count, removing the flag about being rethrown +// assert ( exception->handlerCount != 0 ); + exception->handlerCount = exception->handlerCount < 0 ? + -exception->handlerCount + 1 : exception->handlerCount + 1; + +// place the exception on the top of the stack if it's not there. + if ( exception != globals->caughtExceptions ) { + exception->nextException = globals->caughtExceptions; + globals->caughtExceptions = exception; + } + + globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local + return thrown_object_from_exception ( exception ); + } + + +/* +Upon exit for any reason, a handler must call: + void __cxa_end_catch (); + +This routine: +* Locates the most recently caught exception and decrements its handler count. +* Removes the exception from the caught exception stack, if the handler count goes to zero. +* Destroys the exception if the handler count goes to zero, and the exception was not re-thrown by throw. +*/ +void __cxa_end_catch() { + __cxa_eh_globals *globals = __cxa_get_globals (); + __cxa_exception *current_exception = globals->caughtExceptions; + + if ( NULL != current_exception ) { + if ( current_exception->handlerCount < 0 ) { + // The exception has been rethrown + current_exception->handlerCount += 1; // TODO: should be atomic? + if ( 0 == current_exception->handlerCount ) + globals->caughtExceptions = current_exception->nextException; + // Howard says: If the exception has been rethrown, don't destroy. + } + else { + current_exception->handlerCount -= 1; // TODO: should be atomic? + if ( 0 == current_exception->handlerCount ) { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = current_exception->nextException; + if ( !isDependentException ( ¤t_exception->unwindHeader )) + _Unwind_DeleteException ( ¤t_exception->unwindHeader ); + else { + // TODO: deal with a dependent exception + } + } + } + } + } + + +std::type_info * __cxa_current_exception_type() { +// get the current exception + __cxa_eh_globals *globals = __cxa_get_globals (); + __cxa_exception *current_exception = globals->caughtExceptions; + if ( NULL == current_exception ) + return NULL; // No current exception +// TODO add stuff for dependent exceptions. + return current_exception->exceptionType; + } + +// 2.5.4 Rethrowing Exceptions +/* This routine +* marks the exception object on top of the caughtExceptions stack + (in an implementation-defined way) as being rethrown. +* If the caughtExceptions stack is empty, it calls terminate() + (see [C++FDIS] [except.throw], 15.1.8). +* It then returns to the handler that called it, which must call + __cxa_end_catch(), perform any necessary cleanup, and finally + call _Unwind_Resume() to continue unwinding. +*/ +extern LIBCXXABI_NORETURN void __cxa_rethrow() { + __cxa_eh_globals *globals = __cxa_get_globals (); + __cxa_exception *exception = exception_from_exception_object ( globals->caughtExceptions ); + + if ( NULL == exception ) // there's no current exception! + std::terminate (); + +// Mark the exception as being rethrown + exception->handlerCount = -exception->handlerCount ; + +#if __arm__ + (void) _Unwind_SjLj_Resume_or_Rethrow ( &exception->unwindHeader ); +#else + (void) _Unwind_Resume_or_Rethrow ( &exception->unwindHeader ); +#endif + +// If we get here, some kind of unwinding error has occurred. + failed_throw ( exception ); + } + } // extern "C" } // abi |