Condividi tramite


Gestione delle eccezioni x64

Panoramica della gestione delle eccezioni strutturata e della gestione delle eccezioni C++ nelle convenzioni di codifica e nel comportamento su x64. Per informazioni generali sulla gestione delle eccezioni, vedere Gestione delle eccezioni in Microsoft C++.

Dati di dispiegamento per la gestione delle eccezioni, il supporto del debugger

Per la gestione delle eccezioni e il supporto del debug sono necessarie diverse strutture di dati.

struct RUNTIME_FUNCTION

La gestione delle eccezioni basata su tabelle richiede una voce di tabella per tutte le funzioni che allocano lo spazio dello stack o chiamano un'altra funzione , ad esempio funzioni non foglia. Le voci della tabella delle funzioni hanno il formato seguente:

Dimensione Valore
ULONG Indirizzo iniziale della funzione
ULONG Indirizzo finale della funzione
ULONG Indirizzo informazioni di 'unwind'

La struttura RUNTIME_FUNCTION deve essere allineata su DWORD in memoria. Tutti gli indirizzi sono relativi all'immagine, ovvero sono offset a 32 bit dall'indirizzo iniziale dell'immagine che contiene la voce della tabella della funzione. Queste voci vengono ordinate e inserite nella sezione .pdata di un'immagine PE32+. Per le funzioni generate dinamicamente [compilatori JIT], il runtime per supportare queste funzioni deve usare RtlInstallFunctionTableCallback o RtlAddFunctionTable per fornire queste informazioni al sistema operativo. In caso contrario, la gestione delle eccezioni e il debug dei processi non saranno affidabili.

struttura UNWIND_INFO

La struttura delle informazioni sui dati di unwind viene usata per registrare gli effetti di una funzione sul puntatore dello stack e dove i registri non volatili vengono salvati nello stack.

Dimensione Valore
UBYTE: 3 Versione
UBYTE: 5 Flagge
UBYTE Dimensioni del prologo
UBYTE Numero di codici di disimballaggio
UBYTE: 4 Registro frame
UBYTE: 4 Offset del registro del frame (ridimensionato)
USHORT * n Matrice di codici di rimozione
Variabile Può essere di formato (1) o (2) di seguito

Gestore delle eccezioni

Dimensione Valore
ULONG Indirizzo del gestore eccezioni
Variabile Dati del gestore specifici del linguaggio (facoltativo)

(2) Informazioni di disimballaggio concatenate

Dimensione Valore
ULONG Indirizzo iniziale della funzione
ULONG Indirizzo finale della funzione
ULONG Indirizzo di informazioni di unwind

