diff --git a/Lib/ctypes/macholib/dyld.py b/Lib/ctypes/macholib/dyld.py index 9d86b058765a3e..0cc04648db75dc 100644 --- a/Lib/ctypes/macholib/dyld.py +++ b/Lib/ctypes/macholib/dyld.py @@ -7,6 +7,12 @@ from ctypes.macholib.dylib import dylib_info from itertools import * +try: + from _ctypes import _dyld_shared_cache_contains_path +except ImportError: + def _dyld_shared_cache_contains_path(*args): + raise NotImplementedError + __all__ = [ 'dyld_find', 'framework_find', 'framework_info', 'dylib_info', @@ -124,6 +130,12 @@ def dyld_find(name, executable_path=None, env=None): ), env): if os.path.isfile(path): return path + try: + if _dyld_shared_cache_contains_path(path): + return path + except NotImplementedError: + pass + raise ValueError("dylib %s could not be found" % (name,)) def framework_find(fn, executable_path=None, env=None): diff --git a/Lib/ctypes/test/test_macholib.py b/Lib/ctypes/test/test_macholib.py index 6b3526951acfab..ed04479abd091b 100644 --- a/Lib/ctypes/test/test_macholib.py +++ b/Lib/ctypes/test/test_macholib.py @@ -46,18 +46,23 @@ class MachOTest(unittest.TestCase): @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test') def test_find(self): - self.assertEqual(find_lib('pthread'), - '/usr/lib/libSystem.B.dylib') + # On Mac OS 11, system dylibs are only present in the shared cache, + # so symlinks like libpthread.dylib -> libSystem.B.dylib will not + # be resolved by dyld_find + + self.assertIn(find_lib('pthread'), + ('/usr/lib/libSystem.B.dylib', '/usr/lib/libpthread.dylib')) result = find_lib('z') # Issue #21093: dyld default search path includes $HOME/lib and # /usr/local/lib before /usr/lib, which caused test failures if # a local copy of libz exists in one of them. Now ignore the head # of the path. - self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") + self.assertRegex(result, r".*/lib/libz.*\.dylib") - self.assertEqual(find_lib('IOKit'), - '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') + self.assertIn(find_lib('IOKit'), + ('/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit', + '/System/Library/Frameworks/IOKit.framework/IOKit')) if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-18-24-28.bpo-41100._QkcD_.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-18-24-28.bpo-41100._QkcD_.rst new file mode 100644 index 00000000000000..dbb732432c0791 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-18-24-28.bpo-41100._QkcD_.rst @@ -0,0 +1 @@ +ctypes: Mac OS 11: check for system libraries in the shared cache \ No newline at end of file diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 6030cc3d43670d..f8007505ed3102 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -64,6 +64,10 @@ #include "ctypes_dlfcn.h" #endif +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH +#include +#endif + #ifdef MS_WIN32 #include #endif @@ -1398,6 +1402,36 @@ copy_com_pointer(PyObject *self, PyObject *args) } #else +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH +static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) +{ + if (__builtin_available(macos 11, ios 14, watchos 7, tvos 14, *)) { + PyObject *name, *name2; + char *name_str; + if (!PyArg_ParseTuple(args, "O", &name)) + return NULL; + + if (name == Py_None) + Py_RETURN_FALSE; + + if (PyUnicode_FSConverter(name, &name2) == 0) + return NULL; + if (PyBytes_Check(name2)) + name_str = PyBytes_AS_STRING(name2); + else + name_str = PyByteArray_AS_STRING(name2); + + if(_dyld_shared_cache_contains_path(name_str)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; + } else { + PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing"); + return NULL; + } +} +#endif + static PyObject *py_dl_open(PyObject *self, PyObject *args) { PyObject *name, *name2; @@ -1908,6 +1942,9 @@ PyMethodDef _ctypes_module_methods[] = { "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"}, {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"}, {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"}, +#endif +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH + {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, #endif {"alignment", align_func, METH_O, alignment_doc}, {"sizeof", sizeof_func, METH_O, sizeof_doc}, diff --git a/configure b/configure index 5024860ca4395a..0ecd1a1d66c793 100755 --- a/configure +++ b/configure @@ -11601,6 +11601,15 @@ fi done +ac_fn_c_check_decl "$LINENO" "_dyld_shared_cache_contains_path" "ac_cv_have_decl__dyld_shared_cache_contains_path" "#include +" +if test "x$ac_cv_have_decl__dyld_shared_cache_contains_path" = xyes; then : + +$as_echo "#define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 1" >>confdefs.h + +fi + + # DYNLOADFILE specifies which dynload_*.o file we will use for dynamic # loading of modules. diff --git a/configure.ac b/configure.ac index 5a3e340aa3e72b..bf7250d0085f6d 100644 --- a/configure.ac +++ b/configure.ac @@ -3603,6 +3603,10 @@ DLINCLDIR=. # platforms, such as AIX, have dlopen(), but don't want to use it. AC_CHECK_FUNCS(dlopen) +AC_CHECK_DECL(_dyld_shared_cache_contains_path, + AC_DEFINE(HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH, 1, Define if you have the '_dyld_shared_cache_contains_path' function), + [], [#include ]) + # DYNLOADFILE specifies which dynload_*.o file we will use for dynamic # loading of modules. AC_SUBST(DYNLOADFILE) diff --git a/pyconfig.h.in b/pyconfig.h.in index bc906a869b623e..53ce2738dabb68 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -284,6 +284,9 @@ /* Define to 1 if you have the `dup3' function. */ #undef HAVE_DUP3 +/* Define if you have the '_dyld_shared_cache_contains_path' function */ +#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH + /* Defined when any dynamic module loading is enabled. */ #undef HAVE_DYNAMIC_LOADING