Skip to content

Add missing libcxx/libcxxabi files from emscripten #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions libcxxabi/src/cxa_exception_emscripten.cpp
Original file line number Diff line number Diff line change
@@ -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<void*>(exception_header + 1));
return static_cast<void*>(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
105 changes: 105 additions & 0 deletions libcxxabi/src/cxa_exception_js_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "cxxabi.h"

#include "cxa_exception.h"
#include "private_typeinfo.h"
#include <stdio.h>
// #include <stdint.h>
// #include <stdlib.h>
#include <string.h>

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<void*>(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<const __shim_type_info*>(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<const __shim_type_info*>(&typeid(std::exception));
int can_catch = catch_type->can_catch(thrown_type, thrown_object);
if (can_catch) {
const char* what =
static_cast<const std::exception*>(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