about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile33
-rw-r--r--elf/libc_early_init.c5
-rw-r--r--elf/tst-single_threaded-mod1.c25
-rw-r--r--elf/tst-single_threaded-mod2.c25
-rw-r--r--elf/tst-single_threaded-mod3.c25
-rw-r--r--elf/tst-single_threaded-mod4.c25
-rw-r--r--elf/tst-single_threaded-pthread-static.c86
-rw-r--r--elf/tst-single_threaded-pthread.c174
-rw-r--r--elf/tst-single_threaded-static-dlopen.c57
-rw-r--r--elf/tst-single_threaded-static.c29
-rw-r--r--elf/tst-single_threaded.c70
11 files changed, 548 insertions, 6 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 6fe1df90bb..81a696c3ef 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -155,7 +155,9 @@ endif
 tests-static-normal := tst-leaks1-static tst-array1-static tst-array5-static \
 	       tst-dl-iter-static \
 	       tst-tlsalign-static tst-tlsalign-extern-static \
-	       tst-linkall-static tst-env-setuid tst-env-setuid-tunables
+	       tst-linkall-static tst-env-setuid tst-env-setuid-tunables \
+	       tst-single_threaded-static tst-single_threaded-pthread-static
+
 tests-static-internal := tst-tls1-static tst-tls2-static \
 	       tst-ptrguard1-static tst-stackguard1-static \
 	       tst-tls1-static-non-pie tst-libc_dlvsym-static
@@ -174,9 +176,11 @@ tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
 tests-static := $(tests-static-normal) $(tests-static-internal)
 
 ifeq (yes,$(build-shared))
-tests-static += tst-tls9-static
-tst-tls9-static-ENV = \
-       LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+tests-static += tst-tls9-static tst-single_threaded-static-dlopen
+static-dlopen-environment = \
+  LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+tst-tls9-static-ENV = $(static-dlopen-environment)
+tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
 
 tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 constload1 order noload filter \
@@ -204,7 +208,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail \
 	 tst-dlopenfail-2 \
 	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
-	 tst-audit14 tst-audit15 tst-audit16
+	 tst-audit14 tst-audit15 tst-audit16 \
+	 tst-single_threaded tst-single_threaded-pthread
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -317,7 +322,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
 		tst-dlopenfailmod3 tst-ldconfig-ld-mod \
 		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee \
-		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3
+		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
+		tst-single_threaded-mod1 tst-single_threaded-mod2 \
+		tst-single_threaded-mod3 tst-single_threaded-mod4
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
 modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
@@ -1748,3 +1755,17 @@ $(objpfx)tst-auxobj: $(objpfx)tst-filterobj-aux.so
 $(objpfx)tst-auxobj-dlopen: $(libdl)
 $(objpfx)tst-auxobj.out: $(objpfx)tst-filterobj-filtee.so
 $(objpfx)tst-auxobj-dlopen.out: $(objpfx)tst-filterobj-filtee.so
+
+$(objpfx)tst-single_threaded: $(objpfx)tst-single_threaded-mod1.so $(libdl)
+$(objpfx)tst-single_threaded.out: \
+  $(objpfx)tst-single_threaded-mod2.so $(objpfx)tst-single_threaded-mod3.so
+$(objpfx)tst-single_threaded-static-dlopen: \
+  $(objpfx)tst-single_threaded-mod1.o $(common-objpfx)dlfcn/libdl.a
+$(objpfx)tst-single_threaded-static-dlopen.out: \
+  $(objpfx)tst-single_threaded-mod2.so
+$(objpfx)tst-single_threaded-pthread: \
+  $(objpfx)tst-single_threaded-mod1.so $(libdl) $(shared-thread-library)
+$(objpfx)tst-single_threaded-pthread.out: \
+  $(objpfx)tst-single_threaded-mod2.so $(objpfx)tst-single_threaded-mod3.so \
+  $(objpfx)tst-single_threaded-mod4.so
+$(objpfx)tst-single_threaded-pthread-static: $(static-thread-library)
diff --git a/elf/libc_early_init.c b/elf/libc_early_init.c
index f0fcf6448e..86da66d5e0 100644
--- a/elf/libc_early_init.c
+++ b/elf/libc_early_init.c
@@ -19,13 +19,18 @@
 #include <ctype.h>
 #include <libc-early-init.h>
 #include <rseq-internal.h>
+#include <sys/single_threaded.h>
 
 void
 __libc_early_init (_Bool initial)
 {
   /* Initialize ctype data.  */
   __ctype_init ();
+
   /* Register rseq ABI to the kernel for the main program's libc.   */
   if (initial)
     rseq_register_current_thread ();
+
+  /* Only the outer namespace is marked as single-threaded.  */
+  __libc_single_threaded = initial;
 }
