Bagikan melalui


Gambaran umum konvensi ARM64 ABI

Antarmuka biner aplikasi dasar (ABI) untuk Windows saat dikompilasi dan dijalankan pada prosesor ARM dalam mode 64-bit (armv8 atau arsitektur yang lebih baru), sebagian besar, mengikuti EABI AArch64 standar ARM. Artikel ini menyoroti beberapa asumsi dan perubahan utama dari apa yang didokumentasikan dalam EABI. Untuk informasi tentang ABI 32-bit, lihat Gambaran Umum konvensi ARM ABI. Untuk informasi selengkapnya tentang ARM EABI standar, lihat Antarmuka Biner Aplikasi (ABI) untuk Arsitektur ARM (tautan eksternal).

Definisi

Dengan diperkenalkannya dukungan 64-bit, ARM telah mendefinisikan beberapa istilah:

  • AArch32 – arsitektur set instruksi (ISA) 32-bit warisan yang ditentukan oleh ARM, termasuk eksekusi mode Thumb.
  • AArch64 – arsitektur set instruksi (ISA) 64-bit baru yang ditentukan oleh ARM.
  • ARMv7 – spesifikasi perangkat keras ARM "generasi ke-7", yang hanya mencakup dukungan untuk AArch32. Versi perangkat keras ARM ini adalah Windows versi pertama untuk ARM yang didukung.
  • ARMv8 – spesifikasi perangkat keras ARM "generasi ke-8", yang mencakup dukungan untuk AArch32 dan AArch64.

Windows juga menggunakan istilah-istilah ini:

  • ARM – mengacu pada arsitektur ARM 32-bit (AArch32), terkadang disebut sebagai WoA (Windows di ARM).
  • ARM32 – sama seperti ARM, di atas; digunakan dalam dokumen ini untuk kejelasan.
  • ARM64 – mengacu pada arsitektur ARM 64-bit (AArch64). Tidak ada yang namanya WoA64.

Terakhir, ketika merujuk ke jenis data, definisi berikut dari ARM dirujuk:

  • Short-Vector – Jenis data yang dapat diwakili langsung dalam SIMD, vektor elemen senilai 8 byte atau 16 byte. Ini selaras dengan ukurannya, baik 8 byte atau 16 byte, di mana setiap elemen bisa 1, 2, 4, atau 8 byte.
  • HFA (Agregat Titik Mengambang Homogen) – Jenis data dengan 2 hingga 4 anggota floating-point yang identik, baik float atau ganda.
  • HVA (Agregat Vektor Pendek Homogen) – Jenis data dengan 2 hingga 4 anggota Short-Vector yang identik.

Persyaratan dasar

Versi ARM64 Dari Windows presupposes bahwa ia berjalan pada armv8 atau arsitektur yang lebih baru setiap saat. Dukungan floating-point dan NEON dianggap ada di perangkat keras.

Spesifikasi ARMv8 menjelaskan kripto opsional baru dan opcode pembantu CRC untuk AArch32 dan AArch64. Dukungan untuk mereka saat ini bersifat opsional, tetapi disarankan. Untuk memanfaatkan opcode ini, aplikasi harus terlebih dahulu melakukan pemeriksaan runtime untuk keberadaannya.

Endianness

Seperti halnya Windows versi ARM32, pada ARM64 Windows dijalankan dalam mode little-endian. Beralih endianness sulit dicapai tanpa dukungan mode kernel di AArch64, sehingga lebih mudah diberlakukan.

Penjajaran

Windows yang berjalan di ARM64 memungkinkan perangkat keras CPU menangani akses yang tidak selaras secara transparan. Dalam peningkatan dari AArch32, dukungan ini sekarang juga berfungsi untuk semua akses bilangan bulat (termasuk akses multi-kata) dan untuk akses floating-point.

Namun, akses ke memori (perangkat) yang belum di-cache masih harus selalu diselaraskan. Jika kode mungkin dapat membaca atau menulis data yang tidak sejajar dari memori yang tidak di-cache, kode harus memastikan untuk menyelaraskan semua akses.

Perataan tata letak default untuk lokal:

Ukuran dalam byte Perataan dalam byte
1 1
2 2
3, 4 4
> 4 8

Perataan tata letak default untuk global dan statis:

Ukuran dalam byte Perataan dalam byte
1 1
2 - 7 4
8 - 63 8
>= 64 16

Daftar bilangan bulat

Arsitektur AArch64 mendukung 32 register bilangan bulat:

