How the Guix FHS binary compatibility service works

Monday, 24 June 2019

This post attempts to explain how my Guix FHS compatibility service works. (Link to service/Link to channel/Link to example system configuration with the FHS service added)


The FHS (Filesystem Hierarchy Standard) binary compatibility service is a service I wrote for Guix that adds support for running portable binaries typically downloaded from a website, including AppImages.

How portable binaries are compiled

Binaries are compiled against a system that assumes all the libs are in FHS-specific directories in the root directory, such as /share, /bin, /lib.

The binary is compiled with a specified path to the glibc interpreter, which is assumed to be in [the prefix of glibc, which is /, so the interpreter is assumed to be in] an FHS-specific directory - /lib or /lib64. The interpreter then looks for the shared libraries that the binary needs - It's not the binary that looks for the shared libraries, it's glibc that looks for them.

glibc

By default, glibc looks for these libraries in "<glibc's compilation prefix>/lib" - On an FHS distribution, the prefix is "/", so it looks for libraries in /lib. On Guix however, the prefix is /gnu/store/...-glibc-<version>, so it will by default look for libraries in /gnu/store/...-glibc-<version>/lib - you can see this by running a binary with strace -o <log-output>, it will be looking for shared libraries in this directory.

Since we can't compile glibc on guix with the prefix as "/" (I assume not, it probably is possible, but maybe it causes other problems, also is probably harder to maintain), and we don't want to put all the shared libraries for the binary into glibc's store path (That would require recomiling glibc each time we want to add or remove libraries), we use a handy feature provided by glibc upstream that tells glibc additional paths to look for shared libraries in: ldconfig.

However, Glibc in guix is built without ldconfig support (a snippet in the package definition for glibc patches a configuration file in the source, that configures glibc to build without ldconfig support). This is because on foreign distributions, there may be an ldconfig cache file that tells glibc which directories to look for additional shared libraries in, by default at /etc/ld.so.cache.

So we need to provide the glibc interpreter where fhs-built binaries expect to find it, and it needs to be built with ldconfig support and we need to provide it with an ldconfig cache file that tells it where to find our guix-packaged shared libraries.

The solution

Build glibc with the snippet removed that disables ldconfig, place a link from /lib64/ld-linux-x86-64.so.2 to <glibc package>/lib/ld-linux-x86-64.so.2 (to provide the FHS-built binaries the glibc interpreter where they expect to find it), and place an ldconfig cache file - created using a list of all our desired libraries to provide the FHS-built binaries - at /etc/ld.so.cache (The default place glibc expects to find it).

Implementing the solution

Using the special-files-service.

Additional profile packages and additional-special-files

These fields don't function any differently to the services they extend, they just make the system configuration better organised, as it makes it clear to the administrator why these special-files and system packages have been added.

Electron/chromium binaries require certain fonts available, or they fail catastrphically (Why this non-graceful-failure has been overlooked is beyond me), so additional packages to add to the system profile can be provided in the fhs-support service. This field is just to keep the system configuration organised, so admins know why these packages have been added to the system profile.

Qt applications have a hardcoded xkb path, which if not found cause keyboard input to fail.