间接使用GNU链接器链接失败:ld成功g++报错问题咨询
问题原因分析与解决办法
当你用arm-none-eabi-g++替代直接调用ld时,核心问题在于g作为C编译链的前端工具,会自动注入一系列默认的启动目标文件和链接规则,而这些是直接调用ld时不会自动添加的,这就导致了两个报错:
1. 多重定义__dso_handle
- g默认会链接
crtbegin.o——这个文件是GCC用来处理C全局对象的构造/析构(动态初始化)的辅助模块,它内部已经定义了__dso_handle符号。 - 你的代码中(具体是
main.cpp里的std::__ioinit相关逻辑)也生成了__dso_handle的定义,两者冲突。而直接调用ld时你没有手动添加crtbegin.o,所以不会触发这个重复定义问题。
2. 未定义__bss_start__和__bss_end__
- g++还会自动链接
crt0.o——这是标准C的启动文件,包含程序的入口_start函数,这个函数依赖__bss_start__和__bss_end__这两个符号来完成BSS段的清零初始化。 - 但你的自定义链接脚本
game.ld里应该只定义了常规的__bss_start和__bss_end(或者_bss_start/_bss_end),符号名和crt0.o期望的不匹配,导致链接时找不到。直接调用ld时你没加crt0.o,自然不会有这个依赖问题。
解决办法
如果你想继续用g++来完成链接(好处是它能自动处理C的一些链接依赖逻辑),最简单的方式是**禁用g自动添加的启动文件**,添加-nostartfiles选项即可:
arm-none-eabi-g++ -nostartfiles -L/home/th/opt/arm-none-eabi/lib/ -L/home/th/opt/lib/gcc/arm-none-eabi/8.2.0/ -T /home/th/autoreverse/game/game.ld main.cpp.obj init.cpp.obj syscalls.cpp.obj queue.cpp.obj echange.cpp.obj echangeur.cpp.obj startup.s.obj -lstdc++ -lc -lgcc -o game.elf
这个选项会让g++表现得和直接调用ld类似,只链接你明确指定的目标文件和库,不会自动注入crt0.o、crtbegin.o这些启动模块——而你已经提供了自己的startup.s.obj,完全不需要依赖默认的启动文件。
如果你的场景确实需要依赖crt0.o的启动逻辑,那就要修改game.ld链接脚本,添加符号别名:
__bss_start__ = __bss_start; __bss_end__ = __bss_end;
让crt0.o能找到它需要的符号,但嵌入式开发中一般更推荐使用自定义的启动文件,所以第一种方法更常用。
内容的提问来源于stack exchange,提问作者bitmap.kid
相关产品推荐
相关产品推荐

