gcc 编译出 elf,和 ld 编译出 elf,真的有区别吗?

190 天前
 amiwrong123

先说下我的场景,我是嵌入式纯裸机编程的,先是 gcc 编译出来各个 o 文件,然后用 ld 链接出 elf ,再把 elf 通过 objcopy 转成 bin 文件,这个 bin 文件就是最终用来裸机直接启动。另外,就是 ld 脚本,Makefile 脚本都需要自己手写。

但是我看了其他项目的代码,发现有的会用 gcc 来生成 elf ,只是会通过-Wl,来传递链接器的参数给 ld 而已。

目前发现了有一个差异点,对于链接 nosys 时,gcc 和 ld 是不一样的。那就是:

  1. 总之,现在就感觉 gcc 和 ld ,都可以编译出来 elf ,这两个编译出来的东西真的有差别吗?
  2. 或者说,你们有遇到过什么坑,本来是用 ld 来编译的,结果发现某个场景或需求必须用 gcc 编译才能解决的?或者反过来
2073 次点击
所在节点    程序员
11 条回复
diivL
190 天前
gcc 调用的也是 ld, 差别就是 gcc 调用的 ld 时附带了一些参数, 手动调用 ld 把参数设置成和 gcc 一样,结果应该是一样的
mahaoqu
190 天前
GCC 命令后面加 -### 就知道它如何调用后面的阶段了。一般来说 GCC 会用 collect2 这个程序间接调用 ld 。

-specs= 项是 GCC 用于调用其各个组件的模板,ld 肯定识别不了。输入 gcc -dumpspecs ,里面 *link 行的内容就是用来组成对 collect2 的调用的参数的。
seers
189 天前
gcc 是 Collection ,后面还是 as 和 ld
sir283
189 天前
gcc 内部调用了 ld ,省去了你手动的步骤,直接用 ld ,就是你自己把控步骤流程。
amiwrong123
189 天前
@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 命令当然还有其他参数,但其他的都不影响报错,我就忽略了。

虽然问题已经解决了,但是还是感觉不可思议,可以解下惑吗
diivL
188 天前
ld 搜索符号的时候只会向后搜索,之前处理后的文件就不在搜索了 -start-group -end-group 中间的文件会被重复搜索。但我看文档应该之适用库,object 文件应该每这个限制。所以你应该把完整的命令参数发出来,并把报错信息也贴出来,才能更准确的确定问题。
mahaoqu
187 天前
@amiwrong123 .o 文件一定要在库文件的前面,因为 ld 是默认单向搜索的。可以参考下《深入理解计算机系统》讲链接的部分。
amiwrong123
185 天前
@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

但上面这些现象的原因我还是很懵,各位大佬有空可以帮忙看看。现在我先解决问题,后面我也研究研究。
amiwrong123
185 天前
先介绍下背景:不管是用 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
mahaoqu
185 天前
@amiwrong123 我没做过 ARM ,但是简单搜了下发现确实有不少门道,这里有篇文章详细解释了两个 specs 文件都是什么意思: https://metebalci.com/blog/demystifying-arm-gnu-toolchain-specs-nano-and-nosys/

实际上我觉得你问 AI 和搜索得到结果更快。总之大概的意思是,gcc 会链接 newlib-nano ,ld 默认情况下不会,所以你还是用 GCC 吧,除非你能够精确地知道 ld 具体该怎么写。
amiwrong123
185 天前
@mahaoqu #10
是的,我决定使用 gcc 了。ld 感觉是要底层一点,有些东西还是让 gcc 帮我处理吧。

门道确实不少,我刚才自己对比了, 各种情况下的 mapfile(-Map 参数)发现里面找到的 libxxx.a 的 名字和路径 都不一样。而且这个 mapfile 内容很多,我只看了一点点,后面有空再慢慢研究了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1114717

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX