Test for lazy tls descriptor initialization

Message ID 564B5B08.50205@arm.com
State New, archived
Headers

Commit Message

Szabolcs Nagy Nov. 17, 2015, 4:51 p.m. UTC
  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

Mike Frysinger Dec. 29, 2015, 7:53 p.m. UTC | #1
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
  

Patch

diff --git a/config.make.in b/config.make.in
index 75bb9cc..09c71fb 100644
--- a/config.make.in
+++ b/config.make.in
@@ -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@
diff --git a/configure b/configure
index 01f5075..4865e48 100755
--- a/configure
+++ b/configure
@@ -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 \
diff --git a/configure.ac b/configure.ac
index 3fdf992..13f6630 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/elf/Makefile b/elf/Makefile
index 76985cc..b919531 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -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
diff --git a/elf/tst-lazytls.h b/elf/tst-lazytls.h
new file mode 100644
index 0000000..4067240
--- /dev/null
+++ b/elf/tst-lazytls.h
@@ -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)
diff --git a/elf/tst-lazytls1.c b/elf/tst-lazytls1.c
new file mode 100644
index 0000000..5130277
--- /dev/null
+++ b/elf/tst-lazytls1.c
@@ -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"
diff --git a/elf/tst-lazytls1mod.c b/elf/tst-lazytls1mod.c
new file mode 100644
index 0000000..b33dcb5
--- /dev/null
+++ b/elf/tst-lazytls1mod.c
@@ -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"
diff --git a/elf/tst-lazytls2.c b/elf/tst-lazytls2.c
new file mode 100644
index 0000000..42afb27
--- /dev/null
+++ b/elf/tst-lazytls2.c
@@ -0,0 +1 @@ 
+#include "tst-lazytls1.c"
diff --git a/elf/tst-lazytls2mod.c b/elf/tst-lazytls2mod.c
new file mode 100644
index 0000000..8df3289
--- /dev/null
+++ b/elf/tst-lazytls2mod.c
@@ -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"
diff --git a/elf/tst-lazytls3.c b/elf/tst-lazytls3.c
new file mode 100644
index 0000000..42afb27
--- /dev/null
+++ b/elf/tst-lazytls3.c
@@ -0,0 +1 @@ 
+#include "tst-lazytls1.c"
diff --git a/elf/tst-lazytls3mod.c b/elf/tst-lazytls3mod.c
new file mode 100644
index 0000000..7551e42
--- /dev/null
+++ b/elf/tst-lazytls3mod.c
@@ -0,0 +1,2 @@ 
+#define LINKAGE
+#include "tst-lazytls1mod.c"
diff --git a/elf/tst-lazytls4.c b/elf/tst-lazytls4.c
new file mode 100644
index 0000000..42afb27
--- /dev/null
+++ b/elf/tst-lazytls4.c
@@ -0,0 +1 @@ 
+#include "tst-lazytls1.c"
diff --git a/elf/tst-lazytls4mod.c b/elf/tst-lazytls4mod.c
new file mode 100644
index 0000000..4d10b29
--- /dev/null
+++ b/elf/tst-lazytls4mod.c
@@ -0,0 +1,2 @@ 
+#define LINKAGE
+#include "tst-lazytls2mod.c"