I've got a project, called Super in this hypothetical example, with a set of .so
files that are built into /home/whatever/super/
. At runtime, a specification configuration file tells Super which plugins to load using their .so
name. This is on Ubuntu 16.04.
Example plugins:
/home/whatever/super/magic.so
/home/whatever/super/wow.so
/home/whatever/super/awesome.so
I've set LD_LIBRARY_PATH
:
export LD_LIBRARY_PATH=/home/whatever/super
Within Super, I load the modules using dlopen()
:
std::string filename = "magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
At this point, everything works. Now I'm trying to package my project and that means moving things to the correct system directories. I've now switched to using /usr/lib/x86_64-linux-gnu/super/
as the base path of the plugins, like so:
/usr/lib/x86_64-linux-gnu/super/magic.so
/usr/lib/x86_64-linux-gnu/super/wow.so
/usr/lib/x86_64-linux-gnu/super/awesome.so
I've also cleared LD_LIBRARY_PATH
. I updated the dlopen()
code to look like this:
std::string filename = "super/magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
Unfortunately, the system won't load my module. I get this error:
Cannot load library: super/magic.so: cannot open shared object file: No such file or directory
I confirmed that /etc/ld.so.conf.d/x86_64-linux-gnu.conf
exists and contains:
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
What am I doing wrong? I've confirmed that /usr/lib/x86_64-linux-gnu/super/magic.so
exists. Why is the loader not searching for the .so
file in that subdirectory of /usr/lib/x86_64-linux-gnu
? I obviously don't want to hard-code the full paths of the .so
files into my C++ code.
Finally, is this a good approach for where to put the plugins and the loading approach?
It looks like the common approach is to encode the full paths to the plugin directory (or the plugin .so
files themselves), then store that path either at compile-time or runtime, rather than relying on the LD components to find the subdirectory.
You can use the CMake process to determine the installation directory of the plugin modules, and write that directory into a file prior to compilation, as in Remmina:
Line within config.h.in
:
#define REMMINA_PLUGINDIR "${REMMINA_PLUGINDIR}"
Lines within CMakeLists.txt
:
if(NOT REMMINA_PLUGINDIR)
set(REMMINA_PLUGINDIR "${CMAKE_INSTALL_FULL_LIBDIR}/remmina/plugins")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h)
For a runtime solution, CMake can write the installation path into a config file that is installed along with the software. For example:
Line within super.conf.in
:
PLUGIN_PATH "@SUPER_PLUGINDIR@"
Lines within CMakeLists.txt
:
if(NOT SUPER_PLUGINDIR)
set(SUPER_PLUGINDIR "${CMAKE_INSTALL_FULL_LIBDIR}/super")
endif()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/super.conf.in
${CMAKE_CURRENT_BINARY_DIR}/super.conf
@ONLY)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/super.conf
DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
On my Ubuntu 16.04 system, CMAKE_INSTALL_LIBDIR
is an alias for lib/x86_64-linux-gnu
and CMAKE_INSTALL_FULL_LIBDIR
is an alias for /usr/lib/x86_64-linux-gnu
when creating a package that installs at the system root.
Either way, you can then build a full path with dlopen
that doesn't require an LD search. Just concatenate the configured string to the plugin filename. The two CMake variable substitution approaches are not critical. I just threw in the ${}
and @@
alternatives for completeness.