La struttura UNWIND_INFO deve essere allineata su DWORD in memoria. Ecco cosa significa ogni campo:

  • Versione

    Numero di versione dei dati di rimozione, attualmente 1.

  • Bandiere

    Attualmente sono definite tre bandiere.

    Bandiera Descrizione
    UNW_FLAG_EHANDLER La funzione ha un gestore eccezioni che deve essere chiamato quando si cercano funzioni che devono esaminare le eccezioni.
    UNW_FLAG_UHANDLER La funzione ha un gestore di terminazione che deve essere chiamato durante la rimozione di un'eccezione.
    UNW_FLAG_CHAININFO Questa struttura delle informazioni di rimozione non è quella principale per la procedura. Al contrario, la voce delle informazioni di sblocca concatenato è costituita dal contenuto di una voce RUNTIME_FUNCTION precedente. Per informazioni, vedere Strutture di informazioni sull'unwind concatenato. Se questo flag è impostato, i flag UNW_FLAG_EHANDLER e UNW_FLAG_UHANDLER devono essere cancellati. Inoltre, il registro dei frame e i campi di allocazione dello stack fisso devono avere gli stessi valori delle informazioni di rimozione primarie.
  • Dimensioni del prologo

    Lunghezza del prologo della funzione in byte.

  • Numero di codici di rimozione

    Numero di slot nella matrice dei codici di rimozione. Alcuni codici di rimozione, ad esempio UWOP_SAVE_NONVOL, richiedono più di uno slot nella matrice.

  • Registro frame

    Se diverso da zero, la funzione utilizza un frame pointer (FP) e questo campo è il numero del registro non volatile usato come frame pointer, utilizzando la stessa codifica del campo info operazione dei nodi UNWIND_CODE.

  • Offset del registro frame (ridimensionato)

    Se il campo del registro cornice è diverso da zero, questo campo rappresenta l'offset scalato da RSP applicato al registro FP quando è stabilito. Il registro FP effettivo è impostato su RSP + 16 * questo numero, consentendo offset da 0 a 240. Questo offset consente di puntare il registro FP al centro dell'allocazione dello stack locale per i frame di stack dinamici, consentendo una maggiore densità del codice tramite istruzioni più brevi. Altre istruzioni possono quindi usare il formato di offset con segno a 8 bit.

  • Matrice di codici di rimozione

    Matrice di elementi che spiega l'effetto del prologo nei registri non volatile e RSP. Vedere la sezione UNWIND_CODE per i significati dei singoli elementi. Ai fini dell'allineamento, questa matrice ha sempre un numero pari di voci e la voce finale è potenzialmente inutilizzata. In questo caso, l'array è più lungo di uno rispetto a quanto indicato dal campo conteggio dei codici di rilascio.

  • Indirizzo del gestore eccezioni

    Puntatore relativo all'immagine al gestore di terminazione o eccezione specifica della lingua della funzione, se il flag UNW_FLAG_CHAININFO è chiaro e viene impostato uno dei flag UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER.

  • Dati del gestore specifici della lingua

    Dati del gestore delle eccezioni specifico per linguaggio della funzione. Il formato di questi dati non è specificato e determinato completamente dal gestore di eccezioni specifico in uso.

  • Informazioni di srotolamento concatenate

    Se il flag UNW_FLAG_CHAININFO è impostato, la struttura UNWIND_INFO termina con tre UWORD. Questi UWORD rappresentano le informazioni di RUNTIME_FUNCTION per la funzione del disimpegnamento concatenato.

struttura UNWIND_CODE

La matrice di codice di rimozione viene usata per registrare la sequenza di operazioni nel prologo che influiscono sui registri non volatile e su RSP. Ogni elemento di codice ha questo formato:

Dimensione Valore
UBYTE Offset nel prologo
UBYTE: 4 Codice dell'operazione di disimballaggio
UBYTE: 4 Informazioni sull'operazione

La matrice viene ordinata in base all'ordine decrescente dell'offset nel prologo.

Offset nel Prolog