Daftar Volatilitas Peran
x0-x8 Volatile Parameter/Coretan hasil mendaftar
x9-x15 Volatile Register Scratch
x16-x17 Volatile Register awal panggilan intra-prosedur
x18 T/A Register platform yang dipesan: dalam mode kernel, menunjuk ke KPCR untuk prosesor saat ini; Dalam mode pengguna, arahkan ke TEB
x19-x28 Non-volatil Register Scratch
x29/fp Non-volatil Penunjuk bingkai
x30/lr Keduanya Daftar Tautan: Fungsi Callee harus mempertahankannya untuk pengembaliannya sendiri, tetapi nilai penelepon akan hilang.

Setiap register dapat diakses sebagai nilai 64-bit penuh (melalui x0-x30) atau sebagai nilai 32-bit (melalui w0-w30). Operasi 32-bit nol memperpanjang hasilnya hingga 64 bit.

Lihat bagian Parameter passing untuk detail tentang penggunaan register parameter.

Tidak seperti AArch32, penghitung program (PC) dan penunjuk tumpukan (SP) tidak terdaftar diindeks. Mereka terbatas dalam bagaimana mereka dapat diakses. Perhatikan juga bahwa tidak ada register x31. Pengodean tersebut digunakan untuk tujuan khusus.

Penunjuk bingkai (x29) diperlukan untuk kompatibilitas dengan stack walking cepat yang digunakan oleh ETW dan layanan lainnya. Ini harus menunjuk ke pasangan {x29, x30} sebelumnya pada tumpukan.

Register Floating-point/SIMD

Arsitektur AArch64 juga mendukung 32 register floating-point/SIMD, yang dirangkum di bawah ini:

Daftar Volatilitas Peran
v0-v7 Volatile Parameter/Coretan hasil mendaftar
v8-v15 Keduanya 64 bit rendah adalah Non-Volatile. Tinggi 64 bit volatil.
v16-v31 Volatile Register Scratch

Setiap register dapat diakses sebagai nilai 128-bit penuh (melalui v0-v31 atau q0-q31). Ini dapat diakses sebagai nilai 64-bit (melalui d0-d31), sebagai nilai 32-bit (melalui s0-s31), sebagai nilai 16-bit (melalui h0-h31), atau sebagai nilai 8-bit (melalui b0-b31). Mengakses lebih kecil dari 128 bit hanya mengakses bit yang lebih rendah dari register 128-bit penuh. Mereka membiarkan bit yang tersisa tidak tersentuh kecuali ditentukan lain. (AArch64 berbeda dari AArch32, di mana register yang lebih kecil dikemas di atas register yang lebih besar.)

Register kontrol floating-point (FPCR) memiliki persyaratan tertentu pada berbagai bitfield di dalamnya:

Bit Makna Volatilitas Peran
26 AHP Non-Volatil Kontrol setengah presisi alternatif.
25 DN Non-Volatil Kontrol mode NaN default.
24 FZ Non-volatil Hapus kontrol mode ke nol.
23-22 RMode Non-volatil Kontrol mode pembulatan.
15,12-8 IDE/IXE/etc Non-Volatil Bit berkemampuan perangkap pengecualian, harus selalu 0.

Register sistem

Seperti AArch32, spesifikasi AArch64 menyediakan tiga register "ID utas" yang dikontrol sistem:

Daftar Peran
TPIDR_EL0 Dicadangkan.
TPIDRRO_EL0 Berisi nomor CPU untuk prosesor saat ini.
TPIDR_EL1 Menunjuk ke struktur KPCR untuk prosesor saat ini.

Pengecualian titik mengambang

Dukungan untuk pengecualian titik mengambang IEEE bersifat opsional pada sistem AArch64. Untuk varian prosesor yang memang memiliki pengecualian floating-point perangkat keras, kernel Windows secara diam-diam menangkap pengecualian dan secara implisit menonaktifkannya di register FPCR. Perangkap ini memastikan perilaku yang dinormalisasi di seluruh varian prosesor. Jika tidak, kode yang dikembangkan pada platform tanpa dukungan pengecualian dapat menemukan dirinya mengambil pengecualian tak terduga saat berjalan di platform dengan dukungan.

Pengoperasian parameter

Untuk fungsi non-variadik, ABI Windows mengikuti aturan yang ditentukan oleh ARM untuk melewati parameter. Aturan ini dikutip langsung dari Prosedur Panggil Standar untuk Arsitektur AArch64:

Tahap A – Inisialisasi

Tahap ini dilakukan tepat sekali, sebelum pemrosesan argumen dimulai.

  1. Nomor Daftar Tujuan Umum Berikutnya (NGRN) diatur ke nol.

  2. SIMD Berikutnya dan Floating-point Register Number (NSRN) diatur ke nol.

  3. Alamat argumen bertumpuk berikutnya (NSAA) diatur ke nilai stack-pointer (SP) saat ini.

