我试着在没有 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
GOT
main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'
如何解决这个问题?
答案 0 :(得分:2)
您在链接时获得undefined reference to printf
的原因是您没有链接到 C 库。同样,当您使用自己的_start
标签作为切入点时,还有其他问题需要克服,如下所述。
要确保在运行时使用适当的动态链接器并链接到 C 库,可以使用 GCC 作为 LD 。要提供您自己的_start
标签并避免 C 运行时启动代码,请使用-nostartfiles
选项。虽然我不推荐这种方法,但您可以链接:
gcc -m64 -nostartfiles main.o -o main
如果您希望使用 LD ,可以使用特定的动态链接器并使用-lc
链接 C 库。要链接x86_64代码,您可以使用:
ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main
如果你一直在编译和链接32位,那么链接可以用:
完成ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main
由于您使用的是 C 库,因此应考虑链接 C 运行时。如果要创建静态可执行文件,此方法也适用。使用 C 库的最佳方法是将_start
标签重命名为main
,然后使用 GCC 进行链接:
gcc -m64 main.o -o main
这样做可以让你通过main
返回(使用 RET )退出程序,就像 C 程序结束一样。这还具有在程序退出时刷新标准输出的优点。
使用 EAX = 60的syscall
退出将不会刷新输出缓冲区。因此,您可能会发现自己必须在输出中添加换行符以在退出之前查看输出。这仍然不能保证您将看到printf
输出的内容。您可以考虑调用 C 库函数exit
,而不是sys_exit SYSCALL 。 exit
的 MAN PAGE 说:
刷新并关闭所有打开的stdio(3)流。删除tmpfile(3)创建的文件。
我建议将sys_exit SYSCALL 替换为:
extern exit
xor edi, edi ; In 64-bit code RDI is 1st argument = return value
call exit
答案 1 :(得分:1)
printf
函数由C标准库libc
提供。要使用它,您需要将-lc
传递给链接器:
ld -o main main.o -lc
我建议你使用C编译器作为链接器的前端,以获得正确的其他标志:
cc -o main -nostartfiles main.o
请注意,由于您的程序没有初始化C标准库,因此其部分内容可能无法正常工作。如果要在汇编程序中使用C标准库,我建议您从main
开始汇编程序并通过调用C编译器链接它:
yasm -f elf64 main.s
cc -o main main.o