Sdílet prostřednictvím


Zpracování výjimek ARM

Systém Windows v ARM používá stejný mechanismus strukturovaného zpracování výjimek pro asynchronní výjimky generované hardwarem a synchronní výjimky generované softwarem. Obslužné rutiny výjimek specifické pro jazyk jsou založené na strukturovaném zpracování výjimek systému Windows pomocí pomocných funkcí jazyka. Tento dokument popisuje zpracování výjimek ve Windows v ARM a pomocné rutiny jazyka Jak Microsoft ARM assembler, tak generování kompilátoru MSVC.

Zpracování výjimek ARM

Windows v ARM používá kódy odvíjení k řízení odvíjení zásobníku během strukturovaného zpracování výjimek (SEH). Unwind kódy jsou posloupnost bajtů uložených v .xdata části spustitelné image. Tyto kódy popisují fungování prologue funkce a epilogu abstraktním způsobem. Obslužná rutina je použije k vrácení efektů funkce prologue, když se uvolní do rámce zásobníku volajícího.

ARM EABI (binární rozhraní vložené aplikace) určuje model pro odvíjení výjimek, který používá kódy odvíjení. Model není dostatečný pro odvíjení SEH ve Windows. Musí zpracovávat asynchronní případy, kdy je procesor uprostřed prologue nebo epilogu funkce. Systém Windows také odděluje odvíjení ovládacího prvku na úrovni funkce a odvíjení oboru specifického pro jazyk, který je sjednocený v ARM EABI. Z těchto důvodů systém Windows v ARM určuje další podrobnosti o odvíjení dat a postupu.

Předpoklady

Spustitelné image pro Windows v ARM používají formát Portable Executable (PE). Další informace naleznete v tématu FORMÁT PE. Informace o zpracování výjimek jsou uloženy v .pdata .xdata částech image.

Mechanismus zpracování výjimek provádí určité předpoklady týkající se kódu, který následuje za ABI pro Windows v ARM:

  • Pokud dojde k výjimce v těle funkce, obslužná rutina může vrátit zpět operace prologue nebo provádět operace epilogu vpřed. Oba by měly mít identické výsledky.

  • Prologues a epilogues mají tendenci se vzájemně zrcadlit. Tato funkce se dá použít ke zmenšení velikosti metadat potřebných k popisu odvíjení.

  • Funkce mají tendenci být relativně malé. Při efektivním balení dat se na toto pozorování spoléhá několik optimalizací.

  • Pokud je podmínka umístěna na epilogu, platí stejně pro každou instrukci v epilogu.

  • Pokud prologue uloží ukazatel zásobníku (SP) v jiném registru, musí tento registr zůstat beze změny v celé funkci, takže původní sp může být kdykoli obnoven.

  • Pokud není SP uložen v jiném registru, musí veškerá manipulace s ním probíhat výhradně v rámci prologu a epilogu.

  • K odvinutí libovolného rámce zásobníku jsou potřeba tyto operace:

    • Upravte r13 (SP) v přírůstcích po 4 bajtech.

    • Zobrazí jedno nebo více celých registrů.

    • Otevřete jeden nebo více registrů VFP (virtuální plovoucí desetinná čárka).

    • Zkopírujte libovolnou hodnotu registru do r13 (SP).

    • Načtěte z zásobníku sp pomocí malé operace po snížení.

    • Parsovat jeden z několika dobře definovaných typů snímků.

.pdata Záznamy

Záznamy .pdata na obrázku ve formátu PE jsou uspořádané pole položek s pevnou délkou, které popisují každou funkci manipulace se zásobníkem. Funkce typu List (funkce, které nevolají jiné funkce) .pdata nevyžadují záznamy, když se s zásobníkem manipulují. (To znamená, že nevyžadují žádné místní úložiště a nemusí ukládat ani obnovovat nestálé registry.) Záznamy pro tyto funkce je možné z oddílu .pdata vynechat, aby se ušetřilo místo. Operace uvolnění z některé z těchto funkcí může jednoduše zkopírovat zpáteční adresu z registru odkazů (LR) do čítače programu (PC), aby se přesunula nahoru na volajícího.

Každý .pdata záznam pro ARM je dlouhý 8 bajtů. Obecný formát záznamu umístí relativní virtuální adresu (RVA) funkce začínající v prvním 32bitovém slově následované druhým slovem, které obsahuje ukazatel na blok s proměnnou délkou .xdata , nebo zabalené slovo popisující sekvenci odvíjení kanonické funkce, jak je znázorněno v této tabulce:

Posun slova Bity Účel
0 0-31 Function Start RVA je 32bitová hodnota RVA začátku funkce. Pokud funkce obsahuje kryptografický kód, musí být nastaven nízký bit této adresy.
0 0-1 Flag je 2bitové pole, které označuje, jak interpretovat zbývající 30 bitů druhého .pdata slova. Pokud Flag je 0, zbývající bity tvoří RVA informace o výjimce (s nízkými dvěma bity implicitně 0). Pokud Flag je nenulová, zbývající bity tvoří strukturu Zabalené unwind data .
0 2-31 Informace o výjimce RVA nebo zabalená data unwinded.

Informace o výjimce RVA je adresa struktury informací o výjimce proměnné délky uložená .xdata v části. Tato data musí být zarovnaná se 4 bajty.

Zabalená data Unwind jsou komprimovaný popis operací potřebných k odvíjení funkce za předpokladu, že jde o kanonický formulář. V tomto případě se nevyžaduje žádný .xdata záznam.

Zabalená data unwind

