Sdílet prostřednictvím


x64 – ošetření výjimek

Přehled strukturovaného zpracování výjimek a konvencí kódování a chování zpracování výjimek jazyka C++ v x64. Obecné informace o zpracování výjimek naleznete v tématu Zpracování výjimek v jazyce Visual C++.

Uvolnění dat pro zpracování výjimek, podpora ladicího programu

Pro zpracování výjimek a ladění se vyžaduje několik datových struktur.

struct RUNTIME_FUNCTION

Zpracování výjimek na základě tabulky vyžaduje položku tabulky pro všechny funkce, které přidělují prostor zásobníku nebo volají jinou funkci (například neleafové funkce). Položky tabulky funkcí mají formát:

Velikost Hodnota
ULONG Počáteční adresa funkce
ULONG Koncová adresa funkce
ULONG Unwind info address

Struktura RUNTIME_FUNCTION musí být zarovnaná do paměti. Všechny adresy jsou relativní vzhledem k obrázku, to znamená, že jsou 32bitové posuny od počáteční adresy obrázku, který obsahuje položku tabulky funkcí. Tyto položky se seřadí a vloží se do části .pdata obrázku PE32+. Pro dynamicky generované funkce [kompilátory JIT] musí modul runtime pro podporu těchto funkcí použít rtlInstallFunctionTableCallback nebo RtlAddFunctionTable k poskytnutí těchto informací operačnímu systému. Pokud to neuděláte, dojde k nespolehlivému zpracování výjimek a ladění procesů.

struct UNWIND_INFO

Struktura informací o odvinutí dat slouží k zaznamenání efektů, které má funkce na ukazatel zásobníku a kde se v zásobníku ukládají nevolatilní registry:

Velikost Hodnota
UBYTE: 3 Verze
UBYTE: 5 Příznaky
UBYTE Velikost prologu
UBYTE Počet odvíjených kódů
UBYTE: 4 Registr rámce
UBYTE: 4 Posun registru rámce (měřítko)
USHORT * n Unwind codes array
proměnná Může mít formu (1) nebo (2) níže.

(1) Obslužná rutina výjimky

Velikost Hodnota
ULONG Adresa obslužné rutiny výjimky
proměnná Data obslužné rutiny specifická pro jazyk (volitelné)

(2) Zřetězený unwind info

Velikost Hodnota
ULONG Počáteční adresa funkce
ULONG Koncová adresa funkce
ULONG Unwind info address

Struktura UNWIND_INFO musí být zarovnaná do paměti. Toto pole znamená:

  • Verze

    Číslo verze odvíjených dat, aktuálně 1.

  • Vlajky

    Aktuálně jsou definovány tři příznaky:

    Příznak Popis
    UNW_FLAG_EHANDLER Funkce má obslužnou rutinu výjimky, která by se měla volat při hledání funkcí, které potřebují prozkoumat výjimky.
    UNW_FLAG_UHANDLER Funkce má obslužnou rutinu ukončení, která by se měla volat při odvíjení výjimky.
    UNW_FLAG_CHAININFO Tato struktura informací o odvíjení není primárním objektem pro postup. Místo toho je zřetězený záznam informací o uvolnění obsahu předchozí položky RUNTIME_FUNCTION. Informace naleznete v tématu Zřetězený unwind info struktury. Pokud je tento příznak nastavený, musí být UNW_FLAG_EHANDLER a UNW_FLAG_UHANDLER příznaky vymazány. Registr rámce a pole přidělení s pevným zásobníkem musí mít také stejné hodnoty jako v primárních informacích o uvolnění.
  • Velikost prologu

    Délka prologu funkce v bajtech

  • Počet odvíjených kódů

    Počet slotů v poli kódů unwind. Některé kódy odvíjení, například UWOP_SAVE_NONVOL, vyžadují v poli více než jeden slot.

  • Registr rámce

    Pokud není nula, použije funkce ukazatel rámce (FP) a toto pole je číslo nevolatilního registru použitého jako ukazatel rámce pomocí stejného kódování pro pole s informacemi o operaci UNWIND_CODE uzlů.

  • Posun registru rámce (měřítko)

    Pokud je pole registru rámce nenulové, jedná se o měřítko posunu od formátu RSP, který se použije u registru FP při jeho vytvoření. Skutečný registr FP je nastaven na RSP + 16 * toto číslo, což umožňuje posuny od 0 do 240. Tento posun umožňuje nasměrovat registr FP doprostřed přidělení místního zásobníku pro dynamické snímky zásobníku, což umožňuje lepší hustotu kódu prostřednictvím kratších instrukcí. (To znamená, že další pokyny můžou použít 8bitový odsazení formuláře.)

  • Unwind codes array

    Pole položek, které vysvětlují účinek prologu na nevolatilní registry a RSP. Informace o významech jednotlivých položek najdete v části UNWIND_CODE. Pro účely zarovnání má toto pole vždy sudý počet položek a konečná položka je potenciálně nepoužitá. V takovém případě je matice delší, než je uvedeno počtem polí kódů odvíjení.

  • Adresa obslužné rutiny výjimky

    Ukazatel relativní vzhledem k obrázku na obslužnou rutinu výjimky nebo ukončení funkce, pokud je příznak UNW_FLAG_CHAININFO jasný a jeden z příznaků UNW_FLAG_EHANDLER nebo UNW_FLAG_UHANDLER je nastavený.

  • Data obslužné rutiny specifické pro jazyk

    Data obslužné rutiny výjimek specifické pro jazyk funkce. Formát těchto dat není zadán a zcela určen konkrétní obslužnou rutinou výjimky, která se používá.

  • Zřetězený informace o unwind

    Pokud je nastavena UNW_FLAG_CHAININFO příznaku, končí struktura UNWIND_INFO třemi identifikátory UWORD. Tyto UWORD představují RUNTIME_FUNCTION informace pro funkci zřetězeného odvíjení.

struct UNWIND_CODE

Pole kódu unwind se používá k zaznamenání posloupnosti operací v prologu, které ovlivňují nevolatilní registry a RSP. Každá položka kódu má tento formát:

Velikost Hodnota
UBYTE Posun v prologu
UBYTE: 4 Unwind operation code
UBYTE: 4 Informace o operaci

Pole je seřazeno sestupně podle posunu v prologu.

Posun v prologu

Posun (od začátku prologu) konce instrukce, která provádí tuto operaci, plus 1 (to znamená posun začátku další instrukce).

Unwind operation code

Poznámka: Některé kódy operací vyžadují posun bez znaménka na hodnotu v místním rámečku zásobníku. Tento posun je od začátku, tj. nejnižší adresa pevného přidělení zásobníku. Pokud je pole Registr rámce v UNWIND_INFO nula, je tento posun z RSP. Pokud je pole Registr rámce nenulové, je toto posun od místa, kde se při vytvoření registru FP nacházelo UMÍSTĚNÍ SERVERU. Rovná se registru FP minus posun registru FP (16 * posun škálovaného registru rámce v UNWIND_INFO). Pokud se použije registr FP, musí být veškerý kód odvíjení posunu použit pouze po vytvoření registru FP v prologu.

Pro všechny opcodes s výjimkou UWOP_SAVE_XMM128 a UWOP_SAVE_XMM128_FAR, posun je vždy násobkem 8, protože všechny hodnoty zásobníku zájmu jsou uloženy na 8 bajtových hranicích (samotný zásobník je vždy zarovnaný 16 bajtů). Pro kódy operací, které mají krátký posun (menší než 512K), obsahuje konečný nástroj USHORT v uzlech pro tento kód posun dělený hodnotou 8. Pro kódy operací, které mají dlouhý posun (512K <= posun < 4 GB), poslední dva uzly USHORT pro tento kód obsahují posun (v malém endian formátu).

Pro opcodes UWOP_SAVE_XMM128 a UWOP_SAVE_XMM128_FAR, posun je vždy násobek 16, protože všechny 128bitové operace XMM musí proběhnout na 16 bajtové zarovnané paměti. Proto se používá měřítko 16 pro UWOP_SAVE_XMM128, což umožňuje posuny menší než 1 M.

