dlmopen和C ++库

时间:2019-03-05 17:19:06

标签: c++ linux plugins dynamic-loading

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

我有一个问题,我需要将插件(共享库)加载到应用程序中,但是该插件可能使用的二进制库与应用程序使用的库的版本不兼容。我的想法是使用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 ++库的新命名空间?任何形式的输入从现在开始都非常欢迎!

2 个答案:

答案 0 :(得分:0)

所以看来答案是不这样做。 dlmopen似乎与C ++有关,可能导致未定义的行为。大概是因为名称空间无法完全解决ODR违规问题。

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

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

答案 1 :(得分:0)

问题与C ++无关。

这是libpthreads的glibc版本中的一个错误,该错误导致装载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的加载地址+入口点地址