summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
-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
-rw-r--r--sysdeps/generic/dl-tls.c30
8 files changed, 195 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index cb746f6f80..42bf1914c6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2002-02-13  Ulrich Drepper  <drepper@redhat.com>
 
+	* elf/dl-close.c (remove_slotinfo): New function.  Handles everything
+	for removing reference of module in slotinfo list.
+	(_dl_close): Use remove_slotinfo.
+	* sysdeps/generic/dl-tls.c: General pretty printing.
+	(oom): Define only if SHARED.
+	(_dl_next_tls_modid): Correct starting point for the case we assume
+	there is a gap.  Add missing instruction grouping (doh!).  Correct
+	tests for reaching maximum index.
+	* elf/Makefile: Add rules to build and run tst-tls7.
+	* elf/tst-tls7.c: New file.
+	* elf/tst-tlsmod3.c: New file.
+
+	* elf/tst-tlsmod1.c: Move #include "tls-macros.h" instead #ifdef
+	USE_TLS.
+	* elf/tst-tlsmod2.c: Likewise.
+
 	* elf/dl-close.c (_dl_close): When closing an object using TLS
 	either decrement dl_tls_max_dtv_idx or set dl_tls_dtv_gaps to
 	true.  Increment dl_tls_generation only if we closed any TLS-using
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
diff --git a/sysdeps/generic/dl-tls.c b/sysdeps/generic/dl-tls.c
index d13b0f93fa..7e45850198 100644
--- a/sysdeps/generic/dl-tls.c
+++ b/sysdeps/generic/dl-tls.c
@@ -29,14 +29,15 @@
 /* We don't need any of this if TLS is not supported.  */
 #ifdef USE_TLS
 
-#include <dl-tls.h>
-#include <ldsodefs.h>
+# include <dl-tls.h>
+# include <ldsodefs.h>
 
 /* Value used for dtv entries for which the allocation is delayed.  */
 # define TLS_DTV_UNALLOCATED	((void *) -1l)
 
 
 /* Out-of-memory handler.  */
+# ifdef SHARED
 static void
 __attribute__ ((__noreturn__))
 oom (void)
@@ -52,11 +53,12 @@ cannot allocate memory for thread-local data: ABORT\n";
   /* Just in case something goes wrong with the kill.  */
   while (1)
     {
-# ifdef ABORT_INSTRUCTION
+#  ifdef ABORT_INSTRUCTION
       ABORT_INSTRUCTION;
-# endif
+#  endif
     }
 }
+# endif
 
 
 
@@ -75,16 +77,20 @@ _dl_next_tls_modid (void)
 	 start since there are no gaps at that time.  Therefore it
 	 does not matter that the dl_tls_dtv_slotinfo is not allocated
 	 yet when the function is called for the first times.  */
-      result = GL(dl_tls_static_nelem);
-      assert (result < GL(dl_tls_max_dtv_idx));
+      result = GL(dl_tls_static_nelem) + 1;
+      /* If the following would not be true we mustn't have assumed
+	 there is a gap.  */
+      assert (result <= GL(dl_tls_max_dtv_idx));
       do
 	{
 	  while (result - disp < runp->len)
-	    if (runp->slotinfo[result - disp].map == NULL)
-	      break;
+	    {
+	      if (runp->slotinfo[result - disp].map == NULL)
+		break;
 
-	  ++result;
-	  assert (result <= GL(dl_tls_max_dtv_idx) + 1);
+	      ++result;
+	      assert (result <= GL(dl_tls_max_dtv_idx) + 1);
+	    }
 
 	  if (result - disp < runp->len)
 	    break;
@@ -93,11 +99,11 @@ _dl_next_tls_modid (void)
 	}
       while ((runp = runp->next) != NULL);
 
-      if (result >= GL(dl_tls_max_dtv_idx) + 1)
+      if (result >= GL(dl_tls_max_dtv_idx))
 	{
 	  /* The new index must indeed be exactly one higher than the
 	     previous high.  */
-	  assert (result == GL(dl_tls_max_dtv_idx) + 1);
+	  assert (result == GL(dl_tls_max_dtv_idx));
 
 	  /* There is no gap anymore.  */
 	  GL(dl_tls_dtv_gaps) = false;