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?
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.
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
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.
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