[PATCH v10] elf: Support THP segment load with madvise enabled THP
H.J. Lu
hjl.tools@gmail.com
Tue May 26 22:44:42 GMT 2026
On Wed, May 27, 2026 at 4:29 AM Adhemerval Zanella Netto
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 26/05/26 08:24, H.J. Lu wrote:
> > Changes in v10:
> >
> > 1. Rebased against
> >
> > commit f531cab5891cc71442dcced4abe8ecc6ef15e61f
> > Author: Diego Nieto Cid <dnietoc@gmail.com>
> > Date: Wed May 20 23:49:27 2026 +0100
> >
> > hurd: let the root user raise its priority
> >
> > From dce423132a5fa7ee69ba860876d7b1dc40022985 Mon Sep 17 00:00:00 2001
> > From: "H.J. Lu" <hjl.tools@gmail.com>
> > Date: Mon, 13 Apr 2026 08:23:05 +0800
> > Subject: [PATCH v10] elf: Support THP segment load with madvise enabled THP
> >
> > 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:
>
> Besides ARM, I am seeing RISCV failures as well:
>
> FAIL: elf/tst-thp-1-no-s-code-pde
> FAIL: elf/tst-thp-1-no-s-code-static
> FAIL: elf/tst-thp-1-pde
> FAIL: elf/tst-thp-1-static
>
> riscv64-linux-gnu-rv64imafdc-lp64d-review$ uname -r
> 6.12.74+deb13+1-riscv64
>
> riscv64-linux-gnu-rv64imafdc-lp64d-review$ strace -E GCONV_PATH=iconvdata -E LOCPATH=localedata -E LC_ALL=C -E GLIBC_TUNABLES=glibc.elf.thp=1 elf/tst-thp-1-no-s-code-pde --direct
> execve("elf/tst-thp-1-no-s-code-pde", ["elf/tst-thp-1-no-s-code-pde", "--direct"], 0x5555c49b6b60 /* 36 vars */) = -1 EPERM (Operation not permitted)
> +++ killed by SIGSEGV +++
> Segmentation fault
>
> It seems RISCV default linker scripts sets p_vaddr to 0, different than
> x86_64/aarch64, and /proc/sys/vm/mmap_min_addr is a non-zero value:
>
> riscv64-linux-gnu-rv64imafdc-lp64d-review$ readelf -lW elf/tst-thp-1-pde | awk '/LOAD/{print $3}'
> 0x0000000000000000
> 0x0000000000200000
> 0x0000000002400000
> 0x00000000027ffcb0
> $ ./elf/tst-thp-1-pde --direct ; echo $?
> Segmentation fault
> 139
> $ echo 0 | sudo tee /proc/sys/vm/mmap_min_addr
> 0
> $ ./elf/tst-thp-1-pde --direct ; echo $?
> 0
>
> I think we will need to either bump the text segment base for non-PIE THP tests
> on RISC-V (e.g. add -Wl,-Ttext-segment=0x200000 or --image-base) alongside
> THP-PAGE-SIZE-LDFLAGS *or* skip the non-PIE THP tests when the linker default
> text base < THP page size (detect this at configure).
I changed it to build these executables with
$(load-address-ldflag)=$(THP-PAGE-SIZE).
> I still reviewing the test, some comments below; but in general it looks ok.
>
> >
> > https://sourceware.org/bugzilla/show_bug.cgi?id=34096
> >
> > Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
> > ---
> > csu/libc-start.c | 4 +
> > elf/dl-load.h | 3 +
> > elf/dl-support.c | 6 +
> > elf/rtld.c | 17 +-
> > sysdeps/generic/dl-exec-post.h | 34 ++++
> > sysdeps/generic/dl-load-post.h | 23 +++
> > sysdeps/generic/hugepages.h | 8 +-
> > sysdeps/generic/ldsodefs.h | 12 ++
> > sysdeps/unix/sysv/linux/Makefile | 152 +++++++++++++++++-
> > sysdeps/unix/sysv/linux/arm/Makefile | 7 +
> > sysdeps/unix/sysv/linux/dl-exec-post.h | 126 +++++++++++++++
> > sysdeps/unix/sysv/linux/dl-load-post.h | 32 ++++
> > .../unix/sysv/linux/dl-map-segment-align.c | 33 ++--
> > .../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 | 63 ++++++++
> > .../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 | 124 ++++++++++++++
> > sysdeps/unix/sysv/linux/tst-thp-align.c | 123 +-------------
> > sysdeps/unix/sysv/linux/x86/hugepages.h | 22 +++
> > 27 files changed, 775 insertions(+), 164 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..bb106b524a 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),
> > @@ -300,6 +301,9 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
> > __pointer_chk_guard_local = pointer_chk_guard;
> > # endif
> >
> > + struct link_map *main_map = _dl_get_dl_main_map ();
> > + _dl_executable_postprocess (main_map, GL(dl_phdr), GL(dl_phnum));
> > +
> > /* Now that the TCB, canary, and pointer guard are in place, run the
> > deferred IFUNC relocations. For non-PIE static binaries this is
> > ARCH_SETUP_IREL (apply_irel); for static-pie it is the IRELATIVE
> > 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..a8114de003 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
> > +int _dl_elf_thp_control = -1;
> > +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..e1ec899756 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>
> >
> > @@ -323,6 +324,9 @@ struct rtld_global _rtld_global =
> > /* Generally the default presumption without further information is an
> > * executable stack but this is not true for all platforms. */
> > ._dl_stack_prot_flags = DEFAULT_STACK_PROT_PERMS,
> > +#ifdef HAVE_THP
> > + ._dl_elf_thp_control = -1,
> > +#endif
> > #ifdef _LIBC_REENTRANT
> > ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
> > ._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
> > @@ -1209,14 +1213,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 +1587,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/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..f7f4957e79 100644
> > --- a/sysdeps/generic/hugepages.h
> > +++ b/sysdeps/generic/hugepages.h
> > @@ -26,10 +26,10 @@ 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 +45,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..ee58e2778a 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,17 @@ 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:
> > + > 0: Enabled by GLIBC_TUNABLES=glibc.elf.thp=1.
> > + 0: Disabled by GLIBC_TUNABLES=glibc.elf.thp=0.
> > + < 0: To be enabled or disabled by GLIBC_TUNABLES. */
> > + EXTERN int _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..edf48c93de 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 \
>
> I think there is no need to duplicate this here.
It is needed. Otherwise, tst-thp-1-static won't run.
> > 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,151 @@ 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)
> > +
> > +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
> > +
> > +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)
> > +LDFLAGS-tst-thp-1-static = -Wl,-z,separate-code $(THP-PAGE-SIZE-LDFLAGS)
> > +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)
> > +LDFLAGS-tst-thp-1-no-s-code-static = -Wl,-z,noseparate-code \
> > + $(THP-PAGE-SIZE-LDFLAGS)
> > +
> > +$(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)
> > +# 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)ld.so \
> > + $(objpfx)tst-thp-1-static
> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=1' \
> > + '$(rpath-link)' $(objpfx)tst-thp-1-static > $@; \
> > + $(evaluate-test)
> > +
> > +$(objpfx)strace-tst-thp-1-static-disabled.out: \
> > + $(..)sysdeps/unix/sysv/linux/strace-tst-thp.sh $(objpfx)ld.so \
> > + $(objpfx)tst-thp-1-static
> > + $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
> > + '$(run-program-env) GLIBC_TUNABLES=glibc.elf.thp=0' \
> > + '$(rpath-link)' $(objpfx)tst-thp-1-static > $@; \
> > + $(evaluate-test)
>
> This would check the loader fails patch to load a static binary, not the
> tst-thp-1-static binary itselt. I think it passes for accidental reasons,
> so it would be better to issue the test directly.
Fixed.
> > +
> > +$(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..1ee8bec9b9 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
> > +
> > +# These 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-pde = yes
> > +test-xfail-tst-thp-1-no-s-code-static = yes
> > +test-xfail-tst-thp-1-pde = yes
> > +test-xfail-tst-thp-1-static = yes
> > 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..3ce7a53b33
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/dl-exec-post.h
> > @@ -0,0 +1,126 @@
> > +/* _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/>. */
> > +
>
> I think it is worth a comment that when the architecture defines
> DL_MAP_DEFAULT_THP_PAGESIZE, the sysfs is not read and variable OMD
> are not considered. Also add the rationale why ssyfs is not consulted
> and madvise is always applied unconditionally.
There are
/* 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;
}
How should comments be improved?
> > +static inline void
> > +_dl_get_thp_config (void)
> > +{
> > + /* Check if there is GLIBC_TUNABLES=glibc.elf.thp=[0|1]. */
> > + GL(dl_elf_thp_control) = TUNABLE_GET_FULL (glibc, elf, thp, int32_t,
> > + NULL);
> > +
> > + /* Return if the tunable is not set or THP is disabled by the
> > + tunable. */
> > + if (GL(dl_elf_thp_control) == 0)
> > + 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 systemcall, 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) = 0;
> > + 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) != -1)
> > + return;
> > +
> > + _dl_get_thp_config ();
> > +
> > + /* Return if THP segment load isn't enabled. */
> > + if (GL(dl_elf_thp_control) <= 0)
> > + 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)
> > + __madvise ((void *) (main_map->l_addr + ph->p_vaddr),
> > + ph->p_memsz, MADV_HUGEPAGE);
> > +}
> > 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..88467764d4
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/dl-load-post.h
> > @@ -0,0 +1,32 @@
> > +/* _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)))
> > + __madvise ((void *) (l->l_addr + c->mapstart),
> > + c->mapend - c->mapstart, MADV_HUGEPAGE);
> > +}
> > diff --git a/sysdeps/unix/sysv/linux/dl-map-segment-align.c b/sysdeps/unix/sysv/linux/dl-map-segment-align.c
> > index a39e74d91b..1260b26c22 100644
> > --- a/sysdeps/unix/sysv/linux/dl-map-segment-align.c
> > +++ b/sysdeps/unix/sysv/linux/dl-map-segment-align.c
> > @@ -17,38 +17,23 @@
> > 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) <= 0 || 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)
>
> Maybe now that 'thp_pagesize <= MAX_THP_PAGESIZE' is removed add a:
>
> _Static_assert (DL_MAP_DEFAULT_THP_PAGESIZE <= MAX_THP_PAGESIZE, ...)
>
These are the removed codes. The new one is
ElfW (Addr)
_dl_map_segment_align (const struct loadcmd *c, ElfW (Addr) p_align_max)
{
size_t thp_pagesize = GL(dl_elf_thp_pagesize);
if (GL(dl_elf_thp_control) <= 0 || p_align_max >= thp_pagesize)
return p_align_max;
/* 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;
}
> > + /* 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..39b9953adb
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/strace-tst-thp.sh
> > @@ -0,0 +1,63 @@
> > +#!/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
> > +
> > +rtld="$1"
> > +test_wrapper_env="$2"
> > +run_program_env="$3"
> > +library_path="$4"
> > +test_prog="$5"
> > +
> > +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)) \
> > + ${test_wrapper_env} ${run_program_env} strace ${rtld} \
> > + --library-path ${library_path} ${test_prog} --direct 2>&1 \
> > + | grep -E \"madvise.*, MADV_HUGEPAGE\""; then
> > + 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..8f1efc0ef1
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/tst-thp-align-check.h
> > @@ -0,0 +1,124 @@
> > +/* 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/xstdio.h>
> > +#include <support/xunistd.h>
> > +#undef attribute_hidden
> > +#define attribute_hidden
> > +#include <hugepages.h> /* For enum thp_mode_t and MAX_THP_PAGESIZE. */
> > +#undef attribute_hidden
> > +
> > +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 (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 (strstr (prot, "x") && strstr (path, name))
> > + TEST_COMPARE (from % thp_size, 0);
> > +
> > + 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)
> > +
> > +#include_next <hugepages.h>
> > --
> > 2.54.0
> >
>
--
H.J.
More information about the Libc-alpha
mailing list