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 stack unwinding per la gestione delle eccezioni e il supporto del debug

Per ripristinare i registri non volatili quando viene gestita un'eccezione, le funzioni non-foglia vengono annotate con dati statici. Questi dati, comunemente indicati come "informazioni di unwinding della funzione", descrivono come eseguire correttamente l'unwinding della funzione in corrispondenza di un'istruzione arbitraria. Questi dati vengono archiviati come dati pdata o procedure, che a loro volta fanno riferimento a xdata, i dati di gestione delle eccezioni.

Le informazioni sulla rimozione delle funzioni sono costituite da diverse strutture di dati, descritte di seguito.

Per informazioni di unwind che supportano Intel APX (Advanced Performance Extensions), consultare la specifica di anteprima Unwind V3.

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 RUNTIME_FUNCTION struttura deve essere DWORD allineata 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 collocate 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 risultano inaffidabili.

struttura UNWIND_INFO

La struttura di informazioni sui dati di unwind registra gli effetti che una funzione ha 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 informazioni di 'unwind'

La UNWIND_INFO struttura deve essere DWORD allineata 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 dispone di un gestore eccezioni che il sistema operativo chiama per esaminare lo stato dell'eccezione e potenzialmente gestirlo. Costrutti del linguaggio come la clausola C __try consentono di registrare un gestore di questo tipo.
    UNW_FLAG_UHANDLER La funzione ha un handler di terminazione che viene chiamato dal sistema operativo durante lo stack unwinding. Questo gestore potrebbe rilasciare le risorse assegnate dalla funzione in codice a prova di eccezioni. Le caratteristiche del linguaggio, come ad esempio i distruttori locali di oggetti C++ e le clausole __finally del C, registrano tale gestore di terminazione.
    UNW_FLAG_CHAININFO Questa struttura di informazioni di unwind non è quella primaria per la procedura. Invece, la voce delle informazioni di unwind concatenate è il contenuto di una precedente voce RUNTIME_FUNCTION. Per informazioni, vedere Strutture di informazioni sull'unwind concatenato. Se questo flag è impostato, i UNW_FLAG_EHANDLER flag 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 unwind, ad esempio UWOP_SAVE_NONVOL, richiedono più di una posizione nell'array.

  • Registro frame

    Se diverso da zero, la funzione usa un puntatore di frame (FP) e questo campo indica il numero del registro non volatile usato come puntatore di frame, utilizzando la stessa codifica del campo delle informazioni sull'operazione dei nodi UNWIND_CODE.

  • Offset del registro frame (ridimensionato)

    Questo campo è un offset scalato tra il RSP valore del registro e il valore del registro frame pointer (FP) selezionato. Il registro FP selezionato è impostato su RSP + 16 * questo numero, il che significa che è possibile usare offset da 0 a 240. Questo offset punta il registro FP al centro dell'allocazione dello stack locale per i frame di stack dinamici, in modo da ottenere 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 relativa al codice delle operazioni di rimozione per i significati dei singoli elementi. Per mantenere il corretto allineamento dei dati, questo array contiene sempre un numero pari di elementi e l'elemento finale potrebbe non essere utilizzato. 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 uno dei flag UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER è impostato.

  • 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 UNWIND_INFO struttura termina con tre UWORDs. Questi UWORD rappresentano le informazioni RUNTIME_FUNCTION per la funzione dell'unwind concatenato.

struttura UNWIND_CODE

Usa l'array di codici di unwind per registrare la sequenza di operazioni nel prologo che modificano i registri non volatili e 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 prologo

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

