I tried to write simple program without main
fmt db "test", 0xa, 0
lea rdi, [fmt] ; print simple string
xor eax, eax
mov eax, 60 ; exit successfully
xor edi, edi
yasm -f elf64 main.s; ld -o main main.o
main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'
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.
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
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