bpo-41100: Support macOS 11 and Apple Silicon (GH-22855)

Co-authored-by:  Lawrence D’Anna <lawrence_danna@apple.com>

* Add support for macOS 11 and Apple Silicon (aka arm64)
   
  As a side effect of this work use the system copy of libffi on macOS, and remove the vendored copy

* Support building on recent versions of macOS while deploying to older versions

  This allows building installers on macOS 11 while still supporting macOS 10.9.
This commit is contained in:
Ronald Oussoren 2020-11-08 10:05:27 +01:00 committed by GitHub
parent fd6f6fa403
commit 41761933c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1654 additions and 412 deletions

View file

@ -1,6 +1,8 @@
#include "Python.h"
#include "frameobject.h"
#include <stdbool.h>
#include <ffi.h>
#ifdef MS_WIN32
#include <windows.h>
@ -18,7 +20,7 @@ CThunkObject_dealloc(PyObject *myself)
Py_XDECREF(self->callable);
Py_XDECREF(self->restype);
if (self->pcl_write)
ffi_closure_free(self->pcl_write);
Py_ffi_closure_free(self->pcl_write);
PyObject_GC_Del(self);
}
@ -362,8 +364,7 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
assert(CThunk_CheckExact((PyObject *)p));
p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure),
&p->pcl_exec);
p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
if (p->pcl_write == NULL) {
PyErr_NoMemory();
goto error;
@ -409,13 +410,35 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
"ffi_prep_cif failed with %d", result);
goto error;
}
#if defined(X86_DARWIN) || defined(POWERPC_DARWIN)
result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
#else
result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
p,
p->pcl_exec);
#if HAVE_FFI_PREP_CLOSURE_LOC
# if USING_APPLE_OS_LIBFFI
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
# else
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
# endif
if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
p,
p->pcl_exec);
} else
#endif
{
#if USING_APPLE_OS_LIBFFI && defined(__arm64__)
PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
goto error;
#else
#ifdef MACOSX
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
#ifdef MACOSX
#pragma clang diagnostic pop
#endif
#endif
}
if (result != FFI_OK) {
PyErr_Format(PyExc_RuntimeError,
"ffi_prep_closure failed with %d", result);

View file

@ -57,6 +57,8 @@
#include "Python.h"
#include "structmember.h" // PyMemberDef
#include <stdbool.h>
#ifdef MS_WIN32
#include <windows.h>
#include <tchar.h>
@ -64,6 +66,10 @@
#include "ctypes_dlfcn.h"
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifdef MS_WIN32
#include <malloc.h>
#endif
@ -812,7 +818,8 @@ static int _call_function_pointer(int flags,
ffi_type **atypes,
ffi_type *restype,
void *resmem,
int argcount)
int argcount,
int argtypecount)
{
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
PyObject *error_object = NULL;
@ -835,14 +842,70 @@ static int _call_function_pointer(int flags,
if ((flags & FUNCFLAG_CDECL) == 0)
cc = FFI_STDCALL;
#endif
if (FFI_OK != ffi_prep_cif(&cif,
cc,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif failed");
return -1;
# if USING_APPLE_OS_LIBFFI
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
# elif HAVE_FFI_PREP_CIF_VAR
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME true
# else
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME false
# endif
/* Even on Apple-arm64 the calling convention for variadic functions conincides
* with the standard calling convention in the case that the function called
* only with its fixed arguments. Thus, we do not need a special flag to be
* set on variadic functions. We treat a function as variadic if it is called
* with a nonzero number of variadic arguments */
bool is_variadic = (argtypecount != 0 && argcount > argtypecount);
(void) is_variadic;
#if defined(__APPLE__) && defined(__arm64__)
if (is_variadic) {
if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
} else {
PyErr_SetString(PyExc_NotImplementedError, "ffi_prep_cif_var() is missing");
return -1;
}
}
#endif
#if HAVE_FFI_PREP_CIF_VAR
if (is_variadic) {
if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
if (FFI_OK != ffi_prep_cif_var(&cif,
cc,
argtypecount,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif_var failed");
return -1;
}
} else {
if (FFI_OK != ffi_prep_cif(&cif,
cc,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif failed");
return -1;
}
}
} else
#endif
{
if (FFI_OK != ffi_prep_cif(&cif,
cc,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif failed");
return -1;
}
}
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
@ -1212,9 +1275,8 @@ PyObject *_ctypes_callproc(PPROC pProc,
if (-1 == _call_function_pointer(flags, pProc, avalues, atypes,
rtype, resbuf,
Py_SAFE_DOWNCAST(argcount,
Py_ssize_t,
int)))
Py_SAFE_DOWNCAST(argcount, Py_ssize_t, int),
Py_SAFE_DOWNCAST(argtype_count, Py_ssize_t, int)))
goto cleanup;
#ifdef WORDS_BIGENDIAN
@ -1398,6 +1460,42 @@ copy_com_pointer(PyObject *self, PyObject *args)
}
#else
#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args)
{
PyObject *name, *name2;
char *name_str;
if (__builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)) {
int r;
if (!PyArg_ParseTuple(args, "O", &name))
return NULL;
if (name == Py_None)
Py_RETURN_FALSE;
if (PyUnicode_FSConverter(name, &name2) == 0)
return NULL;
name_str = PyBytes_AS_STRING(name2);
r = _dyld_shared_cache_contains_path(name_str);
Py_DECREF(name2);
if (r) {
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;
@ -1887,6 +1985,8 @@ buffer_info(PyObject *self, PyObject *arg)
return Py_BuildValue("siN", dict->format, dict->ndim, shape);
}
PyMethodDef _ctypes_module_methods[] = {
{"get_errno", get_errno, METH_NOARGS},
{"set_errno", set_errno, METH_VARARGS},
@ -1908,6 +2008,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
#ifdef 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},

View file

@ -366,6 +366,14 @@ PyObject *_ctypes_get_errobj(int **pspace);
extern PyObject *ComError;
#endif
#if USING_MALLOC_CLOSURE_DOT_C
void Py_ffi_closure_free(void *p);
void *Py_ffi_closure_alloc(size_t size, void** codeloc);
#else
#define Py_ffi_closure_free ffi_closure_free
#define Py_ffi_closure_alloc ffi_closure_alloc
#endif
/*
Local Variables:
compile-command: "python setup.py -q build install --home ~"

View file

@ -89,16 +89,27 @@ static void more_core(void)
/******************************************************************/
/* put the item back into the free list */
void ffi_closure_free(void *p)
void Py_ffi_closure_free(void *p)
{
#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
ffi_closure_free(p);
return;
}
#endif
ITEM *item = (ITEM *)p;
item->next = free_list;
free_list = item;
}
/* return one item from the free list, allocating more if needed */
void *ffi_closure_alloc(size_t ignored, void** codeloc)
void *Py_ffi_closure_alloc(size_t size, void** codeloc)
{
#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
return ffi_closure_alloc(size, codeloc);
}
#endif
ITEM *item;
if (!free_list)
more_core();

View file

@ -923,11 +923,7 @@ static PyStatus
calculate_program_macos(wchar_t **abs_path_p)
{
char execpath[MAXPATHLEN + 1];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
#else
unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
#endif
/* On Mac OS X, if a script uses an interpreter of the form
"#!/opt/python2.3/bin/python", the kernel only passes "python"

File diff suppressed because it is too large Load diff

View file

@ -51,6 +51,15 @@
#define _Py_tzname tzname
#endif
#if defined(__APPLE__ ) && defined(__has_builtin)
# if __has_builtin(__builtin_available)
# define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
# endif
#endif
#ifndef HAVE_CLOCK_GETTIME_RUNTIME
# define HAVE_CLOCK_GETTIME_RUNTIME 1
#endif
#define SEC_TO_NS (1000 * 1000 * 1000)
/* Forward declarations */
@ -149,6 +158,16 @@ perf_counter(_Py_clock_info_t *info)
}
#ifdef HAVE_CLOCK_GETTIME
#ifdef __APPLE__
/*
* The clock_* functions will be removed from the module
* dict entirely when the C API is not available.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#endif
static PyObject *
time_clock_gettime(PyObject *self, PyObject *args)
{
@ -297,6 +316,11 @@ PyDoc_STRVAR(clock_getres_doc,
"clock_getres(clk_id) -> floating point number\n\
\n\
Return the resolution (precision) of the specified clock clk_id.");
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif
#endif /* HAVE_CLOCK_GETRES */
#ifdef HAVE_PTHREAD_GETCPUCLOCKID
@ -1162,31 +1186,35 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#if defined(HAVE_CLOCK_GETTIME) \
&& (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
struct timespec ts;
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#ifdef CLOCK_PROF
const clockid_t clk_id = CLOCK_PROF;
const char *function = "clock_gettime(CLOCK_PROF)";
const clockid_t clk_id = CLOCK_PROF;
const char *function = "clock_gettime(CLOCK_PROF)";
#else
const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
#endif
if (clock_gettime(clk_id, &ts) == 0) {
if (info) {
struct timespec res;
info->implementation = function;
info->monotonic = 1;
info->adjustable = 0;
if (clock_getres(clk_id, &res)) {
PyErr_SetFromErrno(PyExc_OSError);
if (clock_gettime(clk_id, &ts) == 0) {
if (info) {
struct timespec res;
info->implementation = function;
info->monotonic = 1;
info->adjustable = 0;
if (clock_getres(clk_id, &res)) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
}
if (_PyTime_FromTimespec(tp, &ts) < 0) {
return -1;
}
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
return 0;
}
if (_PyTime_FromTimespec(tp, &ts) < 0) {
return -1;
}
return 0;
}
#endif
@ -1390,6 +1418,16 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
#define HAVE_THREAD_TIME
#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
static int
_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
__attribute__((availability(macos, introduced=10.12)))
__attribute__((availability(ios, introduced=10.0)))
__attribute__((availability(tvos, introduced=10.0)))
__attribute__((availability(watchos, introduced=3.0)));
#endif
static int
_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
{
@ -1421,6 +1459,15 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#endif
#ifdef HAVE_THREAD_TIME
#ifdef __APPLE__
/*
* The clock_* functions will be removed from the module
* dict entirely when the C API is not available.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#endif
static PyObject *
time_thread_time(PyObject *self, PyObject *unused)
{
@ -1451,6 +1498,11 @@ PyDoc_STRVAR(thread_time_ns_doc,
\n\
Thread time for profiling as nanoseconds:\n\
sum of the kernel and user-space CPU time.");
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif
#endif
@ -1500,9 +1552,19 @@ time_get_clock_info(PyObject *self, PyObject *args)
}
#ifdef HAVE_THREAD_TIME
else if (strcmp(name, "thread_time") == 0) {
if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
#ifdef __APPLE__
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#endif
if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
return NULL;
}
#ifdef __APPLE__
} else {
PyErr_SetString(PyExc_ValueError, "unknown clock");
return NULL;
}
#endif
}
#endif
else {
@ -1783,68 +1845,116 @@ if it is -1, mktime() should guess based on the date and time.\n");
static int
time_exec(PyObject *module)
{
#if defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
/* pass: ^^^ cannot use '!' here */
} else {
PyObject* dct = PyModule_GetDict(module);
if (dct == NULL) {
return -1;
}
if (PyDict_DelItemString(dct, "clock_gettime") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_gettime_ns") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_settime") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_settime_ns") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_getres") == -1) {
PyErr_Clear();
}
}
#endif
#if defined(__APPLE__) && defined(HAVE_THREAD_TIME)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
/* pass: ^^^ cannot use '!' here */
} else {
PyObject* dct = PyModule_GetDict(module);
if (PyDict_DelItemString(dct, "thread_time") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "thread_time_ns") == -1) {
PyErr_Clear();
}
}
#endif
/* Set, or reset, module variables like time.timezone */
if (init_timezone(module) < 0) {
return -1;
}
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#ifdef CLOCK_REALTIME
if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
return -1;
}
#endif
#ifdef CLOCK_MONOTONIC
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
return -1;
}
#endif
#ifdef CLOCK_MONOTONIC_RAW
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
return -1;
}
#endif
#ifdef CLOCK_HIGHRES
if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
return -1;
}
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
return -1;
}
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
return -1;
}
#endif
#ifdef CLOCK_PROF
if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
return -1;
}
#endif
#ifdef CLOCK_BOOTTIME
if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
return -1;
}
#endif
#ifdef CLOCK_TAI
if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
return -1;
}
#endif
#ifdef CLOCK_UPTIME
if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
return -1;
}
#endif
#ifdef CLOCK_UPTIME_RAW
if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
return -1;
}
if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
return -1;
}
#endif
}
#endif /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */