How do SO (shared object) numbers work?
I understand the idea is to have two distinct files such that two versions of a library can exist on a system (as opposed to «DLL Hell» on Windows). I’d like to know how this works in practice? Often, I see that example.so is in fact a symbolic link to example.so.2 where .2 is the latest version. How then does an application depending on an older version of example.so identify it correctly? Are there any rules as to what numbers one must use? Or is this simply convention? Is it the case that, unlike in Windows where software binaries are transferred between systems, if a system has a newer version of a shared object it is linked to the older version automatically when compiling from source?
I suspect this is related to ldconfig but I’m not sure how.
4 Answers 4
Binaries themselves know which version of a shared library they depend on, and request it specifically. You can use ldd to show the dependencies; mine for ls are:
$ ldd /bin/ls linux-gate.so.1 => (0xb784e000) librt.so.1 => /lib/librt.so.1 (0xb782c000) libacl.so.1 => /lib/libacl.so.1 (0xb7824000) libc.so.6 => /lib/libc.so.6 (0xb76dc000) libpthread.so.0 => /lib/libpthread.so.0 (0xb76c3000) /lib/ld-linux.so.2 (0xb784f000) libattr.so.1 => /lib/libattr.so.1 (0xb76bd000)
As you can see, it points to e.g. libpthread.so.0 , not just libpthread.so .
The reason for the symbolic link is for the linker. When you want to link against libpthread.so directly, you give gcc the flag -lpthread , and it adds on the lib prefix and .so suffix automatically. You can’t tell it to add on the .so.0 suffix, so the symbolic link points to the newest version of the lib to faciliate that
@bmacnaughton That will probably give you an error because ldd requires the complete path to the executable. =ls does that in zsh, but I changed it since not everyone uses that shell
Interesting. I’m running bash on Ubuntu and it seems to work without the full path. Thanks for the explanation — I don’t use zsh.
» so the symbolic link points to the newest version of the lib to faciliate that» the reason for the symlink is to point to the .so.X file that matches the installed headers on the system. Not to point to the newest-version .so.X file. It often is the newest, but there are also often cases where it isn’t (particularly during development.. outside of that, the symlink is useless anyway).
The numbers in the shared libraries are convention used in Linux to identify the API of a library. Typically the format is:
And as you noticed usually there is a symbolic link from libFOO.so to libFOO.so.MAJOR.MINOR. ldconfig is responsible for updating this link to the newest version.
The MAJOR is typically incremented when the API changes (new entry points are removed or the parameters or types changed). The MINOR typically is incremented for bug fix releases or when new APIs are introduced without breaking existing APIs.
A more extensive discussion can be found here: Dissecting shared libraries
Hi Miguel, thanks for that, shame I can’t accept two answers because that complements the above nicely. +1 from me, excellent link too, thanks again!
This answer is so wrong. First, the numbers you see have nothing to do with the API, these are purely ABI. Second, the convention here is not semantic versioning AT ALL like you answer suggest. Rather it’s a libtool convention that has the nice property of mapping to a single library version number that ld.so can compare (see gnu.org/software/libtool/manual/html_node/… for more information)
ldconfig does not update the dev symlink. ldconfig only updates the symlink from libFOO.so.
Shared libraries should be versioned according to the following scheme:
- X = backwards incompatible ABI release
- Y = backwards compatible ABI release
- Z = Internal changes only — no change to the ABI
Typically you only see the first digit like hello.so.1 because the first digit is the only thing needed to identify the «version» of the library since all the other digits are backwards compatible.
ldconfig maintains a table of what shared libraries are available on a system and where the path to that library exists. You can verify this by running:
When a package is built for something like Red Hat, the shared libraries being called out in the binary will be looked up and added as dependencies of the package at RPM build time. Therefore, when you go to install the package, the installer will look up whether or not hello.so.1 is installed on the system by checking ldconfig .
You can see the dependencies of a package by doing something like:
This system (unlike Windows) allows for multiple versions of the hello.so to be installed on a system and be used by different applications at the same time.
Shared libraries should be versioned according to the following scheme (…) – Could you please provide reference for this statement?
can you please give a source for that? I really like the answer, but without credible source I am having a hard time
libNAME.so is the filename used by the compiler/linker when first looking for a library specified by -lNAME. Inside a shared library file is a field called the SONAME. This field is set when the library itself is first linked into a shared object (so) by the build process. This SONAME is actually what a linker stores in an executable depending on that shared object is linked with it. Normally the SONAME is in the form of libNAME.so.MAJOR and is changed anytime the library becomes incompatible with existing executables linked to it and both major versions of the library can be kept installed as needed (though only one will be pointed to for development as libNAME.so) Also, to support easily upgrading between minor versions of a library, libNAME.so.MAJOR is normally a link to a file like libNAME.so.MAJOR.MINOR. A new minor version can be installed and once completed, the link to the old minor version is bumped to point to the new minor version immediately upgrading all new executions to use the upgraded library. Also, see my answer to Linux, GNU GCC, ld, version scripts and the ELF binary format — How does it work?
You must log in to answer this question.
Linked
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.7.13.43531
Linux is a registered trademark of Linus Torvalds. UNIX is a registered trademark of The Open Group.
This site is not affiliated with Linus Torvalds or The Open Group in any way.
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
How are shared libraries really versioned on Linux?
I am getting so much conflicting information about this, both on SE and other tutorials. Most people seem to think the so version is a semantic version. Then others correct them saying it must conform to libtools conventions, and go on to implicitly assume or imply that the -version-info C:R:A numbers passed to libtools create files named libfoo.so.C.R.A . But I don’t see anywhere in the libtools manual that explicitly states how the files are named, and that doesn’t match the behavior that I’m seeing. I’m building a third party package (gdal), and during the build process, it calls libtool —mode=link -version-info 25:4:5 , however the so files left in .libs after the build have version libgdal.20.5.4. I tried with a couple other versions of the same library, and they all seem to follow the same pattern. When you call libtools you pass in current:revision:age, and it generates libfoo.so.current-age.age.revision . This results in an soname of libfoo.so.current-age that is always the minimum version that the library is compatible with, not the maximum as other posts suggest. I did this testing on both RHEL 7 and Debian 10. Is this how things are supposed to work? Is this library doing something weird? Is there any place that documents this authoritatively, or at least correctly?
2 Answers 2
The typical expectation on Linux is that a shared library has a form libfoo.so.N , where N is an integer. It is also possible that it has forms with additional integers, which may have additional meaning depending on what convention you’re using (libtool or otherwise). It is possible to use other forms, and some libraries, such as OpenSSL, do.
Part of a proper shared library on Linux is the ELF entry SONAME , which specifies the shared object name for the binary. For example, with libz:
$ readelf -a /lib/x86_64-linux-gnu/libz.so.1 | grep SONAME 0x000000000000000e (SONAME) Library soname: [libz.so.1]
You see that in this case, the SONAME is libz.so.1 . When your binary links against this shared library, it will have a NEEDED section embedded into it. The dynamic linker, when resolving the shared libraries, will look in the search path for a shared library with a name equivalent to the SONAME. For example, Git requires these four shared libraries:
$ readelf -a /usr/bin/git | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libpcre2-8.so.0] 0x0000000000000001 (NEEDED) Shared library: [libz.so.1] 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
So the thing that practically matters is what’s in the SONAME, which is equivalent to a major version for a semantically versioned piece of code. Making an incompatible change in a shared library without bumping the SONAME (usually to the next integer) value is a grave error that will result in hordes of angry users descending upon your project (and rightfully so).
So it doesn’t really matter for the parts of the shared library that aren’t part of the SONAME. The libtool convention and the semantic versioning convention both make the first integer after the .so portion of the name the version for incompatible changes, which both do the same thing. Since that’s the portion that’s usually in the SONAME, they’re equivalent for this purpose.
I mentioned that some shared libraries do different things. For example, OpenSSL has libssl.so.1.1 and libcrypto.so.1.1 as the shared library SONAMEs it uses. While this is not the recommended convention, it happens to work. They’re changing to semantic versioning in future versions, though, because people find this confusing.
Is it possible to get the version of a shared object?
I’m trying to track down a bug in my software line. I have two release build version 2.0.962 and 2.0.966. The only difference between these versions is a lib.so file. In order to figure out where to start looking in the source tree for lib.so, I need to know its version number in each of the release builds. Is there a command line tool for printing the version of a shared object?
I am not familiar with Android, but in Linux there is a command called readelf which can display information about dynamic libraries. For example, readelf -a library.so
You can integrate some binary artifact checksum generation into your build process. Then you will be able to look up the build version by the library file checksum
I’m not sure why our build tool (TeamCity) doesn’t simply list the versions of artifact dependencies being pulled into the build.
1 Answer 1
I never did native android programming but as Android uses Linux kernel, the executable format used will most likely be «ELF». As far as ELF is concerned, there is no version information of Shared object stored in the file. So unless the file name itself is saying something like «libXXX.VERSION.so» , there is no other way to find out the version number of the shared object. This technique is generally used in Linux.
My suggestions for solving your issue are:
- Use Date modified if possible to differentiate the shared objects
- Use dlopen() or similar to open the shared object and try to see the functions being exported.
Source: http://man7.org/linux/man-pages/man5/elf.5.html Note: E_VERSION in the executable is the version of ELF format, not of executable.
I used option 1 prior to posting the question. It made me sad that I had to resort to that so I asked the question hoping that there was a better way 🙂
The version of an SO is often in the ELF format, as part of SONAME: readelf -a lib.so | grep SONAME . When you link against a library at build time, that SONAME is what gets put into your executable (as an NEEDED section), and is how the linker finds the correct version of the SO to link at runtime. That said, there is no enforced naming/versioning and different libraries use different conventions, so it depends on what tools and options you used when creating your so as to whether the version is included, as well as what granularity (just major, or major & minor, etc)