强制链接器失败并出现多重定义错误,即使包含--whole-archive也是如此

时间:2017-07-11 20:43:11

标签: c++ makefile linker g++

此示例包含多个文件:

(JavascriptExecutor)driver.executeScript("window.scrollBy(0,250)", "");
// baz.cxx
int wat = 0;
int counter = ++wat;
// foo.cxx (empty)
// bar.cxx (empty)
// main.cxx
#include <iostream>
extern int wat;
int main() {
    std::cout << wat << '\n';
}

按原样,如果您只运行// makefile run : main.cxx foo.so bar.so g++ -std=c++11 $^ -o $@ baz.a : baz.cxx g++ -std=c++11 -c $^ -o baz.o -fPIC ar rcs $@ baz.o %.so : %.cxx baz.a g++ -std=c++11 $< -Wl,--whole-archive baz.a -Wl,--no-whole-archive -o $@ -shared -fPIC ,那么所有内容都包含编译,构建,链接,运行和输出make && LD_LIBRARY_PATH=. ./run。这是因为2foo.so都提供了bar.sowat的初始化运行了两次。

在这种情况下,有没有办法以某种方式强制counter无法与多重定义错误相关联,同时仍然确保runfoo.so都有{bar.so的定义1}}?

2 个答案:

答案 0 :(得分:2)

您可以将链接描述文件rule x when Policy( available, $reqs: requestedCovers, $covs: covers ) accumulate( Cover( $type: type ) from $covs; $covered: collectSet( $type ) ) $s: String( this not memberOf $covered ) from $reqs then ...$s is not covered... end libfoo.so与静态部分一起使用,这会产生符号冲突(并在一些非显而易见的地方安装实际的DSO,以便libbar.so和{ {1}}不要接他们。)

这样的事情:

  • -lfoo

    -lbar
  • libfoo.so

    INPUT(conflict.o)
    INPUT(./foo.so)
    
  • libbar.so

    INPUT(conflict.o)
    INPUT(./bar.so)
    

这导致:

conflict.cxx

这只会在链接编辑器的同一次运行中检测冲突。您仍然可以使用int conflict; g++ -std=c++11 main.cxx -o run -L. -lfoo -lbar conflict.o:(.bss+0x0): multiple definition of `conflict' conflict.o:(.bss+0x0): first defined here collect2: error: ld returned 1 exit status 链接两个不同的DSO,并将这两个DSO链接到应用程序中,而不会出现链接编辑器中的错误。

ABI注释解决了类似的问题,但我认为这些问题需要进行特定的-lfoo更改。

如果运行时检查是可接受的,则可以使用每个DSO定义的弱符号列表,这些符号需要冲突,并实现ELF构造函数,如果定义了多个DSO,则会中止该过程。我不知道在静态链接时使用GNU工具链可以获得同样可靠的东西。也许这值得针对binutils的RFE错误。

答案 1 :(得分:1)

我认为解决问题的唯一正确方法是将bar.a解决方案的内容移动到某个共享库,从而解决钻石继承问题......但我认为这超出了这个问题的范围。 / p>

我唯一能想到的就是在链接最终可执行文件之前创建“自定义”验证程序。像这样:

nm *.so | \ # get all symbols
    grep " [B|T] " | \ # only exported ones
    egrep -v "__bss_start|_end|_fini|_init" | \ # filter out some commons, probably to be extended
    awk '{print $3}' | \ # get only symbol name
    sort | uniq -c | \ # sort and count
    egrep -v "^[ ]+1 " # get only those that have multiple definitions

这将打印多次定义的库中的所有强(导出)符号。您可以轻松地将其包装在脚本中,如果输出不为空且有意义的消息,则返回错误状态代码。

修补后的makefile的实验版本如下所示:

run: main.cxx foo.so bar.so
    ! nm foo.so bar.so | grep " [B|T] " | egrep -v "__bss_start|_end|_fini|_init" | awk '{print $3}' | sort | uniq -c | egrep -v "^[ ]+1 "
    g++ -std=c++11 $^ -o $@

(注意!反转最终grep的退出代码,该代码搜索任何不以裸1开头的uniq -c输出

我知道这真的是一种黑客,丑陋和不便携的解决方案,但我认为它可能在像你这样的角落里有一些价值。