diff --git a/repos/base/lib/mk/cxx.mk b/repos/base/lib/mk/cxx.mk index bcc546dff..e197a296c 100644 --- a/repos/base/lib/mk/cxx.mk +++ b/repos/base/lib/mk/cxx.mk @@ -1,4 +1,4 @@ -CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc +CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc emutls.cc INC_DIR += $(REP_DIR)/src/include # We need the libsupc++ include directory STDINC = yes diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index aebb9d3dc..c8874645e 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -638,6 +638,7 @@ __cxa_throw_bad_array_length T __cxa_throw_bad_array_new_length T __cxa_type_match T __dynamic_cast T +__emutls_get_address T __gxx_personality_v0 T __stack_chk_fail W __stack_chk_guard B 8 diff --git a/repos/base/recipes/pkg/test-tls/README b/repos/base/recipes/pkg/test-tls/README new file mode 100644 index 000000000..f53a02511 --- /dev/null +++ b/repos/base/recipes/pkg/test-tls/README @@ -0,0 +1 @@ +Scenario that tests thread-local storage using the 'thread_local' keyword diff --git a/repos/base/recipes/pkg/test-tls/archives b/repos/base/recipes/pkg/test-tls/archives new file mode 100644 index 000000000..4e072d741 --- /dev/null +++ b/repos/base/recipes/pkg/test-tls/archives @@ -0,0 +1,2 @@ +_/src/init +_/src/test-tls diff --git a/repos/base/recipes/pkg/test-tls/hash b/repos/base/recipes/pkg/test-tls/hash new file mode 100644 index 000000000..8ec2f96f0 --- /dev/null +++ b/repos/base/recipes/pkg/test-tls/hash @@ -0,0 +1 @@ +2019-06-14-a 32cef2fb399ad2ead392710f70908d02ca0cfcb0 diff --git a/repos/base/recipes/pkg/test-tls/runtime b/repos/base/recipes/pkg/test-tls/runtime new file mode 100644 index 000000000..471fc010a --- /dev/null +++ b/repos/base/recipes/pkg/test-tls/runtime @@ -0,0 +1,35 @@ + + + + + + [init -> test-tls] main initial: x: -1, y: 0 + [init -> test-tls] thread 0 initial: x: -1, y: 0 + [init -> test-tls] thread 0 : x: 1, y: 2 + [init -> test-tls] thread 1 initial: x: -1, y: 0 + [init -> test-tls] thread 1 : x: 3, y: 4 + [init -> test-tls] main : x: 5, y: 6 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/base/recipes/src/test-tls/content.mk b/repos/base/recipes/src/test-tls/content.mk new file mode 100644 index 000000000..aee99e99c --- /dev/null +++ b/repos/base/recipes/src/test-tls/content.mk @@ -0,0 +1,2 @@ +SRC_DIR = src/test/tls +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/base/recipes/src/test-tls/hash b/repos/base/recipes/src/test-tls/hash new file mode 100644 index 000000000..de0ba407c --- /dev/null +++ b/repos/base/recipes/src/test-tls/hash @@ -0,0 +1 @@ +2019-06-14 a9faa56873451803a1cd0f4df0ad175bd4e4731b diff --git a/repos/base/recipes/src/test-tls/used_apis b/repos/base/recipes/src/test-tls/used_apis new file mode 100644 index 000000000..df967b96a --- /dev/null +++ b/repos/base/recipes/src/test-tls/used_apis @@ -0,0 +1 @@ +base diff --git a/repos/base/src/include/base/internal/globals.h b/repos/base/src/include/base/internal/globals.h index 9c899c422..c934f8ccf 100644 --- a/repos/base/src/include/base/internal/globals.h +++ b/repos/base/src/include/base/internal/globals.h @@ -47,6 +47,7 @@ namespace Genode { void cxx_demangle(char const*, char*, size_t); void cxx_current_exception(char *out, size_t size); + void cxx_free_tls(void *thread); Id_space &env_session_id_space(); Env &internal_env(); diff --git a/repos/base/src/lib/base/thread.cc b/repos/base/src/lib/base/thread.cc index 7c55e1295..769ce2421 100644 --- a/repos/base/src/lib/base/thread.cc +++ b/repos/base/src/lib/base/thread.cc @@ -251,6 +251,8 @@ Thread::~Thread() _deinit_platform_thread(); _free_stack(_stack); + cxx_free_tls(this); + /* * We have to detach the trace control dataspace last because * we cannot invalidate the pointer used by the Trace::Logger diff --git a/repos/base/src/lib/cxx/emutls.cc b/repos/base/src/lib/cxx/emutls.cc new file mode 100644 index 000000000..6100e9b4b --- /dev/null +++ b/repos/base/src/lib/cxx/emutls.cc @@ -0,0 +1,196 @@ +/* + * \brief TLS support ('emutls') + * \author Christian Prochaska + * \date 2019-06-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include + +/* base-internal includes */ +#include + +using namespace Genode; + +/* implemented in 'malloc_free.cc' */ +extern Heap &cxx_heap(); + +static constexpr bool _verbose = false; + + +/* + * An emutls object describes the properties of a thread-local variable. + * Structure layout as defined in libgcc's 'emutls.c'. + */ +struct __emutls_object +{ + size_t const size; /* size of the variable */ + size_t const align; /* alignment of the variable */ + void *ptr; /* used in this implementation for an AVL tree with + references to all the thread-local instances */ + void const *templ; /* template for initializing a thread-local instance */ +}; + + +/* + * AVL node referencing the thread-local variable instance of a specific thread + */ +class Tls_node : public Avl_node +{ + private: + + void *_thread; /* key */ + void *_address; /* value */ + + /* Noncopyable */ + Tls_node(const Tls_node&); + void operator=(const Tls_node&); + + public: + + Tls_node(void *thread, void *address) + : _thread(thread), _address(address) { } + + void *address() { return _address; } + + bool higher(Tls_node *other) + { + return (other->_thread > _thread); + } + + Tls_node *find_by_thread(void *thread) + { + if (thread == _thread) return this; + + Tls_node *c = child(thread > _thread); + return c ? c->find_by_thread(thread) : nullptr; + } +}; + + +struct Tls_tree : Avl_tree, List::Element { }; + + +/* + * List referencing all AVL trees. + * Needed for freeing all allocated variable instances of a thread. + */ +static List &_tls_tree_list() +{ + static List instance; + return instance; +} + + +static Lock &_emutls_lock() +{ + static Lock instance; + return instance; +} + + +/* + * Free all thread-local variable instances of the given thread + */ +void Genode::cxx_free_tls(void *thread) +{ + Lock::Guard lock_guard(_emutls_lock()); + + for (Tls_tree *tls_tree = _tls_tree_list().first(); + tls_tree; tls_tree = tls_tree->next()) { + + if (tls_tree->first()) { + + Tls_node *tls_node = tls_tree->first()->find_by_thread(thread); + + if (tls_node) { + tls_tree->remove(tls_node); + cxx_heap().free(tls_node->address(), 0); + destroy(cxx_heap(), tls_node); + } + } + } +} + + +/* + * This function is called when a thread-local variable is accessed. + * It returns the address of the variable for the current thread and + * allocates and initializes the variable when it is accessed for the + * first time by this thread. + */ +extern "C" void *__emutls_get_address(void *obj) +{ + Lock::Guard lock_guard(_emutls_lock()); + + __emutls_object *emutls_object = reinterpret_cast<__emutls_object*>(obj); + + if (_verbose) + log(__func__, ": emutls_object: ", emutls_object, + ", size: ", emutls_object->size, + ", align: ", emutls_object->align, + ", ptr: ", emutls_object->ptr, + ", templ: ", emutls_object->templ); + + if (!emutls_object->ptr) { + /* + * The variable is accessed for the first time by any thread. + * Create an AVL tree which keeps track of all instances of this + * variable. + */ + Tls_tree *tls_tree = new (cxx_heap()) Tls_tree; + _tls_tree_list().insert(tls_tree); + emutls_object->ptr = tls_tree; + } + + Tls_tree *tls_tree = static_cast(emutls_object->ptr); + + Thread *myself = Thread::myself(); + + Tls_node *tls_node = nullptr; + + if (tls_tree->first()) + tls_node = tls_tree->first()->find_by_thread(myself); + + if (!tls_node) { + + /* + * The variable is accessed for the first time by this thread. + * Allocate and initialize a new variable instance and store a + * reference in the AVL tree. + */ + + /* the heap allocates 16-byte aligned */ + if ((16 % emutls_object->align) != 0) + Genode::warning(__func__, ": cannot ensure alignment of ", + emutls_object->align, " bytes"); + + void *address = nullptr; + + if (!cxx_heap().alloc(emutls_object->size, &address)) { + Genode::error(__func__, + ": could not allocate thread-local variable instance"); + return nullptr; + } + + if (emutls_object->templ) + memcpy(address, emutls_object->templ, emutls_object->size); + else + memset(address, 0, emutls_object->size); + + tls_node = new (cxx_heap()) Tls_node(myself, address); + + tls_tree->insert(tls_node); + } + + return tls_node->address(); +} diff --git a/repos/base/src/lib/cxx/malloc_free.cc b/repos/base/src/lib/cxx/malloc_free.cc index 74ea50c5b..3900be132 100644 --- a/repos/base/src/lib/cxx/malloc_free.cc +++ b/repos/base/src/lib/cxx/malloc_free.cc @@ -30,7 +30,7 @@ using namespace Genode; static Heap *cxx_heap_ptr; -static Heap &cxx_heap() +Heap &cxx_heap() { class Cxx_heap_uninitialized : Exception { }; if (!cxx_heap_ptr) diff --git a/repos/base/src/test/tls/main.cc b/repos/base/src/test/tls/main.cc new file mode 100644 index 000000000..8d988d231 --- /dev/null +++ b/repos/base/src/test/tls/main.cc @@ -0,0 +1,62 @@ +/* + * \brief Test TLS support + * \author Christian Prochaska + * \date 2019-06-13 + */ + +#include +#include +#include + + +static thread_local int thread_local_x = -1; +static thread_local int thread_local_y; + + +struct Test_thread : Genode::Thread +{ + int _thread_id; + int _x; + int _y; + + Test_thread(Genode::Env &env, int thread_id, int x, int y) + : Genode::Thread(env, "test_thread", 16*1024), + _thread_id(thread_id), _x(x), _y(y) + { + start(); + } + + void entry() override + { + Genode::log("thread ", _thread_id, + " initial: x: ", thread_local_x, + ", y: ", thread_local_y); + + thread_local_x = _x; + thread_local_y = _y; + + Genode::log("thread ", _thread_id, + " : x: ", thread_local_x, + ", y: ", thread_local_y); + } +}; + + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + Genode::log("main initial: x: ", thread_local_x, + ", y: ", thread_local_y); + + thread_local_x = 5; + thread_local_y = 6; + + for (int i = 0; i < 2; i++) { + Test_thread test_thread(env, i, (i * 2) + 1, (i * 2) + 2); + test_thread.join(); + } + + Genode::log("main : x: ", thread_local_x, + ", y: ", thread_local_y); +} diff --git a/repos/base/src/test/tls/target.mk b/repos/base/src/test/tls/target.mk new file mode 100644 index 000000000..46d0a41d1 --- /dev/null +++ b/repos/base/src/test/tls/target.mk @@ -0,0 +1,3 @@ +TARGET = test-tls +SRC_CC = main.cc +LIBS = base diff --git a/repos/gems/run/depot_autopilot.run b/repos/gems/run/depot_autopilot.run index f77e38340..f0d4dfc7b 100644 --- a/repos/gems/run/depot_autopilot.run +++ b/repos/gems/run/depot_autopilot.run @@ -722,6 +722,7 @@ set default_test_pkgs { test-terminal_crosslink test-timed_semaphore test-timer + test-tls test-trace test-trace_logger test-utf8 diff --git a/repos/ports/ports/gcc.hash b/repos/ports/ports/gcc.hash index 3a3e4cd70..7f24e9c57 100644 --- a/repos/ports/ports/gcc.hash +++ b/repos/ports/ports/gcc.hash @@ -1 +1 @@ -893d85767720fc2b3c695c56d737d2307b1f2229 +3283673a91ae961dfb4e439854ef4f2a23bdc793 diff --git a/repos/ports/src/noux-pkg/gcc/patches/emutls.patch b/repos/ports/src/noux-pkg/gcc/patches/emutls.patch new file mode 100644 index 000000000..13917f4e3 --- /dev/null +++ b/repos/ports/src/noux-pkg/gcc/patches/emutls.patch @@ -0,0 +1,44 @@ +emutls.patch + +From: Christian Prochaska + + +--- + libgcc/Makefile.in | 9 ++++++--- + libstdc++-v3/configure.ac | 3 +++ + 2 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in +index 8cae772c8..7e4cbd0ea 100644 +--- a/libgcc/Makefile.in ++++ b/libgcc/Makefile.in +@@ -428,9 +428,12 @@ LIB2ADD += enable-execute-stack.c + # While emutls.c has nothing to do with EH, it is in LIB2ADDEH* + # instead of LIB2ADD because that's the way to be sure on some targets + # (e.g. *-*-darwin*) only one copy of it is linked. +-LIB2ADDEH += $(srcdir)/emutls.c +-LIB2ADDEHSTATIC += $(srcdir)/emutls.c +-LIB2ADDEHSHARED += $(srcdir)/emutls.c ++# ++# Genode implements '__emutls_get_address()' in the 'cxx' library ++# ++#LIB2ADDEH += $(srcdir)/emutls.c ++#LIB2ADDEHSTATIC += $(srcdir)/emutls.c ++#LIB2ADDEHSHARED += $(srcdir)/emutls.c + + # Library members defined in libgcc2.c. + lib2funcs = _muldi3 _negdi2 _lshrdi3 _ashldi3 _ashrdi3 _cmpdi2 _ucmpdi2 \ +diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac +index 7d7279b06..84d8c6650 100644 +--- a/libstdc++-v3/configure.ac ++++ b/libstdc++-v3/configure.ac +@@ -367,6 +367,9 @@ else + AC_DEFINE(HAVE_TANL) + AC_DEFINE(HAVE_TANHL) + fi ++ ++ # Genode supports TLS ++ AC_DEFINE(HAVE_TLS) + fi + + # Check for _Unwind_GetIPInfo. diff --git a/repos/ports/src/noux-pkg/gcc/patches/series b/repos/ports/src/noux-pkg/gcc/patches/series index 663d26c30..08a408dbd 100644 --- a/repos/ports/src/noux-pkg/gcc/patches/series +++ b/repos/ports/src/noux-pkg/gcc/patches/series @@ -12,3 +12,4 @@ noux_build.patch arm.patch new_opa.patch aarch64.patch +emutls.patch