Pro funkce, jejichž prologues a epilogues se řídí kanonickým formulářem popsaným níže, lze použít zabalená data unwind. Eliminuje potřebu záznamu .xdata a výrazně snižuje prostor potřebný k poskytování dat odvíjení. Kanonické prologues a epilogues jsou navrženy tak, aby splňovaly běžné požadavky jednoduché funkce, která nevyžaduje obslužnou rutinu výjimky, a provádí operace nastavení a odstranění ve standardním pořadí.

Tato tabulka ukazuje formát záznamu .pdata , který obsahuje zabalená data:

Posun slova Bity Účel
0 0-31 Function Start RVA je 32bitová hodnota RVA začátku funkce. Pokud funkce obsahuje kryptografický kód, musí být nastaven nízký bit této adresy.
0 0-1 Flag je 2bitové pole, které má tyto významy:

- 00 = zabalená unwindová data se nepoužívají; zbývající bity ukazují na .xdata záznam.
- 01 = zabalená data odvíjení.
- 10 = zabalená data unwind, kde se předpokládá, že funkce nemá žádný prologue. To je užitečné pro popis fragmentů funkcí, které jsou nesouvislé se začátkem funkce.
- 11 = rezervováno.
0 2-12 Function Length je 11bitové pole, které poskytuje délku celé funkce v bajtech dělené 2. Pokud je funkce větší než 4K bajtů, musí se místo toho použít úplný .xdata záznam.
0 13-14 Ret je 2bitové pole, které označuje, jak funkce vrací:

- 00 = vrátit přes pop {pc} ( L v tomto případě musí být bit příznaku nastaven na hodnotu 1).
- 01 = vrácení pomocí 16bitové větve.
- 10 = vrátit pomocí 32bitové větve.
- 11 = vůbec žádný epilogue. To je užitečné pro popis nespokojivé části funkce, která může obsahovat pouze prologue, ale jejíž epilogue je jinde.
0 15 H je 1bitový příznak, který označuje, jestli funkce "homes" celočíselného parametru registruje (r0-r3) tím, že je nasdílí na začátek funkce, a před vrácením uvolní 16 bajtů zásobníku. (0 = nemá domácí registry, 1 = domovní registry.)
0 16-18 Reg je 3bitové pole, které označuje index posledního uloženého nevolatilního registru. R Pokud je bit 0, ukládají se pouze celočíselné registry a předpokládá se, že jsou v rozsahu r4-rN, kde N se rovná 4 + Reg. R Pokud je bit 1, ukládají se pouze registry s plovoucí desetinnou čárkou a předpokládá se, že jsou v rozsahu d8-dN, kde N se rovná 8 + Reg. Speciální kombinace R = 1 a Reg = 7 označuje, že nejsou uloženy žádné registry.
0 19 R je 1bitový příznak, který označuje, jestli uložené nevolatilní registry jsou celočíselné registry (0) nebo registry s plovoucí desetinou čárkou (1). Pokud R je nastavená hodnota 1 a Reg pole je nastaveno na hodnotu 7, nebyly vloženy žádné nestálé registry.
0 20 L je 1bitový příznak, který označuje, jestli funkce ukládá/obnovuje LR spolu s dalšími registry označenými polem Reg . (0 = neukládá/neobnoví, 1 = ukládá/obnovuje.)
0 21 C je 1bitový příznak, který označuje, jestli funkce obsahuje další pokyny k nastavení řetězu rámu pro rychlé chůze zásobníku (1) nebo ne (0). Pokud je tento bit nastavený, r11 se implicitně přidá do seznamu celočíslých registrů, které nejsou nestálé. (Pokud se příznak používá, C podívejte se na níže uvedená omezení.)
0 22-31 Stack Adjust je 10bitové pole, které označuje počet bajtů zásobníku, které jsou přiděleny pro tuto funkci, děleno 4. Pouze hodnoty mezi 0x000-0x3F3 je však možné přímo zakódovat. Funkce, které přidělují více než 4044 bajtů zásobníku, musí používat úplný .xdata záznam. Stack Adjust Pokud je pole 0x3F4 nebo větší, mají nízké 4 bity zvláštní význam:

- Bity 0-1 označují počet slov úpravy zásobníku (1-4) minus 1.
- Bit 2 je nastaven na hodnotu 1, pokud prologue zkombinoval tuto úpravu do své push operace.
- Bit 3 je nastaven na 1, pokud epilogue zkombinoval tuto úpravu do jeho pop operace.

Vzhledem k možným redundancím ve výše uvedených kódováních platí tato omezení:

  • C Pokud je příznak nastaven na hodnotu 1:

    • Příznak L musí být také nastaven na hodnotu 1, protože řetězení snímků vyžaduje r11 i LR.

    • r11 nesmí být zahrnuta do sady rejstříků popsaných .Reg To znamená, že pokud se r4-r11 nasdílí, Reg měl by popisovat pouze r4-r10, protože C příznak znamená r11.

  • Ret Pokud je pole nastaveno na hodnotu 0, L musí být příznak nastaven na hodnotu 1.

Porušení těchto omezení způsobuje nepodporovanou sekvenci.

Pro účely níže uvedené diskuze jsou odvozeny dvě pseudoznaky:Stack Adjust

  • PF nebo "prologue folding" označuje, že Stack Adjust je 0x3F4 nebo větší a bit 2 je nastaven.

  • EF nebo "epilogue folding" označuje, že Stack Adjust je 0x3F4 nebo větší a bit 3 je nastaven.

Prologues pro kanonické funkce může mít až 5 instrukcí (všimněte si, že 3a a 3b se vzájemně vylučují):

