-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Do not rely on unversioned .so files #6742
Comments
Interesting; I thought this would be fixed by parsing the output of |
Glad to hear it's the intended behavior.
|
This could be the problem: that output points us to |
No, the file exists (it's the same as /usr/lib64/libopenspecfun.so). At least, it exists when package openspecfun-devel is installed. |
It could be that there's something else going on here, because if I remember correctly we attempt to load the versioned library first, and if that fails, we attempt to load the unversioned library next, so if we for whatever reason can't load either, that error message might be the one that gets printed out. |
I've tried debugging this, but strangely enough I'm not able to reproduce the problem with an in-tree build, only with the RPM version. More precisely, I've tried breaking on |
Could it be that the error message we're seeing here is deceptive? Looking at your error message again:
This sounds to me like the error is that |
No, the error appears even when openlibm or openspecfun are not present at all, so the problem is not in their files. And the error message can be found in ccall.cpp. Thinking about it, I think Julia really needs to store the expected SOVERSION of the libraries it wants to load. It doesn't make sense to try loading any version of a library, since API incompatibilities can arise; what would happen if the user has two versions of the same library installed? Granted, for openlibm and openspecfun at the moment it's not a problem, but we may need to break the API in the future; double-conversion may break it at some point. (For packages it would also be useful, especially since they will not be shipped as distro packages, and thus are particularly exposed to version mismatches.) |
This is a part of Julia that definitely will need work in the future. Pinging @Keno since he's worked on this area before. The way I see it; |
The version shouldn't be so precise, you only need the SONAME. So e.g. just On Windows we probably don't care since the libraries wouldn't be shared with the rest of the system. So Julia could just remove everything after the dot to get unversioned requirements (or keep the version in the name). I don't know how this works on Mac OS X. |
Hm, |
@sebastien-villemot Maybe you want to give us your opinion here? |
I actually agree with @nalimilan : Julia should ideally always include a SONAME version when dlopen'ing a library. This diminishes the risk of calling an old version of a library that does not include the required symbols. For the Debian package, I carry a (quite big) patch where I add a version number to all dlopen calls. Since SONAME can actually vary across distributions, I guess the only practical way of implementing this is to have some sort of detection of libraries at the beginning of the build process, and put the SONAMES into |
I think we can make the following assumptions:
I'm not sure in what circumstances SONAMEs would be necessary given the hooks in BinDeps, (and if you're writing a package that uses binary dependencies, you'll probably need to use the hooks anyway since SONAMEs aren't the standard on other platforms) but I'd like to be enlightened so that we can implement this in the best way possible. The place I can see this being used most is in Base, where we might need to do things like only load |
We probably need to distinguish Julia itself from packages: Julia will be compiled once when making distro packages, and we can save the SONAMEs at that point; OTC, packages will be built each time a user installs them, therefore they will need to detect libraries at that point anyway. So I'd say we should use @sebastien-villemot's solution for Julia, and @staticfloat's for packages. |
For now the workaround which has been suggested while reviewing the Fedora package is to create |
For the Julia Debian package we now automatically detect the library sonames at build-time and generate https://anonscm.debian.org/cgit/pkg-julia/julia.git/tree/debian/shlibdeps.c The use of private library symlinks ensures that external packages load the same library soversion as Base modules, e.g., for :libgmp that is also loaded in some Julia packages such as BigRationals.jl. As a long-term solution, however, I suggest to define constants in |
@petercolberg That sounds interesting. Though I wonder whether we couldn't link libjulia to these libraries instead, even if they are not used from the C++ code. That way, if I'm not mistaken, all calls to |
My proposal above about actually linking against the libraries would fix issues reported with RHEL packages in #12741 (comment). It would allow the package to explicitly depend on the version that was used during the build, which isn't currently possible to do automatically and leads to less clear errors at runtime. |
I think only Linux distribution packagers should ever link libjulia against ccall-only dependencies. Otherwise it's bad for embedding and solves a problem that doesn't really exist in other environments. Could you create the current libjulia as libjulia-runtime and make a new libjulia for your purposes that just links against that plus the ccall deps? That way users of the distro packages who want to embed julia in some other application still have an option comparable to the way the tarball binaries work. |
Of course, we could link to ccall libraries only when some environment variable is passed to make. But I don't understand what's so specific about embedding: if we want |
Embedding use cases would often want to leave out any libraries they do not use. |
@nalimilan Please take a detailed look at the Debian packaging, it generates both private symlinks and package dependencies automatically. We also disable the fallback to
Symlinking libjulia against all libraries needed by Base is not good solution, since it produces warnings during package building about useless dependencies due to not using any symbols of those libraries. Note that @tkelman, @nalimilan If you think it is an idea worthwhile exploring, I will prepare a pull request that integrates shlibdeps.c into the Julia build process for Linux and FreeBSD. This step would query the sonames at build time and store them as constants in |
@petercolberg Warnings that are clearly wrong are a small annoyance, and we could likely get rid of them with appropriate flags (remember, those would only be used to build packages, so any legitimate warning would still be caught during normal builds).
I'm not an expert about this, so others will have to confirm, but it may well be possible to call linked libraries with a higher priority. If linking really couldn't work, then adding a mechanism to list required libraries sounds reasonable. The advantage of linking would have been that packagers wouldn't need to do anything special, while with your solution they need to generate dependencies manually (e.g. it doesn't sound super easy, though doable, for RPM). Regarding how to get the list of required libraries, I don't understand why a creating a specific program is needed. Couldn't we simply list the libraries that Julia needs in a static file, then use |
On Fri, Nov 20, 2015 at 08:50:39AM -0800, Milan Bouchet-Valat wrote:
The solution uses the same linking of all libraries that you propose for libjulia, but for a dummy executable. The packaging tool inspects the dummy executable to generate automatic dependencies.
Yes, you still need to provide one symbol per library, as found in the Base modules. I have a fully automatic solution in mind, too, but that requires more work on the side of the distributions. The GNU dynamic linker provides an interface to audit the loading of libraries ( |
Yes, but the point of my proposal is that package building tools already take care of detecting linked libraries automatically, without all this custom code to be written for each distribution.
I wouldn't care too much about that, since the goal is to build packages in clean environments where the only installed packages are those shipped by distributions and required by the package description. What matters is the robustness on users' systems.
Looks like we can handle this easily too by calling |
On Fri, Nov 20, 2015 at 10:41:08AM -0800, Milan Bouchet-Valat wrote:
I am not sure what you mean with custom code. All that is required for the distributor is to pass the dummy executable to the tool that detects shlibs dependencies.
I have been through that thought process before. You cannot simply dump the linked libraries, because the SONAME might not match the link name. |
Solution 2. is what I'm currently doing in Fedora. It indeed works well, with two drawbacks:
To generate dependencies, RPM needs an executable taking file names via stdin and writing requirements to stdout: http://www.rpm.org/wiki/PackagerDocs/DependencyGenerator But that executable could be a very simple shell script, or even a call to |
Good to know that solution 2 works for Fedora, too. Then I will prepare a pull request that (a) creates symlinks in the private libdir for system libs and (b) generates a text file with the sonames, one per line. A change in soname should in fact require manual intervention from the packager. This way Julia is rebuilt and tested when a dependent library undergoes an incompatible ABI change. In Debian we schedule a binNMU when a library changes ABI, which is an automated rebuild of the same source package using the updated dependencies. |
@opoplawski Could you confirm that using RPM dependency generators (or another solution if it exists) would allow turning a text file listing the sonames into RPM Requires? |
I believe I can write a RPM dependency generator for this case, yes. @petercolberg - have you made any progress on generating the list/links? |
Instead of producing all these symlinks, why can't you add a hash table (generated at compile time) into the |
@kkofler can you explain why you think the symlinks are ugly? On the contrary, I think it’s a transparent and maintainable solution from the point of view of distributions. |
To provide more detail on the maintainability aspect: It is immediately apparent to a non-Julia developer which library soversions julia loads, by inspecting the julia binaries (for compile-time linked libraries, e.g., using This non-Julia developer could for example be a distribution maintainer who has triggered the recompilation of a collection of dependent packages for a library soversion transition. |
I wouldn't consider the use of symlinks as an issue. What matters is that 1) distributions can extract a list of versioned library dependencies automatically, and 2) that any change in version requirements immediately leads to a build/test failure so that the package maintainer notices it. |
@petercolberg I've just discovered that RPM automatically added SONAMES as dependencies when the package contains a symlink to the library .so file. So I've added a series of commands like this:
This hardcodes the SONAME (including SOVERSION) at build time, so that any API/ABI break in one of the dependencies is detected by the package manager. This simple solution could be moved upstream if you'd also like to use it. I don't even need a file with the list of SONAMES, but if that's useful to you, we could create it. Are you still interested in making a PR? |
On Thu, Mar 03, 2016 at 10:21:02AM -0800, Milan Bouchet-Valat wrote:
That won’t produce the correct symlinks, see for example libpcre2 on Debian: /usr/lib/x86_64-linux-gnu/libpcre2-8.so -> libpcre2-8.so.0.2.0 The .so symlink points to .so.0.2.0, which means the private symlink The correct private symlink based on the DT_SONAME field is /usr/lib/x86_64-linux-gnu/julia/libpcre2-8.so -> ../libpcre2-8.so.0
Yes, I will submit a PR soon. Knowning that rpm can derive the library |
Of course you're right. I knew it couldn't be so simple... I guess as a temporary hack until your PR I can switch to |
On Thu, Mar 03, 2016 at 10:59:49AM -0800, Milan Bouchet-Valat wrote:
You could replace realpath with readelf, e.g., readelf -d /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0 | sed -n '/SONAME/s/.(lib[^ ].so.[0-9])./\1/p' |
Thanks, that indeed sounds more robust. |
For reference, openlibm needs to be handled separately since sed only supports greedy matching, which gives |
I have the same problem, as @nalimilan described in this issue, on Alpine Linux – Julia tries to load unversioned so libs which are provided by -dev packages. Moreover, |
Indeed, parsing I would like to implement a reliable query of the library soname based on #6742 (comment). I have yet to figure out how to best integrate this as a substitute for the existing |
@jirutka Have you tried the |
@nalimilan No, but I found another solution/workaround, see alpinelinux/aports#112 (comment). 😺 |
AFAICT that workaround won't automatically add the loaded libraries as package dependencies. That's the real interest of the |
The workaround in Fedora package does not automatically add the loaded libraries either, the libraries are explicitly named here: The workaround I’ve used allows to discover any library, not just those specifically named and not just those available in build time. Its behaviour is almost the same as the original Julia’s solution which relies on |
That's true, but that's exactly the same in your case, via the list of dependencies. Anyway, the list of libraries that Julia uses is relatively stable, so while not perfect it's not a big deal. OTOH, distribution packages are often updated in a backward-incompatible way, so it's best to depend on a specific SONAME so that distribution build systems notice the package needs to be rebuilt. Since the test suite is run during the build, we get a build failure if the ABI break affects Julia and I get notified. Believe me, getting silent failures until a user files a bug days later was really terrible until I found that solution. |
Since Julia does not link to external libraries at compile time, it should not try to access unversioned .so files, which may not be present on the user system as they are typically only provided by -devel packages.
This problem is visible at least for openlibm and openspecfun with Julia from the Fedora RPM package, when the -devel packages are not installed:
I'm not sure how best to fix this. The versioned file names could be saved somewhere at build time (just like a linker would do), in order to call them instead of the unversioned file.
The text was updated successfully, but these errors were encountered: