Skip to content

Commit 89de021

Browse files
authored
Enable os.memfd_create through runtime weak linking (#1164)
Always build CPython with `os.memfd_create` but remove the Python function from the namespace at runtime when glibc does not provide the an implementation. This is accomplished using a weak `memfd_create` symbol which is evaluated at runtime. This requires the modern kernel UAPI headers from #1163 to define the `MFD_*` constants.
1 parent 1b9d44f commit 89de021

5 files changed

Lines changed: 142 additions & 0 deletions

File tree

cpython-unix/build-cpython.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,27 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then
589589
CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_explicit_bzero=no"
590590
fi
591591

592+
# The modern UAPI overlay provides the memfd constants, but some glibc sysroots
593+
# predate the memfd_create() wrapper. Force the configure check on and weak-link
594+
# the wrapper so the function is exposed only when runtime glibc provides it.
595+
# This workaround is specific to glibc builds; the UAPI overlay itself can also
596+
# be used with other Linux libcs.
597+
if [[ -n "${LINUX_UAPI_INCLUDE_ARCH:-}" && "${TARGET_TRIPLE}" == *-linux-gnu* ]]; then
598+
if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then
599+
patch -p1 -i "${ROOT}/patch-posixmodule-memfd-create-weak.patch"
600+
else
601+
patch -p1 -i "${ROOT}/patch-posixmodule-memfd-create-weak-3.13.patch"
602+
fi
603+
604+
# Python 3.10 checks for memfd_create with a custom compile test instead
605+
# of the cached ac_cv_func_memfd_create check used by newer versions.
606+
if [[ -n "${PYTHON_MEETS_MAXIMUM_VERSION_3_10}" ]]; then
607+
patch -p1 -i "${ROOT}/patch-configure-memfd-create-3.10.patch"
608+
fi
609+
610+
CONFIGURE_FLAGS="${CONFIGURE_FLAGS} ac_cv_func_memfd_create=yes"
611+
fi
612+
592613
# Define the base PGO profiling task, which we'll extend below with ignores
593614
export PROFILE_TASK='-m test --pgo'
594615

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
diff --git a/configure.ac b/configure.ac
2+
--- a/configure.ac
3+
+++ b/configure.ac
4+
@@ -3822,6 +3822,10 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mach-o/dyld.h>]], [[void *x=_dyl
5+
])
6+
7+
AC_MSG_CHECKING(for memfd_create)
8+
+if test "$ac_cv_func_memfd_create" = yes; then
9+
+ AC_DEFINE(HAVE_MEMFD_CREATE, 1, Define if you have the 'memfd_create' function.)
10+
+ AC_MSG_RESULT(yes)
11+
+else
12+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
13+
#ifdef HAVE_SYS_MMAN_H
14+
#include <sys/mman.h>
15+
@@ -3834,6 +3838,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
16+
AC_MSG_RESULT(yes)],
17+
[AC_MSG_RESULT(no)
18+
])
19+
+fi
20+
21+
AC_MSG_CHECKING(for eventfd)
22+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
2+
--- a/Modules/posixmodule.c
3+
+++ b/Modules/posixmodule.c
4+
@@ -528,6 +528,12 @@ extern char *ctermid_r(char *);
5+
# include <linux/memfd.h>
6+
#endif
7+
8+
+#ifdef HAVE_MEMFD_CREATE
9+
+/* Weak references. */
10+
+__attribute__((weak))
11+
+int memfd_create(const char *name, unsigned int flags);
12+
+#endif
13+
+
14+
/* eventfd() */
15+
#ifdef HAVE_SYS_EVENTFD_H
16+
# include <sys/eventfd.h>
17+
@@ -15718,6 +15724,18 @@ posixmodule_exec(PyObject *m)
18+
{
19+
_posixstate *state = get_posix_state(m);
20+
21+
+#ifdef HAVE_MEMFD_CREATE
22+
+ if (memfd_create == NULL) {
23+
+ PyObject *dict = PyModule_GetDict(m);
24+
+ if (dict == NULL) {
25+
+ return -1;
26+
+ }
27+
+ if (PyDict_DelItemString(dict, "memfd_create") < 0) {
28+
+ return -1;
29+
+ }
30+
+ }
31+
+#endif
32+
+
33+
#if defined(HAVE_PWRITEV)
34+
if (HAVE_PWRITEV_RUNTIME) {} else {
35+
PyObject* dct = PyModule_GetDict(m);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
2+
--- a/Modules/posixmodule.c
3+
+++ b/Modules/posixmodule.c
4+
@@ -192,6 +192,12 @@
5+
# include <linux/memfd.h> // memfd_create(), MFD_CLOEXEC
6+
#endif
7+
8+
+#ifdef HAVE_MEMFD_CREATE
9+
+/* Weak references. */
10+
+__attribute__((weak))
11+
+int memfd_create(const char *name, unsigned int flags);
12+
+#endif
13+
+
14+
#ifdef HAVE_SYS_EVENTFD_H
15+
# include <sys/eventfd.h> // eventfd()
16+
#endif
17+
@@ -18128,6 +18134,18 @@ posixmodule_exec(PyObject *m)
18+
{
19+
_posixstate *state = get_posix_state(m);
20+
21+
+#ifdef HAVE_MEMFD_CREATE
22+
+ if (memfd_create == NULL) {
23+
+ PyObject *dict = PyModule_GetDict(m);
24+
+ if (dict == NULL) {
25+
+ return -1;
26+
+ }
27+
+ if (PyDict_PopString(dict, "memfd_create", NULL) < 0) {
28+
+ return -1;
29+
+ }
30+
+ }
31+
+#endif
32+
+
33+
#if defined(HAVE_PWRITEV)
34+
if (HAVE_PWRITEV_RUNTIME) {} else {
35+
PyObject* dct = PyModule_GetDict(m);

pythonbuild/disttests/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,35 @@ def test_socket_af_vsock(self):
359359
def test_os_pidfd_open(self):
360360
self.assertTrue(hasattr(os, "pidfd_open"))
361361

362+
@unittest.skipUnless(
363+
"-linux-gnu" in os.environ["TARGET_TRIPLE"],
364+
"memfd_create weak linking is enabled for Linux GNU targets",
365+
)
366+
def test_os_memfd_create(self):
367+
import ctypes
368+
import errno
369+
370+
libc = ctypes.CDLL(None)
371+
libc_has_memfd_create = hasattr(libc, "memfd_create")
372+
self.assertEqual(hasattr(os, "memfd_create"), libc_has_memfd_create)
373+
374+
if not libc_has_memfd_create:
375+
return
376+
377+
try:
378+
fd = os.memfd_create("python-build-standalone-test")
379+
except OSError as exc:
380+
if exc.errno == errno.ENOSYS:
381+
self.skipTest("the runtime kernel does not support memfd_create")
382+
raise
383+
384+
try:
385+
os.write(fd, b"memfd_create")
386+
os.lseek(fd, 0, os.SEEK_SET)
387+
self.assertEqual(os.read(fd, 12), b"memfd_create")
388+
finally:
389+
os.close(fd)
390+
362391
def test_linux_uapi_not_in_sysconfig(self):
363392
import sysconfig
364393

0 commit comments

Comments
 (0)