C++11 thread_local destructors support

This feature is specifically for the C++ compiler to offload calling
thread_local object destructors on thread program exit, to glibc.
This is to overcome the possible complication of destructors of
thread_local objects getting called after the DSO in which they're
defined is unloaded by the dynamic linker.  The DSO is marked as
'unloadable' if it has a constructed thread_local object and marked as
'unloadable' again when all the constructed thread_local objects
defined in it are destroyed.
This commit is contained in:
Siddhesh Poyarekar 2013-02-18 19:08:21 +05:30
parent ffaa74cf68
commit ba384f6ed9
40 changed files with 422 additions and 2 deletions

View file

@ -1,5 +1,38 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* Versions.def: Add GLIBC_2.18.
* include/link.h (struct link_map): New member l_tls_dtor_count.
* include/stdlib.h (__cxa_thread_atexit_impl): Declare.
(__call_tls_dtors): Likewise.
* sysdeps/unix/sysv/linux/i386/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc64/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/s390/s390-32/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/s390/s390-64/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/sh/nptl/libc.abilist: Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc32/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc64/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/x86_64/64/nptl/libc.abilist: Likewise.
* sysdeps/unix/sysv/linux/x86_64/x32/nptl/libc.abilist:
Likewise.
* stdlib/Makefile (routines): Add __cxa_thread_atexit_impl.
(tests): Add test case tst-tls-atexit.
(modules-names): Add shared library for tst-tls-atexit.
* stdlib/Versions (GLIBC_2.17): Add __cxa_thread_atexit_impl.
(GLIBC_PRIVATE): Add __call_tls_dtors.
* stdlib/cxa_thread_atexit_impl.c: New file with helper function
for libstdc++.
* stdlib/exit.c (__run_exit_handlers): Call __call_tls_dtors.
* stdlib/tst-tls-atexit.c: New test case.
* stdlib/tst-tls-atexit-lib.c: New test case.
* misc/tst-pselect.c: Include stdlib.h for declaration of exit.
* nptl/sysdeps/pthread/tst-timer.c: Likewise.
* nptl/tst-barrier4.c: Likewise.

View file

@ -34,6 +34,7 @@ libc {
GLIBC_2.15
GLIBC_2.16
GLIBC_2.17
GLIBC_2.18
HURD_CTHREADS_0.3
%ifdef EXPORT_UNWIND_FIND_FDE
GCC_3.0

View file

@ -302,6 +302,9 @@ struct link_map
/* Index of the module in the dtv array. */
size_t l_tls_modid;
/* Number of thread_local objects constructed by this DSO. */
size_t l_tls_dtor_count;
/* Information used to change permission after the relocations are
done. */
ElfW(Addr) l_relro_addr;

View file

@ -100,6 +100,11 @@ extern int __cxa_atexit (void (*func) (void *), void *arg, void *d);
extern int __cxa_atexit_internal (void (*func) (void *), void *arg, void *d)
attribute_hidden;
extern int __cxa_thread_atexit_impl (void (*func) (void *), void *arg,
void *d);
extern void __call_tls_dtors (void);
libc_hidden_proto (__call_tls_dtors);
extern void __cxa_finalize (void *d);
extern int __posix_memalign (void **memptr, size_t alignment, size_t size);

View file

@ -311,6 +311,9 @@ start_thread (void *arg)
#endif
}
/* Call destructors for the thread_local TLS variables. */
__call_tls_dtors ();
/* Run the destructor for the thread-local data. */
__nptl_deallocate_tsd ();

View file

@ -1,3 +1,8 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/alpha/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
2013-02-14 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View file

@ -1,3 +1,8 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/arm/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
2013-02-13 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View file

@ -1,3 +1,8 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
2013-02-08 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View file

@ -1,3 +1,10 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/m68k/coldfire/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
* sysdeps/unix/sysv/linux/m68k/m680x0/nptl/libc.abilist:
Likewise.
2013-02-08 Andreas Schwab <schwab@linux-m68k.org>
* sysdeps/unix/sysv/linux/m68k/kernel-features.h

View file

