Bagikan melalui


Penanganan Pengecualian ARM

Windows di ARM menggunakan mekanisme penanganan pengecualian terstruktur yang sama untuk pengecualian yang dihasilkan perangkat keras asinkron dan pengecualian yang dihasilkan perangkat lunak sinkron. Penanganan pengecualian khusus bahasa dibangun di atas penanganan pengecualian terstruktur Windows dengan menggunakan fungsi pembantu bahasa. Dokumen ini menjelaskan penanganan pengecualian di Windows di ARM, dan pembantu bahasa baik perakit Microsoft ARM maupun kompilator MSVC menghasilkan.

Penanganan Pengecualian ARM

Windows di ARM menggunakan kode unwind untuk mengontrol pelepasan tumpukan selama penanganan pengecualian terstruktur (SEH). Kode unwind adalah urutan byte yang disimpan di bagian .xdata gambar yang dapat dieksekusi. Kode-kode ini menjelaskan pengoperasian prolog fungsi dan kode epilog dengan cara abstrak. Handler menggunakannya untuk membatalkan efek prolog fungsi saat melepas ke bingkai tumpukan pemanggil.

ARM EABI (antarmuka biner aplikasi yang disematkan) menentukan model untuk pembatalan pengecualian yang menggunakan kode unwind. Model ini tidak cukup untuk melepas seh di Windows. Ini harus menangani kasus asinkron di mana prosesor berada di tengah prolog atau epilog fungsi. Windows juga memisahkan kontrol unwinding menjadi unwinding tingkat fungsi dan unwinding cakupan khusus bahasa, yang disatukan dalam ARM EABI. Untuk alasan ini, Windows di ARM menentukan detail lebih lanjut untuk melepas penat data dan prosedur.

Asumsi

Gambar yang dapat dieksekusi untuk Windows di ARM menggunakan format Portable Executable (PE). Untuk informasi selengkapnya, lihat Format PE. Informasi penanganan pengecualian disimpan di bagian .pdata dan .xdata gambar.

Mekanisme penanganan pengecualian membuat asumsi tertentu tentang kode yang mengikuti ABI untuk Windows di ARM:

  • Ketika pengecualian terjadi dalam tubuh fungsi, handler dapat membatalkan operasi prolog, atau melakukan operasi epilog dengan cara yang lebih maju. Keduanya harus menghasilkan hasil yang identik.

  • Prolog dan epilog cenderung mencerminkan satu sama lain. Fitur ini dapat digunakan untuk mengurangi ukuran metadata yang diperlukan untuk menggambarkan unwinding.

  • Fungsi cenderung relatif kecil. Beberapa pengoptimalan mengandalkan pengamatan ini untuk pengemasan data yang efisien.

  • Jika kondisi ditempatkan pada epilog, itu berlaku sama untuk setiap instruksi di epilog.

  • Jika prolog menyimpan penunjuk tumpukan (SP) di register lain, register tersebut harus tetap tidak berubah di seluruh fungsi, sehingga SP asli dapat dipulihkan kapan saja.

  • Kecuali SP disimpan dalam register lain, semua manipulasinya harus terjadi secara ketat dalam prolog dan epilog.

  • Untuk melepas bingkai tumpukan apa pun, operasi ini diperlukan:

    • Sesuaikan r13 (SP) dalam kenaikan 4 byte.

    • Memunculkan satu atau beberapa register bilangan bulat.

    • Pop satu atau beberapa VFP (titik mengambang virtual) mendaftar.

    • Salin nilai register arbitrer ke r13 (SP).

    • Muat SP dari tumpukan dengan menggunakan operasi pasca-penurunan kecil.

    • Uraikan salah satu dari beberapa jenis bingkai yang terdefinisi dengan baik.

.pdata Catatan

Rekaman .pdata dalam gambar format PE adalah array yang diurutkan dari item dengan panjang tetap yang menjelaskan setiap fungsi manipulasi tumpukan. Fungsi daun (fungsi yang tidak memanggil fungsi lain) tidak memerlukan .pdata rekaman saat tidak memanipulasi tumpukan. (Artinya, mereka tidak memerlukan penyimpanan lokal dan tidak perlu menyimpan atau memulihkan register non-volatil.). Rekaman untuk fungsi-fungsi ini dapat dihilangkan dari bagian .pdata untuk menghemat ruang. Operasi lepas dari salah satu fungsi ini hanya dapat menyalin alamat pengembalian dari Link Register (LR) ke penghitung program (PC) untuk berpindah ke pemanggil.

Setiap .pdata catatan untuk ARM panjangnya 8 byte. Format umum rekaman menempatkan alamat virtual relatif (RVA) fungsi dimulai dalam kata 32-bit pertama, diikuti dengan kata kedua yang berisi penunjuk ke blok panjang .xdata variabel, atau kata yang dikemas yang menjelaskan urutan unwinding fungsi kanonis, seperti yang ditunjukkan dalam tabel ini:

Offset Kata Bit Tujuan
0 0-31 Function Start RVA adalah RVA 32-bit dari awal fungsi. Jika fungsi berisi kode jempol, bit rendah alamat ini harus diatur.
1 0-1 Flag adalah bidang 2-bit yang menunjukkan cara menginterpretasikan 30 bit sisa kata kedua .pdata . Jika Flag adalah 0, maka bit yang tersisa membentuk RVA Informasi Pengecualian (dengan dua bit rendah secara implisit 0). Jika Flag bukan nol, bit yang tersisa membentuk struktur Data Unwind Kemasan.
1 2-31 Informasi Pengecualian RVA atau Data Unwind Yang Dikemas.

Informasi Pengecualian RVA adalah alamat struktur informasi pengecualian panjang variabel, yang disimpan di bagian ..xdata Data ini harus diratakan 4 byte.

Packed Unwind Data adalah deskripsi terkompresi tentang operasi yang diperlukan untuk melepas lelah dari fungsi, dengan asumsi bentuk kanonis. Dalam hal ini, tidak ada .xdata catatan yang diperlukan.

Data Unwind Yang Dikemas

Untuk fungsi yang prolog dan epilognya mengikuti bentuk kanonis yang dijelaskan di bawah ini, data unwind yang dikemas dapat digunakan. Ini menghilangkan kebutuhan akan .xdata rekaman dan secara signifikan mengurangi ruang yang diperlukan untuk menyediakan data yang dilepaskan. Prolog kanonis dan epilog dirancang untuk memenuhi persyaratan umum fungsi sederhana yang tidak memerlukan handler pengecualian, dan melakukan operasi penyiapan dan teardown dalam urutan standar.

Tabel ini memperlihatkan format .pdata rekaman yang telah mengemas data unwind:

Offset Kata Bit Tujuan
0 0-31 Function Start RVA adalah RVA 32-bit dari awal fungsi. Jika fungsi berisi kode jempol, bit rendah alamat ini harus diatur.
1 0-1 Flag adalah bidang 2-bit yang memiliki arti ini:

- 00 = data unwind yang dikemas tidak digunakan; bit yang tersisa menunjuk ke .xdata rekaman.
- 01 = data unwind yang dikemas.
- 10 = data unwind yang dikemas di mana fungsi diasumsikan tidak memiliki prolog. Ini berguna untuk menjelaskan fragmen fungsi yang tidak berduka dengan awal fungsi.
- 11 = dicadangkan.
1 2-12 Function Length adalah bidang 11-bit yang menyediakan panjang seluruh fungsi dalam byte dibagi 2. Jika fungsi lebih besar dari 4K byte, rekaman lengkap .xdata harus digunakan sebagai gantinya.
1 13-14 Ret adalah bidang 2-bit yang menunjukkan bagaimana fungsi mengembalikan:

- 00 = kembali melalui pop {pc} ( L bit bendera harus diatur ke 1 dalam kasus ini).
- 01 = return dengan menggunakan cabang 16-bit.
- 10 = kembali dengan menggunakan cabang 32-bit.
- 11 = tidak ada epilog sama sekali. Ini berguna untuk menggambarkan fragmen fungsi yang tidak disukai yang mungkin hanya mengandung prolog, tetapi epilognya berada di tempat lain.
1 15 H adalah bendera 1-bit yang menunjukkan apakah fungsi "rumah" parameter bilangan bulat mendaftar (r0-r3) dengan mendorongnya di awal fungsi, dan membatalkan alokasi tumpukan 16 byte sebelum kembali. (0 = tidak mendaftar di rumah, 1 = homes register.)
1 16-18 Reg adalah bidang 3-bit yang menunjukkan indeks register non-volatil terakhir yang disimpan. R Jika bit adalah 0, maka hanya register bilangan bulat yang disimpan, dan diasumsikan berada dalam rentang r4-rN, di mana N sama dengan 4 + Reg. R Jika bit adalah 1, maka hanya register floating-point yang disimpan, dan diasumsikan berada dalam rentang d8-dN, di mana N sama dengan 8 + Reg. Kombinasi R khusus = 1 dan Reg = 7 menunjukkan bahwa tidak ada register yang disimpan.
1 19 R adalah bendera 1-bit yang menunjukkan apakah register non-volatile yang disimpan adalah register bilangan bulat (0) atau register floating-point (1). Jika R diatur ke 1 dan Reg bidang diatur ke 7, tidak ada register non-volatil yang didorong.
1 20 L adalah bendera 1-bit yang menunjukkan apakah fungsi menyimpan/memulihkan LR, bersama dengan register lain yang ditunjukkan oleh Reg bidang . (0 = tidak menyimpan/memulihkan, 1 = menyimpan/memulihkan.)
1 21 C adalah bendera 1-bit yang menunjukkan apakah fungsi menyertakan instruksi tambahan untuk menyiapkan rantai bingkai untuk berjalan tumpukan cepat (1) atau tidak (0). Jika bit ini diatur, r11 secara implisit ditambahkan ke daftar register non-volatil bilangan bulat yang disimpan. (Lihat batasan di bawah ini jika C bendera digunakan.)
1 22-31 Stack Adjust adalah bidang 10-bit yang menunjukkan jumlah byte tumpukan yang dialokasikan untuk fungsi ini, dibagi 4. Namun, hanya nilai antara 0x3F3 0x000 yang dapat dikodekan secara langsung. Fungsi yang mengalokasikan lebih dari 4044 byte tumpukan harus menggunakan rekaman lengkap .xdata . Stack Adjust Jika bidang 0x3F4 atau lebih besar, maka 4 bit rendah memiliki arti khusus:

- Bit 0-1 menunjukkan jumlah kata penyesuaian tumpukan (1-4) minus 1.
- Bit 2 diatur ke 1 jika prolog menggabungkan penyesuaian ini ke dalam operasi pendorongannya.
- Bit 3 diatur ke 1 jika epilog menggabungkan penyesuaian ini ke dalam operasi pop-nya.

Karena kemungkinan redundansi dalam pengodean di atas, pembatasan ini berlaku:

  • C Jika bendera diatur ke 1:

    • Bendera L juga harus diatur ke 1, karena rantai bingkai memerlukan r11 dan LR.

    • r11 tidak boleh disertakan dalam kumpulan register yang dijelaskan oleh Reg. Artinya, jika r4-r11 didorong, Reg seharusnya hanya menggambarkan r4-r10, karena C bendera menyiratkan r11.

  • Ret Jika bidang diatur ke 0, L bendera harus diatur ke 1.

Melanggar pembatasan ini menyebabkan urutan yang tidak didukung.

Untuk tujuan diskusi di bawah ini, dua bendera semu berasal dari Stack Adjust:

  • PF atau "lipatan prolog" menunjukkan bahwa Stack Adjust 0x3F4 atau lebih besar dan bit 2 diatur.

  • EF atau "lipatan epilog" menunjukkan bahwa Stack Adjust 0x3F4 atau lebih besar dan bit 3 diatur.

Prolog untuk fungsi kanonis mungkin memiliki hingga 5 instruksi (perhatikan bahwa 3a dan 3b saling eksklusif):

Instruksi Opcode diasumsikan ada jika: Ukuran Opcode Unwind Codes
1 H==1 16 push {r0-r3} 04
2 C==1 atau L==1 atau R==0 atau PF==1 16/32 push {registers} 80-BF/D0-DF/EC-ED
3a C==1 dan (R==1 dan PF==0) 16 mov r11,sp FB
3b C==1 dan (R==0 atau PF==1) 32 add r11,sp,#xx FC
4 R==1 dan Reg != 7 32 vpush {d8-dE} E0-E7
5 Stack Adjust != 0 dan PF==0 16/32 sub sp,sp,#xx 00-7F/E8-EB

Instruksi 1 selalu ada jika H bit diatur ke 1.

Untuk menyiapkan rantai bingkai, ada instruksi 3a atau 3b jika C bit diatur. Ini adalah 16-bit mov jika tidak ada register selain r11 dan LR yang didorong; jika tidak, itu adalah 32-bit add.

Jika penyesuaian yang tidak dilipat ditentukan, instruksi 5 adalah penyesuaian tumpukan eksplisit.

Instruksi 2 dan 4 ditetapkan berdasarkan apakah pendorongan diperlukan. Tabel ini meringkas register mana yang disimpan berdasarkan Cbidang , , LR, dan PF . Dalam semua kasus, N sama dengan Reg + 4, E sama dengan Reg + 8, dan S sama dengan (~Stack Adjust) & 3.

C L R PF Daftar Bilangan Bulat Didorong VFP Registers didorong
0 0 0 0 r4 - r*N* tidak ada
0 0 0 1 r*S* - r*N* tidak ada
0 0 1 0 tidak ada d8 - d*E*
0 0 1 1 r*S* - r3 d8 - d*E*
0 1 0 0 r4 - r*N*, LR tidak ada
0 1 0 1 r*S* - r*N*, LR tidak ada
0 1 1 0 LR d8 - d*E*
0 1 1 1 r*S* - r3, LR d8 - d*E*
1 0 0 0 (pengodean tidak valid) (pengodean tidak valid)
1 0 0 1 (pengodean tidak valid) (pengodean tidak valid)
1 0 1 0 (pengodean tidak valid) (pengodean tidak valid)
1 0 1 1 (pengodean tidak valid) (pengodean tidak valid)
1 1 0 0 r4 - r*N*, r11, LR tidak ada
1 1 0 1 r*S* - r*N*, r11, LR tidak ada
1 1 1 0 r11, LR d8 - d*E*
1 1 1 1 r*S* - r3, r11, LR d8 - d*E*

Epilog untuk fungsi kanonis mengikuti bentuk yang sama, tetapi sebaliknya dan dengan beberapa opsi tambahan. Epilog mungkin memiliki panjang hingga 5 instruksi, dan bentuknya benar-benar ditentukan oleh bentuk prolog.

Instruksi Opcode diasumsikan ada jika: Ukuran Opcode
6 Stack Adjust!=0 dan EF==0 16/32 add sp,sp,#xx
7 R==1 dan Reg!=7 32 vpop {d8-dE}
8 C==1 atau (L==1 dan (H==0 atau Ret !=0)) atau R==0 atau EF==1 16/32 pop {registers}
9a H==1 dan (L==0 atau Ret!=0) 16 add sp,sp,#0x10
9b H==1 dan L==1 dan Ret==0 32 ldr pc,[sp],#0x14
10a Ret==1 16 bx reg
10b Ret==2 32 b address

Instruksi 6 adalah penyesuaian tumpukan eksplisit jika penyesuaian yang tidak dilipat ditentukan. Karena PF independen dari EF, dimungkinkan untuk memiliki instruksi 5 hadir tanpa instruksi 6, atau sebaliknya.

Instruksi 7 dan 8 menggunakan logika yang sama dengan prolog untuk menentukan register mana yang dipulihkan dari tumpukan, tetapi dengan tiga perubahan ini: pertama, EF digunakan sebagai ganti PF; kedua, jika Ret = 0 dan H = 0, maka LR diganti dengan PC dalam daftar register dan epilog berakhir segera; ketiga, jika Ret = 0 dan H = 1, kemudian LR dihilangkan dari daftar register dan diisi oleh instruksi 9b.

Jika H diatur, maka instruksi 9a atau 9b ada. Instruksi 9a digunakan ketika Ret nonzero, yang juga menyiratkan keberadaan 10a atau 10b. Jika L=1, maka LR dimunculkan sebagai bagian dari instruksi 8. Instruksi 9b digunakan ketika L adalah 1 dan Ret nol, untuk menunjukkan akhir awal ke epilog, dan untuk mengembalikan dan menyesuaikan tumpukan pada saat yang sama.

Jika epilog belum berakhir, maka instruksi 10a atau 10b ada, untuk menunjukkan cabang 16-bit atau 32-bit, berdasarkan nilai Ret.

.xdata Catatan

Ketika format unwind yang dikemas tidak mencukupi untuk menggambarkan pelekatan fungsi, rekaman panjang .xdata variabel harus dibuat. Alamat rekaman ini disimpan di kata .pdata kedua rekaman. Format .xdata adalah sekumpulan kata panjang variabel yang dikemas yang memiliki empat bagian:

  1. Header 1 atau 2 kata yang menjelaskan ukuran .xdata keseluruhan struktur dan menyediakan data fungsi utama. Kata kedua hanya ada jika bidang Jumlah Epilog dan Kata Kode keduanya diatur ke 0. Bidang dipecah dalam tabel ini:

    Word Bit Tujuan
    0 0-17 Function Length adalah bidang 18-bit yang menunjukkan panjang total fungsi dalam byte, dibagi 2. Jika fungsi lebih besar dari 512 KB, maka beberapa .pdata dan .xdata rekaman harus digunakan untuk menjelaskan fungsi. Untuk detailnya, lihat bagian Fungsi Besar dalam dokumen ini.
    0 18-19 Vers adalah bidang 2-bit yang menjelaskan versi sisanya.xdata. Hanya versi 0 yang saat ini ditentukan; nilai 1-3 dicadangkan.
    0 20 X adalah bidang 1-bit yang menunjukkan adanya (1) atau tidak adanya (0) data pengecualian.
    0 21 E adalah bidang 1-bit yang menunjukkan bahwa informasi yang menjelaskan satu epilog dikemas ke dalam header (1) daripada memerlukan kata cakupan tambahan nanti (0).
    0 22 F adalah bidang 1-bit yang menunjukkan bahwa rekaman ini menjelaskan fragmen fungsi (1) atau fungsi penuh (0). Fragmen menyiratkan bahwa tidak ada prolog dan bahwa semua pemrosesan prolog harus diabaikan.
    0 23-27 Jumlah Epilog adalah bidang 5-bit yang memiliki dua arti, tergantung pada status E bit:

    - Jika E adalah 0, bidang ini adalah hitungan dari jumlah total cakupan epilog yang dijelaskan dalam bagian 2. Jika ada lebih dari 31 cakupan dalam fungsi, bidang ini dan bidang Kata Kode harus diatur ke 0 untuk menunjukkan bahwa kata ekstensi diperlukan.
    - Jika E adalah 1, bidang ini menentukan indeks kode unwind pertama yang menjelaskan satu-satunya epilog.
    0 28-31 Kata Kode adalah bidang 4-bit yang menentukan jumlah kata 32-bit yang diperlukan untuk berisi semua kode unwind di bagian 4. Jika lebih dari 15 kata diperlukan untuk lebih dari 63 byte kode unwind, bidang ini dan bidang Jumlah Epilog keduanya harus diatur ke 0 untuk menunjukkan bahwa kata ekstensi diperlukan.
    1 0-15 Extended Epilogue Count adalah bidang 16-bit yang menyediakan lebih banyak ruang untuk mengodekan sejumlah besar epilog yang luar biasa besar. Kata ekstensi yang berisi bidang ini hanya ada jika bidang Jumlah Epilog dan Kata Kode di kata header pertama keduanya diatur ke 0.
    1 16-23 Extended Code Words adalah bidang 8-bit yang menyediakan lebih banyak ruang untuk mengodekan sejumlah besar kata kode unwind yang luar biasa. Kata ekstensi yang berisi bidang ini hanya ada jika bidang Jumlah Epilog dan Kata Kode di kata header pertama keduanya diatur ke 0.
    1 24-31 Dicadangkan
  2. Setelah data pengecualian (jika E bit di header diatur ke 0) adalah daftar informasi tentang cakupan epilog, yang dikemas satu ke kata dan disimpan untuk meningkatkan memulai offset. Setiap cakupan berisi bidang-bidang ini:

    Bit Tujuan
    0-17 Epilog Start Offset adalah bidang 18-bit yang menjelaskan offset epilog, dalam byte dibagi 2, relatif terhadap awal fungsi.
    18-19 Res adalah bidang 2-bit yang dicadangkan untuk ekspansi di masa mendatang. Nilainya harus 0.
    20-23 Kondisi adalah bidang 4-bit yang memberikan kondisi di mana epilog dijalankan. Untuk epilog tanpa syarat, epilog harus diatur ke 0xE, yang menunjukkan "selalu". (Epilog harus sepenuhnya bersyarat atau sepenuhnya tidak bersyarat, dan dalam mode Thumb-2, epilog dimulai dengan instruksi pertama setelah opcode IT.)
    24-31 Indeks Mulai Epilog adalah bidang 8-bit yang menunjukkan indeks byte dari kode unwind pertama yang menjelaskan epilog ini.
  3. Setelah daftar cakupan epilog muncul array byte yang berisi kode unwind, yang dijelaskan secara rinci di bagian Unwind Codes di artikel ini. Array ini diisi di akhir ke batas kata lengkap terdekat. Byte disimpan dalam urutan little-endian sehingga mereka dapat langsung diambil dalam mode little-endian.

  4. Jika bidang X di header adalah 1, byte kode unwind diikuti oleh informasi handler pengecualian. Ini terdiri dari satu RVA Handler Pengecualian yang berisi alamat handler pengecualian, diikuti segera oleh jumlah data (panjang variabel) yang diperlukan oleh handler pengecualian.

