【问题标题】:How to build an application that requires both libstdc++.so.5 and libstdc++.so.6?如何构建同时需要 libstdc++.so.5 和 libstdc++.so.6 的应用程序?
【发布时间】:2009-10-31 19:15:44
【问题描述】:

我想以重要的通知作为开头,即我不是 C/C++ 程序员,并且对 C 中的库链接如何工作知之甚少

我们的代码使用 libstdc++.so.6(我认为是 gcc 3.4)。我们有使用 libstdc++.so.5(我认为是 gcc 2.something 或 3.2)的第三方预编译(闭源)库。这是在linux上。我们有第三方库的 .a 和 .so 版本。

是否可以使用 3rd 方库构建我们的应用程序?如何?是否可以在没有安装 libstdc++.so.5 的情况下构建/运行我们的应用程序,如何?

如果我忘记了一些重要信息,请告诉我 - 我几乎不知道与这些内容有什么关系。我意识到一个完整的答案可能是不可能的;我真的在寻找方向和指导。静态链接 this、dynamic that、rebuild that、prebuild so-and-so、切换到版本 x 或符号链接 quizdoodle 等。

更新:

我们尝试使用 dlopenRTLD_LOCAL 将第 3 方库与我们应用程序的其余部分隔离开来。这似乎大部分起作用了,但是,由于未知原因,我们留下了大量的内存泄漏。我们怀疑,当我们调用dlopen 时,第三方库会从已经加载的.so.6 中提取malloc 之类的符号,然后事情就会变得混乱。

为了傻笑,我们尝试将第三方库放入LD_PRELOAD,然后运行我们的应用程序,内存泄漏似乎完全消失了。

【问题讨论】:

  • 我不禁觉得命名空间可以帮助解决这个问题。由于供应商不会修复他们的库,是否可以让他们将他们的东西放在一个额外的命名空间中,比如namespace 5 {}。也就是说,他们的库将使用5::malloc,而最新的东西将只使用malloc。 (我回避了一些问题,比如为什么他们在 C++ 程序中使用malloc)。

标签: gcc build linker libstdc++


【解决方案1】:

您可以尝试围绕您的 3rd 方库构建包装库:使用该库的静态版本 + 将其与静态标准库链接(-static-libgcc - 确保通过 -L 选择正确的版本) .要做的重要事情是正确关闭此包装库,即只应导出原始第 3 方库中的符号,应隐藏其他所有内容。这样,您的包装库将为您的应用程序公开所有需要的符号并将标准内容封装在其中。请注意,特别是如果您的代码和第 3 方代码之间共享某些内存操作(例如,您在代码中分配内存并在第 3 方中取消分配),则不能保证有效......在这种情况下,唯一的选择是保留第 3 次派对库在不同的进程空间中。

我不认为上面提到的动态选项会起作用,因为你会遇到完全相同的问题 - 只是稍后。

一般来说,最好不要在同一进程空间中混合具有不同运行时间的二进制文件。这几乎总是灾难的根源。

