2019-10-20
Replacing ELF shared libraries
Update
- 2022-01-30
-
The linker flag
--no-as-needed
was previously omitted, but it is necessary with more recent toolchains (despite what theld
doc says about the default), at least in Debian and Fedora. I can’t find documentation for that, and I don’t remember whether the change in Fedora was distribution-specific.
There are plenty of words here about a rather simple trick(?), but since I
haven’t come across it anywhere else, and have had it described as
‘clever’,In connexion with a failed
attempt to
provide some sanity in RHEL/Fedora linear algebra packaging. This is just a
rehash.
it seems worth recording. TL;DR.‘Too
long; didn’t read’, if you’re not down with the kids.
Suppose you have a well-defined library ABI, say for linear algebra (BLAS), and a canonical implementation of it, say ‘reference’ or ‘Netlib’ BLAS, which is linked into programs you run. Also, for some reason — probably performance — you want to replace that implementation without re-building those programs. You can do so by making use of the dynamic linker facilities. (This is with GNU Binutils, but I assume it will be similar on any ELF-based system.)
To be concrete, we have various computational programs linked against
reference BLAS (libblas.so.3
), which is an order of magnitude slower
than, say, OpenBLASOr
BLIS in the free software world.
ATLAS seems to be dead, and you can definitely ignore it these days.
(libopenblas.so.0
from the Debian or EPEL packages), which we’d like
programs to use transparently. In principle you could just export
LD_PRELOAD=libopenblas.so.0
, but that affects all programs, which at
least isn’t clean.
So just symlink libopenblas.so.0
to libblas.so.3
? That
won’t work because libopenblas.so.0
’s
soname is wrong; it needs to be
libblas.so.3
to be loaded in place of the original. It seems we need
to rebuild OpenBLAS, redefining the library name.For which there is
a hook in its build system.
Debian does that to satisfy its useful policy
of interchangeable BLAS implementations.Actually, the rebuilt
library is a subset of libopenblas
, removing LAPACK and CBLAS.
Having done so, you can put the substitute in LD_LIBRARY_PATH
globally
or, better, do something like (on Debian)Debian actually uses its
alternatives system to do
the job, without affecting the loader’s search paths.
# echo /usr/lib/x86\_64-linux-gnu/openblas-serial \
>/etc/ld.so.conf.d/openblas.conf
Rebuilding like that is a pain, and perhaps we don’t need to, and can even be
more general. Consider a dummy library that dlopen
s another one as
required,Probably using a GCC constructor
function.
possibly depending on the environment. You can do that, but the dummy one
needs to define all the relevant
symbols,FlexiBLAS
does something like that, and is now enshrined in Fedora policy over my
objections from experience using the technique described here in production
on an HPC cluster.
and you might as well rebuild the library you want to
load (in the simple case).
Instead, here’s the trick to build a trivial shim with the correct name to put on the load path to replace a dynamic library with an ABI-compatible alternative:
cc -shared -o lib$n.so.$sover -Wl,-soname,lib$n.so.$sover -Wl,--no-as-needed -l$other
where $n
is the name of the target library (e.g. blas
),
$sover
is the dynamic library version (e.g. 3
), and
$other
is the name of the substitute (e.g. openblas
), assuming
it doesn’t need extra libraries to link. You can check the result with
readelf -d
, looking for SONAME
and NEEDED
, which is
all the meat.With no code, the shim is still > 5000 kB stripped.
There’s a real example in the Fedora
BLIS packaging
(rather than OpenBLAS) which precedes the unfortunate Fedora policy. Of
course, this works with a proprietary library that you can’t
modify,Actually, modulo legalities, you could copy it and use
patchelf --set-soname
,
but there’s some gotcha with that I don’t remember.
if you must.
The technique served well in production on an HPC cluster I used to manage.
It was nearly all provisioned with RPM packaging, and most of the relevant
packages linked reference BLAS. (I’m talking RHEL6.) All those got an
immediate speed boost for linear algebra operations by subverting the BLAS
dynamic libraries with libopenblas
via
ld.so.conf
.There were more actually targets than
libblas
, primarily R’s libRblas
, and also the equivalent
for LAPACK, since OpenBLAS has various optimized LAPACK routines.
There
were also OpenMP parallelized versions which you could use to
boost
R, in particular, by putting them on LD_LIBRARY_PATH
.
Knights
Landing† systems were just appearing — Liverpool had one of the first in
the UK — and there was no AVX512 support in OpenBLAS, but there was in BLIS
after a while. OpenBLAS was generally preferable, but on the rather
heterogeneous cluster, I could set up ld.so.conf to point to specific shims
— openblas or blis — in the configuration for each node type.