Catatan .xdata dirancang sehingga dimungkinkan untuk mengambil 8 byte pertama dan menghitung ukuran penuh rekaman, tidak termasuk panjang data pengecualian berukuran variabel yang mengikuti. Cuplikan kode ini menghitung ukuran rekaman:

ULONG ComputeXdataSize(PULONG Xdata)
{
    ULONG Size;
    ULONG EpilogueScopes;
    ULONG UnwindWords;

    if ((Xdata[0] >> 23) != 0) {
        Size = 4;
        EpilogueScopes = (Xdata[0] >> 23) & 0x1f;
        UnwindWords = (Xdata[0] >> 28) & 0x0f;
    } else {
        Size = 8;
        EpilogueScopes = Xdata[1] & 0xffff;
        UnwindWords = (Xdata[1] >> 16) & 0xff;
    }

    if (!(Xdata[0] & (1 << 21))) {
        Size += 4 * EpilogueScopes;
    }

    Size += 4 * UnwindWords;

    if (Xdata[0] & (1 << 20)) {
        Size += 4;  // Exception handler RVA
    }

    return Size;
}

Meskipun prolog dan setiap epilog memiliki indeks ke dalam kode unwind, tabel dibagikan di antara mereka. Tidak jarang mereka semua dapat berbagi kode unwind yang sama. Kami menyarankan agar penulis kompilator mengoptimalkan untuk kasus ini, karena indeks terbesar yang dapat ditentukan adalah 255, dan yang membatasi jumlah total kode unwind yang mungkin untuk fungsi tertentu.

Unwind Codes

Array kode unwind adalah kumpulan urutan instruksi yang menjelaskan dengan tepat cara membatalkan efek prolog, dalam urutan operasi harus dibatalkan. Kode unwind adalah set instruksi mini, dikodekan sebagai string byte. Ketika eksekusi selesai, alamat pengembalian ke fungsi panggilan ada di register LR, dan semua register non-volatil dipulihkan ke nilainya pada saat fungsi dipanggil.

Jika pengecualian dijamin hanya pernah terjadi dalam tubuh fungsi, dan tidak pernah dalam prolog atau epilog, maka hanya satu urutan yang tidak tenang yang diperlukan. Namun, model windows unwinding memerlukan kemampuan untuk melepas lelah dari dalam prolog atau epilog yang dijalankan sebagian. Untuk mengakomodasi persyaratan ini, kode unwind telah dirancang dengan hati-hati untuk memiliki pemetaan satu-ke-satu yang tidak ambigu ke setiap opcode yang relevan dalam prolog dan epilog. Ini memiliki beberapa implikasi:

  • Dimungkinkan untuk menghitung panjang prolog dan epilog dengan menghitung jumlah kode unwind. Ini dimungkinkan bahkan dengan instruksi Thumb-2 dengan panjang variabel karena ada pemetaan yang berbeda untuk opkode 16-bit dan 32-bit.

  • Dengan menghitung jumlah instruksi yang melewati awal cakupan epilog, dimungkinkan untuk melewati jumlah kode unwind yang setara, dan menjalankan sisa urutan untuk menyelesaikan unwind yang dijalankan sebagian yang dilakukan epilog.

  • Dengan menghitung jumlah instruksi sebelum akhir prolog, dimungkinkan untuk melewati jumlah kode unwind yang setara, dan menjalankan sisa urutan untuk membatalkan hanya bagian prolog yang telah menyelesaikan eksekusi.

Tabel berikut menunjukkan pemetaan dari kode unwind ke opcode. Kode yang paling umum hanya satu byte, sementara yang kurang umum membutuhkan dua, tiga, atau bahkan empat byte. Setiap kode disimpan dari byte paling signifikan hingga byte yang paling tidak signifikan. Struktur kode unwind berbeda dari pengodean yang dijelaskan dalam ARM EABI, karena kode unwind ini dirancang untuk memiliki pemetaan satu-ke-satu ke opkode dalam prolog dan epilog untuk memungkinkan pelonggaran prolog dan epilog yang dijalankan sebagian.

Byte 1 Byte 2 Byte 3 Byte 4 Opsi Penjelasan
00-7F 16 add sp,sp,#X

where X is (Code &0x7F) * 4
80-BF 00-FF 32 pop {r0-r12, lr}

di mana LR muncul jika Kode & 0x2000 dan r0-r12 muncul jika bit yang sesuai diatur dalam Kode & 0x1FFF
C0-CF 16 mov sp,rX

di mana X adalah Kode &0x0F
D0-D7 16 pop {r4-rX,lr}

di mana X (Kode & 0x03) + 4 dan LR muncul jika Kode & 0x04
D8-DF 32 pop {r4-rX,lr}

di mana X (Kode & 0x03) + 8 dan LR muncul jika Kode & 0x04
E0-E7 32 vpop {d8-dX}

di mana X adalah (Kode & 0x07) + 8
E8-EB 00-FF 32 addw sp,sp,#X

where X is (Code &0x03FF) * 4
EC-ED 00-FF 16 pop {r0-r7,lr}

di mana LR muncul jika Kode & 0x0100 dan r0-r7 muncul jika bit yang sesuai diatur dalam Kode & 0x00FF
EE 00-0F 16 Khusus Microsoft
EE 10-FF 16 Tersedia
EF 00-0F 32 ldr lr,[sp],#X

where X is (Code & 0x000F) * 4
EF 10-FF 32 Tersedia
F0-F4 - Tersedia
F5 00-FF 32 vpop {dS-dE}

di mana S adalah (Kode & 0x00F0) >> 4 dan E adalah Kode & 0x000F
F6 00-FF 32 vpop {dS-dE}

di mana S adalah ((Code & 0x00F0) >> 4) + 16 dan E adalah (Code & 0x000F) + 16
F7 00-FF 00-FF 16 add sp,sp,#X

where X is (Code & 0x00FFFF) * 4
F8 00-FF 00-FF 00-FF 16 add sp,sp,#X

where X is (Code &0x00FFFFFF) * 4
F9 00-FF 00-FF 32 add sp,sp,#X

where X is (Code & 0x00FFFF) * 4
FA 00-FF 00-FF 00-FF 32 add sp,sp,#X

where X is (Code &0x00FFFFFF) * 4
FB 16 nop (16-bit)
FC 32 nop (32-bit)
FD 16 end + nop 16-bit dalam epilog
FE 32 end + nop 32-bit dalam epilog
FF - akhir

