Linker error when calling printf from _start [duplicate]

浪尽此生 提交于 2020-06-09 04:17:06

问题


I tried to write simple program without 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

Compile:

yasm -f elf64 main.s; ld -o main main.o

Got

main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'

How should one fix this?


回答1:


Cause of Your Error

The reason you get undefined reference to printf while linking is because you didn't link against the C library. As well, when using your own _start label as an entry point there are other issues that must be overcome as discussed below.


Using LIBC without C Runtime Startup Code

To make sure you are using the appropriate dynamic linker at runtime and link against the C library you can use GCC as a frontend for LD. To supply your own _start label and avoid the C runtime startup code use the -nostartfiles option. Although I don't recommend this method, you can link with:

gcc -m64 -nostartfiles main.o -o main

If you wish to use LD you can do it by using a specific dynamic linker and link against the C library with -lc. To link x86_64 code you can use:

ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main

If you had been compiling and linking for 32-bit, then the linking could have been done with:

ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main

Preferred Method Using C Runtime Startup Code

Since you are using the C library you should consider linking against the C runtime. This method also works if you were to create a static executable. The best way to use the C library is to rename your _start label to main and then use GCC to link:

gcc -m64 main.o -o main

Doing it this way allows you to exit your program by returning (using RET) from main the same way a C program ends. This also has the advantage of flushing standard output when the program exits.


Flushing output buffers / exit

Using syscall with EAX=60 to exit won't flush the output buffers. Because of this you may find yourself having to add a newline character on your output to see output before exiting. This is still not a guarantee you will see what you output with printf. Rather than the sys_exit SYSCALL you might consider calling the C library function exit. the MAN PAGE for exit says:

All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.

I'd recommend replacing the sys_exit SYSCALL with:

extern exit

xor edi, edi    ; In 64-bit code RDI is 1st argument = return value
call exit



回答2:


The printf function is provided by the C standard library, libc. To use it, you need to pass -lc to the linker:

ld -o main main.o -lc

I recommend you to use the C compiler as a frontend for the linker to get possible other flags right, too:

cc -o main -nostartfiles main.o

Note that since your program doesn't initialize the C standard library, e parts of it may not work as expected. If you want to use the C standard library in your assembly program, I recommend you to make your assembly program start from main and to link it by invoking the C compiler:

yasm -f elf64 main.s
cc -o main main.o


来源:https://stackoverflow.com/questions/39557237/linker-error-when-calling-printf-from-start

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!