[glibc] elf: Add test for THP alignment of large load segments

Wilco Dijkstra via Glibc-cvs <[email protected]>
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=bb5c2cc2ebefc960fe389dd2e4619042e8ca6078

commit bb5c2cc2ebefc960fe389dd2e4619042e8ca6078
Author: WANG Rui <[email protected]>
Date:   Mon Apr 20 10:54:13 2026 +0000

    elf: Add test for THP alignment of large load segments
    
    Add a new test to verify that large executable PT_LOAD segments are
    mapped at addresses aligned to the THP size when the glibc tunable
    glibc.elf.thp=1 is enabled and the system is configured to use THP
    in "always" mode.
    
    The test loads a shared object with a sufficiently large executable
    segment via dlopen and inspects /proc/self/maps to check that the
    mapping address is aligned to the THP page size reported by the kernel.
    
    The test is skipped if the THP size cannot be determined or if THP is
    not enabled in "always" mode.
    
    Signed-off-by: WANG Rui <[email protected]>
    Reviewed-by: Wilco Dijkstra  <[email protected]>

Diff:
---
 sysdeps/unix/sysv/linux/Makefile           |   5 +
 sysdeps/unix/sysv/linux/tst-thp-align.c    | 155 +++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/tst-thp-size-mod.S |  22 ++++
 3 files changed, 182 insertions(+)

diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index b06afbdeaa..0d47e9af57 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -698,6 +698,7 @@ $(objpfx)pldd: $(objpfx)xmalloc.o
 tests += \
   tst-rseq-tls-range \
   tst-rseq-tls-range-4096 \
+  tst-thp-align \
 # tests
 tests-static += \
   tst-rseq-tls-range-4096-static \
@@ -705,17 +706,21 @@ tests-static += \
 # tests-static
 modules-names += \
   tst-rseq-tls-range-mod \
+  tst-thp-size-mod \
 # modules-names
 CFLAGS-tst-rseq-tls-range.c += -DMAIN_TLS_ALIGN=4
 CFLAGS-tst-rseq-tls-range-4096.c += -DMAIN_TLS_ALIGN=4096
 CFLAGS-tst-rseq-tls-range-static.c += -DMAIN_TLS_ALIGN=4
 CFLAGS-tst-rseq-tls-range-4096-static.c += -DMAIN_TLS_ALIGN=4096
+LDFLAGS-tst-thp-size-mod.so += -Wl,-z,noseparate-code
 $(objpfx)tst-rseq-tls-range.out: $(objpfx)tst-rseq-tls-range-mod.so
 $(objpfx)tst-rseq-tls-range-4096.out: $(objpfx)tst-rseq-tls-range-mod.so
 $(objpfx)tst-rseq-tls-range-static.out: $(objpfx)tst-rseq-tls-range-mod.so
 $(objpfx)tst-rseq-tls-range-4096-static.out: $(objpfx)tst-rseq-tls-range-mod.so
+$(objpfx)tst-thp-align.out: $(objpfx)tst-thp-size-mod.so
 tst-rseq-tls-range-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
 tst-rseq-tls-range-4096-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
+tst-thp-align-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
 
 test-internal-extras += tst-nolink-libc
 ifeq ($(run-built-tests),yes)
diff --git a/sysdeps/unix/sysv/linux/tst-thp-align.c b/sysdeps/unix/sysv/linux/tst-thp-align.c
new file mode 100644
index 0000000000..0b3f18e000
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-thp-align.c
@@ -0,0 +1,155 @@
+/* Test the THP compatible alignment of PT_LOAD segments.
+
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   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 <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <intprops.h>
+#include <inttypes.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+#define THP_SIZE_MOD_NAME "tst-thp-size-mod.so"
+#define MAX_THP_PAGESIZE (32 * 1024 * 1024)
+
+enum thp_mode_t
+{
+  thp_mode_always,
+  thp_mode_madvise,
+  thp_mode_never,
+  thp_mode_not_supported
+};
+
+static unsigned long int
+get_thp_size (void)
+{
+  int fd = open ("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size",
+                 O_RDONLY, 0);
+  if (fd == -1)
+    return 0;
+
+  char str[INT_BUFSIZE_BOUND (unsigned long int)];
+  ssize_t s = read (fd, str, sizeof (str));
+  close (fd);
+  if (s < 0)
+    return 0;
+
+  unsigned long int r = 0;
+  for (ssize_t i = 0; i < s; i++)
+    {
+      if (str[i] == '\n')
+    break;
+      r *= 10;
+      r += str[i] - '0';
+    }
+  return r;
+}
+
+static enum thp_mode_t
+get_thp_mode (void)
+{
+  int fd = open ("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY, 0);
+  if (fd == -1)
+    return thp_mode_not_supported;
+
+  static const char mode_always[]  = "[always] madvise never\n";
+  static const char mode_madvise[] = "always [madvise] never\n";
+  static const char mode_never[]   = "always madvise [never]\n";
+
+  char str[sizeof(mode_always)];
+  ssize_t s = read (fd, str, sizeof (str));
+  if (s >= sizeof str || s < 0)
+    return thp_mode_not_supported;
+  str[s] = '\0';
+  close (fd);
+
+  if (s == sizeof (mode_always) - 1)
+    {
+      if (strcmp (str, mode_always) == 0)
+    return thp_mode_always;
+      else if (strcmp (str, mode_madvise) == 0)
+    return thp_mode_madvise;
+      else if (strcmp (str, mode_never) == 0)
+    return thp_mode_never;
+    }
+  return thp_mode_not_supported;
+}
+
+static void
+check_align (void)
+{
+  unsigned long int thp_size = get_thp_size ();
+  enum thp_mode_t thp_mode = get_thp_mode ();
+
+  if (thp_size == 0)
+    {
+      FAIL_UNSUPPORTED ("unable to get THP size.\n");
+      return;
+    }
+
+  if (thp_size > MAX_THP_PAGESIZE)
+    {
+      FAIL_UNSUPPORTED ("THP size exceeds MAX_THP_PAGESIZE.\n");
+      return;
+    }
+
+  if (thp_mode != thp_mode_always)
+    {
+      FAIL_UNSUPPORTED ("THP mode is not always.\n");
+      return;
+    }
+
+  FILE *f = xfopen ("/proc/self/maps", "r");
+  char *line = NULL;
+  size_t len;
+
+  while (xgetline (&line, &len, f))
+    {
+      uintptr_t from, to;
+      char *prot = NULL, *path = NULL;
+      int r = sscanf (line, "%" SCNxPTR "-%" SCNxPTR "%ms%*s%*s%*s%ms",
+                      &from, &to, &prot, &path);
+
+      TEST_VERIFY (r == 3 || r == 4);
+
+      if (strstr (prot, "x") && strstr (path, THP_SIZE_MOD_NAME))
+        TEST_COMPARE (from % thp_size, 0);
+
+      free (path);
+    }
+
+  free (line);
+  xfclose (f);
+}
+
+static int
+do_test (void)
+{
+  void *dl;
+
+  dl = xdlopen (THP_SIZE_MOD_NAME, RTLD_NOW);
+  check_align ();
+  xdlclose (dl);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/tst-thp-size-mod.S b/sysdeps/unix/sysv/linux/tst-thp-size-mod.S
new file mode 100644
index 0000000000..3084c2d0e2
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-thp-size-mod.S
@@ -0,0 +1,22 @@
+/* A module that provides THP code size for testing.
+
+   Copyright (C) 2026 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/>.  */
+
+    .globl thp_size
+thp_size:
+    .space 32 * 1024 * 1024
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.