Ini menunjukkan rentang nilai heksadesimal untuk setiap byte dalam Kode kode unwind, bersama dengan ukuran opcode Opsize dan interpretasi instruksi asli yang sesuai. Sel kosong menunjukkan kode unwind yang lebih pendek. Dalam instruksi yang memiliki nilai besar yang mencakup beberapa byte, bit yang paling signifikan disimpan terlebih dahulu. Bidang Opsize menunjukkan ukuran opcode implisit yang terkait dengan setiap operasi Thumb-2. Entri duplikat yang jelas dalam tabel dengan pengodean yang berbeda digunakan untuk membedakan antara ukuran opcode yang berbeda.

Kode unwind dirancang sehingga byte pertama kode memberi tahu ukuran total dalam byte kode dan ukuran opcode yang sesuai dalam aliran instruksi. Untuk menghitung ukuran prolog atau epilog, jalankan kode unwind dari awal urutan hingga akhir, dan gunakan tabel pencarian atau metode serupa untuk menentukan berapa lama opcode yang sesuai.

Lepaskan kode 0xFD dan 0xFE setara dengan kode akhir reguler 0xFF, tetapi memperhitungkan satu opcode nop tambahan dalam kasus epilog, baik 16-bit atau 32-bit. Untuk prolog, kode 0xFD, 0xFE, dan 0xFF sama persis. Ini menyumbang akhir bx lr epilog umum atau b <tailcall-target>, yang tidak memiliki instruksi prolog yang setara. Ini meningkatkan kemungkinan bahwa urutan unwind dapat dibagikan antara prolog dan epilog.

Dalam banyak kasus, harus dimungkinkan untuk menggunakan set kode unwind yang sama untuk prolog dan semua epilog. Namun, untuk menangani unwinding prolog dan epilog yang dijalankan sebagian, Anda mungkin harus memiliki beberapa urutan kode unwind yang bervariasi dalam urutan atau perilaku. Inilah sebabnya mengapa setiap epilog memiliki indeksnya sendiri ke dalam array unwind untuk menunjukkan tempat untuk mulai mengeksekusi.

Melepas Prolog Parsial dan Epilog

Kasus unwinding yang paling umum adalah ketika pengecualian terjadi dalam tubuh fungsi, jauh dari prolog dan semua epilog. Dalam hal ini, unwinder menjalankan kode dalam array unwind yang dimulai pada indeks 0 dan berlanjut hingga opcode akhir terdeteksi.

Ketika pengecualian terjadi saat prolog atau epilog dijalankan, bingkai tumpukan hanya dibangun sebagian, dan unwinder harus menentukan dengan tepat apa yang telah dilakukan untuk membatalkannya dengan benar.

Misalnya, pertimbangkan urutan prolog dan epilog ini:

0000:   push  {r0-r3}         ; 0x04
0002:   push  {r4-r9, lr}     ; 0xdd
0006:   mov   r7, sp          ; 0xc7
...
0140:   mov   sp, r7          ; 0xc7
0142:   pop   {r4-r9, lr}     ; 0xdd
0146:   add   sp, sp, #16     ; 0x04
0148:   bx    lr

Di samping setiap opcode adalah kode unwind yang sesuai untuk menjelaskan operasi ini. Urutan kode unwind untuk prolog adalah gambar cermin dari kode unwind untuk epilog, tidak menghitung instruksi akhir. Kasus ini umum, dan merupakan alasan kode unwind untuk prolog selalu diasumsikan disimpan dalam urutan terbalik dari urutan eksekusi prolog. Ini memberi kita sekumpulan kode unwind umum:

0xc7, 0xdd, 0x04, 0xfd

Kode 0xFD adalah kode khusus untuk akhir urutan yang berarti bahwa epilog adalah satu instruksi 16-bit lebih lama dari prolog. Hal ini membuat berbagi kode unwind yang lebih besar dapat dilakukan.

Dalam contoh, jika pengecualian terjadi saat isi fungsi antara prolog dan epilog dijalankan, pelepasan dimulai dengan kasus epilog, pada offset 0 dalam kode epilog. Ini sesuai dengan 0x140 offset dalam contoh. Unwinder menjalankan urutan unwind penuh, karena tidak ada pembersihan yang dilakukan. Jika pengecualian terjadi satu instruksi setelah awal kode epilog, unwinder dapat berhasil melepas lelah dengan melewati kode unwind pertama. Mengingat pemetaan satu-ke-satu antara kode opcode dan unwind, jika melepas lelah dari instruksi n di epilog, unwinder harus melewati kode n unwind pertama.

Logika serupa bekerja secara terbalik untuk prolog. Jika melepas kelelahan dari offset 0 dalam prolog, tidak ada yang harus dieksekusi. Jika melepas lelah dari satu instruksi, urutan unwind harus memulai satu kode unwind dari akhir karena kode unwind prolog disimpan dalam urutan terbalik. Dalam kasus umum, jika melepas lelah dari instruksi n dalam prolog, unwinding harus mulai mengeksekusi pada n kode unwind dari akhir daftar kode.

Kode prolog dan epilog unwind tidak selalu cocok persis. Dalam hal ini, array kode unwind mungkin harus berisi beberapa urutan kode. Untuk menentukan offset untuk mulai memproses kode, gunakan logika ini:

  1. Jika melepas lelah dari dalam isi fungsi, mulai eksekusi kode unwind pada indeks 0 dan lanjutkan hingga opcode akhir tercapai.

  2. Jika melepas lelah dari dalam epilog, gunakan indeks awal khusus epilog yang disediakan oleh cakupan epilog. Hitung berapa banyak byte PC dari awal epilog. Lewati maju melalui kode unwind sampai semua instruksi yang sudah dijalankan diperhitungkan. Jalankan urutan unwind mulai pada saat itu.

  3. Jika melepas lelah dari dalam prolog, mulai dari indeks 0 dalam kode unwind. Hitung panjang kode prolog dari urutan, lalu hitung berapa banyak byte PC dari akhir prolog. Lewati maju melalui kode unwind sampai semua instruksi yang tidak tereksperimentasi diperhitungkan. Jalankan urutan unwind mulai pada saat itu.

Kode unwind untuk prolog harus selalu menjadi yang pertama dalam array. mereka juga kode yang digunakan untuk melepas lelah dalam kasus umum melepas lelah dari dalam tubuh. Setiap urutan kode khusus epilog harus segera mengikuti setelah urutan kode prolog.

Fragmen Fungsi

Untuk pengoptimalan kode, mungkin berguna untuk membagi fungsi menjadi bagian yang tidak berlawanan. Ketika ini selesai, setiap fragmen fungsi memerlukan terpisah .pdatasendiri —dan mungkin .xdata—rekam.

Dengan asumsi bahwa prolog fungsi berada di awal fungsi dan tidak dapat dipisahkan, ada empat kasus fragmen fungsi:

  • Prolog saja; semua epilog dalam fragmen lain.

  • Prolog dan satu atau beberapa epilog; lebih banyak epilog dalam fragmen lain.

  • Tidak ada prolog atau epilog; prolog dan satu atau beberapa epilog dalam fragmen lain.

  • Epilog saja; prolog dan mungkin lebih banyak epilog dalam fragmen lain.

