* 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.
This commit is contained in:
Ulrich Drepper 2008-01-17 20:20:00 +00:00
parent a0f6c236e0
commit 4c533566c2
9 changed files with 138 additions and 10 deletions

View file

@ -1,3 +1,17 @@
2007-10-23 Alexandre Oliva <aoliva@redhat.com>
* 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 <drepper@redhat.com>
[BZ #5628]

View file

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

View file

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

View file

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

View file

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

52
elf/tst-tls16.c Normal file
View file

@ -0,0 +1,52 @@
#include <dlfcn.h>
#include <stdio.h>
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"

7
elf/tst-tlsmod16a.c Normal file
View file

@ -0,0 +1,7 @@
#include <tls.h>
#if defined HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE
int __thread tlsvar;
#else
int tlsvar;
#endif

13
elf/tst-tlsmod16b.c Normal file
View file

@ -0,0 +1,13 @@
#include <tls.h>
#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;
}

View file

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