Skip to content

Commit 7bd27e6

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.15] gh-131565: Implement ctypes.util.dllist() in the _ctypes extension (GH-154255) (GH-154882)
On NetBSD dl_iterate_phdr() reports only the link-map group of the calling object. Called through ctypes, the caller is libffi's closure trampoline, which belongs to no object, so only the main executable was reported. Calling dl_iterate_phdr() from the _ctypes extension module makes _ctypes the caller and reports all loaded shared libraries. (cherry picked from commit bfc16a7) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 23bf108 commit 7bd27e6

6 files changed

Lines changed: 65 additions & 47 deletions

File tree

Lib/ctypes/util.py

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -442,53 +442,12 @@ def find_library(name):
442442
_get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name))
443443

444444

445-
# Listing loaded libraries on other systems will try to use
446-
# functions common to Linux and a few other Unix-like systems.
447-
# See the following for several platforms' documentation of the same API:
448-
# https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
449-
# https://man.freebsd.org/cgi/man.cgi?query=dl_iterate_phdr
450-
# https://man.openbsd.org/dl_iterate_phdr
451-
# https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html
452-
if (os.name == "posix" and
453-
sys.platform not in {"darwin", "ios", "tvos", "watchos"}):
454-
import ctypes
455-
if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"):
456-
457-
class _dl_phdr_info(ctypes.Structure):
458-
_fields_ = [
459-
("dlpi_addr", ctypes.c_void_p),
460-
("dlpi_name", ctypes.c_char_p),
461-
("dlpi_phdr", ctypes.c_void_p),
462-
("dlpi_phnum", ctypes.c_ushort),
463-
]
464-
465-
_dl_phdr_callback = ctypes.CFUNCTYPE(
466-
ctypes.c_int,
467-
ctypes.POINTER(_dl_phdr_info),
468-
ctypes.c_size_t,
469-
ctypes.POINTER(ctypes.py_object),
470-
)
471-
472-
@_dl_phdr_callback
473-
def _info_callback(info, _size, data):
474-
libraries = data.contents.value
475-
name = os.fsdecode(info.contents.dlpi_name)
476-
libraries.append(name)
477-
return 0
478-
479-
_dl_iterate_phdr = _libc["dl_iterate_phdr"]
480-
_dl_iterate_phdr.argtypes = [
481-
_dl_phdr_callback,
482-
ctypes.POINTER(ctypes.py_object),
483-
]
484-
_dl_iterate_phdr.restype = ctypes.c_int
485-
486-
def dllist():
487-
"""Return a list of loaded shared libraries in the current process."""
488-
libraries = []
489-
_dl_iterate_phdr(_info_callback,
490-
ctypes.byref(ctypes.py_object(libraries)))
491-
return libraries
445+
# On platforms which provide dl_iterate_phdr(), dllist() is implemented
446+
# in _ctypes.
447+
try:
448+
from _ctypes import dllist
449+
except ImportError:
450+
pass
492451

493452
################################################################
494453
# test code
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:func:`ctypes.util.dllist` now works on NetBSD. It is implemented in the
2+
:mod:`!_ctypes` extension module so that ``dl_iterate_phdr()`` reports all
3+
loaded shared libraries: on NetBSD it only reports the link-map group of
4+
the calling object, which excluded them when called through ctypes.

Modules/_ctypes/callproc.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,42 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
16601660
PyErr_Format(PyExc_OSError, "symbol '%s' not found", name);
16611661
return NULL;
16621662
}
1663+
1664+
// Apple platforms use the dyld API in ctypes.util instead.
1665+
#if defined(HAVE_DL_ITERATE_PHDR) && !defined(__APPLE__)
1666+
#include <link.h>
1667+
1668+
static int
1669+
_dllist_callback(struct dl_phdr_info *info, size_t size, void *data)
1670+
{
1671+
PyObject *list = (PyObject *)data;
1672+
PyObject *name = PyUnicode_DecodeFSDefault(info->dlpi_name);
1673+
if (name == NULL) {
1674+
return -1;
1675+
}
1676+
int res = PyList_Append(list, name);
1677+
Py_DECREF(name);
1678+
return res;
1679+
}
1680+
1681+
static PyObject *
1682+
dllist(PyObject *self, PyObject *Py_UNUSED(ignored))
1683+
{
1684+
// On NetBSD dl_iterate_phdr() only reports the link-map group of the
1685+
// caller, so it cannot be called via a libffi trampoline.
1686+
PyObject *list = PyList_New(0);
1687+
if (list == NULL) {
1688+
return NULL;
1689+
}
1690+
// The return value only echoes the callback result.
1691+
dl_iterate_phdr(_dllist_callback, list);
1692+
if (PyErr_Occurred()) {
1693+
Py_DECREF(list);
1694+
return NULL;
1695+
}
1696+
return list;
1697+
}
1698+
#endif
16631699
#endif
16641700

16651701
/*
@@ -2036,6 +2072,10 @@ PyMethodDef _ctypes_module_methods[] = {
20362072
"dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"},
20372073
{"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"},
20382074
{"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"},
2075+
#if defined(HAVE_DL_ITERATE_PHDR) && !defined(__APPLE__)
2076+
{"dllist", dllist, METH_NOARGS,
2077+
"dllist() return a list of loaded shared libraries"},
2078+
#endif
20392079
#endif
20402080
#ifdef __APPLE__
20412081
{"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},

configure

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5411,6 +5411,9 @@ DLINCLDIR=.
54115411
# platforms have dlopen(), but don't want to use it.
54125412
AC_CHECK_FUNCS([dlopen])
54135413

5414+
# Used by ctypes.util.dllist().
5415+
AC_CHECK_FUNCS([dl_iterate_phdr])
5416+
54145417
# DYNLOADFILE specifies which dynload_*.o file we will use for dynamic
54155418
# loading of modules.
54165419
AC_SUBST([DYNLOADFILE])

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@
317317
/* Define to 1 if you have the 'dlopen' function. */
318318
#undef HAVE_DLOPEN
319319

320+
/* Define to 1 if you have the 'dl_iterate_phdr' function. */
321+
#undef HAVE_DL_ITERATE_PHDR
322+
320323
/* Define to 1 if you have the 'dup' function. */
321324
#undef HAVE_DUP
322325

0 commit comments

Comments
 (0)