// Allocate a __cxa_exception object, and zero-fill it. // Reserve "thrown_size" bytes on the end for the user's exception // 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) throw() { size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size);
// Allocate extra space before the __cxa_exception header to ensure the // start of the thrown object is sufficiently aligned. size_t header_offset = get_cxa_exception_offset(); char *raw_buffer = (char *)__aligned_malloc_with_fallback(header_offset + actual_size); if (NULL == raw_buffer) std::terminate();
// Return the offset of the __cxa_exception header from the start of the // allocated buffer. If __cxa_exception's alignment is smaller than the maximum // useful alignment for the target machine, padding has to be inserted before // the header to ensure the thrown object that follows the header is // sufficiently aligned. This happens if _Unwind_exception isn't double-word // aligned (on Darwin, for example). staticsize_tget_cxa_exception_offset(){ structS { } __attribute__((aligned));
// Compute the maximum alignment for the target machine. constexprsize_t alignment = alignof(S); constexprsize_t excp_size = sizeof(__cxa_exception); constexprsize_t aligned_size = (excp_size + alignment - 1) / alignment * alignment; constexprsize_t offset = aligned_size - excp_size; static_assert((offset == 0 || alignof(_Unwind_Exception) < alignment), "offset is non-zero only if _Unwind_Exception isn't aligned"); return offset; }
// Walk the free list, looking for a "big enough" chunk for (p = freelist, prev = 0; p && p != list_end; prev = p, p = node_from_offset(p->next_node)) {
if (p->len > nelems) { // chunk is larger, shorten, and return the tail heap_node* q;
// Note: This is never called when exception_header is masquerading as a // __cxa_dependent_exception. static inline void* thrown_object_from_cxa_exception(__cxa_exception* exception_header) { returnstatic_cast<void*>(exception_header + 1); }
// 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. */ void __cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) { __cxa_eh_globals *globals = __cxa_get_globals(); __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
exception_header->unexpectedHandler = std::get_unexpected(); exception_header->terminateHandler = std::get_terminate(); exception_header->exceptionType = tinfo; exception_header->exceptionDestructor = dest; setOurExceptionClass(&exception_header->unwindHeader); exception_header->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local
#if __has_feature(address_sanitizer) // Inform the ASan runtime that now might be a good time to clean stuff up. __asan_handle_no_return(); #endif
#ifdef __USING_SJLJ_EXCEPTIONS__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); #else _Unwind_RaiseException(&exception_header->unwindHeader); #endif // This only happens when there is no handler, or some unexpected unwinding // error happens. failed_throw(exception_header); }
void* __cxa_begin_catch(void* unwind_arg) throw() { _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) );
#if defined(__MVS__) // Remove the exception object from the linked list of exceptions that the z/OS unwinder // maintains before adding it to the libc++abi list of caught exceptions. // The libc++abi will manage the lifetime of the exception from this point forward. _UnwindZOS_PopException(); #endif
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 #if defined(_LIBCXXABI_ARM_EHABI) returnreinterpret_cast<void*>(exception_header->unwindHeader.barrier_cache.bitpattern[0]); #else return exception_header->adjustedPtr; #endif } // 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; }
// Is it one of ours? uint64_t __getExceptionClass(const _Unwind_Exception* unwind_exception) { // On x86 and some ARM unwinders, unwind_exception->exception_class is // a uint64_t. On other ARM unwinders, it is a char[8]. // See: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // So we just copy it into a uint64_t to be sure. uint64_t exClass; ::memcpy(&exClass, &unwind_exception->exception_class, sizeof(exClass)); return exClass; }
void __cxa_end_catch() { static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception), "sizeof(__cxa_exception) must be equal to " "sizeof(__cxa_dependent_exception)"); static_assert(__builtin_offsetof(__cxa_exception, referenceCount) == __builtin_offsetof(__cxa_dependent_exception, primaryException), "the layout of __cxa_exception must match the layout of " "__cxa_dependent_exception"); static_assert(__builtin_offsetof(__cxa_exception, handlerCount) == __builtin_offsetof(__cxa_dependent_exception, handlerCount), "the layout of __cxa_exception must match the layout of " "__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 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 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)); } } } 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; } } }