/* * \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(); }