【问题标题】:dlmopen and C++ librariesdlmopen 和 C++ 库
【发布时间】:2019-03-05 17:19:06
【问题描述】:

(这大概是一个相当高级的问题,对此感到抱歉:-))

我有一个问题,我需要将插件(共享库)加载到应用程序中,但插件可能使用与应用程序使用的库版本二进制不兼容的库。我的想法是使用 dlmopen() 并将插件加载到它自己的命名空间中。我希望获得二进制不兼容库的两个单独副本(以及任何其他常见依赖项,即使二进制兼容)。

这似乎在一定程度上起作用,但在某些情况下,我在 glibc 深处遇到了一个段错误,即调用静态对象的构造函数的地方(这是我在调试器中发现的)。

我做了一个最小的例子来重现这个问题,可以在 github 上找到:https://github.com/mhier/segregatedLinkingExample

该示例使用 libxml++ 作为外部通用 C++ 库,因此您需要安装其开发包。运行“mk.sh”进行编译,然后运行“main”。然后它会崩溃(至少在 Ubuntu 16.04 和 18.04 上会崩溃)。如果您删除“-DWITH_CRASH”,它将不再崩溃。

WITH_CRASH 编译开关允许在主可执行文件中使用 libxml++。它总是在插件库 libC 中使用。只有 libxml++ 用于主可执行文件和我看到崩溃的插件。在这种情况下,“使用”就像从它派生一个虚拟类并确保派生类的代码真正通过实现构造函数/析构函数生成。它甚至没有在插件中执行代码(除了通过 dl_init -> 静态对象的构造函数等)。

我在 Internet 上找不到很多关于 dlmopen 的信息。我没有发现任何指向正确方向的错误报告。有没有人使用 dlmopen 和 C++ 库的新命名空间?非常欢迎任何形式的输入如何从这一点继续!

【问题讨论】:

  • 你能告诉我们你目前用来加载插件的代码吗?另外,打开成功了吗?如果您在打开失败时尝试调用插件中的函数,这可能就是它会崩溃的原因。
  • @AlexisWilke 未检查打开,但也未使用生成的句柄。
  • 替代解决方案:在与该库兼容的单独可执行文件中加载不兼容的库。违反 ODR 的拨号并不好玩。
  • @AlexisWilke:代码包含在示例中,请参见链接。打开没有完成,因为它内部有段错误(加载库时调用 dl_init)。
  • 我认为dlmopen 是一个相当老套的功能。 GDB 开发人员在某处发表评论称,他们没有对通过 dlmopen 加载的二进制文件进行调试提供适当的支持,因为他们找不到使用它的人。

标签: c++ linux plugins dynamic-loading


【解决方案1】:

问题与 C++ 无关。

这是 glibc 版本的 libpthreads 中的一个错误,导致加载了 dlmopen 的库返回 pthread_key_create 的重复项,从而导致特定于线程的存储被破坏(相同的键意味着相同的内存位置,就像 malloc 返回相同的内存区域多次)。

这立即崩溃的原因是 libglib 在其加载函数中大量使用了线程特定的存储。

具体来说,问题在于直接使用 __pthread_keys 全局变量,它应该通过线程描述符 (THREAD_SELF) 加载,从而确保在所有 libpthread 实例共享的结构中分配线程局部键。

详情见源:https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_key_create.c;h=a584db412b7b550fa7f59e445155dbfddaeb1d23;hb=HEAD

报告给glibc:https://sourceware.org/bugzilla/show_bug.cgi?id=26955

还有在gdb中调试这种东西的时候,提示获取调试符号:

  • 检查 /proc/$pid/maps 以找出 dlmopen 加载库的位置
  • 找到库的入口点(例如 readelf -h /usr/lib/x86_64-linux-gnu/libglib-2.0.so )
  • 在 gdb 中使用 add-symbol-file 加载符号。
    • 作为文件名只需指定库文件,如果您安装了调试符号,gdb 会以正常方式找到它们 - 不要尝试直接指定符号文件
    • 地址是来自/proc/$pid/maps的加载地址+入口点地址

【讨论】:

    【解决方案2】:

    所以看来答案是不这样做。 dlmopen 似乎有 C++ 问题,这可能导致未定义的行为。据推测,命名空间并未完全修复 ODR 违规。

    我承认,这个答案是我的主观看法。我还没有找到很多关于将 dlmopen 用于 C++ 库的好资源。因此我的结论是不要使用它,因为我需要它可靠地工作。我见过非常奇怪的效果,例如如果我将共享库链接到特定的第三方库(即使不使用它),我在问题中的示例再次起作用。除非我能理解这些影响,否则我不会相信解决方案(因为它可能会意外地起作用)。

    dlmopen() 可能在其他情况下工作,例如如果一个人同时控制应用程序和共享库,并且可以测试它是否正确加载。

    【讨论】:

    • 您能否为此引用您找到的任何来源?我在这里是因为我也面临 dlmopen() 的问题。是的,我确实需要 dlmopen()。
    • @DroidCoder 没有直接来源。我只是没有找到任何关于成功使用 dlmopen() 和 C++ 的人的资料。有希望的项目只有一个,但好像被废弃了1.5年:git.collabora.com/cgit/user/vivek/libcapsule.git我没试过。
    • 我已经澄清了我的答案以及我是如何得出结论的。
    • 感谢您的更新和回复。 FWIW,我用 valgrind 3.12.0 试过了。它报告了几个 dlopen() 没有发生的错误,并且这个测试是在我可以安全地替换 dlopen() 的上下文中进行的。因此,在我的 glibc 版本 (2.22) 中,该版本的 valgrind 也不支持 dlmopen() 或存在未解决的问题。
    • 请注意@Reimar 的答案是正确答案.. :)
    猜你喜欢
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 2016-02-03
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2012-02-21
    • 2015-06-07
    相关资源
    最近更新 更多