diff --git a/.github/workflows/build-python-version.yml b/.github/workflows/build-python-version.yml index 6b65af4..3bb6f6c 100644 --- a/.github/workflows/build-python-version.yml +++ b/.github/workflows/build-python-version.yml @@ -83,11 +83,6 @@ jobs: with: python-version: ${{ env.PYTHON_VERSION }} - run: python --version - - name: Install build deps - # patchelf is used by android/build.sh (3.12 path) to set SONAME - # on libpython3.X.so, which CPython's Makefile rule otherwise - # skips when LDLIBRARY==INSTSONAME (the Android case). - run: sudo apt-get update && sudo apt-get install -y patchelf - working-directory: android shell: bash run: | diff --git a/android/build.sh b/android/build.sh index 59a124f..478e01f 100755 --- a/android/build.sh +++ b/android/build.sh @@ -51,7 +51,11 @@ if [ $version_int -le 312 ]; then patches+=" soname" fi if [ $version_int -eq 312 ]; then - patches+=" bldlibrary grp" + # soname_linktime: makes CPython's Makefile rule emit -Wl,-soname even + # when INSTSONAME == LDLIBRARY (the Android case), so the shipped + # libpython3.X.so carries DT_SONAME. 3.13+ already gets this from its official + # Android tooling; this 3.12-only patch matches that behavior at link time. + patches+=" soname_linktime bldlibrary grp" fi for name in $patches; do patch_file="$script_dir/patches/$name.patch" @@ -140,24 +144,6 @@ if [ $version_int -le 312 ]; then make -j $CPU_COUNT make install prefix=$PREFIX - # CPython's Makefile rule for libpython skips `-Wl,-soname` when - # INSTSONAME == LDLIBRARY, which is the case on Android (both are - # `libpython3.X.so`). The shipped libpython3.X.so therefore has no - # DT_SONAME, so consumer wheels record whichever name the linker - # was asked for in DT_NEEDED — e.g. a `-lpython3` resolved via a - # `libpython3.so` shim writes `libpython3.so` instead of the right - # `libpython3.X.so`. Patching the SONAME in here makes consumer - # DT_NEEDED entries correct regardless of how the link was reached. - # 3.13+ uses CPython's official Android tooling, which sets SONAME - # natively, so this branch only needs the post-install fix. - if command -v patchelf >/dev/null 2>&1; then - patchelf --set-soname "libpython$version_short.so" \ - "$PREFIX/lib/libpython$version_short.so" - else - echo "WARNING: patchelf not installed; libpython$version_short.so will lack SONAME." >&2 - echo " Consumer wheels may end up with the wrong DT_NEEDED entry." >&2 - fi - echo ">>> Replacing host platform" sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $PREFIX/lib/python$version_short/config-$version_short/Makefile else diff --git a/android/patches/soname_linktime.patch b/android/patches/soname_linktime.patch new file mode 100644 index 0000000..9848406 --- /dev/null +++ b/android/patches/soname_linktime.patch @@ -0,0 +1,25 @@ +CPython 3.12's libpython link rule only passes -Wl,-soname when +INSTSONAME != LDLIBRARY. On Android both equal "libpython3.X.so", so +the else branch runs and the shipped libpython has no DT_SONAME. +Consumer wheels' DT_NEEDED then records whatever name the linker saw +(e.g. "libpython3.so" via a shim), and the device fails to dlopen at +runtime. + +Adding -Wl,-h$(INSTSONAME) to the else branch makes ld stamp the +SONAME during the actual link — same shape of fix as CPython 3.13's +official Android tooling. Avoids needing a post-process patchelf step +(which on NDK r27d interacts badly with llvm-strip and produces a +PT_LOAD whose p_offset and p_vaddr violate the ELF spec's +`p_offset ≡ p_vaddr (mod p_align)` requirement, breaking dlopen on +strict bionic and crashing Py_Initialize on permissive bionic). + +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -819,5 +819,5 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) + $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(LN) -f $(INSTSONAME) $@; \ + else \ +- $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ ++ $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + fi +