diff --git a/sys/cxx_ctor_guards/Makefile b/sys/cxx_ctor_guards/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/cxx_ctor_guards/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/cxx_ctor_guards/cxa_guard.c b/sys/cxx_ctor_guards/cxa_guard.c new file mode 100644 index 0000000000..56e35b0dd5 --- /dev/null +++ b/sys/cxx_ctor_guards/cxa_guard.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_cxx_ctor_guards + * @{ + * + * @file + * @brief Implementation of the C++ ctor guards + * + * @author Marian Buschsieweke + */ + +#include "test_utils/expect.h" +#include "rmutex.h" + +#ifdef CXX_CTOR_GUARDS_CUSTOM_TYPE +/* Some architectures (such as ARM) have custom types for __guard in their + * ABI defined. To support such cases, a custom header can be provided and + * `CXX_CTOR_GUARDS_CUSTOM_TYPE` can be defined */ +#include "cxx_ctor_guards_arch.h" +#else +/* The ABI for"generic" CPUs defines __guard as follows: + * (see "libstdc++-v3/config/cpu/generic/cxxabi_tweaks.h" in GCC source) */ +__extension__ typedef int __guard __attribute__((mode(__DI__))); +/* Above is just a fancy version of `typedef int64_t __guard`. But let's stick + * with the official type definition */ +#endif + +#define GUARD_DONE 0x01 +#define GUARD_PENDING 0x02 + +/* A recursive mutex is needed, as the initialization of a static class could + * require calling ctors of members of that class. With a regular mutex, this + * would result in a deadlock. + */ +static rmutex_t cxa_rmutex = RMUTEX_INIT; + +int __cxa_guard_acquire(__guard *g) +{ + uint8_t *guard = (uint8_t *)g; + /* Optimize for the case that the instance is already initialized by + * doing a lock-free check. */ + if (__atomic_load_1(guard, __ATOMIC_SEQ_CST) & GUARD_DONE) { + return 0; + } + + rmutex_lock(&cxa_rmutex); + /* Check again (this time protected by the rmutex) for the instance being + * already initialized */ + if (*guard & GUARD_DONE) { + rmutex_unlock(&cxa_rmutex); + return 0; + } + + if (*guard & GUARD_PENDING) { + /* Recursive initialization of the *same* instance --> bug */ + expect(0); + } + + *guard = GUARD_PENDING; + return 1; +} + +void __cxa_guard_release(__guard *g) +{ + uint8_t *guard = (uint8_t *)g; + *guard = GUARD_DONE; + rmutex_unlock(&cxa_rmutex); +} + +void __cxa_guard_abort(__guard *g) +{ + uint8_t *guard = (uint8_t *)g; + *guard = 0; + rmutex_unlock(&cxa_rmutex); +} diff --git a/sys/cxx_ctor_guards/doc.txt b/sys/cxx_ctor_guards/doc.txt new file mode 100644 index 0000000000..0e75d17f37 --- /dev/null +++ b/sys/cxx_ctor_guards/doc.txt @@ -0,0 +1,51 @@ +/** +@defgroup sys_cxx_ctor_guards C++ constructor guards for static instances +@ingroup sys +@brief C++ constructor guards for thread-safe initialization of static + instances + +@warning This implementation is likely only compatible with `g++` + +# Introduction + +The libstd++ ABI requires implementations of the following functions: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c} +int __cxa_guard_acquire(__guard *g); +void __cxa_guard_release(__guard *g); +void __cxa_guard_abort(__guard *g); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These functions are not intended to be ever used by the programmer, instead +the C++ compiler will emit code calling them if statically allocated class +instances are used. In a multi-threaded environment special care needs to be +taken to prevent race conditions while initializing and using such instances. +This modules provides them. + +# Usage + +This module is intended to be used by platforms that want to provide C++ +support, but the used standard C++ library does not provide these guards. In +this case, adding this module will do the trick. The programmer / user should +never interact with any of the functions. + +Note that on some platforms the type `__guard` is defined differently from the +"generic" definition, most notably ARM. For those platforms a header named +`cxx_ctor_guards_arch.h` needs to be created containing the correct `typedef` +and the preprocessor macro `CXX_CTOR_GUARDS_CUSTOM_TYPE` needs to be defined. + +# Implementation + +This implementation provides the C++ ctor guards as defined by the libstd++ ABI +used in g++. It will likely not be compatible with other implementations of +libstd++. + +The libstd++ ABI expects the functions to be implemented as C functions. Most +implementations will put the code into C++ files and wrap everything into an +`extern "C" {...}`. This implementation will just use a plain C file for less +boilerplate. + +The implementation intentionally makes only use of a single byte of the +`__guard` type. This should result in the implementation being usable on any +platform, regardless of the actual size of `__guard`. + */