@@ -303,6 +303,7 @@ tests := \
tst-array4 \
tst-array5 \
tst-auxv \
+ tst-decorate-maps \
tst-dl-hash \
tst-leaks1 \
tst-stringtable \
@@ -3018,3 +3019,5 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
$(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
$(objpfx)tst-dlclose-lazy.out: \
$(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
+
+$(objpfx)tst-decorate-maps: $(shared-thread-library)
new file mode 100644
@@ -0,0 +1,160 @@
+/* Check the VMA name decoration.
+ Copyright (C) 2023 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 <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+#ifndef MAP_STACK
+# define MAP_STACK 0
+#endif
+
+static pthread_barrier_t b;
+
+static void *
+tf (void *closure)
+{
+ /* Wait the thread startup, so thread stack is allocated. */
+ xpthread_barrier_wait (&b);
+
+ /* Wait the test to read the process mapiping. */
+ xpthread_barrier_wait (&b);
+
+ return NULL;
+}
+
+struct proc_maps_t
+{
+ int n_def_threads;
+ int n_user_threads;
+};
+
+static struct proc_maps_t
+read_proc_maps (void)
+{
+ if (test_verbose)
+ printf ("=== print process %jd memory mapping ===\n",
+ (intmax_t) getpid ());
+ struct proc_maps_t r = { 0 };
+
+ FILE *f = xfopen ("/proc/self/maps", "r");
+ char *line = NULL;
+ size_t line_len = 0;
+ while (xgetline (&line, &line_len, f))
+ {
+ if (test_verbose)
+ printf ("%s", line);
+ if (strstr (line, "[anon: glibc: pthread stack:") != NULL)
+ r.n_def_threads++;
+ else if (strstr (line, "[anon: glibc: pthread user stack:") != NULL)
+ r.n_user_threads++;
+ }
+ free (line);
+ xfclose (f);
+
+ if (test_verbose)
+ printf ("===\n");
+ return r;
+}
+
+static void
+do_test_threads (bool set_guard)
+{
+ enum
+ {
+ num_def_threads = 8,
+ num_user_threads = 2,
+ num_threads = num_def_threads + num_user_threads,
+ };
+
+ xpthread_barrier_init (&b, NULL, num_threads + 1);
+
+ pthread_t thr[num_threads];
+ {
+ int i = 0;
+ for (; i < num_threads - num_user_threads; i++)
+ {
+ pthread_attr_t attr;
+ xpthread_attr_init (&attr);
+ /* The guard page is not annotated. */
+ if (!set_guard)
+ xpthread_attr_setguardsize (&attr, 0);
+ thr[i] = xpthread_create (&attr, tf, NULL);
+ xpthread_attr_destroy (&attr);
+ }
+ for (; i < num_threads; i++)
+ {
+ pthread_attr_t attr;
+ xpthread_attr_init (&attr);
+ size_t stacksize = support_small_thread_stack_size ();
+ void *stack = xmmap (0,
+ stacksize,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK,
+ -1);
+ xpthread_attr_setstack (&attr, stack, stacksize);
+ if (!set_guard)
+ xpthread_attr_setguardsize (&attr, 0);
+ thr[i] = xpthread_create (&attr, tf, NULL);
+ xpthread_attr_destroy (&attr);
+ }
+ }
+
+ /* Wait all threads to finshed statup and stack allocation. */
+ xpthread_barrier_wait (&b);
+
+ {
+ struct proc_maps_t r = read_proc_maps ();
+ TEST_COMPARE (r.n_def_threads, num_def_threads);
+ TEST_COMPARE (r.n_user_threads, num_user_threads);
+ }
+
+ /* Let the threads finish. */
+ xpthread_barrier_wait (&b);
+
+ for (int i = 0; i < num_threads; i++)
+ xpthread_join (thr[i]);
+
+ {
+ struct proc_maps_t r = read_proc_maps ();
+ TEST_COMPARE (r.n_def_threads, 0);
+ TEST_COMPARE (r.n_user_threads, 0);
+ }
+}
+
+static int
+do_test (void)
+{
+ support_need_proc ("Reads /proc/self/maps to get stack names.");
+
+ if (!support_set_vma_name ())
+ FAIL_UNSUPPORTED ("kernel does not support PR_SET_VMA_ANON_NAME");
+
+ do_test_threads (false);
+ do_test_threads (true);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -33,6 +33,8 @@
#include <nptl-stack.h>
#include <libc-lock.h>
#include <tls-internal.h>
+#include <intprops.h>
+#include <setvmaname.h>
/* Default alignment of stack. */
#ifndef STACK_ALIGN
@@ -577,3 +579,41 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
return 0;
}
+
+/* Maximum supported name from initial kernel support, not exported
+ by user API. */
+#define ANON_VMA_NAME_MAX_LEN 80
+
+#define SET_STACK_NAME(__prefix, __stack, __stacksize, __tid) \
+ ({ \
+ char __stack_name[sizeof (__prefix) + \
+ INT_BUFSIZE_BOUND (unsigned int)]; \
+ _Static_assert (sizeof __stack_name <= ANON_VMA_NAME_MAX_LEN, \
+ "VMA name size larger than maximum supported"); \
+ __snprintf (__stack_name, sizeof (__stack_name), __prefix "%u", \
+ (unsigned int) __tid); \
+ __set_vma_name (__stack, __stacksize, __stack_name); \
+ })
+
+/* Add or remove an associated name to the PD VMA stack. */
+static void
+name_stack_maps (struct pthread *pd, bool set)
+{
+#if _STACK_GROWS_DOWN && !defined(NEED_SEPARATE_REGISTER_STACK)
+ void *stack = pd->stackblock + pd->guardsize;
+#else
+ void *stack = pd->stackblock;
+#endif
+ size_t stacksize = pd->stackblock_size - pd->guardsize;
+
+ if (!set)
+ __set_vma_name (stack, stacksize, NULL);
+ else
+ {
+ unsigned int tid = pd->tid;
+ if (pd->user_stack)
+ SET_STACK_NAME (" glibc: pthread user stack: ", stack, stacksize, tid);
+ else
+ SET_STACK_NAME (" glibc: pthread stack: ", stack, stacksize, tid);
+ }
+}
@@ -369,6 +369,9 @@ start_thread (void *arg)
/* Initialize pointers to locale data. */
__ctype_init ();
+ /* Name the thread stack if kernel supports it. */
+ name_stack_maps (pd, true);
+
/* Register rseq TLS to the kernel. */
{
bool do_rseq = THREAD_GETMEM (pd, flags) & ATTR_FLAG_DO_RSEQ;
@@ -571,6 +574,9 @@ start_thread (void *arg)
/* Free the TCB. */
__nptl_free_tcb (pd);
+ /* Remove the associated name from the thread stack. */
+ name_stack_maps (pd, false);
+
out:
/* We cannot call '_exit' here. '_exit' will terminate the process.