Dalam kasus pertama, hanya prolog yang harus dijelaskan. Ini dapat dilakukan dalam bentuk ringkas .pdata dengan menjelaskan prolog secara normal dan menentukan Ret nilai 3 untuk menunjukkan tidak ada epilog. Dalam bentuk lengkapnya .xdata , ini dapat dilakukan dengan menyediakan kode unwind prolog pada indeks 0 seperti biasa, dan menentukan jumlah epilog 0.

Kasus kedua sama seperti fungsi normal. Jika hanya ada satu epilog dalam fragmen, dan berada di akhir fragmen, maka catatan ringkas .pdata dapat digunakan. Jika tidak, catatan lengkap .xdata harus digunakan. Perlu diingat bahwa offset yang ditentukan untuk awal epilog relatif terhadap awal fragmen, bukan awal asli fungsi.

Kasus ketiga dan keempat adalah varian dari kasus pertama dan kedua, masing-masing, kecuali tidak berisi prolog. Dalam situasi ini, diasumsikan bahwa ada kode sebelum dimulainya epilog dan dianggap sebagai bagian dari tubuh fungsi, yang biasanya akan dibatalkan dengan membatalkan efek prolog. Oleh karena itu, kasus-kasus ini harus dikodekan dengan prolog semu, yang menjelaskan cara melepas lelah dari dalam tubuh, tetapi yang diperlakukan sebagai panjang 0 saat menentukan apakah akan melakukan pelengkapan parsial pada awal fragmen. Atau, prolog semu ini dapat dijelaskan dengan menggunakan kode unwind yang sama dengan epilog karena mungkin melakukan operasi yang setara.

Dalam kasus ketiga dan keempat, keberadaan prolog semu ditentukan baik dengan mengatur Flag bidang rekaman ringkas .pdata ke 2, atau dengan mengatur bendera F di .xdata header ke 1. Dalam kedua kasus, pemeriksaan unwind prolog parsial diabaikan, dan semua unwind non-epilog dianggap penuh.

Fungsi Besar

Fragmen dapat digunakan untuk menggambarkan fungsi yang lebih besar dari batas 512 KB yang diberlakukan oleh bidang bit di .xdata header. Untuk menggambarkan fungsi yang lebih besar, cukup pecahkan menjadi fragmen yang lebih kecil dari 512 KB. Setiap fragmen harus disesuaikan sehingga tidak membagi epilog menjadi beberapa bagian.

Hanya fragmen pertama fungsi yang berisi prolog. Semua fragmen lainnya ditandai sebagai tidak memiliki prolog. Tergantung pada jumlah epilog, setiap fragmen mungkin berisi nol atau lebih epilog. Perlu diingat bahwa setiap cakupan epilog dalam fragmen menentukan offset awal relatif terhadap awal fragmen, bukan awal fungsi.

Jika fragmen tidak memiliki prolog dan tidak ada epilog, itu masih memerlukan sendiri .pdata—dan mungkin .xdata—catat untuk menggambarkan cara melepas lelah dari dalam tubuh fungsi.

Penyusutan-pembungkusan

Kasus fragmen fungsi yang lebih kompleks disebut shrink-wrapping. Ini adalah teknik untuk menungguhkan penyimpanan register dari awal fungsi ke nanti dalam fungsi. Ini mengoptimalkan untuk kasus sederhana yang tidak memerlukan penyimpanan register. Kasus ini memiliki dua bagian: ada wilayah luar yang mengalokasikan ruang tumpukan tetapi menyimpan sekumpulan register minimal, dan wilayah dalam yang menyimpan dan memulihkan register lain.

ShrinkWrappedFunction
    push   {r4, lr}          ; A: save minimal non-volatiles
    sub    sp, sp, #0x100    ; A: allocate all stack space up front
    ...                      ; A:
    add    r0, sp, #0xE4     ; A: prepare to do the inner save
    stm    r0, {r5-r11}      ; A: save remaining non-volatiles
    ...                      ; B:
    add    r0, sp, #0xE4     ; B: prepare to do the inner restore
    ldm    r0, {r5-r11}      ; B: restore remaining non-volatiles
    ...                      ; C:
    pop    {r4, pc}          ; C:

Fungsi yang dibungkus menyusut biasanya diharapkan untuk mengalokasikan ruang untuk penyimpanan register tambahan dalam prolog reguler, dan kemudian menyimpan register dengan menggunakan str atau stm alih-alih push. Tindakan ini menyimpan semua manipulasi stack-pointer dalam prolog asli fungsi.

Contoh fungsi yang dibungkus menyusut harus dipecah menjadi tiga wilayah, yang ditandai sebagai A, B, dan C dalam komentar. Wilayah pertama A mencakup awal fungsi hingga akhir penyimpanan non-volatil tambahan. Catatan .pdata atau .xdata harus dibangun untuk menggambarkan fragmen ini sebagai memiliki prolog dan tanpa epilog.

Wilayah tengah B mendapatkan sendiri .pdata atau .xdata catatan yang menggambarkan fragmen yang tidak memiliki prolog dan tidak ada epilog. Namun, kode unwind untuk wilayah ini masih harus ada karena dianggap sebagai isi fungsi. Kode harus menjelaskan prolog komposit yang mewakili register asli yang disimpan di prolog wilayah A dan register tambahan yang disimpan sebelum memasuki wilayah B, seolah-olah mereka diproduksi oleh satu urutan operasi.

Penyimpanan register untuk wilayah B tidak dapat dianggap sebagai "prolog dalam" karena prolog komposit yang dijelaskan untuk wilayah B harus menjelaskan prolog wilayah A dan register tambahan yang disimpan. Jika fragmen B memiliki prolog, kode unwind juga akan menyiratkan ukuran prolog itu, dan tidak ada cara untuk menggambarkan prolog komposit dengan cara yang memetakan satu-ke-satu dengan opcode yang hanya menyimpan register tambahan.

Penyimpanan register tambahan harus dianggap sebagai bagian dari wilayah A, karena sampai selesai, prolog komposit tidak secara akurat menggambarkan status tumpukan.

Wilayah terakhir C mendapatkan sendiri .pdata atau .xdata merekam, menggambarkan fragmen yang tidak memiliki prolog tetapi memang memiliki epilog.

Pendekatan alternatif juga dapat berfungsi jika manipulasi tumpukan dilakukan sebelum memasuki wilayah B dapat dikurangi menjadi satu instruksi:

ShrinkWrappedFunction
    push   {r4, lr}          ; A: save minimal non-volatile registers
    sub    sp, sp, #0xE0     ; A: allocate minimal stack space up front
    ...                      ; A:
    push   {r4-r9}           ; A: save remaining non-volatiles
    ...                      ; B:
    pop    {r4-r9}           ; B: restore remaining non-volatiles
    ...                      ; C:
    pop    {r4, pc}          ; C: restore non-volatile registers

Wawasan utama adalah bahwa pada setiap batas instruksi, tumpukan sepenuhnya konsisten dengan kode unwind untuk wilayah tersebut. Jika unwind terjadi sebelum dorongan dalam dalam dalam contoh ini, itu dianggap sebagai bagian dari wilayah A. Hanya prolog wilayah A yang tidak diungkapkan. Jika unwind terjadi setelah dorongan dalam, itu dianggap sebagai bagian dari wilayah B, yang tidak memiliki prolog. Namun, ia memiliki kode unwind yang menggambarkan dorongan dalam dan prolog asli dari wilayah A. Logika serupa berlaku untuk pop dalam.

