V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
amiwrong123
V2EX  ›  程序员

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

  •  
  •   amiwrong123 · 32 天前 · 1842 次点击
    这是一个创建于 32 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    • gcc -Wl,--start-group -lnosys -specs=nosys.specs -specs=nano.specs -Wl,--end-group 。gcc 可以多携带两个参数。
    • ld -lnosys 。因为 ld 不识别-specs=nosys.specs -specs=nano.specs 这两个参数,所以 ld 只能写成 ld -lnosys
    1. 总之,现在就感觉 gcc 和 ld ,都可以编译出来 elf ,这两个编译出来的东西真的有差别吗?
    2. 或者说,你们有遇到过什么坑,本来是用 ld 来编译的,结果发现某个场景或需求必须用 gcc 编译才能解决的?或者反过来
    11 条回复    2025-03-04 21:09:00 +08:00
    diivL
        1
    diivL  
       32 天前   ❤️ 2
    gcc 调用的也是 ld, 差别就是 gcc 调用的 ld 时附带了一些参数, 手动调用 ld 把参数设置成和 gcc 一样,结果应该是一样的
    mahaoqu
        2
    mahaoqu  
       32 天前   ❤️ 2
    GCC 命令后面加 -### 就知道它如何调用后面的阶段了。一般来说 GCC 会用 collect2 这个程序间接调用 ld 。

    -specs= 项是 GCC 用于调用其各个组件的模板,ld 肯定识别不了。输入 gcc -dumpspecs ,里面 *link 行的内容就是用来组成对 collect2 的调用的参数的。
    seers
        3
    seers  
       32 天前
    gcc 是 Collection ,后面还是 as 和 ld
    sir283
        4
    sir283  
       31 天前 via Android
    gcc 内部调用了 ld ,省去了你手动的步骤,直接用 ld ,就是你自己把控步骤流程。
    amiwrong123
        5
    amiwrong123  
    OP
       31 天前
    @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
        6
    diivL  
       30 天前
    ld 搜索符号的时候只会向后搜索,之前处理后的文件就不在搜索了 -start-group -end-group 中间的文件会被重复搜索。但我看文档应该之适用库,object 文件应该每这个限制。所以你应该把完整的命令参数发出来,并把报错信息也贴出来,才能更准确的确定问题。
    mahaoqu
        7
    mahaoqu  
       30 天前
    @amiwrong123 .o 文件一定要在库文件的前面,因为 ld 是默认单向搜索的。可以参考下《深入理解计算机系统》讲链接的部分。
    amiwrong123
        8
    amiwrong123  
    OP
       27 天前
    @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
        9
    amiwrong123  
    OP
       27 天前
    先介绍下背景:不管是用 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
        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 具体该怎么写。
    amiwrong123
        11
    amiwrong123  
    OP
       27 天前
    @mahaoqu #10
    是的,我决定使用 gcc 了。ld 感觉是要底层一点,有些东西还是让 gcc 帮我处理吧。

    门道确实不少,我刚才自己对比了, 各种情况下的 mapfile(-Map 参数)发现里面找到的 libxxx.a 的 名字和路径 都不一样。而且这个 mapfile 内容很多,我只看了一点点,后面有空再慢慢研究了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1132 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:15 · PVG 02:15 · LAX 11:15 · JFK 14:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.