Bagikan melalui


Ringkasan Konvensi ABI ARM32

Antarmuka biner aplikasi (ABI) untuk kode yang dikompilasi untuk Windows pada prosesor ARM didasarkan pada ARM EABI standar. Artikel ini menyoroti perbedaan utama antara Windows di ARM dan standar. Dokumen ini mencakup ARM32 ABI. Untuk informasi tentang ARM64 ABI, lihat Gambaran Umum konvensi ABI ARM64. Untuk informasi selengkapnya tentang ARM EABI standar, lihat Antarmuka Biner Aplikasi (ABI) untuk Arsitektur ARM (tautan eksternal).

Persyaratan Dasar

Windows di ARM selalu berasumsi bahwa windows berjalan pada arsitektur ARMv7. Dukungan floating-point dalam bentuk VFPv3-D32 atau yang lebih baru harus ada di perangkat keras. VFP harus mendukung floating-point presisi tunggal dan presisi ganda dalam perangkat keras. Runtime Windows tidak mendukung emulasi floating-point untuk mengaktifkan berjalan pada perangkat keras non-VFP.

Dukungan Advanced SIMD Extensions (NEON), termasuk operasi bilangan bulat dan floating-point, juga harus ada di perangkat keras. Tidak ada dukungan run-time untuk emulasi yang disediakan.

Dukungan pembagi bilangan bulat (UDIV/SDIV) disarankan tetapi tidak diperlukan. Platform yang tidak memiliki dukungan pembagian bilangan bulat dapat dikenakan penalti performa karena operasi ini harus terjebak dan mungkin di-patch.

Endianness

Windows pada ARM dijalankan dalam mode little-endian. Kompilator MSVC dan runtime Windows selalu mengharapkan data little-endian. Instruksi SETEND dalam arsitektur set instruksi ARM (ISA) memungkinkan bahkan kode mode pengguna untuk mengubah endianness saat ini. Namun, melakukannya tidak disarankan karena berbahaya untuk aplikasi. Jika pengecualian dihasilkan dalam mode big-endian, perilaku tersebut tidak dapat diprediksi. Ini dapat menyebabkan kesalahan aplikasi dalam mode pengguna, atau pemeriksaan bug dalam mode kernel.

Penjajaran

Meskipun Windows memungkinkan perangkat keras ARM untuk menangani akses bilangan bulat yang tidak selaras secara transparan, kesalahan penyelarasan masih dapat dihasilkan dalam beberapa situasi. Ikuti aturan ini untuk perataan:

  • Anda tidak perlu menyelaraskan beban dan penyimpanan bilangan bulat berukuran setengah kata (16-bit) dan berukuran kata (32-bit). Perangkat keras menanganinya secara efisien dan transparan.

  • Beban dan penyimpanan floating-point harus diselaraskan. Kernel menangani beban dan penyimpanan yang tidak sejajar secara transparan, tetapi dengan overhead yang signifikan.

  • Operasi muat atau simpan ganda (LDRD/STRD) dan beberapa (LDM/STM) harus diselaraskan. Kernel menangani sebagian besar secara transparan, tetapi dengan overhead yang signifikan.

  • Semua akses memori yang tidak di-cache harus diselaraskan, bahkan untuk akses bilangan bulat. Akses yang tidak disejajarkan menyebabkan kesalahan penyelarasan.

Set Instruksi

Instruksi yang ditetapkan untuk Windows pada ARM sangat terbatas pada Thumb-2. Semua kode yang dijalankan pada platform ini diharapkan dimulai dan selalu tetap dalam mode Thumb. Upaya untuk beralih ke set instruksi ARM warisan mungkin berhasil. Namun, jika ya, pengecualian atau gangguan apa pun yang terjadi dapat menyebabkan kesalahan aplikasi dalam mode pengguna, atau pemeriksaan bug dalam mode kernel.

Efek samping dari persyaratan ini adalah bahwa semua penunjuk kode harus memiliki set bit rendah. Kemudian, ketika dimuat dan bercabang melalui BLX atau BX, prosesor tetap dalam mode Thumb. Ini tidak mencoba menjalankan kode target sebagai instruksi ARM 32-bit.

Instruksi SDIV/UDIV

Penggunaan instruksi pembagian bilangan bulat SDIV dan UDIV didukung sepenuhnya, bahkan pada platform tanpa perangkat keras asli untuk menanganinya. Overhead ekstra per SDIV atau UDIV dibagi pada prosesor Cortex-A9 adalah sekitar 80 siklus. Itu ditambahkan ke waktu pembagian keseluruhan 20-250 siklus, tergantung pada input.

Daftar bilangan bulat

