我正在开发一个与Clang链接的工具,我需要对某些操作进行少量更改。为了缩短开发时间,我决定不重新构建Clang,而是决定在程序代码中重新定义感兴趣的符号,然后让链接程序处理其余的操作:in most cases,这两个符号中都定义了该符号的程序版本程序代码和静态库在链接时优先考虑,而不必大惊小怪。 (链接的答案与Linux有关,但我发现它通常也可以在macOS上工作。)
当我使用可从LLVM网站下载的适用于macOS的通用Clang版本时,此功能非常有用。但是,我目前正在尝试切换到公司自定义的Clang(我从源代码构建了一次,希望以相同的方式进行进一步的修改),现在我遇到了重复的符号错误。
我不知道是什么引起了这个问题。我项目的链接器标记保持不变(为一个新的静态库保存):重要的是,它们不包含-all_load
或它的-force_load
表哥,这告诉链接器尝试包括静态库中定义的每个符号。当我在库存档案库和自定义档案库中使用nm
检查它们时,试图覆盖的符号以相同的方式定义。区别必须在于我构建LLVM的方式,但是仅仅知道这并不能真正帮助我确定需要更改的内容。
例如,说我想重新定义clang::Qualifiers::getAsString() const
。使用现有的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
因此,假设或多或少相同的符号定义和相同的链接器标志,为什么我以前能够以这种方式覆盖静态库符号,为什么我不再能够?
答案 0 :(得分:1)
前提的这一部分不太正确:
In most cases(在程序代码和静态库中定义的符号的程序版本)在链接时具有优先权,而不必大惊小怪。 (链接的答案与Linux有关,但我发现它通常也可以在macOS上工作。)
链接的答案似乎正确,但是我最初误会了它。通过将-Wl,-why_load
传递给Clang(或将-why_load
传递给链接器)可以证明实际行为如下:
.__SYMDEF
文件,以了解哪个目标文件具有该文件。问题是,切换到自定义Clang时,我不小心拉了对与我要重新定义的符号在同一目标文件中定义的符号的引用,从而使链接程序看到了这两个定义。我可以通过使用链接器的-why_load
参数来解决该问题,然后查找哪个符号导致问题对象文件被加载。然后,我在程序中复制了该符号的定义,现在链接程序不再抱怨了。
这个故事的士气在于,这种技术在macOS上不如在Linux上可靠,并且,如果这样做,则必须全神贯注。最好将整个源文件和将其复制到您的项目中,而不是尝试逐段选择符号。
答案 1 :(得分:1)
实际上,这种行为在Linux中是相同的,请参见此复制器:
第一种情况:构建库,其中符号位于不同的对象文件中:
Children
与此库链接有效,不会发出Human
-错误的多重定义,因为目标文件//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
,因此链接程序会发出错误消息。
this article是对链接程序在Linux上如何工作(在MacOS上非常相似)的很好描述。