diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index fcbe2122d9f1a7..8b213ef1d49932 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1360,104 +1360,6 @@ is already known, on a case by case basis. ctypes reference ---------------- - -.. _ctypes-finding-shared-libraries: - -Finding shared libraries -^^^^^^^^^^^^^^^^^^^^^^^^ - -When programming in a compiled language, shared libraries are accessed when -compiling/linking a program, and when the program is run. - -The purpose of the :func:`~ctypes.util.find_library` function is to locate a library in a way -similar to what the compiler or runtime loader does (on platforms with several -versions of a shared library the most recent should be loaded), while the ctypes -library loaders act like when a program is run, and call the runtime loader -directly. - -The :mod:`!ctypes.util` module provides a function which can help to determine -the library to load. - - -.. data:: find_library(name) - :module: ctypes.util - :noindex: - - Try to find a library and return a pathname. *name* is the library name without - any prefix like *lib*, suffix like ``.so``, ``.dylib`` or version number (this - is the form used for the posix linker option :option:`!-l`). If no library can - be found, returns ``None``. - -The exact functionality is system dependent. - -On Linux, :func:`~ctypes.util.find_library` tries to run external programs -(``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the library file. -It returns the filename of the library file. - -Note that if the output of these programs does not correspond to the dynamic -linker used by Python, the result of this function may be misleading. - -.. versionchanged:: 3.6 - On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used - when searching for libraries, if a library cannot be found by any other means. - -Here are some examples:: - - >>> from ctypes.util import find_library - >>> find_library("m") - 'libm.so.6' - >>> find_library("c") - 'libc.so.6' - >>> find_library("bz2") - 'libbz2.so.1.0' - >>> - -On macOS and Android, :func:`~ctypes.util.find_library` uses the system's -standard naming schemes and paths to locate the library, and returns a full -pathname if successful:: - - >>> from ctypes.util import find_library - >>> find_library("c") - '/usr/lib/libc.dylib' - >>> find_library("m") - '/usr/lib/libm.dylib' - >>> find_library("bz2") - '/usr/lib/libbz2.dylib' - >>> find_library("AGL") - '/System/Library/Frameworks/AGL.framework/AGL' - >>> - -On Windows, :func:`~ctypes.util.find_library` searches along the system search path, and -returns the full pathname, but since there is no predefined naming scheme a call -like ``find_library("c")`` will fail and return ``None``. - -If wrapping a shared library with :mod:`!ctypes`, it *may* be better to determine -the shared library name at development time, and hardcode that into the wrapper -module instead of using :func:`~ctypes.util.find_library` to locate the library at runtime. - - -.. _ctypes-listing-loaded-shared-libraries: - -Listing loaded shared libraries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When writing code that relies on code loaded from shared libraries, it can be -useful to know which shared libraries have already been loaded into the current -process. - -The :mod:`!ctypes.util` module provides the :func:`~ctypes.util.dllist` function, -which calls the different APIs provided by the various platforms to help determine -which shared libraries have already been loaded into the current process. - -The exact output of this function will be system dependent. On most platforms, -the first entry of this list represents the current process itself, which may -be an empty string. -For example, on glibc-based Linux, the return may look like:: - - >>> from ctypes.util import dllist - >>> dllist() - ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ] - .. _ctypes-loading-shared-libraries: Loading shared libraries @@ -1485,13 +1387,20 @@ way is to instantiate :py:class:`CDLL` or one of its subclasses: attribute, but it may be adjusted and/or validated. If *handle* is ``None``, the underlying platform's :manpage:`dlopen(3)` or - :c:func:`!LoadLibrary` function is used to load the library into + `LoadLibraryExW`_ function is used to load the library into the process, and to get a handle to it. *name* is the pathname of the shared library to open. If *name* does not contain a path separator, the library is found in a platform-specific way. + On Windows, the ``.DLL`` suffix may be missing. (For details, see + `LoadLibraryExW`_ documentation.) + Other platform-specific prefixes and suffixes (for example, ``lib``, + ``.so``, ``.dylib``, or version numbers) must be present in *name*; + they are not added automatically. + See :ref:`ctypes-finding-shared-libraries` for more information. + On non-Windows systems, *name* can be ``None``. In this case, :c:func:`!dlopen` is called with ``NULL``, which opens the main program as a "library". @@ -1536,7 +1445,7 @@ way is to instantiate :py:class:`CDLL` or one of its subclasses: The *winmode* parameter is used on Windows to specify how the library is loaded (since *mode* is ignored). It takes any value that is valid for the Win32 API - ``LoadLibraryEx`` flags parameter. When omitted, the default is to use the + `LoadLibraryExW`_ flags parameter. When omitted, the default is to use the flags that result in the most secure DLL load, which avoids issues such as DLL hijacking. Passing the full path to the DLL is the safest way to ensure the correct library and dependencies are loaded. @@ -1587,6 +1496,8 @@ way is to instantiate :py:class:`CDLL` or one of its subclasses: The name of the library passed in the constructor. +.. _LoadLibraryExW: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw + .. class:: OleDLL See :py:class:`~ctypes.CDLL`, the superclass, for common information. @@ -1665,39 +1576,36 @@ attribute of the loader instance. These prefabricated library loaders are available: -.. data:: cdll - - Creates :class:`CDLL` instances. + .. data:: cdll + Creates :class:`CDLL` instances. -.. data:: windll - Creates :class:`WinDLL` instances. + .. data:: windll - .. availability:: Windows + Creates :class:`WinDLL` instances. + .. availability:: Windows -.. data:: oledll - Creates :class:`OleDLL` instances. + .. data:: oledll - .. availability:: Windows + Creates :class:`OleDLL` instances. + .. availability:: Windows -.. data:: pydll - Creates :class:`PyDLL` instances. + .. data:: pydll + Creates :class:`PyDLL` instances. -For accessing the C Python api directly, a ready-to-use Python shared library -object is available: -.. data:: pythonapi + .. data:: pythonapi - An instance of :class:`PyDLL` that exposes Python C API functions as - attributes. Note that all these functions are assumed to return C - :c:expr:`int`, which is of course not always the truth, so you have to assign - the correct :attr:`!restype` attribute to use these functions. + An instance of :class:`PyDLL` that exposes Python C API functions as + attributes. Note that all these functions are assumed to return C + :c:expr:`int`, which is of course not always the truth, so you have to assign + the correct :attr:`!restype` attribute to use these functions. .. audit-event:: ctypes.dlopen name ctypes.LibraryLoader @@ -1717,6 +1625,135 @@ object is available: accessing a function raises an auditing event ``ctypes.dlsym/handle`` with arguments ``handle`` (the raw library handle) and ``name``. + +.. _ctypes-finding-shared-libraries: + +Finding shared libraries +^^^^^^^^^^^^^^^^^^^^^^^^ + +When programming in a compiled language, shared libraries are accessed when +compiling/linking a program, and when the program is run. +The programmer specifies a short name; the C compiler, linker, and +runtime dynamic library loader then interact in system-specific ways to find +the filename of the library to load. + +While the mapping from short names to filenames is not consistently exposed +by platforms, the :mod:`!ctypes.util` module provides a function, +:func:`!find_library`, that attempts to match it. +However, as backwards compatibility concerns make it difficult to adjust +its behavior for new platforms and configurations, the function +is :term:`soft deprecated`. + +If wrapping a shared library with :mod:`!ctypes`, consider determining the +shared library name at development time, and hardcoding it into the wrapper +module instead of using :func:`!find_library` to locate the library +at runtime. +Also consider addding a configuration option or environment variable to let +users select a library to use, and then perhaps use :func:`!find_library` +as a default or fallback. + +.. function:: find_library(name) + :module: ctypes.util + + Try to find a library and return a pathname. + + *name* is the "short" library name without any prefix like ``lib``, + suffix like ``.so``, ``.dylib`` or version number. + (This is the form used for the posix linker option :option:`!-l`.) + The result is in a format suitable for passing to :py:class:`~ctypes.CDLL`. + + If no library can be found, return ``None``. + + The exact functionality is system dependent, and is *not guaranteed* + to match the behavior of the compiler, linker, and loader used for + (or by) Python. + It is recommended to only use this function as a default or fallback, + + .. deprecated:: next + + This function is :term:`soft deprecated`. + It is kept for use in cases where it works, but not expected to be + updated for additional platforms and configurations. + +On Linux, :func:`!find_library` tries to run external +programs (``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the +library file. +If the output of these programs does not correspond to the dynamic +linker used by Python, the result of this function may be misleading. + +.. versionchanged:: 3.6 + On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used + when searching for libraries, if a library cannot be found by any other means. + +Here are some examples:: + + >>> from ctypes.util import find_library + >>> find_library("m") + 'libm.so.6' + >>> find_library("c") + 'libc.so.6' + >>> find_library("bz2") + 'libbz2.so.1.0' + >>> + +On macOS and Android, :func:`!find_library` uses the system's +standard naming schemes and paths to locate the library, and returns a full +pathname if successful:: + + >>> from ctypes.util import find_library + >>> find_library("c") + '/usr/lib/libc.dylib' + >>> find_library("m") + '/usr/lib/libm.dylib' + >>> find_library("bz2") + '/usr/lib/libbz2.dylib' + >>> find_library("AGL") + '/System/Library/Frameworks/AGL.framework/AGL' + >>> + +On Windows, :func:`!find_library` searches along the system search path, and +returns the full pathname, but since there is no predefined naming scheme a call +like ``find_library("c")`` will fail and return ``None``. + +.. function:: find_msvcrt() + :module: ctypes.util + + Returns the filename of the VC runtime library used by Python, + and by the extension modules. + + If the name of the library cannot be determined, ``None`` is returned. + Notably, this will happen for recent versions of the VC runtime library, + which are not directly loadable. + + If you need to free memory, for example, allocated by an extension module + with a call to the ``free(void *)``, it is important that you use the + function in the same library that allocated the memory. + + .. availability:: Windows + + +.. _ctypes-listing-loaded-shared-libraries: + +Listing loaded shared libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When writing code that relies on code loaded from shared libraries, it can be +useful to know which shared libraries have already been loaded into the current +process. + +The :mod:`!ctypes.util` module provides the :func:`~ctypes.util.dllist` function, +which calls the different APIs provided by the various platforms to help determine +which shared libraries have already been loaded into the current process. + +The exact output of this function will be system dependent. On most platforms, +the first entry of this list represents the current process itself, which may +be an empty string. +For example, on glibc-based Linux, the return may look like:: + + >>> from ctypes.util import dllist + >>> dllist() + ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ] + .. _ctypes-foreign-functions: Foreign functions @@ -2139,33 +2176,6 @@ Utility functions .. availability:: Windows -.. function:: find_library(name) - :module: ctypes.util - - Try to find a library and return a pathname. *name* is the library name - without any prefix like ``lib``, suffix like ``.so``, ``.dylib`` or version - number (this is the form used for the posix linker option :option:`!-l`). If - no library can be found, returns ``None``. - - The exact functionality is system dependent. - - See :ref:`ctypes-finding-shared-libraries` for complete documentation. - - -.. function:: find_msvcrt() - :module: ctypes.util - - Returns the filename of the VC runtime library used by Python, - and by the extension modules. If the name of the library cannot be - determined, ``None`` is returned. - - If you need to free memory, for example, allocated by an extension module - with a call to the ``free(void *)``, it is important that you use the - function in the same library that allocated the memory. - - .. availability:: Windows - - .. function:: dllist() :module: ctypes.util