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

H.J. Lu hjl.tools@gmail.com
Fri Apr 17 07:44:57 GMT 2026


On Fri, Apr 17, 2026 at 12:23 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Fri, Apr 17, 2026 at 11:56 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>
>
> I'd like to combine your test change with my patch to enable
> THP segment load with madvise enabled THP:
>
> https://patchwork.sourceware.org/project/glibc/list/?series=59892
>
> Can you provide feedback on my patch?

The updated patch is at

https://patchwork.sourceware.org/project/glibc/patch/CAMe9rOryN0j-SZT_C4EYzEHt+B7kQTvC5t4GaQ3H8dPXG+aejg@mail.gmail.com/

> Thanks.
>
> > ---
> > Changes since [v9]:
> >   * Use FAIL_UNSUPPORTED instead of printf.
> >   * Skip tests when THP size exceeds MAX_THP_PAGESIZE.
> >   * Add dependency of tst-thp-align on tst-thp-size-mod.
> >   * Rebase onto current master.
> >
> > [v9]: https://sourceware.org/pipermail/libc-alpha/2026-April/176598.html
> > ---
> >  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(+)
> >  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 b06afbdeaab..0d47e9af57e 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 00000000000..0b3f18e000f
> > --- /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 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.



-- 
H.J.


More information about the Libc-alpha mailing list