使用 musl-cross-make 对标准库中符号的未定义引用
Undefined references to symbols in standard libraries with 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 时没有必要指定标准库和启动文件,还是我错了?
这就是 GNU 工具的工作方式 gcc 最聪明的地方在于它不仅用于调用编译器,还用于调用 linker 它知道一切都与它相关,binutils 和 C库,这就是为什么您在构建基于 GNU 的工具链时看到的内容。检查从 gcc 到 linker 的调用并看到它指定了所有内容是微不足道的。 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)以使 linking 工作。
[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]
如果你想 link 自己,你必须指出所有事情。一个例外是默认的 linker 脚本,因为你想替换你的 C 库,你绝对必须也替换它。
这或者你将 knowledge/mechanism 构建到 gcc 中,让它将新库信息传递给 linker 而不是 GNU C 库 information/paths...
另一个例子,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 库,它的 bootstrap,gcc 库,等等......这一切都被煮进了 gcc,可能是设计的。 (gcc 当然是一个包装器,它调用多个程序来编译解析器和其他一些程序,然后是实际的编译器,然后是汇编程序,然后是 linker,也许还有其他程序,就像汇编一样,ld 只是 links).
很多人熟悉 baremetal,因此不使用 C 库或其他东西,所以 linker 脚本有点微不足道(当然不像 ld file.o -o 那样微不足道file.elf,除非您有针对该目标的手工制作环境)。当你想添加库时,你要么教 gcc 关于它,要么将所有内容传递给它需要的 linker。
-lgcc 只是意味着例如在 -L 指定路径中查找 libgcc,没有 -L 指定路径,没有乐趣,因为 ld 不知道 binutils 之外的事情并且可能在本地查找。
binutils 是一组实用程序。 gcc 比那个级别更高,并且依赖于 binutils(或替代品)。 gcc 不能没有 binutils,bintuils 肯定没有 gcc,这和 Unix 方式,显然 gcc 将 binutils 之外的所有内容传递给 binutils,以便它成为 gcc 的二进制实用程序。
@old_timer 谢谢,你是对的。当我启动 gcc 而不是 ld 来生成可执行文件时,我没有未解决的外部问题:
arm-linux-musleabihf-gcc -c hello.c -o hello.o
arm-linux-musleabihf-gcc -o hello.elf hello.o
我正在为 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 时没有必要指定标准库和启动文件,还是我错了?
这就是 GNU 工具的工作方式 gcc 最聪明的地方在于它不仅用于调用编译器,还用于调用 linker 它知道一切都与它相关,binutils 和 C库,这就是为什么您在构建基于 GNU 的工具链时看到的内容。检查从 gcc 到 linker 的调用并看到它指定了所有内容是微不足道的。 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)以使 linking 工作。
[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]
如果你想 link 自己,你必须指出所有事情。一个例外是默认的 linker 脚本,因为你想替换你的 C 库,你绝对必须也替换它。
这或者你将 knowledge/mechanism 构建到 gcc 中,让它将新库信息传递给 linker 而不是 GNU C 库 information/paths...
另一个例子,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 库,它的 bootstrap,gcc 库,等等......这一切都被煮进了 gcc,可能是设计的。 (gcc 当然是一个包装器,它调用多个程序来编译解析器和其他一些程序,然后是实际的编译器,然后是汇编程序,然后是 linker,也许还有其他程序,就像汇编一样,ld 只是 links).
很多人熟悉 baremetal,因此不使用 C 库或其他东西,所以 linker 脚本有点微不足道(当然不像 ld file.o -o 那样微不足道file.elf,除非您有针对该目标的手工制作环境)。当你想添加库时,你要么教 gcc 关于它,要么将所有内容传递给它需要的 linker。
-lgcc 只是意味着例如在 -L 指定路径中查找 libgcc,没有 -L 指定路径,没有乐趣,因为 ld 不知道 binutils 之外的事情并且可能在本地查找。
binutils 是一组实用程序。 gcc 比那个级别更高,并且依赖于 binutils(或替代品)。 gcc 不能没有 binutils,bintuils 肯定没有 gcc,这和 Unix 方式,显然 gcc 将 binutils 之外的所有内容传递给 binutils,以便它成为 gcc 的二进制实用程序。
@old_timer 谢谢,你是对的。当我启动 gcc 而不是 ld 来生成可执行文件时,我没有未解决的外部问题:
arm-linux-musleabihf-gcc -c hello.c -o hello.o
arm-linux-musleabihf-gcc -o hello.elf hello.o