Commits

Armin Rigo committed 0c8581e

Change the default dlopen() flags from RTLD_LAZY to RTLD_NOW.
Give access to all flags, for more precise control.

  • Participants
  • Parent commits dbbbdfe

Comments (0)

Files changed (8)

File c/_cffi_backend.c

     char *filename_or_null, *printable_filename;
     void *handle;
     DynLibObject *dlobj;
-    int is_global = 0;
+    int flags = 0;
 
     if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
-        filename_or_null = NULL;
-        is_global = 1;
+        PyObject *dummy;
+        if (!PyArg_ParseTuple(args, "|Oi:load_library",
+                              &dummy, &flags))
+            return NULL;
     }
     else if (!PyArg_ParseTuple(args, "et|i:load_library",
                           Py_FileSystemDefaultEncoding, &filename_or_null,
-                          &is_global))
+                          &flags))
         return NULL;
 
+    if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
+        flags |= RTLD_NOW;
+
     printable_filename = filename_or_null ? filename_or_null : "<None>";
-    handle = dlopen(filename_or_null,
-                    RTLD_LAZY | (is_global?RTLD_GLOBAL:RTLD_LOCAL));
+    handle = dlopen(filename_or_null, flags);
     if (handle == NULL) {
         PyErr_Format(PyExc_OSError, "cannot load library %s: %s",
                      printable_filename, dlerror());
     if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
         INITERROR;
 
+    if (PyModule_AddIntConstant(m, "FFI_DEFAULT_ABI", FFI_DEFAULT_ABI) < 0 ||
 #if defined(MS_WIN32) && !defined(_WIN64)
-    v = PyInt_FromLong(FFI_STDCALL);
-    if (v == NULL || PyModule_AddObject(m, "FFI_STDCALL", v) < 0)
-        INITERROR;
+        PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 ||
 #endif
-    v = PyInt_FromLong(FFI_DEFAULT_ABI);
-    if (v == NULL || PyModule_AddObject(m, "FFI_DEFAULT_ABI", v) < 0)
-        INITERROR;
-    Py_INCREF(v);
-    if (PyModule_AddObject(m, "FFI_CDECL", v) < 0)  /* win32 name */
-        INITERROR;
+#ifdef FFI_CDECL
+        PyModule_AddIntConstant(m, "FFI_CDECL", FFI_CDECL) < 0 ||   /* win32 */
+#else
+        PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 ||
+#endif
+
+        PyModule_AddIntConstant(m, "RTLD_LAZY",   RTLD_LAZY) < 0 ||
+        PyModule_AddIntConstant(m, "RTLD_NOW",    RTLD_NOW) < 0 ||
+        PyModule_AddIntConstant(m, "RTLD_GLOBAL", RTLD_GLOBAL) < 0 ||
+#ifdef RTLD_LOCAL
+        PyModule_AddIntConstant(m, "RTLD_LOCAL",  RTLD_LOCAL) < 0 ||
+#else
+        PyModule_AddIntConstant(m, "RTLD_LOCAL",  0) < 0 ||
+#endif
+#ifdef RTLD_NODELETE
+        PyModule_AddIntConstant(m, "RTLD_NODELETE",  RTLD_NODELETE) < 0 ||
+#endif
+#ifdef RTLD_NOLOAD
+        PyModule_AddIntConstant(m, "RTLD_NOLOAD",  RTLD_NOLOAD) < 0 ||
+#endif
+#ifdef RTLD_DEEPBIND
+        PyModule_AddIntConstant(m, "RTLD_DEEPBIND",  RTLD_DEEPBIND) < 0 ||
+#endif
+        0)
+      INITERROR;
 
     init_errno();
 

File c/misc_win32.h

 /* Emulate dlopen()&co. from the Windows API */
 
 #define RTLD_LAZY   0
+#define RTLD_NOW    0
 #define RTLD_GLOBAL 0
 #define RTLD_LOCAL  0
 
     return sizeof(BPtr)
 
 
-def find_and_load_library(name, is_global=0):
+def find_and_load_library(name, flags=RTLD_NOW):
     import ctypes.util
     if name is None:
         path = None
     else:
         path = ctypes.util.find_library(name)
-    return load_library(path, is_global)
+    return load_library(path, flags)
 
 def test_load_library():
     x = find_and_load_library('c')
     assert repr(x).startswith("<clibrary '")
-    x = find_and_load_library('c', 1)
+    x = find_and_load_library('c', RTLD_NOW | RTLD_GLOBAL)
     assert repr(x).startswith("<clibrary '")
+    x = find_and_load_library('c', RTLD_LAZY)
+    assert repr(x).startswith("<clibrary '")
+
+def test_all_rtld_symbols():
+    import sys
+    FFI_DEFAULT_ABI        # these symbols must be defined
+    FFI_CDECL
+    RTLD_LAZY
+    RTLD_NOW
+    RTLD_GLOBAL
+    RTLD_LOCAL
+    if sys.platform.startswith("linux"):
+        RTLD_NODELETE
+        RTLD_NOLOAD
+        RTLD_DEEPBIND
 
 def test_nonstandard_integer_types():
     d = nonstandard_integer_types()
         self._pointer_type_cache = {}
         if hasattr(backend, 'set_ffi'):
             backend.set_ffi(self)
+        for name in backend.__dict__:
+            if name.startswith('RTLD_'):
+                setattr(self, name, getattr(backend, name))
         #
         lines = []
         by_size = {}
             for cache in self._function_caches:
                 cache.clear()
 
-    def dlopen(self, name):
+    def dlopen(self, name, flags=0):
         """Load and return a dynamic library identified by 'name'.
         The standard C library can be loaded by passing None.
         Note that functions and types declared by 'ffi.cdef()' are not
         library we only look for the actual (untyped) symbols.
         """
         assert isinstance(name, str) or name is None
-        lib, function_cache = _make_ffi_library(self, name)
+        lib, function_cache = _make_ffi_library(self, name, flags)
         self._function_caches.append(function_cache)
         return lib
 
         return self._backend.rawaddressof(ctypeptr, cdata, offset)
 
 
-def _make_ffi_library(ffi, libname):
+def _make_ffi_library(ffi, libname, flags):
+    import os
     name = libname
     if name is None:
         name = 'c'    # on Posix only
-    if '/' in name:
+    if os.path.sep in name or (
+            os.path.altsep is not None and os.path.altsep in name):
         path = name
     else:
         import ctypes.util
             raise OSError("library not found: %r" % (name,))
     #
     backend = ffi._backend
-    backendlib = backend.load_library(path)
+    backendlib = backend.load_library(path, flags)
     #
     def make_accessor(name):
         key = 'function ' + name

File cffi/backend_ctypes.py

         '_Bool': ctypes.c_bool,
     }
 
