aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/tst-rseq.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/tst-rseq.c')
-rw-r--r--sysdeps/unix/sysv/linux/tst-rseq.c97
1 files changed, 81 insertions, 16 deletions
diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c
index b152280..00181cf 100644
--- a/sysdeps/unix/sysv/linux/tst-rseq.c
+++ b/sysdeps/unix/sysv/linux/tst-rseq.c
@@ -19,6 +19,8 @@
not linked against libpthread. */
#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xthread.h>
#include <stdio.h>
#include <sys/rseq.h>
#include <unistd.h>
@@ -32,25 +34,82 @@
# include <sys/auxv.h>
# include <thread_pointer.h>
# include <tls.h>
+# include <dl-tls.h>
+# include <sys/auxv.h>
# include "tst-rseq.h"
+/* Used to check if the address of the rseq area comes before or after the tls
+ blocks depending on the TLS model. */
+static __thread char tls_var __attribute__ ((tls_model ("initial-exec")));
+
static void
do_rseq_main_test (void)
{
- struct pthread *pd = THREAD_SELF;
- size_t rseq_feature_size = MIN (MAX (getauxval (AT_RSEQ_FEATURE_SIZE),
- RSEQ_AREA_SIZE_INITIAL_USED),
- RSEQ_AREA_SIZE_MAX_USED);
+ size_t rseq_align = MAX (getauxval (AT_RSEQ_ALIGN), RSEQ_MIN_ALIGN);
+ size_t rseq_feature_size = MAX (getauxval (AT_RSEQ_FEATURE_SIZE),
+ RSEQ_AREA_SIZE_INITIAL_USED);
+ size_t rseq_alloc_size = roundup (MAX (rseq_feature_size,
+ RSEQ_AREA_SIZE_INITIAL_USED), rseq_align);
+ struct rseq *rseq_abi = __thread_pointer () + __rseq_offset;
TEST_VERIFY_EXIT (rseq_thread_registered ());
+
+ /* __rseq_flags is unused and should always be '0'. */
TEST_COMPARE (__rseq_flags, 0);
- TEST_VERIFY ((char *) __thread_pointer () + __rseq_offset
- == (char *) &pd->rseq_area);
+
+ /* When rseq is registered, __rseq_size should report the feature size. */
TEST_COMPARE (__rseq_size, rseq_feature_size);
+
+ /* When rseq is registered, the 'cpu_id' field should be set to a valid cpu
+ * number. */
+ TEST_VERIFY ((int32_t) rseq_abi->cpu_id >= 0);
+
+ /* The rseq area address must be aligned. */
+ TEST_VERIFY (((unsigned long) rseq_abi % rseq_align) == 0);
+
+#if TLS_TCB_AT_TP
+ /* The rseq area block should come before the thread pointer and be at least
+ 32 bytes. */
+ TEST_VERIFY (__rseq_offset <= -RSEQ_AREA_SIZE_INITIAL);
+
+ /* The rseq area block should come before TLS variables. */
+ TEST_VERIFY ((intptr_t) rseq_abi < (intptr_t) &tls_var);
+#elif TLS_DTV_AT_TP
+ /* The rseq area block should come after the TCB, add the TLS block offset to
+ the rseq offset to get a value relative to the TCB and test that it's
+ non-negative. */
+ TEST_VERIFY (__rseq_offset + TLS_TP_OFFSET >= 0);
+
+ /* The rseq area block should come after TLS variables. */
+ TEST_VERIFY ((intptr_t) rseq_abi > (intptr_t) &tls_var);
+#else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+#endif
+
+ /* Test a rseq registration with the same arguments as the internal
+ registration which should fail with errno == EBUSY. */
+ TEST_VERIFY (((unsigned long) rseq_abi % rseq_align) == 0);
+ TEST_VERIFY (__rseq_size <= rseq_alloc_size);
+ int ret = syscall (__NR_rseq, rseq_abi, rseq_alloc_size, 0, RSEQ_SIG);
+ TEST_VERIFY (ret != 0);
+ TEST_COMPARE (errno, EBUSY);
+}
+
+static void *
+thread_func (void *ignored)
+{
+ do_rseq_main_test ();
+ return NULL;
}
static void
-do_rseq_test (void)
+proc_func (void *ignored)
+{
+ do_rseq_main_test ();
+}
+
+static int
+do_test (void)
{
if (!rseq_available ())
{
@@ -62,21 +121,27 @@ do_rseq_test (void)
printf ("info: getauxval (AT_RSEQ_FEATURE_SIZE): %ld\n",
getauxval (AT_RSEQ_FEATURE_SIZE));
printf ("info: getauxval (AT_RSEQ_ALIGN): %ld\n", getauxval (AT_RSEQ_ALIGN));
+
+ puts ("info: checking main thread");
+ do_rseq_main_test ();
+
+ puts ("info: checking main thread (2)");
do_rseq_main_test ();
+
+ puts ("info: checking new thread");
+ xpthread_join (xpthread_create (NULL, thread_func, NULL));
+
+ puts ("info: checking subprocess");
+ support_isolate_in_subprocess (proc_func, NULL);
+
+ return 0;
}
#else /* RSEQ_SIG */
-static void
-do_rseq_test (void)
-{
- FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test");
-}
-#endif /* RSEQ_SIG */
-
static int
do_test (void)
{
- do_rseq_test ();
- return 0;
+ FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test");
}
+#endif /* RSEQ_SIG */
#include <support/test-driver.c>