【问题标题】:What is the proper way to reference shared library plugins with dlopen()?使用 dlopen() 引用共享库插件的正确方法是什么?
【发布时间】:2017-07-22 17:26:48
【问题描述】:

我有一个项目,在这个假设的示例中称为 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++ 代码中。

最后,这是放置插件和加载方法的好方法吗?

【问题讨论】:

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


    【解决方案1】:

    恐怕 Glibc 不会处理 dlopen 中的相对文件路径(可能还有其他函数)。 Here 你可以看到 any 斜杠会导致它将文件名视为绝对文件名,而不是在标准路径中搜索)。

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

    【讨论】:

    • 是的,我也得出了这个结论。对正确的插件方法有任何见解吗?我看到几个官方包将他们的插件放入共享库文件夹的子目录中。
    • @EthanT 您可以使用链接器的-rpath 选项指向插件目录。此外,如果此目录是相对于主可执行文件,您可以使用$ORIGIN 指定相对路径。
    【解决方案2】:

    看起来常见的方法是编码插件目录的完整路径(或插件.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 变量替换方法并不重要。为了完整起见,我只是添加了 ${}@@ 替代方案。

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 1970-01-01
      • 2020-03-14
      • 2013-06-15
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多