Tahap B – Pra-padding dan ekstensi argumen

Untuk setiap argumen dalam daftar, aturan pencocokan pertama dari daftar berikut diterapkan. Jika tidak ada aturan yang cocok, argumen digunakan tidak dimodifikasi.

  1. Jika jenis argumen adalah Tipe Komposit yang ukurannya tidak dapat ditentukan secara statis oleh penelepon dan penerima panggilan, argumen disalin ke memori dan argumen digantikan oleh penunjuk ke salinan. (Tidak ada jenis seperti itu di C/C++ tetapi ada dalam bahasa lain atau dalam ekstensi bahasa).

  2. Jika jenis argumen adalah HFA atau HVA, maka argumen digunakan tidak dimodifikasi.

  3. Jika jenis argumen adalah Tipe Komposit yang lebih besar dari 16 byte, maka argumen disalin ke memori yang dialokasikan oleh pemanggil, dan argumen digantikan oleh penunjuk ke salinan.

  4. Jika jenis argumen adalah Tipe Komposit, maka ukuran argumen dibulatkan ke atas ke kelipatan terdekat dari 8 byte.

Tahap C – Penetapan argumen untuk mendaftar dan menumpuk

Untuk setiap argumen dalam daftar, aturan berikut diterapkan pada gilirannya hingga argumen dialokasikan. Ketika argumen ditetapkan ke register, setiap bit yang tidak digunakan dalam register memiliki nilai yang tidak ditentukan. Jika argumen ditetapkan ke slot tumpukan, byte padding yang tidak digunakan memiliki nilai yang tidak ditentukan.

  1. Jika argumen adalah Tipe Floating-point atau Vektor Pendek Setengah, Tunggal, Ganda, atau Quad-presisi, dan NSRN kurang dari 8, maka argumen dialokasikan ke bit register v[NSRN] yang paling tidak signifikan. NSRN bertambah bertahap oleh satu. Argumen sekarang telah dialokasikan.

  2. Jika argumen adalah HFA atau HVA, dan ada cukup DAFTAR SIMD dan Floating-point yang tidak dialokasikan (NSRN + jumlah anggota ≤ 8), maka argumen dialokasikan untuk SIMD dan Floating-point Registers, satu register per anggota HFA atau HVA. NSRN bertambah bertahap dengan jumlah register yang digunakan. Argumen sekarang telah dialokasikan.

  3. Jika argumen adalah HFA atau HVA, maka NSRN diatur ke 8, dan ukuran argumen dibulatkan ke kelipatan terdekat 8 byte.

  4. Jika argumen adalah HFA, HVA, Floating-point presisi Quad atau Jenis Vektor Pendek, maka NSAA dibulatkan ke atas ke yang lebih besar dari 8 atau Penyelarasan Alami dari jenis argumen.

  5. Jika argumen adalah jenis Floating Point Setengah atau Presisi Tunggal, maka ukuran argumen diatur ke 8 byte. Efeknya seolah-olah argumen telah disalin ke bit paling tidak signifikan dari register 64-bit, dan bit yang tersisa diisi dengan nilai yang tidak ditentukan.

  6. Jika argumen adalah HFA, HVA, Half-, Single-, Double-, atau Quad-precision Floating-point atau Short Vector Type, maka argumen disalin ke memori pada NSAA yang disesuaikan. NSAA bertambah berdasarkan ukuran argumen. Argumen sekarang telah dialokasikan.

  7. Jika argumen adalah Tipe Integral atau Pointer, ukuran argumen kurang dari atau sama dengan 8 byte, dan NGRN kurang dari 8, argumen disalin ke bit yang paling tidak signifikan dalam x[NGRN]. NGRN bertambah bertahap oleh satu. Argumen sekarang telah dialokasikan.

  8. Jika argumen memiliki perataan 16, maka NGRN dibulatkan ke atas ke angka genap berikutnya.

  9. Jika argumen adalah Jenis Integral, ukuran argumen sama dengan 16, dan NGRN kurang dari 7, argumen disalin ke x[NGRN] dan x[NGRN+1]. x[NGRN] harus berisi kata ganda yang ditangani lebih rendah dari representasi memori argumen. NGRN bertambah dua. Argumen sekarang telah dialokasikan.

  10. Jika argumen adalah Jenis Komposit, dan ukuran dalam kata ganda argumen tidak lebih dari 8 dikurangi NGRN, maka argumen disalin ke dalam register tujuan umum berturut-turut, dimulai dari x[NGRN]. Argumen diteruskan seolah-olah telah dimuat ke dalam register dari alamat yang selaras dua kata, dengan urutan instruksi LDR yang sesuai yang memuat register berturut-turut dari memori. Isi dari bagian register yang tidak digunakan tidak ditentukan oleh standar ini. NGRN bertambah bertahap dengan jumlah register yang digunakan. Argumen sekarang telah dialokasikan.

  11. NGRN diatur ke 8.

  12. NSAA dibulatkan ke atas ke yang lebih besar dari 8 atau Penyelarasan Alami dari jenis argumen.

  13. Jika argumen adalah jenis komposit, maka argumen disalin ke memori di NSAA yang disesuaikan. NSAA bertambah berdasarkan ukuran argumen. Argumen sekarang telah dialokasikan.

  14. Jika ukuran argumen kurang dari 8 byte, maka ukuran argumen diatur ke 8 byte. Efeknya seolah-olah argumen disalin ke bit yang paling tidak signifikan dari register 64-bit, dan bit yang tersisa diisi dengan nilai yang tidak ditentukan.

  15. Argumen disalin ke memori di NSAA yang disesuaikan. NSAA bertambah berdasarkan ukuran argumen. Argumen sekarang telah dialokasikan.

