dlmopen 和 C++ 库
dlmopen and C++ libraries
(这可能是一个相当高级的问题,对此深感抱歉:-))
我遇到了一个问题,我需要将插件(共享库)加载到应用程序中,但插件可能使用与应用程序使用的库版本二进制不兼容的库。我的想法是使用 dlmopen() 并将插件加载到它自己的命名空间中。我希望获得二进制不兼容库的两个单独副本(以及任何其他常见依赖项,即使二进制兼容也是如此)。
这似乎在一定程度上有效,但在某些情况下,我在 glibc 的深处遇到了一个段错误,在调用静态对象的构造函数的地方(这是我在调试器中发现的)。
我做了一个最小的例子来重现这个问题,可以在github上找到:https://github.com/mhier/segregatedLinkingExample
该示例使用 libxml++ 作为外部通用 C++ 库,因此您需要安装其开发包。 运行"mk.sh"编译然后"main"。然后它会崩溃(至少它在 Ubuntu 16.04 和 18.04 上会崩溃)。如果删除“-DWITH_CRASH”,它不再崩溃。
WITH_CRASH 编译开关允许在主可执行文件中使用 if libxml++。它总是在插件库 libC 中使用。在我看到崩溃的主要可执行文件和插件中只使用了 libxml++。 "Using" 在这种情况下就像从中派生一个虚拟 class 并确保派生的 class 的代码真的通过实现 constructor/destructor 生成。它甚至不执行插件中的代码(除了通过 dl_init -> 静态对象的构造函数等)。
我在 Internet 上找不到太多关于 dlmopen 的信息。我还没有发现任何指向正确方向的错误报告。有没有人将 dlmopen 与 C++ 库的新命名空间一起使用?非常欢迎任何形式的输入如何从这一点继续!
看来答案是不做。 dlmopen 似乎与 C++ 有问题,这可能导致未定义的行为。据推测,ODR 违规并未被命名空间完美修复。
我承认,这个回答是我的主观看法。我没有找到很多关于将 dlmopen 用于 C++ 库的好资源。因此我的结论是不要使用它,因为我需要它可靠地工作。我看到了非常奇怪的效果,例如如果我 link 针对特定第三方库的共享库(即使不使用它),我在问题中的示例再次起作用。除非我能理解这些影响,否则我不会相信解决方案(因为它可能会意外起作用)。
dlmopen() 可能适用于其他情况,例如如果有人同时控制应用程序和共享库并且可以测试它是否正确加载。
问题与 C++ 无关。
这是 libpthreads 的 glibc 版本中的一个错误,它会导致使用 dlmopen 加载的库返回 pthread_key_create 的重复项,导致线程特定的存储被破坏(相同的键意味着相同的内存位置,就像malloc 多次返回相同的内存区域)。
立即崩溃的原因是因为 libglib 在其加载函数中大量使用线程特定存储。
具体来说,问题在于直接使用了 __pthread_keys 全局变量,而应该通过线程描述符 (THREAD_SELF) 加载该变量,从而确保在结构中分配线程局部键由 libpthread 的所有实例共享。
报告给 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加载地址+入口点地址
(这可能是一个相当高级的问题,对此深感抱歉:-))
我遇到了一个问题,我需要将插件(共享库)加载到应用程序中,但插件可能使用与应用程序使用的库版本二进制不兼容的库。我的想法是使用 dlmopen() 并将插件加载到它自己的命名空间中。我希望获得二进制不兼容库的两个单独副本(以及任何其他常见依赖项,即使二进制兼容也是如此)。
这似乎在一定程度上有效,但在某些情况下,我在 glibc 的深处遇到了一个段错误,在调用静态对象的构造函数的地方(这是我在调试器中发现的)。
我做了一个最小的例子来重现这个问题,可以在github上找到:https://github.com/mhier/segregatedLinkingExample
该示例使用 libxml++ 作为外部通用 C++ 库,因此您需要安装其开发包。 运行"mk.sh"编译然后"main"。然后它会崩溃(至少它在 Ubuntu 16.04 和 18.04 上会崩溃)。如果删除“-DWITH_CRASH”,它不再崩溃。
WITH_CRASH 编译开关允许在主可执行文件中使用 if libxml++。它总是在插件库 libC 中使用。在我看到崩溃的主要可执行文件和插件中只使用了 libxml++。 "Using" 在这种情况下就像从中派生一个虚拟 class 并确保派生的 class 的代码真的通过实现 constructor/destructor 生成。它甚至不执行插件中的代码(除了通过 dl_init -> 静态对象的构造函数等)。
我在 Internet 上找不到太多关于 dlmopen 的信息。我还没有发现任何指向正确方向的错误报告。有没有人将 dlmopen 与 C++ 库的新命名空间一起使用?非常欢迎任何形式的输入如何从这一点继续!
看来答案是不做。 dlmopen 似乎与 C++ 有问题,这可能导致未定义的行为。据推测,ODR 违规并未被命名空间完美修复。
我承认,这个回答是我的主观看法。我没有找到很多关于将 dlmopen 用于 C++ 库的好资源。因此我的结论是不要使用它,因为我需要它可靠地工作。我看到了非常奇怪的效果,例如如果我 link 针对特定第三方库的共享库(即使不使用它),我在问题中的示例再次起作用。除非我能理解这些影响,否则我不会相信解决方案(因为它可能会意外起作用)。
dlmopen() 可能适用于其他情况,例如如果有人同时控制应用程序和共享库并且可以测试它是否正确加载。
问题与 C++ 无关。
这是 libpthreads 的 glibc 版本中的一个错误,它会导致使用 dlmopen 加载的库返回 pthread_key_create 的重复项,导致线程特定的存储被破坏(相同的键意味着相同的内存位置,就像malloc 多次返回相同的内存区域)。
立即崩溃的原因是因为 libglib 在其加载函数中大量使用线程特定存储。
具体来说,问题在于直接使用了 __pthread_keys 全局变量,而应该通过线程描述符 (THREAD_SELF) 加载该变量,从而确保在结构中分配线程局部键由 libpthread 的所有实例共享。
报告给 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加载地址+入口点地址