about summary refs log tree commit diff
path: root/stdlib/tst-tls-atexit.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/tst-tls-atexit.c')
-rw-r--r--stdlib/tst-tls-atexit.c60
1 files changed, 46 insertions, 14 deletions
diff --git a/stdlib/tst-tls-atexit.c b/stdlib/tst-tls-atexit.c
index cea655decc..e9839d8b15 100644
--- a/stdlib/tst-tls-atexit.c
+++ b/stdlib/tst-tls-atexit.c
@@ -16,12 +16,20 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-/* This test dynamically loads a DSO and spawns a thread that subsequently
-   calls into the DSO to register a destructor for an object in the DSO and
-   then calls dlclose on the handle for the DSO.  When the thread exits, the
-   DSO should not be unloaded or else the destructor called during thread exit
-   will crash.  Further in the main thread, the DSO is opened and closed again,
-   at which point the DSO should be unloaded.  */
+/* For the default case, i.e. NO_DELETE not defined, the test dynamically loads
+   a DSO and spawns a thread that subsequently calls into the DSO to register a
+   destructor for an object in the DSO and then calls dlclose on the handle for
+   the DSO.  When the thread exits, the DSO should not be unloaded or else the
+   destructor called during thread exit will crash.  Further in the main
+   thread, the DSO is opened and closed again, at which point the DSO should be
+   unloaded.
+
+   When NO_DELETE is defined, the DSO is loaded twice, once with just RTLD_LAZY
+   flag and the second time with the RTLD_NODELETE flag set.  The thread is
+   spawned, destructor registered and then thread exits without closing the
+   DSO.  In the main thread, the first handle is then closed, followed by the
+   second handle.  In the end, the DSO should remain loaded due to the
+   RTLD_NODELETE flag being set in the second dlopen call.  */
 
 #include <dlfcn.h>
 #include <pthread.h>
@@ -31,6 +39,14 @@
 #include <errno.h>
 #include <link.h>
 
+#ifndef NO_DELETE
+# define LOADED_IS_GOOD false
+#endif
+
+#ifndef H2_RTLD_FLAGS
+# define H2_RTLD_FLAGS (RTLD_LAZY)
+#endif
+
 #define DSO_NAME "$ORIGIN/tst-tls-atexit-lib.so"
 
 /* Walk through the map in the _r_debug structure to see if our lib is still
@@ -43,7 +59,10 @@ is_loaded (void)
   for (; lm; lm = lm->l_next)
     if (lm->l_type == lt_loaded && lm->l_name
 	&& strcmp (basename (DSO_NAME), basename (lm->l_name)) == 0)
-	  return true;
+      {
+	printf ("%s is still loaded\n", lm->l_name);
+	return true;
+      }
   return false;
 }
 
@@ -63,7 +82,9 @@ reg_dtor_and_close (void *h)
 
   reg_dtor ();
 
+#ifndef NO_DELETE
   dlclose (h);
+#endif
 
   return NULL;
 }
@@ -104,19 +125,30 @@ do_test (void)
       return 1;
     }
 
+#ifndef NO_DELETE
   if (spawn_thread (h1) != 0)
     return 1;
+#endif
 
-  /* Now this should unload the DSO.  FIXME: This is a bug, calling dlclose
-     like this is actually wrong, but it works because cxa_thread_atexit_impl
-     has a bug which results in dlclose allowing this to work.  */
-  dlclose (h1);
+  void *h2 = dlopen (DSO_NAME, H2_RTLD_FLAGS);
+  if (h2 == NULL)
+    {
+      printf ("h2: Unable to load DSO: %s\n", dlerror ());
+      return 1;
+    }
 
-  /* Check link maps to ensure that the DSO has unloaded.  */
-  if (is_loaded ())
+#ifdef NO_DELETE
+  if (spawn_thread (h1) != 0)
     return 1;
 
-  return 0;
+  dlclose (h1);
+#endif
+  dlclose (h2);
+
+  /* Check link maps to ensure that the DSO has unloaded.  In the normal case,
+     the DSO should be unloaded if there are no uses.  However, if one of the
+     dlopen calls were with RTLD_NODELETE, the DSO should remain loaded.  */
+  return is_loaded () == LOADED_IS_GOOD ? 0 : 1;
 }
 
 #define TEST_FUNCTION do_test ()