Belajar Assembly - ABI & Calling Convention (System V x86-64)
Episode 9 of 23

Belajar Assembly - ABI & Calling Convention (System V x86-64)

Aturan main agar fungsi assembly bisa dipanggil dari C dan sebaliknya: System V AMD64 ABI dengan argumen di rdi/rsi/rdx/rcx/r8/r9 lalu stack, hasil di rax, register caller-saved vs callee-saved, red zone, dan alignment 16-byte. Termasuk perbandingan dengan ABI Windows x64 yang memakai rcx/rdx/r8/r9 dan shadow space.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Di episode 8 kalian menyusun subroutine dengan "aturan tak tertulis" sendiri: letakkan argumen di rsi/rdx, jangan sentuh register pemanggil, dst. Episode 9 mengubah aturan tak tertulis itu menjadi kontrak resmi: ABI (Application Binary Interface). ABI menentukan bagaimana fungsi berkomunikasi pada level biner — dan inilah yang membuat assembly kalian bisa dipanggil dari C, dan sebaliknya.

Mengapa ini penting? Karena di dunia nyata, hampir tidak ada yang menulis semua program dalam assembly. Kalian akan menulis fungsi-fungsi kecil yang kritis dalam assembly, lalu memanggilnya dari C, atau memanggil libc (printf, read) dari assembly. Tanpa mengikuti ABI, register-nya akan bertabrakan dan hasilnya kacau.

System V AMD64 ABI: Konvensi Linux/Unix

Semua sistem Linux, BSD, dan macOS memakai System V AMD64 ABI. Aturan intinya:

1. Argumen Fungsi

Enam argumen integer/pointer pertama masuk ke register, sisanya lewat stack:

Argumen ke-Register
1rdi
2rsi
3rdx
4rcx
5r8
6r9
7+stack (dengan align 16-byte)
Dari C ke assembly
; long add_five(long a, long b, long c, long d, long e)
;  a=rdi, b=rsi, c=rdx, d=rcx, e=r8
;  return a+b+c+d+e  →  rax
add_five:
    mov rax, rdi
    add rax, rsi
    add rax, rdx
    add rax, rcx
    add rax, r8
    ret

2. Nilai Kembalian

  • Integer/pointer: rax.
  • Lebar 128-bit (misal __int128): rax (rendah) + rdx (tinggi).
  • Floating point: xmm0 (dibahas di episode 12).

3. Caller-Saved vs Callee-Saved

Pembagian tanggung jawab — bagian yang paling sering membingungkan:

KategoriRegisterSiapa yang menyimpan
Caller-saved (scratch)rax, rcx, rdx, rsi, rdi, r8r11Pemanggil (caller) — boleh berubah setelah call
Callee-savedrbx, rbp, r12r15Pemanggil (callee) — wajib dipulihkan sebelum ret

Artinya: jika kalian punya nilai penting di rbx lalu memanggil fungsi lain, nilai itu dijamin tetap utuh setelah call kembali (karena fungsi tersebut wajib memulihkannya). Sebaliknya, nilai di rax bisa berubah seenaknya.

Callee-saved wajib dipulihkan
my_func:
    push rbx          ; simpan karena kita akan pakai
    push rbp
    mov rbx, 100
    mov rbp, 200
    ; ... lakukan sesuatu ...
    pop rbp           ; pulihkan sebelum ret
    pop rbx
    ret

4. Red Zone: 128 Byte di Bawah RSP

System V menyediakan red zone — 128 byte di bawah rsp yang dijamin tidak akan disentuh signal/interrupt. Leaf function bisa memakainya tanpa sub rsp, N:

Red zone (leaf function saja)
leaf_using_red_zone:
    mov [rsp - 8], rax    ; aman — di dalam red zone
    mov [rsp - 16], rbx
    ret

Warning

Red zone hanya aman untuk leaf function. Jika fungsi memanggil fungsi lain, call akan mendorong alamat kembali ke bawah rsp dan menimpa red zone. Kernel Linux juga punya red zone masing-masing — jangan pernah mengasumsikan red zone di interrupt handler.

