by jchw | 2026-01-23 23:11:14+00:00
I think this is just solving a different but related problem. Symbol versioning enables you to make observable changes to the behavior of some library like a libc while providing backwards compatibility by providing the old behaviors indefinitely. I've never gotten the impression that it is "prescribed" that shared objects on a Linux system don't have a specific/definite version, and I don't think that symbol versioning is necessarily appropriate for all programs, either-it just happens to be a mechanism used by glibc, and I don't think it is very common outside of glibc (could be wrong.)

On the other hand, the Windows NE/PE VERSIONINFO resource is mostly informational in nature. I think early on in the life of Windows, it was often used to determine whether or not the system library shipped by an installer was newer than the installed version, so that program installers could safely update the system libraries when necessary without accidentally overwriting new versions of things with older versions. That's just simply a problem that ELF shared objects don't try to solve, I reckon, because there aren't usually canonical binaries to install anyways, and the problem of distributing system libraries is usually left up to the OS vendor.

Actually though, there is another mechanism that Linux shared objects often use for versioning and ABI compatibility: the filename/soname. Where you might have a system library, libwacom.so.9.0.0, but then you also have symlinks to it from libwacom.so.9 and libwacom.so, and the soname is set to libwacom.so.9.

There is, of course, no specific reason why ELF binaries don't contain metadata, and you could certainly extend ELF to contain PE-style resources if you want. (I remember some attempts at this existed when I was still getting into Linux.) It may be considered "bad practice" to check the runtime version of a library or program before doing something, in many cases, but it is still relatively common practice; even though there's no standard way to do it, a lot of shared objects do export a library function that returns the runtime version number, like libpng's png_access_version_number. Since distributions patch libraries and maybe even use drop-in replacements, there is obviously no guarantee as to what this version number entails, but often it does entail a promise that the ABI should be compatible with the version that is returned. (There is obviously not necessarily a canonical "ABI" either, but in most cases if you are dealing with two completely incompatible system/compiler ABIs there isn't much that can be done anyways, so it's probably OK to ignore this case.)

So I think the most "standard" way to embed the "version" of a shared object on Linux is to export a symbol that either returns the version number or points to it, but there is no direct equivalent to the PE VERSIONINFO standard, which may be a pain when the version number would be useful for something, but the developers did not think to explicitly add it to the Linux port of their software, since it is often not used by the software itself.

But I personally wouldn't agree with the conclusion that "shared objects on GNU/Linux do not have a finite version" as a matter of course; I think the more conservative thing to say is that there is not necessarily a definite version, and there is not necessarily a "canonical" binary. In practice, though, if your copy of libSDL is an almost-unmodified build of the source tree at a given version 3.0.0, it is entirely reasonable to say that it is "definitely" libSDL 3.0.0, even though it may not refer to an exact byte-for-byte source tree or binary file.