Kód operace unwind je jednou z těchto hodnot:

  • UWOP_PUSH_NONVOL (0) 1 uzel

    Nasdílí nevolatilní celočíselnou registraci, která dekrementuje RSP o 8. Informace o operaci jsou číslo registru. Vzhledem k omezením epilogů se UWOP_PUSH_NONVOL kódy odvíjení musí objevit jako první v prologu a odpovídajícím způsobem, a to v poli kódu odvíjení. Toto relativní řazení platí pro všechny ostatní kódy odvíjení s výjimkou UWOP_PUSH_MACHFRAME.

  • UWOP_ALLOC_LARGE (1) 2 nebo 3 uzly

    Přidělte na zásobníku velkou plochu. Existují dvě formy. Pokud se informace o operaci rovná 0, pak se velikost přidělení dělené hodnotou 8 zaznamená do dalšího slotu, což umožňuje přidělení až 512 K – 8. Pokud se informace o operaci rovná 1, pak se velikost přidělení nezaznamená v následujících dvou slotech v malém endovém formátu, což umožňuje přidělení až 4 GB – 8.

  • UWOP_ALLOC_SMALL (2) 1 uzel

    Přidělte v zásobníku malou plochu. Velikost přidělení je pole s informacemi o operaci * 8 + 8, což umožňuje přidělení od 8 do 128 bajtů.

    Kód uvolnění pro přidělení zásobníku by měl vždy používat nejkratší možné kódování:

    Velikost přidělení Unwind Code
    8 až 128 bajtů UWOP_ALLOC_SMALL
    136 až 512 K-8 bajtů UWOP_ALLOC_LARGE, informace o operaci = 0
    512K až 4G-8 bajtů UWOP_ALLOC_LARGE, informace o operaci = 1
  • UWOP_SET_FPREG (3) 1 uzel

    Vytvořte registr ukazatele rámce nastavením registru na určitý posun aktuálníHO SOUBORU RSP. Posun je roven posunu registru rámce (měřítko) v UNWIND_INFO * 16, což umožňuje posuny od 0 do 240. Použití posunu umožňuje navázat ukazatel rámce, který odkazuje na střed pevného přidělení zásobníku, což pomáhá hustotě kódu tím, že umožňuje více přístupů k používání krátkých instrukčních formulářů. Pole s informacemi o operaci je rezervované a nemělo by se používat.

  • UWOP_SAVE_NONVOL (4) 2 uzly

    Uložte nevolatilní celočíselnou registraci v zásobníku pomocí MOV místo funkce PUSH. Tento kód se primárně používá pro obtékání zmenšení, kdy se do zásobníku, který byl dříve přidělen, uloží nevolatilní registr. Informace o operaci jsou číslo registru. Posun zásobníku se škálováním o 8 se zaznamená do dalšího slotu kódu operace unwind, jak je popsáno v poznámce výše.

  • UWOP_SAVE_NONVOL_FAR (5) 3 uzly

    Uložte nevolatilní celočíselnou registraci na zásobníku s dlouhým posunem pomocí MOV místo funkce PUSH. Tento kód se primárně používá pro obtékání zmenšení, kdy se do zásobníku, který byl dříve přidělen, uloží nevolatilní registr. Informace o operaci jsou číslo registru. Posun zásobníku bez měřítka se zaznamená do dalších dvou slotů kódu operace unwind, jak je popsáno v poznámce výše.

  • UWOP_SAVE_XMM128 (8) 2 uzly

    Uložte všechny 128 bitů nevolatilního registru XMM do zásobníku. Informace o operaci jsou číslo registru. Posun zásobníku se škálováním o 16 se zaznamená do dalšího slotu.

  • UWOP_SAVE_XMM128_FAR (9) 3 uzly

    Uložte všechny 128 bitů nevolatilního registru XMM na zásobníku s dlouhým posunem. Informace o operaci jsou číslo registru. Posun zásobníku bez měřítka se zaznamená do následujících dvou slotů.

  • UWOP_PUSH_MACHFRAME (10) 1 uzel

    Nasdílení rámu stroje Tento odvinutí kódu slouží k zaznamenání efektu přerušení nebo výjimky hardwaru. Existují dvě formy. Pokud se informace o operaci rovná 0, jeden z těchto snímků byl vložen do zásobníku:

    Umístění Hodnota
    RSP+32 SS
    RSP+24 Starý RSP
    RSP+16 EFLAGS
    RSP+8 CS
    RSP ROZTRHNOUT

    Pokud se informace o operaci rovná 1, pak se nasdílí jeden z těchto snímků:

    Umístění Hodnota
    RSP+40 SS
    RSP+32 Starý RSP
    RSP+24 EFLAGS
    RSP+16 CS
    RSP+8 ROZTRHNOUT
    RSP Kód chyby

    Tento odvíjecí kód se vždy zobrazí v fiktivním prologu, který se nikdy nespustí, ale místo toho se zobrazí před skutečným vstupním bodem rutiny přerušení a existuje pouze místo pro simulaci nasdílení snímku počítače. UWOP_PUSH_MACHFRAME zaznamenává, že simulace, která označuje, že počítač tuto operaci provedl koncepčně:

    1. Návratová adresa POP RIP z horní části zásobníku do tempu

    2. Nabízení SS

    3. Nasdílení starýCH DAT

    4. Nasdílení změn EFLAGS

    5. Nasdílení změn CS

    6. Nasdílení temp

    7. Nasdílení kódu chyby (pokud se informace o operacích rovná 1)

    Simulovaná UWOP_PUSH_MACHFRAME operace snižuje hodnotu RSP o 40 (provozní informace se rovná 0) nebo 48 (provozní informace se rovná 1).

