1

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?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Bulat M.
  • 680
  • 9
  • 25
  • 1
    because printf is provided by the CRT and you are not either initializating nor linking it. – Margaret Bloom Sep 18 '16 at 11:43
  • Did you link against the libc? – fuz Sep 18 '16 at 11:49
  • No, ld -o main main.o. I thought program links dynamically to libc at runtime. Correct please, if I am wrong. – Bulat M. Sep 18 '16 at 11:52
  • 1
    @BulatM. Yes, they do, but you still need to tell the linker to do so. How else can it know that it can find `printf` in the libc? – fuz Sep 18 '16 at 11:53
  • @MichaelPetch It seems like ld chooses an incorrect interpreter name by default. – fuz Sep 18 '16 at 12:06
  • @MichaelPetch It uses `/lib/ld64.so.1`, which is not present. – fuz Sep 18 '16 at 12:07
  • @Michael, it works. Could you please show how to dispense with compiler using only assembler and linker tools? Want to learn how that basic tools work to get proper understanding. – Bulat M. Sep 18 '16 at 12:08
  • @MichaelPetch I was critizing your sentence “It doesn't use one at all.” No, it does try to use a dynamic loader, namely `/lib/ld64.so.1`. It's just that that loader is not present. – fuz Sep 18 '16 at 12:09
  • I do not have `/lib/ld64.so.1` but have `/lib64/ld-linux-x86-64.so.2` which is symlink to `/lib/x86_64-linux-gnu/ld-2.23.so` – Bulat M. Sep 18 '16 at 12:12

2 Answers2

2

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
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • it works. Could you please show how to dispense with compiler using only assembler and linker tools? Want to learn how those basic tools work to get proper understanding not to just use stopgap without understanding. – Bulat M. Sep 18 '16 at 12:14
  • @BulatM. I edited the answer – Michael Petch Sep 18 '16 at 12:19
  • Works. Could you please elaborate what linker was used when doing `ld -o main main.o` and why bash gives error(very strange)? – Bulat M. Sep 18 '16 at 12:23
  • 1
    @BulatM. Bash gave the strange error because no dynamic loader was specified when creating the executable. – Michael Petch Sep 18 '16 at 12:29
  • Interesting, beforehand I thought that shells just fork and execute one of functions from exec family without bothering with loaders. – Bulat M. Sep 18 '16 at 12:42
1

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
fuz
  • 88,405
  • 25
  • 200
  • 352
  • I do `ld -o main main.o -lc` Links successfully. Then try to execute `./main` - it fails `-bash: ./main: No such file or directory` It is not related to linking, but do not know where is the problem. It is there: `ls -al -rwxrwxr-x 1 name name 2832 Sep 18 15:00 main` – Bulat M. Sep 18 '16 at 11:57
  • @BulatM. Very interesting. I'm not sure what the problem is, let me investigate. – fuz Sep 18 '16 at 12:03
  • @BulatM.: see [this answer](http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain/36901649#36901649) re: getting the right ELF interpreter when using `ld` directly, and more generally how to build static / dynamic binaries with/without libc, from source that defines main or _start. – Peter Cordes Sep 18 '16 at 18:44
  • I should really retitle that question, since the canonical answer I wrote there is the answer to a boatload of 64-bit-only questions too. – Peter Cordes Sep 18 '16 at 18:46