[PATCH v12] elf: Support THP segment load with madvise enabled THP
H.J. Lu
hjl.tools@gmail.com
Mon Jun 15 23:07:59 GMT 2026
On Fri, Jun 5, 2026 at 12:33 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
>
> On Thu, Jun 4, 2026, 10:13 PM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 29/05/26 19:43, H.J. Lu wrote:
>> > Changes in v12:
>> >
>> > 1. Call _dl_executable_postprocess after ARCH_SETUP_IREL.
>> > 2. Add enum dl_elf_thp_control_t and change _dl_elf_thp_control to
>> > enum dl_elf_thp_control_t.
>> > 3. Add get_thp_mode and get_thp_size to support/support_check_hugetlb.c.
>> > Update support_thp_is_madvise to use get_thp_mode. Check path != NULL
>> > in check_align. Close fd after read in get_thp_mode.
>> > 4. Xfail -z noseparate-code THP tests with lld since -z noseparate-code is a
>> > nop for lld.
>> > 5. Only xfail tst-thp-1-no-s-code-static and tst-thp-1-static on arm for static
>> > PIE.
>> > 6. Print madvise result for LD_DEBUGS=files:
>> >
>> > 4073596: madvise (0x0000560f3a400000, 0x00000000020029fd,
>> > MADV_HUGEPAGE) returns 0
>> >
>> > 4077956: file=tst-thp-size-mod.so [0]; dynamically loaded by
>> > elf/tst-thp-align [0]
>> > 4077956: file=tst-thp-size-mod.so [0]; generating link map
>> > 4077956: madvise (0x00005611e6600000, 0x0000000002001000,
>> > MADV_HUGEPAGE) returns 0
>> > 4077956: dynamic: 0x00005611e89ffdf8 base: 0x00005611e6600000
>> > size: 0x0000000002400008
>> > 4077956: entry: 0x00005611e6600000 phdr: 0x00005611e6600040
>> > phnum: 9
>> >
>> > --
>> > H.J.
>> > ---
>> > The current THP segment load approach works only when THP is enabled
>> > with always in the kernel. If THP is enabled with madvise in the
>> > kernel, to enable THP segment load in an application, madvise should
>> > be called with MADV_HUGEPAGE on all THP eligible PT_LOAD segments:
>> >
>> > 1. Define DL_MAP_DEFAULT_THP_PAGESIZE in hugepages.h and default it to 0.
>> > If DL_MAP_DEFAULT_THP_PAGESIZE is defined, assume kernel THP madvise mode.
>> > If kernel THP mode is always or never, there is an extra madvise call
>> > which has no impact. DL_MAP_DEFAULT_THP_PAGESIZE is defined for x86 and
>> > 64-bit loongarch.
>> > 2. Update _dl_map_segment_align to support madvise THP mode. This fixes
>> > BZ #34079.
>> > 3. Call _dl_executable_postprocess in rtld_setup_main_map for dynamic
>> > executables and in LIBC_START_MAIN for static executables, which calls
>> > madvise with MADV_HUGEPAGE on all THP eligible PT_LOAD segments in
>> > executable. This fixes BZ #34080 for both dynamic and static executables.
>> > 4. Call _dl_postprocess_loadcmd_extra in _dl_postprocess_loadcmd, which
>> > calls madvise with MADV_HUGEPAGE on all THP eligible PT_LOAD segments
>> > when loading an object after they have been mapped in. This fixes
>> > BZ #34080 for shared objects.
>> > 5. Set the maximum page alignment on THP tests to THP page size as the
>> > default maximum page alignment may be smaller than THP page size.
>> > 6. Add tests to verify that large executable PT_LOAD segments in
>> > executables are mapped at addresses aligned to THP page size when the
>> > kernel is configured to use THP in "always" mode or "madvise" mode by
>> > inspecting /proc/self/maps to check that the mapping address is aligned
>> > to THP page size reported by the kernel. Also verify that madvise is
>> > called with MADV_HUGEPAGE when the glibc tunable glibc.elf.thp=1 is used
>> > and madvise isn't called with MADV_HUGEPAGE when the glibc tunable
>> > glibc.elf.thp=0 is used.
>> >
>> > Skip these tests if THP page size cannot be determined or if THP is not
>> > enabled in "always" mode nor "madvise" mode.
>> >
>> > Quote WANG Rui <wangrui@loongson.cn>:
>> >
>> > From benchmarking a clang build of the Linux kernel on x86_64 with
>> > your patch in THP madvise mode, I observed that iTLB misses were
>> > reduced, similar to what we see in THP always mode.
>> >
>> > NB: Some THP tests fail on arm due to limitations of arm32 kABI:
>> >
>> > https://sourceware.org/bugzilla/show_bug.cgi?id=34096
>>
>> The patch looks ok in general, some comments below mostly for testing part and a
>> suggestion for x86_64. I think we will still need a v13, and I would like to
>> run a full tests on some ABIs that support hugepage (most are on qemu, but
>> it does allow adding coverage).
>
>
> I will work on it in a few days.
>
> Thanks.
>
>>
>> > ---
>> > csu/libc-start.c | 6 +
>> > elf/dl-load.h | 3 +
>> > elf/dl-support.c | 6 +
>> > elf/rtld.c | 14 +-
>> > support/support_check_hugetlb.c | 62 ++++++-
>> > support/support_check_hugetlb.h | 12 ++
>> > sysdeps/generic/dl-exec-post.h | 34 ++++
>> > sysdeps/generic/dl-load-post.h | 23 +++
>> > sysdeps/generic/hugepages.h | 19 +-
>> > sysdeps/generic/ldsodefs.h | 9 +
>> > sysdeps/unix/sysv/linux/Makefile | 173 +++++++++++++++++-
>> > sysdeps/unix/sysv/linux/arm/Makefile | 7 +
>> > sysdeps/unix/sysv/linux/dl-exec-post.h | 138 ++++++++++++++
>> > sysdeps/unix/sysv/linux/dl-load-post.h | 42 +++++
>> > .../unix/sysv/linux/dl-map-segment-align.c | 34 +---
>> > .../unix/sysv/linux/dl-map-segment-align.h | 17 +-
>> > sysdeps/unix/sysv/linux/ldsodefs.h | 3 +
>> > sysdeps/unix/sysv/linux/loongarch/Makefile | 3 +
>> > .../{dl-map-segment-align.h => hugepages.h} | 4 +-
>> > sysdeps/unix/sysv/linux/strace-tst-thp.sh | 80 ++++++++
>> > .../unix/sysv/linux/tst-thp-1-no-s-code-pde.c | 19 ++
>> > .../sysv/linux/tst-thp-1-no-s-code-static.c | 19 ++
>> > sysdeps/unix/sysv/linux/tst-thp-1-no-s-code.c | 19 ++
>> > sysdeps/unix/sysv/linux/tst-thp-1-pde.c | 19 ++
>> > sysdeps/unix/sysv/linux/tst-thp-1-static.c | 19 ++
>> > sysdeps/unix/sysv/linux/tst-thp-1.c | 28 +++
>> > sysdeps/unix/sysv/linux/tst-thp-align-check.h | 67 +++++++
>> > sysdeps/unix/sysv/linux/tst-thp-align.c | 123 +------------
>> > sysdeps/unix/sysv/linux/x86/hugepages.h | 22 +++
>> > 29 files changed, 851 insertions(+), 173 deletions(-)
>> > create mode 100644 sysdeps/generic/dl-exec-post.h
>> > create mode 100644 sysdeps/generic/dl-load-post.h
>> > create mode 100644 sysdeps/unix/sysv/linux/dl-exec-post.h
>> > create mode 100644 sysdeps/unix/sysv/linux/dl-load-post.h
>> > rename sysdeps/unix/sysv/linux/loongarch/lp64/{dl-map-segment-align.h => hugepages.h} (90%)
>> > create mode 100644 sysdeps/unix/sysv/linux/strace-tst-thp.sh
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-pde.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-static.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1-no-s-code.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1-pde.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1-static.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-1.c
>> > create mode 100644 sysdeps/unix/sysv/linux/tst-thp-align-check.h
>> > create mode 100644 sysdeps/unix/sysv/linux/x86/hugepages.h
>> >
>> > diff --git a/csu/libc-start.c b/csu/libc-start.c
>> > index 03d770ef15..64ba0bc3a9 100644
>> > --- a/csu/libc-start.c
>> > +++ b/csu/libc-start.c
>> > @@ -205,6 +205,7 @@ call_fini (void *unused)
>> > #endif /* !SHARED */
>> >
>> > #include <libc-start.h>
>> > +#include <dl-exec-post.h>
>> >
>> > STATIC int LIBC_START_MAIN (int (*main) (int, char **, char **
>> > MAIN_AUXVEC_DECL),
>> > @@ -307,6 +308,11 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
>> > _dl_relocate_static_pie_ifunc ();
>> > ARCH_SETUP_IREL ();
>> >
>> > + /* This must run after the IFUNC relocations: _dl_executable_postprocess
>> > + may call IFUNC-resolved routines. */
>> > + struct link_map *main_map = _dl_get_dl_main_map ();
>> > + _dl_executable_postprocess (main_map, GL(dl_phdr), GL(dl_phnum));
>> > +
>> > /* Initialize libpthread if linked in. */
>> > if (__pthread_initialize_minimal != NULL)
>> > __pthread_initialize_minimal ();
>> > diff --git a/elf/dl-load.h b/elf/dl-load.h
>> > index 80ae5db4b3..84b82e183d 100644
>> > --- a/elf/dl-load.h
>> > +++ b/elf/dl-load.h
>> > @@ -112,6 +112,7 @@ struct loadcmd
>> > int prot; /* PROT_* bits. */
>> > };
>> >
>> > +#include <dl-load-post.h>
>> >
>> > /* Iterator for program header segments. Initialize with
>> > _dl_pt_load_iterator_init, then either walk PT_LOAD segments via
>> > @@ -215,6 +216,8 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
>> > /* Found the program header in this segment. */
>> > l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
>> > - c->mapoff);
>> > +
>> > + _dl_postprocess_loadcmd_extra (l, c);
>> > }
>> >
>> >
>> > diff --git a/elf/dl-support.c b/elf/dl-support.c
>> > index 0508d6113b..b57fd74670 100644
>> > --- a/elf/dl-support.c
>> > +++ b/elf/dl-support.c
>> > @@ -179,6 +179,12 @@ int _dl_stack_cache_lock;
>> > #endif
>> > struct dl_scope_free_list *_dl_scope_free_list;
>> >
>> > +#ifdef HAVE_THP
>> > +enum dl_elf_thp_control_t _dl_elf_thp_control;
>> > +enum thp_mode_t _dl_thp_mode;
>> > +size_t _dl_elf_thp_pagesize;
>> > +#endif
>> > +
>> > #ifdef NEED_DL_SYSINFO
>> > /* Needed for improved syscall handling on at least x86/Linux. NB: Don't
>> > initialize it here to avoid RELATIVE relocation in static PIE. */
>> > diff --git a/elf/rtld.c b/elf/rtld.c
>> > index 12e1b4dd71..57f829cff2 100644
>> > --- a/elf/rtld.c
>> > +++ b/elf/rtld.c
>> > @@ -52,6 +52,7 @@
>> > #include <dl-find_object.h>
>> > #include <dl-audit-check.h>
>> > #include <dl-call_tls_init_tp.h>
>> > +#include <dl-exec-post.h>
>> >
>> > #include <assert.h>
>> >
>> > @@ -1209,14 +1210,8 @@ rtld_setup_main_map (struct link_map *main_map)
>> > main_map->l_relro_size = ph->p_memsz;
>> > break;
>> > }
>> > - /* Process program headers again, but scan them backwards since
>> > - PT_GNU_PROPERTY is close to the end of program headers. */
>> > - for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
>> > - if (ph[-1].p_type == PT_GNU_PROPERTY)
>> > - {
>> > - _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
>> > - break;
>> > - }
>> > +
>> > + _dl_executable_postprocess (main_map, phdr, phnum);
>> >
>> > /* Adjust the address of the TLS initialization image in case
>> > the executable is actually an ET_DYN object. */
>> > @@ -1589,6 +1584,9 @@ dl_main (const ElfW(Phdr) *phdr,
>> > {
>> > RTLD_TIMING_VAR (start);
>> > rtld_timer_start (&start);
>> > +#ifdef HAVE_THP
>> > + _dl_get_thp_config ();
>> > +#endif
>> > _dl_map_object (NULL, rtld_progname, lt_executable, 0,
>> > __RTLD_OPENEXEC, LM_ID_BASE);
>> > rtld_timer_stop (&load_time, start);
>> > diff --git a/support/support_check_hugetlb.c b/support/support_check_hugetlb.c
>> > index be7e10e80d..6d30c4041b 100644
>> > --- a/support/support_check_hugetlb.c
>> > +++ b/support/support_check_hugetlb.c
>> > @@ -27,22 +27,66 @@
>> > #include <support/support_check_hugetlb.h>
>> > #include <support/check.h>
>> >
>> > -bool
>> > -support_thp_is_madvise (void)
>> > +enum thp_mode_t
>> > +get_thp_mode (void)
>> > {
>> > - int fd = open ("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
>> > + int fd = open ("/sys/kernel/mm/transparent_hugepage/enabled",
>> > + O_RDONLY, 0);
>> > if (fd == -1)
>> > - return false;
>> > + return thp_mode_not_supported;
>> >
>> > -#define MODE_MADVISE "always [madvise] never\n"
>> > + 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_MADVISE)];
>> > + char str[sizeof(mode_always)];
>> > ssize_t s = read (fd, str, sizeof (str));
>> > close (fd);
>> > - if (s != sizeof (str) - 1)
>> > - return false;
>> > + if (s >= sizeof str || s < 0)
>> > + return thp_mode_not_supported;
>> > str[s] = '\0';
>> > - return strcmp (str, MODE_MADVISE) == 0;
>> > +
>> > + 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;
>> > +}
>> > +
>> > +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;
>> > +}
>> > +
>> > +bool
>> > +support_thp_is_madvise (void)
>> > +{
>> > + return get_thp_mode () == thp_mode_madvise;
>> > }
>> >
>> > bool
>>
>> I have change this file with 0b271861516cb0c736ef2097a1a916caca43dbeb, so you
>> will need to rebase your changes. I would rewrote support_thp_work_madvise
>> as:
>>
>> static bool
>> support_thp_work_madvise (void)
>> {
>> enum thp_mode_t mode = get_thp_mode ();
>> return mode == thp_mode_always || mode == thp_mode_madvise;
>> }
>>
>> Also, use the libsupport naming scheme for get_thp_size and get_thp_mode,
>> with something like support_thp_size and support_thp_mode.
Fixed.
>> > diff --git a/support/support_check_hugetlb.h b/support/support_check_hugetlb.h
>> > index 2c5f5cb80c..01fae1753a 100644
>> > --- a/support/support_check_hugetlb.h
>> > +++ b/support/support_check_hugetlb.h
>> > @@ -21,6 +21,10 @@
>> >
>> > #include <stdbool.h>
>> > #include <sys/cdefs.h>
>> > +#undef attribute_hidden
>> > +#define attribute_hidden
>> > +#include <hugepages.h> /* For enum thp_mode_t. */
>> > +#undef attribute_hidden
>> >
>> > __BEGIN_DECLS
>> >
>> > @@ -43,6 +47,14 @@ bool support_hugepages_reserved (void);
>> > so it is safe to call unconditionally. */
>> > void support_check_malloc_hugetlb (void);
>> >
>> > +/* Returns Transparent Huge Pages (THP) mode from
>> > + /sys/kernel/mm/transparent_hugepage/enabled. */
>> > +extern enum thp_mode_t get_thp_mode (void);
>> > +
>> > +/* Returns Transparent Huge Pages (THP) page size from
>> > + /sys/kernel/mm/transparent_hugepage/hpage_pmd_size. */
>> > +extern unsigned long int get_thp_size (void);
>> > +
>> > __END_DECLS
>> >
>> > #endif /* SUPPORT_SUPPORT_CHECK_HUGETLB_H */
>> > diff --git a/sysdeps/generic/dl-exec-post.h b/sysdeps/generic/dl-exec-post.h
>> > new file mode 100644
>> > index 0000000000..f5dcdc093a
>> > --- /dev/null
>> > +++ b/sysdeps/generic/dl-exec-post.h
>> > @@ -0,0 +1,34 @@
>> > +/* _dl_executable_postprocess. Generic version.
>> > + Copyright (C) 2026 Free Software Foundation, Inc.
>> > + Copyright The GNU Toolchain Authors.
>> > + 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/>. */
>> > +
>> > +static inline void
>> > +_dl_executable_postprocess (struct link_map *main_map,
>> > + const ElfW(Phdr) *phdr, ElfW(Word) phnum)
>> > +{
>> > +#ifdef SHARED
>> > + /* Process program headers again, but scan them backwards since
>> > + PT_GNU_PROPERTY is close to the end of program headers. */
>> > + for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
>> > + if (ph[-1].p_type == PT_GNU_PROPERTY)
>> > + {
>> > + _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
>> > + break;
>> > + }
>> > +#endif
>> > +}
>> > diff --git a/sysdeps/generic/dl-load-post.h b/sysdeps/generic/dl-load-post.h
>> > new file mode 100644
>> > index 0000000000..1a6d205e33
>> > --- /dev/null
>> > +++ b/sysdeps/generic/dl-load-post.h
>> > @@ -0,0 +1,23 @@
>> > +/* _dl_postprocess_loadcmd_extra. Generic version.
>> > + Copyright (C) 2026 Free Software Foundation, Inc.
>> > + Copyright The GNU Toolchain Authors.
>> > + 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/>. */
>> > +
>> > +static inline void
>> > +_dl_postprocess_loadcmd_extra (struct link_map *l, const struct loadcmd *c)
>> > +{
>> > +}
>> > diff --git a/sysdeps/generic/hugepages.h b/sysdeps/generic/hugepages.h
>> > index 5fc9b5c8de..8e54661527 100644
>> > --- a/sysdeps/generic/hugepages.h
>> > +++ b/sysdeps/generic/hugepages.h
>> > @@ -21,15 +21,26 @@
>> >
>> > #include <stddef.h>
>> >
>> > +/* The THP segment load control mode. */
>> > +enum dl_elf_thp_control_t
>> > +{
>> > + /* To be enabled or disabled by GLIBC_TUNABLES. */
>> > + dl_elf_thp_control_default = 0,
>> > + /* Enabled by GLIBC_TUNABLES=glibc.elf.thp=1. */
>> > + dl_elf_thp_control_enabled,
>> > + /* Disabled by GLIBC_TUNABLES=glibc.elf.thp=0. */
>> > + dl_elf_thp_control_disabled
>> > +};
>> > +
>> > /* Return the default transparent huge page size. */
>> > unsigned long int __get_thp_size (void) attribute_hidden;
>> >
>> > enum thp_mode_t
>> > {
>> > + thp_mode_not_supported = 0,
>> > thp_mode_always,
>> > thp_mode_madvise,
>> > - thp_mode_never,
>> > - thp_mode_not_supported
>> > + thp_mode_never
>> > };
>> >
>> > enum thp_mode_t __get_thp_mode (void) attribute_hidden;
>> > @@ -45,6 +56,10 @@ void __get_hugepage_config (size_t requested, size_t *pagesize, int *flags)
>> > # define MALLOC_DEFAULT_THP_PAGESIZE 0
>> > #endif
>> >
>> > +#ifndef DL_MAP_DEFAULT_THP_PAGESIZE
>> > +# define DL_MAP_DEFAULT_THP_PAGESIZE 0
>> > +#endif
>> > +
>> > #ifndef MAX_THP_PAGESIZE
>> > # define MAX_THP_PAGESIZE (32 * 1024 * 1024)
>> > #endif
>> > diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
>> > index 24529db8a1..f94247ad9f 100644
>> > --- a/sysdeps/generic/ldsodefs.h
>> > +++ b/sysdeps/generic/ldsodefs.h
>> > @@ -39,6 +39,7 @@
>> > #include <libc-lock.h>
>> > #include <hp-timing.h>
>> > #include <list_t.h>
>> > +#include <hugepages.h>
>> >
>> > __BEGIN_DECLS
>> >
>> > @@ -477,6 +478,14 @@ struct rtld_global
>> > EXTERN struct __pthread **_dl_pthread_threads;
>> > __mach_rwlock_define (EXTERN, _dl_pthread_threads_lock)
>> > #endif
>> > +#ifdef HAVE_THP
>> > + /* The THP segment load control. */
>> > + EXTERN enum dl_elf_thp_control_t _dl_elf_thp_control;
>> > + /* The kernel THP mode. */
>> > + EXTERN enum thp_mode_t _dl_thp_mode;
>> > + /* Page size used for THP segment load. */
>> > + EXTERN size_t _dl_elf_thp_pagesize;
>> > +#endif
>> > #ifdef SHARED
>> > };
>> > # define __rtld_global_attribute__
>> > diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
>> > index 63e7046cb3..86c553789b 100644
>> > --- a/sysdeps/unix/sysv/linux/Makefile
>> > +++ b/sysdeps/unix/sysv/linux/Makefile
>> > @@ -698,11 +698,15 @@ $(objpfx)pldd: $(objpfx)xmalloc.o
>> > tests += \
>> > tst-rseq-tls-range \
>> > tst-rseq-tls-range-4096 \
>> > + tst-thp-1 \
>> > + tst-thp-1-pde \
>> > + tst-thp-1-static \
>> > tst-thp-align \
>> > # tests
>> > tests-static += \
>> > tst-rseq-tls-range-4096-static \
>> > tst-rseq-tls-range-static \
>> > + tst-thp-1-static \
>> > # tests-static
>> > modules-names += \
>> > tst-rseq-tls-range-mod \
>> > @@ -712,15 +716,12 @@ 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)
>> > @@ -729,6 +730,172 @@ tests-special += \
>> > $(objpfx)tst-nolink-libc-2.out \
>> > # tests-special
>> > endif
>> > +
>> > +ifndef THP-PAGE-SIZE
>> > +# Align PT_LOAD segments in THP tests to THP page size so that kernel will
>> > +# map PIE to the address aligned to THP page size. Default THP page size
>> > +# to 2MB which can be overridden in Makefile in subdirectories.
>> > +THP-PAGE-SIZE = 0x200000
>> > +endif
>> > +
>> > +THP-PAGE-SIZE-LDFLAGS = -Wl,-z,max-page-size=$(THP-PAGE-SIZE)
>> > +
>> > +# -Wl,-z,max-page-size=$(THP-PAGE-SIZE) alone doesn't work for PDE when
>> > +# text-segment address is lower than the maximum page size:
>> > +# https://sourceware.org/bugzilla/show_bug.cgi?id=34184
>> > +ifneq (,$(load-address-ldflag))
>> > +LOAD-THP-ADDRESS-LDFLAGS = $(load-address-ldflag)=$(THP-PAGE-SIZE)
>> > +endif
>> > +
>> > +LDFLAGS-tst-thp-size-mod.so = -Wl,-z,noseparate-code \
>> > + $(THP-PAGE-SIZE-LDFLAGS)
>> > +tst-thp-align-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +$(objpfx)tst-thp-align.out: $(objpfx)tst-thp-size-mod.so
>> > +
>> > +tests += \
>> > + tst-thp-1-no-s-code \
>> > + tst-thp-1-no-s-code-pde \
>> > + tst-thp-1-no-s-code-static \
>> > +# tests
>> > +tests-static += \
>> > + tst-thp-1-no-s-code-static \
>> > +# tests-static
>> > +
>> > +ifeq (yes,$(with-lld))
>> > +# -z noseparate-code is a nop for lld. It doesn't create an executable
>> > +# segment placed at 0 file offset.
>> > +test-xfail-tst-thp-1-no-s-code = yes
>> > +test-xfail-tst-thp-1-no-s-code-pde = yes
>> > +test-xfail-tst-thp-1-no-s-code-static = yes
>> > +endif
>> > +
>> > +LDFLAGS-tst-thp-1 = -Wl,-z,separate-code $(THP-PAGE-SIZE-LDFLAGS)
>> > +LDFLAGS-tst-thp-1-pde = -Wl,-z,separate-code $(THP-PAGE-SIZE-LDFLAGS) \
>> > + $(LOAD-THP-ADDRESS-LDFLAGS)
>> > +LDFLAGS-tst-thp-1-static = -Wl,-z,separate-code $(THP-PAGE-SIZE-LDFLAGS)
>> > +ifneq (yes,$(enable-static-pie))
>> > +LDFLAGS-tst-thp-1-static += $(LOAD-THP-ADDRESS-LDFLAGS)
>> > +endif
>> > +LDFLAGS-tst-thp-1-no-s-code = -Wl,-z,noseparate-code \
>> > + $(THP-PAGE-SIZE-LDFLAGS)
>> > +LDFLAGS-tst-thp-1-no-s-code-pde = -Wl,-z,noseparate-code \
>> > + $(THP-PAGE-SIZE-LDFLAGS) \
>> > + $(LOAD-THP-ADDRESS-LDFLAGS)
>> > +LDFLAGS-tst-thp-1-no-s-code-static = -Wl,-z,noseparate-code \
>> > + $(THP-PAGE-SIZE-LDFLAGS)
>> > +ifneq (yes,$(enable-static-pie))
>> > +LDFLAGS-tst-thp-1-no-s-code-static += $(LOAD-THP-ADDRESS-LDFLAGS)
>> > +endif
>> > +
>> > +$(objpfx)tst-thp-1-no-s-code: $(objpfx)tst-thp-size-mod.o
>> > +$(objpfx)tst-thp-1-no-s-code-pde: $(objpfx)tst-thp-size-mod.o
>> > +$(objpfx)tst-thp-1-no-s-code-static: $(objpfx)tst-thp-size-mod.o
>> > +
>> > +tst-thp-1-no-s-code-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +tst-thp-1-no-s-code-pde-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +tst-thp-1-no-s-code-static-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +
>> > +tst-thp-1-no-s-code-pde-no-pie = yes
>> > +
>> > +tst-thp-1-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +tst-thp-1-pde-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +tst-thp-1-static-ENV = GLIBC_TUNABLES=glibc.elf.thp=1
>> > +
>> > +$(objpfx)tst-thp-1: $(objpfx)tst-thp-size-mod.o
>> > +$(objpfx)tst-thp-1-pde: $(objpfx)tst-thp-size-mod.o
>> > +$(objpfx)tst-thp-1-static: $(objpfx)tst-thp-size-mod.o
>> > +
>> > +tst-thp-1-pde-no-pie = yes
>> > +
>> > +# Don't run strace tests for cross-compiling.
>> > +ifeq (no,$(cross-compiling))
>> > +thp-kernel-status = $(shell grep madvise /sys/kernel/mm/transparent_hugepage/enabled)
>>
>> I think now that we have support/support_check_hugetlb.c the new tests
>> can be built with TEST_HUGETLB. This will call support_check_malloc_hugetlb
>> and they fail with EXIT_UNSUPPORTED if hugepage is not support by the
>> kernel.
It won't work for THP tests since support_check_malloc_hugetlb has
if (!TUNABLE_IS_INITIALIZED (glibc, malloc, hugetlb))
return;
>> This should works seamlessly with the strace/bash tests, where it would just
>> propagate the failure.
>>
>> It would also allow to use $(run-built-tests) instead of $(cross-compiling)
>> whether to run the tests; and also allows running when THP is set to 'always'
>> (which is redundant, but it does increase testing coverage).
Only strace madvise tests are run when THP is set to madvise.
>> > +# Verify that madvise is called with MADV_HUGEPAGE when THP is enabled
>> > +# under madvise THP kernel.
>> > +ifneq ($(findstring [madvise],$(thp-kernel-status)),)
>> > +tests-special += \
>> > + $(objpfx)strace-tst-thp-1-disabled.out \
>> > + $(objpfx)strace-tst-thp-1-enabled.out \
>> > + $(objpfx)strace-tst-thp-1-pde-disabled.out \
>> > + $(objpfx)strace-tst-thp-1-pde-enabled.out \
>> > + $(objpfx)strace-tst-thp-1-static-disabled.out \
>> > + $(objpfx)strace-tst-thp-1-static-enabled.out \
>> > + $(objpfx)strace-tst-thp-align-default.out \
>> > + $(objpfx)strace-tst-thp-align-disabled.out \
>> > + $(objpfx)strace-tst-thp-align-enabled.out \
>> > +# tests-special
>> > +
>> > +$(objpfx)strace-tst-thp-1-enabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-1
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=1' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-1 > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-1-disabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-1
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=0' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-1 > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-1-pde-enabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-1-pde
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=1' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-1-pde > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-1-pde-disabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-1-pde
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=0' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-1-pde > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-1-static-enabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh \
>> > + $(objpfx)tst-thp-1-static
>> > + $(SHELL) $< $(objpfx)tst-thp-1-static '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=1' > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-1-static-disabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh \
>> > + $(objpfx)tst-thp-1-static
>> > + $(SHELL) $< $(objpfx)tst-thp-1-static '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=0' > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-align-default.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-align
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env)' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-align > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-align-enabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-align
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=1' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-align > $@; \
>> > + $(evaluate-test)
>> > +
>> > +$(objpfx)strace-tst-thp-align-disabled.out: \
>> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
>> > + $(objpfx)tst-thp-align
>> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=0' \
>> > + '$(rpath-link)' $(objpfx)tst-thp-align > $@; \
>> > + $(evaluate-test)
>> > +endif # [madvise]
>> > +endif # $(cross-compiling)
>> > endif # $(subdir) == elf
>> >
>> > ifeq ($(subdir),rt)
>> > diff --git a/sysdeps/unix/sysv/linux/arm/Makefile b/sysdeps/unix/sysv/linux/arm/Makefile
>> > index e73ce4f811..428949fefd 100644
>> > --- a/sysdeps/unix/sysv/linux/arm/Makefile
>> > +++ b/sysdeps/unix/sysv/linux/arm/Makefile
>> > @@ -3,6 +3,13 @@ sysdep-rtld-routines += aeabi_read_tp libc-do-syscall
>> > # The test uses INTERNAL_SYSCALL_CALL. In thumb mode, this uses
>> > # an undefined reference to __libc_do_syscall.
>> > CFLAGS-tst-nolink-libc.c += -marm
>> > +
>> > +ifeq (yes,$(enable-static-pie))
>> > +# These static PIE tests fail on arm due to limitations of arm32 kABI:
>> > +# https://sourceware.org/bugzilla/show_bug.cgi?id=34096
>> > +test-xfail-tst-thp-1-no-s-code-static = yes
>> > +test-xfail-tst-thp-1-static = yes
>> > +endif
>> > endif
>> >
>> > ifeq ($(subdir),misc)
>> > diff --git a/sysdeps/unix/sysv/linux/dl-exec-post.h b/sysdeps/unix/sysv/linux/dl-exec-post.h
>> > new file mode 100644
>> > index 0000000000..788e427d06
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/dl-exec-post.h
>> > @@ -0,0 +1,138 @@
>> > +/* _dl_executable_postprocess. Linux version.
>> > + Copyright (C) 2026 Free Software Foundation, Inc.
>> > + Copyright The GNU Toolchain Authors.
>> > + 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/>. */
>> > +
>> > +static inline void
>> > +_dl_get_thp_config (void)
>> > +{
>> > + /* Check if there is GLIBC_TUNABLES=glibc.elf.thp=[0|1]. */
>> > + if (TUNABLE_GET_FULL (glibc, elf, thp, int32_t, NULL) == 0)
>> > + GL(dl_elf_thp_control) = dl_elf_thp_control_disabled;
>> > + else
>> > + GL(dl_elf_thp_control) = dl_elf_thp_control_enabled;
>> > +
>> > + /* Return if the tunable is not set or THP is disabled by the
>> > + tunable. */
>> > + if (GL(dl_elf_thp_control) == dl_elf_thp_control_disabled)
>> > + return;
>> > +
>> > + _Static_assert (DL_MAP_DEFAULT_THP_PAGESIZE <= MAX_THP_PAGESIZE,
>> > + "DL_MAP_DEFAULT_THP_PAGESIZE <= MAX_THP_PAGESIZE");
>> > +
>> > + /* NB: Accessing /sys/kernel/mm files is quite expensive and the file
>> > + may not be accessible in containers. If DL_MAP_DEFAULT_THP_PAGESIZE
>> > + is non-zero, assume THP mode is madvise and always call madvise.
>> > + Since madvise is a fast system call, it adds only a small overhead
>> > + compared to the cost of accessing /sys/kernel/mm files. */
>> > + if (DL_MAP_DEFAULT_THP_PAGESIZE != 0)
>> > + {
>> > + GL(dl_elf_thp_pagesize) = DL_MAP_DEFAULT_THP_PAGESIZE;
>> > + GL(dl_thp_mode) = thp_mode_madvise;
>> > + }
>> > + else
>> > + {
>> > + GL(dl_thp_mode) = __get_thp_mode ();
>> > + if (GL(dl_thp_mode) == thp_mode_always
>> > + || GL(dl_thp_mode) == thp_mode_madvise)
>> > + {
>> > + GL(dl_elf_thp_pagesize) = __get_thp_size ();
>> > + /* We cap the huge page size at MAX_THP_PAGESIZE to avoid
>> > + over-aligning on systems with very large normal pages
>> > + (like 64K pages with 512M huge pages). */
>> > + if (GL(dl_elf_thp_pagesize) > MAX_THP_PAGESIZE)
>> > + GL(dl_elf_thp_pagesize) = 0;
>> > + }
>> > + else
>> > + GL(dl_elf_thp_pagesize) = 0;
>> > +
>> > + if (GL(dl_elf_thp_pagesize) == 0)
>> > + {
>> > + GL(dl_elf_thp_control) = dl_elf_thp_control_disabled;
>> > + GL(dl_thp_mode) = thp_mode_not_supported;
>> > + }
>> > + }
>> > +}
>> > +
>> > +static inline void
>> > +_dl_executable_postprocess (struct link_map *main_map,
>> > + const ElfW(Phdr) *phdr, ElfW(Word) phnum)
>> > +{
>> > + /* NB: In static executable, PT_GNU_PROPERTY is processed in target
>> > + libc-start.h if it is needed by target. When ld.so is used, if
>> > + a target doesn't need PT_GNU_PROPERTY, _dl_process_pt_gnu_property
>> > + is an empty function. */
>> > +#ifdef SHARED
>> > + /* Process program headers again, but scan them backwards since
>> > + PT_GNU_PROPERTY is close to the end of program headers. */
>> > + for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
>> > + if (ph[-1].p_type == PT_GNU_PROPERTY)
>> > + {
>> > + _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
>> > + break;
>> > + }
>> > +#endif
>> > +
>> > + /* If THP state was not yet initialized, the main executable was mapped
>> > + by the kernel; in that case this function is the only place that can
>> > + apply MADV_HUGEPAGE to the main executable's segments. Otherwise,
>> > + _dl_get_thp_config has already run earlier in dl_main and
>> > + _dl_map_segments has just mapped the main executable, so
>> > + _dl_postprocess_loadcmd_extra has already done the madvise pass; do
>> > + not repeat it here. */
>> > + if (GL(dl_elf_thp_control) != dl_elf_thp_control_default)
>> > + return;
>> > +
>> > + _dl_get_thp_config ();
>> > +
>> > + /* Return if THP segment load isn't enabled. */
>> > + if (GL(dl_elf_thp_control) != dl_elf_thp_control_enabled)
>> > + return;
>> > +
>> > + /* NB: If DL_MAP_DEFAULT_THP_PAGESIZE is non-zero, dl_thp_mode is set
>> > + to thp_mode_madvise. */
>> > + if (DL_MAP_DEFAULT_THP_PAGESIZE == 0
>> > + && GL(dl_thp_mode) != thp_mode_madvise)
>> > + return;
>> > +
>> > + /* When we get here, the main executable have been mapped in. Call
>> > + madvise with MADV_HUGEPAGE for all THP eligible PT_LOAD segments. */
>> > +
>> > + const ElfW(Phdr) *ph;
>> > +
>> > + size_t thp_pagesize = GL(dl_elf_thp_pagesize);
>> > +
>> > + /* Call __madvise if offset and address of the PT_LOAD segment are
>> > + aligned to THP page size and it is read-only. */
>> > + for (ph = phdr; ph < &phdr[phnum]; ++ph)
>> > + if (ph->p_type == PT_LOAD
>> > + && ph->p_memsz >= thp_pagesize
>> > + && ((ph->p_vaddr | ph->p_offset) & (thp_pagesize - 1)) == 0
>> > + && (ph->p_flags & (PF_W | PF_R)) == PF_R)
>> > + {
>>
>> This checks for requires readable and not writable segments, while
>> _dl_segment_thp_eligible only checks for not writable.
>>
>> I think we should consolidate using similar checks, even though the madvise
>> call should work on both cases.
Fixed.
>>
>> > + int ret = __madvise ((void *) (main_map->l_addr + ph->p_vaddr),
>> > + ph->p_memsz, MADV_HUGEPAGE);
>> > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
>> > + _dl_debug_printf ("\
>> > +madvise (0x%0*lx, 0x%0*lx, MADV_HUGEPAGE) returns %d\n",
>> > +(int) sizeof (void *) * 2,
>> > +(unsigned long int) (main_map->l_addr + ph->p_vaddr),
>> > +(int) sizeof (void *) * 2,
>> > +(unsigned long int) ph->p_memsz,
>> > +ret);
>> > + }
>> > +}
>> > diff --git a/sysdeps/unix/sysv/linux/dl-load-post.h b/sysdeps/unix/sysv/linux/dl-load-post.h
>> > new file mode 100644
>> > index 0000000000..741c4ddcb1
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/dl-load-post.h
>> > @@ -0,0 +1,42 @@
>> > +/* _dl_postprocess_loadcmd_extra. Linux version.
>> > + Copyright (C) 2026 Free Software Foundation, Inc.
>> > + Copyright The GNU Toolchain Authors.
>> > + 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/>. */
>> > +
>> > +static bool _dl_segment_thp_eligible (const struct loadcmd *, size_t);
>> > +
>> > +/* After L has been mapped in, call madvise with MADV_HUGEPAGE for THP
>> > + madvise mode if L is THP eligible. */
>> > +
>> > +static inline void
>> > +_dl_postprocess_loadcmd_extra (struct link_map *l, const struct loadcmd *c)
>> > +{
>> > + if (GL(dl_thp_mode) == thp_mode_madvise
>> > + && _dl_segment_thp_eligible (c, GL(dl_elf_thp_pagesize)))
>> > + {
>> > + int ret = __madvise ((void *) (l->l_addr + c->mapstart),
>> > + c->mapend - c->mapstart, MADV_HUGEPAGE);
>> > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
>> > + _dl_debug_printf ("\
>> > + madvise (0x%0*lx, 0x%0*lx, MADV_HUGEPAGE) returns %d\n",
>> > + (int) sizeof (void *) * 2,
>> > + (unsigned long int) (l->l_addr + c->mapstart),
>> > + (int) sizeof (void *) * 2,
>> > + (unsigned long int) (c->mapend - c->mapstart),
>> > + ret);
>> > + }
>> > +}
>> > diff --git a/sysdeps/unix/sysv/linux/dl-map-segment-align.c b/sysdeps/unix/sysv/linux/dl-map-segment-align.c
>> > index a39e74d91b..c062ba0c58 100644
>> > --- a/sysdeps/unix/sysv/linux/dl-map-segment-align.c
>> > +++ b/sysdeps/unix/sysv/linux/dl-map-segment-align.c
>> > @@ -17,38 +17,24 @@
>> > License along with the GNU C Library; if not, see
>> > <https://www.gnu.org/licenses/>. */
>> >
>> > +#include <ldsodefs.h>
>> > #include <dl-map-segment-align.h>
>> > -#include <dl-tunables.h>
>> > -#include <hugepages.h>
>> > +
>> > +/* Return the alignment of the PT_LOAD segment for THP. P_ALIGN_MAX is
>> > + the maximum p_align value in the PT_LOAD segment. */
>> >
>> > ElfW (Addr)
>> > _dl_map_segment_align (const struct loadcmd *c, ElfW (Addr) p_align_max)
>> > {
>> > - static enum thp_mode_t thp_mode = thp_mode_not_supported;
>> > - static unsigned long int thp_pagesize;
>> > + size_t thp_pagesize = GL(dl_elf_thp_pagesize);
>> >
>> > - if (TUNABLE_GET (glibc, elf, thp, int32_t, NULL) == 0)
>> > + if (GL(dl_elf_thp_control) != dl_elf_thp_control_enabled
>> > + || p_align_max >= thp_pagesize)
>> > return p_align_max;
>> >
>> > - if (__glibc_unlikely (thp_mode == thp_mode_not_supported
>> > - || thp_pagesize == 0))
>> > - {
>> > - unsigned long int default_thp_pagesize = DL_MAP_DEFAULT_THP_PAGESIZE;
>> > - thp_mode = default_thp_pagesize ? thp_mode_always : __get_thp_mode ();
>> > - thp_pagesize = default_thp_pagesize ? : __get_thp_size ();
>> > - }
>> > -
>> > - /* Aligning load segments that are large enough to the PMD size helps
>> > - improve THP eligibility and reduces TLB pressure.
>> > - We cap the huge page size at MAX_THP_PAGESIZE to avoid over-aligning
>> > - on systems with very large normal pages (like 64K pages with 512M
>> > - huge pages). */
>> > - if (thp_mode == thp_mode_always
>> > - && thp_pagesize <= MAX_THP_PAGESIZE
>> > - && ((c->mapstart | c->mapoff) & (thp_pagesize - 1)) == 0
>> > - && (c->mapend - c->mapstart) >= thp_pagesize
>> > - && p_align_max < thp_pagesize
>> > - && (c->prot & PROT_WRITE) == 0)
>> > + /* Return true if the segment is THP eligible. It helps improve THP
>> > + eligibility and reduces TLB pressure. */
>> > + if (_dl_segment_thp_eligible (c, thp_pagesize))
>> > return thp_pagesize;
>> >
>> > return p_align_max;
>> > diff --git a/sysdeps/unix/sysv/linux/dl-map-segment-align.h b/sysdeps/unix/sysv/linux/dl-map-segment-align.h
>> > index d9b05181b7..b904e128d8 100644
>> > --- a/sysdeps/unix/sysv/linux/dl-map-segment-align.h
>> > +++ b/sysdeps/unix/sysv/linux/dl-map-segment-align.h
>> > @@ -19,9 +19,18 @@
>> >
>> > #include <dl-load.h>
>> >
>> > -#ifndef DL_MAP_DEFAULT_THP_PAGESIZE
>> > -# define DL_MAP_DEFAULT_THP_PAGESIZE 0
>> > -#endif
>> > -
>> > extern ElfW (Addr) _dl_map_segment_align
>> > (const struct loadcmd *, ElfW (Addr)) attribute_hidden;
>> > +
>> > +/* Return true only if the loadcmd C is THP eligible with THP page size
>> > + THP_PAGESIZE, which means that it is read-only, its size >= THP page
>> > + size, its offset and address of the loadcmd C are aligned to THP page
>> > + size. */
>> > +
>> > +static inline bool
>> > +_dl_segment_thp_eligible (const struct loadcmd *c, size_t thp_pagesize)
>> > +{
>> > + return ((c->prot & PROT_WRITE) == 0
>> > + && (c->mapend - c->mapstart) >= thp_pagesize
>> > + && ((c->mapstart | c->mapoff) & (thp_pagesize - 1)) == 0);
>> > +}
>> > diff --git a/sysdeps/unix/sysv/linux/ldsodefs.h b/sysdeps/unix/sysv/linux/ldsodefs.h
>> > index c63b649432..e39d9afe34 100644
>> > --- a/sysdeps/unix/sysv/linux/ldsodefs.h
>> > +++ b/sysdeps/unix/sysv/linux/ldsodefs.h
>> > @@ -21,6 +21,9 @@
>> > /* We have the auxiliary vector. */
>> > #define HAVE_AUX_VECTOR
>> >
>> > +/* We have transparent huge page. */
>> > +#define HAVE_THP
>> > +
>> > /* Get the real definitions. */
>> > #include_next <ldsodefs.h>
>> >
>> > diff --git a/sysdeps/unix/sysv/linux/loongarch/Makefile b/sysdeps/unix/sysv/linux/loongarch/Makefile
>> > index 0d5f087862..d5beb62440 100644
>> > --- a/sysdeps/unix/sysv/linux/loongarch/Makefile
>> > +++ b/sysdeps/unix/sysv/linux/loongarch/Makefile
>> > @@ -12,3 +12,6 @@ abi-ilp32s-condition := __WORDSIZE == 32 && defined __loongarch_soft_float
>> > abi-ilp32d-condition := __WORDSIZE == 32 && defined __loongarch_double_float
>> > abi-lp64s-condition := __WORDSIZE == 64 && defined __loongarch_soft_float
>> > abi-lp64d-condition := __WORDSIZE == 64 && defined __loongarch_double_float
>> > +
>> > +# Align THP tests to 32MB.
>> > +THP-PAGE-SIZE = 0x2000000
>> > diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/dl-map-segment-align.h b/sysdeps/unix/sysv/linux/loongarch/lp64/hugepages.h
>> > similarity index 90%
>> > rename from sysdeps/unix/sysv/linux/loongarch/lp64/dl-map-segment-align.h
>> > rename to sysdeps/unix/sysv/linux/loongarch/lp64/hugepages.h
>> > index c51ee4ac47..30252a9b86 100644
>> > --- a/sysdeps/unix/sysv/linux/loongarch/lp64/dl-map-segment-align.h
>> > +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/hugepages.h
>> > @@ -1,4 +1,4 @@
>> > -/* _dl_map_segment_align. LoongArch64 Linux version.
>> > +/* Huge Page support. LoongArch64 Linux version.
>> > Copyright (C) 2026 Free Software Foundation, Inc.
>> > Copyright The GNU Toolchain Authors.
>> > This file is part of the GNU C Library.
>> > @@ -19,4 +19,4 @@
>> >
>> > #define DL_MAP_DEFAULT_THP_PAGESIZE (32 * 1024 * 1024)
>> >
>> > -#include_next <dl-map-segment-align.h>
>> > +#include_next <hugepages.h>
>> > diff --git a/sysdeps/unix/sysv/linux/strace-tst-thp.sh b/sysdeps/unix/sysv/linux/strace-tst-thp.sh
>> > new file mode 100644
>> > index 0000000000..3ffb256c71
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/strace-tst-thp.sh
>> > @@ -0,0 +1,80 @@
>> > +#!/bin/bash
>> > +# Run THP test under strace to verify control of the THP segment load.
>> > +# 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/>.
>> > +
>> > +set -e
>> > +
>> > +case x"${1}" in
>> > +*-static)
>> > + rtld=
>> > + test_wrapper_env="$2"
>> > + run_program_env="$3"
>> > + library_path=
>> > + test_prog="$1"
>> > + ;;
>> > +*)
>> > + rtld="$1"
>> > + test_wrapper_env="$2"
>> > + run_program_env="$3"
>> > + library_path="$4"
>> > + test_prog="$5"
>> > + ;;
>> > +esac
>> > +
>> > +
>> > +if test x"${rtld}" = x; then
>> > + cmd="${test_wrapper_env} ${run_program_env} strace ${test_prog}"
>>
>> Although not strickly necessary, we can use this path for
>> --enable-hardcoded-path-in-tests as well. I haven't dig info, but it
>> seems the toolchain generated by build-many-glibcs.py still uses adds
>> RPATH by default (instead of DT_RUNPATH with -Wl,--enable-new-dtags).
Fixed.
>> > +else
>> > + cmd="${test_wrapper_env} ${run_program_env} strace ${rtld} \
>> > + --library-path ${library_path} ${test_prog}"
>> > +fi
>> > +
>> > +TIMEOUTFACTOR=${TIMEOUTFACTOR:-1}
>> > +
>> > +case x"${run_program_env}" in
>> > +*glibc.elf.thp=1*)
>> > + strace_expected=yes
>> > + ;;
>> > +*)
>> > + strace_expected=no
>> > + ;;
>> > +esac
>> > +
>> > +# Verify strace is not just present, but works in this environment. If
>> > +# not, skip the test.
>> > +/bin/sh -c \
>> > + "${test_wrapper_env} ${run_program_env} \
>> > + strace -e trace=none -- /bin/true" > /dev/null 2>&1 || exit 77
>> > +
>> > +# Finally the actual test inside the test environment, using the just
>> > +# build ld.so and new libraries to run the THP test under strace.
>> > +if /bin/sh -c \
>> > + "timeout -k 4 $((3*$TIMEOUTFACTOR)) ${cmd} --direct 2>&1 \
>> > + | grep -E \"madvise.*, MADV_HUGEPAGE\""; then
>>
>> I think we should also handle the cases where strace does not know about
>> the MADV_HUGEPAGE. We can use -X raw (added on 4.23 / June 2018) and
>> just use:
>>
>> strace -X raw ... | grep -E 'madvise\(0x[0-9a-f]+, [0-9]+, 0xe\)'
Fixed.
>> Or check for the unknown flags:
>>
>> strace ... | grep -E '\([^,]+, [0-9]+, (0xe|MADV_HUGEmadvisePAGE|0xe /\* MADV_HUGEPAGE \*/|0xe /\* MADV_\?\?\? \*/)\)'
>>
>>
>> > + if test ${strace_expected} = yes; then
>> > + exit 0
>> > + else
>> > + exit 1
>> > + fi
>> > +else
>> > + if test ${strace_expected} = no; then
>> > + exit 0
>> > + else
>> > + exit 1
>> > + fi
>> > +fi
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-pde.c b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-pde.c
>> > new file mode 100644
>> > index 0000000000..3fd01e9bfe
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-pde.c
>> > @@ -0,0 +1,19 @@
>> > +/* Test PDE with THP segment load linked with -Wl,-z,noseparate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-1.c"
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-static.c b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-static.c
>> > new file mode 100644
>> > index 0000000000..d0ae0f1ff0
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code-static.c
>> > @@ -0,0 +1,19 @@
>> > +/* Test static with THP segment load linked with -Wl,-z,noseparate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-1.c"
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code.c b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code.c
>> > new file mode 100644
>> > index 0000000000..5eb1e005ed
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1-no-s-code.c
>> > @@ -0,0 +1,19 @@
>> > +/* Test THP segment load linked with -Wl,-z,noseparate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-1.c"
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1-pde.c b/sysdeps/unix/sysv/linux/tst-thp-1-pde.c
>> > new file mode 100644
>> > index 0000000000..d854dd43da
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1-pde.c
>> > @@ -0,0 +1,19 @@
>> > +/* Test PDE with THP segment load linked with -Wl,-z,separate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-1.c"
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1-static.c b/sysdeps/unix/sysv/linux/tst-thp-1-static.c
>> > new file mode 100644
>> > index 0000000000..66d7e12954
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1-static.c
>> > @@ -0,0 +1,19 @@
>> > +/* Test static with THP segment load linked with -Wl,-z,separate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-1.c"
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-1.c b/sysdeps/unix/sysv/linux/tst-thp-1.c
>> > new file mode 100644
>> > index 0000000000..49eea7069c
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-1.c
>> > @@ -0,0 +1,28 @@
>> > +/* Test THP segment load linked with -Wl,-z,separate-code.
>> > + 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
>> > + <http://www.gnu.org/licenses/>. */
>> > +
>> > +#include "tst-thp-align-check.h"
>> > +
>> > +static int
>> > +do_test (void)
>> > +{
>> > + check_align ("tst-thp-1");
>> > + return 0;
>> > +}
>> > +
>> > +#include <support/test-driver.c>
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-align-check.h b/sysdeps/unix/sysv/linux/tst-thp-align-check.h
>> > new file mode 100644
>> > index 0000000000..5ec7623ee0
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-align-check.h
>> > @@ -0,0 +1,67 @@
>> > +/* 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/support_check_hugetlb.h>
>> > +#include <support/check.h>
>> > +#include <support/xstdio.h>
>> > +#include <support/xunistd.h>
>> > +
>> > +static void
>> > +check_align (const char *name)
>> > +{
>> > + 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");
>> > +
>> > + if (thp_size > MAX_THP_PAGESIZE)
>> > + FAIL_UNSUPPORTED ("THP size exceeds MAX_THP_PAGESIZE.\n");
>> > +
>> > + if (thp_mode != thp_mode_always && thp_mode != thp_mode_madvise)
>> > + FAIL_UNSUPPORTED ("THP mode is not always nor madvise.\n");
>> > +
>> > + 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 (path != NULL && strstr (prot, "x") && strstr (path, name))
>> > + TEST_COMPARE (from % thp_size, 0);
>> > +
>> > + free (prot);
>> > + free (path);
>> > + }
>> > +
>> > + free (line);
>> > + xfclose (f);
>> > +}
>> > diff --git a/sysdeps/unix/sysv/linux/tst-thp-align.c b/sysdeps/unix/sysv/linux/tst-thp-align.c
>> > index 0b3f18e000..2e44109ba6 100644
>> > --- a/sysdeps/unix/sysv/linux/tst-thp-align.c
>> > +++ b/sysdeps/unix/sysv/linux/tst-thp-align.c
>> > @@ -16,129 +16,10 @@
>> > 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>
>> > +#include "tst-thp-align-check.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)
>> > @@ -146,7 +27,7 @@ do_test (void)
>> > void *dl;
>> >
>> > dl = xdlopen (THP_SIZE_MOD_NAME, RTLD_NOW);
>> > - check_align ();
>> > + check_align (THP_SIZE_MOD_NAME);
>> > xdlclose (dl);
>> >
>> > return 0;
>> > diff --git a/sysdeps/unix/sysv/linux/x86/hugepages.h b/sysdeps/unix/sysv/linux/x86/hugepages.h
>> > new file mode 100644
>> > index 0000000000..1a8c370969
>> > --- /dev/null
>> > +++ b/sysdeps/unix/sysv/linux/x86/hugepages.h
>> > @@ -0,0 +1,22 @@
>> > +/* Huge Page support. Linux/x86 version.
>> > + Copyright (C) 2026 Free Software Foundation, Inc.
>> > + Copyright The GNU Toolchain Authors.
>> > + 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/>. */
>> > +
>> > +#define DL_MAP_DEFAULT_THP_PAGESIZE (2 * 1024 * 1024)
>>
>> I think non-PAE i386 kernels has 4 MiB large pages. Not sure it would matter,
>> 32-bit kernels are not pratically used anymore, but maybe making this x86_64
>> for now.
Fixed.
>> > +
>> > +#include_next <hugepages.h>
>> > --
>> > 2.54.0
>> >
>>
>>
--
H.J.
More information about the Libc-alpha
mailing list