From 88a7e4ff76c0fcda02379f6747e61ca9d9049299 Mon Sep 17 00:00:00 2001 From: Chen Mulong Date: Thu, 22 Jan 2026 16:49:24 +0800 Subject: [PATCH 1/2] gh-104612: Fix libpython3.so stable ABI on Linux using patchelf The previous implementation of libpython3.so created an empty wrapper library that recorded libpython3.X.so in DT_NEEDED but did not properly re-export symbols. The program with proper Py_LIMITED_API defined cannot actually linked with libpython3.so (-lpython3), and it makes the stable API pointless. And libpython3.so cannot simply be a symbolic link to the libpython3.X.so, since due to the SONAME contains the minor version, error will still be reported during the runtime with a different pythons so version. This fix uses patchelf on Linux to copy the versioned library and set its SONAME to libpython3.so, ensuring binaries get the correct DT_NEEDED entry for stable ABI compatibility. - Add PATCHELF variable detection in configure.ac (Linux only) - Require patchelf on Linux, error if not found - Update libpython3.so target to use patchelf when available - Fall back to original behavior on non-Linux platforms A program linked with the newly patched libpython3.so and stable API, can run with the previous empty wrapper libpython3.so. --- Makefile.pre.in | 9 ++++++++- configure | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 11 ++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 8531162943ae35..479ceaabb6a0d4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -67,6 +67,9 @@ SHELL= /bin/sh -e # Use this to make a link between python$(VERSION) and python in $(BINDIR) LN= @LN@ +# patchelf is used on Linux to set SONAME of libpython3.so for stable ABI +PATCHELF= @PATCHELF@ + # Portable install script (configure doesn't always guess right) INSTALL= @INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ @@ -1026,7 +1029,11 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) fi libpython3.so: libpython$(LDVERSION).so - $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + if test -n "$(PATCHELF)"; then \ + cp $^ $@ && $(PATCHELF) --set-soname $@ $@; \ + else \ + $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^; \ + fi libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ diff --git a/configure b/configure index c826a1bb85667b..d9b728fd758eb9 100755 --- a/configure +++ b/configure @@ -941,6 +941,7 @@ DEF_MAKE_RULE DEF_MAKE_ALL_RULE ABI_THREAD ABIFLAGS +PATCHELF LN MKDIR_P INSTALL_DATA @@ -8265,6 +8266,59 @@ if test -z "$LN" ; then esac fi +# patchelf is required on Linux to set the SONAME of libpython3.so for stable ABI support + +case $ac_sys_system in + Linux*) + # Extract the first word of "patchelf", so it can be a program name with args. +set dummy patchelf; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_PATCHELF+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) if test -n "$PATCHELF"; then + ac_cv_prog_PATCHELF="$PATCHELF" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_PATCHELF="patchelf" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi ;; +esac +fi +PATCHELF=$ac_cv_prog_PATCHELF +if test -n "$PATCHELF"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PATCHELF" >&5 +printf "%s\n" "$PATCHELF" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + if test -z "$PATCHELF"; then + as_fn_error $? "patchelf is required on Linux for stable ABI support. Please install patchelf." "$LINENO" 5 + fi + ;; +esac + # For calculating the .so ABI tag. diff --git a/configure.ac b/configure.ac index 322d33dd0e3c99..afcdd8b2f5d563 100644 --- a/configure.ac +++ b/configure.ac @@ -1719,6 +1719,17 @@ if test -z "$LN" ; then esac fi +# patchelf is required on Linux to set the SONAME of libpython3.so for stable ABI support +AC_SUBST([PATCHELF]) +case $ac_sys_system in + Linux*) + AC_CHECK_PROG([PATCHELF], [patchelf], [patchelf], []) + if test -z "$PATCHELF"; then + AC_MSG_ERROR([patchelf is required on Linux for stable ABI support. Please install patchelf.]) + fi + ;; +esac + # For calculating the .so ABI tag. AC_SUBST([ABIFLAGS]) AC_SUBST([ABI_THREAD]) From 1b9d6b5d2f1541d6fb3af2e003e756e2b2820b49 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:03:57 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Build/2026-01-22-09-03-56.gh-issue-104612.z6FSDN.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Build/2026-01-22-09-03-56.gh-issue-104612.z6FSDN.rst diff --git a/Misc/NEWS.d/next/Build/2026-01-22-09-03-56.gh-issue-104612.z6FSDN.rst b/Misc/NEWS.d/next/Build/2026-01-22-09-03-56.gh-issue-104612.z6FSDN.rst new file mode 100644 index 00000000000000..0a7794d9807e9f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-01-22-09-03-56.gh-issue-104612.z6FSDN.rst @@ -0,0 +1 @@ +Fix libpython3.so stable ABI on Linux