Index: uspace/lib/c/arch/abs32le/src/tls.c
===================================================================
--- uspace/lib/c/arch/abs32le/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/abs32le/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -34,12 +34,12 @@
 #include <stdint.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_2(data, size);
+	return tls_alloc_variant_2(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_2(tcb, size);
+	tls_free_variant_2(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/amd64/src/tls.c
===================================================================
--- uspace/lib/c/arch/amd64/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/amd64/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -38,12 +38,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_2(data, size);
+	return tls_alloc_variant_2(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_2(tcb, size);
+	tls_free_variant_2(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/arm32/src/tls.c
===================================================================
--- uspace/lib/c/arch/arm32/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/arm32/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -38,12 +38,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_1(data, size);
+	return tls_alloc_variant_1(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_1(tcb, size);
+	tls_free_variant_1(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/ia32/src/rtld/reloc.c
===================================================================
--- uspace/lib/c/arch/ia32/src/rtld/reloc.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/ia32/src/rtld/reloc.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -181,5 +181,5 @@
 		case R_386_TLS_TPOFF:
 			DPRINTF("fixup R_386_TLS_TPOFF\n");
-			*r_ptr = (dest->ioffs + sym_def->st_value) - dest->rtld->tls_size;
+			*r_ptr = sym_def->st_value + dest->tpoff;
 			break;
 
Index: uspace/lib/c/arch/ia32/src/tls.c
===================================================================
--- uspace/lib/c/arch/ia32/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/ia32/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -45,12 +45,12 @@
 #endif
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_2(data, size);
+	return tls_alloc_variant_2(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_2(tcb, size);
+	tls_free_variant_2(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/ia64/src/tls.c
===================================================================
--- uspace/lib/c/arch/ia64/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/ia64/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -37,12 +37,12 @@
 #include <tls.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_1(data, size);
+	return tls_alloc_variant_1(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_1(tcb, size);
+	tls_free_variant_1(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/mips32/src/tls.c
===================================================================
--- uspace/lib/c/arch/mips32/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/mips32/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -37,12 +37,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_1(data, size);
+	return tls_alloc_variant_1(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_1(tcb, size);
+	tls_free_variant_1(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/ppc32/src/tls.c
===================================================================
--- uspace/lib/c/arch/ppc32/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/ppc32/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -36,12 +36,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_1(data, size);
+	return tls_alloc_variant_1(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_1(tcb, size);
+	tls_free_variant_1(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/riscv64/src/tls.c
===================================================================
--- uspace/lib/c/arch/riscv64/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/riscv64/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -33,12 +33,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_2(data, size);
+	return tls_alloc_variant_2(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_2(tcb, size);
+	tls_free_variant_2(tcb, size, align);
 }
 
Index: uspace/lib/c/arch/sparc64/src/tls.c
===================================================================
--- uspace/lib/c/arch/sparc64/src/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/arch/sparc64/src/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -38,12 +38,12 @@
 #include <stddef.h>
 
-tcb_t *tls_alloc_arch(void **data, size_t size)
+tcb_t *tls_alloc_arch(size_t size, size_t align)
 {
-	return tls_alloc_variant_2(data, size);
+	return tls_alloc_variant_2(size, align);
 }
 
-void tls_free_arch(tcb_t *tcb, size_t size)
+void tls_free_arch(tcb_t *tcb, size_t size, size_t align)
 {
-	tls_free_variant_2(tcb, size);
+	tls_free_variant_2(tcb, size, align);
 }
 
Index: uspace/lib/c/generic/rtld/module.c
===================================================================
--- uspace/lib/c/generic/rtld/module.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/generic/rtld/module.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -35,4 +35,5 @@
  */
 
+#include <align.h>
 #include <adt/list.h>
 #include <elf/elf_load.h>
@@ -42,4 +43,5 @@
 #include <stdlib.h>
 #include <str.h>
+#include <macros.h>
 
 #include <rtld/rtld.h>
@@ -72,10 +74,17 @@
 	const elf_segment_header_t *tls =
 	    elf_get_phdr(__executable_start, PT_TLS);
-	uintptr_t bias = elf_get_bias(__executable_start);
-
-	module->tdata = (void *) (tls->p_vaddr + bias);
-	module->tdata_size = tls->p_filesz;
-	module->tbss_size = tls->p_memsz - tls->p_filesz;
-	module->tls_align = tls->p_align;
+
+	if (tls) {
+		uintptr_t bias = elf_get_bias(__executable_start);
+		module->tdata = (void *) (tls->p_vaddr + bias);
+		module->tdata_size = tls->p_filesz;
+		module->tbss_size = tls->p_memsz - tls->p_filesz;
+		module->tls_align = tls->p_align;
+	} else {
+		module->tdata = NULL;
+		module->tdata_size = 0;
+		module->tbss_size = 0;
+		module->tls_align = 1;
+	}
 
 	list_append(&module->modules_link, &rtld->modules);
@@ -324,22 +333,40 @@
 {
 #ifdef CONFIG_TLS_VARIANT_1
-	list_foreach(rtld->modules, modules_link, module_t, m) {
-		m->ioffs = rtld->tls_size;
-		list_append(&m->imodules_link, &rtmd->imodules);
+	rtld->tls_size = sizeof(tcb_t);
+	rtld->tls_align = _Alignof(tcb_t);
+
+	list_foreach(rtld->modules, modules_link, module_t, m) {
+		list_append(&m->imodules_link, &rtld->imodules);
+		rtld->tls_align = max(rtld->tls_align, m->tls_align);
+
+		rtld->tls_size = ALIGN_UP(rtld->tls_size, m->tls_align);
+		m->tpoff = rtld->tls_size;
 		rtld->tls_size += m->tdata_size + m->tbss_size;
 	}
-#else /* CONFIG_TLS_VARIANT_2 */
-	size_t offs;
-
-	list_foreach(rtld->modules, modules_link, module_t, m) {
+
+#else
+	rtld->tls_size = 0;
+	rtld->tls_align = _Alignof(tcb_t);
+
+	list_foreach(rtld->modules, modules_link, module_t, m) {
+		list_append(&m->imodules_link, &rtld->imodules);
+		rtld->tls_align = max(rtld->tls_align, m->tls_align);
+
+		/* We are allocating spans "backwards", here,
+		 * as described in U. Drepper's paper.
+		 */
 		rtld->tls_size += m->tdata_size + m->tbss_size;
-	}
-
-	offs = 0;
-	list_foreach(rtld->modules, modules_link, module_t, m) {
-		offs += m->tdata_size + m->tbss_size;
-		m->ioffs = rtld->tls_size - offs;
-		list_append(&m->imodules_link, &rtld->imodules);
-	}
+		rtld->tls_size = ALIGN_UP(rtld->tls_size, m->tls_align);
+		m->tpoff = -(ptrdiff_t) rtld->tls_size;
+	}
+
+	/* We are in negative offsets. In order for the alignments to
+	 * be correct, "zero" offset (i.e. the total size) must be aligned
+	 * to the strictest alignment present.
+	 */
+	rtld->tls_size = ALIGN_UP(rtld->tls_size, rtld->tls_align);
+
+	/* Space for the TCB. */
+	rtld->tls_size += sizeof(tcb_t);
 #endif
 }
Index: uspace/lib/c/generic/rtld/rtld.c
===================================================================
--- uspace/lib/c/generic/rtld/rtld.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/generic/rtld/rtld.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -152,12 +152,10 @@
 tcb_t *rtld_tls_make(rtld_t *rtld)
 {
-	void *data;
 	tcb_t *tcb;
-	size_t offset;
 	void **dtv;
 	size_t nmods;
 	size_t i;
 
-	tcb = tls_alloc_arch(&data, rtld->tls_size);
+	tcb = tls_alloc_arch(rtld->tls_size, rtld->tls_align);
 	if (tcb == NULL)
 		return NULL;
@@ -182,35 +180,15 @@
 	 */
 
-#ifdef CONFIG_TLS_VARIANT_1
-	/*
-	 * Ascending addresses
-	 */
-	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;
-		memset(data + offset, 0, m->tbss_size);
-		offset += m->tbss_size;
-	}
-#else /* CONFIG_TLS_VARIANT_2 */
-	/*
-	 * Descending addresses
-	 */
-	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;
-		memset(data + rtld->tls_size - offset, 0, m->tbss_size);
-		offset += m->tdata_size;
-		memcpy(data + rtld->tls_size - offset, m->tdata, m->tdata_size);
-		dtv[i++] = data + rtld->tls_size - offset;
-	}
-#endif
+		assert(i++ == m->id);
+
+		dtv[m->id] = (void *) tcb + m->tpoff;
+
+		assert(((uintptr_t) dtv[m->id]) % m->tls_align == 0);
+
+		memcpy(dtv[m->id], m->tdata, m->tdata_size);
+		memset(dtv[m->id] + m->tdata_size, 0, m->tbss_size);
+	}
 
 	tcb->dtv = dtv;
@@ -259,5 +237,5 @@
 		assert(!link_used(&m->imodules_link));
 
-		tls_block = malloc(m->tdata_size + m->tbss_size);
+		tls_block = memalign(m->tls_align, m->tdata_size + m->tbss_size);
 		/* XXX This can fail if OOM */
 		assert(tls_block != NULL);
Index: uspace/lib/c/generic/tls.c
===================================================================
--- uspace/lib/c/generic/tls.c	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/generic/tls.c	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -36,4 +36,5 @@
  */
 
+#include <assert.h>
 #include <stddef.h>
 #include <align.h>
@@ -41,4 +42,5 @@
 #include <stdlib.h>
 #include <str.h>
+#include <macros.h>
 #include <elf/elf.h>
 
@@ -47,23 +49,25 @@
 #endif
 
-size_t tls_get_size(void)
-{
-#ifdef CONFIG_RTLD
-	if (runtime_env != NULL)
-		return runtime_env->tls_size;
+#if !defined(CONFIG_TLS_VARIANT_1) && !defined(CONFIG_TLS_VARIANT_2)
+#error Unknown TLS variant.
+#endif
+
+/** Get address of static TLS block */
+void *tls_get(void)
+{
+#ifdef CONFIG_RTLD
+	assert(runtime_env == NULL);
 #endif
 
 	const elf_segment_header_t *tls =
 	    elf_get_phdr(__executable_start, PT_TLS);
-	return tls->p_memsz;
-}
-
-/** Get address of static TLS block */
-void *tls_get(void)
-{
+
+	if (tls == NULL)
+		return NULL;
+
 #ifdef CONFIG_TLS_VARIANT_1
-	return (uint8_t *)__tcb_get() + sizeof(tcb_t);
+	return (uint8_t *)__tcb_get() + ALIGN_UP(sizeof(tcb_t), tls->p_align);
 #else /* CONFIG_TLS_VARIANT_2 */
-	return (uint8_t *)__tcb_get() - tls_get_size();
+	return (uint8_t *)__tcb_get() - ALIGN_UP(tls->p_memsz, tls->p_align);
 #endif
 }
@@ -89,6 +93,15 @@
 
 	uintptr_t bias = elf_get_bias(__executable_start);
-
-	tcb = tls_alloc_arch(&data, tls->p_memsz);
+	size_t align = max(tls->p_align, _Alignof(tcb_t));
+
+#ifdef CONFIG_TLS_VARIANT_1
+	tcb = tls_alloc_arch(
+	    ALIGN_UP(sizeof(tcb_t), align) + tls->p_memsz, align);
+	data = (void *) tcb + ALIGN_UP(sizeof(tcb_t), align);
+#else
+	tcb = tls_alloc_arch(
+	    ALIGN_UP(tls->p_memsz, align) + sizeof(tcb_t), align);
+	data = (void *) tcb - ALIGN_UP(tls->p_memsz, tls->p_align);
+#endif
 
 	/*
@@ -108,6 +121,17 @@
 #ifdef CONFIG_RTLD
 	free(tcb->dtv);
-#endif
-	tls_free_arch(tcb, tls_get_size());
+
+	if (runtime_env != NULL) {
+		tls_free_arch(tcb, runtime_env->tls_size, runtime_env->tls_align);
+		return;
+	}
+#endif
+	const elf_segment_header_t *tls =
+	    elf_get_phdr(__executable_start, PT_TLS);
+
+	assert(tls != NULL);
+	tls_free_arch(tcb,
+	    ALIGN_UP(tls->p_memsz, tls->p_align) + sizeof(tcb_t),
+	    max(tls->p_align, _Alignof(tcb_t)));
 }
 
@@ -119,17 +143,10 @@
  * @return 		Pointer to tcb_t structure.
  */
-tcb_t *tls_alloc_variant_1(void **data, size_t size)
-{
-	tcb_t *tcb;
-
-	tcb = malloc(sizeof(tcb_t) + size);
+tcb_t *tls_alloc_variant_1(size_t size, size_t align)
+{
+	tcb_t *tcb = memalign(align, size);
 	if (!tcb)
 		return NULL;
-
-	*data = ((void *) tcb) + sizeof(tcb_t);
-#ifdef CONFIG_RTLD
-	tcb->dtv = NULL;
-#endif
-
+	memset(tcb, 0, sizeof(tcb_t));
 	return tcb;
 }
@@ -140,5 +157,5 @@
  * @param size		This argument is ignored.
  */
-void tls_free_variant_1(tcb_t *tcb, size_t size)
+void tls_free_variant_1(tcb_t *tcb, size_t size, size_t align)
 {
 	free(tcb);
@@ -152,22 +169,16 @@
  * 			actually an output argument.
  * @param size		Size of thread local data.
+ * @param align		Alignment of thread local data.
  * @return		Pointer to TCB structure.
  */
-tcb_t *tls_alloc_variant_2(void **data, size_t size)
-{
-	tcb_t *tcb;
-
-	uintptr_t align = elf_get_phdr(__executable_start, PT_TLS)->p_align;
-
-	size = ALIGN_UP(size, align);
-	*data = memalign(align, sizeof(tcb_t) + size);
-	if (*data == NULL)
-		return NULL;
-	tcb = (tcb_t *) (*data + size);
+tcb_t *tls_alloc_variant_2(size_t size, size_t align)
+{
+	void *data = memalign(align, size);
+	if (data == NULL)
+		return NULL;
+
+	tcb_t *tcb = (tcb_t *) (data + size - sizeof(tcb_t));
+	memset(tcb, 0, sizeof(tcb_t));
 	tcb->self = tcb;
-#ifdef CONFIG_RTLD
-	tcb->dtv = NULL;
-#endif
-
 	return tcb;
 }
@@ -177,11 +188,12 @@
  * @param tcb		Pointer to TCB structure.
  * @param size		Size of thread local data.
- */
-void tls_free_variant_2(tcb_t *tcb, size_t size)
-{
-	uintptr_t align = elf_get_phdr(__executable_start, PT_TLS)->p_align;
-	size = ALIGN_UP(size, align);
-	void *start = ((void *) tcb) - size;
-	free(start);
+ * @param align		Alignment of thread local data.
+ */
+void tls_free_variant_2(tcb_t *tcb, size_t size, size_t align)
+{
+	if (tcb != NULL) {
+		void *start = ((void *) tcb) + sizeof(tcb_t) - size;
+		free(start);
+	}
 }
 #endif
Index: uspace/lib/c/include/tls.h
===================================================================
--- uspace/lib/c/include/tls.h	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/include/tls.h	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -44,18 +44,17 @@
 
 extern tcb_t *tls_make(void);
-extern tcb_t *tls_alloc_arch(void **, size_t);
+extern tcb_t *tls_alloc_arch(size_t, size_t);
 extern void tls_free(tcb_t *);
-extern void tls_free_arch(tcb_t *, size_t);
-extern size_t tls_get_size(void);
+extern void tls_free_arch(tcb_t *, size_t, size_t);
 extern void *tls_get(void);
 
 #ifdef CONFIG_TLS_VARIANT_1
-extern tcb_t *tls_alloc_variant_1(void **, size_t);
-extern void tls_free_variant_1(tcb_t *, size_t);
+extern tcb_t *tls_alloc_variant_1(size_t, size_t);
+extern void tls_free_variant_1(tcb_t *, size_t, size_t);
 #endif
 
 #ifdef CONFIG_TLS_VARIANT_2
-extern tcb_t *tls_alloc_variant_2(void **, size_t);
-extern void tls_free_variant_2(tcb_t *, size_t);
+extern tcb_t *tls_alloc_variant_2(size_t, size_t);
+extern void tls_free_variant_2(tcb_t *, size_t, size_t);
 #endif
 
Index: uspace/lib/c/include/types/rtld/module.h
===================================================================
--- uspace/lib/c/include/types/rtld/module.h	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/include/types/rtld/module.h	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -62,5 +62,6 @@
 	size_t tls_align;
 
-	size_t ioffs;
+	/** Offset of this module's TLS from the thread pointer. */
+	ptrdiff_t tpoff;
 
 	/** Containing rtld */
Index: uspace/lib/c/include/types/rtld/rtld.h
===================================================================
--- uspace/lib/c/include/types/rtld/rtld.h	(revision 8d58fcaa7e5744b458fcb7645e5a20ddd6eb8092)
+++ uspace/lib/c/include/types/rtld/rtld.h	(revision 4f205248dfb2dd7692ef1b857648ba9d96fe496c)
@@ -54,4 +54,5 @@
 	/** Size of initial TLS tdata + tbss */
 	size_t tls_size;
+	size_t tls_align;
 
 	/** List of all loaded modules including rtld and the program */
