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žeC
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, žeStack Adjust
je 0x3F4 nebo větší a bit 2 je nastaven.EF
nebo "epilogue folding" označuje, žeStack 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 add
verzi .
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 C
základě polí , L
, R
a 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:
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
:
- PokudE
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í.
- PokudE
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 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. 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.
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:
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.
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.
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ý .pdata
záznam (a případně i .xdata
zá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
, B
a 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átyFunction Length
= 0x31 (= 0x62/2)Ret
= 1 označující 16bitovou větev returnH
= 0, označující, že parametry nebyly domaR
= 0 aReg
= 1, označující push/pop r4-r5L
= 0, což znamená, že se neukládá nebo neobnoví LRC
= 0, udávající bez řetězení rámuStack 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átyFunction Length
= 0x35 (= 0x6A/2)Ret
= 0 označující návrat pop {pc}H
= 0, označující, že parametry nebyly domaR
= 0 aReg
= 3, označující push/pop r4-r7L
= 1, indikující, že LR byl uložen/obnovenC
= 0, udávající bez řetězení rámuStack 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átyFunction 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ányR
= 0 aReg
= 2, označující push/pop r4-r6L
= 1, indikující, že LR byl uložen/obnovenC
= 0, udávající bez řetězení rámuStack 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ýjimceE
= 0, označující seznam oborů epiloguF
= 0 označující úplný popis funkce, včetně prologueEpilogue Count
= 0x04 označující 4 celkové rozsahy epiloguCode 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ýjimceE
= 0, označující seznam oborů epiloguF
= 0 označující úplný popis funkce, včetně prologueEpilogue Count
= 0x001 označující celkový rozsah epiloguCode 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ýjimceE
= 1 označující jeden epilogueF
= 0 označující úplný popis funkce, včetně prologueEpilogue Count
= 0x00 označující kódy epilogu odvíjení začínají odsazením 0x00Code 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átyFunction Length
= 0x0B (= 0x16/2)Ret
= 0 označující návrat pop {pc}H
= 0, označující, že parametry nebyly domaR
= 0 aReg
= 7, což znamená, že nebyly uloženy/obnoveny žádné registryL
= 1, indikující, že LR byl uložen/obnovenC
= 0, udávající bez řetězení rámuStack 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++