问题
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