Problem description
While trying to fix PyInstaller's Termux CI pipeline for the recent update of Termux python to 3.13, we also noticed a considerable slow-down of the pipeline, which previously took ~40 minutes to finish, and now takes ~2 hours.
The slow-down seems to come from binary dependency analysis step across all tests; this step makes heavy use of the ldd utility to analyze dependencies of binaries collected from the build environment. The time-frame of the change seems to suggest that this is caused by switch from GNU binutils to LLVM binutils (#28586), which changed the readelf utility used under-the-hood by the ldd script. (We were installing the "old" binutils package in our Termux CI environment).
Comparing the timings of an example call to (system-provided) LLVM readelf with timings of a call to manually-compiled GNU readelf shows an order of magnitude in difference (in a termux/termux-docker:x86_64 container running under podman on Fedora Linux host):
time with LLVM readelf
$ time readelf -d /data/data/com.termux/files/usr/lib/libpython3.13.so
Dynamic section at offset 0x4967d8 contains 30 entries:
Tag Type Name/Value
0x000000000000001d (RUNPATH) Library runpath: [/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib]
0x0000000000000001 (NEEDED) Shared library: [libandroid-support.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x000000000000000e (SONAME) Library soname: [libpython3.13.so]
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) NOW
0x0000000000000007 (RELA) 0x193f0
0x0000000000000008 (RELASZ) 385776 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffff9 (RELACOUNT) 11373
0x0000000000000017 (JMPREL) 0x776e0
0x0000000000000002 (PLTRELSZ) 29184 (bytes)
0x0000000000000003 (PLTGOT) 0x49b0d0
0x0000000000000014 (PLTREL) RELA
0x0000000000000006 (SYMTAB) 0x2d0
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000005 (STRTAB) 0xfeac
0x000000000000000a (STRSZ) 38207 (bytes)
0x000000006ffffef5 (GNU_HASH) 0xcdc8
0x0000000000000019 (INIT_ARRAY) 0x49a7d0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x49a7c0
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
0x000000006ffffff0 (VERSYM) 0xbdc0
0x000000006ffffffe (VERNEED) 0xcd54
0x000000006fffffff (VERNEEDNUM) 3
0x0000000000000000 (NULL) 0x0
real 0m0.021s
user 0m0.005s
sys 0m0.016s
time with GNU readelf
$ time ./binutils/readelf -d /data/data/com.termux/files/usr/lib/libpython3.13.so
Dynamic section at offset 0x4967d8 contains 30 entries:
Tag Type Name/Value
0x000000000000001d (RUNPATH) Library runpath: [/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib]
0x0000000000000001 (NEEDED) Shared library: [libandroid-support.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x000000000000000e (SONAME) Library soname: [libpython3.13.so]
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x0000000000000007 (RELA) 0x193f0
0x0000000000000008 (RELASZ) 385776 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffff9 (RELACOUNT) 11373
0x0000000000000017 (JMPREL) 0x776e0
0x0000000000000002 (PLTRELSZ) 29184 (bytes)
0x0000000000000003 (PLTGOT) 0x49b0d0
0x0000000000000014 (PLTREL) RELA
0x0000000000000006 (SYMTAB) 0x2d0
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000005 (STRTAB) 0xfeac
0x000000000000000a (STRSZ) 38207 (bytes)
0x000000006ffffef5 (GNU_HASH) 0xcdc8
0x0000000000000019 (INIT_ARRAY) 0x49a7d0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x49a7c0
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
0x000000006ffffff0 (VERSYM) 0xbdc0
0x000000006ffffffe (VERNEED) 0xcd54
0x000000006fffffff (VERNEEDNUM) 3
0x0000000000000000 (NULL) 0x0
real 0m0.002s
user 0m0.001s
sys 0m0.002s
(While the absolute times are small, but readelf ends up being called multiple times as ldd recursively inspects the dependencies of the given file; and similarly PyInstaller calls ldd on every collected binary; so the
difference keeps accumulating).
I'm not sure if this is a general issue with LLVM binutils, or specific to Termux, because the difference between GNU readelf and LLVM readelf on the host system (both packaged by Fedora) seems to be less drastic:
time with LLVM readelf (Fedora Linux)
$ time llvm-readelf -d /usr/lib64/libpython3.13.so.1.0
Dynamic section at offset 0x4c8760 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libpython3.13.so.1.0]
0x000000000000000c (INIT) 0x304
0x000000000000000d (FINI) 0x2d0164
0x0000000000000019 (INIT_ARRAY) 0x479968
0x000000000000001b (INIT_ARRAYSZ) 16 (bytes)
0x000000000000001a (FINI_ARRAY) 0x479978
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x2d1000
0x0000000000000005 (STRTAB) 0x2e0570
0x0000000000000006 (SYMTAB) 0x2d42a0
0x000000000000000a (STRSZ) 39151 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x4c9990
0x0000000000000002 (PLTRELSZ) 9432 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x307568
0x0000000000000007 (RELA) 0x2eb080
0x0000000000000008 (RELASZ) 115944 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) NOW
0x000000006ffffffe (VERNEED) 0x2eaea0
0x000000006fffffff (VERNEEDNUM) 2
0x000000006ffffff0 (VERSYM) 0x2e9e60
0x0000000000000024 (RELR) 0x309a40
0x0000000000000023 (RELRSZ) 4760 (bytes)
0x0000000000000025 (RELRENT) 8 (bytes)
0x0000000000000000 (NULL) 0x0
real 0m0,006s
user 0m0,004s
sys 0m0,002s
time with GNU readelf (Fedora Linux)
$ time readelf -d /usr/lib64/libpython3.13.so.1.0
Dynamic section at offset 0x4c8760 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libpython3.13.so.1.0]
0x000000000000000c (INIT) 0x304
0x000000000000000d (FINI) 0x2d0164
0x0000000000000019 (INIT_ARRAY) 0x479968
0x000000000000001b (INIT_ARRAYSZ) 16 (bytes)
0x000000000000001a (FINI_ARRAY) 0x479978
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x2d1000
0x0000000000000005 (STRTAB) 0x2e0570
0x0000000000000006 (SYMTAB) 0x2d42a0
0x000000000000000a (STRSZ) 39151 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x4c9990
0x0000000000000002 (PLTRELSZ) 9432 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x307568
0x0000000000000007 (RELA) 0x2eb080
0x0000000000000008 (RELASZ) 115944 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffffe (VERNEED) 0x2eaea0
0x000000006fffffff (VERNEEDNUM) 2
0x000000006ffffff0 (VERSYM) 0x2e9e60
0x0000000000000024 (RELR) 0x309a40
0x0000000000000023 (RELRSZ) 4760 (bytes)
0x0000000000000025 (RELRENT) 8 (bytes)
0x0000000000000000 (NULL) 0x0
real 0m0,003s
user 0m0,001s
sys 0m0,002s
And on both systems, same version of LLVM readelf is used:
readelf --version (Termux)
$ readelf --version
LLVM (http://llvm.org/):
LLVM version 21.1.8
Optimized build.
llvm-readelf --version (Fedora)
$ llvm-readelf --version
LLVM (http://llvm.org/):
LLVM version 21.1.8
Optimized build.
What steps will reproduce the bug?
- Install
binutils package on the system
- Run system-installed
readelf against a system-installed shared library and use time to monitor execution time
- Compile GNU
readelf from source
- Run manually-compiled GNU
readelf against the same system-installed shared library and use time to monitor execution time
- Compare the times
What is the expected behavior?
Execution time of LLVM readelf should be comparable to (same order of magnitude) GNU readelf.
System information
Termux running in `termux/termux-docker:x86_64` container running under `podman` on Fedora Linux host:
$ termux-info
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
/data/data/com.termux/files/usr/bin/getprop: 3: exec: /system/bin/getprop: not found
Termux Variables:
unsupported
Packages CPU architecture:
x86_64
Subscribed repositories:
# sources.list
deb https://packages-cf.termux.dev/apt/termux-main stable main
Updatable packages:
All packages up to date
termux-tools version:
1.45.0
Android version:
Kernel build information:
Linux b25c81460349 6.19.6-200.fc43.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Mar 5 00:10:35 UTC 2026 x86_64 Android
Device manufacturer:
Device model:
Supported ABIs:
SUPPORTED_ABIS:
SUPPORTED_32_BIT_ABIS:
SUPPORTED_64_BIT_ABIS:
LD Variables:
LD_LIBRARY_PATH=
LD_PRELOAD=/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so
Problem description
While trying to fix PyInstaller's Termux CI pipeline for the recent update of Termux python to 3.13, we also noticed a considerable slow-down of the pipeline, which previously took ~40 minutes to finish, and now takes ~2 hours.
The slow-down seems to come from binary dependency analysis step across all tests; this step makes heavy use of the
lddutility to analyze dependencies of binaries collected from the build environment. The time-frame of the change seems to suggest that this is caused by switch from GNUbinutilsto LLVMbinutils(#28586), which changed thereadelfutility used under-the-hood by thelddscript. (We were installing the "old"binutilspackage in our Termux CI environment).Comparing the timings of an example call to (system-provided) LLVM
readelfwith timings of a call to manually-compiled GNUreadelfshows an order of magnitude in difference (in atermux/termux-docker:x86_64container running underpodmanon Fedora Linux host):time with LLVM readelf
time with GNU readelf
(While the absolute times are small, but
readelfends up being called multiple times aslddrecursively inspects the dependencies of the given file; and similarly PyInstaller callslddon every collected binary; so thedifference keeps accumulating).
I'm not sure if this is a general issue with LLVM binutils, or specific to Termux, because the difference between GNU
readelfand LLVMreadelfon the host system (both packaged by Fedora) seems to be less drastic:time with LLVM readelf (Fedora Linux)
time with GNU readelf (Fedora Linux)
And on both systems, same version of LLVM
readelfis used:readelf --version (Termux)
llvm-readelf --version (Fedora)
What steps will reproduce the bug?
binutilspackage on the systemreadelfagainst a system-installed shared library and usetimeto monitor execution timereadelffrom sourcereadelfagainst the same system-installed shared library and usetimeto monitor execution timeWhat is the expected behavior?
Execution time of LLVM
readelfshould be comparable to (same order of magnitude) GNUreadelf.System information