Offset (dall'inizio del prologo) relativo alla fine dell'istruzione che esegue questa operazione, più 1 (cioè l'offset dell'inizio dell'istruzione successiva).

Codice dell'operazione di disimballaggio

Nota: alcuni codici di operazione richiedono un offset senza segno a un valore nello stack frame locale. Questo offset è dall'inizio, ovvero l'indirizzo più basso dell'allocazione dello stack fisso. Se il campo Frame Register nella UNWIND_INFO è zero, questo offset proviene dall'RSP. Se il campo registro frame è diverso da zero, questo scostamento è a partire da dove si trovava RSP quando è stato stabilito il registro FP. È uguale al registro FP meno l'offset del registro FP (16 * offset del registro frame scalato nella UNWIND_INFO). Se viene utilizzato un registro FP, qualsiasi codice di unwind che accetta un offset deve essere utilizzato solo dopo che il registro FP viene stabilito nel prologo.

Per tutti gli opcode tranne UWOP_SAVE_XMM128 e UWOP_SAVE_XMM128_FAR, l'offset è sempre un multiplo di 8, perché tutti i valori dello stack di interesse vengono archiviati su limiti a 8 byte (lo stack stesso è sempre allineato a 16 byte). Per i codici di operazione che accettano un offset breve (inferiore a 512K), l'USHORT finale nei nodi per questo codice contiene l'offset diviso per 8. Per i codici operativi che accettano un offset lungo (512K <= offset < 4GB), gli ultimi due nodi USHORT per questo codice mantengono l'offset (in formato little-endian).

Per l'opcode UWOP_SAVE_XMM128 e UWOP_SAVE_XMM128_FAR, l'offset è sempre un multiplo di 16, poiché tutte le operazioni XMM a 128 bit devono avvenire in memoria allineata a 16 byte. Pertanto, viene usato un fattore di scala pari a 16 per UWOP_SAVE_XMM128, consentendo offset inferiori a 1M.

Il codice dell'operazione di rimozione è uno di questi valori:

  • UWOP_PUSH_NONVOL (0) 1 nodo

    Spingere un registro intero non volatile, decrementando RSP di 8. L'informazione operativa è il numero del registro. A causa dei vincoli sugli epiloghi, UWOP_PUSH_NONVOL i codici di disimpegno devono apparire per primi nel prologo e di conseguenza, ultimi nell'array dei codici di disimpegno. Questo ordinamento relativo si applica a tutti gli altri codici di unwind ad eccezione di UWOP_PUSH_MACHFRAME.

  • UWOP_ALLOC_LARGE (1) 2 o 3 nodi

    Allocare un'area di grandi dimensioni nello stack. Sono disponibili due moduli. Se le informazioni sull'operazione sono uguali a 0, la dimensione dell'allocazione divisa per 8 viene registrata nello slot successivo, consentendo un'allocazione fino a 512K - 8. Se le informazioni sull'operazione sono uguali a 1, le dimensioni non ridimensionate dell'allocazione vengono registrate nei due slot successivi in formato little-endian, consentendo allocazioni fino a 4 GB - 8.

  • UWOP_ALLOC_SMALL (2) 1 nodo

    Allocare un'area di piccole dimensioni nello stack. La dimensione dell'allocazione è il campo informazioni sull'operazione * 8 + 8, consentendo allocazioni da 8 a 128 byte.

    Il codice di rimozione per un'allocazione dello stack deve usare sempre la codifica più breve possibile:

    Dimensione di allocazione Codice di rimozione
    Da 8 a 128 byte UWOP_ALLOC_SMALL
    Da 136 a 512 KB - 8 byte UWOP_ALLOC_LARGE, informazioni sull'operazione = 0
    Da 512 KB a 4 GB-8 byte UWOP_ALLOC_LARGE, informazioni sull'operazione = 1
  • UWOP_SET_FPREG (3) 1 nodo

    Stabilire il registro puntatore di frame impostando il registro su un offset del RSP corrente. L'offset è uguale al campo Offset del frame register (ridimensionato) nello UNWIND_INFO * 16, consentendo offset da 0 a 240. L'uso di un offset consente di stabilire un puntatore al frame che punta al centro dell'allocazione dello stack fisso, migliorando la densità del codice permettendo che più accessi utilizzino formati brevi delle istruzioni. Il campo informazioni sull'operazione è riservato e non deve essere usato.

  • UWOP_SAVE_NONVOL (4) 2 nodi

    Salvare un registro intero non volatile nello stack usando un MOV anziché un'istruzione PUSH. Questo codice viene utilizzato principalmente per l'incapsulamento, in cui un registro non volatile viene salvato nello stack in una posizione allocata in precedenza. L'informazione operativa è il numero del registro. L'offset dello stack con scala di 8 viene registrato nello slot successivo del codice operazione di unwind, come descritto nella nota precedente.

  • UWOP_SAVE_NONVOL_FAR (5) 3 nodi

    Salvare un registro intero non volatile nello stack con un offset lungo, usando un MOV anziché un'operazione PUSH. Questo codice è principalmente utilizzato per l'incapsulamento, dove un registro non volatile viene salvato nello stack in una posizione precedentemente allocata. Le informazioni operative sono il numero del registro. L'offset dello stack non ridimensionato viene registrato nei due slot di codice dell'operazione di rimozione successivi, come descritto nella nota precedente.

  • UWOP_SAVE_XMM128 (8) 2 nodi

    Salvare tutti i 128 bit di un registro XMM non volatile nello stack. Le informazioni operative sono il numero del registro. L'offset dello stack moltiplicato per 16 viene registrato nello slot successivo.

  • UWOP_SAVE_XMM128_FAR (9) 3 nodi

    Salvare tutti i 128 bit di un registro XMM non volatile nello stack con un offset lungo. Le informazioni operative sono il numero del registro. L'offset dello stack non ridimensionato viene registrato nei due slot successivi.

  • UWOP_PUSH_MACHFRAME (10) 1 nodo

    Spingere un telaio della macchina. Questo codice di rimozione viene usato per registrare l'effetto di un'interruzione hardware o di un'eccezione. Sono disponibili due moduli. Se le informazioni sull'operazione sono pari a 0, uno di questi fotogrammi è stato inserito nello stack:

    Posizione Valore
    RSP+32 Server del sito (SS)
    RSP+24 RSP precedente
    RSP+16 EFLAGS
    RSP+8 Informatica
    RSP RIP

    Se l'informazione sull'operazione è uguale a 1, è stato eseguito il push di uno di questi fotogrammi.

    Posizione Valore
    RSP+40 SS
    RSP+32 RSP precedente
    RSP+24 EFLAGS
    RSP+16 Informatica
    RSP+8 ELABORATORE DI IMMAGINI RASTER
    RSP Codice errore

    Questo codice di rimozione viene sempre visualizzato in un prologo fittizio, che non viene mai eseguito, ma viene visualizzato prima del punto di ingresso reale di una routine di interrupt ed esiste solo per fornire un luogo in cui simulare il push di un frame di macchina. UWOP_PUSH_MACHFRAME registra la simulazione, che indica che il computer ha eseguito concettualmente questa operazione:

    1. Estrai l'indirizzo di ritorno RIP dalla cima dello stack in Temp

    2. Push SS

    3. Pushare il vecchio RSP

    4. Push EFLAGS (mette EFLAGS nello stack)

    5. Premere CS

    6. Push temp

    7. Push Error Code (se op info è uguale a 1)

    L'operazione simulata UWOP_PUSH_MACHFRAME decrementa RSP di 40 (informazioni operative uguali a 0) o 48 (informazioni operative uguali a 1).

Informazioni sull'operazione

Il significato dei bit delle informazioni sull'operazione dipende dal codice dell'operazione. Per codificare un registro generico (integer), viene usato questo mapping:

Pezzo Registrazione
0 RAX
1 RCX
2 RDX
3 RBX
4 RSP
5 RBP
6 RSI
7 RDI
da 8 a 15 Da R8 a R15

Strutture concatenate di informazioni di disimballaggio

Se il flag UNW_FLAG_CHAININFO è impostato, una struttura di informazioni di disimpegno è una struttura secondaria, e il campo dell'indirizzo del gestore delle eccezioni condiviso/informazioni concatenate contiene le informazioni di disimpegno primarie. Questo codice di esempio recupera le informazioni di disimballaggio primarie, presupponendo che unwindInfo sia la struttura con il flag UNW_FLAG_CHAININFO attivato.

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

Le informazioni concatenate sono utili in due situazioni. In primo luogo, può essere usato per segmenti di codice non contigui. Usando le informazioni concatenate, è possibile ridurre le dimensioni delle informazioni di smistamento necessarie, perché non è necessario duplicare la matrice dei codici di smistamento dalle informazioni di smistamento primarie.

È anche possibile usare le informazioni concatenati per raggruppare i salvataggi dei registri volatili. Il compilatore potrebbe ritardare il salvataggio di alcuni registri volatili fino a che non si trovi al di fuori del prologo della funzione. È possibile registrarli con le informazioni di disimballo primarie per la parte della funzione prima del codice raggruppato, quindi configurare le informazioni concatenate con una dimensione del prologo diversa da zero, in cui i codici di disimballo nelle informazioni concatenate riflettono i salvataggi dei registri non volatili. In tal caso, i codici di rimozione sono tutte istanze di UWOP_SAVE_NONVOL. Un raggruppamento che salva i registri non volatile usando un push o modifica il registro RSP tramite un'allocazione aggiuntiva dello stack fisso non è supportato.

Un elemento UNWIND_INFO con UNW_FLAG_CHAININFO impostato può contenere una voce di RUNTIME_FUNCTION e il cui elemento UNWIND_INFO ha anch'esso UNW_FLAG_CHAININFO impostato, talvolta denominato shrink-wrapping multiplo. Alla fine, i puntatori alle informazioni di annullamento concatenati arrivano a un elemento UNWIND_INFO che ha UNW_FLAG_CHAININFO azzerato. L'elemento principale UNWIND_INFO è questo, che punta al punto di ingresso effettivo della procedura.

Procedura di rimozione

L'array di codice di srotolamento viene ordinato in ordine decrescente. Quando si verifica un'eccezione, il contesto completo viene archiviato dal sistema operativo in un record di contesto. Viene quindi richiamata la logica di invio dell'eccezione, che esegue ripetutamente questi passaggi per trovare un gestore eccezioni:

  1. Usare l'RIP corrente archiviato nel record di contesto per cercare una voce della tabella RUNTIME_FUNCTION che descrive la funzione corrente (o parte della funzione, per le voci di UNWIND_INFO concatenate).

  2. Se non viene trovata alcuna voce di tabella della funzione, si trova in una funzione foglio e RSP punta direttamente al puntatore di ritorno. Il puntatore di ritorno in [RSP] viene archiviato nel contesto aggiornato, l'RSP simulato viene incrementato di 8 e il passaggio 1 viene ripetuto.

  3. Se viene trovata una voce di tabella delle funzioni, RIP può trovarsi all'interno di tre aree: a) in un epilogo, b) nel prologo, o c) nel codice che può essere coperto da un gestore di eccezioni.

    • Case a) Se il RIP si trova all'interno di un epilogo, il controllo sta uscendo dalla funzione, non può esistere alcun gestore di eccezioni associato a questa eccezione per questa funzione, e gli effetti dell'epilogo devono essere continuati per calcolare il contesto della funzione chiamante. Per determinare se il RIP si trova all'interno di un epilogo, viene esaminato il flusso di codice dal RIP in avanti. Se tale flusso di codice può essere associato alla parte finale di un epilogo legittimo, si trova in un epilogo e la parte rimanente dell'epilogo viene simulata, con il record di contesto aggiornato durante l'elaborazione di ogni istruzione. Dopo questa elaborazione, il passaggio 1 viene ripetuto.

    • Caso b) Se il RIP si trova all'interno del prologo, il controllo non è entrato nella funzione, non può essere associato alcun gestore di eccezioni a questa eccezione per la funzione, e gli effetti del prologo devono essere annullati per determinare il contesto della funzione chiamante. Il RIP si trova all'interno del prologo se la distanza dall'inizio della funzione al RIP è minore o uguale alla dimensione del prologo codificata nelle informazioni di disattivazione. Gli effetti del prologo vengono annullati scorrendo in avanti la matrice dei codici di disallocazione fino a trovare la prima voce con un offset minore o uguale all'offset del RIP dall'inizio della funzione, quindi annullando l'effetto di tutti gli elementi rimanenti nella matrice dei codici di disallocazione. Il passaggio 1 viene quindi ripetuto.

    • Caso c) Se il rip non si trova all'interno di un prologo o di un epilogo e la funzione ha un gestore eccezioni (UNW_FLAG_EHANDLER è impostato), viene chiamato il gestore specifico del linguaggio. Il gestore analizza i dati e chiama le funzioni di filtro in base alle esigenze. Il gestore specifico della lingua può restituire che l'eccezione è stata gestita o che la ricerca deve essere continuata. Può anche avviare una rimozione direttamente.

  4. Se il gestore specifico del linguaggio restituisce uno stato gestito, l'esecuzione continua usando il record di contesto originale.

  5. Se non è presente alcun gestore specifico per la lingua o il gestore restituisce uno stato di "continua la ricerca", il record di contesto deve essere ripristinato allo stato del chiamante. Questa operazione viene eseguita elaborando tutti gli elementi della matrice di codice di rimozione, annullando l'effetto di ognuno. Il passaggio 1 viene quindi ripetuto.

