[PATCH v5] elf: Support THP segment load with madvise enabled THP

H.J. Lu hjl.tools@gmail.com
Wed Apr 22 22:28:27 GMT 2026


On Wed, Apr 22, 2026 at 10:41 PM Adhemerval Zanella Netto
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 20/04/26 22:31, H.J. Lu wrote:
> >
> > The added THP tests pass on x86 and aarch64.  Some of them failed on
> > arm:
> >
> > https://patchwork.sourceware.org/project/glibc/patch/CAMe9rOqKfFXMDY07GRuppudP3V9fsCDXoyxDesPMNQLDrRhzvg@mail.gmail.com/
> >
> > Produces 4 regressions:
> >    |
> >    | regressions.sum:
> >    | Running glibc:elf ...
> >    | 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
> >
> > It looks like PIE works, but PDE and static PIE don't work on arm.  The
> > Linaro report doesn't have useful information for these failures.  My
> > arm glibc test binaries look like
> >
>
> It seems to be a limitation of arm32 kABI. The strace of elf/tst-thp-1-static
> shows that execve failed to load the binary:
>
>   $ strace -f elf/tst-thp-1-static --direct
>   execve("elf/tst-thp-1-static", ["elf/tst-thp-1-static", "--direct"], 0xbeda8468 /* 31 vars */) = -1 EINVAL (Invalid argument)
>   +++ killed by SIGSEGV +++
>   Segmentation fault
>
> And tracing where kernel triggers and error shows:
>
>   # echo function_graph > /sys/kernel/debug/tracing/current_tracer
>   # echo vm_mmap > /sys/kernel/debug/tracing/set_graph_function
>   # echo 0 > /sys/kernel/debug/tracing/tracing_on
>
>   # Clear the buffer
>   echo > /sys/kernel/debug/tracing/trace
>
>   # Filter to just the shell's PID
>   # echo $$ > /sys/kernel/debug/tracing/set_ftrace_pid
>
>   # echo 1 > /sys/kernel/debug/tracing/tracing_on
>   # elf/tst-thp-1-static --direct
>   # echo 0 > /sys/kernel/debug/tracing/tracing_on
>
>   # cat /sys/kernel/debug/tracing/trace
>
> The trace output shows:
>
> [...]
>  3)               |      do_mmap() {
>  3)   9.760 us    |        __get_unmapped_area();
>  3) + 32.176 us   |      }
>  3)   1.344 us    |      up_write();
>  3)   7.152 us    |      userfaultfd_unmap_complete();
> [...]
>
> The ARM defines a arch_mmap_check hook (arch/arm/include/uapi/asm/mman.h):
>
>   #define arch_mmap_check(addr, len, flags) \
>           (((flags) & MAP_FIXED && (addr) < FIRST_USER_ADDRESS) ? -EINVAL : 0)
>
> which is the first thing called by __get_unmapped_area. For MMU configuration,
> FIRST_USER_ADDRESS is 2 times the page size:
>
> arch/arm/include/asm/pgtable.h:
>   /*
>    * This is the lowest virtual address we can permit any user space
>    * mapping to be mapped at.  This is particularly important for
>    * non-high vector CPUs.
>    */
>   #define FIRST_USER_ADDRESS      (PAGE_SIZE * 2)
>
> The kernel issues arch_mmap_check(0x0, 0x1000, MAP_FIXED|...), since the file
> is ET_EXEC (and SUPPORT_STATIC_PIE is not currently defined for arm32):
>
> fs/binfmt_elf.c:
> 1066                 } else if (elf_ex->e_type == ET_EXEC) {
> 1067                         /*
> 1068                          * This logic is run once for the first LOAD Program
> 1069                          * Header for ET_EXEC binaries. No special handling
> 1070                          * is needed.
> 1071                          */
> 1072                         elf_flags |= MAP_FIXED_NOREPLACE;
>
> Afaik the FIRST_USER_ADDRESS is a requirement to handle the exception vector table
> placement in ARMv4T/ARMv5 eras and with modern kernels on recent hardware all
> the required machinery is mapped on high addresses:
>
>   $ cat /proc/self/maps
>   [...]
>   bed3d000-bed5e000 rw-p 00000000 00:00 0          [stack]
>   beda9000-bedaa000 r-xp 00000000 00:00 0          [sigpage]
>   bedaa000-bedae000 r--p 00000000 00:00 0          [vvar]
>   bedae000-bedb0000 r-xp 00000000 00:00 0          [vdso]
>   ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]
>
> To test this, I removed the arch_mmap_check and process startup fails with a different
> error:
>
>   $ strace -f elf/tst-thp-1-static --direct
>   execve("elf/tst-thp-1-static", ["elf/tst-thp-1-static", "--direct"], 0xbeda8468 /* 31 vars */) = -1 EPERM (Operation not permitted)
>   +++ killed by SIGSEGV +++
>   Segmentation fault
>
> And this is due a missing capability:
>
>   $ ./tst-thp-1-static
>   Segmentation fault
>   $ sudo setcap cap_sys_rawio+ep tst-thp-1-static
>   $ ./tst-thp-1-static
>   $
>
> This is the same issue for dynamic linked binaries:
>
>   $ strace -f -e mmap ./elf/ld.so --library-path . elf/tst-thp-1-pde --direct
>   [pid 1197551] mmap(NULL, 73728, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = -1 EPERM (Operation not permitted)
>
> So in theory it would be possible to run such binaries on arm32, but I do not
> know all the implications of allowing binaries mapping the low addresses
> (and requiring cap_sys_rawio+ep has other implications as well).
>
> It seems to be a ARM32 idiosyncrasy, so I think maybe we should enable this along
> with arch-specific knob as we for the static-pie (SUPPORT_STATIC_PIE).

I added:

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

to the v6 patch.

Thanks.

-- 
H.J.


More information about the Libc-alpha mailing list