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

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed Apr 22 14:40:54 GMT 2026



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).


More information about the Libc-alpha mailing list