This is the fifth post about x86 assembly. In this post I will show how to call functions in x86 assembly.

Below is a simple program that has three functions besides main.

global main
extern printf
section   .text
main:
        mov rdi, 5
        call times2
        mov rdi, rax
        call print_value
        call exit
times2:
        push rbp
        mov rbp, rsp

        mov rax, rdi
        add rax, rax

        mov rsp, rbp
        pop rbp
        ret
print_value:
        push rbp
        mov rbp, rsp

        push rdi
        mov	rdi, fmt
        pop	rsi
        mov	rax, 0
        call printf wrt ..plt

        mov rsp, rbp
        pop rbp
        ret
exit:
        mov rdi, 0 ; exit code 0
        mov rax, 60 ; system call for exit
        syscall

section .data
fmt:    db "The value is %d", 10, 0

Passing values to functions

The code above is following the Linux 64-bit ABI where the first six arguments(integers and pointers) are passed through registers. You could also pass the arguments on the stack or hardcoded memory locations.

Function prologue and epilogue

The beginning and the end in the functions times2 and print_value is called the function prologue and epilogue.

  push rbp
  mov rbp, rsp

  ...

  mov rsp, rbp
  pop rbp
  ret

This a convention for preparing the stack for usage. The prologue creates a new stack frame for the called function and the epilogue restores the stack frame for the calling function.