Skip to content
This repository was archived by the owner on Jan 3, 2024. It is now read-only.

Commit 689c08c

Browse files
committed
Fix loading system libraries with ctypes on macOS Big Sur.
On Big Sur, there is no longer a separate dylib file for each system library. Instead, they have been combined into a single shared cache file. As a result, it no longer suffices to check file existence to determine whether a system library is present. Fix this by calling _dyld_shared_cache_contains_path to check the dyld shared cache as well. The changes under lib-python/ are copied from python/cpython#22855. The _dyld_shared_cache_contains_path function is exposed to the ctypes python code using CFFI, similar to what is done in other modules, such as lib_pypy/_sha3. Fixes issue #3314.
1 parent d34ddbf commit 689c08c

File tree

5 files changed

+62
-11
lines changed

5 files changed

+62
-11
lines changed

lib-python/3/ctypes/macholib/dyld.py

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
from ctypes.macholib.framework import framework_info
77
from ctypes.macholib.dylib import dylib_info
88
from itertools import *
9+
try:
10+
from _ctypes import _dyld_shared_cache_contains_path
11+
except ImportError:
12+
def _dyld_shared_cache_contains_path(*args):
13+
raise NotImplementedError
914

1015
__all__ = [
1116
'dyld_find', 'framework_find',
@@ -122,8 +127,15 @@ def dyld_find(name, executable_path=None, env=None):
122127
dyld_executable_path_search(name, executable_path),
123128
dyld_default_search(name, env),
124129
), env):
130+
125131
if os.path.isfile(path):
126132
return path
133+
try:
134+
if _dyld_shared_cache_contains_path(path):
135+
return path
136+
except NotImplementedError:
137+
pass
138+
127139
raise ValueError("dylib %s could not be found" % (name,))
128140

129141
def framework_find(fn, executable_path=None, env=None):

lib-python/3/ctypes/test/test_macholib.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,22 @@ def find_lib(name):
4545
class MachOTest(unittest.TestCase):
4646
@unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test')
4747
def test_find(self):
48-
49-
self.assertEqual(find_lib('pthread'),
50-
'/usr/lib/libSystem.B.dylib')
48+
# On Mac OS 11, system dylibs are only present in the shared cache,
49+
# so symlinks like libpthread.dylib -> libSystem.B.dylib will not
50+
# be resolved by dyld_find
51+
self.assertIn(find_lib('pthread'),
52+
('/usr/lib/libSystem.B.dylib', '/usr/lib/libpthread.dylib'))
5153

5254
result = find_lib('z')
5355
# Issue #21093: dyld default search path includes $HOME/lib and
5456
# /usr/local/lib before /usr/lib, which caused test failures if
5557
# a local copy of libz exists in one of them. Now ignore the head
5658
# of the path.
57-
self.assertRegex(result, r".*/lib/libz\..*.*\.dylib")
59+
self.assertRegex(result, r".*/lib/libz.*\.dylib")
5860

59-
self.assertEqual(find_lib('IOKit'),
60-
'/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit')
61+
self.assertIn(find_lib('IOKit'),
62+
('/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit',
63+
'/System/Library/Frameworks/IOKit.framework/IOKit'))
6164

6265
if __name__ == "__main__":
6366
unittest.main()

lib_pypy/_ctypes/__init__.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
_string_at_addr, _wstring_at_addr, set_conversion_mode)
1414
from _ctypes.union import Union
1515

16+
try: from __pypy__ import builtinify
17+
except ImportError: builtinify = lambda f: f
18+
1619
import os as _os
1720

1821
if _os.name in ("nt", "ce"):
1922
from _rawffi import FormatError
2023
from _rawffi import check_HRESULT as _check_HRESULT
2124

22-
try: from __pypy__ import builtinify
23-
except ImportError: builtinify = lambda f: f
24-
2525
@builtinify
2626
def CopyComPointer(src, dst):
2727
from ctypes import c_void_p, cast
@@ -32,8 +32,6 @@ def CopyComPointer(src, dst):
3232
dst[0] = cast(src, c_void_p).value
3333
return 0
3434

35-
del builtinify
36-
3735
LoadLibrary = dlopen
3836

3937
from _rawffi import FUNCFLAG_STDCALL, FUNCFLAG_CDECL, FUNCFLAG_PYTHONAPI
@@ -43,6 +41,20 @@ def CopyComPointer(src, dst):
4341
if _os.name in ("nt", "ce"):
4442
from _ctypes.builtin import get_last_error, set_last_error
4543

44+
import sys as _sys
45+
if _sys.platform == 'darwin':
46+
try:
47+
from ._ctypes_cffi import ffi as _ffi, lib as _lib
48+
@builtinify
49+
def _dyld_shared_cache_contains_path(path):
50+
if not hasattr(_lib, '_dyld_shared_cache_contains_path'):
51+
raise NotImplementedError
52+
return _lib._dyld_shared_cache_contains_path(path.encode())
53+
except ImportError:
54+
pass
55+
56+
del builtinify
57+
4658
__version__ = '1.1.0'
4759
#XXX platform dependant?
4860
RTLD_LOCAL = 0

lib_pypy/_ctypes/_ctypes_build.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import platform
3+
4+
from cffi import FFI
5+
ffi = FFI()
6+
7+
def main():
8+
if platform.system() != 'Darwin':
9+
return
10+
11+
release, _, _ = platform.mac_ver()
12+
release = tuple(map(int, release.split('.')))
13+
if release < (10, 16):
14+
return
15+
16+
ffi.cdef('bool _dyld_shared_cache_contains_path(const char* path);')
17+
ffi.set_source('_ctypes_cffi', '#include <mach-o/dyld.h>')
18+
19+
os.chdir(os.path.dirname(__file__))
20+
ffi.compile()
21+
22+
if __name__ == '__main__':
23+
main()

lib_pypy/pypy_tools/build_cffi_imports.py

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class MissingDependenciesError(Exception):
2323

2424

2525
cffi_build_scripts = {
26+
"_ctypes": "_ctypes/_ctypes_build.py" if sys.platform == "darwin" else None,
2627
"_blake2": "_blake2/_blake2_build.py",
2728
"_ssl": "_ssl_build.py",
2829
"sqlite3": "_sqlite3_build.py",

0 commit comments

Comments
 (0)