Prosesor ARM mendukung 16 register bilangan bulat:

Daftar Volatile? Peran
r0 Volatile Parameter, hasil, awal register 1
r1 Volatile Parameter, hasil, awal register 2
R2 Volatile Parameter, scratch register 3
r3 Volatile Parameter, scratch register 4
r4 Non-volatil
r5 Non-volatil
r6 Non-volatil
r7 Non-volatil
r8 Non-volatil
r9 Non-volatil
r10 Non-volatil
r11 Non-volatil Penunjuk bingkai
r12 Volatile Register awal panggilan intra-prosedur
r13 (SP) Non-volatil Penunjuk tumpukan
r14 (LR) Non-volatil Daftar tautan
r15 (PC) Non-volatil Penghitung program

Untuk detail tentang cara menggunakan parameter dan daftar nilai pengembalian, lihat bagian Parameter Passing di artikel ini.

Windows menggunakan r11 untuk berjalan cepat dari bingkai tumpukan. Untuk informasi selengkapnya, lihat bagian Stack Walking. Karena persyaratan ini, r11 harus selalu menunjuk ke tautan paling atas dalam rantai. Jangan gunakan r11 untuk tujuan umum, karena kode Anda tidak akan menghasilkan stack walk yang benar selama analisis.

Register VFP

Windows hanya mendukung varian ARM yang memiliki dukungan coprocessor VFPv3-D32. Ini berarti register floating-point selalu ada dan dapat diandalkan untuk melewati parameter. Dan, set lengkap 32 register tersedia untuk digunakan. VFP mendaftar dan penggunaannya dirangkum dalam tabel ini:

Single Ganda Quads Volatile? Peran
s0-s3 d0-d1 q0 Volatile Parameter, hasil, awal mendaftar
s4-s7 d2-d3 q1 Volatile Parameter, scratch register
s8-s11 d4-d5 q2 Volatile Parameter, scratch register
s12-s15 d6-d7 q3 Volatile Parameter, scratch register
s16-s19 d8-d9 q4 Non-volatil
s20-s23 d10-d11 Q5 Non-volatil
s24-s27 d12-d13 q6 Non-volatil
s28-s31 d14-d15 Q7 Non-volatil
d16-d31 q8-q15 Volatile

Tabel berikutnya mengilustrasikan bitfields floating-point status and control register (FPSCR):

Bit Makna Volatile? Peran
31-28 NZCV Volatile Bendera status
27 QC Volatile Saturasi kumulatif
26 AHP Non-volatil Kontrol setengah presisi alternatif
25 DN Non-volatil Kontrol mode NaN default
24 FZ Non-volatil Kontrol mode flush-to-zero
23-22 RMode Non-volatil Kontrol mode pembulatan
21-20 Langkahnya Non-volatil Vektor Stride, harus selalu 0
18-16 Len Non-volatil Panjang Vektor, harus selalu 0
15, 12-8 IDE, IXE, dan sebagainya Non-volatil Bit pengaktifan perangkap pengecualian, harus selalu 0
7, 4-0 IDC, IXC, dan sebagainya Volatile Bendera pengecualian kumulatif

Pengecualian titik mengambang

Sebagian besar perangkat keras ARM tidak mendukung pengecualian titik pecahan IEEE. Pada varian prosesor yang memang memiliki pengecualian floating-point perangkat keras, kernel Windows secara diam-diam menangkap pengecualian dan secara implisit menonaktifkannya di register FPSCR. Tindakan ini memastikan perilaku yang dinormalisasi di seluruh varian prosesor. Jika tidak, kode yang dikembangkan pada platform yang tidak memiliki dukungan pengecualian dapat menerima pengecualian tak terduga ketika berjalan pada platform yang memang memiliki dukungan pengecualian.

Pengoperasian parameter

Windows pada ARM ABI mengikuti aturan ARM untuk parameter yang lolos untuk fungsi non-variadik. Aturan ABI mencakup ekstensi VFP dan Advanced SIMD. Aturan ini mengikuti Prosedur Panggil Standar untuk Arsitektur ARM, dikombinasikan dengan ekstensi VFP. Secara default, empat argumen bilangan bulat pertama dan hingga delapan argumen floating-point atau vektor diteruskan dalam register. Argumen lebih lanjut diteruskan pada tumpukan. Argumen ditetapkan ke register atau tumpukan dengan menggunakan prosedur ini:

Tahap A: Inisialisasi

