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; } |