@ -1,3 +1,12 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist: Add
__cxa_thread_atexit_impl.
* sysdeps/unix/sysv/linux/mips/mips64/n32/nptl/libc.abilist:
Likewise.
* sysdeps/unix/sysv/linux/mips/mips64/n64/nptl/libc.abilist:
Likewise.
2013-02-13 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View file

@ -1,3 +1,10 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/nptl/libc.abilist:
Add __cxa_thread_atexit_impl.
* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/nptl/libc.abilist:
Likewise.
2013-02-14 Joseph Myers <joseph@codesourcery.com>
[BZ #13550]

View file

@ -1,3 +1,10 @@
2013-02-18 Siddhesh Poyarekar <siddhesh@redhat.com>
* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/nptl/libc.abilist:
Add __cxa_thread_atexit_impl.
* sysdeps/unix/sysv/linux/tile/tilepro/nptl/libc.abilist:
Likewise.
2013-01-10 Chris Metcalf <cmetcalf@tilera.com>
* sysdeps/unix/sysv/linux/tile/tilegx/ldd-rewrite.sed: New file.

View file

@ -1819,6 +1819,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -86,6 +86,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.4
GLIBC_2.4 A
_Exit F

View file

@ -86,6 +86,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -87,6 +87,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.4
GLIBC_2.4 A
_Exit F

View file

@ -1775,6 +1775,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -2250,3 +2250,6 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F

View file

@ -1398,6 +1398,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -1396,6 +1396,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -1781,6 +1781,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -2088,3 +2088,6 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F

View file

@ -2088,3 +2088,6 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F

View file

@ -2088,3 +2088,6 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F

View file

@ -34,7 +34,7 @@ routines := \
bsearch qsort msort \
getenv putenv setenv secure-getenv \
exit on_exit atexit cxa_atexit cxa_finalize old_atexit \
quick_exit at_quick_exit cxa_at_quick_exit \
quick_exit at_quick_exit cxa_at_quick_exit cxa_thread_atexit_impl \
abs labs llabs \
div ldiv lldiv \
mblen mbstowcs mbtowc wcstombs wctomb \
@ -71,9 +71,11 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
tst-makecontext2 tst-strtod6 tst-unsetenv1 \
tst-makecontext3 bug-getcontext bug-fmtmsg1 \
tst-secure-getenv tst-strtod-overflow tst-strtod-round \
tst-tininess tst-strtod-underflow
tst-tininess tst-strtod-underflow tst-tls-atexit
tests-static := tst-secure-getenv
modules-names = tst-tls-atexit-lib
include ../Makeconfig
ifeq ($(build-shared),yes)
@ -155,3 +157,9 @@ $(objpfx)bug-getcontext: $(link-libm)
$(objpfx)tst-strtod-round: $(link-libm)
$(objpfx)tst-tininess: $(link-libm)
$(objpfx)tst-strtod-underflow: $(link-libm)
tst-tls-atexit-lib.so-no-z-defs = yes
LDFLAGS-tst-tls-atexit = $(common-objpfx)nptl/libpthread.so \
$(common-objpfx)dlfcn/libdl.so
$(objpfx)tst-tls-atexit.out: $(objpfx)tst-tls-atexit-lib.so

View file

@ -106,6 +106,9 @@ libc {
GLIBC_2.17 {
secure_getenv;
}
GLIBC_2.18 {
__cxa_thread_atexit_impl;
}
GLIBC_PRIVATE {
# functions which have an additional interface since they are
# are cancelable.
@ -114,5 +117,6 @@ libc {
__abort_msg;
# Used from other libraries
__libc_secure_getenv;
__call_tls_dtors;
}
}

View file

@ -0,0 +1,102 @@
/* Register destructors for C++ TLS variables declared with thread_local.
Copyright (C) 2012 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 <stdlib.h>
#include <ldsodefs.h>
typedef void (*dtor_func) (void *);
struct dtor_list
{
dtor_func func;
void *obj;
struct link_map *map;
struct dtor_list *next;
};
static __thread struct dtor_list *tls_dtor_list;
static __thread void *dso_symbol_cache;
static __thread struct link_map *lm_cache;
/* Register a destructor for TLS variables declared with the 'thread_local'
keyword. This function is only called from code generated by the C++
compiler. FUNC is the destructor function and OBJ is the object to be
passed to the destructor. DSO_SYMBOL is the __dso_handle symbol that each
DSO has at a unique address in its map, added from crtbegin.o during the
linking phase. */
int
__cxa_thread_atexit_impl (dtor_func func, void *obj, void *dso_symbol)
{
/* Prepend. */
struct dtor_list *new = calloc (1, sizeof (struct dtor_list));
new->func = func;
new->obj = obj;
new->next = tls_dtor_list;
tls_dtor_list = new;
/* See if we already encountered the DSO. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
if (__builtin_expect (dso_symbol_cache != dso_symbol, 0))
{
ElfW(Addr) caller = (ElfW(Addr)) dso_symbol;
struct link_map *l = _dl_find_dso_for_object (caller);
/* 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;
new->map = lm_cache;
new->map->l_tls_dtor_count++;
__rtld_lock_unlock_recursive (GL(dl_load_lock));
return 0;
}
/* Call the destructors. This is called either when a thread returns from the
initial function or when the process exits via the exit(3) function. */
void
__call_tls_dtors (void)
{
while (tls_dtor_list)
{
struct dtor_list *cur = tls_dtor_list;
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));
free (cur);
}
}
libc_hidden_def (__call_tls_dtors)

View file

@ -33,6 +33,9 @@ attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit)
{
/* First, call the TLS destructors. */
__call_tls_dtors ();
/* We do it this way to handle recursive calls to exit () made by
the functions registered with `atexit' and `on_exit'. We call
everyone on the list and use the status value in the last

View file

@ -0,0 +1,36 @@
/* Verify that DSO is unloaded only if its TLS objects are destroyed - the DSO.
Copyright (C) 2012 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/>. */
extern void *__dso_handle;
typedef struct
{
void *val;
} A;
/* We only care about the destructor. */
void A_dtor (void *obj)
{
((A *)obj)->val = obj;
}
void do_foo (void)
{
static __thread A b;
__cxa_thread_atexit_impl (A_dtor, &b, __dso_handle);
}

111
stdlib/tst-tls-atexit.c Normal file
View file

@ -0,0 +1,111 @@
/* Verify that DSO is unloaded only if its TLS objects are destroyed.
Copyright (C) 2012 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/>. */
/* There are two tests in this test case. The first is implicit where it is
assumed that the destructor call on exit of the LOAD function does not
segfault. The other is a verification that after the thread has exited, a
dlclose will unload the DSO. */
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
void *handle;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void *
load (void *u)
{
pthread_mutex_lock (&m);
handle = dlopen ("$ORIGIN/tst-tls-atexit-lib.so", RTLD_LAZY);
if (!handle)
{
printf ("Unable to load DSO: %s\n", dlerror ());
return (void *) (uintptr_t) 1;
}
void (*foo) (void) = (void (*) (void)) dlsym(handle, "do_foo");
if (!foo)
{
printf ("Unable to find symbol: %s\n", dlerror ());
exit (1);
}
foo ();
/* This should not unload the DSO. If it does, then the thread exit will
result in a segfault. */
dlclose (handle);
pthread_mutex_unlock (&m);
return NULL;
}
int
main (void)
{
pthread_t t;
int ret;
void *thr_ret;
if ((ret = pthread_create (&t, NULL, load, NULL)) != 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;
/* Now this should unload the DSO. */
dlclose (handle);
/* 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 1;
}
}
free (line);
return 0;
}

View file

@ -1819,6 +1819,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -1781,6 +1781,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -87,6 +87,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.3
GLIBC_2.3 A
_Exit F

View file

@ -1771,6 +1771,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -92,6 +92,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -92,6 +92,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -1776,6 +1776,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_IO_adjust_wcolumn F

View file

@ -97,6 +97,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2
GLIBC_2.2 A
_Exit F

View file

@ -88,6 +88,9 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F
GLIBC_2.2.5
GLIBC_2.2.5 A
_Exit F

View file

@ -2086,3 +2086,6 @@ GLIBC_2.17
clock_nanosleep F
clock_settime F
secure_getenv F
GLIBC_2.18
GLIBC_2.18 A
__cxa_thread_atexit_impl F