Pokyn Opcode se předpokládá, pokud: Velikost Opcode Unwind Codes
0 H==1 16 push {r0-r3} 04
2 C==1 nebo L==1 nebo R==0 nebo PF==1 16/32 push {registers} 80-BF/D0-DF/EC-ED
3a C==1 a (R==1 a PF==0) 16 mov r11,sp FB
3b C==1 a (R==0 nebo PF==1) 32 add r11,sp,#xx FC
4 R==1 a Reg != 7 32 vpush {d8-dE} E0-E7
5 Stack Adjust != 0 a PF==0 16/32 sub sp,sp,#xx 00-7F/E8-EB

Instrukce 1 je vždy k dispozici, pokud H je bit nastaven na hodnotu 1.

Chcete-li nastavit řetězení rámu, buď instrukce 3a, nebo 3b je k dispozici, pokud C je bit nastaven. Jedná se o 16bitovou mov verzi, pokud se nezaregistruje jinak než r11 a LR se nenasdílí. V opačném případě se jedná o 32bitovou addverzi .

Pokud je zadána neskládaná úprava, je instrukce 5 explicitní úpravou zásobníku.

Pokyny 2 a 4 jsou nastaveny na základě toho, jestli se vyžaduje nabízení. Tato tabulka shrnuje, které registry se ukládají na Czákladě polí , L, Ra PF polí. Ve všech případech se N rovná + Reg 4, E je rovno Reg + 8 a S je roven (~Stack Adjust) & 3.

C L R PF Integer Registers Pushed Nasdílené registrace VFP
0 0 0 0 r4 - r*N* Žádná
0 0 0 1 r*S* - r*N* Žádná
0 0 1 0 Žádná d8 - d*E*
0 0 1 1 r*S* – r3 d8 - d*E*
0 1 0 0 r4 - r*N*, LR Žádná
0 1 0 1 r*S* - r*N*, LR Žádná
0 1 1 0 LR d8 - d*E*
0 1 1 1 r*S* - r3, LR d8 - d*E*
0 0 0 0 (neplatné kódování) (neplatné kódování)
0 0 0 1 (neplatné kódování) (neplatné kódování)
0 0 1 0 (neplatné kódování) (neplatné kódování)
0 0 1 1 (neplatné kódování) (neplatné kódování)
1 1 0 0 r4 - r*N*, r11, LR Žádná
1 1 0 1 r*S* - r*N*, r11, LR Žádná
1 1 1 0 r11, LR d8 - d*E*
1 1 1 1 r*S* - r3, r11, LR d8 - d*E*

Epilogues pro kanonické funkce se řídí podobnou formou, ale obráceně a s některými dalšími možnostmi. Epilog může být dlouhý až 5 instrukcí a jeho forma je přísně určena formou prologu.

Pokyn Opcode se předpokládá, pokud: Velikost Opcode
6 Stack Adjust!=0 a EF==0 16/32 add sp,sp,#xx
7 R==1 a Reg!=7 32 vpop {d8-dE}
8 C==1 nebo (L==1 a (H==0 nebo Ret !=0)) nebo R==0 nebo EF==1 16/32 pop {registers}
9a H==1 a (L==0 nebo Ret!=0) 16 add sp,sp,#0x10
9b H==1 a L==1 a Ret==0 32 ldr pc,[sp],#0x14
10a Ret==1 16 bx reg
10b Ret==2 32 b address

Instrukce 6 je explicitní úprava zásobníku, pokud je zadána nepřeložená úprava. Vzhledem k tomu PF , že je nezávislý na EF, je možné mít instrukce 5 přítomné bez instrukce 6, nebo naopak.

Instrukce 7 a 8 používají stejnou logiku jako prologue k určení, které registry jsou obnoveny ze zásobníku, ale s těmito třemi změnami: první, EF použije se místo PF; druhý, pokud Ret = 0 a H = 0, pak LR se nahradí počítačem v seznamu rejstříku a epilog končí okamžitě; třetí, pokud Ret = 0 a H = 1, poté se v seznamu registrů vynechá LR a zobrazí se instrukce 9b.

Pokud H je nastavena, je k dispozici instrukce 9a nebo 9b. Instrukce 9a se používá, pokud Ret je nenulová, což také znamená přítomnost 10a nebo 10b. Pokud L=1, pak se LR vyskočil jako součást instrukce 8. Instrukce 9b se používá, když L je 1 a Ret je nula, k označení počátečního konce epilogu a k vrácení a úpravě zásobníku ve stejnou dobu.

Pokud epilogue ještě nekončí, pak buď instrukce 10a nebo 10b je přítomna, označující 16bitovou nebo 32bitovou větev na základě hodnoty Ret.

.xdata Záznamy

