[PATCH v9 6/6] elf: Add test for THP alignment of large load segments
H.J. Lu
hjl.tools@gmail.com
Tue Apr 14 20:07:23 GMT 2026
On Mon, Apr 13, 2026 at 1:44 AM WANG Rui <wangrui@loongson.cn> wrote:
>
> 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 <wangrui@loongson.cn>
> ---
> sysdeps/unix/sysv/linux/Makefile | 7 +-
> sysdeps/unix/sysv/linux/tst-thp-align.c | 149 +++++++++++++++++++++
> sysdeps/unix/sysv/linux/tst-thp-size-mod.S | 22 +++
> 3 files changed, 176 insertions(+), 2 deletions(-)
> create mode 100644 sysdeps/unix/sysv/linux/tst-thp-align.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-thp-size-mod.S
>
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index 9879fec9a93..6e237dd4934 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -695,19 +695,22 @@ install-bin += \
>
> $(objpfx)pldd: $(objpfx)xmalloc.o
>
> -tests += tst-rseq-tls-range tst-rseq-tls-range-4096
> +tests += tst-rseq-tls-range tst-rseq-tls-range-4096 tst-thp-align
> tests-static += tst-rseq-tls-range-static tst-rseq-tls-range-4096-static
> -modules-names += tst-rseq-tls-range-mod
> +modules-names += tst-rseq-tls-range-mod tst-thp-size-mod
> 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
> 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
> +tst-thp-align-modules = tst-thp-size-mod
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What is this for? tst-thp-align.out should depend on tst-thp-size-mod.so.
>
> 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 00000000000..ec0a8d84a85
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-thp-align.c
> @@ -0,0 +1,149 @@
> +/* 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 <stdio.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"
> +
> +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)
> + {
> + printf ("Skipping test: unable to get THP size.\n");
> + return;
> + }
> +
> + if (thp_mode != thp_mode_always)
> + {
> + printf ("Skipping test: 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 00000000000..3084c2d0e2c
> --- /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
> --
> 2.53.0
>
--
H.J.
More information about the Libc-alpha
mailing list