Inisialisasi dilakukan tepat sekali, sebelum pemrosesan argumen dimulai:

  1. Next Core Register Number (NCRN) diatur ke r0.

  2. Register VFP ditandai sebagai tidak dialokasikan.

  3. Alamat Argumen Bertumpuk Berikutnya (NSAA) diatur ke SP saat ini.

  4. Jika fungsi yang mengembalikan hasil dalam memori dipanggil, maka alamat untuk hasil ditempatkan dalam r0 dan NCRN diatur ke r1.

Tahap B: Pra-padding dan ekstensi argumen

Untuk setiap argumen dalam daftar, aturan pencocokan pertama dari daftar berikut diterapkan:

  1. Jika argumen adalah tipe komposit yang ukurannya tidak dapat ditentukan secara statis oleh penelepon dan penerima panggilan, argumen disalin ke memori dan digantikan oleh penunjuk ke salinan.

  2. Jika argumen adalah byte atau setengah kata 16-bit, maka argumen tersebut diperluas nol atau diperluas tanda ke kata lengkap 32-bit dan diperlakukan sebagai argumen 4-byte.

  3. Jika argumen adalah jenis komposit, ukurannya dibulatkan ke atas ke kelipatan 4 terdekat.

Tahap C: Penugasan argumen untuk mendaftar dan menumpuk

Untuk setiap argumen dalam daftar, aturan berikut diterapkan pada gilirannya hingga argumen dialokasikan:

  1. Jika argumen adalah jenis VFP dan ada cukup register VFP yang tidak dialokasikan berturut-turut dari jenis yang sesuai, maka argumen dialokasikan ke urutan bernomor terendah dari register tersebut.

  2. Jika argumen adalah jenis VFP, semua register yang tidak dialokasikan yang tersisa ditandai sebagai tidak tersedia. NSAA disesuaikan ke atas hingga diratakan dengan benar untuk jenis argumen dan argumen disalin ke tumpukan pada NSAA yang disesuaikan. NSAA kemudian bertambah berdasarkan ukuran argumen.

  3. Jika argumen memerlukan perataan 8-byte, NCRN dibulatkan ke atas ke nomor register genap berikutnya.

  4. Jika ukuran argumen dalam kata-kata 32-bit tidak lebih dari r4 minus NCRN, argumen disalin ke dalam register inti, dimulai dari NCRN, dengan bit paling tidak signifikan yang menempati register bernomor bawah. NCRN bertambah bertahap dengan jumlah register yang digunakan.

  5. Jika NCRN kurang dari r4 dan NSAA sama dengan SP, argumen dibagi antara register inti dan tumpukan. Bagian pertama dari argumen disalin ke dalam register inti, mulai dari NCRN, hingga dan termasuk r3. Argumen lainnya disalin ke tumpukan, dimulai dari NSAA. NCRN diatur ke r4 dan NSAA bertambah berdasarkan ukuran argumen dikurangi jumlah yang diteruskan dalam register.

  6. Jika argumen memerlukan perataan 8 byte, NSAA dibulatkan ke atas ke alamat rata 8-byte berikutnya.

  7. Argumen disalin ke dalam memori di NSAA. NSAA bertambah berdasarkan ukuran argumen.

Register VFP tidak digunakan untuk fungsi variadik, dan aturan Tahap C 1 dan 2 diabaikan. Ini berarti bahwa fungsi variadik dapat dimulai dengan pendorongan opsional {r0-r3} untuk menambahkan argumen register ke argumen tambahan yang diteruskan oleh pemanggil, lalu mengakses seluruh daftar argumen langsung dari tumpukan.

Nilai jenis bilangan bulat dikembalikan dalam r0, secara opsional diperluas ke r1 untuk nilai pengembalian 64-bit. Nilai jenis floating-point atau SIMD VFP/NEON dikembalikan dalam s0, d0, atau q0, yang sesuai.

Tumpukan

Tumpukan harus selalu tetap rata 4-byte, dan harus selaras 8-byte pada batas fungsi apa pun. Diperlukan untuk mendukung penggunaan sering operasi yang saling mengunci pada variabel tumpukan 64-bit. ARM EABI menyatakan bahwa tumpukan selaras 8 byte pada antarmuka publik apa pun. Untuk konsistensi, Windows pada ARM ABI menganggap batas fungsi apa pun sebagai antarmuka publik.

Fungsi yang harus menggunakan penunjuk bingkai—misalnya, fungsi yang memanggil alloca atau yang mengubah penunjuk tumpukan secara dinamis—harus mengatur penunjuk bingkai di r11 dalam prolog fungsi dan membiarkannya tidak berubah hingga epilog. Fungsi yang tidak memerlukan penunjuk bingkai harus melakukan semua pembaruan tumpukan di prolog dan membiarkan penunjuk tumpukan tidak berubah sampai epilog.