Pokud zabalený formát unwind není dostatečný k popisu odvíjení funkce, je nutné vytvořit záznam o proměnné délce .xdata . Adresa tohoto záznamu je uložena ve druhém slově záznamu .pdata . Formát .xdata je zabalená sada slov s proměnlivou délkou, která má čtyři části:

  1. Záhlaví 1 nebo 2 slov, které popisuje celkovou .xdata velikost struktury a poskytuje klíčová data funkcí. Druhé slovo je přítomno pouze v případě, že pole Počet epilogu a Slova kódu jsou nastavena na hodnotu 0. Pole jsou rozdělená v této tabulce:

    Word Bity Účel
    0 0-17 Function Length je 18bitové pole, které označuje celkovou délku funkce v bajtech děleno 2. Pokud je funkce větší než 512 kB, musí být k popisu funkce použito více .pdata záznamů a .xdata záznamů. Podrobnosti najdete v části Velké funkce v tomto dokumentu.
    0 18-19 Vers je 2bitové pole, které popisuje verzi zbývající.xdata verze. V současné době je definována pouze verze 0; hodnoty 1–3 jsou vyhrazeny.
    0 20 X je 1bitové pole, které označuje přítomnost (1) nebo nepřítomnost (0) dat výjimek.
    0 21 E je 1bitové pole, které označuje, že informace, které popisují jeden epilogue, jsou zabaleny do hlavičky (1) a nevyžadují další slova oboru později (0).
    0 22 F je 1bitové pole, které označuje, že tento záznam popisuje fragment funkce (1) nebo úplnou funkci (0). Fragment znamená, že neexistuje žádný prologue a že veškeré zpracování prologue by mělo být ignorováno.
    0 23-27 Epilogue Count je 5bitové pole, které má dva významy v závislosti na stavu bitu E :

    - Pokud E je 0, jedná se o počet celkového počtu oborů epilogu popsaných v části 2. Pokud ve funkci existuje více než 31 oborů, musí být toto pole i pole Kódová slova nastaveny na hodnotu 0, aby bylo možné označit, že je požadováno slovo rozšíření.
    - Pokud E je 1, toto pole určuje index prvního odvinutí kódu, který popisuje jediný epilog.
    0 28-31 Code Words je 4bitové pole, které určuje počet 32bitových slov potřebných k zahrnutí všech kódů odvíjení v oddílu 4. Pokud je pro více než 63 bajtů kódu vyžadováno více než 15 slov, musí být toto pole a pole Epilogue Count nastaveno na hodnotu 0, aby bylo možné označit, že je požadováno slovo rozšíření.
    0 0-15 Extended Epilogue Count je 16bitové pole, které poskytuje více místa pro kódování neobvykle velkého počtu epilogů. Slovo rozšíření, které obsahuje toto pole, je přítomné pouze v případě, že pole Epilogue Count a Code Words v prvním záhlaví slova jsou nastavena na hodnotu 0.
    0 16-23 Extended Code Words je 8bitové pole, které poskytuje více místa pro kódování neobvykle velkého počtu odvíjených slov kódu. Slovo rozšíření, které obsahuje toto pole, je přítomné pouze v případě, že pole Epilogue Count a Code Words v prvním záhlaví slova jsou nastavena na hodnotu 0.
    0 24-31 Rezervováno
  2. Po datech výjimky (pokud E byl bit v hlavičce nastaven na hodnotu 0) je seznam informací o oborech epilogu, které jsou zabalené na slovo a uloženy v pořadí zvýšení počátečního posunu. Každý obor obsahuje tato pole:

    Bity Účel
    0-17 Počáteční posun epilogu je 18bitové pole, které popisuje posun epilogu v bajtech děleno 2 vzhledem ke začátku funkce.
    18-19 Res je 2bitové pole vyhrazené pro budoucí rozšíření. Jeho hodnota musí být 0.
    20-23 Podmínka je 4bitové pole, které dává podmínku, pod kterou se epilog provádí. Pro nepodmíněné epilogy by měla být nastavena na 0xE, což označuje "vždy". (Epilog musí být zcela podmíněný nebo zcela nepodmíněný a v režimu Thumb-2 začíná epilog první instrukcí po operaci IT.)
    24-31 Epilogue Start Index je 8bitové pole, které označuje bajtový index prvního odvinutí kódu, který popisuje tento epilogue.
  3. Jakmile seznam oborů epilogu obsahuje pole bajtů obsahující kódy odvíjení, které jsou podrobně popsány v části Unwind Codes v tomto článku. Toto pole je vycpané na konci k nejbližší hranici celého slova. Bajty jsou uloženy v malém-endovém pořadí, aby je bylo možné přímo načíst v režimu little-endian.

  4. Pokud je pole X v hlavičce 1, za informacemi obslužné rutiny výjimky následuje odvinutí bajtů kódu. Skládá se z jedné obslužné rutiny výjimky RVA , která obsahuje adresu obslužné rutiny výjimky, následované okamžitou (proměnnou délkou) množství dat vyžadovaných obslužnou rutinou výjimky.

Záznam .xdata je navržený tak, aby bylo možné načíst prvních 8 bajtů a vypočítat úplnou velikost záznamu, nikoli délku dat výjimky s proměnlivou velikostí, která následuje. Tento fragment kódu vypočítá velikost záznamu:

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;
}

I když má prologue a každý epilogue index do odvíjecí kódy, tabulka se mezi nimi sdílí. Není neobvyklé, že všechny můžou sdílet stejné kódy odvíjení. Doporučujeme, aby se zapisovače kompilátoru optimalizovaly pro tento případ, protože největší index, který lze zadat, je 255 a omezuje celkový počet kódů odvíjení možné pro konkrétní funkci.

Unwind Codes

Pole kódů odvíjení je fond sekvencí instrukcí, které přesně popisují, jak vrátit zpět účinky prologue v pořadí, v jakém musí být operace vráceny zpět. Kódy unwind jsou mini instrukční sada kódovaná jako řetězec bajtů. Po dokončení provádění je návratová adresa volající funkce v registru LR a všechny nevolatelní registry se obnoví na jejich hodnoty v době, kdy byla funkce volána.

Pokud by výjimky byly zaručeny pouze někdy v těle funkce, a nikdy v prologue nebo epilogue, pak by byla nutná pouze jedna sekvence odvíjení. Model odvíjení windows však vyžaduje schopnost odvíjení z částečně spuštěného prologue nebo epilogue. Pro splnění tohoto požadavku byly kódy unwind pečlivě navrženy tak, aby měly jednoznačné mapování 1:1 na každý relevantní opcode v prologu a epilogu. To má několik důsledků:

  • Délku prologue a epilogue je možné vypočítat počítáním počtu odvíjených kódů. To je možné i s instrukcemi Thumb-2 o proměnlivé délce, protože existují různá mapování pro 16bitové a 32bitové opcode.

  • Když spočítáte počet instrukcí za začátkem oboru epilogu, je možné přeskočit ekvivalentní počet odvíjecích kódů a spustit zbytek sekvence, aby se dokončil částečně spuštěný odvíječ, který epilogue prováděl.

  • Když spočítáte počet instrukcí před koncem prologue, je možné přeskočit ekvivalentní počet odvíjecích kódů a zbytek sekvence vrátit zpět pouze ty části prologue, které dokončily provádění.

