Skip to content

POC: Add a relocatable CPython build mode - #861

Draft
zanieb wants to merge 6 commits into
mainfrom
zb/relocatable-flag
Draft

zanieb wants to merge 6 commits into
mainfrom
zb/relocatable-flag

Conversation

@zanieb

@zanieb zanieb commented Nov 21, 2025 •

Copy link
Copy Markdown
Member

PBS currently adjusts installed binaries after linking to give them relative library search paths. For Python 3.15 and newer, backport an experimental --enable-relocatable mode that supplies paths when linking installed interpreters, libpython, and standard-library extensions. Keep these flags out of LDSHARED, since third-party extensions may use a different layout.

Link build-tree and install binaries separately from the same objects. An installed-layout DT_RPATH in the build-tree interpreter can otherwise select an unrelated libpython before RUNSHARED's LD_LIBRARY_PATH. When BOLT is enabled, apply the build binaries' profiles to the install variants as well. On glibc, DT_RPATH entries are searched before LD_LIBRARY_PATH when the requesting object has no DT_RUNPATH; they can also be used for indirect dependencies. On macOS, use an @rpath identity and relative search paths. For copied interpreters from non-framework shared builds, venv creates a symlink to the base library directory so the interpreter can find libpython. The environment still depends on its base installation.

PBS retains its explicit relative glibc libpython3.so dependency, macOS zlib normalization, static-module linking, Python path discovery, and Tcl/Tk data discovery changes. This mode supports the default installation layout on Linux and non-framework macOS. Dependency bundling, data-file relocation, and rewriting shebangs or sysconfig paths remain separate concerns.

Skip Windows build jobs when the selected matrix is empty.

The upstream drafts compare this approach in zanieb/cpython#48 with a DT_RUNPATH alternative in zanieb/cpython#49.

@geofft geofft left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I think there's a good amount of stuff missing compared to our current implementation, and this is a hairy enough spot that I don't think we should have any regressions in 3.15 compared to what we do on older versions.

On glibc/Linux, we add a DT_NEEDED for bin/python to $ORIGIN/../lib/libpython3.x.so.1.0 (and for lib/libpython3.so to $ORIGIN/libpython3.x.so.1.0) , to avoid issues with LD_LIBRARY_PATH taking precedence. I'm relatively sure this is annoying to do without a separate patchelf step, but maybe it can be done.

We also set an DT_RPATH for bin/python on Linux (required on musl, compatibility on glibc). This is not a DT_RUNPATH, so this needs -Wl,--disable-new-dtags.

I don't see either of those in the Makefile diff, in addition to the other things noted in the comments.

If you really wanted, you could start by upstreaming this gated to macOS only, which might be a little less code to write.

Also speaking of platform support, the dynamic linkers on the BSDs and Solaris works much like glibc, so most of our Linux support should apply there too in the version that gets upstreamed.

In either case, I would error out if --enable-relocatable is passed on another platform.

Author: Zanie Blue <contact@zanie.dev>
Date: Fri Nov 21 15:44:42 2025 -0600

Set linker arguments when `--enabled-relocatable` is used

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Set linker arguments when `--enabled-relocatable` is used
Set linker arguments when `--enable-relocatable` is used

-change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \
${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}

# We also normalize /tools/deps/lib/libz.1.dylib to the system location.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come you didn't need to preserve this?

Comment thread cpython-unix/build-cpython.sh Outdated
Comment on lines 727 to 732

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This almost certainly needs to be preserved, this fixes up _tkinter, right? That is, you need this to go into the CPython Makefile rule for building shared-library extension modules (... which we might not be using, to make things more exciting).

@@ -1025,10 +1025,19 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
fi

libpython3.so: libpython$(LDVERSION).so

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to patch the libpython$(LDVERSION).so target too. (LINKFORSHARED is only used when building the interpreter.)

+ LINKFORSHARED="-Xlinker -export-dynamic"
+ # Add rpath for relocatable builds
+ if test "x$enable_relocatable" = xyes; then
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath=\$ORIGIN/../lib"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rpath here is not escaped correctly resulting in the the RPATH in the binary being set to RIGIN/../lib

This causes among other issues, _tkinter not to be importable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath=\$ORIGIN/../lib"
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath,'\$\$ORIGIN/../lib'"

+ LINKFORSHARED="-Xlinker -export-dynamic"
+ # Add rpath for relocatable builds
+ if test "x$enable_relocatable" = xyes; then
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath=\$ORIGIN/../lib"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath=\$ORIGIN/../lib"
+ LINKFORSHARED="$LINKFORSHARED -Wl,-rpath,'\$\$ORIGIN/../lib'"

@jjhelmus

jjhelmus commented Dec 3, 2025

Copy link
Copy Markdown
Contributor

A question that I suspect will get asked when upstreaming this, is what should be done to accommodate third-party libraries such as libtcl, libtk, libz in relocatable builds.

With the exception of libtcl/tk these are statically linked but this patch should support cases where these are dynamically linked. Adding a RPATH entry to the extensions with a relative path to lib ($ORIGIN/../..) would be good but not required as bin/python includes an RPATH.

I could also see an opportunity to hash the SO name of third-party libraries on Linux to protect against clashes with system libraries. This approach is used by auditwheel when bundling libraries into the wheel. macOS has two-level namespaces which makes this largely unnecessary.

@zanieb zanieb changed the title POC: Add a relocatable build flag POC: Add a relocatable CPython build mode Oct 21, 2026
@zanieb
zanieb force-pushed the zb/relocatable-flag branch from d715fa3 to 2df7631 Compare October 21, 2026 18:43

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform:darwin Specific to the macOS platform platform:linux Specific to the Linux platform python:3.15 python:3.16

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants