【问题标题】:Undefined references to symbols in standard libraries with musl-cross-make使用 musl-cross-make 对标准库中符号的未定义引用
【发布时间】:2023-04-02 02:09:02
【问题描述】:

我正在为 arm 使用 musl-targeting 交叉编译器,使用 musl-cross-make (gcc 9.2.0, musl 1.2.0) 构建。当我用 printf 编译一个简单的 hello world c 程序时,我得到标准库中符号的未定义引用:

cross-musl/bin/arm-linux-musleabihf-gcc -c hello.c -o hello.o
cross-musl/bin/arm-linux-musleabihf-ld  hello.o -o hello.elf
cross-musl/bin/arm-linux-musleabihf-ld: warning: cannot find entry symbol _start; defaulting to 0000000000010074
cross-musl/bin/arm-linux-musleabihf-ld: hello.o: in function `main':
hello.c:(.text+0x18): undefined reference to `puts'

当我将 libc.a 和 crt1.o 添加到链接器命令时,我没有收到任何错误:

cross-musl/bin/arm-linux-musleabihf-gcc -c hello.c -o hello.o
cross-musl/bin/arm-linux-musleabihf-ld -Lcross-musl/arm-linux-musleabihf/lib -lc cross-musl/arm-linux-musleabihf/lib/crt1.o hello.o -o hello.elf

我以为在不使用-nostartfiles、-nostdlib或-nodefaultlibs的时候不需要指定标准库和启动文件,还是我错了?

【问题讨论】:

  • 因为引导程序位于 C 库中,而不是编译器的一部分(显然也不是您的代码)。您仍然需要检查输出并确认链接器脚本与您的目标匹配并且代码是可启动/可运行的。
  • 检查对链接器的正常 gcc hello.c -o hello 调用是什么样的。它里面有路径/库。
  • @old_timer:这个工具链的目标是 linux,而不是裸机目标,所以这应该可以工作,不是吗? @ErwinP:我能够使用 https://musl.cc/armel-linux-musleabihf-cross.tgz 重现您的问题 - 您并不孤单。
  • 工具链不知道区别是吗?它只是一个愚蠢的编译器和链接器。在编译器进行链接时检查链接器调用。
  • 大声笑,对于 x86 linux,链接器脚本调用非常不适合这里,但是像 x86_64-linux-gnu/crti.o 和 -L/lib/x86_64-linux-gnu 和 -lc 这样的东西和 -lgcc 有点跳出那里

标签: arm cross-compiling musl


【解决方案1】:

这就是 GNU 工具的工作方式 gcc 最聪明的是,它不仅用于调用编译器,还用于调用链接器,它知道一切与它相关的位置、binutils 和 C 库,这就是你看到的原因当你构建一个基于 GNU 的工具链时你会看到什么。检查从 gcc 到链接器的调用并查看它指定了所有内容是很简单的。 ld 不知道它在哪里,也没有办法弄清楚它在哪里,必须告诉一切。这就是 ld 的设计方式。 Linux 程序和交叉编译器的简单示例。

#include <stdlib.h>
int main ( void )
{
    exit(1);
}

arm-linux-gnueabi-gcc so.c -o so.o

这就是传递给 ld 的内容(当从 arm-whatever-gcc 调用时,实际的二进制文件名为 ld)以进行链接。

[1][-plugin]
[2][/usr/lib/gcc-cross/arm-linux-gnueabi/5/liblto_plugin.so]
[3][-plugin-opt=/usr/lib/gcc-cross/arm-linux-gnueabi/5/lto-wrapper]
[4][-plugin-opt=-fresolution=/tmp/ccaUZvi4.res]
[5][-plugin-opt=-pass-through=-lgcc]
[6][-plugin-opt=-pass-through=-lgcc_s]
[7][-plugin-opt=-pass-through=-lc]
[8][-plugin-opt=-pass-through=-lgcc]
[9][-plugin-opt=-pass-through=-lgcc_s]
[10][--sysroot=/]
[11][--build-id]
[12][--eh-frame-hdr]
[13][-dynamic-linker]
[14][/lib/ld-linux.so.3]
[15][-X]
[16][--hash-style=gnu]
[17][--as-needed]
[18][-m]
[19][armelf_linux_eabi]
[20][-z]
[21][relro]
[22][-o]
[23][so.o]
[24][crt1.o]
[25][crti.o]
[26][/usr/lib/gcc-cross/arm-linux-gnueabi/5/crtbegin.o]
[27][-L/usr/lib/gcc-cross/arm-linux-gnueabi/5]
[28][-L/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/lib/../lib]
[29][-L/lib/../lib]
[30][-L/usr/lib/../lib]
[31][-L/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/lib]
[32][/tmp/ccmlFvlr.o]
[33][-lgcc]
[34][--as-needed]
[35][-lgcc_s]
[36][--no-as-needed]
[37][-lc]
[38][-lgcc]
[39][--as-needed]
[40][-lgcc_s]
[41][--no-as-needed]
[42][/usr/lib/gcc-cross/arm-linux-gnueabi/5/crtend.o]
[43][crtn.o]

