Skip to content

Commit e03b97c

Browse files
committed
Add 512 bytes of 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. For instance, uv currently runs install_name_tool on end-user systems after unpacking the installation; this makes it easier to avoid doing that.
1 parent 0ca55fe commit e03b97c

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed

cpython-unix/build-cpython.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then
322322
patch -p1 -i ${ROOT}/patch-test-embed-prevent-segfault.patch
323323
fi
324324

325+
# Pad the install name with slashes. We'll replace this with NULs after the build.
326+
if [ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_11}" ]; then
327+
patch -p1 -i ${ROOT}/patch-python-install-name-padding-3.11.patch
328+
else
329+
patch -p1 -i ${ROOT}/patch-python-install-name-padding.patch
330+
fi
331+
325332
# Most bits look at CFLAGS. But setup.py only looks at CPPFLAGS.
326333
# So we need to set both.
327334
CFLAGS="${EXTRA_TARGET_CFLAGS} -fPIC -I${TOOLS_PATH}/deps/include -I${TOOLS_PATH}/deps/include/ncursesw"
@@ -723,6 +730,11 @@ if [ "${PYBUILD_SHARED}" = "1" ]; then
723730
-change /install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} @executable_path/../lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME} \
724731
${ROOT}/out/python/install/bin/python${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}
725732
fi
733+
734+
# For cleanness, replace the slash-based padding for the install name with NUL-based
735+
# padding. install_name_tool will not accept trailing NULs so we do it ourselves. Do this
736+
# after any calls to install_name_tool.
737+
${BUILD_PYTHON} ${ROOT}/repad_install_name.py ${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}
726738
else # (not macos)
727739
LIBPYTHON_SHARED_LIBRARY_BASENAME=libpython${PYTHON_MAJMIN_VERSION}${PYTHON_BINARY_SUFFIX}.so.1.0
728740
LIBPYTHON_SHARED_LIBRARY=${ROOT}/out/python/install/lib/${LIBPYTHON_SHARED_LIBRARY_BASENAME}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
From 2861855c08df773eaa640f1fe354c6f792b649a6 Mon Sep 17 00:00:00 2001
2+
From: Geoffrey Thomas <[email protected]>
3+
Date: Fri, 25 Jul 2025 09:53:37 -0400
4+
Subject: [PATCH 1/1] Makefile.pre.in: Add padding to libpython.dylib
5+
install_name
6+
7+
This gives some room for later users to change the name by binary
8+
editing, without requiring install_name_tool or some other Mach-O
9+
writer.
10+
---
11+
Makefile.pre.in | 5 ++++-
12+
1 file changed, 4 insertions(+), 1 deletion(-)
13+
14+
diff --git a/Makefile.pre.in b/Makefile.pre.in
15+
index 8fbcd7ac170..ff88a390cab 100644
16+
--- a/Makefile.pre.in
17+
+++ b/Makefile.pre.in
18+
@@ -760,8 +760,11 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
19+
libpython3.so: libpython$(LDVERSION).so
20+
$(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^
21+
22+
+PADDING_128 := /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.
23+
+PADDING_512 := $(PADDING_128)$(PADDING_128)$(PADDING_128)$(PADDING_128)
24+
+
25+
libpython$(LDVERSION).dylib: $(LIBRARY_OBJS)
26+
- $(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); \
27+
+ $(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); \
28+
29+
30+
libpython$(VERSION).sl: $(LIBRARY_OBJS)
31+
--
32+
2.39.5 (Apple Git-154)
33+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
From 676e5422e4ee442508c8a8cb4f8c49123ddb5d66 Mon Sep 17 00:00:00 2001
2+
From: Geoffrey Thomas <[email protected]>
3+
Date: Fri, 25 Jul 2025 09:53:37 -0400
4+
Subject: [PATCH 1/1] Makefile.pre.in: Add padding to libpython.dylib
5+
install_name
6+
7+
This gives some room for later users to change the name by binary
8+
editing, without requiring install_name_tool or some other Mach-O
9+
writer.
10+
---
11+
Makefile.pre.in | 5 ++++-
12+
1 file changed, 4 insertions(+), 1 deletion(-)
13+
14+
diff --git a/Makefile.pre.in b/Makefile.pre.in
15+
index 538229220fd..dd0b044abf3 100644
16+
--- a/Makefile.pre.in
17+
+++ b/Makefile.pre.in
18+
@@ -918,8 +918,11 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
19+
libpython3.so: libpython$(LDVERSION).so
20+
$(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^
21+
22+
+PADDING_128 := /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.
23+
+PADDING_512 := $(PADDING_128)$(PADDING_128)$(PADDING_128)$(PADDING_128)
24+
+
25+
libpython$(LDVERSION).dylib: $(LIBRARY_OBJS)
26+
- $(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); \
27+
+ $(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); \
28+
29+
30+
libpython$(VERSION).sl: $(LIBRARY_OBJS)
31+
--
32+
2.39.5 (Apple Git-154)
33+

cpython-unix/repad_install_name.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
#
3+
# usage: repad_install_name.py <filename>
4+
# Rewrites /usr/lib/././././libpython.dylib to /usr/lib/libpython.dylib,
5+
# keeping NUL padding in the load command.
6+
#
7+
# Useful references:
8+
# `xcrun --show-sdk-platform-path`/Developer/usr/include/mach-o/loader.h
9+
# https://en.wikipedia.org/wiki/Mach-O (some factual inaccuracies)
10+
11+
import re
12+
import struct
13+
import sys
14+
15+
16+
def rewrite(f):
17+
f.seek(0, 0)
18+
mach_header = f.read(28)
19+
magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = struct.unpack(
20+
"=7I", mach_header
21+
)
22+
if magic == 0xFEEDFACE:
23+
pass
24+
elif magic == 0xFEEDFACF:
25+
_reserved = f.read(4)
26+
elif magic in (0xCEFAEDFE, 0xCFFAEDFE):
27+
raise RuntimeError("Wrong-endian Mach-O file")
28+
else:
29+
raise RuntimeError("Not a Mach-O file?")
30+
31+
loadstart = f.tell()
32+
33+
for i in range(ncmds):
34+
load_command_header = f.read(8)
35+
cmd, cmdsize = struct.unpack("=2I", load_command_header)
36+
load_command_body = f.read(cmdsize - 8)
37+
if cmd == 0xD: # LC_ID_DYLIB
38+
name_offset, timestamp, current_version, compatbility_version = (
39+
struct.unpack_from("=4I", load_command_body)
40+
)
41+
bufsize = cmdsize - 24
42+
if name_offset != 24 or bufsize <= 0:
43+
raise RuntimeError("Malformed load command")
44+
install_name = load_command_body[16:].rstrip(b"\0")
45+
new_install_name, replacements = re.subn(b"/(\./)+", b"/", install_name)
46+
if replacements > 0:
47+
print(f"Rewriting install_name to {new_install_name}")
48+
f.seek(-bufsize, 1)
49+
f.write(new_install_name.ljust(bufsize, b"\0"))
50+
51+
if f.tell() != loadstart + sizeofcmds:
52+
raise RuntimeError("Unexpected end of load commands, is file corrupt?")
53+
54+
55+
if __name__ == "__main__":
56+
with open(sys.argv[1], "r+b") as f:
57+
rewrite(f)

0 commit comments

Comments
 (0)