Quando sono coinvolte informazioni di eliminazione del vincolo concatenate, questi passaggi di base vengono comunque seguiti. L'unica differenza è che, mentre si percorre la matrice del codice di risoluzione per rimuovere gli effetti di un prologo, una volta raggiunta la fine della matrice, viene quindi collegata alle informazioni di risoluzione del genitore e l'intera matrice di codice di risoluzione trovata lì è percorsa. Questo collegamento continua fino a quando non si arriva a informazioni di unwind senza il flag UNW_CHAINED_INFO e quindi completa il percorso dell'array dei codici di unwind.

Il set più piccolo di dati di unwind è di 8 byte. Ciò rappresenterebbe una funzione che ha allocato solo 128 byte di stack o meno, ed eventualmente ha salvato un registro non-volatile. È anche la dimensione di una struttura di informazioni di rimozione concatenati per un prologo di lunghezza zero senza codici di rimozione.

Gestore specifico del linguaggio

L'indirizzo relativo del gestore specifico della lingua è presente nel UNWIND_INFO ogni volta che vengono impostati flag UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER. Come descritto nella sezione precedente, il gestore specifico della lingua viene chiamato come parte della ricerca di un gestore di eccezioni o come parte di un processo di unwind. Ha questo prototipo:

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

ExceptionRecord fornisce un puntatore a un record di eccezione, che ha la definizione Win64 standard.

