about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile8
-rw-r--r--elf/dl-close.c73
-rw-r--r--elf/tst-tls7.c61
-rw-r--r--elf/tst-tlsmod1.c3
-rw-r--r--elf/tst-tlsmod2.c3
-rw-r--r--elf/tst-tlsmod3.c34
6 files changed, 161 insertions, 21 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 1a0338a600..b22f3afae3 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -119,7 +119,7 @@ tests = loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
 	$(tests-nodlopen-$(have-z-nodlopen)) neededtest neededtest2 \
 	neededtest3 neededtest4 unload2 lateglobal initfirst global \
 	restest2 next dblload dblunload reldep5 reldep6 tst-tls1 tst-tls2 \
-	tst-tls3 tst-tls4 tst-tls5 tst-tls6
+	tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7
 test-srcs = tst-pathopt
 tests-vis-yes = vismain
 tests-nodelete-yes = nodelete
@@ -137,7 +137,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		unload2mod unload2dep ltglobmod1 ltglobmod2 pathoptobj \
 		dblloadmod1 dblloadmod2 dblloadmod3 reldepmod5 reldepmod6 \
 	        reldep6mod0 reldep6mod1 reldep6mod2 reldep6mod3 reldep6mod4 \
-		tst-tlsmod1 tst-tlsmod2
+		tst-tlsmod1 tst-tlsmod2 tst-tlsmod3
 modules-vis-yes = vismod1 vismod2 vismod3
 modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4
 modules-nodlopen-yes = nodlopenmod nodlopenmod2
@@ -292,6 +292,7 @@ $(objpfx)reldep6mod1.so: $(objpfx)reldep6mod0.so
 $(objpfx)reldep6mod2.so: $(objpfx)reldep6mod1.so
 $(objpfx)reldep6mod3.so: $(objpfx)reldep6mod2.so
 $(objpfx)reldep6mod4.so: $(objpfx)reldep6mod1.so
+$(objpfx)tst-tlsmod3.so: $(objpfx)tst-tlsmod2.so
 
 # filtmod1.so has a special rule
 $(filter-out $(objpfx)filtmod1.so, $(test-modules)): $(objpfx)%.so: $(objpfx)%.os
@@ -451,3 +452,6 @@ $(objpfx)tst-tls5.out: $(objpfx)tst-tlsmod2.so
 
 $(objpfx)tst-tls6: $(libdl)
 $(objpfx)tst-tls6.out: $(objpfx)tst-tlsmod2.so
+
+$(objpfx)tst-tls7: $(libdl)
+$(objpfx)tst-tls7.out: $(objpfx)tst-tlsmod3.so
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 7e7547de99..a5941268b9 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -32,6 +32,58 @@
 typedef void (*fini_t) (void);
 
 
+#ifdef USE_TLS
+/* Returns true we an non-empty was found.  */
+static bool
+remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp)
+{
+  if (idx - disp >= listp->len)
+    {
+      /* There must be a next entry.  Otherwise would the index be wrong.  */
+      assert (listp->next != NULL);
+
+      if (remove_slotinfo (idx, listp->next, disp + listp->len))
+	return true;
+
+      /* No non-empty entry.  Search from the end of this elements
+	 slotinfo array.  */
+      idx = disp + listp->len;
+    }
+  else
+    {
+      struct link_map *old_map = listp->slotinfo[idx - disp].map;
+      assert (old_map != NULL);
+
+      /* Mark the entry as unused.  */
+      listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1;
+      listp->slotinfo[idx - disp].map = NULL;
+
+      /* If this is not the last currently used entry no need to look
+	 further.  */
+      if (old_map->l_tls_modid != GL(dl_tls_max_dtv_idx))
+	return true;
+
+      assert (old_map->l_tls_modid == GL(dl_tls_max_dtv_idx));
+    }
+
+  while (idx - disp > disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0)
+    {
+      --idx;
+
+      if (listp->slotinfo[idx - disp].map != NULL)
+	{
+	  /* Found a new last used index.  */
+	  GL(dl_tls_max_dtv_idx) = idx;
+	  return true;
+	}
+    }
+
+  /* No non-entry in this list element.  */
+  return false;
+}
+#endif
+
+
 void
 internal_function
 _dl_close (void *_map)
