POSIX equivalent of Windows DLL import libraries
In a nutshell, Implib.so is a simple equivalent of Windows DLL import libraries for POSIX shared libraries.
On Linux/Android, if you link against shared library you normally use -lxyz
compiler option which makes your application depend on libxyz.so
. This would cause libxyz.so
to be forcedly loaded at program startup (and its constructors to be executed) even if you never call any of its functions.
If you instead want to delay loading of libxyz.so
(e.g. its unlikely to be used and you don't want to waste resources on it or slow down startup time or you want to select best platform-specific implementation at runtime), you can remove dependency from LDFLAGS
and issue dlopen
call manually. But this would cause ld
to err because it won't be able to statically resolve symbols which are supposed to come from this shared library. At this point you have only two choices:
ld
via -Wl,-z,nodefs
; this is undesired because you loose ability to detect link errors for other libraries staticallydlsym
and call them via function pointers; this isn't very convenient because you have to keep track which symbols your program uses, properly cast function types and also somehow manage global function pointersImplib.so provides an easy solution - link your program with a wrapper which
Generated wrapper code (often also called "shim" code or "shim" library) is analogous to Windows import libraries which achieve the same functionality for DLLs.
Implib.so can also be used to reduce API provided by existing shared library or rename it's exported symbols. See this page for info on integrating Implib.so into a CMake project.
Implib.so was originally inspired by Stackoverflow question Is there an elegant way to avoid dlsym when using dlopen in C?.
A typical use-case would look like this:
$ implib-gen.py libxyz.so
This will generate code for host platform (presumably x86_64). For other targets do
$ implib-gen.py --target $TARGET libxyz.so
where TARGET
can be any of
Script generates two files: libxyz.so.tramp.S
and libxyz.so.init.c
which need to be linked to your application (instead of -lxyz
):
$ gcc myfile1.c myfile2.c ... libxyz.so.tramp.S libxyz.so.init.c ... -ldl
Note that you need to link against libdl.so. On ARM in case your app is compiled to Thumb code (which e.g. Ubuntu's arm-linux-gnueabihf-gcc
does by default) you'll also need to add -mthumb-interwork
.
Application can then freely call functions from libxyz.so
without linking to it. Library will be loaded (via dlopen
) on first call to any of its functions. If you want to forcedly resolve all symbols (e.g. if you want to avoid delays further on) you can call void libxyz_init_all()
.
Above command would perform a lazy load i.e. load library on first call to one of it's symbols. If you want to load it at startup, run
$ implib-gen.py --no-lazy-load libxyz.so
If you don't want dlopen
to be called automatically and prefer to load library yourself at program startup, run script as
$ implib-gen.py --no-dlopen libxys.so
If you do want to load library via dlopen
but would prefer to call it yourself (e.g. with custom parameters or with modified library name), run script as
$ cat mycallback.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
#endif
// Callback that tries different library names
void *mycallback(const char *lib_name) {
lib_name = lib_name; // Please the compiler
void *h;
h = dlopen("libxyz.so", RTLD_LAZY);
if (h)
return h;
h = dlopen("libxyz-stub.so", RTLD_LAZY);
if (h)
return h;
fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(1);
}
$ implib-gen.py --dlopen-callback=mycallback libxyz.so
(callback must have signature void *(*)(const char *lib_name)
and return handle of loaded library).
Normally symbols are located via dlsym
function but this can be overriden with custom callback
by using --dlsym-callback
(which must have signature
void *(*)(void *handle, const char *sym_name)
).
Finally to force library load and resolution of all symbols, call
void _LIBNAME_tramp_resolve_all(void);
By default the tool does not try to wrap vtables exported from the library. This can be enabled via --vtables
flag:
$ implib-gen.py --vtables ...
Implib.so overhead on a fast path boils down to
This is very similar to normal shlib call:
so it should have equivalent performance.
The tool does not transparently support all features of POSIX shared libraries. In particular
dlopen
and library constructors)The tool also lacks the following important features:
Finally, there are some minor TODOs in code.
As mentioned in introduction import libraries are first class citizens on Windows platform:
Delay-loaded libraries were once present on OSX (via -lazy_lXXX
and -lazy_library
options).
Lazy loading is supported by Solaris shared libraries but was never implemented in Linux. There have been some discussions in libc-alpha but no patches were posted.
Implib.so-like functionality is used in OpenGL loading libraries e.g. GLEW via custom project-specific scripts.