Informace o operaci

Význam informačních bitů operace závisí na kódu operace. K kódování registru pro obecné účely (celé číslo) se používá toto mapování:

Bit Registrovat
0 RAX
0 RCX
2 RDX
3 RBX
4 RSP
5 RBP
6 RSI
7 RDI
8 až 15 R8 až R15

Zřetězený informační struktury unwind

Pokud je nastaven příznak UNW_FLAG_CHAININFO, pak je sekundární informační struktura a pole se sdílenou obslužnou rutinou výjimky nebo zřetězeným informačním polem obsahuje primární informace o odvíjení. Tento ukázkový kód načte primární informace o odvíjení za předpokladu, že unwindInfo jde o strukturu, která má nastavenou UNW_FLAG_CHAININFO příznak.

PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);

Zřetězený informace jsou užitečné ve dvou situacích. Nejprve je možné ho použít pro nesouvislé segmenty kódu. Pomocí zřetězených informací můžete zmenšit velikost požadovaných informací o uvolnění, protože pole kódů odvíjení nemusíte duplikovat z primárních informací o odvíjení.

Pomocí zřetězených informací můžete také seskupit nestálé ukládání registrací. Kompilátor může zpozdit ukládání některých nestálých registrů, dokud není mimo prolog prologu prologu funkce. Můžete je zaznamenat tak, že budete mít před seskupeným kódem primární informace o rozvětvování pro část funkce a pak nastavíte zřetězené informace s nenulovou velikostí prologu, kde kódy odvíjení v zřetězených informacích odrážejí uložení nevolatilních registrů. V takovém případě jsou kódy unwind všechny instance UWOP_SAVE_NONVOL. Seskupení, které ukládá nevolatilní registry pomocí funkce PUSH nebo upravuje registr RSP pomocí dodatečného pevného přidělení zásobníku, není podporováno.

Položka UNWIND_INFO, která má sadu UNW_FLAG_CHAININFO, může obsahovat položku RUNTIME_FUNCTION, jejíž položka UNWIND_INFO má také UNW_FLAG_CHAININFO sadu, někdy označovanou jako více obtékání. Nakonec se zřetězenými ukazateli informací o uvolnění zobrazí UNWIND_INFO položka, která UNW_FLAG_CHAININFO vymaže. Tato položka je primární UNWIND_INFO položka, která odkazuje na skutečný vstupní bod procedury.

