Reading — step 1 of 5
Learn
Assembly functions follow the System V AMD64 ABI on Linux. Understand it once and you can debug any compiled C code.
Calling convention
Argument registers (first 6 ints/pointers):
rdi,rsi,rdx,rcx,r8,r9
Return value: rax (and rdx for 128-bit returns).
Caller-saved (callee may clobber):
rax,rcx,rdx,rsi,rdi,r8,r9,r10,r11
Callee-saved (function must preserve):
rbx,rsp,rbp,r12,r13,r14,r15
If you use a callee-saved register, push it on entry and pop on exit.
A simple function
Square an integer: int square(int n) { return n * n; }
section .text
global _start
; int square(int n) ; n in edi, returns in eax
square:
mov eax, edi
imul eax, eax
ret
_start:
mov edi, 7
call square
; eax now contains 49
; Convert and print would go here.
mov rax, 60
xor rdi, rdi
syscall
Notes:
call labelpushes the return address and jumpsretpops the return address and jumps to it- We use
edi(32-bit) since n isint(32-bit). Upper 32 bits ofrdiare ignored. - Result returns in
eax. Caller reads it.
With a stack frame
Longer functions need the stack:
; int sum(int a, int b)
sum:
push rbp ; save old frame pointer
mov rbp, rsp ; new frame
sub rsp, 16 ; allocate 16 bytes (alignment)
mov [rbp-4], edi ; save a
mov [rbp-8], esi ; save b
mov eax, [rbp-4]
add eax, [rbp-8]
mov rsp, rbp ; deallocate
pop rbp ; restore old frame
ret
Stack alignment
The stack must be 16-byte aligned before a call. Each call pushes 8 bytes (return address); inside the function, rsp is misaligned by 8 until you push rbp (then it's aligned again). This matters for SSE/AVX instructions which fault on misaligned access.
Recursive function example
Factorial:
; int factorial(int n) ; n in edi, returns in eax
factorial:
cmp edi, 1
jle .base
push rdi ; save n (callee-saved? No — but we need it)
sub rsp, 8 ; scratch space (NOT alignment — after the push, rsp is already 16-aligned)
dec edi
call factorial ; eax = factorial(n-1)
add rsp, 8
pop rdi ; restore n
imul eax, edi ; eax = n * factorial(n-1)
ret
.base:
mov eax, 1
ret
Common patterns
Save/restore:
push rbx
push r12
; ... use rbx, r12 ...
pop r12
pop rbx
ret
Local variables on stack:
sub rsp, 16 ; 2 qwords of locals
mov [rsp + 0], rax
mov [rsp + 8], rbx
; ...
add rsp, 16 ; deallocate
Functions vs syscalls
call func— user-mode function call, jumps directlysyscall— kernel transition, much slower (~1μs vs ~1ns)
Use functions for user-space logic; reserve syscalls for I/O, process management, etc.
Learning the ABI lets you call C functions from your assembly (and vice versa). It's how all language runtimes work under the hood.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…