diff --git a/libcxxabi/src/cxa_exception_emscripten.cpp b/libcxxabi/src/cxa_exception_emscripten.cpp new file mode 100644 index 0000000000000..fef9bf9a0e90d --- /dev/null +++ b/libcxxabi/src/cxa_exception_emscripten.cpp @@ -0,0 +1,155 @@ +//===-------------------- cxa_exception_emscripten.cpp --------------------===// +// +// Most code in the file is directly copied from cxa_exception.cpp. +// TODO(sbc): consider merging them +// +// Notable changes: + // __cxa_allocate_exception doesn't add get_cxa_exception_offset +// __cxa_decrement_exception_refcount dosn't call the destructor if rethrown +// Both of these changes are mirrored from the historical JS implemenation of +// thse functions. +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" +#include "cxa_exception.h" +#include "include/atomic_support.h" +#include "fallback_malloc.h" +#include "stdio.h" +#include "assert.h" + +// Define to enable extra debugging on stderr. +#if EXCEPTIONS_DEBUG +#include "emscripten/console.h" +#define DEBUG emscripten_errf +#else +#define DEBUG(...) +#endif + +// Until recently, Rust's `rust_eh_personality` for emscripten referred to this +// symbol. If Emscripten doesn't provide it, there will be errors when linking +// rust. The rust personality function is never called so we can just abort. +// We need this to support old versions of Rust. +// https://github.com/rust-lang/rust/pull/97888 +// TODO: Remove this when Rust doesn't need it anymore. +extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code +__gxx_personality_v0(int version, + _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) { + abort(); +} + +namespace __cxxabiv1 { + +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + DEBUG("cxa_exception_from_thrown_object %p -> %p", + thrown_object, static_cast<__cxa_exception*>(thrown_object) - 1); + return static_cast<__cxa_exception*>(thrown_object) - 1; +} + +// 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) +{ + DEBUG("thrown_object_from_cxa_exception %p -> %p", + exception_header, static_cast(exception_header + 1)); + return static_cast(exception_header + 1); +} + +// Round s up to next multiple of a. +static inline +size_t aligned_allocation_size(size_t s, size_t a) { + return (s + a - 1) & ~(a - 1); +} + +static inline +size_t cxa_exception_size_from_exception_thrown_size(size_t size) { + return aligned_allocation_size(size + sizeof (__cxa_exception), + alignof(__cxa_exception)); +} + +extern "C" { + +// 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) _NOEXCEPT { + size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); + + char *raw_buffer = + (char *)__aligned_malloc_with_fallback(actual_size); + if (NULL == raw_buffer) + std::terminate(); + __cxa_exception *exception_header = + static_cast<__cxa_exception *>((void *)(raw_buffer)); + ::memset(exception_header, 0, actual_size); + return thrown_object_from_cxa_exception(exception_header); +} + + +// Free a __cxa_exception object allocated with __cxa_allocate_exception. +void __cxa_free_exception(void *thrown_object) _NOEXCEPT { + // Compute the size of the padding before the header. + char *raw_buffer = + ((char *)cxa_exception_from_thrown_object(thrown_object)); + __aligned_free_with_fallback((void *)raw_buffer); +} + +/* + 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 { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + DEBUG("INC: %p refcnt=%zu", thrown_object, exception_header->referenceCount); + std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1)); + } +} + +/* + 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. +*/ +_LIBCXXABI_NO_CFI +void __cxa_decrement_exception_refcount(void *thrown_object) _NOEXCEPT { + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + DEBUG("DEC: %p refcnt=%zu rethrown=%d", thrown_object, + exception_header->referenceCount, exception_header->rethrown); + assert(exception_header->referenceCount > 0); + if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0 && !exception_header->rethrown) + { + DEBUG("DEL: %p (dtor=%p)", thrown_object, exception_header->exceptionDestructor); + if (NULL != exception_header->exceptionDestructor) + exception_header->exceptionDestructor(thrown_object); + __cxa_free_exception(thrown_object); + } + } +} + +} // extern "C" + +} // abi diff --git a/libcxxabi/src/cxa_exception_js_utils.cpp b/libcxxabi/src/cxa_exception_js_utils.cpp new file mode 100644 index 0000000000000..e2c61aa0967e5 --- /dev/null +++ b/libcxxabi/src/cxa_exception_js_utils.cpp @@ -0,0 +1,105 @@ +#include "cxxabi.h" + +#include "cxa_exception.h" +#include "private_typeinfo.h" +#include +// #include +// #include +#include + +namespace __cxxabiv1 { + +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + return static_cast<__cxa_exception*>(thrown_object) - 1; +} + +// 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) +{ + return static_cast(exception_header + 1); +} + +// 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 inline __cxa_exception* cxa_exception_from_unwind_exception( + _Unwind_Exception* unwind_exception) { + return cxa_exception_from_thrown_object(unwind_exception + 1); +} + +extern "C" { + +void* __thrown_object_from_unwind_exception( + _Unwind_Exception* unwind_exception) { + __cxa_exception* exception_header = + cxa_exception_from_unwind_exception(unwind_exception); + return thrown_object_from_cxa_exception(exception_header); +} + +// Given a thrown_object, puts the information about its type and message into +// 'type' and 'message' output parameters. 'type' will contain the string +// representation of the type of the exception, e.g., 'int'. 'message' will +// contain the result of 'std::exception::what()' method if the type of the +// exception is a subclass of std::exception; otherwise it will be NULL. The +// caller is responsible for freeing 'type' buffer and also 'message' buffer, if +// it is not NULL. +void __get_exception_message(void* thrown_object, char** type, char** message) { + __cxa_exception* exception_header = + cxa_exception_from_thrown_object(thrown_object); + const __shim_type_info* thrown_type = + static_cast(exception_header->exceptionType); + const char* type_name = thrown_type->name(); + + int status = 0; + char* demangled_buf = __cxa_demangle(type_name, 0, 0, &status); + if (status == 0 && demangled_buf) { + *type = demangled_buf; + } else { + if (demangled_buf) { + free(demangled_buf); + } + *type = (char*)malloc(strlen(type_name) + 1); + strcpy(*type, type_name); + } + + *message = NULL; + const __shim_type_info* catch_type = + static_cast(&typeid(std::exception)); + int can_catch = catch_type->can_catch(thrown_type, thrown_object); + if (can_catch) { + const char* what = + static_cast(thrown_object)->what(); + *message = (char*)malloc(strlen(what) + 1); + strcpy(*message, what); + } +} + +// Returns a message saying that execution was terminated due to an exception. +// This message is freshly malloc'd and should be freed. +char* __get_exception_terminate_message(void* thrown_object) { + char* type; + char* message; + __get_exception_message(thrown_object, &type, &message); + char* result; + if (message != NULL) { + asprintf( + &result, "terminating with uncaught exception %s: %s", type, message); + free(message); + } else { + asprintf(&result, "terminating with uncaught exception of type %s", type); + } + free(type); + return result; +} +} // extern "C" + +} // namespace __cxxabiv1