Alcuni codici operativi richiedono un offset senza segno relativo a un valore nello stack frame locale. Questo offset è dall'inizio, ovvero l'indirizzo più basso dell'allocazione dello stack fisso. Se il campo Registro frame in UNWIND_INFO è zero, questo offset è relativo a RSP. Se il campo Registro di frame è diverso da zero, questo offset è calcolato a partire dalla posizione in cui si trovava RSP quando è stato impostato il registro FP. È uguale al registro FP meno l'offset del registro FP (16 * l'offset scalato del registro del frame nel 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 operativi che richiedono un offset breve (inferiore a 512K), l'elemento finale USHORT nei nodi di questo codice contiene l'offset diviso per 8. Per i codici operativi che prevedono un offset lungo (512K <= offset < 4GB), gli ultimi due USHORT nodi di questo codice contengono l'offset (in formato little-endian).

Per gli 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 su 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

    Eseguire il push di 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

    Impostare il registro frame pointer impostando il registro a un determinato offset rispetto al RSP corrente. L'offset è uguale al campo offset del registro frame (scalato) in UNWIND_INFO * 16, consentendo valori di 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 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 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 non volatile XMM nello stack. L'informazione operativa è 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 non volatile XMM nello stack con un offset lungo. L'informazione operativa è 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 unwinding descrive l'effetto di un'interruzione hardware o di un'eccezione. Ha due forme. Il valore 0 indica che l'hardware ha eseguito il push di un frame, ad esempio questo nello stack:

    Posizione Valore
    RSP+32 SS
    RSP+24 Precedente RSP
    RSP+16 EFLAGS
    RSP+8 CS
    RSP RIP

    Il valore 1 indica che l'hardware ha eseguito il push di un frame come questo nello stack:

    Posizione Valore
    RSP+40 SS
    RSP+32 Precedente RSP
    RSP+24 EFLAGS
    RSP+16 CS
    RSP+8 RIP
    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. Estrae RIP l'indirizzo di ritorno dalla cima dello stack in Temp

    2. Spingere SS

    3. Premi vecchio RSP

    4. Spingere EFLAGS

    5. Spingere CS

    6. Push temp

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

    L'operazione simulata UWOP_PUSH_MACHFRAME decrementa RSP di 40 (se le informazioni operative sono uguali a 0) o 48 (se le informazioni operative sono 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 R8 a R15

Strutture concatenate di informazioni di disimballaggio

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

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. Utilizzando le informazioni concatenate, è possibile ridurre le dimensioni delle informazioni di unwind necessarie, perché non è necessario duplicare l'array dei codici di unwind dalle informazioni di unwind primarie.

È anche possibile usare le informazioni concatenati per raggruppare i salvataggi dei registri volatili. Il compilatore potrebbe ritardare il salvataggio di alcuni registri volatili finché non esce dal prologo di ingresso della funzione. È possibile descriverli usando informazioni di unwind primarie per la parte della funzione che precede il codice raggruppato, quindi configurando informazioni concatenate con una dimensione del prologo non pari a zero, in cui i codici di unwind nelle informazioni concatenate riflettono i salvataggi dei registri non volatili. In questo caso, i codici unwind sono tutti istanze di UWOP_SAVE_NONVOL. Non è supportato un raggruppamento che salva i registri non volatili usando un PUSH o che modifica il registro RSP usando un'ulteriore allocazione fissa dello stack.

Un elemento UNWIND_INFO con UNW_FLAG_CHAININFO impostato può contenere una voce RUNTIME_FUNCTION il cui elemento UNWIND_INFO ha impostato anche UNW_FLAG_CHAININFO, talvolta chiamato avvolgimento multiplo. Alla fine, i puntatori concatenati alle informazioni di srotolamento puntano a un elemento UNWIND_INFO con UNW_FLAG_CHAININFO azzerato. Questo elemento è l'elemento principale UNWIND_INFO, che rimanda 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 sistema operativo archivia il contesto completo 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. Usa il valore corrente RIP memorizzato nel record di contesto per cercare una voce della tabella RUNTIME_FUNCTION che descrive la funzione corrente (o una parte della funzione, per le voci UNWIND_INFO concatenate).

  2. Se la ricerca non trova una voce nella tabella delle funzioni, si presume che il codice faccia parte di una funzione leaf e RSP faccia direttamente riferimento al puntatore di ritorno. Il puntatore di ritorno in [RSP] viene memorizzato nel contesto aggiornato, il RSP simulato viene incrementato di 8 e il passaggio 1 viene ripetuto.

  3. Se la ricerca trova 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 potrebbe essere coperto da un gestore eccezioni.

    • Caso a) Se il RIP si trova in un epilogo, il controllo sta uscendo dalla funzione. Non è possibile associare alcun gestore eccezioni a questa eccezione per questa funzione. Gli effetti dell'epilogo devono continuare a determinare il contesto della funzione chiamante. Per determinare se l'oggetto RIP si trova all'interno di un epilogo, viene esaminato il flusso di codice da RIP poi. Se tale flusso di codice corrisponde alla parte finale di un epilogo legittimo, si trova in un epilogo. 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 RIP si trova nel prologo, il controllo non è ancora entrato nella funzione. Non è possibile associare alcun gestore eccezioni a questa eccezione per questa funzione. Gli effetti del prologo devono essere annullati per calcolare il contesto della funzione chiamante. RIP si trova nel prologo se la distanza dall'inizio della funzione al RIP è minore o uguale alla dimensione del prologo codificata nelle informazioni di unwind. Il componente di rimozione analizza in avanti la matrice dei codici di rimozione per la prima voce con un offset minore o uguale all'offset di RIP dall'inizio della funzione, quindi annulla l'effetto di tutti gli elementi rimanenti nella matrice di codice di rimozione. Il passaggio 1 viene quindi ripetuto.
    • Caso c) Se RIP non si trova all'interno di un prologo o di un epilogo e la funzione ha un gestore di 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. L'unwinder annulla l'effetto di ciascun elemento nell'array dei codici di unwind. 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 scorre l'array del codice di unwind per annullare gli effetti di un prologo, una volta che il processo raggiunge la fine dell'array, si collega alle informazioni di unwind del livello padre e scorre l'intero array del codice di unwind presente lì. Questo concatenamento continua fino a raggiungere informazioni di unwind senza il flag UNW_CHAINED_INFO, quindi termina di scorrere il relativo array di codice di unwind.

