[systemd-devel] Using filter objects instead of dlopen()?
Ludwig Nussel
ludwig.nussel at suse.de
Mon Dec 19 15:58:28 UTC 2022
Hi,
In order to avoid hard requiring certain optional libraries, systemd
started to use dlopen(). From the top of my head dlopen has several
disadvantages, eg
- need to duplicate function prototypes in your own code
- need to have (macro)code to load all symbols you need
- you hardly notice when the library changes, both API and ABI wise
- it's easy to make mistakes when the library uses symbol versioning
- tools like ldd don't show what libs you may use
So while dlopen is fine to load plugins with an interface you define
yourself, it's not all that great to load system libraries IMO.
There's a rather under-documented ld feature that may be useful instead:
filter objects using DT_AUXILIARY¹. The idea of that is to have shared
libraries that define names of other shared libraries that can overload
the original symbols with alternative implementations. ld does the
linking magic for you, no code changes needed.
That also works the other way around. So you could write a stub library
that would specify the original library as alternative implementation.
If the original is there it would be used, otherwise your stubs would be
called. As long as you don't actually call the stubs they do not need to
have a meaningful prototype nor content. So that works best for
libraries that have some init function that can return error. Then you'd
just have to reimplement the init function in the stub lib, use a
boilerplate for the rest and make sure no other symbol is used in case
of initialization error.
Somehow I figured that stuff out ages ago in a toy project. There's a
script to generate stub functions that just segfault when called as well
as a linker map given the path to a shared library:
https://github.com/XQF/xqf/blob/0c68cf75fc65eabdfa3e44ff45696f1ba56284fd/src/gensyms.pl
The init functions need reimplementation but one can include the
original header of course to make sure the prototype is correct:
https://github.com/XQF/xqf/blob/0c68cf75fc65eabdfa3e44ff45696f1ba56284fd/src/libxqf_dummy_GeoIP_stubs.c
In that case the main program would only link against
libxqf_dummy_GeoIP.so.0 (probably in a private location).
That dummy library is produced using something like
-nostdlib -shared -Wl,-f,libGeoIP.so.0 -Wl,-soname,libxqf_dummy_GeoIP.so.0
So DT_AUXILIARY is set to the soname of the original lib as determined
during build.
What I don't know is whether DT_AUXILIARY is just some exotic misfeature
or actually considered a good idea. AFAIK that stuff worked fine in XQF
back then. YMMV, just wanted to mention it as it that feature is not
widely known. Maybe someone is curious enough to give it a try. Or find
arguments why dlopen is still the lesser evil for the sake of
documenting that for the future :-)
cu
Ludwig
[1]
https://manpages.opensuse.org/Tumbleweed/binutils/ld.1.en.html#auxiliary=
--
(o_ Ludwig Nussel
//\
V_/_ http://www.suse.com/
SUSE Software Solutions Germany GmbH, GF: Ivo Totev
HRB 36809 (AG Nürnberg)
More information about the systemd-devel
mailing list