diff --git a/elf/tst-single_threaded-mod1.c b/elf/tst-single_threaded-mod1.c
new file mode 100644
index 0000000000..a66de65679
--- /dev/null
+++ b/elf/tst-single_threaded-mod1.c
@@ -0,0 +1,25 @@
+/* Test support for single-thread optimizations.  Shared object 1.
+   Copyright (C) 2020 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 <sys/single_threaded.h>
+
+_Bool
+single_threaded_1 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod2.c b/elf/tst-single_threaded-mod2.c
new file mode 100644
index 0000000000..74d1e2a6f4
--- /dev/null
+++ b/elf/tst-single_threaded-mod2.c
@@ -0,0 +1,25 @@
+/* Test support for single-thread optimizations.  Shared object 2.
+   Copyright (C) 2020 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 <sys/single_threaded.h>
+
+_Bool
+single_threaded_2 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod3.c b/elf/tst-single_threaded-mod3.c
new file mode 100644
index 0000000000..ac5e49cfa5
--- /dev/null
+++ b/elf/tst-single_threaded-mod3.c
@@ -0,0 +1,25 @@
+/* Test support for single-thread optimizations.  Shared object 3.
+   Copyright (C) 2020 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 <sys/single_threaded.h>
+
+_Bool
+single_threaded_3 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod4.c b/elf/tst-single_threaded-mod4.c
new file mode 100644
index 0000000000..9f3a8ecef1
--- /dev/null
+++ b/elf/tst-single_threaded-mod4.c
@@ -0,0 +1,25 @@
+/* Test support for single-thread optimizations.  Shared object 4.
+   Copyright (C) 2020 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 <sys/single_threaded.h>
+
+_Bool
+single_threaded_4 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-pthread-static.c b/elf/tst-single_threaded-pthread-static.c
new file mode 100644
index 0000000000..9f383a0191
--- /dev/null
+++ b/elf/tst-single_threaded-pthread-static.c
@@ -0,0 +1,86 @@
+/* Test support for single-thread optimizations.  With threads, static version.
+   Copyright (C) 2020 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/>.  */
+
+/* This test is a stripped-down version of
+   tst-single_threaded-pthread.c, without any loading of dynamic
+   objects.  */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xthread.h>
+#include <sys/single_threaded.h>
+
+/* First barrier synchronizes main thread, thread 1, thread 2.  */
+static pthread_barrier_t barrier1;
+
+/* Second barrier synchronizes main thread, thread 2.  */
+static pthread_barrier_t barrier2;
+
+static void *
+threadfunc (void *closure)
+{
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Wait for the main thread and the other thread.  */
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Second thread waits on second barrier, too.  */
+  if (closure != NULL)
+    xpthread_barrier_wait (&barrier2);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+
+  /* Two threads plus main thread.  */
+  xpthread_barrier_init (&barrier1, NULL, 3);
+
+  /* Main thread and second thread.  */
+  xpthread_barrier_init (&barrier2, NULL, 2);
+
+  pthread_t thr1 = xpthread_create (NULL, threadfunc, NULL);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  pthread_t thr2 = xpthread_create (NULL, threadfunc, &thr2);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Join first thread.  This should not bring us back into
+     single-threaded mode.  */
+  xpthread_join (thr1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* We may be back in single-threaded mode after joining both
+     threads, but this is not guaranteed.  */
+  xpthread_barrier_wait (&barrier2);
+  xpthread_join (thr2);
+  printf ("info: __libc_single_threaded after joining all threads: %d\n",
+          __libc_single_threaded);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-pthread.c b/elf/tst-single_threaded-pthread.c
new file mode 100644
index 0000000000..b7112b03c1
--- /dev/null
+++ b/elf/tst-single_threaded-pthread.c
@@ -0,0 +1,174 @@
+/* Test support for single-thread optimizations.  With threads.
+   Copyright (C) 2020 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 <stddef.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xdlfcn.h>
+#include <support/xthread.h>
+#include <sys/single_threaded.h>
+
+/* First barrier synchronizes main thread, thread 1, thread 2.  */
+static pthread_barrier_t barrier1;
+
+/* Second barrier synchronizes main thread, thread 2.  */
+static pthread_barrier_t barrier2;
+
+/* Defined in tst-single-threaded-mod1.so.  */
+_Bool single_threaded_1 (void);
+
+/* Initialized via dlsym.  */
+static _Bool (*single_threaded_2) (void);
+static _Bool (*single_threaded_3) (void);
+static _Bool (*single_threaded_4) (void);
+
+static void *
+threadfunc (void *closure)
+{
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+
+  /* Wait until the main thread loads more functions.  */
+  xpthread_barrier_wait (&barrier1);
+
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+
+  /* Second thread waits on second barrier, too.  */
+  if (closure != NULL)
+    xpthread_barrier_wait (&barrier2);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+
+  return NULL;
+}
+
+/* Used for closure arguments to the subprocess function.  */
+static char expected_false = 0;
+static char expected_true = 1;
+
+/* A subprocess inherits currently inherits the single-threaded state
+   of the parent process.  */
+static void
+subprocess (void *closure)
+{
+  const char *expected = closure;
+  TEST_COMPARE (__libc_single_threaded, *expected);
+  TEST_COMPARE (single_threaded_1 (), *expected);
+  if (single_threaded_2 != NULL)
+    TEST_COMPARE (single_threaded_2 (), *expected);
+  if (single_threaded_3 != NULL)
+    TEST_COMPARE (single_threaded_3 (), *expected);
+  if (single_threaded_4 != NULL)
+    TEST_VERIFY (!single_threaded_4 ());
+}
+
+static int
+do_test (void)
+{
+  printf ("info: main __libc_single_threaded address: %p\n",
+          &__libc_single_threaded);
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  support_isolate_in_subprocess (subprocess, &expected_true);
+
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  single_threaded_2 = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (single_threaded_2 ());
+
+  /* Two threads plus main thread.  */
+  xpthread_barrier_init (&barrier1, NULL, 3);
+
+  /* Main thread and second thread.  */
+  xpthread_barrier_init (&barrier2, NULL, 2);
+
+  pthread_t thr1 = xpthread_create (NULL, threadfunc, NULL);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  pthread_t thr2 = xpthread_create (NULL, threadfunc, &thr2);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  /* Delayed library load, while already multi-threaded.  */
+  void *handle_mod3 = xdlopen ("tst-single_threaded-mod3.so", RTLD_LAZY);
+  single_threaded_3 = xdlsym (handle_mod3, "single_threaded_3");
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  /* Same with dlmopen.  */
+  void *handle_mod4 = dlmopen (LM_ID_NEWLM, "tst-single_threaded-mod4.so",
+                               RTLD_LAZY);
+  single_threaded_4 = xdlsym (handle_mod4, "single_threaded_4");
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  /* Run the newly loaded functions from the other threads as
+     well.  */
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  /* Join first thread.  This should not bring us back into
+     single-threaded mode.  */
+  xpthread_join (thr1);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, &expected_false);
+
+  /* We may be back in single-threaded mode after joining both
+     threads, but this is not guaranteed.  */
+  xpthread_barrier_wait (&barrier2);
+  xpthread_join (thr2);
+  printf ("info: __libc_single_threaded after joining all threads: %d\n",
+          __libc_single_threaded);
+
+  xdlclose (handle_mod4);
+  xdlclose (handle_mod3);
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-static-dlopen.c b/elf/tst-single_threaded-static-dlopen.c
new file mode 100644
index 0000000000..f33588312d
--- /dev/null
+++ b/elf/tst-single_threaded-static-dlopen.c
@@ -0,0 +1,57 @@
+/* Test support for single-thread optimizations.  No threads, static dlopen.
+   Copyright (C) 2020 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/>.  */
+
+/* In a static dlopen scenario, the single-threaded optimization is
+   not possible because their is no globally shared dynamic linker
+   across all namespaces.  */
+
+#include <stddef.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <sys/single_threaded.h>
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+
+  /* Defined in tst-single-threaded-mod1.o.  */
+  extern _Bool single_threaded_1 (void);
+  TEST_VERIFY (single_threaded_1 ());
+
+  /* A failed dlopen does not change the multi-threaded status.  */
+  TEST_VERIFY (dlopen ("tst-single_threaded-does-not-exist.so", RTLD_LAZY)
+               == NULL);
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+
+  /* And neither does a successful dlopen for outer (static) libc.  */
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  _Bool (*single_threaded_2) (void)
+    = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  /* The inner libc always assumes multi-threaded use.  */
+  TEST_VERIFY (!single_threaded_2 ());
+
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-static.c b/elf/tst-single_threaded-static.c
new file mode 100644
index 0000000000..593da8866d
--- /dev/null
+++ b/elf/tst-single_threaded-static.c
@@ -0,0 +1,29 @@
+/* Test support for single-thread optimizations.  Static, no threads.
+   Copyright (C) 2020 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 <sys/single_threaded.h>
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded.c b/elf/tst-single_threaded.c
new file mode 100644
index 0000000000..2c36342779
--- /dev/null
+++ b/elf/tst-single_threaded.c
@@ -0,0 +1,70 @@
+/* Test support for single-thread optimizations.  No threads.
+   Copyright (C) 2020 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 <stddef.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xdlfcn.h>
+#include <sys/single_threaded.h>
+
+/* Defined in tst-single-threaded-mod1.so.  */
+extern _Bool single_threaded_1 (void);
+
+/* Initialized via dlsym.  */
+_Bool (*single_threaded_2) (void);
+_Bool (*single_threaded_3) (void);
+
+static void
+subprocess (void *closure)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  if (single_threaded_2 != NULL)
+    TEST_VERIFY (single_threaded_2 ());
+  if (single_threaded_3 != NULL)
+    TEST_VERIFY (!single_threaded_3 ());
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  single_threaded_2 = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* The current implementation treats the inner namespace as
+     multi-threaded.  */
+  void *handle_mod3 = dlmopen (LM_ID_NEWLM, "tst-single_threaded-mod3.so",
+                               RTLD_LAZY);
+  single_threaded_3 = xdlsym (handle_mod3, "single_threaded_3");
+  TEST_VERIFY (!single_threaded_3 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  xdlclose (handle_mod3);
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>