about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/env/__init_tls.c55
-rw-r--r--src/env/__stack_chk_fail.c8
-rw-r--r--src/errno/__errno_location.c2
-rw-r--r--src/internal/libc.h4
-rw-r--r--src/ldso/dynlink.c28
-rw-r--r--src/process/fork.c5
-rw-r--r--src/signal/sigaction.c19
-rw-r--r--src/stdio/__stdio_read.c10
-rw-r--r--src/stdio/__stdio_write.c10
-rw-r--r--src/thread/cancel_impl.c3
-rw-r--r--src/thread/pthread_create.c11
-rw-r--r--src/thread/pthread_key_create.c8
-rw-r--r--src/thread/pthread_self.c41
-rw-r--r--src/thread/pthread_setcancelstate.c12
14 files changed, 118 insertions, 98 deletions
diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c
index dbfe62e7..8ac0036b 100644
--- a/src/env/__init_tls.c
+++ b/src/env/__init_tls.c
@@ -7,8 +7,28 @@
 #include "atomic.h"
 #include "syscall.h"
 
+int __init_tp(void *p)
+{
+	pthread_t td = p;
+	td->self = td;
+	if (__set_thread_area(TP_ADJ(p)) < 0)
+		return -1;
+	td->tid = td->pid = __syscall(SYS_set_tid_address, &td->tid);
+	td->errno_ptr = &td->errno_val;
+	/* Currently, both of these predicates depend in the same thing:
+	 * successful initialization of the thread pointer. However, in
+	 * the future, we may support setups where setting the thread
+	 * pointer is possible but threads other than the main thread
+	 * cannot work, so it's best to keep the predicates separate. */
+	libc.has_thread_pointer = 1;
+	libc.can_do_threads = 1;
+	return 0;
+}
+
 #ifndef SHARED
 
