You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

交叉编译Cortex-A72项目时Libc被移出循环依赖组导致链接错误的问题咨询

交叉编译Cortex-A72项目时Libc被移出循环依赖组导致链接错误的问题咨询

你好,我来帮你分析这个在交叉编译Cortex-A72项目时遇到的链接错误问题,先理清楚你的场景和核心矛盾:

你的项目是一个极简的C++嵌入式程序,自己实现了嵌入式环境必需的系统调用桩函数(比如_close、_lseek),但链接时出现了符号未定义的报错,查看实际链接命令发现你显式放在--start-group/--end-group里的-lc被移出了这个循环依赖组,这才导致了问题。

先把你的项目代码整理一下方便参考:

项目文件内容

main.cpp

int main() { return 0; }

syscalls.cpp

#include <sys/types.h>
extern "C" {
caddr_t _sbrk (intptr_t) { return (caddr_t)0; }
int _close(int) { return -1; }
int _write (int, char*, int) { return 0; }
int _lseek(int, int, int) { return 0; }
int _read (int, char*, int) { return 0; }
void _exit (int) { while(1); }
}

原始Makefile

CC = aarch64-none-elf-g++
LIBS = -lc -lgcc -lstdc++

all: myexe.elf

%.o: %.cpp
	$(CC) -std=gnu++20 -c $<

syscalls.a: syscalls.o
	ar -rsc syscalls.a syscalls.o

myexe.elf: main.o syscalls.a
	$(CC) -Wl,--start-group $^ $(LIBS) -Wl,--end-group -o $@

clean:
	rm -rf myexe.elf *.o *.a

.PHONY: all clean

问题现象

链接时出现类似如下的符号未定义错误:

.../libc.a(libc_a-lseekr.o): in function `_lseek_r':
(.text+0x28): undefined reference to `_lseek'
.../libc.a(libc_a-closer.o): in function `_close_r':
(.text+0x20): undefined reference to `_close'

而查看实际执行的链接命令,发现-lc被移出了--start-group/--end-group,变成了:

collect2 ... --start-group main.o syscalls.a -lgcc -lstdc++ --end-group -lstdc++ -lm -lgcc -lc -lgcc

现在来解答你的两个核心疑问:


1. 为什么显式包含的-lc会被移出--start-group/--end-group?

这是GCC交叉编译器驱动程序的内部逻辑导致的,你用的aarch64-none-elf-g++并不是直接把你写的链接选项原封不动传给链接器,它会对选项做「智能」处理:排序、过滤、调整标准库的位置。

对于嵌入式交叉编译器来说,它默认认为标准库(比如libc.a)的依赖关系是明确的,不需要参与循环扫描(--start-group/--end-group的作用就是让链接器反复扫描组内文件,直到没有未定义符号),所以会主动把-lc这类标准库选项从循环依赖组中提取出来,放到组的最后面扫描。

但你的场景是特殊的:你自己实现的syscalls.a提供了标准库需要的符号,形成了反向依赖(标准库调用你的代码),而GCC驱动的默认处理忽略了这种反向依赖,导致-lc被移到组外后,链接器无法回头找到你实现的符号。

你可以在链接命令中加-v选项(比如$(CC) -v -Wl,--start-group ...),就能看到GCC驱动是如何一步步修改你的链接选项的。


2. 为什么重复加-lc就可以成功?

当你在--start-group里写两次-lc时,GCC驱动的处理逻辑只会移除其中一个到组外,剩下的一个-lc会留在组内。

这样链接器在处理循环依赖组时,会反复扫描组内的syscalls.a和-lc:

  • 第一次扫描syscalls.a时,记录下你实现的_close、_lseek等符号;
  • 扫描组内的-lc时,发现它需要这些符号;
  • 因为在--start-group内,链接器会回头重新扫描组内文件,这次就找到了syscalls.a里的实现,成功解析依赖。

组外的那个-lc此时已经不会造成问题,因为需要的符号已经被组内的扫描解决了。


更可靠的解决方法

依赖重复加-lc的方法虽然能临时解决问题,但不够优雅,推荐这几种更稳妥的方案:

方案1:强制让-lc留在循环依赖组内

直接把-lc写在--start-group的内部,并且避免用变量间接传递,让GCC驱动无法轻易调整它的位置:

myexe.elf: main.o syscalls.a
	$(CC) -Wl,--start-group $^ -lc -lgcc -lstdc++ -Wl,--end-group -o $@

方案2:用-nostdlib完全掌控链接顺序

添加-nostdlib选项,禁止GCC自动添加标准库,手动指定所有需要的库,确保syscalls.a和-lc都在循环依赖组内:

myexe.elf: main.o syscalls.a
	$(CC) -nostdlib -Wl,--start-group $^ -lc -lgcc -lstdc++ -Wl,--end-group -o $@

方案3:用--whole-archive暴露所有自定义符号

强制链接器包含syscalls.a的所有符号,这样链接器在扫描main.o时就能提前看到你的系统调用实现,之后扫描-lc时就不会有未定义符号了,甚至不需要用--start-group/--end-group:

myexe.elf: main.o syscalls.a
	$(CC) -Wl,--whole-archive syscalls.a -Wl,--no-whole-archive main.o $(LIBS) -o $@

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 10:53:10