فهرست منبع

TLS (GNU/C11 thread-local storage) support for static-linked programs

the design for TLS in dynamic-linked programs is mostly complete too,
but I have not yet implemented it. cost is nonzero but still low for
programs which do not use TLS and/or do not use threads (a few hundred
bytes of new code, plus dependency on memcpy). i believe it can be
made smaller at some point by merging __init_tls and __init_security
into __libc_start_main and avoiding duplicate auxv-parsing code.

at the same time, I've also slightly changed the logic pthread_create
uses to allocate guard pages to ensure that guard pages are not
counted towards commit charge.
Rich Felker 12 سال پیش
والد
کامیت
8431d7972f
6فایلهای تغییر یافته به همراه117 افزوده شده و 14 حذف شده
  1. 71 0
      src/env/__init_tls.c
  2. 2 0
      src/env/__libc_start_main.c
  3. 1 0
      src/internal/libc.h
  4. 10 0
      src/ldso/dynlink.c
  5. 18 5
      src/thread/pthread_create.c
  6. 15 9
      src/thread/pthread_self.c

+ 71 - 0
src/env/__init_tls.c

@@ -0,0 +1,71 @@
+#include <elf.h>
+#include <limits.h>
+#include "pthread_impl.h"
+#include "libc.h"
+#include "atomic.h"
+
+#ifndef SHARED
+
+static void *image;
+static size_t len, bss, align;
+
+void *__copy_tls(unsigned char *mem, size_t cnt)
+{
+	mem += ((uintptr_t)image - (uintptr_t)mem) & (align-1);
+	memcpy(mem, image, len);
+	return mem + len + bss;
+}
+
+static void *simple(void *p)
+{
+	*(void **)p = p;
+	return __set_thread_area(p) ? 0 : p;
+}
+
+weak_alias(simple, __install_initial_tls);
+
+void *__mmap(void *, size_t, int, int, int, off_t);
+
+#if ULONG_MAX == 0xffffffff
+typedef Elf32_Phdr Phdr;
+#else
+typedef Elf64_Phdr Phdr;
+#endif
+
+#define AUX_CNT 6
+
+void __init_tls(size_t *auxv)
+{
+	size_t i, aux[AUX_CNT] = { 0 };
+	unsigned char *p, *mem;
+	size_t n, d;
+	Phdr *phdr, *tls_phdr=0;
+	size_t base = 0;
+
+	for (; auxv[0]; auxv+=2) if (auxv[0]<AUX_CNT) aux[auxv[0]] = auxv[1];
+	p = (void *)aux[AT_PHDR];
+	for (p=(void *)aux[AT_PHDR]; aux[AT_PHNUM]--; p+=aux[AT_PHENT]) {
+		phdr = (void *)p;
+		if (phdr->p_type == PT_PHDR)
+			base = aux[AT_PHDR] - phdr->p_vaddr;
+		if (phdr->p_type == PT_TLS)
+			tls_phdr = phdr;
+	}
+	if (!tls_phdr) return;
+
+	libc.tls_size = len+bss+align+4*sizeof(size_t)+sizeof(struct pthread);
+
+	image = (void *)(base + tls_phdr->p_vaddr);
+	len = tls_phdr->p_filesz;
+	bss = tls_phdr->p_memsz - len;
+	align = tls_phdr->p_align;
+	if (align < 4*sizeof(size_t)) align = 4*sizeof(size_t);
+	mem = __mmap(0, libc.tls_size, PROT_READ|PROT_WRITE,
+		MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+	if (mem == MAP_FAILED) a_crash();
+
+	if (!__install_initial_tls(__copy_tls(mem, 0))) a_crash();
+}
+#else
+void __init_tls(size_t *auxv) { }
+#endif

+ 2 - 0
src/env/__libc_start_main.c

@@ -1,5 +1,6 @@
 #include "libc.h"
 
+void __init_tls(size_t *);
 void __init_security(size_t *);
 
 int __libc_start_main(
@@ -15,6 +16,7 @@ int __libc_start_main(
 	libc.ldso_fini = ldso_fini;
 	libc.fini = fini;
 
+	__init_tls((void *)auxv);
 	__init_security((void *)auxv);
 
 	/* Execute constructors (static) linked into the application */

+ 1 - 0
src/internal/libc.h

@@ -16,6 +16,7 @@ struct __libc {
 	int canceldisable;
 	FILE *ofl_head;
 	int ofl_lock[2];
+	volatile size_t tls_size, tls_cnt;
 };
 
 extern size_t __hwcap;

+ 10 - 0
src/ldso/dynlink.c

@@ -17,6 +17,9 @@
 #include <pthread.h>
 #include <ctype.h>
 #include <dlfcn.h>
+#include "pthread_impl.h"
+#include "libc.h"
+#undef libc
 
 static int errflag;
 static char errbuf[128];
@@ -788,6 +791,8 @@ void *__dynlink(int argc, char **argv)
 	debug.state = 0;
 	_dl_debug_state();
 
+	/* Stand-in until real TLS support is added to dynamic linker */
+	__libc.tls_size = sizeof(struct pthread) + 4*sizeof(size_t);
 	if (ssp_used) __init_ssp(auxv);
 
 	do_init_fini(tail);
@@ -802,6 +807,11 @@ void *__dynlink(int argc, char **argv)
 	return (void *)aux[AT_ENTRY];
 }
 
+void *__copy_tls(unsigned char *mem, size_t cnt)
+{
+	return mem;
+}
+
 void *dlopen(const char *file, int mode)
 {
 	struct dso *volatile p, *orig_tail = tail, *next;

+ 18 - 5
src/thread/pthread_create.c

@@ -84,6 +84,8 @@ static void init_file_lock(FILE *f)
 	if (f && f->lock<0) f->lock = 0;
 }
 
+void *__copy_tls(unsigned char *, size_t);
+
 int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr, void *(*entry)(void *), void *restrict arg)
 {
 	int ret;
@@ -92,6 +94,8 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr,
 	struct pthread *self = pthread_self(), *new;
 	unsigned char *map, *stack, *tsd;
 	unsigned flags = 0x7d8f00;
+	size_t tls_cnt = libc.tls_cnt;
+	size_t tls_size = libc.tls_size;
 
 	if (!self) return ENOSYS;
 	if (!libc.threaded) {
@@ -109,15 +113,24 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attr,
 	} else {
 		if (attr) {
 			guard = ROUND(attr->_a_guardsize + DEFAULT_GUARD_SIZE);
-			size = guard + ROUND(attr->_a_stacksize + DEFAULT_STACK_SIZE);
+			size = guard + ROUND(attr->_a_stacksize
+				+ DEFAULT_STACK_SIZE + tls_size);
 		}
 		size += __pthread_tsd_size;
-		map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
-		if (map == MAP_FAILED) return EAGAIN;
-		if (guard) mprotect(map, guard, PROT_NONE);
+		if (guard) {
+			map = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+			if (map == MAP_FAILED) return EAGAIN;
+			if (mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
+				munmap(map, size);
+				return EAGAIN;
+			}
+		} else {
+			map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+			if (map == MAP_FAILED) return EAGAIN;
+		}
 		tsd = map + size - __pthread_tsd_size;
 	}
-	new = (void *)(tsd - sizeof *new - PAGE_SIZE%sizeof *new);
+	new = __copy_tls(tsd - tls_size, tls_cnt);
 	new->map_base = map;
 	new->map_size = size;
 	new->pid = self->pid;

+ 15 - 9
src/thread/pthread_self.c

@@ -1,6 +1,6 @@
 #include "pthread_impl.h"
 
-static struct pthread main_thread;
+static struct pthread *main_thread = &(struct pthread){0};
 
 /* pthread_key_create.c overrides this */
 static const void *dummy[1] = { 0 };
@@ -10,14 +10,14 @@ static int init_main_thread()
 {
 	__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
 		SIGPT_SET, 0, __SYSCALL_SSLEN);
-	if (__set_thread_area(&main_thread) < 0) return -1;
-	main_thread.canceldisable = libc.canceldisable;
-	main_thread.tsd = (void **)__pthread_tsd_main;
-	main_thread.errno_ptr = __errno_location();
-	main_thread.self = &main_thread;
-	main_thread.tid = main_thread.pid =
-		__syscall(SYS_set_tid_address, &main_thread.tid);
-	libc.main_thread = &main_thread;
+	if (__set_thread_area(main_thread) < 0) return -1;
+	main_thread->canceldisable = libc.canceldisable;
+	main_thread->tsd = (void **)__pthread_tsd_main;
+	main_thread->errno_ptr = __errno_location();
+	main_thread->self = main_thread;
+	main_thread->tid = main_thread->pid =
+		__syscall(SYS_set_tid_address, &main_thread->tid);
+	libc.main_thread = main_thread;
 	return 0;
 }
 
@@ -35,3 +35,9 @@ pthread_t __pthread_self_def()
 
 weak_alias(__pthread_self_def, pthread_self);
 weak_alias(__pthread_self_def, __pthread_self_init);
+
+void *__install_initial_tls(void *p)
+{
+	main_thread = p;
+	return __pthread_self_def();
+}