【问题标题】:How to avoid errors when linking C++ shared libs into C programs将 C++ 共享库链接到 C 程序时如何避免错误
【发布时间】:2018-02-22 14:33:03
【问题描述】:

我正在使用一个库,它带有通常的 AutoTools 生成的configure && make && make install 过程。该库包含一个主(共享)库和一些工具,主要是用 C 语言编写的。

现在我遇到了一个问题,其中一个工具的构建在使用仪器时失败(Score-P 包装了编译器调用以发挥其魔力)。

我将其缩小到以下事实:

libMain 使用 C 文件和 1 个 C++ 文件,C 文件使用 gcc 编译,C++ 文件使用 g++。该库作为共享库与 g++ 链接。
binTool 仅使用 C 文件,但链接到 libMain。

这在没有仪器的情况下有效。但是,在使用时,它会在与使用 C++ 功能的 g++ 链接时添加额外的库。将 binTool 与 gcc 链接然后给出undefined reference to 'operator delete[](void*)'(以及一些类似的)

首先:有人可以向我解释一下,为什么我在链接共享库时必须小心(即使二进制文件只使用 C 代码,也要使用 g++)?我的印象是,共享二进制文件的链接已经完成,所以链接不应该引入任何新的依赖关系或者依赖关系已经解决(在这种情况下,libMain 会知道它需要libc++ 并且已经引用了它/存储/无论精灵在做什么)

第二:通过阅读 AutoTools 文档,我发现程序的链接器是根据其源文件选择的。由于libMain 使用 C++ 文件,因此它与 g++ 链接。 binTool 仅使用 C 文件,因此它与 gcc 链接。但是binTool 也链接了libMain,它是 C++ 链接的,似乎需要与 g++ 链接。
那么罪魁祸首在哪里呢? AutoTools 是否为binTool 发出了错误的链接器命令?还是 g++ 在链接libMain 时应该做些不同的事情?

供参考:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)

ldd libMain:

linux-vdso.so.1
librt.so.1
libpthread.so.0
libm.so.6 libc.so.6
libgcc_s.so.1
libdl.so.2
libnuma.so.1
libltdl.so.7

【问题讨论】:

  • 你的问题不清楚,应该有一些minimal reproducible example。请注意,在 Linux 上,共享库可以与另一个共享库链接(当您构建该库时)。特别是,您可以(并且可能应该)通过将libMainlibstdc++ 链接来构建它
  • 顺便说一句,如果libMain 是开源的,你最好在你的问题中给它命名(并给出它的 URL),并且你可能会对其进行错误报告
  • 另见this非常相关的答案
  • 好主意,我通常会这样做,但在这种情况下,我未能创建 MWE。一开始我没有尝试,因为它会很复杂(包括至少 4 个不同的文件内置到 2-3 个库和一个二进制文件中)但是在研究了 why 之后我未能创建 MWE(它没有重现所描述的行为)我找到了解决方案。

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


【解决方案1】:

正如我所评论的,您可以将共享库(当构建该库时)与另一个共享库链接。有关详细信息,请参阅this 答案。阅读 Drepper 的 How To Write Shared Libraries 论文。

您可能应该重新配置并重新编译并重新构建您的libMain。您希望将其明确-lstdc++ 链接起来。

也许将一些LDFLAGS=-lstdc++LIBES=-lstdc++ 传递给configurelibMain 可能会有所帮助。见this

顺便说一句,有一些用 C++ 编写的自动配置库,可以从纯 C 程序调用(例如 libgccjit),它们与 -lstdc++ 链接

【讨论】:

  • 谢谢,但不幸的是没有帮助。使用 g++ 意味着 -lstdc++ 因此没有任何变化。我什至发现 libTool 在做类似g++ -nostdlib ... -lstdc++ 的事情。无论如何,该库没有出现在ldd 中,我发现这是因为它被认为是未使用的而被删除。
【解决方案2】:

解决您的问题的想法是编写一个包装库,其标头位于 C 中,内部使用 C++ 编译器编译主体,以便它可以调用您的 C++ 库函数。

