Konvensi Microsoft x64

Perbedaan Windows: rcx, rdx, r8, dan r9 untuk argumen, shadow space 32 byte, dan tanpa red zone.

Daftar isi

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.

AspekSystem V AMD64 (Linux)Microsoft x64 (Windows)
Register argumenrdi, rsi, rdx, rcx, r8, r9rcx, rdx, r8, r9
Argumen pecahanxmm0 sampai xmm7xmm0 sampai xmm3
Balikanrax, xmm0rax, xmm0
Ruang untuk yang dipanggilRed zone 128 byte di bawah rspShadow space 32 byte dialokasikan pemanggil
Callee-savedrbx, rbp, r12 sampai r15rbx, rbp, rdi, rsi, r12 sampai r15
Penyelarasan pada callrsp kelipatan 16rsp 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.

nasm contoh-windows.asm
; 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]
    ret

Fungsi 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, dan r9, dengan pecahan di xmm0 sampai xmm3, dan balikan tetap di rax atau xmm0.
  • Shadow space 32 byte dialokasikan pemanggil dan menjadi tempat fungsi menitipkan empat register argumennya, menggantikan peran red zone.
  • rdi dan rsi termasuk callee-saved di Windows, berbeda dengan System V yang menjadikan keduanya register argumen caller-saved.
  • Penyelarasan rsp kelipatan 16 pada instruksi call berlaku di kedua konvensi.

Referensi#