[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