Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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_EHANDLERLa funzione ha un gestore eccezioni che deve essere chiamato quando si cercano funzioni che devono esaminare le eccezioni. UNW_FLAG_UHANDLERLa funzione ha un gestore di terminazione che deve essere chiamato durante la rimozione di un'eccezione. UNW_FLAG_CHAININFOQuesta 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 nodoSpingere un registro intero non volatile, decrementando RSP di 8. L'informazione operativa è il numero del registro. A causa dei vincoli sugli epiloghi,
UWOP_PUSH_NONVOLi 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 diUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 o 3 nodiAllocare 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 nodoAllocare 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_SMALLDa 136 a 512 KB - 8 byte UWOP_ALLOC_LARGE, informazioni sull'operazione = 0Da 512 KB a 4 GB-8 byte UWOP_ALLOC_LARGE, informazioni sull'operazione = 1UWOP_SET_FPREG(3) 1 nodoStabilire 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 nodiSalvare 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 nodiSalvare 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 nodiSalvare 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 nodiSalvare 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 nodoSpingere 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_MACHFRAMEregistra la simulazione, che indica che il computer ha eseguito concettualmente questa operazione:Estrai l'indirizzo di ritorno RIP dalla cima dello stack in Temp
Push SS
Pushare il vecchio RSP
Push EFLAGS (mette EFLAGS nello stack)
Premere CS
Push temp
Push Error Code (se op info è uguale a 1)
L'operazione simulata
UWOP_PUSH_MACHFRAMEdecrementa 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:
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).
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.
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.
Se il gestore specifico del linguaggio restituisce uno stato gestito, l'esecuzione continua usando il record di contesto originale.
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))