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