Unwind procedure

Pole unwind kódu je seřazeno do sestupného pořadí. Když dojde k výjimce, celý kontext je uložen operačním systémem v kontextovém záznamu. Pak se vyvolá logika odeslání výjimky, která opakovaně provede tyto kroky a vyhledá obslužnou rutinu výjimky:

  1. Pomocí aktuálního protokolu RIP uloženého v kontextovém záznamu vyhledejte položku tabulky RUNTIME_FUNCTION, která popisuje aktuální funkci (nebo část funkce pro zřetězený UNWIND_INFO položek).

  2. Pokud nebyla nalezena žádná položka tabulky funkcí, je v listové funkci a APLIKACE RSP přímo adresuje návratový ukazatel. Návratový ukazatel na [RSP] je uložen v aktualizovaném kontextu, simulovaný RSP se zvýší o 8 a krok 1 se opakuje.

  3. Pokud se najde položka tabulky funkcí, může rip lhát ve třech oblastech: a) v epilogu, b) v prologu nebo c) v kódu, který může být pokryt obslužnou rutinou výjimky.

    • Případ a) Pokud je protokol RIP v epilogu, ovládací prvek opouští funkci, nemůže existovat žádná obslužná rutina výjimky přidružená k této výjimce této funkce a účinky epilogu musí být pokračovat při výpočtu kontextu funkce volajícího. Aby bylo možné zjistit, jestli se protokol RIP nachází v epilogu, je zkoumán datový proud kódu z protokolu RIP. Pokud se tento stream kódu dá spárovat s koncovou částí legitimního epilogu, pak se nachází v epilogu a zbývající část epilogu se simuluje, přičemž kontextový záznam se aktualizuje při zpracování jednotlivých instrukcí. Po tomto zpracování se krok 1 opakuje.

    • B) Pokud protokol RIP leží v prologu, ovládací prvek nezadá funkci, nemůže existovat žádná obslužná rutina výjimky přidružená k této výjimce pro tuto funkci a účinky prologu musí být vráceny zpět, aby bylo možné vypočítat kontext funkce volajícího. Protokol RIP je v prologu, pokud vzdálenost od začátku funkce k protokolu RIP je menší nebo rovna velikosti prologu zakódované v informacích o odvíjení. Účinky prologu jsou unwound skenováním dopředu přes pole odvíjecí kódy pro první položku s posunem menší než nebo rovno posunu RIP od začátku funkce a následným vrácením efektu všech zbývajících položek v poli unwind kódu. Krok 1 se pak opakuje.

    • C) Pokud protokol RIP není v prologu nebo epilogu a funkce má obslužnou rutinu výjimky (UNW_FLAG_EHANDLER je nastavena), volá se obslužná rutina specifická pro jazyk. Obslužná rutina prohledává data a podle potřeby volá funkce filtru. Obslužná rutina specifická pro jazyk může vrátit, že byla zpracována výjimka nebo že má vyhledávání pokračovat. Může také zahájit odvíjení přímo.

  4. Pokud obslužná rutina specifická pro jazyk vrátí zpracovaný stav, bude provádění pokračovat pomocí původního kontextového záznamu.

  5. Pokud neexistuje žádná obslužná rutina specifická pro jazyk nebo obslužná rutina vrátí stav "pokračovat v hledání", musí být kontextový záznam unwound do stavu volajícího. Provádí se zpracováním všech prvků pole kódu unwind a vrácením efektu každého z nich zpět. Krok 1 se pak opakuje.

Při zřetězených informacích o uvolnění se stále postupuje podle těchto základních kroků. Jediným rozdílem je, že při procházení pole odvíjení kódu k odvinutí efektů prologu je po dosažení konce pole propojeno s nadřazenými informacemi o odvíjení a celé pole kódu odvíjejícího kódu je procházené. Toto propojení pokračuje, dokud nepřichází na informace o uvolnění bez příznaku UNW_CHAINED_INFO a pak dokončí procházení pole odvíjejícího kódu.

