There is no day so wasted that you can’t take notes about what didn’t work, so here’s some talk about ABI-compliance-checking. ABI-compliance, or ABI-compatibility, is roughly when a shared library can be changed (to a different version, usually an update and upgrade) and users of that shared library (applications, or other libraries) just work with the new version. This requires some discipline, and there are tools to help out.
“Wasted” is, in the end, relative: there’s this blog entry, and I have some new tooling to build now that I know what works and what does not.
KDE libraries – kdelibs in KDE4 times, and now KDE Frameworks – have pretty stringent ABI requirements. Frameworks update every month, so we need to be sure that consumers don’t need to be recompiled with the new version all the time. There is some fairly extensive documentation with do’s and don’ts.
One way to help maintain binary compatibility is to use tools that check the ABI: figure out the shape of the ABI in one version, the shape in another version, and compare those shapes. KDE Frameworks have checks in place, like this one (that link assumes openSUSE and Qt 5.15 are still in use and that there was a recent successful build).
Generally, an ABI-shape getting bigger is not a problem (from a technical perspective, although you can have all kinds of semantic mix-ups). Things that go away – functions, variables, etc. – those are problematic.
Calamares is a Linux system installer – it can be customized by Linux distro’s to act as the installer for their ISO images. It’s a C++ program offering modules for all kinds of system-installation services. It also offers an ABI: the modules use the ABI of the Calamares libraries to talk to the main program. Calamares supports “third-party” modules, e.g. modules specific to one distro or otherwise customized, and for those third-party modules, ABI compatibility suddenly becomes an issue: it would be nice if they didn’t have to be recompiled when a new Calamares library comes out. That can only happen if the Calamares libraries commit to ABI compatibility.
Traditionally, Calamares has had zero ABI compatibility. That comes down to me not having that discipline, and not having (er .. maybe “not knowing about”) useful tools. But Harald was nagging me about it, and I realise it would be useful for friends at Linux distro’s that build their own modules.
Library updates example
Consider the source code for this library:
// Library version 1 int library_version = 1;
Assume there’s a header that says
extern int library_version;, etc. etc.
In any case, this library contains at least one exported symbol,
library_version. We can build an executable against this shared
library and run it:
adridg:build$ ./hello Hello, library_version=1
We can update the library and build the library, then without changing
the executable, update the library by replacing the old
adridg:build$ cp libabi2.so libabi.so adridg:build$ ./hello Hello, library_version=2
Lovely! Compatible symbols all around. But the code looks Pythonic, rather than Qt, so we’ll release a newer version with a different coding style:
// Library version 3 int libraryVersion = 3;
That is in no way, shape, or form, a drop-in replacement: applications
that link to the symbol
library_version will fail to run. Just copying
over the library leads to a run-time-linker error like this one:
adridg:build$ cp libabi3.so libabi.so adridg:build$ ./hello ./hello: symbol lookup error: ./hello: undefined symbol: library_version
This is the most egregious form of breaking-ABI, and an ABI-checking tool should spot that from a mile off.
The first tool I looked at was ABI compliance checker. It’s actually the tool behind the KDE Frameworks check mentioned earlier. There is a description with a lovely monkey as mascot here from 2010 – so the tool has been around in various incarnations for a long time.
This tool is packaged for FreeBSD, but in a very very old version, so I have a TODO item of “Update the port”.
There is a companion tool,
abi-dumper, to be used with
The documentation states that using the dumper is faster, more reliable, and simpler.
That documentation chewed up a day and half of my time, and simply isn’t correct (edit: it is correct, but you need up-to-date versions of the tooling, see below).
abi-dumper will produce suitable perl-variables
that describe the library, including the symbols that are
defined by the library. The tool
abi-dumper also has a command-line-argument
-compare to do just that: compare two dumps.
Applied to a dump of versions 1 and 3 of the egregious library example,
abi-dumper can tell me that the libraries are different: it says I removed
library_version and added
libraryVersion which is true!
abi-compliance-checker with the resulting
dumps says that the libraries are 100% compatible, forward and backward.
This is a useless tool.
Since the KDE Frameworks use the tool I’ve just callously and carelessly labeled “useless”, and those reports don’t look completely useless, there must be something more to it. Having spent half a day looking at different versions of the tool, checking if there is more documentation .. nope. It turns out that the KDE tooling uses the “old” way of invoking the tool, with an “XML” file describing library- and header-locations. This invocation of the tool does some compiling of its own.
Running this version of the tool shows me 0% compatibility.
Yay! So the tool can work if you ignore some of the documentation
that tells you to use the newer, faster, simpler way (edit: or get a recent
Applying this older invocation to Calamares actually produces some
nice reports, useful ones, too for figuring out when I can
start committing to binary compatibility (e.g. if there’s even compatibility
Passive-agressive dickery via a blog (like this one) is not going to solve anything, so I spent some time putting together an extensive example, with a test script, so I could write a proper bug report upstream (in GitHub).
While doing that I spotted a fairly recent
version 1.2, with release notes “Fedora 30 compatibility ; misc fixes”.
On a whim I copied the git version of that tool over top of
the version installed in my Debian machine.
And that fixed the “useless tool” part as well. I am duly chastened for using a stable Debian version, although I Have Opinions about release notes and release frequencies. Also note that the newer version hasn’t reached Ubuntu, so if I’d been on any of my other likely testing platforms I would have hit exactly the same problem.
I knew I should have checked repology first, before using any tools at all. So I closed my upstream bug report and commented on some open issues that could probably be fixed with an update, too.
Feed the tool two
.so files and it spits out a report and a useful
exit code. The report is plain text, not HTML so it’s a bit unstructured,
but certainly readable. It seems to be much faster than
as well (probably because it is reading the ELF files directly, rather than
doing a round-trip through external tools).
The abigail tools have extensive documentation. That’s a big plus.
Where does this leave Calamares
This leaves Calamares in a bit of an awkward spot, actually. The HTML output from the one tool is nice to read. The other tool has a better getting-started experience.
There may be another qualitative issue: abigail tooling looks only at the symbols, while abi-compliance-checker in its “old style” invocation takes headers into account, which presumably means that it can take what-does-the-header-promise into account as well. For “new style” (with the new
abi-dumper) I don’t think there’s a difference.
For my CI pipeline I think I’ll end up with abigail,
simply because it has a version available on my CI host that works.
It may not pick up subtle changes, but will at least tell
me quickly when I’ve removed the constructor for
Calamares from the lib.