+    def __init__(self):
+        self.RTLD_LAZY = 0   # not supported anyway by ctypes
+        self.RTLD_NOW  = 0
+        self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
+        self.RTLD_LOCAL = ctypes.RTLD_LOCAL
+
     def set_ffi(self, ffi):
         self.ffi = ffi
 
                 result['ssize_t'] = size
         return result
 
-    def load_library(self, path):
-        cdll = ctypes.CDLL(path)
+    def load_library(self, path, flags=0):
+        cdll = ctypes.CDLL(path, flags)
         return CTypesLibrary(self, cdll)
 
     def new_void_type(self):

File doc/source/index.rst

 Loading libraries
 -----------------
 
-``ffi.dlopen(libpath)``: this function opens a shared library and
+``ffi.dlopen(libpath, [flags])``: this function opens a shared library and
 returns a module-like library object.  You need to use *either*
 ``ffi.dlopen()`` *or* ``ffi.verify()``, documented below_.
 
 cannot call functions from a library without linking it in your program,
 as ``dlopen()`` does dynamically in C.
 
+For the optional ``flags`` argument, see ``man dlopen`` (ignored on
+Windows).  It defaults to ``ffi.RTLD_NOW``.
+
 .. _below:
 
 

File testing/test_function.py

         x = m.sin(1.23)
         assert x is None
 
+    def test_dlopen_flags(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            double cos(double x);
+        """)
+        m = ffi.dlopen("m", ffi.RTLD_LAZY | ffi.RTLD_LOCAL)
+        x = m.cos(1.23)
+        assert x == math.cos(1.23)
+
     def test_tlsalloc(self):
         if sys.platform != 'win32':
             py.test.skip("win32 only")

File testing/test_parsing.py

     def sizeof(self, name):
         return 1
 
-    def load_library(self, name):
+    def load_library(self, name, flags):
         if sys.platform == 'win32':
             assert "msvcr" in name
         else: