从_start调用printf时出现链接器错误

时间:2016-09-18 11:37:19

标签: linux assembly x86-64 ld

我试着在没有 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'

如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

您的错误原因

您在链接时获得undefined reference to printf的原因是您没有链接到 C 库。同样,当您使用自己的_start标签作为切入点时,还有其他问题需要克服,如下所述。

使用不带C运行时启动代码的LIBC

要确保在运行时使用适当的动态链接器并链接到 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 运行时。如果要创建静态可执行文件,此方法也适用。使用 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