Condividi tramite


Gestione delle eccezioni x64

Panoramica della gestione delle eccezioni strutturata e della gestione delle eccezioni C++ delle convenzioni di codifica e del comportamento in x64. Per informazioni generali sulla gestione delle eccezioni, vedere Gestione delle eccezioni in Visual C++.

Dati di rimozione per la gestione delle eccezioni, 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 rimozione

La struttura RUNTIME_FUNCTION deve essere 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 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.

struct UNWIND_INFO

La struttura delle informazioni sui dati di rimozione viene usata per registrare gli effetti di una funzione sul puntatore dello stack e in cui i registri non volatile vengono salvati nello stack:

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

(1) Gestore eccezioni

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

(2) Informazioni di rimozione concatenati

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

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

  • Versione

    Numero di versione dei dati di rimozione, attualmente 1.

  • Flag

    Sono attualmente definiti tre flag:

    Contrassegno 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 di informazioni di rimozione concatenati è il contenuto di una voce di RUNTIME_FUNCTION precedente. Per informazioni, vedere Strutture di informazioni sulla rimozione concatenati. 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 usa un puntatore a fotogrammi (FP) e questo campo è il numero del registro non volatile usato come puntatore al fotogramma, usando la stessa codifica per il campo informazioni sull'operazione dei nodi UNWIND_CODE.

  • Offset del registro frame (ridimensionato)

    Se il campo registro dei fotogrammi è diverso da zero, questo campo è l'offset scalato da RSP applicato al registro FP quando viene 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 tal caso, la matrice è maggiore di quella indicata dal campo conteggio dei codici di rimozione.

  • 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 del linguaggio

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

  • Informazioni di rimozione concatenati

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

struct 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 rimozione
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) della fine dell'istruzione che esegue questa operazione, più 1 (ovvero l'offset dell'inizio dell'istruzione successiva).

Codice dell'operazione di rimozione

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 Registro frame nella UNWIND_INFO è zero, questo offset proviene da RSP. Se il campo Registro frame è diverso da zero, questo offset è 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 rimozione che accetta un offset deve essere utilizzato solo dopo che il registro FP è stato 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 operazione che accettano un offset lungo (512K <= offset < 4GB), i due nodi USHORT finali per questo codice contengono l'offset (in formato little-endian).

Per gli UWOP_SAVE_XMM128 opcode e UWOP_SAVE_XMM128_FAR, l'offset è sempre un multiplo di 16, poiché tutte le operazioni XMM a 128 bit devono verificarsi 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

    Eseguire il push di un registro intero non volatile, decrementando RSP di 8. Le informazioni sull'operazione sono il numero del registro. A causa dei vincoli sugli epilogi, UWOP_PUSH_NONVOL i codici di rimozione devono essere visualizzati prima nel prologo e in modo corrispondente, ultimo nella matrice di codice di rimozione. Questo ordinamento relativo si applica a tutti gli altri codici di rimozione ad eccezione UWOP_PUSH_MACHFRAMEdi .

  • 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:

    Dimensioni 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 4G-8 byte UWOP_ALLOC_LARGE, informazioni sull'operazione = 1
  • UWOP_SET_FPREG (3) 1 nodo

    Stabilire il registro dei puntatori di fotogramma impostando il registro su un offset del provider di servizi di configurazione corrente. L'offset è uguale al campo Offset frame register (ridimensionato) nel UNWIND_INFO * 16, consentendo offset da 0 a 240. L'uso di un offset consente di stabilire un puntatore a fotogrammi che punta al centro dell'allocazione dello stack fisso, consentendo la densità del codice consentendo più accessi all'uso di moduli brevi di 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 usato principalmente per il wrapping della compattazione, in cui un registro non volatile viene salvato nello stack in una posizione allocata in precedenza. Le informazioni sull'operazione sono il numero del registro. L'offset dello stack scaled by-8 viene registrato nello slot di codice dell'operazione di rimozione successiva, 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 usato principalmente per il wrapping della compattazione, in cui un registro non volatile viene salvato nello stack in una posizione allocata in precedenza. Le informazioni sull'operazione 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 sull'operazione sono il numero del registro. L'offset dello stack scaled-by-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 sull'operazione sono il numero del registro. L'offset dello stack non ridimensionato viene registrato nei due slot successivi.

  • UWOP_PUSH_MACHFRAME (10) 1 nodo

    Eseguire il push di un telaio del computer. 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 uguali a 0, è stato eseguito il push di uno di questi fotogrammi nello stack:

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

    Se le informazioni sull'operazione sono uguali a 1, viene eseguito il push di uno di questi fotogrammi:

    Titolo Valore
    RSP+40 Server del sito (SS)
    RSP+32 RSP precedente
    RSP+24 EFLAGS
    RSP+16 CS
    RSP+8 RIP
    RSP Codice di 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. Pop RIP return address from top of stack into Temp (Pop RIP return address from top of stack into Temp)

    2. Push SS

    3. Eseguire il push di RSP precedente

    4. Push EFLAGS

    5. Push 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:

Bit 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 di informazioni di rimozione concatenati

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

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

