如何让GNU链接器仅从指定静态库查找当前未解析符号?
我在一个被主程序和独立库共同使用的现有src文件中新增了部分符号,这些新增符号属于主程序,因此被放在一个包含多来源符号的静态库lib3.a中。现在要让独立库能包含这些新增符号的定义,尝试让ld链接lib3.a,但该库包含大量外部符号,无法引入所有依赖。
此前的链接配置为:
EXTRA_LIBS := $(addprefix $(LOC_TOOLS)/foo/bar/, lib1.a lib2.a)
想请教:
- 是否有ld选项能实现仅从指定库(如lib3.a)查找当前未解析的符号(即本次新增的符号)?
- 或者是否需要在单独的dummy源文件中定义未使用的未解析符号再进行链接?
编辑1
查阅资料后我认为ld没有此类“部分提取”功能,但有两个疑问:
- 为何GNU不为静态库提供类似共享库的符号按需查找功能?是否与PIC(位置无关代码)有关?
- 有观点称链接器对链接对象进行单遍处理并提取所有符号,但我遇到两处异常:
- 假设lib1和lib2包含所有必要符号(此前链接正常),在lib2的src文件中添加一个lib1中已定义的符号并重新编译,理论上无需修改配置,因为链接器已提取lib1的符号,但实际出现新增符号未定义的错误。
- 针对上述例子,在lib2后再次追加lib1,配置改为:
却出现lib1.a中大量符号未定义的错误,按道理依赖库的所有符号已被提取,不应出现该问题。EXTRA_LIBS := $(addprefix $(LOC_TOOLS)/foo/bar/, lib1.a lib2.a lib1.a)
编辑2
感谢解答与评论,现在仅需确认一个核心细节:
链接器是提取待链接静态库的所有符号定义,还是仅提取当前所需的符号定义?
能否提供GNU官方文档中相关行为的中文说明?
关于ld是否支持仅从指定库查找未解析符号
GNU ld没有直接支持“仅从某指定静态库查找当前未解析符号”的选项。静态库的链接逻辑是:链接器按命令行顺序处理输入文件,遇到静态库时,只会提取当前已存在未引用符号所需的目标文件,而非整个库。如果要让lib3.a仅为lib2.a的未解析符号提供定义,需要调整链接顺序,把lib3.a放在lib2.a之后,但这无法避免lib3.a中其他依赖被触发(如果其提供的目标文件本身有未解析符号)。
替代方案:
- 从lib3.a中单独提取包含新增符号的目标文件,将该目标文件直接参与链接,而非整个静态库。可以用
ar x lib3.a target_file.o提取目标文件,再将target_file.o加入链接列表。 - 如果无法单独提取目标文件,可以编写一个dummy源文件,仅声明并引用新增符号,将该dummy文件编译后与lib3.a一起链接,此时链接器只会提取lib3.a中与新增符号相关的目标文件。
为何静态库没有类似共享库的按需查找功能?
这与静态库和共享库的设计本质有关,和PIC无直接关联:
- 共享库是在运行时动态加载的,符号查找是延迟到运行时进行,且共享库本身是完整的可加载单元,所有符号都已处理为PIC形式,运行时可以按需解析。
- 静态库本质是目标文件的归档,链接器处理静态库时是一次性、按顺序的静态分析,仅解决当前已存在的未解析符号,不会提前处理后续可能出现的符号需求,也不会保留整个库的符号索引供后续查找。
链接器处理静态库的符号提取规则(核心细节)
GNU ld处理静态库时,仅提取当前链接阶段中未解析符号所需的目标文件,而非整个静态库的所有符号。
GNU ld官方文档的相关说明翻译如下:
当链接器处理静态库(.a文件)时,它会检查库中的每个目标文件,判断该文件是否包含能解决当前未定义符号的定义。如果包含,链接器会将该目标文件加入链接集合,然后重新检查所有未定义符号,看是否有新的符号能被刚加入的目标文件解决。这个过程会重复,直到没有新的目标文件被加入或所有未定义符号都被解决。如果库中的目标文件没有解决任何当前未定义符号,链接器会忽略该目标文件,不会将其加入最终的可执行文件或库中。
针对你遇到的异常场景解释:
- 在lib2中新增引用lib1的符号后出现未定义错误:链接器按顺序处理
lib1.a→lib2.a,处理lib1.a时,当时还没有来自lib2.a的未解析符号需求,所以lib1中对应符号的目标文件没有被提取;后续处理lib2.a时出现新的未解析符号,但链接器不会回头重新处理之前的lib1.a,因此报错。 - 追加
lib1.a后出现大量符号未定义:第二次出现的lib1.a会被链接器重新处理,此时如果lib1中的某些目标文件被提取,但这些目标文件本身依赖其他未被链接的符号(比如主程序中的符号),就会出现大量未定义错误——因为第一次处理lib1.a时只提取了满足当时需求的目标文件,第二次处理时提取的新目标文件可能带有未解决的依赖。
内容的提问来源于stack exchange,提问作者PkDrew

