Macos 为什么可以覆盖某些静态库中的符号,而不覆盖其他库中的符号?
我正在开发一个链接到Clang的工具,我需要对一些操作进行少量更改。为了缩短开发时间,而不是重建Clang,我决定在程序代码中重新定义感兴趣的符号,并让链接器处理其余部分:,在程序代码和静态库中定义的符号的程序版本在链接时优先。(链接的答案与Linux有关,但我发现这也适用于macOS——通常情况下) 这在我使用macOS的stock Clang构建时非常有效,可以从LLVM网站下载。然而,我目前正在尝试切换到我公司的定制Clang(我曾经从源代码构建过它,并希望以同样的方式进一步修改),现在我得到了重复的符号错误 我不知道是什么导致了这个问题。我的项目的链接器标志保持不变(除了一个新的静态库):重要的是,它们不包含Macos 为什么可以覆盖某些静态库中的符号,而不覆盖其他库中的符号?,macos,linker,Macos,Linker,我正在开发一个链接到Clang的工具,我需要对一些操作进行少量更改。为了缩短开发时间,而不是重建Clang,我决定在程序代码中重新定义感兴趣的符号,并让链接器处理其余部分:,在程序代码和静态库中定义的符号的程序版本在链接时优先。(链接的答案与Linux有关,但我发现这也适用于macOS——通常情况下) 这在我使用macOS的stock Clang构建时非常有效,可以从LLVM网站下载。然而,我目前正在尝试切换到我公司的定制Clang(我曾经从源代码构建过它,并希望以同样的方式进一步修改),现在我
-all\u load
或其-force\u 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
检查这两个档案,我会得到非常相似的结果
# 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
因此,假设符号定义或多或少相同,链接器标志也相同,为什么我以前能够以这种方式覆盖静态库符号,为什么我不再能够覆盖静态库符号?前提的这一部分不太正确:
,在程序代码和静态库中定义的符号的程序版本在链接时优先,不会引起混乱。(链接的答案与Linux有关,但我发现这也适用于macOS——通常情况下)
链接的答案似乎正确,但我最初误解了它。通过将-Wl,-why_load
传递给Clang(或-why_load
传递给链接器)可以证明实际行为如下:
如果引用了符号,请尝试在程序代码中查找其定义。
- 如果它是在程序代码中定义的,那么就完成了;不要搜索静态库
如果未在程序代码中定义,请在静态库中查找\u SYMDEF
文件,以了解哪个对象文件包含它
使用该对象文件中的所有定义
问题是,切换到自定义Clang时,我意外地引入了对与我正在重新定义的符号在同一个对象文件中定义的符号的引用,导致链接器同时看到这两个定义。我可以通过使用链接器的-why_load
参数来解决这个问题,然后查找导致加载问题对象文件的符号。然后我将该符号的定义复制到我的程序中,现在链接器不再抱怨了
这个故事的寓意是,这种技术在macOS上不如在Linux上那么可靠,如果你这样做,你就必须全力以赴。最好将整个源文件复制到您的项目中,而不是尝试分段拾取符号。实际上,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
-错误的多个定义,因为根本没有使用来自对象文件error\u 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
,因此将同时获取对象文件.o
,但它也包含重复的符号main
,因此链接器发出错误
链接器如何在Linux上工作(在MacOS上非常类似)的精彩描述如下。感谢您的澄清!