Il set più piccolo di dati di unwind è di 8 byte. Questo insieme rappresenta una funzione che ha allocato soltanto 128 byte di stack o meno e potrebbe aver 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

La UNWIND_INFO struttura fornisce l'indirizzo relativo del gestore specifico della lingua quando vengono impostati flag UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER . Come descritto nella sezione precedente, la ricerca di un gestore di eccezioni o il processo di rimozione chiama il gestore specifico della lingua. Il gestore usa 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. Viene RIP utilizzato 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. Gli offset a 32 bit usati nell'entry di funzione e nelle informazioni di unwinding devono essere aggiunti a ImageBase per ottenere l'indirizzo finale.

FunctionEntry fornisce un puntatore alla voce di funzione RUNTIME_FUNCTION che contiene gli indirizzi relativi all'immagine di base della funzione e delle informazioni di unwind 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 a cui proseguire l'unwinding. 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, usare un set di pseudo-operazioni insieme alle istruzioni di assembly effettive. Queste pseudo-operazioni creano i .pdata e .xdata appropriati. Usare inoltre un set di macro che semplificano l'uso di queste 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 di funzione in .pdata e le informazioni di rimozione in .xdata per il comportamento di rimozione delle eccezioni strutturate di una funzione. Se ehandler è presente, questa proc viene immessa in .xdata come gestore specifico della lingua.

Quando si utilizza l'attributo FRAME, farlo seguire 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 registro Genera una UWOP_PUSH_NONVOL voce di codice unwind per il numero di registro specificato, utilizzando l'offset corrente nel prologo.

Usarlo solo con registri interi nonvolatili. Per le operazioni di push dei registri volatili, usare invece .ALLOCSTACK 8.
.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 una UWOP_SET_FPREG voce di codice di unwinding per il registro indicato utilizzando l'offset corrente del prologo.
.ALLOCSTACK dimensione Genera un UWOP_ALLOC_SMALL o un UWOP_ALLOC_LARGE della dimensione specificata per l'offset corrente nel prologo.

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

offset deve essere positivo e un multiplo di 8. offset è relativo alla base del frame della procedura, che si trova generalmente in RSP, oppure, se si usa un puntatore di frame, nel puntatore di frame non scalato.
SAVEXMM128 register, offset Genera una voce di codice di unwind UWOP_SAVE_XMM128 o UWOP_SAVE_XMM128_FAR per il registro e l'offset XMM specificati, utilizzando 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 procedura, che in genere si trova in RSP, oppure, se si utilizza un frame pointer, nel frame pointer non scalato.
.PUSHFRAME [codice] Genera una UWOP_PUSH_MACHFRAME voce di codice di rimozione. Se si specifica il codice facoltativo, la voce del codice di rimozione ottiene 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, utilizzare l'insieme di macro definite in ksamd64.inc. Queste macro consentono di creare prologhi e epiloghi di procedure tipici.

Macro Descrizione
alloc_stack(n) Alloca un stack frame di n byte (usando sub rsp, n) e genera le informazioni di rimozione appropriate (con estensione allocstack n)
save_reg reg, loc Salva il registro non volatile reg nello stack all'offset RSPloc e genera le informazioni di unwind appropriate (.savereg reg, loc)
push_reg reg Inserisce il registro non volatile reg sullo stack e genera le informazioni di unwind appropriate (.pushreg reg)
rex_push_reg reg Salva un registro non volatile nello stack tramite un'istruzione push di 2 byte e genera le informazioni di unwind 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 non volatile XMMreg nello stack all'offset RSPloc e genera le informazioni di unwind appropriate (.savexmm128 reg, loc)
set_frame reg, offset Imposta il registro del frame reg su RSP + offset (utilizzando un mov o un lea) e genera le informazioni di unwinding appropriate (.set_frame reg, offset)
push_eflags Inserisce gli eflag usando un'istruzione pushfq e genera le informazioni di rimozione appropriate (.alloc_stack 8)

Ecco 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