EstablisherFrame è l'indirizzo della base dell'allocazione dello stack fisso per questa funzione.

ContextRecord punta al contesto dell'eccezione al momento in cui è stata generata (nel caso del gestore delle eccezioni) o al contesto corrente di "disimpegno" (nel caso del gestore di terminazione).

DispatcherContext punta al contesto del dispatcher per questa funzione. Ha questa definizione:

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 è il valore di RIP all'interno di questa funzione. Questo valore è un indirizzo di eccezione o l'indirizzo in corrispondenza del quale il controllo ha lasciato la funzione di definizione. Il RIP viene usato per determinare se il controllo si trova all'interno di un costrutto sorvegliato all'interno di questa funzione, ad esempio un __try blocco per __try/__except o __try/__finally.

ImageBase è la base dell'immagine (indirizzo di caricamento) del modulo contenente questa funzione, da aggiungere agli offset a 32 bit usati nell'entrata della funzione e nelle informazioni di ripristino al fine di registrare gli indirizzi relativi.

FunctionEntry fornisce un puntatore alla voce della funzione RUNTIME_FUNCTION che contiene la funzione e le informazioni di svolgimento con indirizzi relativi all'immagine di base per questa funzione.

EstablisherFrame è l'indirizzo della base dell'allocazione dello stack fisso per questa funzione.

