DEV Community

Jonathan Mourtada
Jonathan Mourtada

Posted on • Originally published at mourtada.se on

Calling printf from the C standard library in assembly

This is the fourth post about x86 assembly. In this post I will show an example how to call the printf function from the C standard library in assembly code.

section .text default rel extern printf global main main: ; Create a stack-frame, re-aligning the stack to 16-byte alignment before calls push rbp mov rdi, fmt mov rsi, message mov rax, 0 ; Call printf call printf wrt ..plt pop rbp ; Pop stack mov rax,0 ; Exit code 0 ret ; Return section .data message: db "Hello, World", 10, 0 fmt: db "%s", 10, 0 $ nasm printf.asm -f elf64 -o printf.o $ gcc printf.o $ ./a.out # Hello world 
Enter fullscreen mode Exit fullscreen mode

The first part

 default rel extern printf global main 
Enter fullscreen mode Exit fullscreen mode

The default rel is a nasm assembly directive. It tells nasm to use rip relative adressing. In short this makes the assembler to rewrite the references in instructions that uses our fmt and message constants relative to the instruction pointer. This is needed because default for the linker in 64-bit linux is to use position-independent code.

The extern printf part tells the assembler that this symbol exists outside of this file and needs to be referenced at a later stage.

Last row here is global main which is needed for gcc and it’s the entry point for libc.

main

First we need to align the stack because the x86_64 ABI requires the stackpointer to always be 16-byte aligned so therefor we push a value to it.

Then we prepare our registers for the function call to printf and then we call printf wrt ..plt. What happens here is that we load printf from the libc shared library and this is a little bit complicated but very briefly it says call printf with relation to procedure linkage table. The PCL will then the first time printf is called resolve where printf is in memory with the help of the dynamic link loader in linux. It then stores that adress for future calls. We could link printf statically which would copy the code of printf into the executable.

Lastly we need to set return value of main to zero and do a ret.

call and ret

When you do a call operation two things happen. First the instruction on the next instruction is pushed onto the stack. In our example above it will be the memory adress of the instruction pop rbp. Secondly it will jump to the memory adress where printf starts. When printf is done it will do a ret instruction which will pop our memory adress from the stack and jump back to our main function.

Top comments (0)