From 4c533566c2bb94162bcc1f66c5cf9db609e20803 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 17 Jan 2008 20:20:00 +0000 Subject: [PATCH] * include/link.h (FORCED_DYNAMIC_TLS_OFFSET): Define. * elf/dl-close.c (_dl_close): Check for it. * elf/dl-reloc.c (CHECK_STATIC_TLS): Likewise. (_dl_allocate_static_tls): Likewise. * elf/dl-tls.c (_dl_allocate_tls_init): Likewise. (__tls_get_addr): Protect from race conditions in setting l_tls_offset to it. * elf/tst-tls16.c: New file. * elf/tst-tlsmod16a.c: New file. * elf/tst-tlsmod16b.c: New file. * elf/Makefile: Add rules to build and run tst-tls16. --- ChangeLog | 14 ++++++++++++ elf/Makefile | 8 +++++-- elf/dl-close.c | 3 ++- elf/dl-reloc.c | 16 ++++++++------ elf/dl-tls.c | 26 ++++++++++++++++++++++- elf/tst-tls16.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ elf/tst-tlsmod16a.c | 7 ++++++ elf/tst-tlsmod16b.c | 13 ++++++++++++ include/link.h | 9 ++++++++ 9 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 elf/tst-tls16.c create mode 100644 elf/tst-tlsmod16a.c create mode 100644 elf/tst-tlsmod16b.c diff --git a/ChangeLog b/ChangeLog index e773070f4d..0c4a50dde0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-10-23 Alexandre Oliva + + * include/link.h (FORCED_DYNAMIC_TLS_OFFSET): Define. + * elf/dl-close.c (_dl_close): Check for it. + * elf/dl-reloc.c (CHECK_STATIC_TLS): Likewise. + (_dl_allocate_static_tls): Likewise. + * elf/dl-tls.c (_dl_allocate_tls_init): Likewise. + (__tls_get_addr): Protect from race conditions in setting l_tls_offset + to it. + * elf/tst-tls16.c: New file. + * elf/tst-tlsmod16a.c: New file. + * elf/tst-tlsmod16b.c: New file. + * elf/Makefile: Add rules to build and run tst-tls16. + 2008-01-16 Ulrich Drepper [BZ #5628] diff --git a/elf/Makefile b/elf/Makefile index e5812e3668..4230b55df6 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -165,7 +165,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \ - tst-tls-dlinfo \ + tst-tls16 tst-tls-dlinfo \ tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \ tst-dlmodcount tst-dlopenrpath tst-deep1 \ tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \ @@ -199,7 +199,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \ tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \ tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \ - tst-tlsmod15a tst-tlsmod15b \ + tst-tlsmod15a tst-tlsmod15b tst-tlsmod16a tst-tlsmod16b \ circlemod1 circlemod1a circlemod2 circlemod2a \ circlemod3 circlemod3a \ reldep8mod1 reldep8mod2 reldep8mod3 \ @@ -492,6 +492,7 @@ tst-tlsmod12.so-no-z-defs = yes tst-tlsmod14a.so-no-z-defs = yes tst-tlsmod14b.so-no-z-defs = yes tst-tlsmod15a.so-no-z-defs = yes +tst-tlsmod16b.so-no-z-defs = yes circlemod2.so-no-z-defs = yes circlemod3.so-no-z-defs = yes circlemod3a.so-no-z-defs = yes @@ -711,6 +712,9 @@ $(objpfx)tst-tls-dlinfo.out: $(objpfx)tst-tlsmod2.so +$(objpfx)tst-tls16: $(libdl) +$(objpfx)tst-tls16.out: $(objpfx)tst-tlsmod16a.so $(objpfx)tst-tlsmod16b.so + CFLAGS-tst-align.c = $(stack-align-test-flags) CFLAGS-tst-align2.c = $(stack-align-test-flags) CFLAGS-tst-alignmod.c = $(stack-align-test-flags) diff --git a/elf/dl-close.c b/elf/dl-close.c index 264e13a8ee..46f1a40adc 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -531,7 +531,8 @@ _dl_close_worker (struct link_map *map) /* All dynamically loaded modules with TLS are unloaded. */ GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); - if (imap->l_tls_offset != NO_TLS_OFFSET) + if (imap->l_tls_offset != NO_TLS_OFFSET + && imap->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET) { /* Collect a contiguous chunk built from the objects in this search list, going in either direction. When the diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index c315b5d972..e9784c2094 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -47,8 +47,10 @@ void internal_function __attribute_noinline__ _dl_allocate_static_tls (struct link_map *map) { - /* If the alignment requirements are too high fail. */ - if (map->l_tls_align > GL(dl_tls_static_align)) + /* If we've already used the variable with dynamic access, or if the + alignment requirements are too high, fail. */ + if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET + || map->l_tls_align > GL(dl_tls_static_align)) { fail: _dl_signal_error (0, map->l_name, NULL, N_("\ @@ -255,10 +257,12 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], an attempt to allocate it in surplus space on the fly. If that can't be done, we fall back to the error that DF_STATIC_TLS is intended to produce. */ -#define CHECK_STATIC_TLS(map, sym_map) \ - do { \ - if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ - _dl_allocate_static_tls (sym_map); \ +#define CHECK_STATIC_TLS(map, sym_map) \ + do { \ + if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET \ + || ((sym_map)->l_tls_offset \ + == FORCED_DYNAMIC_TLS_OFFSET), 0)) \ + _dl_allocate_static_tls (sym_map); \ } while (0) #include "dynamic-link.h" diff --git a/elf/dl-tls.c b/elf/dl-tls.c index d5865ab409..3059481043 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -413,7 +413,8 @@ _dl_allocate_tls_init (void *result) not be the generation counter. */ maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); - if (map->l_tls_offset == NO_TLS_OFFSET) + if (map->l_tls_offset == NO_TLS_OFFSET + || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET) { /* For dynamically loaded modules we simply store the value indicating deferred allocation. */ @@ -702,6 +703,7 @@ __tls_get_addr (GET_ADDR_ARGS) if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0)) the_map = _dl_update_slotinfo (GET_ADDR_MODULE); + retry: p = dtv[GET_ADDR_MODULE].pointer.val; if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0)) @@ -722,6 +724,28 @@ __tls_get_addr (GET_ADDR_ARGS) 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 (__builtin_expect (the_map->l_tls_offset + != FORCED_DYNAMIC_TLS_OFFSET, 0)) + { + __rtld_lock_lock_recursive (GL(dl_load_lock)); + if (__builtin_expect (the_map->l_tls_offset == NO_TLS_OFFSET, 1)) + { + the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET; + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + } + else + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + if (__builtin_expect (the_map->l_tls_offset + != FORCED_DYNAMIC_TLS_OFFSET, 1)) + goto retry; + } + } p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map); dtv[GET_ADDR_MODULE].pointer.is_static = false; } diff --git a/elf/tst-tls16.c b/elf/tst-tls16.c new file mode 100644 index 0000000000..b3519858a0 --- /dev/null +++ b/elf/tst-tls16.c @@ -0,0 +1,52 @@ +#include +#include + +static int +do_test (void) +{ + void *h = dlopen ("tst-tlsmod16a.so", RTLD_LAZY | RTLD_GLOBAL); + if (h == NULL) + { + puts ("unexpectedly failed to open tst-tlsmod16a.so"); + exit (1); + } + + void *p = dlsym (h, "tlsvar"); + + /* This dlopen should indeed fail, because tlsvar was assigned to + dynamic TLS, and the new module requests it to be in static TLS. + However, there's a possibility that dlopen succeeds if the + variable is, for whatever reason, assigned to static TLS, or if + the module fails to require static TLS, or even if TLS is not + supported. */ + h = dlopen ("tst-tlsmod16b.so", RTLD_NOW | RTLD_GLOBAL); + if (h == NULL) + { + return 0; + } + + puts ("unexpectedly succeeded to open tst-tlsmod16b.so"); + + + void *(*fp) (void) = (void *(*) (void)) dlsym (h, "in_dso"); + if (fp == NULL) + { + puts ("cannot find in_dso"); + exit (1); + } + + /* If the dlopen passes, at least make sure the address returned by + dlsym is the same as that returned by the initial-exec access. + If the variable was assigned to dynamic TLS during dlsym, this + portion will fail. */ + if (fp () != p) + { + puts ("returned values do not match"); + exit (1); + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-tlsmod16a.c b/elf/tst-tlsmod16a.c new file mode 100644 index 0000000000..847c8090f5 --- /dev/null +++ b/elf/tst-tlsmod16a.c @@ -0,0 +1,7 @@ +#include + +#if defined HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE +int __thread tlsvar; +#else +int tlsvar; +#endif diff --git a/elf/tst-tlsmod16b.c b/elf/tst-tlsmod16b.c new file mode 100644 index 0000000000..308e6bae92 --- /dev/null +++ b/elf/tst-tlsmod16b.c @@ -0,0 +1,13 @@ +#include + +#if defined HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE +extern __thread int tlsvar __attribute__((tls_model("initial-exec"))); +#else +extern int tlsvar; +#endif + +void * +in_dso (void) +{ + return &tlsvar; +} diff --git a/include/link.h b/include/link.h index 16980ef664..4b9978ad61 100644 --- a/include/link.h +++ b/include/link.h @@ -278,6 +278,15 @@ struct link_map size_t l_tls_firstbyte_offset; #ifndef NO_TLS_OFFSET # define NO_TLS_OFFSET 0 +#endif +#ifndef FORCED_DYNAMIC_TLS_OFFSET +# if NO_TLS_OFFSET == 0 +# define FORCED_DYNAMIC_TLS_OFFSET 1 +# elif NO_TLS_OFFSET == -1 +# define FORCED_DYNAMIC_TLS_OFFSET -2 +# else +# error "FORCED_DYNAMIC_TLS_OFFSET is not defined" +# endif #endif /* For objects present at startup time: offset in the static TLS block. */ ptrdiff_t l_tls_offset;