@@ -153,7 +153,11 @@ _dl_close_worker (struct link_map *map, bool force)
maps[idx] = l;
++idx;
- /* Clear DF_1_NODELETE to force object deletion. */
+ /* Clear DF_1_NODELETE to force object deletion. We don't need to touch
+ l_tls_dtor_count because forced object deletion only happens when an
+ error occurs during object load. Destructor registration for TLS
+ non-POD objects should not have happened till then for this
+ object. */
if (force)
l->l_flags_1 &= ~DF_1_NODELETE;
}
@@ -173,10 +177,13 @@ _dl_close_worker (struct link_map *map, bool force)
/* Already handled. */
continue;
+ size_t tls_dtor_count = atomic_load_relaxed (&l->l_tls_dtor_count);
+
/* Check whether this object is still used. */
if (l->l_type == lt_loaded
&& l->l_direct_opencount == 0
&& (l->l_flags_1 & DF_1_NODELETE) == 0
+ && tls_dtor_count == 0
&& !used[done_index])
continue;
@@ -74,7 +74,7 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
tst-makecontext3 bug-getcontext bug-fmtmsg1 \
tst-secure-getenv tst-strtod-overflow tst-strtod-round \
tst-tininess tst-strtod-underflow tst-tls-atexit \
- tst-setcontext3
+ tst-setcontext3 tst-tls-atexit-nodelete
tests-static := tst-secure-getenv
modules-names = tst-tls-atexit-lib
@@ -159,6 +159,9 @@ tst-tls-atexit-lib.so-no-z-defs = yes
$(objpfx)tst-tls-atexit: $(shared-thread-library) $(libdl)
$(objpfx)tst-tls-atexit.out: $(objpfx)tst-tls-atexit-lib.so
+$(objpfx)tst-tls-atexit-nodelete: $(shared-thread-library) $(libdl)
+$(objpfx)tst-tls-atexit-nodelete.out: $(objpfx)tst-tls-atexit-lib.so
+
$(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3
$(SHELL) $< $(common-objpfx) '$(test-program-prefix-before-env)' \
'$(run-program-env)' '$(test-program-prefix-after-env)' \
@@ -50,27 +50,25 @@ __cxa_thread_atexit_impl (dtor_func func, void *obj, void *dso_symbol)
tls_dtor_list = new;
/* See if we already encountered the DSO. */
- __rtld_lock_lock_recursive (GL(dl_load_lock));
-
if (__glibc_unlikely (dso_symbol_cache != dso_symbol))
{
ElfW(Addr) caller = (ElfW(Addr)) dso_symbol;
+ /* _dl_find_dso_for_object assumes that we have the dl_load_lock. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
struct link_map *l = _dl_find_dso_for_object (caller);
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
/* If the address is not recognized the call comes from the main
program (we hope). */
lm_cache = l ? l : GL(dl_ns)[LM_ID_BASE]._ns_loaded;
}
+
/* A destructor could result in a thread_local construction and the former
could have cleared the flag. */
- if (lm_cache->l_type == lt_loaded && lm_cache->l_tls_dtor_count == 0)
- lm_cache->l_flags_1 |= DF_1_NODELETE;
+ atomic_increment (&lm_cache->l_tls_dtor_count);
new->map = lm_cache;
- new->map->l_tls_dtor_count++;
-
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
return 0;
}
@@ -83,19 +81,10 @@ __call_tls_dtors (void)
while (tls_dtor_list)
{
struct dtor_list *cur = tls_dtor_list;
- tls_dtor_list = tls_dtor_list->next;
+ tls_dtor_list = tls_dtor_list->next;
cur->func (cur->obj);
-
- __rtld_lock_lock_recursive (GL(dl_load_lock));
-
- /* Allow DSO unload if count drops to zero. */
- cur->map->l_tls_dtor_count--;
- if (cur->map->l_tls_dtor_count == 0 && cur->map->l_type == lt_loaded)
- cur->map->l_flags_1 &= ~DF_1_NODELETE;
-
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
-
+ atomic_decrement (&cur->map->l_tls_dtor_count);
free (cur);
}
}
new file mode 100644
@@ -0,0 +1,118 @@
+/* Verify that a RTLD_NODELETE DSO is not unloaded even if its TLS objects are
+ destroyed.
+
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static void *
+use_handle (void *h)
+{
+ void (*foo) (void) = (void (*) (void)) dlsym(h, "do_foo");
+
+ if (!foo)
+ {
+ printf ("Unable to find symbol: %s\n", dlerror ());
+ exit (1);
+ }
+
+ foo ();
+
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ pthread_t t;
+ int ret;
+ void *thr_ret;
+
+ /* Load our module. We access (and hence construct the object) in the
+ thread. */
+ void *handle = dlopen ("$ORIGIN/tst-tls-atexit-lib.so", RTLD_LAZY);
+ if (handle == NULL)
+ {
+ printf ("Unable to load DSO: %s\n", dlerror ());
+ return 1;
+ }
+
+ if ((ret = pthread_create (&t, NULL, use_handle, handle)) != 0)
+ {
+ printf ("pthread_create failed: %s\n", strerror (ret));
+ return 1;
+ }
+
+ if ((ret = pthread_join (t, &thr_ret)) != 0)
+ {
+ printf ("pthread_create failed: %s\n", strerror (ret));
+ return 1;
+ }
+
+ if (thr_ret != NULL)
+ return 1;
+
+ /* This sequence should not unload the DSO. */
+ void *h2 = dlopen ("$ORIGIN/tst-tls-atexit-lib.so",
+ RTLD_LAZY | RTLD_NODELETE);
+ if (h2 == NULL)
+ {
+ printf ("main thread: Unable to load DSO: %s\n", dlerror ());
+ return 1;
+ }
+
+ use_handle (h2);
+
+ /* Close the handle we created in the thread. */
+ dlclose (handle);
+ dlclose (h2);
+
+ /* Run through our maps and ensure that the DSO is unloaded. */
+ FILE *f = fopen ("/proc/self/maps", "r");
+
+ if (f == NULL)
+ {
+ perror ("Failed to open /proc/self/maps");
+ fprintf (stderr, "Skipping verification of DSO unload\n");
+ return 0;
+ }
+
+ char *line = NULL;
+ size_t s = 0;
+ while (getline (&line, &s, f) > 0)
+ {
+ if (strstr (line, "tst-tls-atexit-lib.so"))
+ {
+ printf ("DSO not unloaded yet:\n%s", line);
+ return 0;
+ }
+ }
+ free (line);
+
+ /* The module was unloaded even when we specified RTLD_NODELETE. FAIL. */
+ printf ("tst-tls-atexit-lib.so was unloaded.\n");
+ return 1;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
@@ -79,7 +79,14 @@ do_test (void)
if (thr_ret != NULL)
return 1;
- /* Now this should unload the DSO. */
+ /* Now this sequence should unload the DSO. */
+ handle = dlopen ("$ORIGIN/tst-tls-atexit-lib.so", RTLD_LAZY);
+ if (handle == NULL)
+ {
+ printf ("main thread: Unable to load DSO: %s\n", dlerror ());
+ return 1;
+ }
+
dlclose (handle);
/* Run through our maps and ensure that the DSO is unloaded. */