Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 6 additions & 47 deletions Lib/ctypes/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,53 +442,12 @@ def find_library(name):
_get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name))


# Listing loaded libraries on other systems will try to use
# functions common to Linux and a few other Unix-like systems.
# See the following for several platforms' documentation of the same API:
# https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
# https://man.freebsd.org/cgi/man.cgi?query=dl_iterate_phdr
# https://man.openbsd.org/dl_iterate_phdr
# https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html
if (os.name == "posix" and
sys.platform not in {"darwin", "ios", "tvos", "watchos"}):
import ctypes
if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"):

class _dl_phdr_info(ctypes.Structure):
_fields_ = [
("dlpi_addr", ctypes.c_void_p),
("dlpi_name", ctypes.c_char_p),
("dlpi_phdr", ctypes.c_void_p),
("dlpi_phnum", ctypes.c_ushort),
]

_dl_phdr_callback = ctypes.CFUNCTYPE(
ctypes.c_int,
ctypes.POINTER(_dl_phdr_info),
ctypes.c_size_t,
ctypes.POINTER(ctypes.py_object),
)

@_dl_phdr_callback
def _info_callback(info, _size, data):
libraries = data.contents.value
name = os.fsdecode(info.contents.dlpi_name)
libraries.append(name)
return 0

_dl_iterate_phdr = _libc["dl_iterate_phdr"]
_dl_iterate_phdr.argtypes = [
_dl_phdr_callback,
ctypes.POINTER(ctypes.py_object),
]
_dl_iterate_phdr.restype = ctypes.c_int

def dllist():
"""Return a list of loaded shared libraries in the current process."""
libraries = []
_dl_iterate_phdr(_info_callback,
ctypes.byref(ctypes.py_object(libraries)))
return libraries
# On platforms which provide dl_iterate_phdr(), dllist() is implemented
# in _ctypes.
try:
from _ctypes import dllist
except ImportError:
pass

################################################################
# test code
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:func:`ctypes.util.dllist` now works on NetBSD. It is implemented in the
:mod:`!_ctypes` extension module so that ``dl_iterate_phdr()`` reports all
loaded shared libraries: on NetBSD it only reports the link-map group of
the calling object, which excluded them when called through ctypes.
40 changes: 40 additions & 0 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,42 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
PyErr_Format(PyExc_OSError, "symbol '%s' not found", name);
return NULL;
}

// Apple platforms use the dyld API in ctypes.util instead.
#if defined(HAVE_DL_ITERATE_PHDR) && !defined(__APPLE__)
#include <link.h>

static int
_dllist_callback(struct dl_phdr_info *info, size_t size, void *data)
{
PyObject *list = (PyObject *)data;
PyObject *name = PyUnicode_DecodeFSDefault(info->dlpi_name);
if (name == NULL) {
return -1;
}
int res = PyList_Append(list, name);
Py_DECREF(name);
return res;
}

static PyObject *
dllist(PyObject *self, PyObject *Py_UNUSED(ignored))
{
// On NetBSD dl_iterate_phdr() only reports the link-map group of the
// caller, so it cannot be called via a libffi trampoline.
PyObject *list = PyList_New(0);
if (list == NULL) {
return NULL;
}
// The return value only echoes the callback result.
dl_iterate_phdr(_dllist_callback, list);
if (PyErr_Occurred()) {
Py_DECREF(list);
return NULL;
}
return list;
}
#endif
#endif

/*
Expand Down Expand Up @@ -2036,6 +2072,10 @@ 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"},
#if defined(HAVE_DL_ITERATE_PHDR) && !defined(__APPLE__)
{"dllist", dllist, METH_NOARGS,
"dllist() return a list of loaded shared libraries"},
#endif
#endif
#ifdef __APPLE__
{"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},
Expand Down
9 changes: 9 additions & 0 deletions configure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5411,6 +5411,9 @@ DLINCLDIR=.
# platforms have dlopen(), but don't want to use it.
AC_CHECK_FUNCS([dlopen])

# Used by ctypes.util.dllist().
AC_CHECK_FUNCS([dl_iterate_phdr])

# DYNLOADFILE specifies which dynload_*.o file we will use for dynamic
# loading of modules.
AC_SUBST([DYNLOADFILE])
Expand Down
3 changes: 3 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@
/* Define to 1 if you have the 'dlopen' function. */
#undef HAVE_DLOPEN

/* Define to 1 if you have the 'dl_iterate_phdr' function. */
#undef HAVE_DL_ITERATE_PHDR

/* Define to 1 if you have the 'dup' function. */
#undef HAVE_DUP

Expand Down
Loading