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

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

本文关键字:方法 是什么 插件 dlopen 引用 共享 使用      更新时间:2023-10-16

我有一个项目,在这个假设的例子中叫做Super,其中包含一组内置于/home/whatever/super/中的.so文件。在运行时,规范配置文件会告诉 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中的相对文件路径(可能还有其他函数)。在这里你可以看到任何斜杠会导致它把文件名视为绝对的,这不会在标准路径中搜索它)。

我认为最好的解决方案是将绝对插件路径传递给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的别名。

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