[PR dynamic-link/18457] * elf/dl-tls.c (tls_get_addr_tail): Don't take the rtld lock if we already have a final static TLS offset. * nptl/tst-join7.c, nptl/tst-join7mod.c: New. Index: glibc-2.21/elf/dl-tls.c =================================================================== --- glibc-2.21.orig/elf/dl-tls.c +++ glibc-2.21/elf/dl-tls.c @@ -755,30 +755,44 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t the_map = listp->slotinfo[idx].map; } - /* Make sure that, if a dlopen running in parallel forces the - variable into static storage, we'll wait until the address in the - static TLS block is set up, and use that. If we're undecided - yet, make sure we make the decision holding the lock as well. */ - if (__glibc_unlikely (the_map->l_tls_offset - != FORCED_DYNAMIC_TLS_OFFSET)) + /* If the TLS block for the map is already assigned to dynamic or to + static TLS, avoid the lock. Be careful to use the same value for + both tests; if we reloaded it, the second test might mistake + forced dynamic for an offset. Now, if the decision hasn't been + made, take the rtld lock, so that an ongoing dlopen gets a chance + to complete, and then retest; if the decision is still pending, + force the module to dynamic TLS. */ + ptrdiff_t offset = atomic_load_relaxed (&the_map->l_tls_offset); + if (__glibc_unlikely (offset != FORCED_DYNAMIC_TLS_OFFSET)) { + if (__glibc_unlikely (offset != NO_TLS_OFFSET)) + goto static_tls; __rtld_lock_lock_recursive (GL(dl_load_lock)); - if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET)) + offset = the_map->l_tls_offset; + if (__glibc_likely (offset == NO_TLS_OFFSET)) { the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET; __rtld_lock_unlock_recursive (GL(dl_load_lock)); } - else if (__glibc_likely (the_map->l_tls_offset - != FORCED_DYNAMIC_TLS_OFFSET)) + else if (__glibc_likely (offset != FORCED_DYNAMIC_TLS_OFFSET)) { + /* The decision is made, and it is final. We use the value + we've already loaded, but we could even load the offset + after releasing the lock, since it won't change. Should + the module be released while another thread references + one of its TLS variables, that's undefined behavior. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + static_tls: + ; + #if TLS_TCB_AT_TP - void *p = (char *) THREAD_SELF - the_map->l_tls_offset; + void *p = (char *) THREAD_SELF - offset; #elif TLS_DTV_AT_TP - void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE; + void *p = (char *) THREAD_SELF + offset + TLS_PRE_TCB_SIZE; #else # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" #endif - __rtld_lock_unlock_recursive (GL(dl_load_lock)); dtv[GET_ADDR_MODULE].pointer.is_static = true; dtv[GET_ADDR_MODULE].pointer.val = p; Index: glibc-2.21/nptl/Makefile =================================================================== --- glibc-2.21.orig/nptl/Makefile +++ glibc-2.21/nptl/Makefile @@ -234,7 +234,7 @@ tests = tst-typesizes \ tst-basic7 \ tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \ tst-raise1 \ - tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 \ + tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 tst-join7 \ tst-detach1 \ tst-eintr1 tst-eintr2 tst-eintr3 tst-eintr4 tst-eintr5 \ tst-tsd1 tst-tsd2 tst-tsd3 tst-tsd4 tst-tsd5 tst-tsd6 \ @@ -312,7 +312,8 @@ endif modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \ tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \ tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \ - tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod + tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \ + tst-join7mod extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) tst-cleanup4aux.o test-extras += $(modules-names) tst-cleanup4aux test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) @@ -517,6 +518,11 @@ $(objpfx)tst-tls6.out: tst-tls6.sh $(obj $(evaluate-test) endif +$(objpfx)tst-join7: $(libdl) $(shared-thread-library) +$(objpfx)tst-join7.out: $(objpfx)tst-join7mod.so +$(objpfx)tst-join7mod.so: $(shared-thread-library) +LDFLAGS-tst-join7mod.so = -Wl,-soname,tst-join7mod.so + $(objpfx)tst-dlsym1: $(libdl) $(shared-thread-library) $(objpfx)tst-fini1: $(shared-thread-library) $(objpfx)tst-fini1mod.so Index: glibc-2.21/nptl/tst-join7.c =================================================================== --- /dev/null +++ glibc-2.21/nptl/tst-join7.c @@ -0,0 +1,12 @@ +#include + +int +do_test (void) +{ + void *f = dlopen ("tst-join7mod.so", RTLD_NOW | RTLD_GLOBAL); + if (f) dlclose (f); else return 1; + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" Index: glibc-2.21/nptl/tst-join7mod.c =================================================================== --- /dev/null +++ glibc-2.21/nptl/tst-join7mod.c @@ -0,0 +1,29 @@ +#include +#include + +static pthread_t th; +static int running = 1; + +static void * +test_run (void *p) +{ + while (running) + fprintf (stderr, "XXX test_run\n"); + fprintf (stderr, "XXX test_run FINISHED\n"); + return NULL; +} + +static void __attribute__ ((constructor)) +do_init (void) +{ + pthread_create (&th, NULL, test_run, NULL); +} + +static void __attribute__ ((destructor)) +do_end (void) +{ + running = 0; + fprintf (stderr, "thread_join...\n"); + pthread_join (th, NULL); + fprintf (stderr, "thread_join DONE\n"); +}