+static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];
+
 struct tls_image {
 	void *image;
 	size_t len, size, align;
@@ -62,10 +82,11 @@ typedef Elf64_Phdr Phdr;
 
 void __init_tls(size_t *aux)
 {
-	unsigned char *p, *mem;
+	unsigned char *p;
 	size_t n;
 	Phdr *phdr, *tls_phdr=0;
 	size_t base = 0;
+	void *mem;
 
 	libc.tls_size = sizeof(struct pthread);
 
@@ -76,28 +97,38 @@ void __init_tls(size_t *aux)
 		if (phdr->p_type == PT_TLS)
 			tls_phdr = phdr;
 	}
-	if (!tls_phdr) return;
 
-	T.image = (void *)(base + tls_phdr->p_vaddr);
-	T.len = tls_phdr->p_filesz;
-	T.size = tls_phdr->p_memsz;
-	T.align = tls_phdr->p_align;
+	if (tls_phdr) {
+		T.image = (void *)(base + tls_phdr->p_vaddr);
+		T.len = tls_phdr->p_filesz;
+		T.size = tls_phdr->p_memsz;
+		T.align = tls_phdr->p_align;
+	}
 
 	T.size += (-T.size - (uintptr_t)T.image) & (T.align-1);
 	if (T.align < 4*sizeof(size_t)) T.align = 4*sizeof(size_t);
 
 	libc.tls_size = 2*sizeof(void *)+T.size+T.align+sizeof(struct pthread);
 
-	mem = (void *)__syscall(
+	if (libc.tls_size > sizeof builtin_tls) {
+		mem = (void *)__syscall(
 #ifdef SYS_mmap2
-		SYS_mmap2,
+			SYS_mmap2,
 #else
-		SYS_mmap,
+			SYS_mmap,
 #endif
-		0, libc.tls_size, PROT_READ|PROT_WRITE,
-		MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+			0, libc.tls_size, PROT_READ|PROT_WRITE,
+			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+		/* -4095...-1 cast to void * will crash on dereference anyway,
+		 * so don't bloat the init code checking for error codes and
+		 * explicitly calling a_crash(). */
+	} else {
+		mem = builtin_tls;
+	}
 
-	if (!__install_initial_tls(__copy_tls(mem))) a_crash();
+	/* Failure to initialize thread pointer is fatal if TLS is used. */
+	if (__init_tp(__copy_tls(mem)) < 0 && tls_phdr)
+		a_crash();
 }
 #else
 void __init_tls(size_t *auxv) { }
diff --git a/src/env/__stack_chk_fail.c b/src/env/__stack_chk_fail.c
index daa1b078..00634d38 100644
--- a/src/env/__stack_chk_fail.c
+++ b/src/env/__stack_chk_fail.c
@@ -7,7 +7,13 @@ uintptr_t __stack_chk_guard;
 
 void __init_ssp(void *entropy)
 {
-	pthread_t self = __pthread_self_init();
+	/* Here the thread pointer is used without checking whether
+	 * it is available; this will crash if it's not. However,
+	 * this function is only meant to be called if the program
+	 * being run uses stack protector, and in that case, it would
+	 * crash without a thread pointer anyway, so it's better to
+	 * crash early before there is state to be lost on crash. */
+	pthread_t self = __pthread_self();
 	uintptr_t canary;
 	if (entropy) memcpy(&canary, entropy, sizeof canary);
 	else canary = (uintptr_t)&canary * 1103515245;
diff --git a/src/errno/__errno_location.c b/src/errno/__errno_location.c
index 3e92d7c7..84191076 100644
--- a/src/errno/__errno_location.c
+++ b/src/errno/__errno_location.c
@@ -3,6 +3,6 @@
 int *__errno_location(void)
 {
 	static int e;
-	if (libc.main_thread) return __pthread_self()->errno_ptr;
+	if (libc.has_thread_pointer) return __pthread_self()->errno_ptr;
 	return &e;
 }
diff --git a/src/internal/libc.h b/src/internal/libc.h
index d625b56a..fb4d9bc0 100644
--- a/src/internal/libc.h
+++ b/src/internal/libc.h
@@ -6,12 +6,12 @@
 #include <limits.h>
 
 struct __libc {
-	void *main_thread;
+	int has_thread_pointer;
+	int can_do_threads;
 	int threaded;
 	int secure;
 	size_t *auxv;
 	volatile int threads_minus_1;
-	int canceldisable;
 	FILE *ofl_head;
 	int ofl_lock[2];
 	size_t tls_size;
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index a1bdf0fb..616dc3e1 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -90,7 +90,7 @@ struct symdef {
 #include "reloc.h"
 
 void __init_ssp(size_t *);
-void *__install_initial_tls(void *);
+int __init_tp(void *);
 void __init_libc(char **, char *);
 
 const char *__libc_get_version(void);
@@ -108,6 +108,7 @@ static pthread_rwlock_t lock;
 static struct debug debug;
 static size_t tls_cnt, tls_offset, tls_align = 4*sizeof(size_t);
 static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
+static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];
 
 struct debug *_dl_debug_addr = &debug;
 
@@ -673,9 +674,10 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
 	/* Add a shortname only if name arg was not an explicit pathname. */
 	if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
 	if (p->tls_image) {
-		if (runtime && !__pthread_self_init()) {
+		if (runtime && !libc.has_thread_pointer) {
 			munmap(map, p->map_len);
 			free(p);
+			errno = ENOSYS;
 			return 0;
 		}
 		p->tls_id = ++tls_cnt;
@@ -866,10 +868,13 @@ void *__copy_tls(unsigned char *mem)
 	pthread_t td;
 	struct dso *p;
 
-	if (!tls_cnt) return mem;
-
 	void **dtv = (void *)mem;
 	dtv[0] = (void *)tls_cnt;
+	if (!tls_cnt) {
+		td = (void *)(dtv+1);
+		td->dtv = dtv;
+		return td;
+	}
 
 #ifdef TLS_ABOVE_TP
 	mem += sizeof(void *) * (tls_cnt+1);
@@ -962,6 +967,7 @@ void *__dynlink(int argc, char **argv)
 	size_t vdso_base;
 	size_t *auxv;
 	char **envp = argv+argc+1;
+	void *initial_tls;
 
 	/* Find aux vector just past environ[] */
 	for (i=argc+1; argv[i]; i++)
@@ -1144,15 +1150,19 @@ void *__dynlink(int argc, char **argv)
 	reloc_all(app);
 
 	update_tls_size();
-	if (tls_cnt) {
-		void *mem = mmap(0, libc.tls_size, PROT_READ|PROT_WRITE,
-			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
-		if (mem==MAP_FAILED ||
-		    !__install_initial_tls(__copy_tls(mem))) {
+	if (libc.tls_size > sizeof builtin_tls) {
+		initial_tls = calloc(libc.tls_size, 1);
+		if (!initial_tls) {
 			dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n",
 				argv[0], libc.tls_size);
 			_exit(127);
 		}
+	} else {
+		initial_tls = builtin_tls;
+	}
+	if (__init_tp(__copy_tls(initial_tls)) < 0 && tls_cnt) {
+		dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]);
+		_exit(127);
 	}
 
 	if (ldso_fail) _exit(127);
diff --git a/src/process/fork.c b/src/process/fork.c
index 1a82f428..864c7d7a 100644
--- a/src/process/fork.c
+++ b/src/process/fork.c
@@ -17,12 +17,11 @@ pid_t fork(void)
 	__fork_handler(-1);
 	__block_all_sigs(&set);
 	ret = syscall(SYS_fork);
-	if (libc.main_thread && !ret) {
+	if (libc.has_thread_pointer && !ret) {
 		pthread_t self = __pthread_self();
-		self->tid = self->pid = syscall(SYS_getpid);
+		self->tid = self->pid = __syscall(SYS_getpid);
 		memset(&self->robust_list, 0, sizeof self->robust_list);
 		libc.threads_minus_1 = 0;
-		libc.main_thread = self;
 	}
 	__restore_sigs(&set);
 	__fork_handler(!ret);
diff --git a/src/signal/sigaction.c b/src/signal/sigaction.c
index f7ff4a61..d5f47741 100644
--- a/src/signal/sigaction.c
+++ b/src/signal/sigaction.c
@@ -8,9 +8,7 @@
 
 void __restore(), __restore_rt();
 
-static pthread_t dummy(void) { return 0; }
-weak_alias(dummy, __pthread_self_def);
-
+static int unmask_done;
 static unsigned long handler_set[_NSIG/(8*sizeof(long))];
 
 void __get_handler_set(sigset_t *set)
@@ -29,7 +27,20 @@ int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigact
 		if ((uintptr_t)sa->sa_handler > 1UL) {
 			a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
 				1UL<<(sig-1)%(8*sizeof(long)));
-			__pthread_self_def();
+
+			/* If pthread_create has not yet been called,
+			 * implementation-internal signals might not
+			 * yet have been unblocked. They must be
+			 * unblocked before any signal handler is
+			 * installed, so that an application cannot
+			 * receive an illegal sigset_t (with them
+			 * blocked) as part of the ucontext_t passed
+			 * to the signal handler. */
+			if (!libc.threaded && !unmask_done) {
+				__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
+					SIGPT_SET, 0, _NSIG/8);
+				unmask_done = 1;
+			}
 		}
 		ksa.handler = sa->sa_handler;
 		ksa.flags = sa->sa_flags | SA_RESTORER;
diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c
index 05e56f92..6cd7b073 100644
--- a/src/stdio/__stdio_read.c
+++ b/src/stdio/__stdio_read.c
@@ -16,13 +16,9 @@ size_t __stdio_read(FILE *f, unsigned char *buf, size_t len)
 	};
 	ssize_t cnt;
 
-	if (libc.main_thread) {
-		pthread_cleanup_push(cleanup, f);
-		cnt = syscall_cp(SYS_readv, f->fd, iov, 2);
-		pthread_cleanup_pop(0);
-	} else {
-		cnt = syscall(SYS_readv, f->fd, iov, 2);
-	}
+	pthread_cleanup_push(cleanup, f);
+	cnt = syscall_cp(SYS_readv, f->fd, iov, 2);
+	pthread_cleanup_pop(0);
 	if (cnt <= 0) {
 		f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt);
 		f->rpos = f->rend = 0;
diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c
index e52e91ae..8c89389a 100644
--- a/src/stdio/__stdio_write.c
+++ b/src/stdio/__stdio_write.c
@@ -19,13 +19,9 @@ size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
 	int iovcnt = 2;
 	ssize_t cnt;
 	for (;;) {
-		if (libc.main_thread) {
-			pthread_cleanup_push(cleanup, f);
-			cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
-			pthread_cleanup_pop(0);
-		} else {
-			cnt = syscall(SYS_writev, f->fd, iov, iovcnt);
-		}
+		pthread_cleanup_push(cleanup, f);
+		cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
+		pthread_cleanup_pop(0);
 		if (cnt == rem) {
 			f->wend = f->buf + f->buf_size;
 			f->wpos = f->wbase = f->buf;
diff --git a/src/thread/cancel_impl.c b/src/thread/cancel_impl.c
index c835813a..525d2904 100644
--- a/src/thread/cancel_impl.c
+++ b/src/thread/cancel_impl.c
@@ -20,7 +20,7 @@ long (__syscall_cp)(syscall_arg_t nr,
 	pthread_t self;
 	long r;
 
-	if (!libc.main_thread || (self = __pthread_self())->canceldisable)
+	if (!libc.has_thread_pointer || (self = __pthread_self())->canceldisable)
 		return __syscall(nr, u, v, w, x, y, z);
 
 	r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
@@ -57,6 +57,7 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)
 
 void __testcancel()
 {
+	if (!libc.has_thread_pointer) return;
 	pthread_t self = pthread_self();
 	if (self->cancel && !self->canceldisable)
 		__cancel();
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index ee6c31c4..08c74110 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -77,6 +77,7 @@ _Noreturn void pthread_exit(void *result)
 
 void __do_cleanup_push(struct __ptcb *cb)
 {
+	if (!libc.has_thread_pointer) return;
 	struct pthread *self = pthread_self();
 	cb->__next = self->cancelbuf;
 	self->cancelbuf = cb;
@@ -84,6 +85,7 @@ void __do_cleanup_push(struct __ptcb *cb)
 
 void __do_cleanup_pop(struct __ptcb *cb)
 {
+	if (!libc.has_thread_pointer) return;
 	__pthread_self()->cancelbuf = cb->__next;
 }
 
@@ -110,6 +112,8 @@ static int start(void *p)
 /* pthread_key_create.c overrides this */
 static const size_t dummy = 0;
 weak_alias(dummy, __pthread_tsd_size);
+static const void *dummy_tsd[1] = { 0 };
+weak_alias(dummy_tsd, __pthread_tsd_main);
 
 static FILE *const dummy_file = 0;
 weak_alias(dummy_file, __stdin_used);
@@ -127,7 +131,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
 {
 	int ret;
 	size_t size, guard;
-	struct pthread *self = pthread_self(), *new;
+	struct pthread *self, *new;
 	unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
 	unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
 		| CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS
@@ -135,13 +139,16 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
 	int do_sched = 0;
 	pthread_attr_t attr = {0};
 
-	if (!self) return ENOSYS;
+	if (!libc.can_do_threads) return ENOSYS;
+	self = __pthread_self();
 	if (!libc.threaded) {
 		for (FILE *f=libc.ofl_head; f; f=f->next)
 			init_file_lock(f);
 		init_file_lock(__stdin_used);
 		init_file_lock(__stdout_used);
 		init_file_lock(__stderr_used);
+		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
+		self->tsd = __pthread_tsd_main;
 		libc.threaded = 1;
 	}
 	if (attrp) attr = *attrp;
diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c
index c29935c1..ef8a755d 100644
--- a/src/thread/pthread_key_create.c
+++ b/src/thread/pthread_key_create.c
@@ -14,7 +14,13 @@ int pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
 	unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX;
 	unsigned j = i;
 
-	__pthread_self_init();
+	if (libc.has_thread_pointer) {
+		pthread_t self = __pthread_self();
+		/* This can only happen in the main thread before
+		 * pthread_create has been called. */
+		if (!self->tsd) self->tsd = __pthread_tsd_main;
+	}
+
 	if (!dtor) dtor = nodtor;
 	do {
 		if (!a_cas_p(keys+j, 0, (void *)dtor)) {
diff --git a/src/thread/pthread_self.c b/src/thread/pthread_self.c
index aed4b5f1..5f9e6516 100644
--- a/src/thread/pthread_self.c
+++ b/src/thread/pthread_self.c
@@ -1,45 +1,6 @@
 #include "pthread_impl.h"
 
-static struct pthread *main_thread = &(struct pthread){0};
-
-/* pthread_key_create.c overrides this */
-static const void *dummy[1] = { 0 };
-weak_alias(dummy, __pthread_tsd_main);
-
-static int init_main_thread()
-{
-	__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
-		SIGPT_SET, 0, _NSIG/8);
-	if (__set_thread_area(TP_ADJ(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);
-	if (!main_thread->dtv)
-		main_thread->dtv = (void *)dummy;
-	libc.main_thread = main_thread;
-	return 0;
-}
-
-pthread_t __pthread_self_def()
+pthread_t pthread_self()
 {
-	static int init, failed;
-	if (!init) {
-		if (failed) return 0;
-		if (init_main_thread() < 0) failed = 1;
-		if (failed) return 0;
-		init = 1;
-	}
 	return __pthread_self();
 }
-
-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();
-}
diff --git a/src/thread/pthread_setcancelstate.c b/src/thread/pthread_setcancelstate.c
index ba2b2311..060bcdcc 100644
--- a/src/thread/pthread_setcancelstate.c
+++ b/src/thread/pthread_setcancelstate.c
@@ -3,13 +3,9 @@
 int pthread_setcancelstate(int new, int *old)
 {
 	if (new > 1U) return EINVAL;
-	if (libc.main_thread) {
-		struct pthread *self = __pthread_self();
-		if (old) *old = self->canceldisable;
-		self->canceldisable = new;
-	} else {
-		if (old) *old = libc.canceldisable;
-		libc.canceldisable = new;
-	}
+	if (!libc.has_thread_pointer) return ENOSYS;
+	struct pthread *self = __pthread_self();
+	if (old) *old = self->canceldisable;
+	self->canceldisable = new;
 	return 0;
 }