TargetIp Fornisce un indirizzo di istruzione facoltativo che specifica l'indirizzo per la continuazione dell'unwind. Questo indirizzo viene ignorato se EstablisherFrame non è specificato.

ContextRecord punta al contesto dell'eccezione, per l'uso da parte del codice di distribuzione/rimozione delle eccezioni di sistema.

LanguageHandler punta alla routine del gestore specifica della lingua chiamata.

HandlerData punta ai dati del gestore specifici della lingua per questa funzione.

Assistenti di riavvolgimento per MASM

Per scrivere routine di assembly appropriate, è disponibile un set di pseudo-operazioni che possono essere usate in parallelo con le istruzioni di assembly effettive per creare i file con estensione pdata e xdata appropriati. È inoltre disponibile un set di macro che forniscono un uso semplificato delle pseudo-operazioni per gli usi più comuni.

Pseudo-operazioni grezze

Pseudo operazione Descrizione
PROC FRAME [:ehandler] Fa sì che MASM generi una voce di tabella delle funzioni in .pdata e informazioni di unwind in .xdata per il comportamento di gestione delle eccezioni strutturate di una funzione. Se ehandler è presente, questa proc viene immessa in .xdata come gestore specifico della lingua.

Quando viene usato l'attributo FRAME, deve essere seguito dalla direttiva .ENDPROLOG. Se la funzione è una funzione foglia (come definito nei tipi di funzione) l'attributo FRAME non è necessario, come nel resto di queste pseudo-operazioni.
.PUSHREG register Genera un elemento di codice di disimballaggio UWOP_PUSH_NONVOL per il numero di registro specificato utilizzando l'offset attuale nel prologo.