Následující tabulka ukazuje mapování od odvíjecích kódů na opcodes. Nejběžnější kódy jsou jen jeden bajt, zatímco méně běžné kódy vyžadují dvě, tři nebo dokonce čtyři bajty. Každý kód je uložen od nejvýznamnějšího bajtu po nejméně významný bajt. Struktura kódu unwind se liší od kódování popsaného v ARM EABI, protože tyto kódy unwind jsou navrženy tak, aby měly mapování 1:1 na opcode v prologu a epilogu, aby bylo možné uvolnit částečně spuštěné prologues a epilogues.

Bajt 1 Bajt 2 Bajt 3 Bajt 4 Opsize Vysvětlení
00-7F 16 add sp,sp,#X

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

kde se zobrazí LR, pokud je kód & 0x2000 a r0-r12 v případě, že je odpovídající bit nastaven v code &0x1FFF
C0-CF 16 mov sp,rX

where X is Code &0x0F
D0-D7 16 pop {r4-rX,lr}

kde X je (kód &0x03) + 4 a LR se zobrazí, pokud kód &0x04
D8-DF 32 pop {r4-rX,lr}

kde X je (kód &0x03) + 8 a LR se zobrazí, pokud kód & 0x04
E0-E7 32 vpop {d8-dX}

where X is (Code &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}

kde se zobrazí LR, pokud kód &0x0100 a r0-r7 se zobrazí, pokud je odpovídající bit nastaven v kódu &0x00FF
EE 00-0F 16 specifické pro společnost Microsoft
EE 10-FF 16 dostupný
EF 00-0F 32 ldr lr,[sp],#X

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

where S is (Code &0x00F0) >> 4 and E is Code &0x000F
F6 00-FF 32 vpop {dS-dE}

