Challenges around ABI compatibility

Introduction:  The challenges around ABI compatibility

Ensuring the forward compatibility of application binary interfaces (ABIs) exposed by native shared libraries has been a kind of black art for quite some time, due to many factors.

circles

The scope of the term ABI is quite broad, even when it is restricted to shared software libraries. It encompasses low-level concepts like the binary format, the processor instructions set used in the binary, the calling convention of the operating system on a given processor architecture, as well as higher level considerations like the layout and size of the data types used by the entry points of the library.

Even when considering only one binary format, one processor instruction set, and one calling convention, ensuring that changes in the source code will not impact the ABI of the shared library in an incompatible way is a challenging proposition.

Let's consider the particular use case of programmers regularly releasing new versions of the system shared software library they are working on. They obviously want to ensure that applications built against older versions of the library will continue to work when they use the newer versions of the same library.

Ideally, they want to know of any ABI differences between the two versions of their library in a sufficiently meaningful way such that any incompatible differences can be easily spotted during reviews.

This article is the first in a series of two articles about how we used tools built upon the libabigail project to review the ABI differences of the standard C++ shared library across Red Hat Enterprise Linux versions 6.x and 7.x to the latest release.

The libabigail project is still in its early days but it has already been quite useful in doing this ABI compatibility analysis.

Reviewing ABI differences with abidiff

In the Free Software world, developers generally review source code changes by reading a flat textual representation of the source code change. That representation usually is the output of the GNU diff tool.

Rather than reviewing source code changes, we want to review ABI changes in a way that makes it easy for developers not only to spot the incompatible changes, but also to understand which source code change created a given ABI change.

This is where the abidiff tool comes into play. Given two ELF shared libraries and their debug information, the tool emits a textual representation of their ABI differences.

By analyzing the debug information of the ELF libraries, abidiff constructs a representation of the defined functions and variables they export. That representation obviously includes the types of these global variables and functions. Then abidiff compares the variables and functions exported by the two libraries and emits a textual report about the functions and variables that were removed or added to the first library, as well as functions and variables whose type signature hasn't changed, but might be impacted by a layout, size, or other change in a sub-type.

Getting and Building abidiff

Sources for abidiff can be downloaded from the libabigail git repo, per the instructions at that link. Building instructions can also be found on that page. You may find you need to install the elfutils-devel package prior to building.

Getting the RHEL 6.5 and 7.0 packages for libstdc++

To review the ABI differences for the versions of libstdc++ from Red Hat Enterprise Linux 6.5 and 7, we first need to get the relevant RPM packages from the download section of the Red Hat Customer Portal. For simplicity, we perform the analysis for the x86_64 platform but we could have done it for any other platform for which the RPM packages are available.

These RPM packages are:

If you do not have access to the Red Hat Customer Portal you can get close enough RPM packages from the CentOS project, for instance, and similar libraries can be found in the Fedora Linux Distribution. When we say 'close enough', it's only for the pedagogic purpose of this article. CentOS binaries might otherwise differ from Red Hat Enterprise Linux binaries in subtle ways - and most notably are not backed up by Red Hat's enterprise support services. Anyway, if you want the CentOS binaries, or similar Fedora binaries, the hyperlinks on the package list above points to places from which you can download them. The rest of this article will assume you are using the RHEL binaries.

For the sake of precision, let's assume that these packages are dropped into the /tmp/rpms directory on your local system. Let's also assume that you have cloned the source code of Libabigail into /tmp/libabigail, that you have compiled it and the resulting binaries are in /tmp/libabigail/build.

We are going to extract the content of RHEL 6.5 RPM packages into /tmp/extracted/6.5 and RHEL 7 packages into /tmp/extracted/7. Here are the command-line instructions to type in your favorite terminal:

mkdir -p /tmp/extracted/6.5 && cd /tmp/extracted/6.5
rpm2cpio /tmp/rpms/libstdc++-4.4.7-4.el6.x86_64.rpm | cpio -id
rpm2cpio /tmp/rpms/gcc-debuginfo-4.4.7-4.el6.x86_64.rpm | cpio -id
mkdir /tmp/extracted/7 && cd /tmp/extracted/7
rpm2cpio /tmp/rpms/libstdc++-4.8.2-16.el7.x86_64.rpm | cpio -id
rpm2cpio /tmp/rpms/gcc-base-debuginfo-4.8.2-16.el7.x86_64.rpm | cpio -id

Note that the exact .rpm names should match those you downloaded earlier. Now we should have the two libstdc++ libraries to compare at /tmp/extracted/6.5/usr/lib64/libstdc++.so.6 and /tmp/extracted/7/usr/lib64/libstdc++.so.6. Their respective debug information files are available in /tmp/extracted/6.5/usr/lib/debug and /tmp/extracted/7/usr/lib/debug.

Running abidiff on libstdc++ binaries

As input, abidiff mainly needs the two libraries to compare. In this particular case, as the libraries have their debug information split into separate files, and as these files are located at non-standard places, we need to tell the tool where to find them as well.

First let's create a directory to store the resulting report of abidiff:

mkdir /tmp/abidiff-results

The abidiff command-line to perform the comparison is thus:

/tmp/libabigail/build/tools/abidiff --debug-info-dir1 /tmp/extracted/6.5/usr/lib/debug --debug-info-dir2 /tmp/extracted/7/usr/lib/debug /tmp/extracted/6.5/usr/lib64/libstdc++.so.6 /tmp/extracted/7/usr/lib64/libstdc++.so.6 > /tmp/abidiff-results/libstdc++.so.abidiff

Note that the path to abidiff may differ, depending on the path to which you installed Libabigail at build time.

At this point, abidiff has emitted an ABI changes report in the text file located at /tmp/abidiff-results/libstdc++.so.abidiff.  We will analyze that output in the next article.  Stay tuned.

Last updated: February 7, 2024