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.
Ambito
Unwind Information V3 aggiunge il supporto a Intel APX (Advanced Performance Extensions). Offre inoltre una maggiore flessibilità per la generazione di codice consentita sia nei prologhi di funzione che negli epiloghi, consentendo ai compilatori di ottimizzare meglio le funzioni nel suo complesso.
Unwind V3 è necessario per il codice che supporta APX. Il codice non abilitato per APX deve comunque usare le informazioni di rimozione convenzionali (pre-V3).
Avvertimento
Unwind Information V3 è una specifica in anteprima. Esiste ancora un rischio di modifiche o omissioni che causano un'interruzione. Il codice prodotto in questa guida deve essere considerato codice di anteprima.
Terminologia
| Termine | Definizione |
|---|---|
| Frammento | Area contigua del codice del computer descritto da una singola RUNTIME_FUNCTION / UNWIND_INFO_V3 coppia. Una funzione può essere costituita da un frammento principale e da zero o più deframmentamenti concatenati. Per altre informazioni, vedere Strutture di informazioni di unwind concatenate. |
| WOD | Il descrittore dell'operazione di unwinding è una codifica compatta a lunghezza variabile di una singola operazione di unwinding (push, alloc, save e così via). |
| Pool WOD | Matrice di byte all'interno del payload che archivia tutti i WOD per il prologo e l'epilogo di un frammento. |
| IP Offset | Offset di byte senza segno di un'istruzione rispetto all'inizio del prologo o dell'epilogo. |
| Payload | La regione a lunghezza variabile immediatamente successiva all'intestazione UNWIND_INFO_V3 di 4 byte, la cui dimensione è espressa in PayloadWords parole di 16 bit. Contiene l'estensione del prologo di grandi dimensioni, gli offset IP del prologo, i descrittori dell'epilogo (con i relativi offset IP) e il pool WOD. |
| RVA | L'indirizzo virtuale relativo è l'offset dall'indirizzo di base dell'immagine in fase di caricamento. |
Layout generale del file oggetto
Unwind V3 riutilizza senza modificarle le convenzioni di sezione PE/COFF .pdata e .xdata esistenti:
.pdata - sorted array of IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY (12 bytes each)
.xdata - UNWIND_INFO_V3 structures referenced by .pdata entries
Ogni RUNTIME_FUNCTION voce è:
| Scarto | Dimensione | Campo |
|---|---|---|
| 0 | 4 |
BeginAddress - RVA dell'inizio del frammento |
| 4 | 4 |
EndAddress - RVA del primo byte successivo alla fine del frammento |
| 8 | 4 |
UnwindInfoAddress - RVA di UNWIND_INFO_V3 in .xdata |
Nessuna modifica da V1 o V2.
UNWIND_INFO_V3 Intestazione
Tutti i campi multi byte sono little-endian. Bit 0 è l'LSB di ogni byte.
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0 | [2:0] |
Version (= 3) |
3 bit |
| 0 | [7:3] | Flags |
5 bit |
| 1 | [7:0] | SizeOfProlog |
8 bit |
| 2 | [7:0] | PayloadWords |
8 bit |
| 3 | [4:0] | NumberOfOps |
5 bit |
| 3 | [7:5] | NumberOfEpilogs |
3 bit |
Semantica dei campi
| Campo | Descrizione |
|---|---|
Version |
Deve essere 3. |
Flags |
Stesse definizioni di flag di V1 o V2: UNW_FLAG_EHANDLER (0x01), UNW_FLAG_UHANDLER (0x02), UNW_FLAG_CHAININFO (0x04). Novità nella versione 3: UNW_FLAG_LARGE (0x08). Bit 4 riservato (zero). |
SizeOfProlog |
Offset di byte all'inizio della prima istruzione che non fa parte del prologo. Quando UNW_FLAG_LARGE è impostato, questo campo di 8 bit corrisponde al byte meno significativo di una dimensione del prologo di 16 bit. Per altre informazioni sul modulo a 16 bit, vedere UNW_FLAG_LARGE e UNWIND_INFO_LARGE_V3. |
PayloadWords |
Numero di parole a 16 bit del payload successive a questo header. Il payload contiene gli offset IP del prologo, tutti i descrittori di epilogo (compresi i relativi offset IP) e il pool WOD. Non include l'RVA del gestore di eccezioni né l'elemento concatenato RUNTIME_FUNCTION che potrebbe seguire. L'algoritmo per individuare i dati del gestore o della catena è uguale a V1 o V2: header + 4 + PayloadWords * 2, allineato a DWORD. |
NumberOfOps |
Numero di WOD nel prologo (0-31). Zero indica che non è previsto alcun prologo. Se una funzione richiede più di 31 operazioni del prologo, usare un sottoframmento. |
NumberOfEpilogs |
Conteggio dei descrittori EPILOG_INFO_V3 che seguono gli offset IP del prologo (0–7). Zero indica che in questo frammento non sono presenti epilogi. Se sono necessari più di 7, usare un sottoframmento. |
UNW_FLAG_LARGE e UNWIND_INFO_LARGE_V3
Quando UNW_FLAG_LARGE è impostato nell'intestazione Flags, il primo byte del payload è un'estensione a 1 byte UNWIND_INFO_LARGE_V3 :
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0 | [7:0] | SizeOfPrologHighByte |
8 bit |
SizeOfPrologHighByte viene combinato con UNWIND_INFO_V3.SizeOfProlog per formare una dimensione di prologo a 16 bit: SizeOfProlog16 = (SizeOfPrologHighByte << 8) | SizeOfProlog.
Inoltre, quando UNW_FLAG_LARGE è impostato:
- Le voci di offset IP prolog sono senza segno a 16 bit (2 byte ciascuno) anziché a 8 bit.
Il UNWIND_INFO_LARGE_V3 byte fa parte del payload ed è incluso in PayloadWords. La formula di offset dell'handler rimane invariata.
Questo flag è necessario solo per i prolog che superano i 255 byte.
Individuazione del gestore delle eccezioni / informazioni della catena
Identico a V1 o V2:
handler_offset = ALIGN_UP(sizeof(UNWIND_INFO_V3) + PayloadWords * 2, 4)
Se UNW_FLAG_EHANDLER o UNW_FLAG_UHANDLER è impostato, un'RVA a 4 byte del gestore si trova in handler_offset, seguita dai dati del gestore specifici del linguaggio.
Se UNW_FLAG_CHAININFO è impostato, un byte a 12 byte RUNTIME_FUNCTION si trova in handler_offset.
Layout del payload
Immediatamente dopo l'intestazione di 4 byte, il payload è organizzato nel seguente ordine (il tutto in formato packed, semantica #pragma pack(1)):
-
UNWIND_INFO_LARGE_V3- 1 byte, presente solo seUNW_FLAG_LARGEè impostato. - Offset IP del prologo -
NumberOfOpsbyte (o parole di 16 bit seUNW_FLAG_LARGE). - Per ogni epilogo (ripetuto
NumberOfEpilogsvolte):-
EPILOG_INFO_V3Descrittore. -
EPILOG_INFO_EX_V3oEPILOG_INFO_LARGE_EX_V3descrittore esteso (presente solo quandoNumberOfOps > 0). - Matrice di offset IP :
NumberOfOpsbyte (o parole a 16 bit seEPILOG_INFO_LARGE).
-
- Pool WOD: byte rimanenti.
Dimensione totale = PayloadWords × 2 byte (può includere riempimento per riempire le parole).
Offset IP di Prolog
Un array di elementi NumberOfOps. Ogni voce è un byte senza segno che indica l'offset IP dall'inizio del frammento di istruzione che esegue la corrispondente operazione di unwinding. Quando UNW_FLAG_LARGE è impostata, ogni voce è invece una word a 16 bit senza segno.
Ordine: Il primo elemento corrisponde all'operazione più vicina al corpo della funzione (l'ultima istruzione del prologo con un effetto di unwinding). L'ultima voce corrisponde all'operazione più vicina al punto di ingresso della funzione (la prima istruzione di prologo). Corrisponde alla convenzione V1 o V2 di elencare i codici di rimozione nell'ordine di esecuzione inversa. I WOD prologo iniziano sempre in corrispondenza dell'offset di byte zero del pool WOD e si applica lo stesso ordinamento.
Descrittori di epilogo
Zero o più strutture EPILOG_INFO_V3 seguono gli offset IP del prologo. Ognuno può avere un valore facoltativo EPILOG_INFO_EX_V3 (o EPILOG_INFO_LARGE_EX_V3 quando il EPILOG_INFO_LARGE flag è impostato) più la propria matrice di offset IP a dimensione variabile, di dimensioni NumberOfOps:
Quando NumberOfOps > 0 (descrittore completo)
Modulo standard (EPILOG_INFO_LARGE non impostato):
EPILOG_INFO_V3:
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bit |
| 0 | [7:3] | NumberOfOps |
5 bit |
| 1–2 | [15:0] | EpilogOffset |
Segno a 16 bit |
EPILOG_INFO_EX_V3:
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
Senza segno a 16 bit |
| 2 | [7:0] | IpOffsetOfLastInstruction |
8 bit senza segno |
| 3 .. | - | Matrice di offset IP |
NumberOfOps byte, senza segno |
Formato grande (EPILOG_INFO_LARGE impostato):
EPILOG_INFO_V3:
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bit |
| 0 | [7:3] | NumberOfOps |
5 bit |
| 1–2 | [15:0] | EpilogOffset |
Segno a 16 bit |
EPILOG_INFO_LARGE_EX_V3:
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
Senza segno a 16 bit |
| 2–3 | [15:0] | IpOffsetOfLastInstruction |
16 bit senza segno |
| 4 .. | - | Matrice di offset IP |
NumberOfOps × 2 byte, senza segno a 16 bit |
Quando NumberOfOps == 0 (descrittore ereditato)
EPILOG_INFO_V3:
| Byte | Bit | Campo | Larghezza |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bit |
| 0 | [7:3] |
NumberOfOps (= 0) |
5 bit |
| 1–2 | [15:0] | EpilogOffset |
Segno a 16 bit |
Un EPILOG_INFO_V3 record con NumberOfOps == 0 eredita i valori effettivi per NumberOfOps, FirstOp, IpOffsetOfLastInstruction campi e matrice di offset IP dal primo oggetto precedente EPILOG_INFO_V3 con NumberOfOps != 0. Il compilatore deve garantire che Flags i bit 0 e 1 in un record di epilogo abbiano NumberOfOps == 0 lo stesso valore di quelli del primo EPILOG_INFO_V3 precedente con NumberOfOps != 0.
Semantica del campo Epilog
| Campo | Descrizione |
|---|---|
Flags |
Bit 0: EPILOG_INFO_PARENT_FRAGMENT_TRANSFER - impostato se questo epilogo trasferisce il controllo al frammento padre (ad esempio, tramite JMP) anziché restituirlo al chiamante. Bit 1: EPILOG_INFO_LARGE - se impostato, il descrittore esteso usa EPILOG_INFO_LARGE_EX_V3 (16 bit IpOffsetOfLastInstruction) e ogni voce dell'offset IP è a 16 bit, consentendo di gestire epiloghi che superano i 255 byte. Bit 2: riservato (zero). |
NumberOfOps |
Numero di WOD per questo epilogo (0-31). Zero è un valore speciale che significa "eredita dal descrittore dell'epilogo precedente". |
EpilogOffset |
Spostamento con segno a 16 bit alla prima istruzione di questo epilogo. Per il primo descrittore di epilogo, i valori positivi sono offset in byte dall'inizio del frammento e i valori negativi sono offset in byte rispetto alla coda del frammento, ovvero il primo byte oltre la fine. Per i descrittori di epilogo successivi: delta a partire dall'inizio dell'epilogo precedente. Tutti gli epilogi devono usare lo stesso segno, ovvero tutti in ordine crescente dall'inizio o tutti ordinati in ordine decrescente dalla coda. |
FirstOp |
Indice di byte nel pool WOD in cui risiede il primo WOD per questo epilogo. Si tratta di un offset di byte, non di un indice WOD. Gli epiloghi possono condividere i WOD con il prologo o fra loro puntando alla stessa area del pool. |
IpOffsetOfLastInstruction |
Offset in byte senza segno calcolato dall'inizio dell'epilogo fino all'ultima istruzione dell'epilogo, in genere RET o JMP. 8 bit in EPILOG_INFO_EX_V3; 16 bit in EPILOG_INFO_LARGE_EX_V3. L'unwinder utilizza questo valore per determinare dove finisce l'epilogo e riprende il corpo della funzione. |
Offset IP di Epilog
Subito dopo ogni descrittore epilogo completo (quando NumberOfOps > 0), una matrice di NumberOfOps voci restituisce l'offset IP di ogni istruzione epiloga con un WOD corrispondente. Ogni voce è un byte senza segno o una parola senza segno a 16 bit quando EPILOG_INFO_LARGE viene impostata. Ordinamento: prima voce = operazione più vicina al corpo (prima istruzione dell'epilogo con un effetto di unwinding), ultima voce = operazione più vicina all'istruzione di trasferimento del controllo.
WOD Pool
I byte rimanenti nel payload formano il pool WOD. I WOD del prologo iniziano in modo implicito in corrispondenza dell'offset di byte 0 di questo pool. L'elenco WOD di ogni epilogo inizia in corrispondenza dell'offset di byte specificato dal relativo FirstOp campo. I WOD sono compressi senza allineamento o spaziatura interna tra di essi.
Il numero di WOD consumati per il prologo è UNWIND_INFO_V3.NumberOfOps. Il numero consumato per ciascun epilogo è quello del rispettivo EPILOG_INFO_V3.NumberOfOps (o quello ereditato).
Informazioni di riferimento sulla codifica WOD
I WOD sono a lunghezza variabile (1-5 byte), compressi con #pragma pack(1). Il codice operativo viene codificato nei bit di ordine minimo del primo byte. La decodifica richiede un'ispezione a più livelli.
Tabella di smistamento dei codici operativi
Per decodificare un WOD, leggere il primo byte e testarne i bit bassi nell'ordine seguente:
| Test | Valore del codice operativo | Tipo di WOD | Dimensione |
|---|---|---|---|
byte[0] == 0x00 |
0 | WOD_SET_FPREG |
2 byte |
byte[0] == 0x01 |
1 | WOD_ALLOC_HUGE |
5 byte |
byte[0] == 0x02 |
2 | WOD_ALLOC_LARGE |
3 byte |
byte[0] == 0x03 |
3 | WOD_PUSH_CANONICAL_FRAME |
2 byte |
(byte[0] & 0x07) == 0x04 |
4 | WOD_PUSH |
1 byte |
(byte[0] & 0x07) == 0x05 |
5 | WOD_SAVE_NONVOL_FAR |
5 byte |
(byte[0] & 0x07) == 0x06 |
6 | WOD_SAVE_NONVOL |
3 byte |
(byte[0] & 0x07) == 0x07 |
7 | WOD_PUSH_CONSECUTIVE_2 |
1 byte |
(byte[0] & 0x0F) == 0x08 |
8 | WOD_ALLOC_SMALL |
1 byte |
(byte[0] & 0x0F) == 0x09 |
9 | WOD_SAVE_XMM128_FAR |
5 byte |
(byte[0] & 0x0F) == 0x0A |
10 | WOD_SAVE_XMM128 |
3 byte |
(byte[0] & 0x3F) == 0x20 |
32 | WOD_PUSH2 |
2 byte |
** Algoritmo di decodifica WOD (pseudocodice):**
uint8_t b0 = pool[offset];
uint8_t op3 = b0 & 0x07;
switch (op3)
{
case 4: return WOD_PUSH; // 3-bit opcode = 100b
case 5: return WOD_SAVE_NONVOL_FAR;// 3-bit opcode = 101b
case 6: return WOD_SAVE_NONVOL; // 3-bit opcode = 110b
case 7: return WOD_PUSH_CONSECUTIVE_2; // 3-bit opcode = 111b
default: break; // bits[2:0] are 0b000, 0b001, 0b010, or 0b011
}
uint8_t op4 = b0 & 0x0F;
switch (op4)
{
case 0x08: return WOD_ALLOC_SMALL;
case 0x09: return WOD_SAVE_XMM128_FAR;
case 0x0A: return WOD_SAVE_XMM128;
default: break;
}
uint8_t op6 = b0 & 0x3F;
if (op6 == 0x20) return WOD_PUSH2;
// 8-bit opcode (full byte match)
switch (b0)
{
case 0x00: return WOD_SET_FPREG;
case 0x01: return WOD_ALLOC_HUGE;
case 0x02: return WOD_ALLOC_LARGE;
case 0x03: return WOD_PUSH_CANONICAL_FRAME;
default: return INVALID;
}
layout WOD (a livello di bit)
Tutte le posizioni di bit sono numerate LSB-first all'interno di ogni byte. I campi integer a più byte sono little-endian.
WOD_PUSH - 1 byte
Byte 0: [2:0] = 100b (opcode 4)
[7:3] = Register (5 bits, AMD64 integer register number)
Effetto:PUSH <reg> regola RSP di 8 e archivia la registrazione.
WOD_PUSH2 - 2 byte
Byte 0: [5:0] = 100000b (opcode 32)
[7:6] = Register1[1:0] (low 2 bits)
Byte 1: [2:0] = Register1[4:2] (high 3 bits)
[7:3] = Register2 (5 bits)
Effetto:PUSH2 <reg1>, <reg2> esegue il push di due registri con una singola istruzione (APX). Regola l'RSP di 16.
WOD_PUSH_CONSECUTIVE_2 - 1 byte
Byte 0: [2:0] = 111b (opcode 7)
[7:3] = Register (5 bits)
Effetto: Spinge Register e Register+1 consecutivamente. Regola RSP di 16 in totale.
Register il valore deve essere limitato a [0, 30], in quanto il valore di 31 verrebbe posizionato Register+1 fuori dai limiti.
WOD_ALLOC_SMALL - 1 byte
Byte 0: [3:0] = 1000b (opcode 8)
[7:4] = Size (4 bits)
Allocazione effettiva:(Size + 1) * 8 byte. Intervallo: 8–128 byte con incrementi di 8.
WOD_ALLOC_LARGE - 3 byte
Byte 0: [7:0] = 0x02 (opcode 2)
Bytes 1–2: Size (16-bit unsigned, little-endian)
Allocazione effettiva:Size * 8 byte. Intervallo: fino a 524.280 byte.
WOD_ALLOC_HUGE - 5 byte
Byte 0: [7:0] = 0x01 (opcode 1)
Bytes 1–4: Size (32-bit unsigned, little-endian)
Allocazione effettiva: Numero di byte non elaborato (nessuna scalabilità). Intervallo: fino a 4 GiB.
WOD_SET_FPREG - 2 byte
Byte 0: [7:0] = 0x00 (opcode 0)
Byte 1: [3:0] = Register (4 bits, 0–15)
[7:4] = Offset (4 bits)
Effetto: Stabilisce un puntatore al frame.
<reg> = RSP + Offset * 16.
WOD_SAVE_NONVOL - 3 byte
Byte 0: [2:0] = 110b (opcode 6)
[7:3] = Register (5 bits)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Effetto:MOV [RSP + Displacement * 8], <reg> salva un registro intero non volatile nello stack.
WOD_SAVE_NONVOL_FAR - 5 byte
Byte 0: [2:0] = 101b (opcode 5)
[7:3] = Register (5 bits)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Effetto: Uguale a WOD_SAVE_NONVOL ma con uno spostamento di byte non ridimensionato a 32 bit. Usato quando l'offset non rientra in 16 bit moltiplicati per 8.
WOD_SAVE_XMM128 - 3 byte
Byte 0: [3:0] = 1010b (opcode 10)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Effetto:MOVAPS [RSP + Displacement * 16], <xmm> salva un registro XMM a 128 bit.
WOD_SAVE_XMM128_FAR - 5 byte
Byte 0: [3:0] = 1001b (opcode 9)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Effetto: Uguale a WOD_SAVE_XMM128 ma con uno spostamento di byte non ridimensionato a 32 bit.
WOD_PUSH_CANONICAL_FRAME - 2 byte
Byte 0: [7:0] = 0x03 (opcode 3)
Byte 1: [7:0] = Type (8 bits)
Effetto: Indica che l'hardware/sistema operativo ha eseguito il push di un frame canonico nello stack. I valori di tipo distinguono:
- Telaio della macchina senza codice di errore
- Telaio della macchina con codice di errore
- Telaio della macchina con spinta della pila ombra
- Record di contesto
I valori di tipo esatti sono definiti dal sistema operativo. Consultare le intestazioni Windows SDK.
Riepilogo delle costanti del codice operativo
#define WOD_OP_SET_FPREG 0 // 8-bit opcode, 2 bytes
#define WOD_OP_ALLOC_HUGE 1 // 8-bit opcode, 5 bytes
#define WOD_OP_ALLOC_LARGE 2 // 8-bit opcode, 3 bytes
#define WOD_OP_PUSH_CANONICAL_FRAME 3 // 8-bit opcode, 2 bytes
#define WOD_OP_PUSH 4 // 3-bit opcode, 1 byte
#define WOD_OP_SAVE_NONVOL_FAR 5 // 3-bit opcode, 5 bytes
#define WOD_OP_SAVE_NONVOL 6 // 3-bit opcode, 3 bytes
#define WOD_OP_PUSH_CONSECUTIVE_2 7 // 3-bit opcode, 1 byte
#define WOD_OP_ALLOC_SMALL 8 // 4-bit opcode, 1 byte
#define WOD_OP_SAVE_XMM128_FAR 9 // 4-bit opcode, 5 bytes
#define WOD_OP_SAVE_XMM128 10 // 4-bit opcode, 3 bytes
#define WOD_OP_PUSH2 32 // 6-bit opcode, 2 bytes
Codifica del registro
I registri interi usano la numerazione AMD64 standard (5 bit, 0-31):
| Value | Registrazione |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | RSI |
| 7 | RDI |
| 8 - 15 | R8 - R15 |
| 16 - 31 | R16 - R31 (APX) |
I campi del registro XMM sono di 4 bit (0–15) e corrispondono direttamente a XMM0–XMM15.