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

时间:2017-03-02 19:53:28

标签: c++ plugins shared-libraries ubuntu-16.04 dlopen

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

示例插件:

  • .so
  • /home/whatever/super/magic.so
  • /home/whatever/super/wow.so

我已设置/home/whatever/super/awesome.so

LD_LIBRARY_PATH

在Super中,我使用export LD_LIBRARY_PATH=/home/whatever/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的子目录中搜索.so文件?我显然不想将/usr/lib/x86_64-linux-gnu文件的完整路径硬编码到我的C ++代码中。

最后,这是一个很好的方法,可以放置插件和加载方法吗?

2 个答案:

答案 0 :(得分:2)

我担心Glibc不会处理dlopen中的相对文件路径(以及其他功能)。 Here你可以看到任何斜杠导致它将文件名视为绝对值,而不是在标准路径中搜索它。)

我认为最好的解决方案是将绝对插件路径传递给dlopen。可能还要求Glibc维护人员更好地诊断此类错误。

答案 1 :(得分:1)

看起来常见的方法是对插件目录(或插件.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的别名。< / p>

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