summaryrefslogtreecommitdiff
path: root/src/cxa_exception.cpp
diff options
context:
space:
mode:
authorHoward Hinnant <hhinnant@apple.com>2012-01-30 16:07:00 +0000
committerHoward Hinnant <hhinnant@apple.com>2012-01-30 16:07:00 +0000
commit7da9b96afc66c4496eb8809897c1b707b574a5c4 (patch)
tree06a075d4f2c6aab26fefe7d5b18f38693d8e50ae /src/cxa_exception.cpp
parent2b891bfd06e8e1ecf399b2684d5616de507e8403 (diff)
Add a descriptive name for a constant. Also I'm at least temporarily waging war on throw specs, both old and new style. Except where we have already publicly exposed the throw spec, I'm getting rid of them. They may come back later. But they seem somewhat prone to cyclic dependencies here. The throw spec implies compiler generated code that this library has to jump to during stack unwinding. I'd like to minimize the possiblity that the code used to properly make that jump is itself creating such jumps.
git-svn-id: https://llvm.org/svn/llvm-project/libcxxabi/trunk@149251 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'src/cxa_exception.cpp')
-rw-r--r--src/cxa_exception.cpp311
1 files changed, 211 insertions, 100 deletions
diff --git a/src/cxa_exception.cpp b/src/cxa_exception.cpp
index 3f516c5..2035fae 100644
--- a/src/cxa_exception.cpp
+++ b/src/cxa_exception.cpp
@@ -21,6 +21,8 @@
#include "cxa_exception.hpp"
#include "cxa_handlers.hpp"
+#include <stdio.h>
+
// +---------------------------+-----------------------------+---------------+
// | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object |
// +---------------------------+-----------------------------+---------------+
@@ -32,14 +34,13 @@
// | __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 |
// +---------------------------+-----------------------------+
-
namespace __cxxabiv1 {
// Utility routines
static
inline
__cxa_exception*
-cxa_exception_from_thrown_object(void* thrown_object) noexcept
+cxa_exception_from_thrown_object(void* thrown_object)
{
return static_cast<__cxa_exception*>(thrown_object) - 1;
}
@@ -49,7 +50,7 @@ cxa_exception_from_thrown_object(void* thrown_object) noexcept
static
inline
void*
-thrown_object_from_cxa_exception(__cxa_exception* exception_header) noexcept
+thrown_object_from_cxa_exception(__cxa_exception* exception_header)
{
return static_cast<void*>(exception_header + 1);
}
@@ -60,7 +61,7 @@ thrown_object_from_cxa_exception(__cxa_exception* exception_header) noexcept
static
inline
__cxa_exception*
-cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception) noexcept
+cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception)
{
return cxa_exception_from_thrown_object(unwind_exception + 1 );
}
@@ -68,50 +69,50 @@ cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exceptio
static
inline
size_t
-cxa_exception_size_from_exception_thrown_size(size_t size) noexcept
+cxa_exception_size_from_exception_thrown_size(size_t size)
{
return size + sizeof (__cxa_exception);
}
-static void setExceptionClass(_Unwind_Exception* unwind_exception) noexcept {
+static void setExceptionClass(_Unwind_Exception* unwind_exception) {
unwind_exception->exception_class = kOurExceptionClass;
}
-static void setDependentExceptionClass(_Unwind_Exception* unwind_exception) noexcept {
+static void setDependentExceptionClass(_Unwind_Exception* unwind_exception) {
unwind_exception->exception_class = kOurDependentExceptionClass;
}
// Is it one of ours?
-static bool isOurExceptionClass(_Unwind_Exception* unwind_exception) noexcept {
- return(unwind_exception->exception_class == kOurExceptionClass)||
- (unwind_exception->exception_class == kOurDependentExceptionClass);
+static bool isOurExceptionClass(const _Unwind_Exception* unwind_exception) {
+ return (unwind_exception->exception_class & get_language) ==
+ (kOurExceptionClass & get_language);
}
-static bool isDependentException(_Unwind_Exception* unwind_exception) noexcept {
+static bool isDependentException(_Unwind_Exception* unwind_exception) {
return (unwind_exception->exception_class & 0xFF) == 0x01;
}
// This does not need to be atomic
-static inline int incrementHandlerCount(__cxa_exception *exception) noexcept {
+static inline int incrementHandlerCount(__cxa_exception *exception) {
return ++exception->handlerCount;
}
// This does not need to be atomic
-static inline int decrementHandlerCount(__cxa_exception *exception) noexcept {
+static inline int decrementHandlerCount(__cxa_exception *exception) {
return --exception->handlerCount;
}
#include "fallback_malloc.ipp"
// Allocate some memory from _somewhere_
-static void *do_malloc(size_t size) noexcept {
+static void *do_malloc(size_t size) {
void *ptr = std::malloc(size);
if (NULL == ptr) // if malloc fails, fall back to emergency stash
ptr = fallback_malloc(size);
return ptr;
}
-static void do_free(void *ptr) noexcept {
+static void do_free(void *ptr) {
is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr);
}
@@ -130,13 +131,14 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc
if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason)
std::__terminate(exception_header->terminateHandler);
+ // TODO: Shouldn't this check the reference count first?
void * thrown_object = thrown_object_from_cxa_exception(exception_header);
if (NULL != exception_header->exceptionDestructor)
exception_header->exceptionDestructor(thrown_object);
__cxa_free_exception(thrown_object);
}
-static LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) noexcept {
+static LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) {
// Section 2.5.3 says:
// * For purposes of this ABI, several things are considered exception handlers:
// ** A terminate() call due to a throw.
@@ -155,7 +157,7 @@ extern "C" {
// object. Zero-fill the object. If memory can't be allocated, call
// std::terminate. Return a pointer to the memory to be used for the
// user's exception object.
-void * __cxa_allocate_exception (size_t thrown_size) noexcept {
+void * __cxa_allocate_exception (size_t thrown_size) throw() {
size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size);
__cxa_exception* exception_header = static_cast<__cxa_exception*>(do_malloc(actual_size));
if (NULL == exception_header)
@@ -166,7 +168,7 @@ void * __cxa_allocate_exception (size_t thrown_size) noexcept {
// Free a __cxa_exception object allocated with __cxa_allocate_exception.
-void __cxa_free_exception (void * thrown_object) noexcept {
+void __cxa_free_exception (void * thrown_object) throw() {
do_free(cxa_exception_from_thrown_object(thrown_object));
}
@@ -174,7 +176,7 @@ void __cxa_free_exception (void * thrown_object) noexcept {
// This function shall allocate a __cxa_dependent_exception and
// return a pointer to it. (Really to the object, not past its' end).
// Otherwise, it will work like __cxa_allocate_exception.
-void * __cxa_allocate_dependent_exception () noexcept {
+void * __cxa_allocate_dependent_exception () {
size_t actual_size = sizeof(__cxa_dependent_exception);
void *ptr = do_malloc(actual_size);
if (NULL == ptr)
@@ -186,7 +188,7 @@ void * __cxa_allocate_dependent_exception () noexcept {
// This function shall free a dependent_exception.
// It does not affect the reference count of the primary exception.
-void __cxa_free_dependent_exception (void * dependent_exception) noexcept {
+void __cxa_free_dependent_exception (void * dependent_exception) {
do_free(dependent_exception);
}
@@ -249,9 +251,11 @@ __cxa_throw(void* thrown_object, std::type_info* tinfo, void (*dest)(void*))
The adjusted pointer is computed by the personality routine during phase 1
and saved in the exception header (either __cxa_exception or
__cxa_dependent_exception).
+
+ Requires: exception is native
*/
void*
-__cxa_get_exception_ptr(void* unwind_exception) noexcept
+__cxa_get_exception_ptr(void* unwind_exception) throw()
{
return cxa_exception_from_exception_unwind_exception
(
@@ -259,39 +263,75 @@ __cxa_get_exception_ptr(void* unwind_exception) noexcept
)->adjustedPtr;
}
-
/*
-This routine:
+This routine can catch foreign or native exceptions. If native, the exception
+can be a primary or dependent variety. This routine may remain blissfully
+ignorant of whether the native exception is primary or dependent.
+
+If the exception is native:
* 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.
+* Push the exception on the stack of currently-caught exceptions if it is not
+ already there (from a rethrow).
* Decrements the uncaught_exception count.
-* Returns the adjusted pointer to the exception object.
+* Returns the adjusted pointer to the exception object, which is stored in
+ the __cxa_exception by the personality routine.
+
+If the exception is foreign, this means it did not originate from one of throw
+routines. The foreign exception does not necessarily have a __cxa_exception
+header. However we can catch it here with a catch (...), or with a call
+to terminate or unexpected during unwinding.
+* Do not try to increment the exception's handler count, we don't know where
+ it is.
+* Push the exception on the stack of currently-caught exceptions only if the
+ stack is empty. The foreign exception has no way to link to the current
+ top of stack. If the stack is not empty, call terminate. Even with an
+ empty stack, this is hacked in by pushing a pointer to an imaginary
+ __cxa_exception block in front of the foreign exception. It would be better
+ if the __cxa_eh_globals structure had a stack of _Unwind_Exception, but it
+ doesn't. It has a stack of __cxa_exception (which has a next* in it).
+* Do not decrement the uncaught_exception count because we didn't increment it
+ in __cxa_throw (or one of our rethrow functions).
+* If we haven't terminated, assume the exception object is just past the
+ _Unwind_Exception and return a pointer to that.
*/
void*
-__cxa_begin_catch(void* unwind_exception) noexcept
+__cxa_begin_catch(void* unwind_arg) throw()
{
- __cxa_eh_globals *globals = __cxa_get_globals();
+printf("entering __cxa_begin_catch\n");
+ _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg);
+ bool native_exception = isOurExceptionClass(unwind_exception);
+ __cxa_eh_globals* globals = __cxa_get_globals();
+ // exception_header is a hackish offset from a foreign exception, but it
+ // works as long as we're careful not to try to access any __cxa_exception
+ // parts.
__cxa_exception* exception_header =
cxa_exception_from_exception_unwind_exception
(
static_cast<_Unwind_Exception*>(unwind_exception)
);
-
-// TODO: Handle foreign exceptions? How?
-
-// Increment the handler count, removing the flag about being rethrown
- exception_header->handlerCount = exception_header->handlerCount < 0 ?
- -exception_header->handlerCount + 1 : exception_header->handlerCount + 1;
-
-// place the exception on the top of the stack if it's not there.
- if (exception_header != globals->caughtExceptions) {
- exception_header->nextException = globals->caughtExceptions;
- globals->caughtExceptions = exception_header;
+ if (native_exception)
+ {
+ // Increment the handler count, removing the flag about being rethrown
+ exception_header->handlerCount = exception_header->handlerCount < 0 ?
+ -exception_header->handlerCount + 1 : exception_header->handlerCount + 1;
+ // place the exception on the top of the stack if it's not already
+ // there by a previous rethrow
+ if (exception_header != globals->caughtExceptions)
+ {
+ exception_header->nextException = globals->caughtExceptions;
+ globals->caughtExceptions = exception_header;
+ }
+ globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local
+printf("leaving __cxa_begin_catch\n");
+ return exception_header->adjustedPtr;
}
-
- globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local
- return exception_header->adjustedPtr;
+ // Else this is a foreign exception
+ // If the caughtExceptions stack is not empty, terminate
+ if (globals->caughtExceptions != 0)
+ std::terminate();
+ // Push the foreign exception on to the stack
+ globals->caughtExceptions = exception_header;
+ return unwind_exception + 1;
}
@@ -299,7 +339,8 @@ __cxa_begin_catch(void* unwind_exception) noexcept
Upon exit for any reason, a handler must call:
void __cxa_end_catch ();
-This routine:
+This routine can be called for either a native or foreign exception.
+For a native exception:
* 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.
* If the handler count goes down to zero, and the exception was not re-thrown
@@ -307,45 +348,81 @@ This routine:
it's handling) and decrements its reference count. If that reference count
goes to zero, the function destroys the exception. In any case, if the current
exception is a dependent exception, it destroys that.
+
+For a foreign exception:
+* If it has been rethrown, there is nothing to do.
+* Otherwise delete the exception and pop the catch stack to empty.
*/
-void __cxa_end_catch() {
+void __cxa_end_catch()
+{
+printf("entering __cxa_end_catch\n");
static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception),
"sizeof(__cxa_exception) must be equal to sizeof(__cxa_dependent_exception)");
- __cxa_eh_globals *globals = __cxa_get_globals_fast(); // __cxa_get_globals called in __cxa_begin_catch
- __cxa_exception *exception_header = globals->caughtExceptions;
-
- if (NULL != exception_header) {
- // TODO: Handle foreign exceptions? How?
- if (exception_header->handlerCount < 0) {
- // The exception has been rethrown
- if (0 == incrementHandlerCount(exception_header)) {
- // Remove from the chain of uncaught exceptions
- globals->caughtExceptions = exception_header->nextException;
- // but don't destroy
+ __cxa_eh_globals* globals = __cxa_get_globals_fast(); // __cxa_get_globals called in __cxa_begin_catch
+ __cxa_exception* exception_header = globals->caughtExceptions;
+ // If we've rethrown a foreign exception, then globals->caughtExceptions
+ // will have been made an empty stack by __cxa_rethrow() and there is
+ // nothing more to be done. Do nothing!
+ if (NULL != exception_header)
+ {
+ bool native_exception = isOurExceptionClass(&exception_header->unwindHeader);
+ if (native_exception)
+ {
+ // This is a native exception
+ if (exception_header->handlerCount < 0)
+ {
+ // The exception has been rethrown by __cxa_rethrow, so don't delete it
+ if (0 == incrementHandlerCount(exception_header))
+ {
+ // Remove from the chain of uncaught exceptions
+ globals->caughtExceptions = exception_header->nextException;
+ // but don't destroy
+ }
+ // Keep handlerCount negative in case there are nested catch's
+ // that need to be told that this exception is rethrown. Don't
+ // erase this rethrow flag until the exception is recaught.
}
- }
- else { // The exception has not been rethrown
- if (0 == decrementHandlerCount(exception_header)) {
- // Remove from the chain of uncaught exceptions
- globals->caughtExceptions = exception_header->nextException;
- if (isDependentException(&exception_header->unwindHeader)) {
- // Reset exception_header to primaryException and deallocate the dependent exception
- __cxa_dependent_exception* dep_exception_header =
- reinterpret_cast<__cxa_dependent_exception*>(exception_header);
- exception_header =
- cxa_exception_from_thrown_object(dep_exception_header->primaryException);
- __cxa_free_dependent_exception(dep_exception_header);
+ else
+ {
+ // The native exception has not been rethrown
+ if (0 == decrementHandlerCount(exception_header))
+ {
+ // Remove from the chain of uncaught exceptions
+ globals->caughtExceptions = exception_header->nextException;
+ // Destroy this exception, being careful to distinguish
+ // between dependent and primary exceptions
+ if (isDependentException(&exception_header->unwindHeader))
+ {
+ // Reset exception_header to primaryException and deallocate the dependent exception
+ __cxa_dependent_exception* dep_exception_header =
+ reinterpret_cast<__cxa_dependent_exception*>(exception_header);
+ exception_header =
+ cxa_exception_from_thrown_object(dep_exception_header->primaryException);
+ __cxa_free_dependent_exception(dep_exception_header);
+ }
+ // Destroy the primary exception only if its referenceCount goes to 0
+ // (this decrement must be atomic)
+ __cxa_decrement_exception_refcount(thrown_object_from_cxa_exception(exception_header));
}
- // Destroy the primary exception only if its referenceCount goes to 0
- // (this decrement must be atomic)
- __cxa_decrement_exception_refcount(thrown_object_from_cxa_exception(exception_header));
}
}
+ else
+ {
+ // The foreign exception has not been rethrown. Pop the stack
+ // and delete it. If there are nested catch's and they try
+ // to touch a foreign exception in any way, that is undefined
+ // behavior. They likely can't since the only way to catch
+ // a foreign exception is with catch (...)!
+ _Unwind_DeleteException(&globals->caughtExceptions->unwindHeader);
+ globals->caughtExceptions = 0;
+ }
}
+printf("leaving __cxa_end_catch\n");
}
// Note: exception_header may be masquerading as a __cxa_dependent_exception
// and that's ok. exceptionType is there too.
+// However watch out for foreign exceptions. Return null for them.
std::type_info * __cxa_current_exception_type() {
// get the current exception
__cxa_eh_globals *globals = __cxa_get_globals_fast();
@@ -354,51 +431,76 @@ std::type_info * __cxa_current_exception_type() {
__cxa_exception *exception_header = globals->caughtExceptions;
if (NULL == exception_header)
return NULL; // No current exception
+ if (!isOurExceptionClass(&exception_header->unwindHeader))
+ return NULL;
return exception_header->exceptionType;
}
// 2.5.4 Rethrowing Exceptions
-/* This routine
+/* This routine can rethrow native or foreign exceptions.
+If the exception is native:
* 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 calls _Unwind_Resume_or_Rethrow which should not return
+* It then calls _Unwind_RaiseException which should not return
(terminate if it does).
Note: exception_header may be masquerading as a __cxa_dependent_exception
and that's ok.
*/
-extern LIBCXXABI_NORETURN void __cxa_rethrow() {
- __cxa_eh_globals *globals = __cxa_get_globals();
- __cxa_exception *exception_header = globals->caughtExceptions;
-
- if (NULL == exception_header) // there's no current exception!
- std::terminate ();
-
-// TODO: Handle foreign exceptions? How?
-// Rethrow the foreign exception without touching anything!
-
-// Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch)
- exception_header->handlerCount = -exception_header->handlerCount;
- globals->uncaughtExceptions += 1;
-// __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary
-
+LIBCXXABI_NORETURN
+void
+__cxa_rethrow()
+{
+ __cxa_eh_globals* globals = __cxa_get_globals();
+//printf("entering __cxa_rethrow\n");
+ __cxa_exception* exception_header = globals->caughtExceptions;
+ if (NULL == exception_header)
+ std::terminate(); // throw; called outside of a exception handler
+ bool native_exception = isOurExceptionClass(&exception_header->unwindHeader);
+ if (native_exception)
+ {
+//printf("__cxa_rethrow native branch\n");
+ // Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch)
+ exception_header->handlerCount = -exception_header->handlerCount;
+ globals->uncaughtExceptions += 1;
+ // __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary
+ }
+ else // this is a foreign exception
+ {
+ // The only way to communicate to __cxa_end_catch that we've rethrown
+ // a foreign exception, so don't delete us, is to pop the stack here
+ // which must be empty afterwards. Then __cxa_end_catch will do
+ // nothing
+ globals->caughtExceptions = 0;
+ }
+//printf("leaving __cxa_rethrow, private_1 = %lu\n", exception_header->unwindHeader.private_1);
#if __arm__
(void) _Unwind_SjLj_Resume_or_Rethrow(&exception_header->unwindHeader);
#else
- (void) _Unwind_Resume_or_Rethrow (&exception_header->unwindHeader);
+ (void)_Unwind_RaiseException(&exception_header->unwindHeader);
#endif
-// If we get here, some kind of unwinding error has occurred.
- failed_throw(exception_header);
+ // If we get here, some kind of unwinding error has occurred.
+ // There is some weird code generation bug happening with
+ // Apple clang version 4.0 (tags/Apple/clang-418.0.2) (based on LLVM 3.1svn)
+ // If we call failed_throw here. Turns up with -O2 or higher, and -Os.
+ __cxa_begin_catch(&exception_header->unwindHeader);
+ if (native_exception)
+ std::__terminate(exception_header->terminateHandler);
+ // Foreign exception: can't get exception_header->terminateHandler
+ std::terminate();
}
/*
- If p is not null, atomically increment the referenceCount field of the
- __cxa_exception header associated with the thrown object referred to by p.
+ If thrown_object is not null, atomically increment the referenceCount field
+ of the __cxa_exception header associated with the thrown object referred to
+ by thrown_object.
+
+ Requires: If thrown_object is not NULL, it is a native exception.
*/
void
-__cxa_increment_exception_refcount(void* thrown_object) noexcept
+__cxa_increment_exception_refcount(void* thrown_object) throw()
{
if (thrown_object != NULL )
{
@@ -408,12 +510,15 @@ __cxa_increment_exception_refcount(void* thrown_object) noexcept
}
/*
- If p is not null, atomically decrement the referenceCount field of the
- __cxa_exception header associated with the thrown object referred to by p.
- If the referenceCount drops to zero, destroy and deallocate the exception.
+ If thrown_object is not null, atomically decrement the referenceCount field
+ of the __cxa_exception header associated with the thrown object referred to
+ by thrown_object. If the referenceCount drops to zero, destroy and
+ deallocate the exception.
+
+ Requires: If thrown_object is not NULL, it is a native exception.
*/
void
-__cxa_decrement_exception_refcount(void* thrown_object) noexcept
+__cxa_decrement_exception_refcount(void* thrown_object) throw()
{
if (thrown_object != NULL )
{
@@ -430,14 +535,15 @@ __cxa_decrement_exception_refcount(void* thrown_object) noexcept
/*
Returns a pointer to the thrown object (if any) at the top of the
caughtExceptions stack. Atommically increment the exception's referenceCount.
- If there is no such thrown object, returns null.
+ If there is no such thrown object or if the thrown object is foreign,
+ returns null.
We can use __cxa_get_globals_fast here to get the globals because if there have
been no exceptions thrown, ever, on this thread, we can return NULL without
the need to allocate the exception-handling globals.
*/
void*
-__cxa_current_primary_exception() noexcept
+__cxa_current_primary_exception() throw()
{
// get the current exception
__cxa_eh_globals* globals = __cxa_get_globals_fast();
@@ -446,6 +552,8 @@ __cxa_current_primary_exception() noexcept
__cxa_exception* exception_header = globals->caughtExceptions;
if (NULL == exception_header)
return NULL; // No current exception
+ if (!isOurExceptionClass(&exception_header->unwindHeader))
+ return NULL; // Can't capture a foreign exception (no way to refcount it)
if (isDependentException(&exception_header->unwindHeader)) {
__cxa_dependent_exception* dep_exception_header =
reinterpret_cast<__cxa_dependent_exception*>(exception_header);
@@ -483,6 +591,8 @@ __cxa_rethrow_primary_exception(void* thrown_object)
{
if ( thrown_object != NULL )
{
+ // thrown_object guaranteed to be native because
+ // __cxa_current_primary_exception returns NULL for foreign exceptions
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
__cxa_dependent_exception* dep_exception_header =
static_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception());
@@ -506,8 +616,9 @@ __cxa_rethrow_primary_exception(void* thrown_object)
}
bool
-__cxa_uncaught_exception() noexcept
+__cxa_uncaught_exception() throw()
{
+ // This does not report foreign exceptions in flight
__cxa_eh_globals* globals = __cxa_get_globals_fast();
if (globals == 0)
return false;