always initialize thread pointer at program start

this is the first step in an overhaul aimed at greatly simplifying and
optimizing everything dealing with thread-local state.

previously, the thread pointer was initialized lazily on first access,
or at program startup if stack protector was in use, or at certain
random places where inconsistent state could be reached if it were not
initialized early. while believed to be fully correct, the logic was
fragile and non-obvious.

in the first phase of the thread pointer overhaul, support is retained
(and in some cases improved) for systems/situation where loading the
thread pointer fails, e.g. old kernels.

some notes on specific changes:

- the confusing use of libc.main_thread as an indicator that the
  thread pointer is initialized is eliminated in favor of an explicit
  has_thread_pointer predicate.

- sigaction no longer needs to ensure that the thread pointer is
  initialized before installing a signal handler (this was needed to
  prevent a situation where the signal handler caused the thread
  pointer to be initialized and the subsequent sigreturn cleared it
  again) but it still needs to ensure that implementation-internal
  thread-related signals are not blocked.

- pthread tsd initialization for the main thread is deferred in a new
  manner to minimize bloat in the static-linked __init_tp code.

- pthread_setcancelstate no longer needs special handling for the
  situation before the thread pointer is initialized. it simply fails
  on systems that cannot support a thread pointer, which are
  non-conforming anyway.

- pthread_cleanup_push/pop now check for missing thread pointer and
  nop themselves out in this case, so stdio no longer needs to avoid
  the cancellable path when the thread pointer is not available.

a number of cases remain where certain interfaces may crash if the
system does not support a thread pointer. at this point, these should
be limited to pthread interfaces, and the number of such cases should
be fewer than before.
This commit is contained in:
Rich Felker 2014-03-24 16:57:11 -04:00
commit dab441aea2
14 changed files with 118 additions and 98 deletions

View file

@ -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();

View file

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

View file

@ -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)) {

View file

@ -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()
pthread_t pthread_self()
{
__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()
{
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();
}

View file

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