Test for lazy tls descriptor initialization
Commit Message
These tests can trigger BZ 19129, 18572, 18034 with varying reliability.
They test concurrent first access of tls objects with internal and external
linkage in a dso. The four variants are {static,extern} x 2 alignments.
(The tlsdesc race is more likely to trigger when the tls descriptors
are not double-word aligned in the .rel.plt section.)
I enabled the tests for tlsdesc because that's the only case when first
tls access does lazy initialization.
There might be a cleaner solution for the -mtls-dialect=... check.
Currently I'm looking at disabling lazy symbol binding of tlsdesc,
but it's not as trivial as i'd like it to be, in case such patch
is accepted then these tests are not useful.
ChangeLog:
2015-11-17 Szabolcs Nagy <szabolcs.nagy@arm.com>
* config.make.in (have-tlsdesc, config-clfags-tlsdesc): New.
* configure.ac (libc_cv_tlsdesc, libc_cv_cc_tlsdesc): New.
* configure: Regenerated.
* elf/Makefile (tests): Add tst-lazytls{1,2,3,4}.
(modules-names): Add tst-lazytls{1,2,3,4}mod.
($(objpfx)tst-lazytls{1,2,3,4}): Add dependencies.
* elf/tst-lazytls.h: New.
* elf/tst-lazytls1.c: New.
* elf/tst-lazytls2.c: New.
* elf/tst-lazytls3.c: New.
* elf/tst-lazytls4.c: New.
* elf/tst-lazytls1mod.c: New.
* elf/tst-lazytls2mod.c: New.
* elf/tst-lazytls3mod.c: New.
* elf/tst-lazytls4mod.c: New.
Comments
On 17 Nov 2015 16:51, Szabolcs Nagy wrote:
> +AC_CACHE_CHECK(for compiler option to enable TLS descriptors,
> + libc_cv_cc_tlsdesc, [dnl
> +libc_cv_cc_tlsdesc=-no-tlsdesc-support
should use "no" rather than an arbitrary nonsense string. this is a cache
var, so people running configure would have to do:
libc_cv_cc_tlsdesc=-no-tlsdesc-support
instead of the more common:
libc_cv_cc_tlsdesc=no
> --- /dev/null
> +++ b/elf/tst-lazytls.h
> @@ -0,0 +1,11 @@
> +#define A(n) \
all these files need proper comment block headers
> +#include <assert.h>
you can't use assert in tests because it writes to stderr.
you'll need to use printf/exit yourself.
> + extern void f##n (void); \
style on these files is wrong -- indentation needs to use spaces rather
than tabs
-mike
@@ -53,6 +53,8 @@ have-protected-data = @libc_cv_protected_data@
with-fp = @with_fp@
enable-timezone-tools = @enable_timezone_tools@
unwind-find-fde = @libc_cv_gcc_unwind_find_fde@
+have-tlsdesc = @libc_cv_tlsdesc@
+config-cflags-tlsdesc = @libc_cv_cc_tlsdesc@
have-fpie = @libc_cv_fpie@
stack-protector = @stack_protector@
have-selinux = @have_selinux@
@@ -617,6 +617,8 @@ have_libcap
have_libaudit
LIBGD
libc_cv_cc_loop_to_function
+libc_cv_tlsdesc
+libc_cv_cc_tlsdesc
libc_cv_cc_submachine
libc_cv_cc_nofma
stack_protector
@@ -5929,6 +5931,57 @@ $as_echo "$libc_cv_cc_submachine" >&6; }
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler option to enable TLS descriptors" >&5
+$as_echo_n "checking for compiler option to enable TLS descriptors... " >&6; }
+if ${libc_cv_cc_tlsdesc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ libc_cv_cc_tlsdesc=-no-tlsdesc-support
+for opt in -mtls-dialect=desc -mtls-dialect=gnu2; do
+ if { ac_try='${CC-cc} $opt -xc /dev/null -S -o /dev/null'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ libc_cv_cc_tlsdesc=$opt; break
+fi
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cc_tlsdesc" >&5
+$as_echo "$libc_cv_cc_tlsdesc" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TLS descriptor support" >&5
+$as_echo_n "checking for TLS descriptor support... " >&6; }
+if ${libc_cv_tlsdesc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ libc_cv_tlsdesc=no
+if test "$libc_cv_cc_tlsdesc" != -no-tlsdesc-support; then
+cat > conftest.c <<EOF
+__thread int bar;
+int foo (void) { return bar; }
+EOF
+ if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS
+ -fPIC -shared -o conftest.so conftest.c
+ "$libc_cv_cc_tlsdesc" -nostdlib -nostartfiles'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ if $READELF -rW conftest.so | grep _TLSDESC > /dev/null; then
+ libc_cv_tlsdesc=yes
+ fi
+ fi
+fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_tlsdesc" >&5
+$as_echo "$libc_cv_tlsdesc" >&6; }
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -fno-tree-loop-distribute-patterns with \
__attribute__ ((__optimize__))" >&5
$as_echo_n "checking if $CC accepts -fno-tree-loop-distribute-patterns with \
@@ -1474,6 +1474,32 @@ if test -n "$submachine"; then
fi
AC_SUBST(libc_cv_cc_submachine)
+AC_CACHE_CHECK(for compiler option to enable TLS descriptors,
+ libc_cv_cc_tlsdesc, [dnl
+libc_cv_cc_tlsdesc=-no-tlsdesc-support
+for opt in -mtls-dialect=desc -mtls-dialect=gnu2; do
+ LIBC_TRY_CC_OPTION([$opt], [libc_cv_cc_tlsdesc=$opt; break])
+done])
+AC_SUBST(libc_cv_cc_tlsdesc)
+
+AC_CACHE_CHECK(for TLS descriptor support, libc_cv_tlsdesc, [dnl
+libc_cv_tlsdesc=no
+if test "$libc_cv_cc_tlsdesc" != -no-tlsdesc-support; then
+cat > conftest.c <<EOF
+__thread int bar;
+int foo (void) { return bar; }
+EOF
+ if AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS
+ -fPIC -shared -o conftest.so conftest.c
+ "$libc_cv_cc_tlsdesc" -nostdlib -nostartfiles]); then
+ if $READELF -rW conftest.so | grep _TLSDESC > /dev/null; then
+ libc_cv_tlsdesc=yes
+ fi
+ fi
+fi
+rm -f conftest*])
+AC_SUBST(libc_cv_tlsdesc)
+
AC_CACHE_CHECK(if $CC accepts -fno-tree-loop-distribute-patterns with \
__attribute__ ((__optimize__)), libc_cv_cc_loop_to_function, [dnl
cat > conftest.c <<EOF
@@ -160,6 +160,9 @@ selinux-enabled := $(shell cat /selinux/enforce 2> /dev/null)
ifneq ($(selinux-enabled),1)
tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
endif
+ifeq ($(have-tlsdesc),yes)
+tests += tst-lazytls1 tst-lazytls2 tst-lazytls3 tst-lazytls4
+endif
endif
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-leaks1-mem.out \
@@ -279,6 +282,10 @@ modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6
endif
endif
+ifeq ($(have-tlsdesc),yes)
+modules-names += tst-lazytls1mod tst-lazytls2mod tst-lazytls3mod tst-lazytls4mod
+endif
+
ifeq (yes,$(build-shared))
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out
@@ -1234,3 +1241,15 @@ $(objpfx)tst-audit12: $(libdl)
tst-audit12-ENV = LD_AUDIT=$(objpfx)tst-auditmod12.so
$(objpfx)tst-audit12mod1.so: $(objpfx)tst-audit12mod2.so
LDFLAGS-tst-audit12mod2.so = -Wl,--version-script=tst-audit12mod2.map
+
+$(objpfx)tst-lazytls1: $(shared-thread-library) $(objpfx)tst-lazytls1mod.so
+$(objpfx)tst-lazytls2: $(shared-thread-library) $(objpfx)tst-lazytls2mod.so
+$(objpfx)tst-lazytls3: $(shared-thread-library) $(objpfx)tst-lazytls3mod.so
+$(objpfx)tst-lazytls4: $(shared-thread-library) $(objpfx)tst-lazytls4mod.so
+
+ifeq ($(have-tlsdesc),yes)
+CFLAGS-tst-lazytls1mod.c += $(config-cflags-tlsdesc)
+CFLAGS-tst-lazytls2mod.c += $(config-cflags-tlsdesc)
+CFLAGS-tst-lazytls3mod.c += $(config-cflags-tlsdesc)
+CFLAGS-tst-lazytls4mod.c += $(config-cflags-tlsdesc)
+endif
new file mode 100644
@@ -0,0 +1,11 @@
+#define A(n) \
+B(n##0) B(n##1) B(n##2) B(n##3) B(n##4) B(n##5) B(n##6) B(n##7) B(n##8) B(n##9)
+#define B(n) \
+T(n##0) T(n##1) T(n##2) T(n##3) T(n##4) T(n##5) T(n##6) T(n##7) T(n##8) T(n##9)
+
+/* Invoke T 500 times with different arguments. */
+A(0)
+A(1)
+A(2)
+A(3)
+A(4)
new file mode 100644
@@ -0,0 +1,38 @@
+#include <pthread.h>
+#include <assert.h>
+
+static pthread_barrier_t b;
+
+void *
+start (void *a)
+{
+ pthread_barrier_wait (&b);
+
+#define T(n) \
+ extern void f##n (void); \
+ f##n ();
+
+#include "tst-lazytls.h"
+
+ return 0;
+}
+
+#define NTHREAD 66
+
+static int
+do_test (void)
+{
+ pthread_t td[NTHREAD];
+ int i;
+
+ assert (pthread_barrier_init (&b, 0, NTHREAD) == 0);
+ for (i = 0; i < NTHREAD; i++)
+ assert (pthread_create (td+i, 0, start, 0) == 0);
+ for (i = 0; i < NTHREAD; i++)
+ assert (pthread_join (td[i], 0) == 0);
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
new file mode 100644
@@ -0,0 +1,17 @@
+int want = 8;
+
+#ifndef LINKAGE
+#define LINKAGE static
+#endif
+
+#define T(n) \
+__attribute__ ((section (".a."#n))) \
+LINKAGE __thread int a##n = 7; \
+\
+void \
+f##n (void) \
+{ \
+ if (++a##n != want) __builtin_trap (); \
+}
+
+#include "tst-lazytls.h"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-lazytls1.c"
new file mode 100644
@@ -0,0 +1,10 @@
+/* Add an extern call hoping that it adds a relocation entry to .rel.plt
+ that changes the alignment of the tlsdesc entries. */
+void
+unused (void)
+{
+ extern int puts (const char*);
+ puts ("");
+}
+
+#include "tst-lazytls1mod.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-lazytls1.c"
new file mode 100644
@@ -0,0 +1,2 @@
+#define LINKAGE
+#include "tst-lazytls1mod.c"
new file mode 100644
@@ -0,0 +1 @@
+#include "tst-lazytls1.c"
new file mode 100644
@@ -0,0 +1,2 @@
+#define LINKAGE
+#include "tst-lazytls2mod.c"