Pengoptimalan Pengodean

Kekayaan kode unwind, dan kemampuan untuk menggunakan bentuk data yang ringkas dan diperluas, memberikan banyak peluang untuk mengoptimalkan pengodean untuk lebih mengurangi ruang. Dengan penggunaan teknik ini yang agresif, overhead bersih dari menggambarkan fungsi dan fragmen dengan menggunakan kode unwind dapat diminimalkan.

Ide pengoptimalan yang paling penting: Jangan membingungkan batas prolog dan epilog untuk tujuan melepas lelah dengan prolog logis dan batas epilog dari perspektif kompilator. Batas-batas yang melepas lelah dapat menyusut dan dibuat lebih ketat untuk meningkatkan efisiensi. Misalnya, prolog mungkin berisi kode setelah penyiapan tumpukan untuk melakukan pemeriksaan verifikasi. Tetapi setelah semua manipulasi tumpukan selesai, tidak perlu mengodekan operasi lebih lanjut, dan apa pun di luar yang dapat dihapus dari prolog yang melepas lelah.

Aturan yang sama ini berlaku untuk panjang fungsi. Jika ada data (seperti kumpulan harfiah) yang mengikuti epilog dalam fungsi, itu tidak boleh disertakan sebagai bagian dari panjang fungsi. Dengan menyusutkan fungsi hanya ke kode yang merupakan bagian dari fungsi, kemungkinan jauh lebih besar bahwa epilog berada di bagian paling akhir dan rekaman ringkas .pdata dapat digunakan.

Dalam prolog, setelah penunjuk tumpukan disimpan ke register lain, biasanya tidak perlu merekam opcode lebih lanjut. Untuk melepas kelelahan fungsi, hal pertama yang dilakukan adalah memulihkan SP dari register yang disimpan. Operasi lebih lanjut tidak berpengaruh pada melepas lelah.

Epilog instruksi tunggal tidak perlu dikodekan sama sekali, baik sebagai cakupan atau sebagai kode unwind. Jika unwind terjadi sebelum instruksi tersebut dijalankan, maka aman untuk mengasumsikan itu dari dalam isi fungsi. Hanya menjalankan kode unwind prolog sudah cukup. Ketika unwind terjadi setelah instruksi tunggal dijalankan, maka menurut definisi itu terjadi di wilayah lain.

Epilog multi-instruksi tidak perlu mengodekan instruksi pertama epilog, karena alasan yang sama seperti titik sebelumnya: jika unwind terjadi sebelum instruksi tersebut dijalankan, unwind prolog penuh sudah cukup. Jika unwind terjadi setelah instruksi itu, maka hanya operasi selanjutnya yang harus dipertimbangkan.

Penggunaan kembali kode unwind harus agresif. Indeks setiap cakupan epilog menentukan poin ke titik awal arbitrer dalam array kode unwind. Ini tidak harus menunjuk ke awal urutan sebelumnya; itu bisa menunjuk di tengah. Pendekatan terbaik adalah menghasilkan urutan kode unwind. Kemudian, pindai kecocokan byte yang tepat di kumpulan urutan yang sudah dikodekan. Gunakan kecocokan sempurna sebagai titik awal untuk digunakan kembali.

Setelah epilog instruksi tunggal diabaikan, jika tidak ada epilog yang tersisa, pertimbangkan untuk menggunakan bentuk yang ringkas .pdata ; itu menjadi jauh lebih mungkin dengan tidak adanya epilog.

Contoh

Dalam contoh ini, basis gambar berada di 0x00400000.

Contoh 1: Fungsi Daun, Tidak Ada Lokal

Prologue:
  004535F8: B430      push        {r4-r5}
Epilogue:
  00453656: BC30      pop         {r4-r5}
  00453658: 4770      bx          lr

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x000535F8 (= 0x00400000 0x004535F8)
  • Word 1

    • Flag = 1, menunjukkan prolog kanonis dan format epilog

    • Function Length = 0x31 (= 0x62/2)

    • Ret = 1, menunjukkan pengembalian cabang 16-bit

    • H = 0, menunjukkan parameter tidak dirumahkan

    • R = 0 dan Reg = 1, menunjukkan dorong/pop r4-r5

    • L = 0, menunjukkan tidak ada simpan/pulihkan LR

    • C = 0, menunjukkan tidak ada rantai bingkai

    • Stack Adjust = 0, menunjukkan tidak ada penyesuaian tumpukan

Contoh 2: Fungsi Berlapis dengan Alokasi Lokal

Prologue:
  004533AC: B5F0      push        {r4-r7, lr}
  004533AE: B083      sub         sp, sp, #0xC
Epilogue:
  00453412: B003      add         sp, sp, #0xC
  00453414: BDF0      pop         {r4-r7, pc}

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x000533AC (= 0x004533AC -0x00400000)
  • Word 1

    • Flag = 1, menunjukkan prolog kanonis dan format epilog

    • Function Length = 0x35 (= 0x6A/2)

    • Ret = 0, menunjukkan pengembalian {pc} pop

    • H = 0, menunjukkan parameter tidak dirumahkan

    • R = 0 dan Reg = 3, menunjukkan dorong/pop r4-r7

    • L = 1, menunjukkan LR disimpan/dipulihkan

    • C = 0, menunjukkan tidak ada rantai bingkai

    • Stack Adjust = 3 (= 0x0C/4)

Contoh 3: Fungsi Variadik Berlapis

Prologue:
  00453988: B40F      push        {r0-r3}
  0045398A: B570      push        {r4-r6, lr}
