diff --git a/stdlib/Makefile b/stdlib/Makefile index 0d9fbf1d..d1c1e528 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -4,4 +4,4 @@ # Copyright 2018, 2019, 2020 Phoenix Systems # -OBJS += $(addprefix $(PREFIX_O)stdlib/, abort.o bsearch.o div.o exit.o mktemp.o qsort.o random.o strtoul.o atexit.o env.o malloc_dl.o pty.o rand.o strtod.o strtoull.o) +OBJS += $(addprefix $(PREFIX_O)stdlib/, abort.o bsearch.o div.o exit.o mktemp.o qsort.o random.o strtoul.o atexit.o env.o malloc_dl.o pty.o rand.o strtod.o strtoull.o cxa_thread_atexit.o) diff --git a/stdlib/atexit.c b/stdlib/atexit.c index e6c095c5..19c208f2 100644 --- a/stdlib/atexit.c +++ b/stdlib/atexit.c @@ -45,12 +45,17 @@ static struct { } atexit_common = { .head = &((struct atexit_node) {}) }; +extern void __cxa_thread_atexit_init(void); + + /* Initialise atexit_common structure before main */ void _atexit_init(void) { mutexCreate(&atexit_common.lock); memset(atexit_common.head, 0, sizeof(struct atexit_node)); atexit_common.idx = 0; + + __cxa_thread_atexit_init(); } diff --git a/stdlib/cxa_thread_atexit.c b/stdlib/cxa_thread_atexit.c new file mode 100644 index 00000000..c98cf35f --- /dev/null +++ b/stdlib/cxa_thread_atexit.c @@ -0,0 +1,130 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * atexit.c + * + * Copyright 2019 2022 Phoenix Systems + * Author: Kamil Amanowicz, Dawid Szpejna + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include + + +void __cxa_thread_atexit_init(void); +void __cxa_thread_atexit_run(void); +int __cxa_thread_atexit_impl(void (*)(void *), void *, void *); + + +#ifdef __LIBPHOENIX_ARCH_TLS_SUPPORTED + + +void __libc__dl_cxa_refcount(void *addr, ssize_t delta) +{ + /* Do nothing in static lib */ + (void)addr; + (void)delta; +} + + +extern void __dl_cxa_refcount(void *, ssize_t) __attribute__((weak, alias("__libc__dl_cxa_refcount"))); + + +struct __cxa_atexit_node { + struct __cxa_atexit_node *prev; + void (*dtor)(void *); + void *obj; + void *handle; +}; + + +static __thread struct __cxa_atexit_node *__cxa_thread_atexit_head; + + +extern int can_use_tls; + + +void __cxa_thread_atexit_init(void) +{ + /* Special case for dynamic linker exit */ + if (can_use_tls != 0) { + atexit(__cxa_thread_atexit_run); + } +} + + +void __cxa_thread_atexit_run(void) +{ + while (__cxa_thread_atexit_head != NULL) { + struct __cxa_atexit_node *last = __cxa_thread_atexit_head; + + __cxa_thread_atexit_head->dtor(__cxa_thread_atexit_head->obj); + __cxa_thread_atexit_head = __cxa_thread_atexit_head->prev; + + if (last->handle != NULL) { + __dl_cxa_refcount(last->handle, -1); + } + + free(last); + } +} + + +/* C++ thread-local destructor handler */ +int __cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *handle) +{ + struct __cxa_atexit_node *node = malloc(sizeof(struct __cxa_atexit_node)); + + if (node == NULL) { + return -1; + } + + node->dtor = dtor; + node->obj = obj; + node->handle = handle; + node->prev = __cxa_thread_atexit_head; + + if (node->handle != NULL) { + __dl_cxa_refcount(node->handle, 1); + } + + __cxa_thread_atexit_head = node; + + return 0; +} + + +#else + + +void __cxa_thread_atexit_init(void) +{ + /* Nothing to do */ +} + + +void __cxa_thread_atexit_run(void) +{ + /* Nothing to do */ +} + + +/* NOTE: no support for __cxa_thread_atexit_impl on platforms not supporting TLS. */ +int __cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *handle) +{ + (void)dtor; + (void)obj; + (void)handle; + + abort(); +} + + +#endif diff --git a/sys/threads.c b/sys/threads.c index 746d22cd..a945f872 100644 --- a/sys/threads.c +++ b/sys/threads.c @@ -52,8 +52,12 @@ int beginthreadex(void (*start)(void *), unsigned int priority, void *stack, uns } +void __cxa_thread_atexit_run(void); + + __attribute__((noreturn)) void endthread(void) { + __cxa_thread_atexit_run(); _rtld_tls_free_curr(); endthreadsvc(); }