首页 > 解决方案 > 使用 musl-cross-make 对标准库中符号的未定义引用

问题描述

我正在为 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 时不需要指定标准库和启动文件,还是我错了?

标签: armcross-compilingmusl

解决方案


这就是 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 原生:

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 的二进制实用程序。


推荐阅读