Epilogue:
  004539D4: E8BD 4070 pop         {r4-r6}
  004539D8: F85D FB14 ldr         pc, [sp], #0x14

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x00053988 (= 0x00453988-0x00400000)
  • Word 1

    • Flag = 1, menunjukkan prolog kanonis dan format epilog

    • Function Length = 0x2A (= 0x54/2)

    • Ret = 0, menunjukkan pengembalian pop {pc}-style (dalam hal ini pengembalian ldr pc,[sp],#0x14 )

    • H = 1, menunjukkan parameter dirumahkan

    • R = 0 dan Reg = 2, menunjukkan dorong/pop r4-r6

    • L = 1, menunjukkan LR disimpan/dipulihkan

    • C = 0, menunjukkan tidak ada rantai bingkai

    • Stack Adjust = 0, menunjukkan tidak ada penyesuaian tumpukan

Contoh 4: Fungsi dengan Beberapa Epilog

Prologue:
  004592F4: E92D 47F0 stmdb       sp!, {r4-r10, lr}
  004592F8: B086      sub         sp, sp, #0x18
Epilogues:
  00459316: B006      add         sp, sp, #0x18
  00459318: E8BD 87F0 ldm         sp!, {r4-r10, pc}
  ...
  0045943E: B006      add         sp, sp, #0x18
  00459440: E8BD 87F0 ldm         sp!, {r4-r10, pc}
  ...
  004595D4: B006      add         sp, sp, #0x18
  004595D6: E8BD 87F0 ldm         sp!, {r4-r10, pc}
  ...
  00459606: B006      add         sp, sp, #0x18
  00459608: E8BD 87F0 ldm         sp!, {r4-r10, pc}
  ...
  00459636: F028 FF0F bl          KeBugCheckEx     ; end of function

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x000592F4 (= 0x004592F4-0x00400000)
  • Word 1

    • Flag = 0, menunjukkan .xdata adanya rekaman (diperlukan untuk beberapa epilog)

    • .xdata address - 0x00400000

.xdata (variabel, 6 kata):

  • Word 0

    • Function Length = 0x0001A3 (= 0x000346/2)

    • Vers = 0, menunjukkan versi pertama.xdata

    • X = 0, menunjukkan tidak ada data pengecualian

    • E = 0, menunjukkan daftar cakupan epilog

    • F = 0, menunjukkan deskripsi fungsi penuh, termasuk prolog

    • Epilogue Count = 0x04, menunjukkan 4 total cakupan epilog

    • Code Words = 0x01, menunjukkan satu kata 32-bit kode unwind

  • Kata 1-4, menjelaskan 4 cakupan epilog di 4 lokasi. Setiap cakupan memiliki sekumpulan kode unwind umum, dibagikan dengan prolog, pada 0x00 offset, dan tidak bersyarat, menentukan kondisi 0x0E (selalu).

  • Lepaskan kode, dimulai dari Word 5: (dibagi antara prolog/epilog)

    • Lepaskan kode 0 = 0x06: sp += (6 << 2)

    • Lepaskan kode 1 = 0xDE: pop {r4-r10, lr}

    • Lepaskan kode 2 = 0xFF: end

Contoh 5: Fungsi dengan Dynamic Stack dan Inner Epilogue

Prologue:
  00485A20: B40F      push        {r0-r3}
  00485A22: E92D 41F0 stmdb       sp!, {r4-r8, lr}
  00485A26: 466E      mov         r6, sp
  00485A28: 0934      lsrs        r4, r6, #4
  00485A2A: 0124      lsls        r4, r4, #4
  00485A2C: 46A5      mov         sp, r4
  00485A2E: F2AD 2D90 subw        sp, sp, #0x290
Epilogue:
  00485BAC: 46B5      mov         sp, r6
  00485BAE: E8BD 41F0 ldm         sp!, {r4-r8, lr}
  00485BB2: B004      add         sp, sp, #0x10
  00485BB4: 4770      bx          lr
  ...
  00485E2A: F7FF BE7D b           #0x485B28    ; end of function

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x00085A20 (= 0x00485A20-0x00400000)
  • Word 1

    • Flag = 0, menunjukkan .xdata adanya rekaman (diperlukan untuk beberapa epilog)

    • .xdata address - 0x00400000

.xdata (variabel, 3 kata):

  • Word 0

    • Function Length = 0x0001A3 (= 0x000346/2)

    • Vers = 0, menunjukkan versi pertama.xdata

    • X = 0, menunjukkan tidak ada data pengecualian

    • E = 0, menunjukkan daftar cakupan epilog

    • F = 0, menunjukkan deskripsi fungsi penuh, termasuk prolog

    • Epilogue Count = 0x001, menunjukkan 1 total cakupan epilog

    • Code Words = 0x01, menunjukkan satu kata 32-bit kode unwind

  • Word 1: Cakupan Epilog di offset 0xC6 (= 0x18C/2), memulai indeks kode unwind pada 0x00, dan dengan kondisi 0x0E (selalu)

  • Lepaskan kode, mulai dari Word 2: (dibagi antara prolog/epilog)

    • Lepaskan kode 0 = 0xC6: sp = r6

    • Lepaskan kode 1 = 0xDC: pop {r4-r8, lr}

    • Lepaskan kode 2 = 0x04: sp += (4 << 2)

    • Unwind code 3 = 0xFD: end, dihitung sebagai instruksi 16-bit untuk epilog

Contoh 6: Fungsi dengan Handler Pengecualian

Prologue:
  00488C1C: 0059 A7ED dc.w  0x0059A7ED
  00488C20: 005A 8ED0 dc.w  0x005A8ED0
FunctionStart:
  00488C24: B590      push        {r4, r7, lr}
  00488C26: B085      sub         sp, sp, #0x14
  00488C28: 466F      mov         r7, sp
Epilogue:
  00488C6C: 46BD      mov         sp, r7
  00488C6E: B005      add         sp, sp, #0x14
  00488C70: BD90      pop         {r4, r7, pc}

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x00088C24 (= 0x00400000 0x00488C24)
  • Word 1

    • Flag = 0, menunjukkan .xdata adanya rekaman (diperlukan untuk beberapa epilog)

    • .xdata address - 0x00400000

.xdata (variabel, 5 kata):

  • Word 0

    • Function Length =0x000027 (= 0x00004E/2)

    • Vers = 0, menunjukkan versi pertama.xdata

    • X = 1, menunjukkan adanya data pengecualian

    • E = 1, menunjukkan satu epilog

    • F = 0, menunjukkan deskripsi fungsi penuh, termasuk prolog

    • Epilogue Count = 0x00, menunjukkan kode unwind epilog dimulai pada offset 0x00

    • Code Words = 0x02, menunjukkan dua kata kode unwind 32-bit

  • Lepaskan kode, dimulai dari Word 1:

    • Lepaskan kode 0 = 0xC7: sp = r7

    • Lepaskan kode 1 = 0x05: sp += (5 << 2)

    • Lepaskan kode 2 = 0xED/0x90: pop {r4, r7, lr}

    • Lepaskan kode 4 = 0xFF: akhir

  • Word 3 menentukan penanganan pengecualian = 0x0019A7ED (= 0x0059A7ED - 0x00400000)

  • Kata 4 dan seterusnya adalah data pengecualian sebaris

Contoh 7: Funclet

Function:
  00488C72: B500      push        {lr}
  00488C74: B081      sub         sp, sp, #4
  00488C76: 3F20      subs        r7, #0x20
  00488C78: F117 0308 adds        r3, r7, #8
  00488C7C: 1D3A      adds        r2, r7, #4
  00488C7E: 1C39      adds        r1, r7, #0
  00488C80: F7FF FFAC bl          target
  00488C84: B001      add         sp, sp, #4
  00488C86: BD00      pop         {pc}

.pdata (tetap, 2 kata):

  • Word 0

    • Function Start RVA = 0x00088C72 (= 0x00400000 0x00488C72)
  • Word 1

    • Flag = 1, menunjukkan prolog kanonis dan format epilog

    • Function Length = 0x0B (= 0x16/2)

    • Ret = 0, menunjukkan pengembalian {pc} pop

    • H = 0, menunjukkan parameter tidak dirumahkan

    • R = 0 dan Reg = 7, menunjukkan tidak ada register yang disimpan/dipulihkan

    • L = 1, menunjukkan LR disimpan/dipulihkan

    • C = 0, menunjukkan tidak ada rantai bingkai

    • Stack Adjust = 1, menunjukkan penyesuaian tumpukan 1 × 4 byte

Lihat juga

Ringkasan Konvensi ABI ARM
Masalah Umum Migrasi VISUAL C++ ARM