【问题标题】:Segfault on C++ Plugin Library with Duplicate Symbols具有重复符号的 C++ 插件库上的 Segfault
【发布时间】:2009-11-30 17:20:45
【问题描述】:

我有一个跨平台的 C++ 应用程序,它被分成几个共享库并从插件共享库加载附加功能。插件库应该是自包含的并自行运行,无需了解或依赖于调用应用程序。

其中一个插件包含从主应用程序复制的代码,因此包含与引擎中的符号名称重复的符号名称。 (是的,我知道这通常是一个禁忌,但在编写插件时,引擎是一个单一的二进制文件,无法共享库。)在 Windows 上,一切运行良好。在 Linux 上,我们遇到了段错误。通过查看错误的堆栈跟踪,它是在插件中调用重复类名中的函数时发生的。这似乎是由于引擎和插件的共享代码版本略有不同(某些类功能在插件中被注释掉了)。就好像插件将它的符号运行时链接到引擎而不是它自己的。我们通过将dlopen 的参数更改为dlopen(pFilepath, RTLD_LAZY | RTLD_LOCAL) 来“修复”该问题。

但是当我们重写引擎以将其拆分为共享库时(为了最终在插件中重用),我们再次收到段错误错误。查看堆栈跟踪,它来自引擎 -> 插件 -> 引擎。

有没有办法指定运行时链接器不将插件的符号映射到引擎(特别是如果它们在插件中定义)?

谢谢! 马特


2009-12-3 编辑

我首先尝试将插件的代码包装在它自己的命名空间中。这不起作用,因为它静态链接到一个也链接到引擎的库。静态库的版本不一样,所以segfault!

然后我更改了引擎的构建和静态链接的库。当我运行它时,我不再有这个问题。因此,这似乎是导出共享库符号然后在打开插件时动态重定位到插件中的结果。但是当引擎的所有代码都在一个可执行文件中时,它不会导出其符号(因此它不会尝试将插件的符号重新定位到引擎中)。

我仍然有一个问题,因为有一个并行版本的程序(使用 Open-MPI)并且仍然会出现段错误。它似乎仍在导出引擎的符号并重新定位插件的符号。这可能与 Open-MPI 执行应用程序的方式有关。

是否有可以在插件共享库上使用的链接器标志,告诉它不要在运行时动态重定位符号?或者隐藏它的符号,这样它们就不会被重新定位?我试过-s(“省略所有符号信息”),但这显然没有改变动态符号(使用nm -D <plugin>检查)。

【问题讨论】:

  • 这些符号是全局的还是函数名?你能对代码做些小改动吗?
  • 它们是类和它们的成员函数。引擎代码库中使用了 36 个文件,因此我不想修改每个类名或文件。虽然最终目标是重写插件,但由于时间限制和代码验证,如果我不需要,我不想这样做。
  • @CuppM,例如,您有一个类“Foo”,其成员“bar”在 2 个地方定义?并且“Foo”在这两种情况下都在同一个命名空间中?如果是这样,这将永远不会为您正常工作。将其中一个“Foo”移动到它自己的命名空间中,生活会更轻松。
  • 嗯,我有“Foo”、“FoosFriend”、“FoosUncleJim”等,我想将文件尽可能靠近源(以便更容易跟上日期)。 Linux 处理共享库的方式是否使其成为不可能?链接器不应该在库的构建时正确映射符号(到自身)吗?为什么它会尝试在运行时重新映射它们?

标签: c++ gcc linker runtime shared-libraries


【解决方案1】:

我想我找到了解决方案,链接器标志-Bsymbolic。本质上,此标志在共享库中添加了一个标志,以告诉运行时链接器首先尝试解析自身内部的符号名称。当插件与该标志链接时,引擎能够在所有情况下(单片 exe、带共享库的 exe、带和不带命名空间包装的插件)与插件一起运行。

似乎确实有一些批评者对-Bsymbolic提出警告:
http://www.technovelty.org/code/c/bsymbolic.html
http://software.intel.com/en-us/articles/performance-tools-for-software-developers-bsymbolic-can-cause-dangerous-side-effects/

但是考虑到他们的警告以及插件的意图是什么,我认为这对我来说是正确的选择。至少现在是这样。

【讨论】:

    【解决方案2】:

    我同意 Glen 的观点——除非你修改类名,可能通过命名空间,否则你不会真正解决这个问题。与尝试可靠地修复它而不更改符号名称相比,即使是 36 个文件也可能花费更少的时间来修改。

    首先确定所有需要调整名称的类。您的链接器可能已经为您列出了它们。然后我会至少暂时更改 both 组类的名称(例如从 Foo 到 Engine::Foo 和 Plugin::Foo)。这样,您可以让编译器找到对有问题的类的所有引用。在插件编译并引用正确的新插件类名之前,请远离插件源代码。完成后,将 Engine:: 类改回它们的旧名称(除非您也想永久修改引擎源,听起来您不想这样做)。该插件现在应该编译并链接到正确的、唯一命名的类。

    【讨论】:

    • 虽然您对此可能是正确的。不过,这不是一个令人满意的答案,所以我将暂时搁置这个问题以防万一。我将修改插件代码以将命名空间添加到有问题的位。直到我能够使用引擎的共享库重写它,因为我更改了引擎中的所有代码,所以我修改副本无关紧要。
    【解决方案3】:

    我只想用 PluginX 命名空间包装所有插件的代码。这肯定会让你免于这些错误。 无论如何,这是一个非常好的、重要的练习。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-13
      • 2013-08-19
      • 2017-12-26
      • 2017-08-30
      • 2020-12-11
      • 2011-04-06
      • 2012-11-07
      相关资源
      最近更新 更多