5. Stack Alignment: 16 Byte

Aturan terakhir yang sering membuat kode assembly crash misterius: sebelum call, rsp harus kelipatan 16. Karena call mendorong 8 byte alamat kembali, maka saat memasuki fungsi, rsp % 16 == 8. Prologue push rbp membuat rsp % 16 == 0, dan setiap frame ditambah sub rsp, N dengan N kelipatan 16.

Jika aturan ini dilanggar saat memanggil fungsi libc, hasilnya crash SIGSEGV yang membingungkan — terutama saat memanggil printf (materi episode 19).

Windows x64 ABI: Konvensi Microsoft

Saat menulis kode yang berjalan di Windows, ABI-nya berbeda total:

AspekSystem VWindows x64
Argumen 1–4rdi, rsi, rdx, rcxrcx, rdx, r8, r9
Argumen 5+stackstack
Shadow spacetidak ada32 byte di atas alamat kembali
Caller-savedrax,rcx,rdx,rsi,rdi,r8r11rax,rcx,rdx,r8r11
Callee-savedrbx,rbp,r12r15rbx,rbp,rdi,rsi,r12r15
Stack alignment16-byte (RSP = 8 mod 16 saat call)16-byte

Perbedaan yang menonjol: urutan argumen (rcx pertama, bukan rdi), dan shadow space — 32 byte yang wajib dialokasikan pemanggil di stack sebelum memanggil fungsi (ruang cadangan untuk argumen register, berguna saat fungsi menyimpan argumennya). Jika kode kalian dipanggil dari Windows, ingat kedua aturan ini.

Contoh: Fungsi Assembly Dipanggil dari C

Kombinasi paling umum di dunia nyata — file assembly berisi fungsi ABI-compliant, dipanggil C:

math.asm (NASM)
section .text
    global my_add
 
; long my_add(long a, long b);
my_add:
    mov rax, rdi      ; rax = a
    add rax, rsi      ; rax += b
    ret
Cmain.c
#include <stdio.h>
 
long my_add(long a, long b);
 
int main(void) {
    printf("%ld\n", my_add(3, 4));
    return 0;
}
Link assembly + C
nasm -f elf64 math.asm -o math.o
gcc -no-pie main.c math.o -o app
./app

Tip

Di episode ini kode assembly bertindak sebagai callee yang mengikuti ABI — ia menerima argumen dan mengembalikan hasil. Di episode 19 kalian akan membalik peran: assembly menjadi caller yang memanggil printf dari libc, dan semua aturan ABI ini (alignment 16-byte!) wajib dipatuhi.

Common Pitfalls

  • Salah urutan argumenrdi, rsi vs rcx, rdx (Windows). Selalu cek target OS.
  • Tidak memulihkan callee-saved (rbx, rbp, r12r15) → pemanggil C korup.
  • Menggunakan red zone di fungsi non-leaf → data tertimpa oleh call.
  • RSP tidak kelipatan 16 sebelum call → crash di fungsi libc.
  • Return type mismatchint 32-bit vs long 64-bit: pastikan eax vs rax.

Penutup

Pada episode 9 ini, kalian telah mempelajari kontrak resmi antar-fungsi di level biner.

Inti yang harus dibawa pulang:

  • System V: argumen rdi/rsi/rdx/rcx/r8/r9 → stack; return di rax.
  • Caller-saved (rax/rcx/rdx/rsi/rdi/r8-r11) vs callee-saved (rbx/rbp/r12-r15).
  • Red zone 128 byte hanya untuk leaf function.
  • RSP harus kelipatan 16 sebelum call — aturan yang sering dilupakan.
  • Windows x64: rcx/rdx/r8/r9, shadow space 32 byte, callee-saved berbeda.

Di episode 10 selanjutnya, kita keluar dari antar-fungsi ke antar-proses: syscall & interaksi OS di Linux — instruksi syscall, tabel nomor syscall di /usr/include/asm/unistd_64.h, dan implementasi file I/O (open/read/write/close) murni tanpa libc, plus debugging dengan strace. Saatnya program kalian berbicara dengan kernel!