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.

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.
Semua sistem Linux, BSD, dan macOS memakai System V AMD64 ABI. Aturan intinya:
Enam argumen integer/pointer pertama masuk ke register, sisanya lewat stack:
| Argumen ke- | Register |
|---|---|
| 1 | rdi |
| 2 | rsi |
| 3 | rdx |
| 4 | rcx |
| 5 | r8 |
| 6 | r9 |
| 7+ | stack (dengan align 16-byte) |
; 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
retrax.__int128): rax (rendah) + rdx (tinggi).xmm0 (dibahas di episode 12).Pembagian tanggung jawab — bagian yang paling sering membingungkan:
| Kategori | Register | Siapa yang menyimpan |
|---|---|---|
| Caller-saved (scratch) | rax, rcx, rdx, rsi, rdi, r8–r11 | Pemanggil (caller) — boleh berubah setelah call |
| Callee-saved | rbx, rbp, r12–r15 | Pemanggil (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.
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
retSystem V menyediakan red zone — 128 byte di bawah rsp yang dijamin tidak akan disentuh signal/interrupt. Leaf function bisa memakainya tanpa sub rsp, N:
leaf_using_red_zone:
mov [rsp - 8], rax ; aman — di dalam red zone
mov [rsp - 16], rbx
retWarning
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.
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).
Saat menulis kode yang berjalan di Windows, ABI-nya berbeda total:
| Aspek | System V | Windows x64 |
|---|---|---|
| Argumen 1–4 | rdi, rsi, rdx, rcx | rcx, rdx, r8, r9 |
| Argumen 5+ | stack | stack |
| Shadow space | tidak ada | 32 byte di atas alamat kembali |
| Caller-saved | rax,rcx,rdx,rsi,rdi,r8–r11 | rax,rcx,rdx,r8–r11 |
| Callee-saved | rbx,rbp,r12–r15 | rbx,rbp,rdi,rsi,r12–r15 |
| Stack alignment | 16-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.
Kombinasi paling umum di dunia nyata — file assembly berisi fungsi ABI-compliant, dipanggil C:
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#include <stdio.h>
long my_add(long a, long b);
int main(void) {
printf("%ld\n", my_add(3, 4));
return 0;
}nasm -f elf64 math.asm -o math.o
gcc -no-pie main.c math.o -o app
./appTip
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.
rdi, rsi vs rcx, rdx (Windows). Selalu cek target OS.rbx, rbp, r12–r15) → pemanggil C korup.call.call → crash di fungsi libc.int 32-bit vs long 64-bit: pastikan eax vs rax.Pada episode 9 ini, kalian telah mempelajari kontrak resmi antar-fungsi di level biner.
Inti yang harus dibawa pulang:
rdi/rsi/rdx/rcx/r8/r9 → stack; return di rax.rax/rcx/rdx/rsi/rdi/r8-r11) vs callee-saved (rbx/rbp/r12-r15).call — aturan yang sering dilupakan.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!