[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