where S is ((Code &0x00F0) 4) >> + 16 and E is (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 (16bitová verze)
FC 32 nop (32bitová verze)
FD 16 end + 16bitová nop v epilogu
FE 32 end + 32bitová nop v epilogu
FF - end

Zobrazí se rozsah šestnáctkových hodnot pro každý bajt v kódu unwind code spolu s velikostí opcode Opsize a odpovídající původní interpretací instrukcí. Prázdné buňky označují kratší kódy odvíjení. V pokynech s velkými hodnotami, které pokrývají více bajtů, jsou nejdůležitější bity uloženy jako první. Pole Opsize zobrazuje implicitní velikost opcode přidruženou ke každé operaci Thumb-2. Zjevné duplicitní položky v tabulce s různými kódováními se používají k rozlišení mezi různými velikostmi operačního kódu.

Kódy odvíjení jsou navrženy tak, aby první bajt kódu sdělil celkovou velikost v bajtech kódu a velikost odpovídajícího opcode v instrukčním streamu. Pokud chcete vypočítat velikost prologue nebo epilogue, procházejte kódy od začátku sekvence na konec a pomocí vyhledávací tabulky nebo podobné metody určete, jak dlouho je odpovídající opcode.

Kódy odvíjení 0xFD a 0xFE jsou ekvivalentní běžnému koncovému kódu 0xFF, ale v epilogu se v případě epilogu 16bitového nebo 32bitového kódu vyrovnají jeden dodatečný opcode nop. V případě prologues jsou kódy 0xFD, 0xFE a 0xFF přesně ekvivalentní. Tento účet představuje běžné epilogické zakončení bx lr nebo b <tailcall-target>, které nemají ekvivalentní prologue instrukce. Tím se zvyšuje pravděpodobnost, že se sekvence odvíjení dají sdílet mezi prologue a epilogy.

V mnoha případech by mělo být možné použít stejnou sadu kódů prologu a všech epilogů. Pokud ale chcete zpracovat odvíjení částečně spuštěných prologues a epilogues, možná budete muset mít několik sekvencí odvíjení kódu, které se liší v pořadí nebo chování. Proto má každý epilogue svůj vlastní index do pole unwind, aby ukázal, kde začít spouštět.

Unwinding Partial Prologues and Epilogues

Nejběžnějším případem odvíjení je, když dojde k výjimce v těle funkce, mimo prologue a všechny epilogues. V tomto případě unwinder spustí kódy v poli unwind počínaje indexem 0 a pokračuje až do zjištění koncového opcode.

Pokud dojde k výjimce, když se provádí prologue nebo epilogue, je rámec zásobníku vytvořen pouze částečně a odvíječ musí přesně určit, co bylo provedeno, aby bylo možné jej správně vrátit zpět.

Představte si například tuto prologue a epilogickou sekvenci:

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

Vedle každého opcode je vhodný odvinutí kódu pro popis této operace. Posloupnost kódů odvíjení pro prologue je zrcadlový obraz kódů odvíjení pro epilogu, nepočítá poslední instrukce. Tento případ je běžný a je důvodem, proč se kódy prologue vždy považují za uložené v opačném pořadí od pořadí provádění prologue. To nám dává společnou sadu kódů odvíjení:

0xc7, 0xdd, 0x04, 0xfd

0xFD kód je speciální kód pro konec sekvence, což znamená, že epilog je jedna 16bitová instrukce delší než prologue. To umožňuje větší sdílení kódů odvíjení.

Pokud v tomto příkladu dojde k výjimce, zatímco tělo funkce mezi prologue a epilogue je spuštěno, odvíjení začíná případem epilogu v posunu 0 v rámci kódu epilogu. To odpovídá posunu 0x140 v příkladu. Unwinder spustí úplnou sekvenci odvíjení, protože nebylo provedeno žádné vyčištění. Pokud místo toho dojde k výjimce jedna instrukce po začátku epilogue kódu, unwinder může úspěšně odvinout tím, že přeskočí první unwind kód. Při mapování 1:1 mezi opkódy a odvíjením kódů, pokud se odvíjí od instrukce n v epilogu, měl by unwinder přeskočit první n odvíjecí kódy.

Podobná logika funguje obráceně pro prologue. Pokud se odvíjeje od posunu 0 v prologu, nemusí se nic provést. Pokud se odvíjí od jedné instrukce, měla by sekvence odvíjení zahájit jeden odvíjecí kód od konce, protože kódy prologue unwind jsou uloženy v obráceném pořadí. Obecně platí, že pokud se odvíjeje od instrukce n v prologue, odvíjení by mělo začít spouštět na n odvičení kódů od konce seznamu kódů.

Kódy prologue a epilogu se vždy neshoduje přesně. V takovém případě může pole kódu unwind obsahovat několik sekvencí kódů. Pokud chcete určit posun pro zahájení zpracování kódů, použijte tuto logiku:

  1. Pokud se odvíjeje od těla funkce, začněte spouštět odvíjecí kódy na indexu 0 a pokračujte, dokud nedosáhnete koncového opcode.

  2. Pokud se odvíjí od epilogu, použijte počáteční počáteční index specifický pro epilogue poskytovaný oborem epilogu. Vypočítat, kolik bajtů pc je od začátku epilogu. Přeskočte kódy odvíjení dopředu, dokud se nezapočítávají všechny již provedené pokyny. Spusťte odvíjecí sekvenci počínaje tímto okamžikem.

  3. Pokud se odvíjeje od prologue, začněte od indexu 0 v kódech odvíjení. Vypočítejte délku prologue kódu ze sekvence a pak vypočítejte, kolik bajtů je pc z konce prologue. Přeskočte kódy odvíjení dopředu, dokud nebudou zohledněny všechny neuskutečněné pokyny. Spusťte odvíjecí sekvenci počínaje tímto okamžikem.

Kódy prologue musí být vždy první v poli. jsou to také kódy, které se používají k odvíjení v obecném případě odvíjení z těla. Všechny sekvence kódu specifické pro epilogu by měly následovat hned po posloupnosti kódu prologue.

Fragmenty funkcí

Pro optimalizaci kódu může být užitečné rozdělit funkci na nesouvislé části. Po dokončení tohoto kroku vyžaduje každý fragment funkce vlastní samostatný .pdatazáznam (a případně i .xdatazáznam).

Za předpokladu, že je funkce prologue na začátku funkce a nejde ji rozdělit, existují čtyři případy fragmentu funkce:

  • Pouze prologue; všechny epilogy v jiných fragmentech.

  • Prologue a jeden nebo více epilogů; více epilogů v jiných fragmentech.

  • Žádný prologue ani epilogues; prologue a jeden nebo více epilogů v jiných fragmentech.

  • Pouze epilogy; prologue a možná více epilogů v jiných fragmentech.

V prvním případě musí být popsán pouze prologue. To lze provést v kompaktní .pdata podobě popisem prologue normálně a zadáním Ret hodnoty 3 označující žádný epilogue. V plné .xdata podobě to lze provést poskytnutím kódů prologue unwind v indexu 0 jako obvykle a určení počtu epilogue 0.

Druhý případ je stejně jako normální funkce. Pokud je v fragmentu jen jeden epilogue a je na konci fragmentu, lze použít kompaktní .pdata záznam. Jinak se musí použít úplný .xdata záznam. Mějte na paměti, že posuny zadané pro začátek epilogu jsou relativní ke začátku fragmentu, nikoli k původnímu začátku funkce.

Třetí a čtvrtý případy jsou varianty prvních a druhých případů, s výjimkou případů, které neobsahují prologue. V těchto situacích se předpokládá, že před začátkem epilogu existuje kód a je považován za součást těla funkce, která by obvykle byla nezahlcená vrácením účinků prologue. Tyto případy musí být proto zakódovány pseudoprologem, který popisuje, jak se odvítá od těla, ale který je považován za 0-délka při určování, zda provést částečný odvinutí na začátku fragmentu. Případně může být tento pseudoprologue popsán pomocí stejných kódů odvíjení jako epilog, protože pravděpodobně provádějí ekvivalentní operace.

Ve třetím a čtvrtém případě je přítomnost pseudoprologu určena buď nastavením Flag pole kompaktního .pdata záznamu na 2, nebo nastavením příznaku F v .xdata záhlaví na 1. V obou případech je kontrola částečného prologu unwind ignorována a všechny ne-epilogue unwinds jsou považovány za úplné.

Velké funkce

Fragmenty lze použít k popisu funkcí větších než limit 512 kB uložený bitovými poli v .xdata hlavičce. Pokud chcete popsat větší funkci, stačí ji rozdělit na fragmenty menší než 512 kB. Každý fragment by měl být upraven tak, aby nerozdělil epilogue na více částí.

Pouze první fragment funkce obsahuje prologue. Všechny ostatní fragmenty jsou označeny jako bez prologue. V závislosti na počtuepich Mějte na paměti, že každý rozsah epilogu v fragmentu určuje počáteční posun vzhledem ke začátku fragmentu, nikoli začátek funkce.

Pokud fragment nemá žádný prologue a žádný epilogue, stále vyžaduje vlastní .pdata–a případně .xdata– záznam popisovat, jak se odvinout z těla funkce.

Obtékání zmenšení

Složitější speciální případ fragmentů funkcí se nazývá obtékání zmenšení. Jedná se o techniku odložení uložení registru od začátku funkce na pozdější verzi funkce. Optimalizuje se pro jednoduché případy, které nevyžadují ukládání do registru. Tento případ má dvě části: existuje vnější oblast, která přiděluje prostor zásobníku, ale šetří minimální sadu registrů a vnitřní oblast, která ukládá a obnovuje další registry.

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:

Funkce s oblomenou velikostí se obvykle očekávají, že předem přidělí prostor pro extra registr, který se uloží do pravidelného prologu, a pak je uloží pomocí str nebo stm místo push. Tato akce uchovává veškerou manipulaci s ukazatelem zásobníku v původní prologue funkce.

Příklad funkce zabalené zmenšení musí být rozdělen do tří oblastí, které jsou označeny jako A, Ba C v komentářích. První A oblast pokrývá začátek funkce až do konce dalších nestálých úspor. .xdata Pro .pdata popis tohoto fragmentu musí být vytvořen záznam, který by měl prologue a žádný epilogues.

Střední B oblast získá vlastní .pdata nebo .xdata záznam, který popisuje fragment, který nemá žádný prologue a žádný epilogue. Kódy odvíjení pro tuto oblast však musí být stále přítomné, protože se považuje za tělo funkce. Kódy musí popisovat složený prologue, který představuje jak původní registry uložené v oblasti A prologue, tak dodatečné registry uložené před vstupem do oblasti B, jako by byly vytvořeny jednou posloupností operací.

Registrace se B nedá považovat za "vnitřní prologue", protože složená prologue popsaná pro oblast B musí popisovat oblast A prologue i další uložené registry. Pokud by fragment B měl prologue, kódy unwind by také znamenaly velikost tohoto prologue a neexistuje způsob, jak popsat složený prologue způsobem, který mapuje jeden-k-1 s opcode, které ukládají pouze další registry.

Další úspory registru musí být považovány za součást oblasti A, protože dokud nebudou dokončeny, složený prologue přesně nepopisuje stav zásobníku.

Poslední C oblast získá vlastní .pdata nebo .xdata záznam popisující fragment, který nemá žádný prologue, ale má epilog.

Alternativní přístup může fungovat také v případě, že se manipulace se zásobníkem před vstupem do oblasti B může snížit na jednu instrukci:

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

Klíčovým přehledem je, že na každé hranici instrukce je zásobník plně konzistentní s kódy odvíjení pro danou oblast. Pokud dojde k odvinutí před vnitřním nasdílením v tomto příkladu, považuje se za součást oblasti A. Jen oblast A prologue je unwound. Pokud se odvíjení objeví po vnitřním nasdílení, považuje se za součást oblasti B, která nemá žádný prologue. Má však odvinut kódy, které popisují vnitřní push i původní prologue z oblasti A. Podobná logika platí pro vnitřní pop.

Optimalizace kódování

Bohaté kódy unwind a schopnost využívat kompaktní a rozšířené formy dat, poskytují mnoho příležitostí k optimalizaci kódování pro další snížení prostoru. Při agresivním použití těchto technik je možné minimalizovat čistou režii popisování funkcí a fragmentů pomocí odvíjecích kódů.

Nejdůležitější myšlenkou optimalizace: Nezaměňujte hranice prologue a epilogu pro účely odvíjení s logickými prologue a epilogue hranicemi z pohledu kompilátoru. Hranice odvíjení se dají zmenšit a zpřesnit, aby se zlepšila efektivita. Například prologue může po nastavení zásobníku obsahovat kód, který provede kontroly ověření. Jakmile je ale veškerá manipulace se zásobníkem hotová, není nutné kódovat další operace a cokoli nad rámec toho, co je možné odebrat z odvíjecího prologue.

Stejné pravidlo platí pro délku funkce. Pokud existují data (například literálový fond), která ve funkci následuje za epiloguem, neměla by být zahrnuta jako součást délky funkce. Zmenšením funkce jen na kód, který je součástí funkce, je šance mnohem větší, že epilog je na samém konci a lze použít kompaktní .pdata záznam.

Po uložení ukazatele zásobníku do jiného registru není obvykle nutné zaznamenávat žádné další opcode. Pokud chcete funkci uvolnit, první věcí, která se provádí, je obnovení sp z uloženého registru. Další operace nemají žádný vliv na odvíjení.

Jednotlivé instrukční epilogy nemusí být kódovány vůbec, buď jako obory, nebo jako odvíjecí kódy. Pokud před provedením této instrukce dojde k odvinutí, je bezpečné předpokládat, že se nachází v těle funkce. Stačí jen spustit kódy prologue unwind. Když se odvíjení provede po provedení jedné instrukce, pak podle definice proběhne v jiné oblasti.

Epilogy s více instrukcemi nemusí kódovat první instrukce epilogu z stejného důvodu jako předchozí bod: pokud se před provedením této instrukce provede odvíjení, stačí úplný prologue unwind. Pokud se po této výuce provede odvíjení, je třeba zvážit pouze pozdější operace.

Opětovné použití kódu by mělo být agresivní. Index každý obor epilogu určuje body na libovolný výchozí bod v poli kódů odvíjení. Nemusí odkazovat na začátek předchozí sekvence; může nasměrovat na střed. Nejlepším přístupem je vygenerovat sekvenci kódu odvíjení. Pak vyhledejte přesnou shodu bajtů v již zakódovaném fondu sekvencí. Jako výchozí bod pro opakované použití použijte libovolnou dokonalou shodu.

Po ignorování jednoho instrukčního epilogu, pokud neexistují žádné zbývající epilogy, zvažte použití kompaktní .pdata formy; stává se mnohem pravděpodobnější v nepřítomnosti epilogu.

Příklady

V těchtopříkladch 0x00400000 ch

Příklad 1: Listová funkce, žádné místní hodnoty

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

.pdata (opraveno, 2 slova):

  • Word 0

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

    • Flag = 1, označující kanonický prologue a epilogue formáty

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

    • Ret = 1 označující 16bitovou větev return

    • H = 0, označující, že parametry nebyly doma

    • R = 0 a Reg = 1, označující push/pop r4-r5

    • L = 0, což znamená, že se neukládá nebo neobnoví LR

    • C = 0, udávající bez řetězení rámu

    • Stack Adjust = 0, označující žádné úpravy zásobníku

Příklad 2: Vnořená funkce s místním přidělením

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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 1, označující kanonický prologue a epilogue formáty

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

    • Ret = 0 označující návrat pop {pc}

    • H = 0, označující, že parametry nebyly doma

    • R = 0 a Reg = 3, označující push/pop r4-r7

    • L = 1, indikující, že LR byl uložen/obnoven

    • C = 0, udávající bez řetězení rámu

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

Příklad 3: Vnořená Variadická funkce

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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 1, označující kanonický prologue a epilogue formáty

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

    • Ret = 0, označující návrat typu pop {pc}(v tomto případě ldr pc,[sp],#0x14 návrat)

    • H = 1, indikující, že parametry byly umisovány

    • R = 0 a Reg = 2, označující push/pop r4-r6

    • L = 1, indikující, že LR byl uložen/obnoven

    • C = 0, udávající bez řetězení rámu

    • Stack Adjust = 0, označující žádné úpravy zásobníku

Příklad 4: Funkce s více epilogy

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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 0, označující .xdata přítomnost záznamu (vyžadováno pro více epilogů)

    • .xdata adresa – 0x00400000

.xdata (proměnná, 6 slov):

  • Word 0

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

    • Vers = 0, označující první verzi.xdata

    • X = 0, označující žádná data o výjimce

    • E = 0, označující seznam oborů epilogu

    • F = 0 označující úplný popis funkce, včetně prologue

    • Epilogue Count = 0x04 označující 4 celkové rozsahy epilogu

    • Code Words = 0x01 označující jedno 32bitové slovo kódů odvíjení

  • Slova 1-4 popisující 4 rozsahy epilogu na 4 místech. Každý obor má společnou sadu kódů odvíjení, které jsou sdíleny s prologue, při posunu 0x00 a je nepodmíněné a určuje podmínku 0x0E (vždy).

  • Odvíjení kódů od Wordu 5: (sdíleno mezi prologue/epilogue)

    • Unwind code 0 = 0x06: sp += (6 << 2)

    • Unwind code 1 = 0xDE: pop {r4-r10, lr}

    • Unwind code 2 = 0xFF: end

Příklad 5: Funkce s dynamickým zásobníkem a vnitřním epilogem

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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 0, označující .xdata přítomnost záznamu (potřebný pro více epilogů)

    • .xdata adresa – 0x00400000

.xdata (proměnná, 3 slova):

  • Word 0

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

    • Vers = 0, označující první verzi.xdata

    • X = 0, označující žádná data o výjimce

    • E = 0, označující seznam oborů epilogu

    • F = 0 označující úplný popis funkce, včetně prologue

    • Epilogue Count = 0x001 označující celkový rozsah epilogu

    • Code Words = 0x01 označující jedno 32bitové slovo kódů odvíjení

  • Word 1: Rozsah epilogu na posunu 0xC6 (= 0x18C/2), spuštění indexu kódu od 0x00 a s podmínkou 0x0E (vždy)

  • Odvíjení kódů od Wordu 2: (sdíleno mezi prologue/epilogue)

    • Unwind code 0 = 0xC6: sp = r6

    • Unwind code 1 = 0xDC: pop {r4-r8, lr}

    • Unwind code 2 = 0x04: sp += (4 << 2)

    • Unwind code 3 = 0xFD: end, počítá se jako 16bitová instrukce pro epilog

Příklad 6: Funkce s obslužnou rutinou výjimky

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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 0, označující .xdata přítomnost záznamu (potřebný pro více epilogů)

    • .xdata adresa – 0x00400000

.xdata (proměnná, 5 slov):

  • Word 0

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

    • Vers = 0, označující první verzi.xdata

    • X = 1, označující údaje o výjimce

    • E = 1 označující jeden epilogue

    • F = 0 označující úplný popis funkce, včetně prologue

    • Epilogue Count = 0x00 označující kódy epilogu odvíjení začínají odsazením 0x00

    • Code Words = 0x02 označující dvě 32bitová slova odvíjecích kódů

  • Odvíjení kódů od Wordu 1:

    • Unwind code 0 = 0xC7: sp = r7

    • Odvinutí kódu 1 = 0x05: sp += (5 << 2)

    • Unwind code 2 = 0xED/0x90: pop {r4, r7, lr}

    • Unwind code 4 = 0xFF: end

  • Word 3 určuje obslužnou rutinu výjimky = 0x0019A7ED (= 0x0059A7ED – 0x00400000).

  • Slova 4 a novější jsou vložená data výjimek.

Příklad 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 (opraveno, 2 slova):

  • Word 0

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

    • Flag = 1, označující kanonický prologue a epilogue formáty

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

    • Ret = 0 označující návrat pop {pc}

    • H = 0, označující, že parametry nebyly doma

    • R = 0 a Reg = 7, což znamená, že nebyly uloženy/obnoveny žádné registry

    • L = 1, indikující, že LR byl uložen/obnoven

    • C = 0, udávající bez řetězení rámu

    • Stack Adjust = 1 označující 1 × 4 bajtové úpravy zásobníku

Viz také

Přehled konvencí ARM ABI
Běžné problémy s migrací ARM v prostředí Visual C++