diff --git a/cxxabi.h b/cxxabi.h index e021f85..acf9974 100644 --- a/cxxabi.h +++ b/cxxabi.h @@ -249,6 +249,10 @@ char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status); + +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE +size_t __cxa_collect_current_exception_backtrace(void** dest, size_t size); +#endif #ifdef __cplusplus } // extern "C" } // namespace diff --git a/exception.cc b/exception.cc index 15f93ae..c4b15ae 100644 --- a/exception.cc +++ b/exception.cc @@ -255,12 +255,20 @@ namespace std * various checks may test for equality of the class, which is incorrect. */ static const uint64_t exception_class = +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + _YNDX_LIBUNWIND_EXCEPTION_BACKTRACE_PRIMARY_CLASS; +#else EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\0'); +#endif /** * Class used for dependent exceptions. */ static const uint64_t dependent_exception_class = +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + _YNDX_LIBUNWIND_EXCEPTION_BACKTRACE_DEPENDENT_CLASS; +#else EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\x01'); +#endif /** * The low four bytes of the exception class, indicating that we conform to the * Itanium C++ ABI. This is currently unused, but should be used in the future @@ -605,6 +613,27 @@ static void free_exception(char *e) } #endif +static constexpr size_t align_to(size_t size, size_t alignment) noexcept { + return (size + alignment - 1) / alignment * alignment; +} + +static_assert(align_to(15, 16) == 16); +static_assert(align_to(16, 16) == 16); +static_assert(align_to(17, 16) == 32); + +static constexpr size_t exception_size = align_to(sizeof(__cxa_exception), 16); +static constexpr size_t dependent_exception_size = align_to(sizeof(__cxa_dependent_exception), 16); +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE +static constexpr size_t backtrace_buffer_size = align_to(sizeof(_Unwind_Backtrace_Buffer), 16); + +static_assert( + _YNDX_LIBUNWIND_EXCEPTION_BACKTRACE_MAGIC_OFFSET == + offsetof(__cxa_exception, unwindHeader) + backtrace_buffer_size - sizeof(_Unwind_Backtrace_Buffer)); +#else +static constexpr size_t backtrace_buffer_size = 0; +#endif + + /** * Allocates an exception structure. Returns a pointer to the space that can * be used to store an object of thrown_size bytes. This function will use an @@ -613,16 +642,19 @@ static void free_exception(char *e) */ extern "C" void *__cxa_allocate_exception(size_t thrown_size) _LIBCXXRT_NOEXCEPT { - size_t size = thrown_size + sizeof(__cxa_exception); + size_t size = thrown_size + exception_size + backtrace_buffer_size; char *buffer = alloc_or_die(size); - return buffer+sizeof(__cxa_exception); +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + ((_Unwind_Backtrace_Buffer *)buffer)->size = 0; +#endif + return buffer + exception_size + backtrace_buffer_size; } extern "C" void *__cxa_allocate_dependent_exception(void) { - size_t size = sizeof(__cxa_dependent_exception); + size_t size = dependent_exception_size + backtrace_buffer_size; char *buffer = alloc_or_die(size); - return buffer+sizeof(__cxa_dependent_exception); + return buffer + dependent_exception_size + backtrace_buffer_size; } /** @@ -650,7 +682,8 @@ extern "C" void __cxa_free_exception(void *thrown_exception) _LIBCXXRT_NOEXCEPT } } - free_exception(reinterpret_cast<char*>(ex)); + free_exception( + reinterpret_cast<char*>(thrown_exception) - exception_size - backtrace_buffer_size); } static void releaseException(__cxa_exception *exception) @@ -677,7 +710,7 @@ void __cxa_free_dependent_exception(void *thrown_exception) { releaseException(realExceptionFromException(reinterpret_cast<__cxa_exception*>(ex))); } - free_exception(reinterpret_cast<char*>(ex)); + free_exception(reinterpret_cast<char*>(thrown_exception) - dependent_exception_size - backtrace_buffer_size); } /** @@ -864,6 +897,32 @@ extern "C" void __cxa_decrement_exception_refcount(void* thrown_exception) releaseException(ex); } +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE +static size_t __cxa_collect_backtrace(__cxa_exception* ex, void** dest, size_t size) { + if (!ex) { + return 0; + } + if (!isCXXException(ex->unwindHeader.exception_class)) { + return 0; + } + size_t i = 0; + if (isDependentException(ex->unwindHeader.exception_class)) { + i = __cxa_collect_backtrace( + (__cxa_exception *)((__cxa_dependent_exception *)ex)->primaryException - 1, dest, size); + } + _Unwind_Backtrace_Buffer* backtraceBuffer = (_Unwind_Backtrace_Buffer*)( + (char *)(ex + 1) - exception_size - backtrace_buffer_size); + for (size_t j = 0; i != size && j != backtraceBuffer->size; ++i, ++j) { + dest[i] = backtraceBuffer->backtrace[j]; + } + return i; +} + +extern "C" size_t __cxa_collect_current_exception_backtrace(void** dest, size_t size) { + return __cxa_collect_backtrace(__cxa_get_globals()->caughtExceptions, dest, size); +} +#endif + /** * ABI function. Rethrows the current exception. Does not remove the * exception from the stack or decrement its handler count - the compiler is