Skip to content

Commit 76eb0a3

Browse files
authored
Add missing libcxx/libcxxabi files from emscripten (#4)
1 parent 076a6c3 commit 76eb0a3

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//===-------------------- cxa_exception_emscripten.cpp --------------------===//
2+
//
3+
// Most code in the file is directly copied from cxa_exception.cpp.
4+
// TODO(sbc): consider merging them
5+
//
6+
// Notable changes:
7+
// __cxa_allocate_exception doesn't add get_cxa_exception_offset
8+
// __cxa_decrement_exception_refcount dosn't call the destructor if rethrown
9+
// Both of these changes are mirrored from the historical JS implemenation of
10+
// thse functions.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "cxxabi.h"
15+
#include "cxa_exception.h"
16+
#include "include/atomic_support.h"
17+
#include "fallback_malloc.h"
18+
#include "stdio.h"
19+
#include "assert.h"
20+
21+
// Define to enable extra debugging on stderr.
22+
#if EXCEPTIONS_DEBUG
23+
#include "emscripten/console.h"
24+
#define DEBUG emscripten_errf
25+
#else
26+
#define DEBUG(...)
27+
#endif
28+
29+
// Until recently, Rust's `rust_eh_personality` for emscripten referred to this
30+
// symbol. If Emscripten doesn't provide it, there will be errors when linking
31+
// rust. The rust personality function is never called so we can just abort.
32+
// We need this to support old versions of Rust.
33+
// https://github.com/rust-lang/rust/pull/97888
34+
// TODO: Remove this when Rust doesn't need it anymore.
35+
extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
36+
__gxx_personality_v0(int version,
37+
_Unwind_Action actions,
38+
uint64_t exceptionClass,
39+
_Unwind_Exception* unwind_exception,
40+
_Unwind_Context* context) {
41+
abort();
42+
}
43+
44+
namespace __cxxabiv1 {
45+
46+
// Utility routines
47+
static
48+
inline
49+
__cxa_exception*
50+
cxa_exception_from_thrown_object(void* thrown_object)
51+
{
52+
DEBUG("cxa_exception_from_thrown_object %p -> %p",
53+
thrown_object, static_cast<__cxa_exception*>(thrown_object) - 1);
54+
return static_cast<__cxa_exception*>(thrown_object) - 1;
55+
}
56+
57+
// Note: This is never called when exception_header is masquerading as a
58+
// __cxa_dependent_exception.
59+
static
60+
inline
61+
void*
62+
thrown_object_from_cxa_exception(__cxa_exception* exception_header)
63+
{
64+
DEBUG("thrown_object_from_cxa_exception %p -> %p",
65+
exception_header, static_cast<void*>(exception_header + 1));
66+
return static_cast<void*>(exception_header + 1);
67+
}
68+
69+
// Round s up to next multiple of a.
70+
static inline
71+
size_t aligned_allocation_size(size_t s, size_t a) {
72+
return (s + a - 1) & ~(a - 1);
73+
}
74+
75+
static inline
76+
size_t cxa_exception_size_from_exception_thrown_size(size_t size) {
77+
return aligned_allocation_size(size + sizeof (__cxa_exception),
78+
alignof(__cxa_exception));
79+
}
80+
81+
extern "C" {
82+
83+
// Allocate a __cxa_exception object, and zero-fill it.
84+
// Reserve "thrown_size" bytes on the end for the user's exception
85+
// object. Zero-fill the object. If memory can't be allocated, call
86+
// std::terminate. Return a pointer to the memory to be used for the
87+
// user's exception object.
88+
void *__cxa_allocate_exception(size_t thrown_size) _NOEXCEPT {
89+
size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size);
90+
91+
char *raw_buffer =
92+
(char *)__aligned_malloc_with_fallback(actual_size);
93+
if (NULL == raw_buffer)
94+
std::terminate();
95+
__cxa_exception *exception_header =
96+
static_cast<__cxa_exception *>((void *)(raw_buffer));
97+
::memset(exception_header, 0, actual_size);
98+
return thrown_object_from_cxa_exception(exception_header);
99+
}
100+
101+
102+
// Free a __cxa_exception object allocated with __cxa_allocate_exception.
103+
void __cxa_free_exception(void *thrown_object) _NOEXCEPT {
104+
// Compute the size of the padding before the header.
105+
char *raw_buffer =
106+
((char *)cxa_exception_from_thrown_object(thrown_object));
107+
__aligned_free_with_fallback((void *)raw_buffer);
108+
}
109+
110+
/*
111+
If thrown_object is not null, atomically increment the referenceCount field
112+
of the __cxa_exception header associated with the thrown object referred to
113+
by thrown_object.
114+
115+
Requires: If thrown_object is not NULL, it is a native exception.
116+
*/
117+
void
118+
__cxa_increment_exception_refcount(void *thrown_object) _NOEXCEPT {
119+
if (thrown_object != NULL )
120+
{
121+
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
122+
DEBUG("INC: %p refcnt=%zu", thrown_object, exception_header->referenceCount);
123+
std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(1));
124+
}
125+
}
126+
127+
/*
128+
If thrown_object is not null, atomically decrement the referenceCount field
129+
of the __cxa_exception header associated with the thrown object referred to
130+
by thrown_object. If the referenceCount drops to zero, destroy and
131+
deallocate the exception.
132+
133+
Requires: If thrown_object is not NULL, it is a native exception.
134+
*/
135+
_LIBCXXABI_NO_CFI
136+
void __cxa_decrement_exception_refcount(void *thrown_object) _NOEXCEPT {
137+
if (thrown_object != NULL )
138+
{
139+
__cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object);
140+
DEBUG("DEC: %p refcnt=%zu rethrown=%d", thrown_object,
141+
exception_header->referenceCount, exception_header->rethrown);
142+
assert(exception_header->referenceCount > 0);
143+
if (std::__libcpp_atomic_add(&exception_header->referenceCount, size_t(-1)) == 0 && !exception_header->rethrown)
144+
{
145+
DEBUG("DEL: %p (dtor=%p)", thrown_object, exception_header->exceptionDestructor);
146+
if (NULL != exception_header->exceptionDestructor)
147+
exception_header->exceptionDestructor(thrown_object);
148+
__cxa_free_exception(thrown_object);
149+
}
150+
}
151+
}
152+
153+
} // extern "C"
154+
155+
} // abi
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include "cxxabi.h"
2+
3+
#include "cxa_exception.h"
4+
#include "private_typeinfo.h"
5+
#include <stdio.h>
6+
// #include <stdint.h>
7+
// #include <stdlib.h>
8+
#include <string.h>
9+
10+
namespace __cxxabiv1 {
11+
12+
// Utility routines
13+
static
14+
inline
15+
__cxa_exception*
16+
cxa_exception_from_thrown_object(void* thrown_object)
17+
{
18+
return static_cast<__cxa_exception*>(thrown_object) - 1;
19+
}
20+
21+
// Note: This is never called when exception_header is masquerading as a
22+
// __cxa_dependent_exception.
23+
static
24+
inline
25+
void*
26+
thrown_object_from_cxa_exception(__cxa_exception* exception_header)
27+
{
28+
return static_cast<void*>(exception_header + 1);
29+
}
30+
31+
// Get the exception object from the unwind pointer.
32+
// Relies on the structure layout, where the unwind pointer is right in
33+
// front of the user's exception object
34+
static inline __cxa_exception* cxa_exception_from_unwind_exception(
35+
_Unwind_Exception* unwind_exception) {
36+
return cxa_exception_from_thrown_object(unwind_exception + 1);
37+
}
38+
39+
extern "C" {
40+
41+
void* __thrown_object_from_unwind_exception(
42+
_Unwind_Exception* unwind_exception) {
43+
__cxa_exception* exception_header =
44+
cxa_exception_from_unwind_exception(unwind_exception);
45+
return thrown_object_from_cxa_exception(exception_header);
46+
}
47+
48+
// Given a thrown_object, puts the information about its type and message into
49+
// 'type' and 'message' output parameters. 'type' will contain the string
50+
// representation of the type of the exception, e.g., 'int'. 'message' will
51+
// contain the result of 'std::exception::what()' method if the type of the
52+
// exception is a subclass of std::exception; otherwise it will be NULL. The
53+
// caller is responsible for freeing 'type' buffer and also 'message' buffer, if
54+
// it is not NULL.
55+
void __get_exception_message(void* thrown_object, char** type, char** message) {
56+
__cxa_exception* exception_header =
57+
cxa_exception_from_thrown_object(thrown_object);
58+
const __shim_type_info* thrown_type =
59+
static_cast<const __shim_type_info*>(exception_header->exceptionType);
60+
const char* type_name = thrown_type->name();
61+
62+
int status = 0;
63+
char* demangled_buf = __cxa_demangle(type_name, 0, 0, &status);
64+
if (status == 0 && demangled_buf) {
65+
*type = demangled_buf;
66+
} else {
67+
if (demangled_buf) {
68+
free(demangled_buf);
69+
}
70+
*type = (char*)malloc(strlen(type_name) + 1);
71+
strcpy(*type, type_name);
72+
}
73+
74+
*message = NULL;
75+
const __shim_type_info* catch_type =
76+
static_cast<const __shim_type_info*>(&typeid(std::exception));
77+
int can_catch = catch_type->can_catch(thrown_type, thrown_object);
78+
if (can_catch) {
79+
const char* what =
80+
static_cast<const std::exception*>(thrown_object)->what();
81+
*message = (char*)malloc(strlen(what) + 1);
82+
strcpy(*message, what);
83+
}
84+
}
85+
86+
// Returns a message saying that execution was terminated due to an exception.
87+
// This message is freshly malloc'd and should be freed.
88+
char* __get_exception_terminate_message(void* thrown_object) {
89+
char* type;
90+
char* message;
91+
__get_exception_message(thrown_object, &type, &message);
92+
char* result;
93+
if (message != NULL) {
94+
asprintf(
95+
&result, "terminating with uncaught exception %s: %s", type, message);
96+
free(message);
97+
} else {
98+
asprintf(&result, "terminating with uncaught exception of type %s", type);
99+
}
100+
free(type);
101+
return result;
102+
}
103+
} // extern "C"
104+
105+
} // namespace __cxxabiv1

0 commit comments

Comments
 (0)