libtool: Linking with dlopened modules
1
1 10.3 Linking with dlopened modules
1 ==================================
1
1 When, say, an interpreter application uses dlopened modules to extend
1 the list of methods it provides, an obvious abstraction for the
1 maintainers of the interpreter is to have all methods (including the
1 built in ones supplied with the interpreter) accessed through dlopen.
1 For one thing, the dlopening functionality will be tested even during
1 routine invocations. For another, only one subsystem has to be written
1 for getting methods into the interpreter.
1
1 The downside of this abstraction is, of course, that environments
1 that provide only static linkage can't even load the intrinsic
1 interpreter methods. Not so! We can statically link those methods by
1 *dlpreopening* them.
1
1 Unfortunately, since platforms such as AIX and cygwin require that
1 all library symbols must be resolved at compile time, the interpreter
1 maintainers will need to provide a library to both its own dlpreopened
1 modules, and third-party modules loaded by dlopen. In itself, that is
1 not so bad, except that the interpreter too must provide those same
1 symbols otherwise it will be impossible to resolve all the symbols
1 required by the modules as they are loaded. Things are even worse if
1 the code that loads the modules for the interpreter is itself in a
1 library - and that is usually the case for any non-trivial application.
1 Modern platforms take care of this by automatically loading all of a
1 module's dependency libraries as the module is loaded (libltdl can do
1 this even on platforms that can't do it by themselves). In the end,
1 this leads to problems with duplicated symbols and prevents modules from
1 loading, and prevents the application from compiling when modules are
1 preloaded.
1
1 ,-------------. ,------------------. ,-----------------.
1 | Interpreter |----> Module------------> Third-party |
1 `-------------' | Loader | |Dlopened Modules |
1 | | | `-----------------'
1 |,-------v--------.| |
1 || Dlpreopened || |
1 || Modules || |
1 |`----------------'| |
1 | | | |
1 |,-------v--------.| ,--------v--------.
1 ||Module Interface|| |Module Interface |
1 || Library || | Library |
1 |`----------------'| `-----------------'
1 `------------------'
1
1 Libtool has the concept of "weak library interfaces" to circumvent
1 this problem. Recall that the code that dlopens method-provider modules
1 for the interpreter application resides in a library: All of the modules
1 and the dlopener library itself should be linked against the common
1 library that resolves the module symbols at compile time. To guard
1 against duplicate symbol definitions, and for dlpreopened modules to
1 work at all in this scenario, the dlopener library must declare that it
1 provides a weak library interface to the common symbols in the library
1 it shares with the modules. That way, when 'libtool' links the *Module
1 Loader* library with some *Dlpreopened Modules* that were in turn linked
1 against the *Module Interface Library*, it knows that the *Module
1 Loader* provides an already loaded *Module Interface Library* to resolve
1 symbols for the *Dlpreopened Modules*, and doesn't ask the compiler
1 driver to link an identical *Module Interface Library* dependency
1 library too.
1
1 In conjunction with Automake, the 'Makefile.am' for the *Module
1 Loader* might look like this:
1
1 lib_LTLIBRARIES = libinterface.la libloader.la
1
1 libinterface_la_SOURCES = interface.c interface.h
1 libinterface_la_LDFLAGS = -version-info 3:2:1
1
1 libloader_la_SOURCES = loader.c
1 libloader_la_LDFLAGS = -weak libinterface.la \
1 -version-info 3:2:1 \
1 -dlpreopen ../modules/intrinsics.la
1 libloader_la_LIBADD = $(libinterface_la_OBJECTS)
1
1 And the 'Makefile.am' for the 'intrinsics.la' module in a sibling
1 'modules' directory might look like this:
1
1 AM_CPPFLAGS = -I$(srcdir)/../libloader
1 AM_LDFLAGS = -no-undefined -module -avoid-version \
1 -export-dynamic
1
1 noinst_LTLIBRARIES = intrinsics.la
1
1 intrinsics_la_LIBADD = ../libloader/libinterface.la
1
1 ../libloader/libinterface.la:
1 cd ../libloader && $(MAKE) $(AM_MAKEFLAGS) libinterface.la
1
1 For a more complex example, see the sources of 'libltdl' in the
1 Libtool distribution, which is built with the help of the '-weak'
1 option.
1