Skip to content

[Bug]: ldd in Termux is much slower than ldd in GNU/Linux (and LLVM readelf is slower than GNU readelf) #28904

@rokm

Description

@rokm

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?

  1. Install binutils package on the system
  2. Run system-installed readelf against a system-installed shared library and use time to monitor execution time
  3. Compile GNU readelf from source
  4. Run manually-compiled GNU readelf against the same system-installed shared library and use time to monitor execution time
  5. 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

Metadata

Metadata

Assignees

Labels

bug reportSomething is not working properlypythonIssue is about Python related stuff, including pipupstream issueIt is an upstream issue

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions