Skip to content

Add 512 bytes of padding to libpython.dylib install_name #714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cpython-unix/build-cpython.sh
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then
patch -p1 -i ${ROOT}/patch-test-embed-prevent-segfault.patch
fi

# Pad the install name with slashes. We'll replace this with NULs after the build.
if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_11}" ]; then
patch -p1 -i ${ROOT}/patch-python-install-name-padding-3.11.patch
else
patch -p1 -i ${ROOT}/patch-python-install-name-padding.patch
fi

# Most bits look at CFLAGS. But setup.py only looks at CPPFLAGS.
# So we need to set both.
CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I${TOOLS_PATH}/deps/include -I${TOOLS_PATH}/deps/include/ncursesw"
Expand Down Expand Up @@ -723,6 +730,11 @@ if [ "${PYBUILD_SHARED}" = "1" ]; then
-change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \
${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}
fi

# For cleanness, replace the slash-based padding for the install name with NUL-based
# padding. install_name_tool will not accept trailing NULs so we do it ourselves. Do this
# after any calls to install_name_tool.
${BUILD_PYTHON} ${ROOT}/repad_install_name.py ${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}
else # (not macos)
LIBPYTHON_SHARED_LIBRARY_BASENAME=libpython${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}.so.1.0
LIBPYTHON_SHARED_LIBRARY=${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}
Expand Down
33 changes: 33 additions & 0 deletions cpython-unix/patch-python-install-name-padding-3.12.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
From 2861855c08df773eaa640f1fe354c6f792b649a6 Mon Sep 17 00:00:00 2001
From: Geoffrey Thomas <[email protected]>
Date: Fri, 25 Jul 2025 09:53:37 -0400
Subject: [PATCH 1/1] Makefile.pre.in: Add padding to libpython.dylib
install_name

This gives some room for later users to change the name by binary
editing, without requiring install_name_tool or some other Mach-O
writer.
---
Makefile.pre.in | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Makefile.pre.in b/Makefile.pre.in
index 8fbcd7ac170..ff88a390cab 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -760,8 +760,11 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
libpython3.so: libpython$(LDVERSION).so
$(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^

+PADDING_128 := /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.
+PADDING_512 := $(PADDING_128)$(PADDING_128)$(PADDING_128)$(PADDING_128)
+
libpython$(LDVERSION).dylib: $(LIBRARY_OBJS)
- $(CC) -dynamiclib -Wl,-single_module $(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); \
+ $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)$(PADDING_512)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \


libpython$(VERSION).sl: $(LIBRARY_OBJS)
--
2.39.5 (Apple Git-154)

33 changes: 33 additions & 0 deletions cpython-unix/patch-python-install-name-padding.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
From 676e5422e4ee442508c8a8cb4f8c49123ddb5d66 Mon Sep 17 00:00:00 2001
From: Geoffrey Thomas <[email protected]>
Date: Fri, 25 Jul 2025 09:53:37 -0400
Subject: [PATCH 1/1] Makefile.pre.in: Add padding to libpython.dylib
install_name

This gives some room for later users to change the name by binary
editing, without requiring install_name_tool or some other Mach-O
writer.
---
Makefile.pre.in | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Makefile.pre.in b/Makefile.pre.in
index 538229220fd..dd0b044abf3 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -918,8 +918,11 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
libpython3.so: libpython$(LDVERSION).so
$(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^

+PADDING_128 := /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.
+PADDING_512 := $(PADDING_128)$(PADDING_128)$(PADDING_128)$(PADDING_128)
+
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); \
+ $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)$(PADDING_512)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \


libpython$(VERSION).sl: $(LIBRARY_OBJS)
--
2.39.5 (Apple Git-154)

57 changes: 57 additions & 0 deletions cpython-unix/repad_install_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
#
# usage: repad_install_name.py <filename>
# Rewrites /usr/lib/././././libpython.dylib to /usr/lib/libpython.dylib,
# keeping NUL padding in the load command.
#
# Useful references:
# `xcrun --show-sdk-platform-path`/Developer/usr/include/mach-o/loader.h
# https://en.wikipedia.org/wiki/Mach-O (some factual inaccuracies)

import re
import struct
import sys


def rewrite(f):
f.seek(0, 0)
mach_header = f.read(28)
magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = struct.unpack(
"=7I", mach_header
)
if magic == 0xFEEDFACE:
pass
elif magic == 0xFEEDFACF:
_reserved = f.read(4)
elif magic in (0xCEFAEDFE, 0xCFFAEDFE):
raise RuntimeError("Wrong-endian Mach-O file")
else:
raise RuntimeError("Not a Mach-O file?")

loadstart = f.tell()

for i in range(ncmds):
load_command_header = f.read(8)
cmd, cmdsize = struct.unpack("=2I", load_command_header)
load_command_body = f.read(cmdsize - 8)
if cmd == 0xD: # LC_ID_DYLIB
name_offset, timestamp, current_version, compatbility_version = (
struct.unpack_from("=4I", load_command_body)
)
bufsize = cmdsize - 24
if name_offset != 24 or bufsize <= 0:
raise RuntimeError("Malformed load command")
install_name = load_command_body[16:].rstrip(b"\0")
new_install_name, replacements = re.subn(b"/(\./)+", b"/", install_name)
if replacements > 0:
print(f"Rewriting install_name to {new_install_name}")
f.seek(-bufsize, 1)
f.write(new_install_name.ljust(bufsize, b"\0"))

if f.tell() != loadstart + sizeofcmds:
raise RuntimeError("Unexpected end of load commands, is file corrupt?")


if __name__ == "__main__":
with open(sys.argv[1], "r+b") as f:
rewrite(f)
Loading