You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
[serious_python] Dart error running Python: Failed to load dynamic library 'libpython3.12.so':
dlopen failed: empty/missing DT_HASH/DT_GNU_HASH (new hashtype 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)"
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
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.
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.
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.
The
python-android-mobile-forge-3.12.tar.gzasset re-uploaded after PR #5 merged ships alibpython3.12.sothat 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):
Reproduced locally on a clean arm64 emulator (API 35, Pixel 9 Pro XL) — different surface message, same broken file:
Timeline
49d866c"android: set SONAME on libpython3.X.so via patchelf (3.12 path)"python-android-mobile-forge-3.12.tar.gzre-uploadedThe asset's
updated_atmatches the merge, and the breakage matches the asset upload.Forensic evidence
Extracted
install/android/arm64-v8a/python-3.12.12/lib/libpython3.12.sofrom the live tarball download.llvm-readelf -dshows the patchelf intervention took effect —SONAME=libpython3.12.sois now set, which is the intended outcome — butllvm-readelf -lWreveals patchelf injected a 4thPT_LOADwith 64KB alignment alongside the original 16KB-aligned segments:The mismatched alignment + the new
.dynstr/.dynamicindices 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_HASHitself 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 patchelfpulls Ubuntu's stock package (patchelf 0.14 onubuntu-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
Revert
49d866cand rely onreplace_libpython_stubalone. Feodor's own table on PR Fix Android mobile-forge support artifact relocation #5 documents both fixes as "belt-and-suspenders", butreplace_libpython_stubis the only one that's actually load-bearing for the originalcannot 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.Pin a newer patchelf in the workflow (
pip install patchelf==0.18.0exposes a maintained wheel; or grab the official aarch64 binary from the patchelf release page) before running the--set-sonamestep.Skip patchelf and use the linker directly at libpython build time — add
-Wl,-soname,libpython3.X.sovia 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
.sois fetched by everyflet build apkuntil the v3.12 asset is replaced. Once the asset is re-published with a workinglibpython3.12.so, the user's nextflet build apkproduces a working APK with no app-side change.