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