Fungsi yang mengalokasikan 4 KB atau lebih pada tumpukan harus memastikan bahwa setiap halaman sebelum halaman akhir disentuh secara berurutan. Urutan ini memastikan bahwa tidak ada kode yang dapat "melompati" halaman penjaga yang digunakan Windows untuk memperluas tumpukan. Biasanya, perluasan dilakukan oleh pembantu __chkstk , yang melewati total alokasi tumpukan dalam byte dibagi 4 dalam r4, dan yang mengembalikan jumlah alokasi tumpukan akhir dalam byte kembali dalam r4.

Zona merah

Area 8-byte tepat di bawah penunjuk tumpukan saat ini dicadangkan untuk analisis dan patching dinamis. Ini memungkinkan kode yang dihasilkan dengan hati-hati untuk dimasukkan, yang menyimpan 2 register di [sp, #-8] dan untuk sementara menggunakannya untuk tujuan arbitrer. Kernel Windows menjamin bahwa 8 byte tersebut tidak akan ditimpa jika pengecualian atau gangguan terjadi dalam mode pengguna dan mode kernel.

Tumpukan kernel

Tumpukan mode kernel default di Windows adalah tiga halaman (12 KB). Berhati-hatilah untuk tidak membuat fungsi yang memiliki buffer tumpukan besar dalam mode kernel. Gangguan bisa datang dengan ruang kepala tumpukan yang sangat kecil dan menyebabkan cek bug panik tumpukan.

Spesifikasi C/C++

Enumerasi adalah jenis bilangan bulat 32-bit kecuali setidaknya satu nilai dalam enumerasi memerlukan penyimpanan kata ganda 64-bit. Dalam hal ini, enumerasi dipromosikan ke jenis bilangan bulat 64-bit.

wchar_t didefinisikan setara dengan unsigned short, untuk mempertahankan kompatibilitas dengan platform lain.

Stack berjalan

Kode Windows dikompilasi dengan pointer bingkai diaktifkan (/Oy (Frame-Pointer Omission)) untuk mengaktifkan stack walking cepat. Umumnya, r11 mendaftar menunjuk ke tautan berikutnya dalam rantai, yang merupakan pasangan {r11, lr} yang menentukan penunjuk ke bingkai sebelumnya pada tumpukan dan alamat pengembalian. Kami menyarankan agar kode Anda juga mengaktifkan penunjuk bingkai untuk pembuatan profil dan pelacakan yang ditingkatkan.

Pembukaan pengecualian

Stack unwinding selama penanganan pengecualian diaktifkan oleh penggunaan kode unwind. Kode unwind adalah urutan byte yang disimpan di bagian .xdata dari gambar yang dapat dieksekusi. Mereka menjelaskan pengoperasian prolog fungsi dan kode epilog secara abstrak, sehingga efek prolog fungsi dapat dibatalkan sebagai persiapan untuk melepas penat ke bingkai tumpukan pemanggil.

ARM EABI menentukan model unwinding pengecualian yang menggunakan kode unwind. Namun, spesifikasi ini tidak cukup untuk melepas lelah di Windows, yang harus menangani kasus di mana prosesor berada di tengah prolog atau epilog fungsi. Untuk informasi selengkapnya tentang Windows pada data pengecualian ARM dan unwinding, lihat Penanganan Pengecualian ARM.

Sebaiknya kode yang dihasilkan secara dinamis dijelaskan dengan menggunakan tabel fungsi dinamis yang ditentukan dalam panggilan ke RtlAddFunctionTable dan fungsi terkait, sehingga kode yang dihasilkan dapat berpartisipasi dalam penanganan pengecualian.

Penghitung siklus

Prosesor ARM yang menjalankan Windows diperlukan untuk mendukung penghitung siklus, tetapi menggunakan penghitung secara langsung dapat menyebabkan masalah. Untuk menghindari masalah ini, Windows di ARM menggunakan opcode yang tidak terdefinisi untuk meminta nilai penghitung siklus 64-bit yang dinormalisasi. Dari C atau C++, gunakan __rdpmccntr64 intrinsik untuk memancarkan opcode yang sesuai; dari rakitan, gunakan __rdpmccntr64 instruksi. Membaca penghitung siklus membutuhkan sekitar 60 siklus pada Cortex-A9.

Penghitung adalah penghitung siklus sejati, bukan jam; oleh karena itu, frekuensi penghitungan bervariasi menurut frekuensi prosesor. Jika Anda ingin mengukur waktu jam yang berlalu, gunakan QueryPerformanceCounter.

Baca juga

Masalah Umum Migrasi VISUAL C++ ARM
Penanganan Pengecualian ARM