【讨论】:

  • 您能解释一下如何正确隐藏符号,以及您对内存操作的看法吗?我们尝试将第 3 方库包装在使用 dlopen(lib, RTLD_LOCAL) 加载第 3 方库的函数中。这使我们的代码无法链接到 .so.5,但是,我们怀疑,第 3 方代码正在从已经加载的 .so.6(由我们的代码加载)中提取符号(如 malloc),并导致内存泄漏.我们首先尝试使用 LD_PRELOAD 来提高 3rdparty 库的音量,然后内存泄漏几乎消失了。
  • 我们在我们这边分配,第三方库释放。例如:ThirdPartyStruct * foo = malloc(...);第三方 APICall(foo);第三方FreeStruct(foo); - 你是说如果我们使用 0.6 和它使用 0.5 就不可能了?如果我们将 .5 与我们的包装函数一起使用,并通过包装库进行所有分配/解除分配怎么办?
  • 在不同的标准库中执行分配/解除分配时,您无法确定它们是否以相同的方式执行此操作……您根本不知道。我不能说这在 Linux 上是否是一个真正的问题,但在 Windows 上肯定是:如果您在库中使用不同的运行时并在一个库中进行分配并在另一个库中进行释放,您将获得运行时异常。我什至不会考虑在您的情况下混合使用不同的标准库,即使它可以在具有这些特定运行时的特定平台上运行,您也不能确定当您迁移到新版本的 libstd 时它是否会正常运行......
  • 一些关于二进制兼容性的阅读:gcc.gnu.org/onlinedocs/gcc/Compatibility.html
  • 要限制 Linux 上的导出符号,您应该为链接器使用 --version-script 标志(如果通过 gcc 链接,请使用 -Wl,--version-script)。此标志需要一个特殊文件,该文件定义您的库(和版本)中的导出/本地文件,如下所示: .1 { global: symbol1;符号2; ... 当地的: *; );谷歌了解详情。链接库后,您可以使用“nm”检查哪些符号已导出(对其他库可见)以及哪些符号是本地的。同样,它可以为定义明确的简短 API 完成
【解决方案2】:

请向您的供应商索取更新版本的库,该版本使用的东西并不过时。如果做不到这一点,您可以查看您的新应用程序是否仍可与旧版本的库一起使用,并在必要时将其移植回来。尝试拥有同一个库的两个不同版本会很痛苦,我认为您不会找到可接受的解决方案。

【讨论】:

  • 供应商非常反对升级他们的库(我们已经争论了几个星期了)。该应用程序无法反向移植到 .so.5
【解决方案3】:

虽然很容易同时将 libstdc++.so.6 和 libstdc++.so.5 链接到您的应用程序,但它的行为几乎是未定义的,因为符号基本上是偶然从任一库中获取的。

IMO 成功的最佳方法是在旧系统(使用兼容的 gcc,例如 gcc 3.3)上围绕您的 3rd 方库构建自己的应用程序,并让它通过 IPC 与您的主应用程序通信(例如共享内存)。这样就不用了

如果您不想在目标系统上保留 libstdc++.so.5,那么这很简单:使用 gcc 的 -static 标志将 libstdc++.a 链接到您的包装应用程序。

【讨论】:

  • 我发现了一个类似的问题,其中一个回答暗示,通过某种动态链接魔法,可以毫无问题地同时使用两者。这是真的,还是错误的信息? stackoverflow.com/questions/728858/…
【解决方案4】:

虽然到目前为止给出的其中一种方法可能有效,但我认为更安全的说法是,您不能以可靠的方式直接执行此操作。如果您使用完全基于 C 的 API(仅与 C 兼容的结构、由 malloc/free 管理的内存等)编写包装器,那么您也许可以使用 pobedim 的解决方案。但是,如果您需要交换 C++ 结构,这是不安全的,因为即使您可以建立链接,也会在相同的对象上使用不同的标准库实现。此外,C++ ABI 可能在基于 .5 和 .6 的代码库之间不兼容(我不记得几年前与标准库 sonames 相关的主要 Gnu C++ ABI 更改是如何发生的)。

我认为解决此问题的最安全方法是在您的应用程序和基于相关库构建的资源/计算服务器进程之间使用某种 IPC 的多进程方法。您可以使用 CORBA、D-Bus、Sun RPC 或一些通过管道或套接字的 ad-hoc 协议来完成这项工作。我在尝试在 64 位应用程序中使用封闭源代码的 32 位代码时做到了这一点,并且效果很好。您会看到性能下降,但您也将完全回避尝试在单个进程中混合 C++ 运行时所固有的问题。

【讨论】:

  • 我可以确认 pobedim 的解决方案适用于基于 C 的 API。只要从包装器中只导出纯 C 符号,就不会崩溃。
猜你喜欢
  • 1970-01-01
  • 2018-08-06
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
相关资源
最近更新 更多