From 1a78fbb5c8228e4aea2a516818828b76044310f2 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Thu, 22 Mar 2012 18:01:53 +0400 Subject: [PATCH] Initialize TLS before any application code is run. Since e19d702b8e33, dlsym and friends use recursive mutexes that require the current thread id, which is not available before the libc constructor. This prevents us from using dlsym() in .preinit_array. This change moves TLS initialization from libc constructor to the earliest possible point - immediately after linker itself is relocated. As a result, pthread_internal_t for the initial thread is available from the start. As a bonus, values stored in TLS in .preinit_array are not lost when libc is initialized. Change-Id: Iee5a710ee000173bff63e924adeb4a4c600c1e2d --- libc/bionic/libc_init_common.c | 31 +++++++++++++++++++++++++------ libc/bionic/libc_init_static.c | 5 +++++ libc/bionic/pthread.c | 9 +++++---- libc/bionic/pthread_internal.h | 2 ++ libc/private/bionic_tls.h | 3 +++ linker/linker.c | 25 ++++++++----------------- 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c index b6a637923..7fb1246a7 100644 --- a/libc/bionic/libc_init_common.c +++ b/libc/bionic/libc_init_common.c @@ -52,12 +52,19 @@ unsigned int __page_shift = PAGE_SHIFT; int __system_properties_init(void); -void __libc_init_common(uintptr_t *elfdata) +/* Init TLS for the initial thread. Called by the linker _before_ libc is mapped + * in memory. Beware: all writes to libc globals from this function will + * apply to linker-private copies and will not be visible from libc later on. + * + * Note: this function creates a pthread_internal_t for the initial thread and + * stores the pointer in TLS, but does not add it to pthread's gThreadList. This + * has to be done later from libc itself (see __libc_init_common). + * + * This function also stores elfdata argument in a specific TLS slot to be later + * picked up by the libc constructor. + */ +void __libc_init_tls(unsigned** elfdata) { - int argc = *elfdata; - char** argv = (char**)(elfdata + 1); - char** envp = argv + argc + 1; - pthread_attr_t thread_attr; static pthread_internal_t thread; static void* tls_area[BIONIC_TLS_SLOTS]; @@ -72,7 +79,19 @@ void __libc_init_common(uintptr_t *elfdata) _init_thread(&thread, gettid(), &thread_attr, (void*)stackbottom); __init_tls(tls_area, &thread); - /* clear errno - requires TLS area */ + tls_area[TLS_SLOT_BIONIC_PREINIT] = elfdata; +} + +void __libc_init_common(uintptr_t *elfdata) +{ + int argc = *elfdata; + char** argv = (char**)(elfdata + 1); + char** envp = argv + argc + 1; + + /* get the initial thread from TLS and add it to gThreadList */ + _pthread_internal_add(__get_thread()); + + /* clear errno */ errno = 0; /* set program name */ diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index a2c11a9d0..f97961d23 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -65,6 +65,11 @@ __noreturn void __libc_init(uintptr_t *elfdata, int argc; char **argv, **envp; + __libc_init_tls(NULL); + + /* get the initial thread from TLS and add it to gThreadList */ + _pthread_internal_add(__get_thread()); + /* Initialize the C runtime environment */ __libc_init_common(elfdata); diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index fdfe50856..5cad1674b 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -145,7 +145,7 @@ _pthread_internal_remove( pthread_internal_t* thread ) pthread_mutex_unlock(&gThreadListLock); } -static void +__LIBC_ABI_PRIVATE__ void _pthread_internal_add( pthread_internal_t* thread ) { pthread_mutex_lock(&gThreadListLock); @@ -157,7 +157,7 @@ _pthread_internal_add( pthread_internal_t* thread ) pthread_mutex_unlock(&gThreadListLock); } -pthread_internal_t* +__LIBC_ABI_PRIVATE__ pthread_internal_t* __get_thread(void) { void** tls = (void**)__get_tls(); @@ -217,6 +217,7 @@ void __thread_entry(int (*func)(void*), void *arg, void **tls) pthread_exit( (void*)func(arg) ); } +__LIBC_ABI_PRIVATE__ void _init_thread(pthread_internal_t * thread, pid_t kernel_id, pthread_attr_t * attr, void * stack_base) { if (attr == NULL) { @@ -238,8 +239,6 @@ void _init_thread(pthread_internal_t * thread, pid_t kernel_id, pthread_attr_t * thread->join_count = 0; thread->cleanup_stack = NULL; - - _pthread_internal_add(thread); } @@ -371,6 +370,8 @@ int pthread_create(pthread_t *thread_out, pthread_attr_t const * attr, _init_thread(thread, tid, (pthread_attr_t*)attr, stack); + _pthread_internal_add(thread); + if (!madestack) thread->attr.flags |= PTHREAD_ATTR_FLAG_USER_STACK; diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h index 655b8f341..268cacf6c 100644 --- a/libc/bionic/pthread_internal.h +++ b/libc/bionic/pthread_internal.h @@ -47,6 +47,8 @@ typedef struct pthread_internal_t } pthread_internal_t; extern void _init_thread(pthread_internal_t * thread, pid_t kernel_id, pthread_attr_t * attr, void * stack_base); +void _pthread_internal_add( pthread_internal_t* thread ); +pthread_internal_t* __get_thread(void); /* needed by posix-timers.c */ diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h index 008fd2ff7..af19554ec 100644 --- a/libc/private/bionic_tls.h +++ b/libc/private/bionic_tls.h @@ -134,6 +134,9 @@ extern void* __get_tls( void ); /* return the stack base and size, used by our malloc debugger */ extern void* __get_stack_base(int *p_stack_size); +/* Initialize the TLS. */ +extern void __libc_init_tls(unsigned** elfdata); + __END_DECLS #endif /* _SYS_TLS_H */ diff --git a/linker/linker.c b/linker/linker.c index 9805b35e3..6b6282dc8 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -2054,10 +2054,6 @@ static void parse_preloads(const char *path, char *delim) } } -#define ANDROID_TLS_SLOTS BIONIC_TLS_SLOTS - -static void * __tls_area[ANDROID_TLS_SLOTS]; - /* * This code is called after the linker has linked itself and * fixed it's own GOT. It is safe to make references to externs @@ -2076,18 +2072,6 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata) const char *ldpath_env = NULL; const char *ldpreload_env = NULL; - /* Setup a temporary TLS area that is used to get a working - * errno for system calls. - */ - __set_tls(__tls_area); - - pid = getpid(); - -#if TIMING - struct timeval t0, t1; - gettimeofday(&t0, 0); -#endif - /* NOTE: we store the elfdata pointer on a special location * of the temporary TLS area in order to pass it to * the C Library's runtime initializer. @@ -2096,7 +2080,14 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata) * to point to a different location to ensure that no other * shared library constructor can access it. */ - __tls_area[TLS_SLOT_BIONIC_PREINIT] = elfdata; + __libc_init_tls(elfdata); + + pid = getpid(); + +#if TIMING + struct timeval t0, t1; + gettimeofday(&t0, 0); +#endif /* Initialize environment functions, and get to the ELF aux vectors table */ vecs = linker_env_init(vecs);