使用 dlopen() 引用共享库插件的正确方法是什么?

What is the proper way to reference shared library plugins with dlopen()?

我有一个项目,在这个假设的示例中称为 Super,其中包含一组 .so 个内置到 /home/whatever/super/ 中的文件。在运行时,规范配置文件告诉 Super 使用它们的 .so 名称加载哪些插件。这是在 Ubuntu 16.04.

示例插件:

我设置了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/ 作为插件的基本路径,像这样:

我也通关了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_LIBDIRlib/x86_64-linux-gnu 的别名,CMAKE_INSTALL_FULL_LIBDIR/usr/lib/x86_64-linux-gnu 的别名在系统根目录下。

无论哪种方式,您都可以使用 dlopen 构建一个不需要 LD 搜索的完整路径。只需将配置的字符串连接到插件文件名即可。两种 CMake 变量替换方法并不重要。为了完整起见,我只是加入了 ${}@@ 替代方案。