Addendum: Fungsi variadik

Fungsi yang mengambil jumlah variabel argumen ditangani secara berbeda dari di atas, sebagai berikut:

  1. Semua komposit diperlakukan sama; tidak ada perlakuan khusus terhadap HFAs atau HVA.

  2. SIMD dan Floating-point Registers tidak digunakan.

Secara efektif, ini sama dengan aturan berikut C.12–C.15 untuk mengalokasikan argumen ke tumpukan imajiner, di mana 64 byte pertama dari tumpukan dimuat ke dalam x0-x7, dan argumen tumpukan yang tersisa ditempatkan secara normal.

Mengembalikan nilai

Nilai integral dikembalikan dalam x0.

Nilai floating-point dikembalikan dalam s0, d0, atau v0, yang sesuai.

Jenis dianggap sebagai HFA atau HVA jika semua penangguhan berikut:

  • Ini tidak kosong,
  • Ini tidak memiliki default non-sepele atau menyalin konstruktor, destruktor, atau operator penugasan,
  • Semua anggotanya memiliki jenis HFA atau HVA yang sama, atau jenis float, ganda, atau neon yang cocok dengan jenis HFA atau HVA anggota lainnya.

Nilai HVA dengan empat elemen atau lebih sedikit dikembalikan dalam s0-s3, d0-d3, atau v0-v3, sebagaimana mewajibkan.

Jenis yang dikembalikan oleh nilai ditangani secara berbeda tergantung pada apakah mereka memiliki properti tertentu, dan apakah fungsi tersebut adalah fungsi anggota non-statis. Jenis yang memiliki semua properti ini,

  • mereka dikumpulkan oleh definisi standar C++14, yaitu, mereka tidak memiliki konstruktor yang disediakan pengguna, tidak ada anggota data non-statis privat atau terlindungi, tidak ada kelas dasar, dan tidak ada fungsi virtual, dan
  • mereka memiliki operator penugasan salinan sepele, dan
  • Mereka memiliki kerusakan yang sepele,

dan dikembalikan oleh fungsi non-anggota atau fungsi anggota statis, gunakan gaya pengembalian berikut:

  • Jenis yang merupakan HFAs dengan empat elemen atau lebih sedikit dikembalikan dalam s0-s3, d0-d3, atau v0-v3, sebagaimana mewajibkan.
  • Jenis yang kurang dari atau sama dengan 8 byte dikembalikan dalam x0.
  • Jenis kurang dari atau sama dengan 16 byte dikembalikan dalam x0 dan x1, dengan x0 yang berisi 8 byte urutan lebih rendah.
  • Untuk jenis agregat lainnya, pemanggil harus memesan blok memori dengan ukuran dan keselarasan yang memadai untuk menahan hasilnya. Alamat blok memori harus diteruskan sebagai argumen tambahan ke fungsi dalam x8. Penerima panggilan dapat mengubah blok memori hasil kapan saja selama eksekusi subroutine. Penerima panggilan tidak diperlukan untuk mempertahankan nilai yang disimpan dalam x8.

Semua jenis lainnya menggunakan konvensi ini:

  • Pemanggil harus memesan blok memori dengan ukuran dan keselarasan yang memadai untuk menahan hasilnya. Alamat blok memori harus diteruskan sebagai argumen tambahan ke fungsi dalam x0, atau x1 jika $this diteruskan dalam x0. Penerima panggilan dapat mengubah blok memori hasil kapan saja selama eksekusi subroutine. Penerima panggilan mengembalikan alamat blok memori di x0.

