Sharing shared libraries on OS X

Recently at work I encountered a problem with dylibs (shared libraries) with our OS X application. Our application is based on a cross-platform modular framework that runs on four different platforms, and the modular plugin architecture makes it easy to include new features into our programs.

The plugins are compiled as a basic shared library, with the INSTALLDIR set to "@executable_path/libmodulename.dylib". The OS X application looks in the application bundle for plugins, and loads them when needed. Some of the dylibs are not "plugins" per se, but are libraries the plugins are linked against.

Further, we expose some of the module APIs through a SWIG-generated Perl interface, which also must dynamically load these libraries. The Perl support files live inside /usr/local.

The problem was that after copying the original dylibs into the Perl directory, the Perl module fails to load. Even though the dylibs are located in the same directory, the OS X dynamic loader relies on the exported INSTALLDIR for locating libraries. In this case, @executable_path is interpreted at runtime as a nonexistent Perl executable bundle, which is not what we need. This needs to change.

I found a blog entry describing a related issue, as well as a useful tool to fix dylibs' paths. For our Perl support, we make separate copies of the dylibs, then adjust the reference path as well as install paths for each dylib with install_name_tool(1). The dylibs are going into /usr/local/libexec/thingy/libthingy.dylib, so the install_name_tool command is:

install_name_tool -change "@executable_path/libthingy.dylib" /usr/local/libexec/thingy/libthingy.dylib *.dylib

To ensure all inter-linking references are fixed, for libraries A, B and C you must invoke install_name_tool for each combination (including fixing the path of A on A itself).

These changes were simple to make to our build system, did not require a recompile (although that is the other option), and solved the Perl module loading issue.