将C ++共享库链接到C程序时如何避免错误

时间:2018-02-22 14:33:03

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

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

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

我将其缩小到以下事实:

libMain使用C文件和1个C ++文件,C文件使用gcc进行编译,使用g ++进行C ++文件编译。该库与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发出了错误的链接器命令?或者,在链接libMain

时,g ++应该做些不同的事情

供参考: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

3 个答案:

答案 0 :(得分:1)

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

您可能应该重新配置并重新编译并重新构建libMain。您希望将显式-lstdc++链接。

将某些LDFLAGS=-lstdc++LIBES=-lstdc++传递给configure的{​​{1}}可能有所帮助。请参阅this

BTW,有一些自动编写的库用C ++编码,可以从纯C程序中调用(例如libgccjit),它们与libMain

链接

答案 1 :(得分:0)

实现您的问题的想法是编写一个包装器库,其标头位于C中,并且主体内部使用C++编译器进行编译,以便它可以调用您的C++库功能

稍后,您可以将C++库与其C标题链接到您的应用

答案 2 :(得分:0)

我找到了解决方案(TL& DR跳到脂肪解决方案):

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

  • binTool是一个C程序,因此与gcc
  • 相关联
  • libMain包含一个C ++文件,因此与g ++链接。但它不使用任何C ++库功能,因此链接器会忽略libstdc++链接过程中的libMain
  • 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++相关联。

所以可以指出2个问题:

  1. libExtraCxx应该是一个C ++库并且链接libstdc++但是我想这个“技巧”是为了明确地避免对C ++库的依赖所以它可以被GNU,Intel或Clang C ++编译器可能有不同的标准库。
  2. libMain应该省略libstdc++ 而不是。这通常可以通过将-Wl,--no-as-needed传递给编译器/链接器来完成,如here所述。
  3. 由于所涉及的工作量,我无法更改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这个错误#12880尚未修复,所以我正在寻找更多。

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

    供参考:我使用的是starPU,因此libMain实际上是libstarpu-1.2.so。失败的二进制文件(binTool)是starpu_perfmodel_display。 “假”-C ++ - 库来自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包装器,因此它甚至看不到这些标志,因此它们会被透明地添加。