Tumpukan

Setelah ABI diletakkan oleh ARM, tumpukan harus tetap selaras 16-byte setiap saat. AArch64 berisi fitur perangkat keras yang menghasilkan kesalahan penyelarasan tumpukan setiap kali SP tidak selaras 16 byte dan beban atau penyimpanan relatif SP dilakukan. Windows berjalan dengan fitur ini diaktifkan setiap saat.

Fungsi yang mengalokasikan tumpukan 4k atau lebih harus memastikan bahwa setiap halaman sebelum halaman akhir disentuh secara berurutan. Tindakan ini memastikan tidak ada kode yang dapat "melompati" halaman penjaga yang digunakan Windows untuk memperluas tumpukan. Biasanya sentuhan dilakukan oleh pembantu __chkstk , yang memiliki konvensi panggilan kustom yang melewati total alokasi tumpukan dibagi 16 dalam x15.

Zona merah

Area 16-byte tepat di bawah penunjuk tumpukan saat ini dicadangkan untuk digunakan oleh analisis dan skenario patching dinamis. Area ini mengizinkan kode yang dihasilkan dengan hati-hati untuk dimasukkan yang menyimpan dua register di [sp, #-16] dan untuk sementara menggunakannya untuk tujuan sewenang-wenang. Kernel Windows menjamin bahwa 16 byte tersebut tidak ditimpa jika pengecualian atau interupsi diambil, dalam mode pengguna dan kernel.

Tumpukan kernel

Tumpukan mode kernel default di Windows adalah enam halaman (24k). Perhatikan fungsi dengan buffer tumpukan besar dalam mode kernel. Gangguan yang tidak tepat waktu bisa datang dengan ruang kepala kecil dan membuat pemeriksaan bug panik tumpukan.

Stack berjalan

Kode dalam Windows dikompilasi dengan penunjuk bingkai diaktifkan (/Oy-) untuk mengaktifkan stack walking cepat. Umumnya, x29 (fp) menunjuk ke tautan berikutnya dalam rantai, yang merupakan pasangan {fp, lr}, menunjukkan penunjuk ke bingkai sebelumnya pada tumpukan dan alamat pengembalian. Kode pihak ketiga juga didorong untuk mengaktifkan penunjuk bingkai, untuk memungkinkan pembuatan profil dan pelacakan yang ditingkatkan.

Pembukaan pengecualian

Melepas lelah selama penanganan pengecualian dibantu melalui penggunaan kode unwind. Kode unwind adalah urutan byte yang disimpan di bagian .xdata dari executable. Mereka menjelaskan pengoperasian prolog dan epilog dengan cara abstrak, sehingga efek prolog fungsi dapat dibatalkan sebagai persiapan untuk mencadangkan ke bingkai tumpukan pemanggil. Untuk informasi selengkapnya tentang kode unwind, lihat penanganan pengecualian ARM64.

ARM EABI juga menentukan model unwinding pengecualian yang menggunakan kode unwind. Namun, spesifikasi seperti yang disajikan tidak mencukupi untuk melepas lelah di Windows, yang harus menangani kasus di mana PC berada di tengah prolog fungsi atau epilog.

Kode yang dihasilkan secara dinamis harus dijelaskan dengan tabel fungsi dinamis melalui RtlAddFunctionTable dan fungsi terkait, sehingga kode yang dihasilkan dapat berpartisipasi dalam penanganan pengecualian.

Penghitung siklus

Semua CPU ARMv8 diperlukan untuk mendukung register penghitung siklus, register 64-bit yang dikonfigurasi Windows agar dapat dibaca pada tingkat pengecualian apa pun, termasuk mode pengguna. Ini dapat diakses melalui daftar PMCCNTR_EL0 khusus, menggunakan opcode MSR dalam kode rakitan, atau _ReadStatusReg intrinsik dalam kode C/C++.

Penghitung siklus di sini adalah penghitung siklus sejati, bukan jam dinding. Frekuensi penghitungan akan bervariasi menurut frekuensi prosesor. Jika Anda merasa harus mengetahui frekuensi penghitung siklus, Anda tidak boleh menggunakan penghitung siklus. Sebagai gantinya, Anda ingin mengukur waktu jam dinding, yang harus Anda gunakan QueryPerformanceCounter.

Lihat juga

Masalah Umum Migrasi VISUAL C++ ARM
Penanganan pengecualian ARM64