diff options
| -rw-r--r-- | src/env/__init_tls.c | 55 | ||||
| -rw-r--r-- | src/env/__stack_chk_fail.c | 8 | ||||
| -rw-r--r-- | src/errno/__errno_location.c | 2 | ||||
| -rw-r--r-- | src/internal/libc.h | 4 | ||||
| -rw-r--r-- | src/ldso/dynlink.c | 28 | ||||
| -rw-r--r-- | src/process/fork.c | 5 | ||||
| -rw-r--r-- | src/signal/sigaction.c | 19 | ||||
| -rw-r--r-- | src/stdio/__stdio_read.c | 10 | ||||
| -rw-r--r-- | src/stdio/__stdio_write.c | 10 | ||||
| -rw-r--r-- | src/thread/cancel_impl.c | 3 | ||||
| -rw-r--r-- | src/thread/pthread_create.c | 11 | ||||
| -rw-r--r-- | src/thread/pthread_key_create.c | 8 | ||||
| -rw-r--r-- | src/thread/pthread_self.c | 41 | ||||
| -rw-r--r-- | src/thread/pthread_setcancelstate.c | 12 | 
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;  }  | 
