Windows x64 tidak menggunakan System V AMD64 ABI, melainkan konvensi Microsoft x64 yang menata argumen, balikan, dan stack dengan aturan berbeda. Perbedaannya bukan perkara selera, karena setiap perbedaan mengubah kode yang harus ditulis sebuah fungsi assembly. Topik ini membandingkan kedua konvensi tersebut, menjelaskan shadow space dan daftar register yang dipertahankan milik Windows, lalu menutup dengan contoh fungsi assembly yang ditulis untuk Windows.
Perbedaan utama dengan System V#
Tabel berikut menyandingkan kedua konvensi pada aspek-aspek yang memengaruhi kode secara langsung. Yang paling mencolok adalah jumlah register argumen, empat berbanding enam, sehingga fungsi dengan lima argumen bilangan bulat menggunakan stack di Windows tetapi tidak di Linux. Balikan tidak berubah, yaitu rax untuk bilangan bulat dan pointer serta xmm0 untuk pecahan. Kedua konvensi sama-sama menuntut rsp kelipatan 16 pada instruksi call, sehingga disiplin penyelarasan yang sama berlaku di kedua sisi.
| Aspek | System V AMD64 (Linux) | Microsoft x64 (Windows) |
|---|---|---|
| Register argumen | rdi, rsi, rdx, rcx, r8, r9 | rcx, rdx, r8, r9 |
| Argumen pecahan | xmm0 sampai xmm7 | xmm0 sampai xmm3 |
| Balikan | rax, xmm0 | rax, xmm0 |
| Ruang untuk yang dipanggil | Red zone 128 byte di bawah rsp | Shadow space 32 byte dialokasikan pemanggil |
| Callee-saved | rbx, rbp, r12 sampai r15 | rbx, rbp, rdi, rsi, r12 sampai r15 |
| Penyelarasan pada call | rsp kelipatan 16 | rsp kelipatan 16 |
Perbedaan daftar register pecahan juga berdampak nyata. Windows hanya menjamin empat register xmm untuk argumen, sedangkan System V menjamin delapan, sehingga fungsi dengan banyak argumen pecahan menyimpan lebih banyak sisanya ke stack di Windows daripada di Linux. Dua perbedaan berikutnya, yaitu shadow space dan daftar callee-saved, cukup penting untuk dibahas tersendiri.
Shadow space menggantikan red zone#
System V menyediakan red zone 128 byte di bawah rsp yang aman untuk fungsi daun. Windows tidak menyediakannya, dan sebagai gantinya mewajibkan pemanggil menyiapkan shadow space, yaitu 32 byte di atas alamat kembali yang wajib dialokasikan sebelum setiap call, termasuk untuk fungsi tanpa argumen stack. Empat slot itu menampung empat register argumen, sehingga fungsi yang perlu menggunakan rcx, rdx, r8, atau r9 sebagai register kerja boleh menyimpan isinya ke shadow space tanpa memesan memori tambahan. Slot pertama dibaca di [rsp+8] pada titik masuk fungsi, persis seperti posisi argumen ketujuh di System V.
Perbedaan arah tanggung jawab ini mengubah kebiasaan menulis kode. Di Linux, fungsi daun bebas menulis di bawah rsp tanpa alokasi, sedangkan di Windows penulisan itu menimpa data pemanggil dan dilarang. Sebaliknya, fungsi Windows boleh mengandalkan 32 byte di [rsp+8] sampai [rsp+32] sebagai penyimpanan sementara tanpa biaya apa pun, sedangkan wilayah yang sama di Linux memuat argumen stack milik pemanggil.
Callee-saved yang berbeda#
Windows memperluas daftar register yang dipertahankan dengan rdi dan rsi. Kedua register itu bukan register argumen di Windows, melainkan register kerja yang boleh digunakan bebas oleh fungsi yang dipanggil asalkan dipulihkan, sekaligus dua register tambahan yang dapat dipercaya pemanggil untuk menampung nilai lintas panggilan. Bagian lain daftarnya sama dengan System V, yaitu rbx, rbp, dan r12 sampai r15, sedangkan rax, rcx, rdx, dan r8 sampai r11 tetap caller-saved.
Perluasan daftar ini juga mengubah perhitungan biaya penyelamatan. Fungsi Windows yang membutuhkan dua register kerja tambahan dapat menggunakan rdi dan rsi tanpa memengaruhi register argumen, sedangkan di Linux kedua register itu sudah digunakan untuk argumen pertama dan kedua.
Akibat langsungnya pada kode assembly adalah daftar penyelamatan yang berbeda. Fungsi yang menyelamatkan rdi dan rsi berlebihan di Linux, tetapi wajib di Windows, dan fungsi yang menimpa keduanya tanpa menyelamatkan berjalan benar di Linux sambil diam-diam merusak pemanggil di Windows. Compiler C menangani semua ini secara otomatis karena target yang dipilih saat kompilasi menentukan konvensi yang dipatuhi, sedangkan penulis assembly bertanggung jawab atas daftar register miliknya sendiri.
Contoh fungsi untuk Windows#
Contoh berikut adalah padanan fungsi penjumlahan tiga argumen yang ditulis untuk Windows x64. Berkas ini menggunakan ABI dan pustaka Windows sehingga tidak dapat dibangun di sandbox Linux. Contohnya hanya dibaca dan tidak dijalankan. Dua hal patut dicermati, yaitu argumen yang tiba di rcx, rdx, dan r8, serta penggunaan shadow space sebagai penyimpanan sementara sehingga rcx dapat dikosongkan tanpa alokasi apa pun.
; tidak-dijalankan
; Contoh konvensi Microsoft x64. Berkas ini memakai ABI Windows sehingga
; tidak dapat dibangun dan dijalankan pada sandbox Linux.
default rel
global jumlah_tiga
section .text
jumlah_tiga:
; argumen: rcx = a, rdx = b, r8 = c
mov [rsp+8], rcx ; a dititipkan ke shadow space, rcx bebas dipakai
mov rax, rdx
add rax, r8
add rax, [rsp+8]
retFungsi di atas tidak memanggil fungsi lain sehingga tidak membutuhkan penyesuaian stack. Untuk fungsi Windows yang memanggil fungsi lain, pola bakunya adalah sub rsp, 40, yaitu 32 byte shadow space untuk panggilan berikutnya ditambah 8 byte penyetel agar rsp tetap kelipatan 16 pada instruksi call.
Ringkasan#
- Windows x64 menaruh empat argumen pertama di
rcx,rdx,r8, danr9, dengan pecahan dixmm0sampaixmm3, dan balikan tetap diraxatauxmm0. - Shadow space 32 byte dialokasikan pemanggil dan menjadi tempat fungsi menitipkan empat register argumennya, menggantikan peran red zone.
rdidanrsitermasuk callee-saved di Windows, berbeda dengan System V yang menjadikan keduanya register argumen caller-saved.- Penyelarasan
rspkelipatan 16 pada instruksicallberlaku di kedua konvensi.
Referensi#
- x64 calling convention — Microsoft Learn — Dokumentasi resmi register argumen, shadow space, dan register yang dipertahankan pada Windows x64.
- System V Application Binary Interface — AMD64 Architecture Processor Supplement — Konvensi pembanding di sisi Linux.