稍后您可以将带有 C 标头的 C++ 库链接到您的应用程序

【讨论】:

  • libMain 使用(部分)C++ 编译器并与g++ 链接除此之外,它只是 C 语言(尤其是标头),所以这不起作用。从问题中可见的主要问题是,这个(部分)C++ 库不包含对libstdc++ 的引用
【解决方案3】:

我找到了解决方案(TL&DR 跳转到胖子SOLUTION):

情况比我想象的要困难得多。会发生什么: binTool 链接到共享库 libMain 链接到共享库 libExtraCxx

  • binTool 是一个 C 程序,因此与 gcc 链接
  • libMain 包含一个 C++ 文件,因此与 g++ 链接。但它不使用任何 C++ 库功能,因此链接器从 libMain 链接过程中省略了 libstdc++
  • libExtraCxx 是一个 C 库,旨在通过自动包装脚本链接到 C 程序中。由于libMain 使用g++,它得到libExtraCxx 链接。该库应该拦截C++ new/delete 调用,并通过使用GNU -wrap 链接器命令和内部定义(手动)修改版本来实现的 new/delete__wrap_ 为前缀,并声明以 __real_ 为前缀的那些被修改的版本。

通常这是可行的,因为当 binTool 使用 C++ 链接器时,包装器会发出 -wrap 命令,而 libstdc++ 提供 __real_ 函数。

但是错误是,libstdc++ 永远不会被链接:libExtraCxx 是一个 C 库,它只是挂钩到 C++ 函数。 libMain 不使用任何 C++ 库函数,binTool 又是一个 C 程序,并且没有链接到它的共享库有 libstdc++ 链接到它。

所以可以指出两个问题:

  1. libExtraCxx 应该是一个 C++ 库和链接 libstdc++ 但我想这个“技巧”是为了明确避免对 C++ 库的依赖,因此它可以被可能有不同的 GNU、Intel 或 Clang C++ 编译器使用标准库。
  2. libMain 应该有 libstdc++ not 省略。这通常可以通过将-Wl,--no-as-needed 传递给编译器/链接器来完成,如here 所述。

由于涉及的工作量,我无法更改 libExtraCxx,但我可以将参数传递给编译器,所以我选择了 2。

但是,简单地做configure <...> LDFLAGS=-Wl,--no-as-needed 是行不通的。该标志已使用,但 libstdc++ 不存在。

我发现罪魁祸首是 libMain 使用的 libtool:libtool 2.4.2 does not pass the flag at the right position AFAIK 这个 bug#12880 还没有修复,所以我正在寻找更多。

解决方案
我找到了hack here:只需使用CXX="$CXX -Wl,--no-as-needed"。这基本上让 libtool 认为,该标志是编译器命令的一部分,它不会重新排序它而将其留在开头。

供参考:我使用的是starPU,所以libMain实际上是libstarpu-1.2.so。失败的二进制文件 (binTool) 是 starpu_perfmodel_display。 “假”-C++-Library 来自 Score-P libscorep_adapter_memory_event_cxx.so.5 我只是更改了名称以简化它们。

对于 SCORE-P,解决方案有点复杂,因为不能简单地更改 CXX。所以用 ScoreP 编译 starPU 的完整解决方案是:

SCOREP_WRAPPER=off ~/Downloads/starpu-1.2.3/configure --prefix /usr/local CC=scorep-gcc CXX=scorep-g++ FC=scorep-gfortran --with-mpicc=scorep-mpicc --with-mpifort=scorep-mpif77
还有
make SCOREP_WRAPPER_INSTRUMENTER_FLAGS="--opencl --thread=pthread" SCOREP_WRAPPER_COMPILER_FLAGS="-Wl,--no-as-needed"

解释:SCOREP_WRAPPER_COMPILER_FLAGS 将导致包装器将标志传递给编译器。由于 libTool 使用 scorep 包装器,它甚至看不到这些标志,因此它们被透明地添加。

【讨论】:

    猜你喜欢
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    相关资源
    最近更新 更多