-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify correct runtime (versus compile time) libraries on Linux #84
Comments
The distinction between development and runtime library names is important. Note that the latter has a version number suffix: this changes whenever some backward-incompatible change is made to the ABI. It makes sense for the generated ctypes wrapper to explicitly load the filename with the correct version suffix, in case some other version is also present on the system. I admit that this is probably not something easy to do with automatically-generated ctypes wrappers. I create all mine by hand, because I feel that is the only way to create a truly Pythonic library wrapper. |
The pypdfium2-team branch of ctypesgen uses |
Note though that currently this code passage is only active if you do not use custom runtime libdirs, i.e. the library lies in standard system directories. Presumably this would be the case here.
|
Problem
1.0.2
) in environmentA
(Debian Stretch), to generate a Python wrapper for a header file that includesext2fs.h
and declares functions exported by thelibext2fs.so
library.B
(again Debian Stretch). Importing the Python file, resulted in the following error:However, I could successfully compile in
A
a C executable that uses the same header file (to link against the shared library I passed-lext2fs
to gcc), and I could also successfully run the executable inB
without any issues.Inspecting environment
B
, I noticed thatlibext2fs.so
did not exist, since it is part of thee2fslibs-dev
package. However, it may make sense for a development package to be absent from my run environment. I also could not pass thelibext2fs.so.2
library, which exists inB
, to ctypesgen, because this file does not exist inA
. Again there is probably no reason for it to exist inA
, since I do not need to run an executable linked againstlibext2fs
there. I could passlibext2fs.so.2.4
to ctypesgen, which exists in both environments, but then updating thelibext2fs
library to2.5
inB
, would needlessly break the wrapper. Since the library's major version number would be unaltered, the changes would not affect the library's interface and so I should still be able to use the same Python wrapper.In conclusion, it seems that on Linux, ctypesgen does not differentiate between the compile/link-time libraries and the runtime libraries.
Background
According to my current understanding, ctypesgen loads the provided libraries twice. Once in generation time (processing phase), to determine which library contains each function and variable, and once in runtime to load the library and call the underlying function. The printer writes the library name on the exported Python file, and upon runtime, the
libraryloader
code takes care of finding and loading that library.On GCC, to link against shared libraries one uses the
-l
option (-lname
), which after compilation, is passed to the static linker. If I have understood correctly, the latter automatically searches the development-specific library name, by extending the name provided with thelib
prefix and the.so
suffix (libname.so
). Although, this can be the ELF library itself, it is usually a symbolic link to the actual ELF, which has the full-version suffix (.so.X.Y
). The static linker is responsible of specifying the dependency to the shared library. However, it does not specify the.so
library as a dependency, because in another environment this may be a symlink to a different library version, and so the executable will be broken. Actually, it does not even specify the name of the full library version (.so.X.Y
), since this is more restrictive than necessary. It usually specifies the ABI version of the library (.so.X
), which guarantees that the ABI is compatible so that the executable does not break, but also allows for internal changes in the library that do not affect its interface (ABI in general).As far as I know, implementation-wise, this process is facilitated by the
SONAME
entry in an ELF library. For most properly built libraries this filed contains the library name, which implies ABI compatibility at the symbols' level. Therefore, the static linker probably opens the.so
library and specifies the library'sSONAME
as the dependency for the final executable. If the.so
library is a symlink, it actually opens the actual library file (.so.X.Y
), and reads theSONAME
from there. It should still be the ABI library name (.so.X
).Proposal
If the above hold and I am not missing something, in ctypesgen's case, I assume that the user could provide the
.so
library name through the--library
option, and then ctypesgen could inspect that library throughobjdump
and add theSONAME
value, as the library that should be loaded upon runtime. Thus, the same semantics applied to normal executables would be applied.This also solves the potential issue of using two distinct environments for generating and using the Python wrappers. Generation can happen in an environment that only contains the
.so
file that points to.so.X.Y
(but whoseSONAME
is.so.X
), and actual use of the wrappers can happen in an environment that only contains theso.X
file that points to.so.X.Y
. This is actually quite a common practice in Debian, which distinguishes the development packages, which contain files necessary to link against a shared library, from the regular packages, which just contain the shared library itself (ELF).However, since I am not aware of the project details, what is the rationale behind the
--library
option? Is there something that I am missing regarding the specification of libraries? Do you think that extending ctypesgen to specify the ABI name of a library as the runtime dependency would make sense?Thanks in advance and sorry for the length of the issue.
The text was updated successfully, but these errors were encountered: