How is the attribute of the loader instance `cdll` created?

ghz 昨天 ⋅ 1 views

From https://docs.python.org/3/library/ctypes.html#loading-shared-libraries

Shared libraries can also be loaded by using one of the prefabricated objects, which are instances of the LibraryLoader class, either by calling the LoadLibrary() method, or by retrieving the library as attribute of the loader instance.

I found an example for the first way Free the opened ctypes library in Python

I was wondering how to use the second way? In particular, how is the attribute of the loader instance cdll created? My question comes from Why does loading the libc shared library have "'LibraryLoader' object is not callable" error?

The whole point of the LibraryLoader is that it creates the library for you when you access it. And cdll.LoadLibrary("foo") doesn't create cdll.foo.

Is there something wrong with my experiment? Why does cdll.libc never exist?

>>> from ctypes import *
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1afac8>
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> libc=cdll.LoadLibrary("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> CDLL("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1af978>
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory

>>> libc=CDLL("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
    dll = self._dlltype(name)
  File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.__dict__
{'_dlltype': <class 'ctypes.CDLL'>}

Answer

The confusion you're encountering stems from a misunderstanding of how the cdll object works in the ctypes library, particularly how the LibraryLoader class is used to load shared libraries and how attributes like cdll.libc are accessed.

Let me break it down and clarify:

1. The cdll Object

The cdll object is an instance of the LibraryLoader class in ctypes. The purpose of LibraryLoader is to provide a convenient way to load shared libraries and interact with them. The cdll instance itself is not a direct interface to any specific library by name (like libc), but rather it provides the method LoadLibrary() to load shared libraries dynamically.

2. Accessing Libraries as Attributes of cdll

When you use cdll.libc directly, Python will try to access an attribute libc of cdll. If that attribute doesn’t exist, Python will attempt to resolve it dynamically. In the case of ctypes, this dynamic lookup will attempt to load a shared library named libc. This is not how cdll is designed to work for specific libraries like libc.so.6.

To make this clearer:

  • cdll.LoadLibrary("libc.so.6"): This loads the libc.so.6 shared library and returns a CDLL object representing that library.
  • cdll.libc: This causes an error because cdll does not automatically have an attribute libc, and it does not create it when you try to access it.

3. Why cdll.libc doesn't work

When you do cdll.libc, Python looks for an attribute libc in cdll, which doesn't exist by default. Therefore, it tries to load libc as a library, which results in the error:

OSError: libc: cannot open shared object file: No such file or directory

The LibraryLoader class doesn't automatically create attributes like libc for specific libraries; you need to explicitly load libraries using LoadLibrary().

4. Proper Usage of cdll for Loading Libraries

Instead of accessing libraries like cdll.libc, you should explicitly load the library with LoadLibrary(), which returns a CDLL object for that specific library.

Correct approach:

from ctypes import cdll

# Load the library using LoadLibrary
libc = cdll.LoadLibrary("libc.so.6")

# Now you can use the `libc` object to call functions from libc
print(libc)

This will load the libc.so.6 shared library, and you'll be able to interact with it via the libc object returned by cdll.LoadLibrary().

Understanding cdll.__dict__:

If you check cdll.__dict__, you'll see that it contains an attribute _dlltype, which is the CDLL class used by cdll to create instances for loaded libraries. But it does not contain attributes for individual libraries like libc.

Conclusion

To summarize:

  • cdll.libc doesn’t work because cdll doesn’t automatically create a libc attribute.
  • You should explicitly load libraries using cdll.LoadLibrary("libc.so.6") or CDLL("libc.so.6") to interact with shared libraries in ctypes.
  • cdll itself is not designed to automatically load specific libraries when accessed as attributes. You need to manually load libraries using LoadLibrary().

So, the following should work:

from ctypes import cdll

# Load the library explicitly
libc = cdll.LoadLibrary("libc.so.6")

# Now, `libc` is a CDLL object for interacting with the libc shared library

This way, you can access the shared library directly and interact with its functions.