git diff v1.0.0...2b74315d8a31ad8fbcd369116c82e055e0ec3fb7 diff --git a/src/conf/confstr.c b/src/conf/confstr.c index 4332f72..6e9c23a 100644 --- a/src/conf/confstr.c +++ b/src/conf/confstr.c @@ -13,5 +13,5 @@ size_t confstr(int name, char *buf, size_t len) } // snprintf is overkill but avoid wasting code size to implement // this completely useless function and its truncation semantics - return snprintf(buf, len, "%s", s); + return snprintf(buf, len, "%s", s) + 1; } diff --git a/src/env/__init_security.c b/src/env/__init_security.c index 6204c5e..da5ae94 100644 --- a/src/env/__init_security.c +++ b/src/env/__init_security.c @@ -15,9 +15,7 @@ void __init_security(size_t *aux) struct pollfd pfd[3] = { {.fd=0}, {.fd=1}, {.fd=2} }; int i; -#ifndef SHARED __init_ssp((void *)aux[AT_RANDOM]); -#endif if (aux[AT_UID]==aux[AT_EUID] && aux[AT_GID]==aux[AT_EGID] && !aux[AT_SECURE]) return; diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c index 5c17681..8ac0036 100644 --- a/src/env/__init_tls.c +++ b/src/env/__init_tls.c @@ -5,9 +5,30 @@ #include "pthread_impl.h" #include "libc.h" #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; @@ -61,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); @@ -75,21 +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 = __mmap(0, libc.tls_size, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - if (!__install_initial_tls(__copy_tls(mem))) a_crash(); + if (libc.tls_size > sizeof builtin_tls) { + mem = (void *)__syscall( +#ifdef SYS_mmap2 + SYS_mmap2, +#else + SYS_mmap, +#endif + 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; + } + + /* 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 daa1b07..87ac473 100644 --- a/src/env/__stack_chk_fail.c +++ b/src/env/__stack_chk_fail.c @@ -1,18 +1,16 @@ #include #include #include "pthread_impl.h" -#include "atomic.h" uintptr_t __stack_chk_guard; void __init_ssp(void *entropy) { - pthread_t self = __pthread_self_init(); - uintptr_t canary; - if (entropy) memcpy(&canary, entropy, sizeof canary); - else canary = (uintptr_t)&canary * 1103515245; - a_cas_l(&__stack_chk_guard, 0, canary); - self->canary = __stack_chk_guard; + if (entropy) memcpy(&__stack_chk_guard, entropy, sizeof(uintptr_t)); + else __stack_chk_guard = (uintptr_t)&__stack_chk_guard * 1103515245; + + if (libc.has_thread_pointer) + __pthread_self()->canary = __stack_chk_guard; } void __stack_chk_fail(void) diff --git a/src/errno/__errno_location.c b/src/errno/__errno_location.c index 3e92d7c..8419107 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 d625b56..fb4d9bc 100644 --- a/src/internal/libc.h +++ b/src/internal/libc.h @@ -6,12 +6,12 @@ #include 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/internal/syscall_ret.c b/src/internal/syscall_ret.c index d99f4a5..a3f4713 100644 --- a/src/internal/syscall_ret.c +++ b/src/internal/syscall_ret.c @@ -1,4 +1,5 @@ #include +#include "syscall.h" long __syscall_ret(unsigned long r) { diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index a1bdf0f..1517281 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -56,6 +56,7 @@ struct dso { Phdr *phdr; int phnum; + size_t phentsize; int refcnt; Sym *syms; uint32_t *hashtab; @@ -74,6 +75,7 @@ struct dso { char *rpath_orig, *rpath; void *tls_image; size_t tls_len, tls_size, tls_align, tls_id, tls_offset; + size_t relro_start, relro_end; void **new_dtv; unsigned char *new_tls; int new_dtv_idx, new_tls_idx; @@ -89,8 +91,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); @@ -98,7 +99,6 @@ const char *__libc_get_version(void); static struct dso *head, *tail, *ldso, *fini_head; static char *env_path, *sys_path; static unsigned long long gencnt; -static int ssp_used; static int runtime; static int ldd_mode; static int ldso_fail; @@ -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; @@ -198,13 +199,6 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def) { uint32_t h = 0, gh = 0; struct symdef def = {0}; - if (dso->ghashtab) { - gh = gnu_hash(s); - if (gh == 0x1f4039c9 && !strcmp(s, "__stack_chk_fail")) ssp_used = 1; - } else { - h = sysv_hash(s); - if (h == 0x595a4cc && !strcmp(s, "__stack_chk_fail")) ssp_used = 1; - } for (; dso; dso=dso->next) { Sym *sym; if (!dso->global) continue; @@ -280,27 +274,32 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri * and "donate" them to the heap by setting up minimal malloc * structures and then freeing them. */ -static void reclaim(unsigned char *base, size_t start, size_t end) +static void reclaim(struct dso *dso, size_t start, size_t end) { size_t *a, *z; + if (start >= dso->relro_start && start < dso->relro_end) start = dso->relro_end; + if (end >= dso->relro_start && end < dso->relro_end) end = dso->relro_start; start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t); end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t); if (start>end || end-start < 4*sizeof(size_t)) return; - a = (size_t *)(base + start); - z = (size_t *)(base + end); + a = (size_t *)(dso->base + start); + z = (size_t *)(dso->base + end); a[-2] = 1; a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1; z[1] = 1; free(a); } -static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phcnt) +static void reclaim_gaps(struct dso *dso) { - for (; phcnt--; ph=(void *)((char *)ph+phent)) { + Phdr *ph = dso->phdr; + size_t phcnt = dso->phnum; + + for (; phcnt--; ph=(void *)((char *)ph+dso->phentsize)) { if (ph->p_type!=PT_LOAD) continue; if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue; - reclaim(base, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr); - reclaim(base, ph->p_vaddr+ph->p_memsz, + reclaim(dso, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr); + reclaim(dso, ph->p_vaddr+ph->p_memsz, ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE); } } @@ -343,13 +342,16 @@ static void *map_library(int fd, struct dso *dso) ph = ph0 = (void *)((char *)buf + eh->e_phoff); } for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { - if (ph->p_type == PT_DYNAMIC) + if (ph->p_type == PT_DYNAMIC) { dyn = ph->p_vaddr; - if (ph->p_type == PT_TLS) { + } else if (ph->p_type == PT_TLS) { tls_image = ph->p_vaddr; dso->tls_align = ph->p_align; dso->tls_len = ph->p_filesz; dso->tls_size = ph->p_memsz; + } else if (ph->p_type == PT_GNU_RELRO) { + dso->relro_start = ph->p_vaddr & -PAGE_SIZE; + dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE; } if (ph->p_type != PT_LOAD) continue; if (ph->p_vaddr < addr_min) { @@ -393,6 +395,7 @@ static void *map_library(int fd, struct dso *dso) dso->phdr = (void *)(base + ph->p_vaddr + (eh->e_phoff-ph->p_offset)); dso->phnum = eh->e_phnum; + dso->phentsize = eh->e_phentsize; } /* Reuse the existing mapping for the lowest-address LOAD */ if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue; @@ -418,12 +421,12 @@ static void *map_library(int fd, struct dso *dso) goto error; break; } - if (!runtime) reclaim_gaps(base, ph0, eh->e_phentsize, eh->e_phnum); dso->map = map; dso->map_len = map_len; dso->base = base; dso->dynv = (void *)(base+dyn); if (dso->tls_size) dso->tls_image = (void *)(base+tls_image); + if (!runtime) reclaim_gaps(dso); free(allocated_buf); return map; noexec: @@ -673,9 +676,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; @@ -764,22 +768,32 @@ static void reloc_all(struct dso *p) 2+(dyn[DT_PLTREL]==DT_RELA)); do_relocs(p, (void *)(p->base+dyn[DT_REL]), dyn[DT_RELSZ], 2); do_relocs(p, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], 3); + + if (p->relro_start != p->relro_end && + mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) { + snprintf(errbuf, sizeof errbuf, + "Error relocating %s: RELRO protection failed: %m", + p->name); + if (runtime) longjmp(*rtld_fail, 1); + dprintf(2, "%s\n", errbuf); + ldso_fail = 1; + } + p->relocated = 1; } } -static size_t find_dyn(Phdr *ph, size_t cnt, size_t stride) +static void kernel_mapped_dso(struct dso *p) { - for (; cnt--; ph = (void *)((char *)ph + stride)) - if (ph->p_type == PT_DYNAMIC) - return ph->p_vaddr; - return 0; -} - -static void find_map_range(Phdr *ph, size_t cnt, size_t stride, struct dso *p) -{ - size_t min_addr = -1, max_addr = 0; - for (; cnt--; ph = (void *)((char *)ph + stride)) { + size_t min_addr = -1, max_addr = 0, cnt; + Phdr *ph = p->phdr; + for (cnt = p->phnum; cnt--; ph = (void *)((char *)ph + p->phentsize)) { + if (ph->p_type == PT_DYNAMIC) { + p->dynv = (void *)(p->base + ph->p_vaddr); + } else if (ph->p_type == PT_GNU_RELRO) { + p->relro_start = ph->p_vaddr & -PAGE_SIZE; + p->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE; + } if (ph->p_type != PT_LOAD) continue; if (ph->p_vaddr < min_addr) min_addr = ph->p_vaddr; @@ -790,6 +804,7 @@ static void find_map_range(Phdr *ph, size_t cnt, size_t stride, struct dso *p) max_addr = (max_addr + PAGE_SIZE-1) & -PAGE_SIZE; p->map = p->base + min_addr; p->map_len = max_addr - min_addr; + p->kernel_mapped = 1; } static void do_fini() @@ -866,10 +881,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 +980,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++) @@ -996,13 +1015,11 @@ void *__dynlink(int argc, char **argv) lib->base = (void *)aux[AT_BASE]; lib->name = lib->shortname = "libc.so"; lib->global = 1; - lib->kernel_mapped = 1; ehdr = (void *)lib->base; lib->phnum = ehdr->e_phnum; lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff); - find_map_range(lib->phdr, ehdr->e_phnum, ehdr->e_phentsize, lib); - lib->dynv = (void *)(lib->base + find_dyn(lib->phdr, - ehdr->e_phnum, ehdr->e_phentsize)); + lib->phentsize = ehdr->e_phentsize; + kernel_mapped_dso(lib); decode_dyn(lib); if (aux[AT_PHDR]) { @@ -1011,6 +1028,7 @@ void *__dynlink(int argc, char **argv) /* Find load address of the main program, via AT_PHDR vs PT_PHDR. */ app->phdr = phdr = (void *)aux[AT_PHDR]; app->phnum = aux[AT_PHNUM]; + app->phentsize = aux[AT_PHENT]; for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) { if (phdr->p_type == PT_PHDR) app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr); @@ -1030,11 +1048,7 @@ void *__dynlink(int argc, char **argv) app->name = (char *)aux[AT_EXECFN]; else app->name = argv[0]; - app->kernel_mapped = 1; - app->dynv = (void *)(app->base + find_dyn( - (void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT])); - find_map_range((void *)aux[AT_PHDR], - aux[AT_PHNUM], aux[AT_PHENT], app); + kernel_mapped_dso(app); } else { int fd; char *ldname = argv[0]; @@ -1100,6 +1114,7 @@ void *__dynlink(int argc, char **argv) ehdr = (void *)vdso_base; vdso->phdr = phdr = (void *)(vdso_base + ehdr->e_phoff); vdso->phnum = ehdr->e_phnum; + vdso->phentsize = ehdr->e_phentsize; for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) { if (phdr->p_type == PT_DYNAMIC) vdso->dynv = (void *)(vdso_base + phdr->p_offset); @@ -1127,10 +1142,8 @@ void *__dynlink(int argc, char **argv) /* PAST THIS POINT, ALL LIBC INTERFACES ARE FULLY USABLE. */ /* Donate unused parts of app and library mapping to malloc */ - reclaim_gaps(app->base, (void *)aux[AT_PHDR], aux[AT_PHENT], aux[AT_PHNUM]); - ehdr = (void *)lib->base; - reclaim_gaps(lib->base, (void *)(lib->base+ehdr->e_phoff), - ehdr->e_phentsize, ehdr->e_phnum); + reclaim_gaps(app); + reclaim_gaps(lib); /* Load preload/needed libraries, add their symbols to the global * namespace, and perform all remaining relocations. The main @@ -1140,19 +1153,29 @@ void *__dynlink(int argc, char **argv) load_deps(app); make_global(app); +#ifndef DYNAMIC_IS_RO + for (i=0; app->dynv[i]; i+=2) + if (app->dynv[i]==DT_DEBUG) + app->dynv[i+1] = (size_t)&debug; +#endif + reloc_all(app->next); 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); @@ -1164,11 +1187,6 @@ void *__dynlink(int argc, char **argv) * all memory used by the dynamic linker. */ runtime = 1; -#ifndef DYNAMIC_IS_RO - for (i=0; app->dynv[i]; i+=2) - if (app->dynv[i]==DT_DEBUG) - app->dynv[i+1] = (size_t)&debug; -#endif debug.ver = 1; debug.bp = _dl_debug_state; debug.head = head; @@ -1176,7 +1194,6 @@ void *__dynlink(int argc, char **argv) debug.state = 0; _dl_debug_state(); - if (ssp_used) __init_ssp((void *)aux[AT_RANDOM]); __init_libc(envp, argv[0]); atexit(do_fini); errno = 0; @@ -1258,9 +1275,6 @@ void *dlopen(const char *file, int mode) } update_tls_size(); - - if (ssp_used) __init_ssp(libc.auxv); - _dl_debug_state(); orig_tail = tail; end: diff --git a/src/process/fork.c b/src/process/fork.c index 1a82f42..864c7d7 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 f7ff4a6..d5f4774 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 05e56f9..6cd7b07 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 e52e91a..8c89389 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 c835813..525d290 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 ee6c31c..e0b5ef1 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 void *const 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 = (void **)__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 c29935c..ef8a755 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 aed4b5f..5f9e651 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 ba2b231..060bcdc 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; }