如果您想链接自己,这一直是您必须指向所有内容的情况。一个例外是默认链接器脚本,因为您想替换您的 C 库,所以您也必须替换它。

这或者你将知识/机制构建到 gcc 中,让它将新库信息传递给链接器,而不是 GNU C 库信息/路径...

另一个例子,x86 native:

gcc -O2 so.c -o so
[1][-plugin]
[2][/usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so]
[3][-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper]
[4][-plugin-opt=-fresolution=/tmp/ccmhqpU9.res]
[5][-plugin-opt=-pass-through=-lgcc]
[6][-plugin-opt=-pass-through=-lgcc_s]
[7][-plugin-opt=-pass-through=-lc]
[8][-plugin-opt=-pass-through=-lgcc]
[9][-plugin-opt=-pass-through=-lgcc_s]
[10][--sysroot=/]
[11][--build-id]
[12][--eh-frame-hdr]
[13][-m]
[14][elf_x86_64]
[15][--hash-style=gnu]
[16][--as-needed]
[17][-dynamic-linker]
[18][/lib64/ld-linux-x86-64.so.2]
[19][-z]
[20][relro]
[21][-o]
[22][so]
[23][/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o]
[24][/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o]
[25][/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o]
[26][-L/usr/lib/gcc/x86_64-linux-gnu/5]
[27][-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu]
[28][-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib]
[29][-L/lib/x86_64-linux-gnu]
[30][-L/lib/../lib]
[31][-L/usr/lib/x86_64-linux-gnu]
[32][-L/usr/lib/../lib]
[33][-L/usr/lib/gcc/x86_64-linux-gnu/5/../../..]
[34][/tmp/ccg1E03e.o]
[35][-lgcc]
[36][--as-needed]
[37][-lgcc_s]
[38][--no-as-needed]
[39][-lc]
[40][-lgcc]
[41][--as-needed]
[42][-lgcc_s]
[43][--no-as-needed]
[44][/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o]
[45][/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o]

同样的,ld 不知道任何东西在哪里,C 库、它的引导程序、gcc 库等等……这一切都被归入了 gcc,可能是设计使然。 (gcc 当然是一个包装器,它调用多个程序来编译解析器和其他一些程序,然后是实际的编译器,然后是汇编器,然后是链接器,也许还有其他的,就像汇编一样,ld 只是链接)。

很多人都熟悉 baremetal,因此不使用 C 库或其他东西,因此链接器脚本有些琐碎(当然不如 ld file.o -o file.elf 琐碎,除非您有手为该目标创建环境)。当您想添加库时,您可以教 gcc 或将所有内容传递给它需要的链接器。

-lgcc 只是意味着例如在 -L 指定的路径中查找 libgcc,没有 -L 指定的路径,因为 ld 不知道 binutils 之外的事情并且可能在本地查找,所以没有乐趣。

binutils 是一组实用程序。 gcc 比这更高,并且依赖于 binutils(或替代品)。 gcc 不能没有 binutils,bintuils 肯定没有 gcc,这和 Unix 方式,显然 gcc 将 binutils 之外的所有内容传递给 binutils,以便它成为 gcc 的二进制实用程序。

【讨论】:

    【解决方案2】:

    @old_timer 谢谢,你是对的。当我启动 gcc 而不是 ld 来生成可执行文件时,我没有未解决的外部:

    arm-linux-musleabihf-gcc -c hello.c -o hello.o
    arm-linux-musleabihf-gcc -o hello.elf  hello.o
    

    【讨论】:

    • @old_timer: arm-none-linux-gnueabihf-ld -o hello.elf hello.o 工作正常。
    猜你喜欢
    • 2019-08-10
    • 2016-09-20
    • 2023-03-20
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多