从 _start 调用 printf 时出现链接器错误
Linker error when calling printf from _start
我试图在没有 main
的情况下编写简单的程序
segment .data
fmt db "test", 0xa, 0
segment .text
global _start
extern printf
_start:
lea rdi, [fmt] ; print simple string
xor eax, eax
call printf
mov eax, 60 ; exit successfully
xor edi, edi
syscall
编译:
yasm -f elf64 main.s; ld -o main main.o
得到
main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'
应该如何解决这个问题?
printf
函数由C标准库libc
提供。要使用它,您需要将 -lc
传递给 linker:
ld -o main main.o -lc
我建议您使用 C 编译器作为 linker 的前端,以获得其他可能的标志:
cc -o main -nostartfiles main.o
请注意,由于您的程序没有初始化 C 标准库,因此它的某些部分可能无法按预期工作。如果你想在你的汇编程序中使用 C 标准库,我建议你通过调用 C 编译器使你的汇编程序从 main
开始到 link:
yasm -f elf64 main.s
cc -o main main.o
您的错误原因
你在 linking 时得到 undefined reference to printf
的原因是因为你没有 link 反对 C 库。同样,当使用您自己的 _start
标签作为入口点时,还有其他一些问题必须解决,如下所述。
在没有 C 运行时启动代码的情况下使用 LIBC
为了确保您在运行时使用适当的动态 linker 并且 link 针对 C 库,您可以使用 GCC 作为 LD 的前端。要提供您自己的 _start
标签并避免 C 运行时启动代码,请使用 -nostartfiles
选项。虽然我不推荐这种方法,但您可以 link 与:
gcc -m64 -nostartfiles main.o -o main
如果你想使用 LD 你可以通过使用特定的动态 linker 和 link 来对抗 C 图书馆 -lc
。要 link x86_64 您可以使用代码:
ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main
如果您一直在为 32 位编译和 linking,那么 linking 可以通过以下方式完成:
ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main
使用 C 运行时启动代码的首选方法
由于您使用的是 C 库,因此您应该考虑 linking 针对 C 运行时。如果您要创建静态可执行文件,此方法也适用。使用 C 库的最佳方法是将 _start
标签重命名为 main
,然后使用 GCC 将 link:
gcc -m64 main.o -o main
这样做可以让您通过从 main
返回(使用 RET)退出程序,方法与 C[=73 相同=] 程序结束。这也有在程序退出时刷新标准输出的优点。
刷新输出缓冲区/退出
使用 syscall
和 EAX=60 退出不会刷新输出缓冲区。因此,您可能会发现自己必须在输出中添加换行符才能在退出前查看输出。这仍然不能保证您会看到使用 printf
输出的内容。您可以考虑调用 C 库函数 exit
而不是 sys_exit SYSCALL。 MAN PAGE for exit
说:
All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.
我建议将 sys_exit SYSCALL 替换为:
extern exit
xor edi, edi ; In 64-bit code RDI is 1st argument = return value
call exit
我试图在没有 main
的情况下编写简单的程序 segment .data
fmt db "test", 0xa, 0
segment .text
global _start
extern printf
_start:
lea rdi, [fmt] ; print simple string
xor eax, eax
call printf
mov eax, 60 ; exit successfully
xor edi, edi
syscall
编译:
yasm -f elf64 main.s; ld -o main main.o
得到
main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'
应该如何解决这个问题?
printf
函数由C标准库libc
提供。要使用它,您需要将 -lc
传递给 linker:
ld -o main main.o -lc
我建议您使用 C 编译器作为 linker 的前端,以获得其他可能的标志:
cc -o main -nostartfiles main.o
请注意,由于您的程序没有初始化 C 标准库,因此它的某些部分可能无法按预期工作。如果你想在你的汇编程序中使用 C 标准库,我建议你通过调用 C 编译器使你的汇编程序从 main
开始到 link:
yasm -f elf64 main.s
cc -o main main.o
您的错误原因
你在 linking 时得到 undefined reference to printf
的原因是因为你没有 link 反对 C 库。同样,当使用您自己的 _start
标签作为入口点时,还有其他一些问题必须解决,如下所述。
在没有 C 运行时启动代码的情况下使用 LIBC
为了确保您在运行时使用适当的动态 linker 并且 link 针对 C 库,您可以使用 GCC 作为 LD 的前端。要提供您自己的 _start
标签并避免 C 运行时启动代码,请使用 -nostartfiles
选项。虽然我不推荐这种方法,但您可以 link 与:
gcc -m64 -nostartfiles main.o -o main
如果你想使用 LD 你可以通过使用特定的动态 linker 和 link 来对抗 C 图书馆 -lc
。要 link x86_64 您可以使用代码:
ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main
如果您一直在为 32 位编译和 linking,那么 linking 可以通过以下方式完成:
ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main
使用 C 运行时启动代码的首选方法
由于您使用的是 C 库,因此您应该考虑 linking 针对 C 运行时。如果您要创建静态可执行文件,此方法也适用。使用 C 库的最佳方法是将 _start
标签重命名为 main
,然后使用 GCC 将 link:
gcc -m64 main.o -o main
这样做可以让您通过从 main
返回(使用 RET)退出程序,方法与 C[=73 相同=] 程序结束。这也有在程序退出时刷新标准输出的优点。
刷新输出缓冲区/退出
使用 syscall
和 EAX=60 退出不会刷新输出缓冲区。因此,您可能会发现自己必须在输出中添加换行符才能在退出前查看输出。这仍然不能保证您会看到使用 printf
输出的内容。您可以考虑调用 C 库函数 exit
而不是 sys_exit SYSCALL。 MAN PAGE for exit
说:
All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.
我建议将 sys_exit SYSCALL 替换为:
extern exit
xor edi, edi ; In 64-bit code RDI is 1st argument = return value
call exit