x64 – prolog a epilog
Každá funkce, která přiděluje prostor zásobníku, volá jiné funkce, ukládá nevolatilní registry nebo používá zpracování výjimek, musí mít prolog, jehož limity adres jsou popsány v datech odvíjení přidružených k příslušné položce tabulky funkcí. Další informace najdete v tématu zpracování výjimek x64. Prolog uloží argumenty v případě potřeby do svých domovských adres, vloží do zásobníku nevolatilní registry, přidělí pevnou část zásobníku pro místní a dočasné a volitelně vytvoří ukazatel rámce. Přidružená data odvíjení musí popisovat akci prologu a musí poskytnout informace potřebné k vrácení účinku kódu prologu.
Pokud je pevné přidělení v zásobníku více než jedna stránka (tj. větší než 4096 bajtů), je možné, že přidělení zásobníku může zahrnovat více než jednu stránku virtuální paměti, a proto je nutné přidělení zkontrolovat před přidělením. Pro tento účel je k dispozici speciální rutina, která se dá volat z prologu a která nezničí žádný z registrů argumentů.
Upřednostňovanou metodou ukládání nevolatilních registrů je přesunout je do zásobníku před pevným přidělením zásobníku. Pokud se pevné přidělení zásobníku provádí před uložením nevolatilních registrů, je pravděpodobně nutné vyřešit uloženou oblast registru o 32bitovém posunu. (Nahlásilo se, že zápisy registrů jsou stejně rychlé jako pohyby a měly by zůstat v dohledné budoucnosti i přes předpokládané závislosti mezi nabízenými oznámeními.) Nevolatilní registry je možné uložit v libovolném pořadí. Prvním použitím nevolatilního registru v prologu však musí být uložení.
Kód prologu
Kód typického prologu může být následující:
mov [RSP + 8], RCX
push R15
push R14
push R13
sub RSP, fixed-allocation-size
lea R13, 128[RSP]
...
Tento prolog uloží RCX registru argumentů do svého domovského umístění, uloží nevolatilní registry R13-R15, přidělí pevnou část rámce zásobníku a vytvoří ukazatel rámce, který odkazuje na 128 bajtů do pevné alokační oblasti. Použití posunu umožňuje adresovat větší část pevné oblasti přidělení pomocí posunů 1 bajtů.
Pokud je pevná velikost přidělení větší nebo rovna jedné stránce paměti, musí být před úpravou SOUBORU RSP volána pomocná funkce. Tato pomocná __chkstk
rutina testuje rozsah zásobníku, který se má přidělit, aby se zajistilo správné rozšíření zásobníku. V takovém případě by předchozí příklad prologu byl:
mov [RSP + 8], RCX
push R15
push R14
push R13
mov RAX, fixed-allocation-size
call __chkstk
sub RSP, RAX
lea R13, 128[RSP]
...
Pomocník __chkstk
neupraví žádné registry jiné než R10, R11 a kódy podmínek. Konkrétně vrátí RAX beze změny a ponechá všechny nevolatilní registry a registry s předáváním argumentů nezměněné.
Kód epilogu
Kód epilogu existuje na každém výstupu funkce. Zatímco obvykle existuje jen jeden prolog, může existovat mnoho epilogů. Kód epilogu oříznou zásobník na pevnou velikost přidělení (v případě potřeby), uvolní pevné přidělení zásobníku, obnoví nevolatilní registry tak, že v zásobníku přepíná uložené hodnoty a vrátí se.
Kód epilogu musí dodržovat striktní sadu pravidel pro odvíjení kódu, aby spolehlivě odvíjely výjimky a přerušily. Tato pravidla snižují množství požadovaných dat odvíjení, protože k popisu každého epilogu není potřeba žádná další data. Místo toho může unwind kód určit, že se epilog spouští, a to tak, že prohledá přes stream kódu a identifikuje epilog.
Pokud ve funkci není použit žádný ukazatel rámce, musí epilog nejprve uvolnit pevnou část zásobníku, zobrazí se nevolatilní registry a ovládací prvek se vrátí do volající funkce. Příklad:
add RSP, fixed-allocation-size
pop R13
pop R14
pop R15
ret
Pokud je ve funkci použit ukazatel rámce, musí být zásobník před spuštěním epilogu oříznut na pevné přidělení. Tato akce není technicky součástí epilogu. Například následující epilog lze použít k vrácení dříve použitého prologu zpět:
lea RSP, -128[R13]
; epilogue proper starts here
add RSP, fixed-allocation-size
pop R13
pop R14
pop R15
ret
V praxi platí, že pokud se použije ukazatel na rámec, neexistuje žádný dobrý důvod k úpravě RSP ve dvou krocích, takže by se místo toho použil následující epilog:
lea RSP, fixed-allocation-size - 128[R13]
pop R13
pop R14
pop R15
ret
Tyto formy jsou jediné právní formy pro epilog. Musí se skládat z jedné add RSP,constant
nebo lea RSP,constant[FPReg]
, následované řadou 8 bajtů 8 bajtů a return
nebo jmp
. (V epilogu lze povolit pouze podmnožinu jmp
příkazů. Podmnožina je výhradně třída jmp
příkazů s odkazy na paměť ModRM, kde hodnota pole ModRM je 00. Použití jmp
příkazů v epilogu s hodnotou pole ModRM mod 01 nebo 10 je zakázáno. Další informace o povolených odkazech ModRM najdete v tabulce A-15 v příručce AMD x86-64 Architecture Programmer's Manual Volume 3: General Purpose and System Instructions.) Nemůže se zobrazit žádný jiný kód. Konkrétně nelze v epilogu naplánovat nic, včetně načtení návratové hodnoty.
Pokud není použit ukazatel rámce, musí epilog použít add RSP,constant
k uvolnění pevné části zásobníku. Místo toho se nemusí používat lea RSP,constant[RSP]
. Toto omezení existuje, takže při hledání epilogů má méně vzorů, které by bylo potřeba rozpoznat.
Následující pravidla umožňují odvinutí kódu určit, že se aktuálně spouští epilog, a simulovat spuštění zbytku epilogu, aby bylo možné znovu vytvořit kontext volající funkce.