先说下我的场景,我是嵌入式纯裸机编程的,先是 gcc 编译出来各个 o 文件,然后用 ld 链接出 elf ,再把 elf 通过 objcopy 转成 bin 文件,这个 bin 文件就是最终用来裸机直接启动。另外,就是 ld 脚本,Makefile 脚本都需要自己手写。
但是我看了其他项目的代码,发现有的会用 gcc 来生成 elf ,只是会通过-Wl,
来传递链接器的参数给 ld 而已。
目前发现了有一个差异点,对于链接 nosys 时,gcc 和 ld 是不一样的。那就是:
1
diivL 32 天前 ![]() gcc 调用的也是 ld, 差别就是 gcc 调用的 ld 时附带了一些参数, 手动调用 ld 把参数设置成和 gcc 一样,结果应该是一样的
|
2
mahaoqu 32 天前 ![]() GCC 命令后面加 -### 就知道它如何调用后面的阶段了。一般来说 GCC 会用 collect2 这个程序间接调用 ld 。
-specs= 项是 GCC 用于调用其各个组件的模板,ld 肯定识别不了。输入 gcc -dumpspecs ,里面 *link 行的内容就是用来组成对 collect2 的调用的参数的。 |
![]() |
3
seers 32 天前
gcc 是 Collection ,后面还是 as 和 ld
|
4
sir283 31 天前 via Android
gcc 内部调用了 ld ,省去了你手动的步骤,直接用 ld ,就是你自己把控步骤流程。
|
![]() |
5
amiwrong123 OP @diivL #1
@mahaoqu #2 @seers #3 @sir283 #4 各位大佬,又遇到了一个奇怪的问题。还是我帖子说的这种场景,我用 ld 编译的时候,会报错奇怪的 undefined reference to 。 https://blog.csdn.net/qq_24341965/article/details/73928434 我根据这篇博客的灵感,我就 调整命令参数之间的顺序,发现了如下规律: - $(OBJECTS)是 Makefile 变量,是多个 o 文件空格隔开 - ld 命令后,如果是$(OBJECTS) --start-group -lnosys -lc -lgcc --end-group ,就不会报错 undefined reference to - ld 命令后,如果是--start-group -lnosys -lc -lgcc --end-group $(OBJECTS),就会报错 undefined reference to ld 命令当然还有其他参数,但其他的都不影响报错,我就忽略了。 虽然问题已经解决了,但是还是感觉不可思议,可以解下惑吗 |
6
diivL 30 天前
ld 搜索符号的时候只会向后搜索,之前处理后的文件就不在搜索了 -start-group -end-group 中间的文件会被重复搜索。但我看文档应该之适用库,object 文件应该每这个限制。所以你应该把完整的命令参数发出来,并把报错信息也贴出来,才能更准确的确定问题。
|
7
mahaoqu 30 天前
@amiwrong123 .o 文件一定要在库文件的前面,因为 ld 是默认单向搜索的。可以参考下《深入理解计算机系统》讲链接的部分。
|
![]() |
8
amiwrong123 OP @diivL #1
@mahaoqu #2 @seers #3 @sir283 #4 感觉之前的回答啦,现在又遇到了一个问题: 先介绍下背景:不管是用 ld 还是 gcc ,都有这些参数-nostartfiles -nostdlib -static --start-group -lnosys -specs=nosys.specs -specs=nano.specs -lgcc -lc --end-group -gc-sections 首先还是用 ld 来进行编译,编译能够完成,但会报一些链接器的警告( https://stackoverflow.com/questions/73742774/gcc-arm-none-eabi-11-3-is-not-implemented-and-will-always-fail 类似这个链接里的报的警告): warning: _close is not implemented and will always fail ,还有_fstat _getpid _satty _kill _lseek _read _write 。 然后我又去查看了反汇编,发现里面有了一些我不想要的东西,居然还有什么 raise 函数、raise_r 函数的代码,还有一些其他的函数的代码,即使我都没有进行调用的。而且吧,我是加了-gc-sections ,居然还是没有 gc 掉。 然后我换成 gcc 来编译,还是携带相同的参数,ld 的参数,我就用,Wl 来传递。还是发现会报这些警告。 PS:我发现 gcc 有一个方便的地方,就是不像 ld 必须要使用-L 参数指定搜索库的路径。gcc 可以不指定这些路径,它自己会去找。 最终,我使用 gcc 并携带了--specs=nosys.specs --specs=nano.specs ,发现终于不报这些警告了。而且查看 dis 文件,发现也干净了很多,没有 raise 函数(以及其他的乱七八糟没用到的函数)的汇编在里面了。但是呢,却发现多了一些奇怪的 section (从 dis 文件里看):.bss.__malloc_free_list .bss.__malloc_sbrk_start .bss.errno .bss.__lock__malloc_recursive_mutex 最终,我因为不想看到这些烦人的警告,还是选择了这个命令 gcc -nostartfiles -nostdlib -static -Wl,--start-group -lnosys -specs=nosys.specs -specs=nano.specs -lgcc -lc -Wl,--end-group -Wl,-gc-sections 但上面这些现象的原因我还是很懵,各位大佬有空可以帮忙看看。现在我先解决问题,后面我也研究研究。 |
![]() |
9
amiwrong123 OP 先介绍下背景:不管是用 ld 还是 gcc ,都有这些参数-nostartfiles -nostdlib -static --start-group -lnosys -specs=nosys.specs -specs=nano.specs -lgcc -lc --end-group -gc-sections
----------- 这段话修正一下: 如果是 ld ,则携带-nostartfiles -nostdlib -static --start-group -lnosy -lgcc -lc --end-group -gc-sections 如果是 gcc ,则携带-nostartfiles -nostdlib -static --start-group -lnosys -specs=nosys.specs -specs=nano.specs -lgcc -lc --end-group -gc-sections |
10
mahaoqu 27 天前
@amiwrong123 我没做过 ARM ,但是简单搜了下发现确实有不少门道,这里有篇文章详细解释了两个 specs 文件都是什么意思: https://metebalci.com/blog/demystifying-arm-gnu-toolchain-specs-nano-and-nosys/
实际上我觉得你问 AI 和搜索得到结果更快。总之大概的意思是,gcc 会链接 newlib-nano ,ld 默认情况下不会,所以你还是用 GCC 吧,除非你能够精确地知道 ld 具体该怎么写。 |
![]() |
11
amiwrong123 OP @mahaoqu #10
是的,我决定使用 gcc 了。ld 感觉是要底层一点,有些东西还是让 gcc 帮我处理吧。 门道确实不少,我刚才自己对比了, 各种情况下的 mapfile(-Map 参数)发现里面找到的 libxxx.a 的 名字和路径 都不一样。而且这个 mapfile 内容很多,我只看了一点点,后面有空再慢慢研究了。 |