在我处理的有问题的情况下,我想了解 Linux 机器上的动态链接器/加载器行为。
我们的崩溃代码作为插件加载 (dlopen(libwrapper.so, RTLD_GLOBAL))。libwrapper.so 只是一个薄层,它加载了另一个插件来完成真正的工作。这些插件可以命名为:P1 和 P2,它们中的每一个都依赖于名为 F 的通用库(所有这些都非常简化)。
引入包装器 (libwrapper.so) 以允许在没有 RTLD_GLOBAL 的情况下加载 Pn,因为该标志集会导致加载 Pns 的明显链接问题(它们具有相同的 API)。RTLD_DEEPBIND 不是一个选项,因为目标平台太旧 - 不支持它。
令我们惊讶的是,问题在 P2 的加载时出现在 F 库中(当 P1 已经加载(并初始化)并且 F 作为其隐式依赖项时)。在显式加载 P2 时 (dlopen(libP2.so, RTLD_LOCAL | RTLD_NOW)),动态链接器没有报告任何问题,但是在 F 中调用代码来实例化 F 中定义的某些类型实例(再次)会导致各个位置出现分段错误(如果一个被跳过/注释掉,它会在另一个地方崩溃——因此没有花时间调查可能很麻烦的代码模式,因为怀疑有更普遍的问题/误解)。没有使用内联函数,代码与 -Wl、-E 链接,默认可见性,GCC 是 3.4.4.. F 代码非常稳定,过去在独立应用程序中使用或作为插件的一部分使用。
我想将 F 链接为静态库以解决动态链接器可能存在的任何问题,但结果是相同的。
我对这个话题的看法:
- 将 F 链接为动态库会导致动态链接器“知道” F 在第二次加载 P2 时被引用,并且只会增加引用计数器并且不调用静态初始化程序(这没关系),但会进行重定位(再次,这似乎是有问题的) .
- 将 F 作为静态库链接会导致动态链接器将 F 代码加载为 P2 (P2F) 的静态链接部分,并在 P2F 内进行重定位。但是,F 中的“不知何故”常见符号与 P1F 代码实例混淆了。
关于使代码至少工作的解决方法的假设:
- 在单个共享库(单个插件)中链接 P1 ... Pn,F 是否共享/静态无关紧要。这样,任何重定位都只进行一次。
我很感激任何反馈是我对该主题的看法错误/过于简化/缺少重要部分?这是过去已知的 GCC / binutils 错误吗?