* include/link.h (FORCED_DYNAMIC_TLS_OFFSET): Define.
authorUlrich Drepper <drepper@redhat.com>
Thu, 17 Jan 2008 20:20:00 +0000 (20:20 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 17 Jan 2008 20:20:00 +0000 (20:20 +0000)
* 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
elf/Makefile
elf/dl-close.c
elf/dl-reloc.c
elf/dl-tls.c
elf/tst-tls16.c [new file with mode: 0644]
elf/tst-tlsmod16a.c [new file with mode: 0644]
elf/tst-tlsmod16b.c [new file with mode: 0644]
include/link.h

index e773070..0c4a50d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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]
index e5812e3..4230b55 100644 (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)
index 264e13a..46f1a40 100644 (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
index c315b5d..e9784c2 100644 (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"
index d5865ab..3059481 100644 (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;
     }
diff --git a/elf/tst-tls16.c b/elf/tst-tls16.c
new file mode 100644 (file)
index 0000000..b351985
--- /dev/null
@@ -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"
diff --git a/elf/tst-tlsmod16a.c b/elf/tst-tlsmod16a.c
new file mode 100644 (file)
index 0000000..847c809
--- /dev/null
@@ -0,0 +1,7 @@
+#include <tls.h>
+
+#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 (file)
index 0000000..308e6ba
--- /dev/null
@@ -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;
+}
index 16980ef..4b9978a 100644 (file)
@@ -279,6 +279,15 @@ struct link_map
 #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;
     /* Index of the module in the dtv array.  */