Add further test of TLS

Joseph Myers josmyers@redhat.com
Wed Nov 27 01:52:23 GMT 2024


Add an additional test of TLS variables, with different alignment,
accessed from different modules.  The idea of the alignment test is
similar to tst-tlsalign and the same code is shared for setting up
test variables, but unlike the tst-tlsalign code, there are multiple
threads and variables are accessed from multiple objects to verify
that they get a consistent notion of the address of an object within a
thread.  Threads are repeatedly created and shut down to verify proper
initialization in each new thread.  The test is also repeated with TLS
descriptors when supported.  (However, only initial-exec TLS is
covered in this test.)

Tested for x86_64.

diff --git a/elf/Makefile b/elf/Makefile
index ba6b022a2f..45d7e9da8b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -481,6 +481,8 @@ tests += \
   tst-tls19 \
   tst-tls20 \
   tst-tls21 \
+  tst-tls22 \
+  tst-tls22-gnu2 \
   tst-tlsalign \
   tst-tlsalign-extern \
   tst-tlsgap \
@@ -681,9 +683,15 @@ tst-tls-many-dynamic-modules-dep-bad = \
 extra-test-objs += \
   $(tlsmod17a-modules:=.os) \
   $(tlsmod18a-modules:=.os) \
+  tst-tls22-mod1-vars.os \
+  tst-tls22-mod2-vars.os \
+  tst-tls22-vars.o \
   tst-tlsalign-vars.o \
   # extra-test-objs
 test-extras += \
+  tst-tls22-mod1-vars \
+  tst-tls22-mod2-vars \
+  tst-tls22-vars \
   tst-tlsalign-vars \
   tst-tlsmod17a \
   tst-tlsmod18a \
@@ -955,6 +963,10 @@ modules-names += \
   tst-tls19mod3 \
   tst-tls20mod-bad \
   tst-tls21mod \
+  tst-tls22-mod1 \
+  tst-tls22-mod1-gnu2 \
+  tst-tls22-mod2 \
+  tst-tls22-mod2-gnu2 \
   tst-tlsalign-lib \
   tst-tlsgap-mod0 \
   tst-tlsgap-mod1 \
@@ -3189,3 +3201,27 @@ tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 
 # Any shared object should do.
 tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