Nejmenší sada odvíjecích dat je 8 bajtů. To by představovalo funkci, která přidělila pouze 128 bajtů zásobníku nebo méně a pravděpodobně uložila jeden nevolatilní registr. Je to také velikost zřetězené informační struktury prolog s nulovou délkou bez odvíjení kódů.

Obslužná rutina specifická pro jazyk

Relativní adresa obslužné rutiny specifické pro jazyk je v UNWIND_INFO vždy, když jsou nastavené příznaky UNW_FLAG_EHANDLER nebo UNW_FLAG_UHANDLER. Jak je popsáno v předchozí části, obslužná rutina specifická pro jazyk je volána jako součást hledání obslužné rutiny výjimky nebo jako součást odvíjení. Má tento prototyp:

typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN ULONG64 EstablisherFrame,
    IN OUT PCONTEXT ContextRecord,
    IN OUT PDISPATCHER_CONTEXT DispatcherContext
);

ExceptionRecord poskytuje ukazatel na záznam výjimky, který má standardní definici Win64.

EstablisherFrame je adresa základu pevného přidělení zásobníku pro tuto funkci.

KontextZáznam odkazuje na kontext výjimky v době, kdy byla výjimka vyvolána (v případě obslužné rutiny výjimky) nebo aktuální kontext "unwind" (v případě obslužné rutiny ukončení).

DispatcherContext odkazuje na kontext dispečera pro tuto funkci. Má tuto definici:

typedef struct _DISPATCHER_CONTEXT {
    ULONG64 ControlPc;
    ULONG64 ImageBase;
    PRUNTIME_FUNCTION FunctionEntry;
    ULONG64 EstablisherFrame;
    ULONG64 TargetIp;
    PCONTEXT ContextRecord;
    PEXCEPTION_ROUTINE LanguageHandler;
    PVOID HandlerData;
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;

ControlPc je hodnota RIP v rámci této funkce. Tato hodnota je adresa výjimky nebo adresa, na které ovládací prvek opustil navazující funkci. Rip se používá k určení, zda je ovládací prvek uvnitř některé strážené konstrukce uvnitř této funkce, __try například blok pro__except/__try nebo .__try/__finally

ImageBase je základní image (načítá adresa) modulu obsahujícího tuto funkci, která se má přidat k 32bitovým posunům použitým v položce funkce a odvíjet informace o záznamu relativních adres.

FunctionEntry poskytuje ukazatel na položku RUNTIME_FUNCTION funkce, která drží funkci a odvíjeje relativní adresy základní image pro tuto funkci.

EstablisherFrame je adresa základu pevného přidělení zásobníku pro tuto funkci.

TargetIp Poskytuje volitelnou instrukční adresu, která určuje adresu pokračování odvíječe. Tato adresa se ignoruje, pokud není zadaný prvek EstablisherFrame .

KontextZáznam odkazuje na kontext výjimky, který se používá pro odesílání nebo odvíjení kódu výjimky systému.

LanguageHandler odkazuje na volání rutiny obslužné rutiny jazyka specifické pro jazyk.

HandlerData odkazuje na data obslužné rutiny specifické pro jazyk pro tuto funkci.

Odvíjení pomocných rutin pro MASM

Aby bylo možné psát správné rutiny sestavení, existuje sada pseudo-operací, které lze použít paralelně se skutečnými pokyny sestavení k vytvoření příslušné .pdata a .xdata. A existuje sada maker, která poskytují zjednodušené použití pseudoobsadů pro jejich nejběžnější použití.

Nezpracované pseudoobsady

Pseudoobsadová operace Popis
PROC FRAME [:ehandler] Způsobí, že MASM vygeneruje položku tabulky funkcí v souboru .pdata a uvolní informace v souboru .xdata pro strukturované zpracování výjimek funkce. Pokud je k dispozici obslužná rutina , zadá se tento proc do souboru .xdata jako obslužná rutina specifická pro jazyk.

Pokud je použit atribut FRAME, musí být následovaný . ENDPROLOG – direktiva. Pokud je funkce listová funkce (jak je definována v typech funkcí), atribut FRAME není nutný, stejně jako zbytek těchto pseudo-operací.
. REGISTRACE PUSHREG Vygeneruje UWOP_PUSH_NONVOL odvinutí položky kódu pro zadané číslo registru pomocí aktuálního posunu v prologue.

Používejte ho pouze u nevolatilních celých registrů. Pro nabízení nestálých registrů použijte . ALLOCSTACK 8, místo toho
. REGISTR SETFRAME, posun Vyplní pole registru rámečku a posun v informacích o odvíjení pomocí zadaného registru a posunu. Posun musí být násobek 16 a menší nebo roven 240. Tato direktiva také vygeneruje UWOP_SET_FPREG položku kódu pro zadaný registr pomocí aktuálního posunu prologu.
. ALLOCSTACK size Vygeneruje UWOP_ALLOC_SMALL nebo UWOP_ALLOC_LARGE se zadanou velikostí aktuálního posunu v prologu.

Operand velikosti musí být násobkem 8.
. SAVEREG register, offset Generuje UWOP_SAVE_NONVOL nebo UWOP_SAVE_NONVOL_FAR odvinutí položky kódu pro zadaný registr a posun pomocí aktuálního posunu prologu. MASM zvolí nejúčinnější kódování.

posun musí být kladný a násobek 8. posun je relativní vzhledem k základu rámce procedury, který je obecně v RSP, nebo pokud používáte ukazatel rámce, ukazatel rámce bez měřítka.
. SAVEXMM128 registr, posun Generuje UWOP_SAVE_XMM128 nebo UWOP_SAVE_XMM128_FAR odvinutí položky kódu pro zadaný registr XMM a posun pomocí aktuálního posunu prologu. MASM zvolí nejúčinnější kódování.

posun musí být kladný a násobek 16. posun je relativní vzhledem k základu rámce procedury, který je obecně v RSP, nebo pokud používáte ukazatel rámce, ukazatel rámce bez měřítka.
. PUSHFRAME [kód] Vygeneruje UWOP_PUSH_MACHFRAME odvíjení položky kódu. Pokud je zadán volitelný kód , položka odvinutí kódu je uveden modifikátor 1. V opačném případě je modifikátor 0.
.ENDPROLOG Signalizuje konec prologue prohlášení. Musí nastat v prvních 255 bajtech funkce.

Tady je ukázkový prolog funkce se správným použitím většiny opcode:

sample PROC FRAME
    db      048h; emit a REX prefix, to enable hot-patching
    push rbp
    .pushreg rbp
    sub rsp, 040h
    .allocstack 040h
    lea rbp, [rsp+020h]
    .setframe rbp, 020h
    movdqa [rbp], xmm7
    .savexmm128 xmm7, 020h ;the offset is from the base of the frame
                           ;not the scaled offset of the frame
    mov [rbp+018h], rsi
    .savereg rsi, 038h
    mov [rsp+010h], rdi
    .savereg rdi, 010h ; you can still use RSP as the base of the frame
                       ; or any other register you choose
    .endprolog

; you can modify the stack pointer outside of the prologue (similar to alloca)
; because we have a frame pointer.
; if we didn't have a frame pointer, this would be illegal
; if we didn't make this modification,
; there would be no need for a frame pointer

    sub rsp, 060h

; we can unwind from the next AV because of the frame pointer

    mov rax, 0
    mov rax, [rax] ; AV!

; restore the registers that weren't saved with a push
; this isn't part of the official epilog, as described in section 2.5

    movdqa xmm7, [rbp]
    mov rsi, [rbp+018h]
    mov rdi, [rbp-010h]

; Here's the official epilog

    lea rsp, [rbp+020h] ; deallocate both fixed and dynamic portions of the frame
    pop rbp
    ret
sample ENDP

Další informace o příkladu epilogu naleznete v tématu Kód epilogu v x64 prologu a epilogu.

Makra MASM

Aby bylo možné zjednodušit používání pseudo-operací Raw, existuje sada maker definovaných v ksamd64.inc, které lze použít k vytvoření typické procedury prologues a epilogues.

Makro Popis
alloc_stack(n) Přidělí rámec zásobníku n bajtů (pomocí sub rsp, n) a vygeneruje příslušné informace o uvolnění (.allocstack n).
save_reg reg, loc Uloží nevolatilní registr reg na zásobníku na loci posunu PROTOKOLU RSP a vygeneruje příslušné informace o odvíjení. (.savereg reg, loc)
push_reg reg Nasdílí do zásobníku nevolatilní reg registru a vygeneruje příslušné informace o uvolnění. (.pushreg reg)
rex_push_reg reg Uloží nevolatilní registraci do zásobníku pomocí 2bajtů nabízených oznámení a vygeneruje příslušné informace o uvolnění (.pushreg reg). Toto makro použijte, pokud je nasdílení změn první instrukcí funkce, abyste měli jistotu, že je funkce opravitelná za provozu.
save_xmm128 reg, loc Uloží nevolatilní registr XMM na zásobníku v umístění posunu APLIKACE RSP a vygeneruje příslušné informace o uvolnění (.savexmm128 reg, loc).
set_frame reg, posun Nastaví reg registru rámce tak, aby byl posunEM ( pomocí mov, nebo lea) a vygeneruje příslušné informace o odvinutí (.set_frame reg, posun).
push_eflags Nasdílí eflagy pokynem pushfq a vygeneruje příslušné informace o odvinutí (.alloc_stack 8).

Tady je ukázkový prolog funkce se správným použitím maker:

sampleFrame struct
    Fill     dq ?; fill to 8 mod 16
    SavedRdi dq ?; Saved Register RDI
    SavedRsi dq ?; Saved Register RSI
sampleFrame ends

sample2 PROC FRAME
    alloc_stack(sizeof sampleFrame)
    save_reg rdi, sampleFrame.SavedRdi
    save_reg rsi, sampleFrame.SavedRsi
    .end_prolog

; function body

    mov rsi, sampleFrame.SavedRsi[rsp]
    mov rdi, sampleFrame.SavedRdi[rsp]

; Here's the official epilog

    add rsp, (sizeof sampleFrame)
    ret
sample2 ENDP

Uvolnění definic dat v jazyce C

Tady je popis C dat odvíjení:

typedef enum _UNWIND_OP_CODES {
    UWOP_PUSH_NONVOL = 0, /* info == register number */
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
    UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;

typedef unsigned char UBYTE;

typedef union _UNWIND_CODE {
    struct {
        UBYTE CodeOffset;
        UBYTE UnwindOp : 4;
        UBYTE OpInfo   : 4;
    };
    USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;

#define UNW_FLAG_EHANDLER  0x01
#define UNW_FLAG_UHANDLER  0x02
#define UNW_FLAG_CHAININFO 0x04

typedef struct _UNWIND_INFO {
    UBYTE Version       : 3;
    UBYTE Flags         : 5;
    UBYTE SizeOfProlog;
    UBYTE CountOfCodes;
    UBYTE FrameRegister : 4;
    UBYTE FrameOffset   : 4;
    UNWIND_CODE UnwindCode[1];
/*  UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
*   union {
*       OPTIONAL ULONG ExceptionHandler;
*       OPTIONAL ULONG FunctionEntry;
*   };
*   OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;

typedef struct _RUNTIME_FUNCTION {
    ULONG BeginAddress;
    ULONG EndAddress;
    ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;

#define GetUnwindCodeEntry(info, index) \
    ((info)->UnwindCode[index])

#define GetLanguageSpecificDataPtr(info) \
    ((PVOID)&GetUnwindCodeEntry((info),((info)->CountOfCodes + 1) & ~1))

#define GetExceptionHandler(base, info) \
    ((PEXCEPTION_HANDLER)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))

#define GetChainedFunctionEntry(base, info) \
    ((PRUNTIME_FUNCTION)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))

#define GetExceptionDataPtr(info) \
    ((PVOID)((PULONG)GetLanguageSpecificData(info) + 1))

Viz také

x64 – softwarové konvence