代码之家  ›  专栏  ›  技术社区  ›  zneak

为什么可以重写某些静态库中的符号,而不能重写其他静态库中的符号?

  •  1
  • zneak  · 技术社区  · 6 年前

    我正在开发一个与Clang相连接的工具,我需要对一些操作实现一些更改。为了缩短开发时间,我决定重新定义程序代码中感兴趣的符号,而不是重新构建Clang,并让链接器来处理其余的问题: in most cases

    当我使用可以从LLVM网站下载的stock Clang build for macOS时,这非常有效。不过,我目前正在尝试切换到我公司的定制Clang(我从源代码创建了一次,希望以同样的方式进一步修改),现在我得到了重复的符号错误。

    我不知道是什么引起了这个问题。我的项目的链接器标志保持不变(除了一个新的静态库):重要的是,它们不包含 -all_load 或者它的 -force_load 它告诉链接器尝试包含静态库中定义的每个符号。我试图覆盖的符号的定义方式与我检查它们时相同 nm 在库存档案和定制档案中。不同之处在于我是如何构建LLVM的,但仅仅知道这一点并不能真正帮助我找出需要改变的地方。

    clang::Qualifiers::getAsString() const . 我可以使用stock LLVM库做得很好,但是现在我会得到一个重复的符号错误:

    duplicate symbol __ZNK5clang10Qualifiers11getAsStringEv in:
      .../Objects-normal/x86_64/TypePrinter.o
      clang+llvm-internal/lib/libclangAST.a(TypePrinter.cpp.o)
    

    nm -f darwin 为了检查两个档案,我会得到非常相似的结果 __ZNK5clang10Qualifiers11getAsStringEv

    # clang+llvm-6.0.0/lib/libclangAST.a
                     (undefined) external __ZNK5clang10Qualifiers11getAsStringEv
    0000000000000bb0 (__TEXT,__text) external __ZNK5clang10Qualifiers11getAsStringEv
    
    
    # clang+llvm-internal/lib/libclangAST.a
                     (undefined) external __ZNK5clang10Qualifiers11getAsStringEv
    0000000000000d00 (__TEXT,__text) external __ZNK5clang10Qualifiers11getAsStringEv
    

    所以,假设有或多或少相同的符号定义和相同的链接器标志,为什么我以前可以这样重写静态库符号,为什么我现在不能了?

    2 回复  |  直到 6 年前
        1
  •  1
  •   zneak    6 年前

    这部分前提并不完全正确:

    In most cases ,在程序代码和静态库中定义的符号的程序版本在链接时具有优先权,无需大惊小怪。(链接的答案与Linux有关,但我发现通常在macOS上工作。)

    -Wl,-why_load 叮当 -why_load 链接器),如下所示:

      • 如果它是在程序代码中定义的,那么就完成了;不要搜索静态库。
    1. 如果程序代码中没有定义它,请查找 .__SYMDEF 静态库中的文件,以了解哪个对象文件具有它。
    2. 全部的 对象文件中的定义。

    -为什么加载 参数,然后查找导致加载问题对象文件的符号。然后我把这个符号的定义复制到我的程序中,现在链接器不再抱怨了。

    这个故事的精神是,这种技术在macOS上不如在Linux上那么可靠,如果你这么做了,你就必须全力以赴。最好是将整个源文件复制到项目中,而不是尝试逐段选择符号。

        2
  •  1
  •   ead    6 年前

    实际上,Linux的这种行为是相同的,请参阅以下复制程序:

    第一种情况:

    //val.cpp  - contains needed symbol
    int val=42;
    
    //wrong_main.cpp - contains duplicate symbol
    int main(){
       return 21;
    }
    
    >>> g++ -c val.cpp -o val.o
    >>> g++ -c wrong_main.cpp -o wrong.o
    >>> ar rcs libsingle.a val.o wrong.o
    

    链接到此库有效,没有多重定义 main -发出错误,因为根本没有使用对象文件中的符号 wrong_main.o

    //main.cpp
    extern int val;
    int main(){
       return val;
    } 
    
    >>> g++ main.cpp -L. -lsingle -o works 
    

    第二种情况:

    //together.cpp  - contains both, needed and duplicate, symbols
    #include "val.cpp"
    #include "wrong_main.cpp"
    
    >>> g++ -c together.cpp -o together.o
    >>> ar rcs libtogether.a all.o
    

    链接到 libtogether.a 不起作用:

    >>> g++ main.cpp -L. -ltogether -o doesntwork
    ./libtogether.a(all.o): In function `main':
    all.cpp:(.text+0x0): multiple definition of `main'
    /tmp/cc38isDb.o:main.cpp:(.text+0x0): first defined here
    collect2: ld returned 1 exit status
    

    val 是必需的,因此对象文件 together.o 将被获取,但它也包含重复的符号 因此链接器会发出一个错误。

    一个很好的关于链接器在Linux上工作的描述是 this article