+
+$(objpfx)tst-tls22: $(objpfx)tst-tls22-vars.o $(objpfx)tst-tls22-mod1.so \
+  $(objpfx)tst-tls22-mod2.so $(shared-thread-library)
+$(objpfx)tst-tls22-mod1.so: $(objpfx)tst-tls22-mod1.os \
+  $(objpfx)tst-tls22-mod1-vars.os $(objpfx)tst-tls22-mod2.so
+$(objpfx)tst-tls22-mod2.so: $(objpfx)tst-tls22-mod2.os \
+  $(objpfx)tst-tls22-mod2-vars.os
+$(objpfx)tst-tls22-gnu2: $(objpfx)tst-tls22-vars.o \
+  $(objpfx)tst-tls22-mod1-gnu2.so $(objpfx)tst-tls22-mod2-gnu2.so \
+  $(shared-thread-library)
+$(objpfx)tst-tls22-mod1-gnu2.so: $(objpfx)tst-tls22-mod1-gnu2.os \
+  $(objpfx)tst-tls22-mod1-vars.os $(objpfx)tst-tls22-mod2-gnu2.so
+$(objpfx)tst-tls22-mod2-gnu2.so: $(objpfx)tst-tls22-mod2-gnu2.os \
+  $(objpfx)tst-tls22-mod2-vars.os
+ifneq (no,$(have-mtls-descriptor))
+CFLAGS-tst-tls22-gnu2.c += -mtls-dialect=$(have-mtls-descriptor)
+CFLAGS-tst-tls22-mod1-gnu2.c += -mtls-dialect=$(have-mtls-descriptor)
+CFLAGS-tst-tls22-mod2-gnu2.c += -mtls-dialect=$(have-mtls-descriptor)
+endif
+# These reference symbols from the main executable.
+tst-tls22-mod1.so-no-z-defs = yes
+tst-tls22-mod1-gnu2.so-no-z-defs = yes
+tst-tls22-mod2.so-no-z-defs = yes
+tst-tls22-mod2-gnu2.so-no-z-defs = yes
diff --git a/elf/tst-tls22-gnu2.c b/elf/tst-tls22-gnu2.c
new file mode 100644
index 0000000000..d9ce6df0b2
--- /dev/null
+++ b/elf/tst-tls22-gnu2.c
@@ -0,0 +1 @@
+#include <tst-tls22.c>
diff --git a/elf/tst-tls22-mod1-gnu2.c b/elf/tst-tls22-mod1-gnu2.c
new file mode 100644
index 0000000000..0b085fe175
--- /dev/null
+++ b/elf/tst-tls22-mod1-gnu2.c
@@ -0,0 +1 @@
+#include <tst-tls22-mod1.c>
diff --git a/elf/tst-tls22-mod1-vars.c b/elf/tst-tls22-mod1-vars.c
new file mode 100644
index 0000000000..bdb7358287
--- /dev/null
+++ b/elf/tst-tls22-mod1-vars.c
@@ -0,0 +1,9 @@
+#include <tst-tls22.h>
+
+#define tdata1 mod1_tdata1
+#define tdata2 mod1_tdata2
+#define tdata3 mod1_tdata3
+#define tbss1 mod1_tbss1
+#define tbss2 mod1_tbss2
+#define tbss3 mod1_tbss3
+#include <tst-tlsalign-vars.c>
diff --git a/elf/tst-tls22-mod1.c b/elf/tst-tls22-mod1.c
new file mode 100644
index 0000000000..3a47d7bbc6
--- /dev/null
+++ b/elf/tst-tls22-mod1.c
@@ -0,0 +1,27 @@
+/* Test TLS with varied alignment and multiple modules and threads.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <tst-tls22.h>
+
+void
+test_mod1 (struct one_thread_data *data, int base_val)
+{
+  STORE_ADDRS (&data->mod1_self, mod1);
+  STORE_ADDRS (&data->exe_from_mod1, exe);
+  STORE_ADDRS (&data->mod2_from_mod1, mod2);
+}
diff --git a/elf/tst-tls22-mod2-gnu2.c b/elf/tst-tls22-mod2-gnu2.c
new file mode 100644
index 0000000000..a5260e0616
--- /dev/null
+++ b/elf/tst-tls22-mod2-gnu2.c
@@ -0,0 +1 @@
+#include <tst-tls22-mod2.c>
diff --git a/elf/tst-tls22-mod2-vars.c b/elf/tst-tls22-mod2-vars.c
new file mode 100644
index 0000000000..9ef3452bba
--- /dev/null
+++ b/elf/tst-tls22-mod2-vars.c
@@ -0,0 +1,9 @@
+#include <tst-tls22.h>
+
+#define tdata1 mod2_tdata1
+#define tdata2 mod2_tdata2
+#define tdata3 mod2_tdata3
+#define tbss1 mod2_tbss1
+#define tbss2 mod2_tbss2
+#define tbss3 mod2_tbss3
+#include <tst-tlsalign-vars.c>
diff --git a/elf/tst-tls22-mod2.c b/elf/tst-tls22-mod2.c
new file mode 100644
index 0000000000..5d26d592b0
--- /dev/null
+++ b/elf/tst-tls22-mod2.c
@@ -0,0 +1,26 @@
+/* Test TLS with varied alignment and multiple modules and threads.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <tst-tls22.h>
+
+void
+test_mod2 (struct one_thread_data *data, int base_val)
+{
+  STORE_ADDRS (&data->mod2_self, mod2);
+  STORE_ADDRS (&data->exe_from_mod2, exe);
+}
diff --git a/elf/tst-tls22-vars.c b/elf/tst-tls22-vars.c
new file mode 100644
index 0000000000..2ad3ee7a3b
--- /dev/null
+++ b/elf/tst-tls22-vars.c
@@ -0,0 +1,9 @@
+#include <tst-tls22.h>
+
+#define tdata1 exe_tdata1
+#define tdata2 exe_tdata2
+#define tdata3 exe_tdata3
+#define tbss1 exe_tbss1
+#define tbss2 exe_tbss2
+#define tbss3 exe_tbss3
+#include <tst-tlsalign-vars.c>
diff --git a/elf/tst-tls22.c b/elf/tst-tls22.c
new file mode 100644
index 0000000000..35a8cd82b2
--- /dev/null
+++ b/elf/tst-tls22.c
@@ -0,0 +1,147 @@
+/* Test TLS with varied alignment and multiple modules and threads.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/xthread.h>
+#include <tst-tls22.h>
+
+static void
+check_addrs_align (const struct obj_addrs *addrs)
+{
+  TEST_COMPARE (addrs->addr_tdata1 & (__alignof__ (int) - 1), 0);
+  TEST_COMPARE (addrs->addr_tdata2 & 0xf, 0);
+  TEST_COMPARE (addrs->addr_tdata3 & 0xfff, 0);
+  TEST_COMPARE (addrs->addr_tbss1 & (__alignof__ (int) - 1), 0);
+  TEST_COMPARE (addrs->addr_tbss2 & 0xf, 0);
+  TEST_COMPARE (addrs->addr_tbss3 & 0xfff, 0);
+}
+
+static void
+check_addrs_same (const struct obj_addrs *addrs1,
+		  const struct obj_addrs *addrs2)
+{
+  TEST_COMPARE (addrs1->addr_tdata1, addrs2->addr_tdata1);
+  TEST_COMPARE (addrs1->addr_tdata2, addrs2->addr_tdata2);
+  TEST_COMPARE (addrs1->addr_tdata3, addrs2->addr_tdata3);
+  TEST_COMPARE (addrs1->addr_tbss1, addrs2->addr_tbss1);
+  TEST_COMPARE (addrs1->addr_tbss2, addrs2->addr_tbss2);
+  TEST_COMPARE (addrs1->addr_tbss3, addrs2->addr_tbss3);
+}
+
+static void
+check_vals_before (const struct obj_values *vals)
+{
+  TEST_COMPARE (vals->val_tdata1, 1);
+  TEST_COMPARE (vals->val_tdata2, 2);
+  TEST_COMPARE (vals->val_tdata3, 4);
+  TEST_COMPARE (vals->val_tbss1, 0);
+  TEST_COMPARE (vals->val_tbss2, 0);
+  TEST_COMPARE (vals->val_tbss3, 0);
+}
+
+static void
+check_vals_after (const struct obj_values *vals, int base_val)
+{
+  TEST_COMPARE (vals->val_tdata1, base_val);
+  TEST_COMPARE (vals->val_tdata2, base_val + 1);
+  TEST_COMPARE (vals->val_tdata3, base_val + 2);
+  TEST_COMPARE (vals->val_tbss1, base_val + 3);
+  TEST_COMPARE (vals->val_tbss2, base_val + 4);
+  TEST_COMPARE (vals->val_tbss3, base_val + 5);
+}
+
+static void
+check_one_thread (const struct one_thread_data *data, int base_val)
+{
+  check_vals_before (&data->exe_before);
+  check_vals_before (&data->mod1_before);
+  check_vals_before (&data->mod2_before);
+  check_vals_after (&data->exe_after, base_val);
+  check_vals_after (&data->mod1_after, base_val);
+  check_vals_after (&data->mod2_after, base_val);
+  check_addrs_align (&data->exe_self);
+  check_addrs_same (&data->exe_self, &data->exe_from_mod1);
+  check_addrs_same (&data->exe_self, &data->exe_from_mod2);
+  check_addrs_align (&data->mod1_self);
+  check_addrs_same (&data->mod1_self, &data->mod1_from_exe);
+  check_addrs_align (&data->mod2_self);
+  check_addrs_same (&data->mod2_self, &data->mod2_from_exe);
+  check_addrs_same (&data->mod2_self, &data->mod2_from_mod1);
+}
+
+static void *
+thread_func (void *arg)
+{
+  int base_val = (int) (intptr_t) arg + 10;
+  struct one_thread_data data;
+  /* Record the addresses of variables as seen from the main
+     executable (which should be the same as seen from the other
+     modules), and their initial values.  */
+  STORE_ADDRS (&data.exe_self, exe);
+  STORE_ADDRS (&data.mod1_from_exe, mod1);
+  STORE_ADDRS (&data.mod2_from_exe, mod2);
+  STORE_VALUES (&data.exe_before, exe);
+  STORE_VALUES (&data.mod1_before, mod1);
+  STORE_VALUES (&data.mod2_before, mod2);
+  /* Overwrite the value of variables.  */
+  OVERWRITE_VALUES (exe, base_val);
+  OVERWRITE_VALUES (mod1, base_val);
+  OVERWRITE_VALUES (mod2, base_val);
+  /* Record the addresses of variables as seen from other modules.  */
+  test_mod1 (&data, base_val);
+  test_mod2 (&data, base_val);
+  /* Record the overwritten values (thus making sure that no other
+     thread running in parallel has changed this thread's values).  */
+  STORE_VALUES (&data.exe_after, exe);
+  STORE_VALUES (&data.mod1_after, mod1);
+  STORE_VALUES (&data.mod2_after, mod2);
+  /* Check all the addresses and values recorded.  */
+  check_one_thread (&data, base_val);
+  return NULL;
+}
+
+#define NUM_ITERS 50
+#define NUM_THREADS 16
+
+/* For NUM_ITERS iterations, repeatedly create NUM_THREADS threads.
+   In each thread, we determine the addresses of TLS objects (both
+   from the module defining those objects and from other modules), and
+   their initial values, and store in values that are then read back;
+   we check that each object's address is the same regardless of the
+   module in which it is determined, that alignment of objects is as
+   required, and that the values of objects are as expected.  */
+
+static int
+do_test (void)
+{
+  for (size_t i = 0; i < NUM_ITERS; i++)
+    {
+      pthread_t threads[NUM_THREADS];
+      for (size_t j = 0; j < NUM_THREADS; j++)
+	threads[j] = xpthread_create (NULL, thread_func, (void *) j);
+      /* Also run checks in the main thread, but only once because
+	 those values don't get reinitialized.  */
+      if (i == 0)
+	thread_func ((void *) NUM_THREADS);
+      for (size_t j = 0; j < NUM_THREADS; j++)
+	xpthread_join (threads[j]);
+    }
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-tls22.h b/elf/tst-tls22.h
new file mode 100644
index 0000000000..24b2e0a0b6
--- /dev/null
+++ b/elf/tst-tls22.h
@@ -0,0 +1,115 @@
+/* Test TLS with varied alignment and multiple modules and threads: header.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef TST_TLS22_H
+#define TST_TLS22_H
+
+#include <stdint.h>
+
+extern __thread int exe_tdata1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int exe_tdata2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int exe_tdata3 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int exe_tbss1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int exe_tbss2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int exe_tbss3 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tdata1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tdata2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tdata3 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tbss1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tbss2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod1_tbss3 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tdata1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tdata2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tdata3 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tbss1 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tbss2 __attribute__ ((tls_model ("initial-exec")));
+extern __thread int mod2_tbss3 __attribute__ ((tls_model ("initial-exec")));
+
+/* Structure to store the addresses of one set of TLS objects in one
+   thread, as seen by one module in the program.  */
+struct obj_addrs
+{
+  uintptr_t addr_tdata1, addr_tdata2, addr_tdata3;
+  uintptr_t addr_tbss1, addr_tbss2, addr_tbss3;
+};
+
+/* Structure to store the values of one set of TLS objects in one
+   thread.  */
+struct obj_values
+{
+  uintptr_t val_tdata1, val_tdata2, val_tdata3;
+  uintptr_t val_tbss1, val_tbss2, val_tbss3;
+};
+
+/* Structure to store all the data about TLS objects in one
+   thread.  */
+struct one_thread_data
+{
+  struct obj_addrs exe_self, exe_from_mod1, exe_from_mod2;
+  struct obj_addrs mod1_self, mod1_from_exe;
+  struct obj_addrs mod2_self, mod2_from_exe, mod2_from_mod1;
+  struct obj_values exe_before, mod1_before, mod2_before;
+  struct obj_values exe_after, mod1_after, mod2_after;
+};
+
+/* Store the addresses of variables prefixed by PFX in the structure
+   pointed to by DST.  */
+#define STORE_ADDRS(DST, PFX)				\
+  do							\
+    {							\
+      (DST)->addr_tdata1 = (uintptr_t) &PFX ## _tdata1;	\
+      (DST)->addr_tdata2 = (uintptr_t) &PFX ## _tdata2;	\
+      (DST)->addr_tdata3 = (uintptr_t) &PFX ## _tdata3;	\
+      (DST)->addr_tbss1 = (uintptr_t) &PFX ## _tbss1;	\
+      (DST)->addr_tbss2 = (uintptr_t) &PFX ## _tbss2;	\
+      (DST)->addr_tbss3 = (uintptr_t) &PFX ## _tbss3;	\
+    }							\
+  while (0)
+
+/* Store the values of variables prefixed by PFX in the structure
+   pointed to by DST.  */
+#define STORE_VALUES(DST, PFX)			\
+  do						\
+    {						\
+      (DST)->val_tdata1 = PFX ## _tdata1;	\
+      (DST)->val_tdata2 = PFX ## _tdata2;	\
+      (DST)->val_tdata3 = PFX ## _tdata3;	\
+      (DST)->val_tbss1 = PFX ## _tbss1;		\
+      (DST)->val_tbss2 = PFX ## _tbss2;		\
+      (DST)->val_tbss3 = PFX ## _tbss3;		\
+    }						\
+  while (0)
+
+/* Overwrite the values of variables prefixed by PFX with values
+   starting with VAL.  */
+#define OVERWRITE_VALUES(PFX, VAL)		\
+  do						\
+    {						\
+      PFX ## _tdata1 = (VAL);			\
+      PFX ## _tdata2 = (VAL) + 1;		\
+      PFX ## _tdata3 = (VAL) + 2;		\
+      PFX ## _tbss1 = (VAL) + 3;		\
+      PFX ## _tbss2 = (VAL) + 4;		\
+      PFX ## _tbss3 = (VAL) + 5;		\
+    }						\
+  while (0)
+
+void test_mod1 (struct one_thread_data *data, int base_val);
+void test_mod2 (struct one_thread_data *data, int base_val);
+
+#endif /* TST_TLS22_H */

-- 
Joseph S. Myers
josmyers@redhat.com



More information about the Libc-alpha mailing list