使用 dlopen() 引用共享库插件的正确方法是什么?
What is the proper way to reference shared library plugins with dlopen()?
我有一个项目,在这个假设的示例中称为 Super,其中包含一组 .so
个内置到 /home/whatever/super/
中的文件。在运行时,规范配置文件告诉 Super 使用它们的 .so
名称加载哪些插件。这是在 Ubuntu 16.04.
示例插件:
/home/whatever/super/magic.so
/home/whatever/super/wow.so
/home/whatever/super/awesome.so
我设置了LD_LIBRARY_PATH
:
export LD_LIBRARY_PATH=/home/whatever/super
在 Super 中,我使用 dlopen()
:
加载模块
std::string filename = "magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
至此,一切正常。现在我正在尝试打包我的项目,这意味着将东西移动到正确的系统目录。我现在已经切换到使用 /usr/lib/x86_64-linux-gnu/super/
作为插件的基本路径,像这样:
/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
我也通关了LD_LIBRARY_PATH
。我将 dlopen()
代码更新为如下所示:
std::string filename = "super/magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
不幸的是,系统不会加载我的模块。我收到此错误:
Cannot load library: super/magic.so: cannot open shared object file: No such file or directory
我确认 /etc/ld.so.conf.d/x86_64-linux-gnu.conf
存在并包含:
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
我做错了什么?我已确认 /usr/lib/x86_64-linux-gnu/super/magic.so
存在。为什么加载程序不在 /usr/lib/x86_64-linux-gnu
的子目录中搜索 .so
文件?我显然不想将 .so
文件的完整路径硬编码到我的 C++ 代码中。
最后,对于放置插件的位置和加载方法,这是一个好的方法吗?
恐怕 Glibc 不会处理 dlopen
中的相对文件路径(可能还有其他函数)。 Here 你可以看到 any 斜杠导致它将文件名视为绝对文件名,这不会在标准路径中搜索它)。
我认为最好的解决方案是将绝对插件路径传递给 dlopen
。也可能要求 Glibc 维护者更好地诊断此类错误。
看起来常见的方法是对插件目录(或插件 .so
文件本身)的完整路径进行编码,然后在编译时或运行时存储该路径,而不是依赖于LD组件查找子目录。
编译时
您可以使用CMake进程来确定插件模块的安装目录,并在编译之前将该目录写入文件,如Remmina:
config.h.in
内的行:
#define REMMINA_PLUGINDIR "${REMMINA_PLUGINDIR}"
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)
运行时间
对于运行时解决方案,CMake 可以将安装路径写入与软件一起安装的配置文件中。例如:
super.conf.in
内的行:
PLUGIN_PATH "@SUPER_PLUGINDIR@"
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}
)
在我的 Ubuntu 16.04 系统上,CMAKE_INSTALL_LIBDIR
是 lib/x86_64-linux-gnu
的别名,CMAKE_INSTALL_FULL_LIBDIR
是 /usr/lib/x86_64-linux-gnu
的别名在系统根目录下。
无论哪种方式,您都可以使用 dlopen
构建一个不需要 LD 搜索的完整路径。只需将配置的字符串连接到插件文件名即可。两种 CMake 变量替换方法并不重要。为了完整起见,我只是加入了 ${}
和 @@
替代方案。
我有一个项目,在这个假设的示例中称为 Super,其中包含一组 .so
个内置到 /home/whatever/super/
中的文件。在运行时,规范配置文件告诉 Super 使用它们的 .so
名称加载哪些插件。这是在 Ubuntu 16.04.
示例插件:
/home/whatever/super/magic.so
/home/whatever/super/wow.so
/home/whatever/super/awesome.so
我设置了LD_LIBRARY_PATH
:
export LD_LIBRARY_PATH=/home/whatever/super
在 Super 中,我使用 dlopen()
:
std::string filename = "magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
至此,一切正常。现在我正在尝试打包我的项目,这意味着将东西移动到正确的系统目录。我现在已经切换到使用 /usr/lib/x86_64-linux-gnu/super/
作为插件的基本路径,像这样:
/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
我也通关了LD_LIBRARY_PATH
。我将 dlopen()
代码更新为如下所示:
std::string filename = "super/magic.so"
dlopen(filename.c_str(), RTLD_LAZY)
不幸的是,系统不会加载我的模块。我收到此错误:
Cannot load library: super/magic.so: cannot open shared object file: No such file or directory
我确认 /etc/ld.so.conf.d/x86_64-linux-gnu.conf
存在并包含:
# Multiarch support
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
我做错了什么?我已确认 /usr/lib/x86_64-linux-gnu/super/magic.so
存在。为什么加载程序不在 /usr/lib/x86_64-linux-gnu
的子目录中搜索 .so
文件?我显然不想将 .so
文件的完整路径硬编码到我的 C++ 代码中。
最后,对于放置插件的位置和加载方法,这是一个好的方法吗?
恐怕 Glibc 不会处理 dlopen
中的相对文件路径(可能还有其他函数)。 Here 你可以看到 any 斜杠导致它将文件名视为绝对文件名,这不会在标准路径中搜索它)。
我认为最好的解决方案是将绝对插件路径传递给 dlopen
。也可能要求 Glibc 维护者更好地诊断此类错误。
看起来常见的方法是对插件目录(或插件 .so
文件本身)的完整路径进行编码,然后在编译时或运行时存储该路径,而不是依赖于LD组件查找子目录。
编译时
您可以使用CMake进程来确定插件模块的安装目录,并在编译之前将该目录写入文件,如Remmina:
config.h.in
内的行:
#define REMMINA_PLUGINDIR "${REMMINA_PLUGINDIR}"
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)
运行时间
对于运行时解决方案,CMake 可以将安装路径写入与软件一起安装的配置文件中。例如:
super.conf.in
内的行:
PLUGIN_PATH "@SUPER_PLUGINDIR@"
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}
)
在我的 Ubuntu 16.04 系统上,CMAKE_INSTALL_LIBDIR
是 lib/x86_64-linux-gnu
的别名,CMAKE_INSTALL_FULL_LIBDIR
是 /usr/lib/x86_64-linux-gnu
的别名在系统根目录下。
无论哪种方式,您都可以使用 dlopen
构建一个不需要 LD 搜索的完整路径。只需将配置的字符串连接到插件文件名即可。两种 CMake 变量替换方法并不重要。为了完整起见,我只是加入了 ${}
和 @@
替代方案。