@@ -214,25 +266,12 @@ _dl_close (void *_map)
 	     TLS.  */
 	  if (__builtin_expect (imap->l_tls_blocksize > 0, 0))
 	    {
-	      /* Locate the entry in the slotinfo array.  */
-	      size_t idx = imap->l_tls_modid;
-	      struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
-
-	      while (idx >= listp->len)
-		{
-		  idx -= listp->len;
-		  listp = listp->next;
-		}
-
-	      listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
-	      listp->slotinfo[idx].map = NULL;
-
 	      any_tls = true;
 
-	      if (imap->l_tls_modid == GL(dl_tls_max_dtv_idx))
-		--GL(dl_tls_max_dtv_idx);
-	      else
-		GL(dl_tls_dtv_gaps) = true;
+	      if (! remove_slotinfo (imap->l_tls_modid,
+				     GL(dl_tls_dtv_slotinfo_list), 0))
+		/* All dynamically loaded modules with TLS are unloaded.  */
+		GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem);
 	    }
 #endif
 
diff --git a/elf/tst-tls7.c b/elf/tst-tls7.c
new file mode 100644
index 0000000000..4f785fe78e
--- /dev/null
+++ b/elf/tst-tls7.c
@@ -0,0 +1,61 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <link.h>
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+  static const char modname[] = "tst-tlsmod3.so";
+  int result = 0;
+  int (*fp) (void);
+  void *h;
+  int i;
+  int modid = -1;
+
+  for (i = 0; i < 10; ++i)
+    {
+      h = dlopen (modname, RTLD_LAZY);
+      if (h == NULL)
+	{
+	  printf ("cannot open '%s': %s\n", modname, dlerror ());
+	  exit (1);
+	}
+
+      /* Dirty test code here: we peek into a private data structure.
+	 We make sure that the module gets assigned the same ID every
+	 time.  The value of the first round is used.  */
+      if (modid == -1)
+	modid = ((struct link_map *) h)->l_tls_modid;
+      else if (((struct link_map *) h)->l_tls_modid != modid)
+	{
+	  printf ("round %d: modid now %d, initially %d\n",
+		  i, ((struct link_map *) h)->l_tls_modid, modid);
+	  result = 1;
+	}
+
+      fp = dlsym (h, "in_dso2");
+      if (fp == NULL)
+	{
+	  printf ("cannot get symbol 'in_dso2': %s\n", dlerror ());
+	  exit (1);
+	}
+
+      result |= fp ();
+
+      dlclose (h);
+    }
+
+  return result;
+#else
+  return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/elf/tst-tlsmod1.c b/elf/tst-tlsmod1.c
index cca8df6549..7e768a6593 100644
--- a/elf/tst-tlsmod1.c
+++ b/elf/tst-tlsmod1.c
@@ -1,10 +1,11 @@
 #include <stdio.h>
 
 #include <tls.h>
+
+#ifdef USE_TLS
 #include "tls-macros.h"
 
 
-#ifdef USE_TLS
 /* One define int variable, two externs.  */
 COMMON_INT_DEF(foo);
 VAR_INT_DEF(bar);
diff --git a/elf/tst-tlsmod2.c b/elf/tst-tlsmod2.c
index 30ed67163d..6aec8120c0 100644
--- a/elf/tst-tlsmod2.c
+++ b/elf/tst-tlsmod2.c
@@ -1,9 +1,10 @@
 #include <stdio.h>
 
 #include <tls.h>
-#include "tls-macros.h"
 
 #ifdef USE_TLS
+#include "tls-macros.h"
+
 
 COMMON_INT_DEF(foo);
 
diff --git a/elf/tst-tlsmod3.c b/elf/tst-tlsmod3.c
new file mode 100644
index 0000000000..ff4a4460b9
--- /dev/null
+++ b/elf/tst-tlsmod3.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+
+#include <tls.h>
+#include "tls-macros.h"
+
+#ifdef USE_TLS
+extern int in_dso (int n, int *caller_foop);
+
+COMMON_INT_DEF(comm_n);
+
+
+
+
+int
+in_dso2 (void)
+{
+  int *foop = TLS_GD (foo);
+  int result = 0;
+  static int n;
+  int *np = TLS_GD (comm_n);
+
+  if (n != *np)
+    {
+      printf ("n = %d != comm_n = %d\n", n, *np);
+      result = 1;
+    }
+
+  result |= in_dso (*foop = 42 + n++, foop);
+
+  *foop = 16;
+
+  return result;
+}
+#endif