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:
Flag 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 nodoEseguire 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 eccezioneUWOP_PUSH_MACHFRAME
di .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:
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 = 0Da 512 KB a 4G-8 byte UWOP_ALLOC_LARGE
, informazioni sull'operazione = 1UWOP_SET_FPREG
(3) 1 nodoStabilire 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 nodiSalvare 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 nodiSalvare 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 nodiSalvare 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 nodiSalvare 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 nodoEseguire 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:
Ufficio 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:
Ufficio Valore RSP+40 Server del sito (SS) RSP+32 RSP precedente 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:Pop RIP return address from top of stack into Temp (Pop RIP return address from top of stack into Temp)
Push SS
Eseguire il push di RSP precedente
Push EFLAGS
Push CS
Push temp
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:
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).
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.
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.
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 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 SETFRAME, 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 inoltre una voce di codice di rimozione UWOP_SET_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 mov o 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))