Skip to content

v3.12 Android tarball ships an unloadable libpython3.12.so after PR #5 patchelf step #6

@ndonkoHenri

Description

@ndonkoHenri

The python-android-mobile-forge-3.12.tar.gz asset re-uploaded after PR #5 merged ships a libpython3.12.so that Android's bionic linker refuses to load. End users' Flet apps started crashing at startup roughly the moment the new asset went live.

User-reported error (Android API ~30, real device):

[serious_python] Dart error running Python: Failed to load dynamic library 'libpython3.12.so':
dlopen failed: empty/missing DT_HASH/DT_GNU_HASH (new hash type from the future?)

Reproduced locally on a clean arm64 emulator (API 35, Pixel 9 Pro XL) — different surface message, same broken file:

$ adb shell /system/bin/linker64 /data/local/tmp/libpython3.12.so
Illegal instruction          # loader segfaults before reaching the user

# tombstone:
F DEBUG : #00 pc 0000000000000000  /data/local/tmp/libpython3.12.so

Timeline

Event When (UTC)
commit 49d866c "android: set SONAME on libpython3.X.so via patchelf (3.12 path)" 2026-06-01T18:00
PR #5 merged to main 2026-06-01T18:33
v3.12 release asset python-android-mobile-forge-3.12.tar.gz re-uploaded 2026-06-01T18:47
Users report broken Flet apks "yesterday evening" 2026-06-01 evening CEST

The asset's updated_at matches the merge, and the breakage matches the asset upload.

Forensic evidence

Extracted install/android/arm64-v8a/python-3.12.12/lib/libpython3.12.so from the live tarball download. llvm-readelf -d shows the patchelf intervention took effect — SONAME=libpython3.12.so is now set, which is the intended outcome — but llvm-readelf -lW reveals patchelf injected a 4th PT_LOAD with 64KB alignment alongside the original 16KB-aligned segments:

Program Headers:
LOAD  off=0x000000   vaddr=0x000000  filesz=0x4915c0  R E  align=0x4000   # original
LOAD  off=0x4915c0   vaddr=0x4955c0  filesz=0x0775f8  RW   align=0x4000   # original
LOAD  off=0x508bb8   vaddr=0x510bb8  filesz=0x15a0f8  RW   align=0x4000   # original
LOAD  off=0x18a0000  vaddr=0x670000  filesz=0x009b50  RW   align=0x10000  # INJECTED by patchelf

The mismatched alignment + the new .dynstr/.dynamic indices the injected segment carries are what bionic refuses to consume. The exact error string varies by Android API level — "empty/missing DT_HASH/DT_GNU_HASH (new hash type from the future?)" on the user's device, "Illegal instruction" on my API-35 emulator — but it's the same broken artifact.

DT_GNU_HASH itself does point at valid hash data when read raw (nbuckets=433, sane bloom_size/shift), so the corruption isn't in the hash table contents directly. It's the file as a whole that the loader can't reconcile.

Likely cause

The CI runner's sudo apt-get install -y patchelf pulls Ubuntu's stock package (patchelf 0.14 on ubuntu-latest), which is known to mishandle aarch64 ELFs when relocating segments to fit a longer string in .dynstr — see NixOS/patchelf#446, #377. Newer patchelf (0.18+) ships fixes for this class of failure.

Fix options

  1. Revert 49d866c and rely on replace_libpython_stub alone. Feodor's own table on PR Fix Android mobile-forge support artifact relocation #5 documents both fixes as "belt-and-suspenders", but replace_libpython_stub is the only one that's actually load-bearing for the original cannot locate symbol "PyType_IsSubtype" failure he traced — it makes consumer wheels' DT_NEEDED entries correct without touching the libpython binary itself. This is the safest unblock.

  2. Pin a newer patchelf in the workflow (pip install patchelf==0.18.0 exposes a maintained wheel; or grab the official aarch64 binary from the patchelf release page) before running the --set-soname step.

  3. Skip patchelf and use the linker directly at libpython build time — add -Wl,-soname,libpython3.X.so via a Makefile override so the SONAME is set during CPython's link step, not retroactively. This is what 3.13+ does natively.

Option 1 is the fastest path to a fixed v3.12 tarball. Option 3 is the cleanest long-term.

What end users need

Nothing client-side fixes this — the broken .so is fetched by every flet build apk until the v3.12 asset is replaced. Once the asset is re-published with a working libpython3.12.so, the user's next flet build apk produces a working APK with no app-side change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions