Skip to content

Commit 5ea83eb

Browse files
authored
Fix libpython3.12 dlopen crash on Android: set SONAME at link time, drop patchelf (#7)
* revert patchelf changes * fix(android-3.12): set libpython SONAME at link time CPython 3.12's Makefile rule for libpython only passes -Wl,-soname when INSTSONAME != LDLIBRARY. On Android both equal libpython3.X.so, so the else branch runs and the shipped libpython carries no DT_SONAME.
1 parent 3622069 commit 5ea83eb

3 files changed

Lines changed: 30 additions & 24 deletions

File tree

‎.github/workflows/build-python-version.yml‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,6 @@ jobs:
8383
with:
8484
python-version: ${{ env.PYTHON_VERSION }}
8585
- run: python --version
86-
- name: Install build deps
87-
# patchelf is used by android/build.sh (3.12 path) to set SONAME
88-
# on libpython3.X.so, which CPython's Makefile rule otherwise
89-
# skips when LDLIBRARY==INSTSONAME (the Android case).
90-
run: sudo apt-get update && sudo apt-get install -y patchelf
9186
- working-directory: android
9287
shell: bash
9388
run: |

‎android/build.sh‎

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ if [ $version_int -le 312 ]; then
5151
patches+=" soname"
5252
fi
5353
if [ $version_int -eq 312 ]; then
54-
patches+=" bldlibrary grp"
54+
# soname_linktime: makes CPython's Makefile rule emit -Wl,-soname even
55+
# when INSTSONAME == LDLIBRARY (the Android case), so the shipped
56+
# libpython3.X.so carries DT_SONAME. 3.13+ already gets this from its official
57+
# Android tooling; this 3.12-only patch matches that behavior at link time.
58+
patches+=" soname_linktime bldlibrary grp"
5559
fi
5660
for name in $patches; do
5761
patch_file="$script_dir/patches/$name.patch"
@@ -140,24 +144,6 @@ if [ $version_int -le 312 ]; then
140144
make -j $CPU_COUNT
141145
make install prefix=$PREFIX
142146

143-
# CPython's Makefile rule for libpython skips `-Wl,-soname` when
144-
# INSTSONAME == LDLIBRARY, which is the case on Android (both are
145-
# `libpython3.X.so`). The shipped libpython3.X.so therefore has no
146-
# DT_SONAME, so consumer wheels record whichever name the linker
147-
# was asked for in DT_NEEDED — e.g. a `-lpython3` resolved via a
148-
# `libpython3.so` shim writes `libpython3.so` instead of the right
149-
# `libpython3.X.so`. Patching the SONAME in here makes consumer
150-
# DT_NEEDED entries correct regardless of how the link was reached.
151-
# 3.13+ uses CPython's official Android tooling, which sets SONAME
152-
# natively, so this branch only needs the post-install fix.
153-
if command -v patchelf >/dev/null 2>&1; then
154-
patchelf --set-soname "libpython$version_short.so" \
155-
"$PREFIX/lib/libpython$version_short.so"
156-
else
157-
echo "WARNING: patchelf not installed; libpython$version_short.so will lack SONAME." >&2
158-
echo " Consumer wheels may end up with the wrong DT_NEEDED entry." >&2
159-
fi
160-
161147
echo ">>> Replacing host platform"
162148
sed -i -e "s/_PYTHON_HOST_PLATFORM=.*/_PYTHON_HOST_PLATFORM=android-$api_level-$abi/" $PREFIX/lib/python$version_short/config-$version_short/Makefile
163149
else
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CPython 3.12's libpython link rule only passes -Wl,-soname when
2+
INSTSONAME != LDLIBRARY. On Android both equal "libpython3.X.so", so
3+
the else branch runs and the shipped libpython has no DT_SONAME.
4+
Consumer wheels' DT_NEEDED then records whatever name the linker saw
5+
(e.g. "libpython3.so" via a shim), and the device fails to dlopen at
6+
runtime.
7+
8+
Adding -Wl,-h$(INSTSONAME) to the else branch makes ld stamp the
9+
SONAME during the actual link — same shape of fix as CPython 3.13's
10+
official Android tooling. Avoids needing a post-process patchelf step
11+
(which on NDK r27d interacts badly with llvm-strip and produces a
12+
PT_LOAD whose p_offset and p_vaddr violate the ELF spec's
13+
`p_offset ≡ p_vaddr (mod p_align)` requirement, breaking dlopen on
14+
strict bionic and crashing Py_Initialize on permissive bionic).
15+
16+
--- a/Makefile.pre.in
17+
+++ b/Makefile.pre.in
18+
@@ -819,5 +819,5 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
19+
$(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \
20+
$(LN) -f $(INSTSONAME) $@; \
21+
else \
22+
- $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \
23+
+ $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \
24+
fi
25+

0 commit comments

Comments
 (0)