Index: uspace/lib/c/generic/rtld/rtld.c
===================================================================
--- uspace/lib/c/generic/rtld/rtld.c	(revision e2f260026920f1d43bed8d2b775564b1f64a6f6a)
+++ uspace/lib/c/generic/rtld/rtld.c	(revision d2bb25e777311fb1c2e3cb09e66bab0c8b523a13)
@@ -143,4 +143,7 @@
 	tcb_t *tcb;
 	size_t offset;
+	void **dtv;
+	size_t nmods;
+	size_t i;
 
 	tcb = tls_alloc_arch(&data, rtld->tls_size);
@@ -148,7 +151,21 @@
 		return NULL;
 
-	/*
-	 * Copy thread local data from the modules' initialization images.
-	 * Zero out thread-local uninitialized data.
+	/** Allocate dynamic thread vector */
+	nmods = list_count(&rtld->imodules);
+	dtv = malloc((nmods + 1) * sizeof(void *));
+	if (dtv == NULL) {
+		tls_free(tcb);
+		return NULL;
+	}
+
+	/*
+	 * We define generation number to be equal to vector length.
+	 * We start with a vector covering the initially loaded modules.
+	 */
+	DTV_GN(dtv) = nmods;
+
+	/*
+	 * Copy thread local data from the initialization images of initial
+	 * modules. Zero out thread-local uninitialized data.
 	 */
 
@@ -157,7 +174,9 @@
 	 * Ascending addresses
 	 */
-	offset = 0;
+	offset = 0; i = 1;
 	list_foreach(rtld->imodules, imodules_link, module_t, m) {
+		assert(i == m->id);
 		assert(offset + m->tdata_size + m->tbss_size <= rtld->tls_size);
+		dtv[i++] = data + offset;
 		memcpy(data + offset, m->tdata, m->tdata_size);
 		offset += m->tdata_size;
@@ -169,6 +188,7 @@
 	 * Descending addresses
 	 */
-	offset = 0;
+	offset = 0; i = 1;
 	list_foreach(rtld->imodules, imodules_link, module_t, m) {
+		assert(i == m->id);
 		assert(offset + m->tdata_size + m->tbss_size <= rtld->tls_size);
 		offset += m->tbss_size;
@@ -176,7 +196,9 @@
 		offset += m->tdata_size;
 		memcpy(data + rtld->tls_size - offset, m->tdata, m->tdata_size);
+		dtv[i++] = data + rtld->tls_size - offset;
 	}
 #endif
 
+	tcb->dtv = dtv;
 	return tcb;
 }
@@ -187,23 +209,23 @@
 }
 
-void *rtld_tls_get_addr(rtld_t *rtld, unsigned long mod_id,
+/** Get address of thread-local variable.
+ *
+ * @param rtld RTLD instance
+ * @param tcb TCB of the thread whose instance to return
+ * @param mod_id Module ID
+ * @param offset Offset within TLS block of the module
+ *
+ * @return Address of thread-local variable
+ */
+void *rtld_tls_get_addr(rtld_t *rtld, tcb_t *tcb, unsigned long mod_id,
     unsigned long offset)
 {
-	module_t *m;
-	uint8_t *tls;
-
-	m = module_by_id(rtld, mod_id);
-	assert(m != NULL);
-
-	if (!link_used(&m->imodules_link)) {
-		printf("module '%s' is not initial. aborting.\n",
-		    m->dyn.soname);
+	if (DTV_GN(tcb->dtv) < mod_id || tcb->dtv[mod_id] == NULL) {
+		printf("Module is not initial. aborting.\n");
 		abort();
 	}
 
-	tls = tls_get();
-	return tls + m->ioffs + offset;
-}
-
+	return (uint8_t *)(tcb->dtv[mod_id]) + offset;
+}
 
 /** @}
Index: uspace/lib/c/generic/rtld/symbol.c
===================================================================
--- uspace/lib/c/generic/rtld/symbol.c	(revision e2f260026920f1d43bed8d2b775564b1f64a6f6a)
+++ uspace/lib/c/generic/rtld/symbol.c	(revision d2bb25e777311fb1c2e3cb09e66bab0c8b523a13)
@@ -249,8 +249,20 @@
 }
 
-void *symbol_get_addr(elf_symbol_t *sym, module_t *m)
+/** Get symbol address.
+ *
+ * @param sym Symbol
+ * @param m Module contaning the symbol
+ * @param tcb TCB of the thread whose thread-local variable instance should
+ *            be returned. If @a tcb is @c NULL then @c NULL is returned for
+ *            thread-local variables.
+ *
+ * @return Symbol address
+ */
+void *symbol_get_addr(elf_symbol_t *sym, module_t *m, tcb_t *tcb)
 {
 	if (ELF_ST_TYPE(sym->st_info) == STT_TLS) {
-		return rtld_tls_get_addr(m->rtld, m->id, sym->st_value);
+		if (tcb == NULL)
+			return NULL;
+		return rtld_tls_get_addr(m->rtld, tcb, m->id, sym->st_value);
 	} else if (sym->st_shndx == SHN_ABS) {
 		/* Do not add bias to absolute symbols */