Usarlo solo con registri interi nonvolatili. Per i push di registri volatili, usare .ALLOCSTACK 8, invece
.SETFRAME registro, offset Compila il campo del registro frame e l'offset nelle informazioni di unwind utilizzando il registro e l'offset specificati. L'offset deve essere un multiplo di 16 e minore o uguale a 240. Questa direttiva genera anche un'entry di codice di unwind UWOP_SET_FPREG per il registro specificato utilizzando l'offset corrente del prologo.
.ALLOCSTACK dimensione Genera un UWOP_ALLOC_SMALL o un UWOP_ALLOC_LARGE con le dimensioni specificate per l'offset corrente nel prologo.

L'operando di dimensioni deve essere un multiplo di 8.
.SAVEREG register, offset Genera un UWOP_SAVE_NONVOL o una voce di codice di rimozione UWOP_SAVE_NONVOL_FAR per il registro e l'offset specificati utilizzando l'offset del prologo corrente. MASM sceglie la codifica più efficiente.

offset deve essere positivo e un multiplo di 8. offset è relativo alla base del frame della procedura, di solito in RSP o, se si usa un puntatore a frame, il puntatore del frame non scalato.
SAVEXMM128 register, offset Genera un UWOP_SAVE_XMM128 o una voce di codice di unwind UWOP_SAVE_XMM128_FAR per il registro XMM e l'offset specificati, sfruttando l'offset del prologo corrente. MASM sceglie la codifica più efficiente.

offset deve essere positivo e un multiplo di 16. offset è relativo alla base del frame della routine, che è in genere in RSP o, se si usa un puntatore a frame, il puntatore frame non ridimensionato.
.PUSHFRAME [codice] Genera una voce di codice di rimozione UWOP_PUSH_MACHFRAME. Se il codice facoltativo è specificato, alla voce del codice di ripristino viene assegnato un modificatore pari a 1. In caso contrario, il modificatore è 0.
. ENDPROLOG Segnala la fine delle dichiarazioni del prologo. Deve verificarsi nei primi 255 byte della funzione.

Di seguito è riportato un prologo di funzione di esempio con un uso appropriato della maggior parte dei codici operativo:

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

Per ulteriori informazioni sull'esempio di epilogo, vedere il Codice epilogo nella sezione prologo ed epilogo x64.

Macro MASM

Per semplificare l'uso delle pseudo-operazioni raw, è presente un set di macro, definito in ksamd64.inc, che può essere usato per creare prologhi e epiloghi di procedure tipici.

Macro Descrizione
alloc_stack(n) Allocare un frame di stack di n byte (usando sub rsp, n) ed emette le informazioni di recupero appropriate (.allocstack n)
save_reg reg, loc Salva il registro reg nonvolatile nello stack al offset di RSP loc e genera le informazioni di disaccoppiamento appropriate. (.savereg reg, loc)
push_reg reg Inserisce un reg registro non volatile nello stack e genera le informazioni di rimozione appropriate. (.pushreg reg)
rex_push_reg reg Salva un registro non volatile nello stack usando un push a 2 byte e genera le informazioni di rimozione appropriate (.pushreg reg). Utilizzare questa macro se il push è la prima istruzione nella funzione per assicurarsi che la funzione sia patchabile a caldo.
save_xmm128 reg, loc Salva un registro XMM non volatile nello stack all'offset RSP loc e genera le informazioni di rilascio appropriate (.savexmm128 reg, loc)
set_frame reg, offset Imposta il registro frame reg come RSP + offset (usando un movo un lea) e genera le informazioni di rimozione appropriate (.set_frame reg, offset)
push_eflags Inserisce gli eflags con un'istruzione pushfq e genera le informazioni di disimpegno appropriate (.alloc_stack 8)

Di seguito è riportato un prologo di funzione di esempio con l'uso appropriato delle macro:

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

Scomporre le definizioni dei dati in C

Ecco una descrizione in C dei dati di unwind:

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))

Vedi anche

Convenzioni del software x64