Le informazioni concatenati sono utili in due situazioni. In primo luogo, può essere usato per segmenti di codice non contigui. Usando le informazioni concatenati, è possibile ridurre le dimensioni delle informazioni di rimozione necessarie, perché non è necessario duplicare la matrice dei codici di rimozione dalle informazioni di rimozione 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 quando non si trova all'esterno del prologo della voce della funzione. È possibile registrarli con le informazioni di rimozione primarie per la parte della funzione prima del codice raggruppato e quindi configurare le informazioni concatenati con una dimensione diversa da zero del prologo, in cui i codici di rimozione nelle informazioni concatenati riflettono i salvataggi dei registri non volatile. 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 il cui elemento UNWIND_INFO ha anche UNW_FLAG_CHAININFO impostato, talvolta denominato wrapping multiplo. Alla fine, i puntatori alle informazioni di rimozione concatenati arrivano a un elemento UNWIND_INFO che ha UNW_FLAG_CHAININFO cancellato. Questo elemento è l'elemento principale UNWIND_INFO, che punta al punto di ingresso effettivo della routine.

Procedura di rimozione

La matrice di codice di rimozione viene ordinata 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 il rip corrente archiviato nel record di contesto per cercare una voce di tabella RUNTIME_FUNCTION che descrive la funzione corrente (o parte della funzione, per le voci di UNWIND_INFO concatenati).

  2. Se non viene trovata alcuna voce di tabella della funzione, si trova in una funzione foglia e RSP punta direttamente al puntatore restituito. Il puntatore restituito in [RSP] viene archiviato nel contesto aggiornato, il provider di servizi di ripristino 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 eccezioni.

    • Case a) Se il rip si trova all'interno di un epilogo, il controllo lascia la funzione, non può esistere alcun gestore eccezioni associato a questa eccezione per questa funzione e gli effetti dell'epilogo devono essere continui a 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 poi. 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 ha immesso la funzione, non può essere associato alcun gestore eccezioni a questa eccezione per questa funzione e gli effetti del prologo devono essere annullati per calcolare 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 rimozione. Gli effetti del prologo vengono annullati analizzando in avanti la matrice dei codici di rimozione per 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 di codice di rimozione. 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 della lingua o il gestore restituisce uno stato di "continua ricerca", il record di contesto deve essere scollegato 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 rimozione concatenati, questi passaggi di base vengono comunque seguiti. L'unica differenza è che, mentre si cammina sulla matrice di codice di rimozione per rimuovere gli effetti di un prologo, una volta raggiunta la fine della matrice, viene quindi collegato alle informazioni di rimozione padre e l'intera matrice di codice di rimozione trovata è presente. Questo collegamento continua fino a quando non arrivano informazioni di rimozione senza il flag UNW_CHAINED_INFO e quindi completa l'esecuzione della matrice di codice di rimozione.

Il set più piccolo di dati di rimozione è di 8 byte. Ciò rappresenterebbe una funzione che allocato solo 128 byte di stack o minore, ed eventualmente 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 una rimozione. 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 l'eccezione (nel caso del gestore eccezioni) o al contesto corrente di "rimozione" (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__except/__try o .__try/__finally

ImageBase è la base dell'immagine (indirizzo di caricamento) del modulo contenente questa funzione, da aggiungere agli offset a 32 bit usati nella voce della funzione e informazioni di rimozione per registrare gli indirizzi relativi.

FunctionEntry fornisce un puntatore alla voce della funzione RUNTIME_FUNCTION che contiene la funzione e gli indirizzi relativi relativi all'immagine di base dell'immagine 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 di continuazione della rimozione. Questo indirizzo viene ignorato se l'argomento 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 del linguaggio specifica del linguaggio chiamata.

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

Helper di rimozione 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 non elaborate

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

Quando viene usato l'attributo FRAME, deve essere seguito da un oggetto . 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.
. Registro PUSHREG Genera una voce di codice di rimozione UWOP_PUSH_NONVOL per il numero di registro specificato utilizzando l'offset corrente nel prologo.

Usarlo solo con registri interi nonvolatili. Per i push di registri volatili, usare un oggetto . ALLOCSTACK 8, invece
. registro edizione Standard TFRAME, offset Compila il campo registro frame e l'offset nelle informazioni di rimozione 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 voce di codice di rimozione UWOP_edizione StandardT_FPREG per il registro specificato utilizzando l'offset del prologo corrente.
. Dimensioni ALLOCSTACK 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.
. Register SAVEREG, 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 routine, che è in genere in RSP o, se si usa un puntatore a frame, il puntatore frame non ridimensionato.
. SAVEXMM128 registro, offset Genera un UWOP_SAVE_XMM128 o una voce di codice di rimozione UWOP_SAVE_XMM128_FAR per il registro XMM e l'offset 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 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 viene specificato il codice facoltativo , alla voce del codice di rimozione 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 altre informazioni sull'esempio di epilogo, vedere Codice epilogo nel prologo e nell'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) 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 un reg registro non volatile nello stack in corrispondenza del loc offset RSP e genera le informazioni di rimozione 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 reg registro XMM non volatile nello stack in corrispondenza del loc offset RSP e genera le informazioni di rimozione 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 eflag con un'istruzione pushfq e genera le informazioni di rimozione 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

Definizioni di dati di rimozione in C

Ecco una descrizione C dei dati di rimozione:

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