Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Âmbito
O Unwind Information V3 adiciona suporte para o Intel APX (Advanced Performance Extensions). Também traz flexibilidade adicional à geração de código permitida tanto nos prólogos como nos epílogos de funções, permitindo aos compiladores otimizar melhor as funções no seu conjunto.
O Unwind V3 é necessário para o código que suporta APX. O código não habilitado para APX deve continuar a usar a Informação Unwind convencional (pré-V3).
Warning
Unwind Information V3 é uma especificação preliminar. Ainda existe o risco de quebrar alterações ou omissões. O código produzido sob esta orientação deve, por si só, ser considerado código de pré-visualização.
Terminologia
| Prazo | Definition |
|---|---|
| Fragmento | Uma região contígua de código máquina descrita por um único RUNTIME_FUNCTION / UNWIND_INFO_V3 par. Uma função pode consistir num fragmento principal e zero ou mais subfragmentos encadeados. Para mais informações, consulte Estruturas de informações de desenrolamento encadeadas. |
| WOD | O Descritor de Operação de Desempilhamento é uma codificação compacta de comprimento variável de uma única operação de desempilhamento (push, alloc, save, e assim por diante). |
| Pool WOD | O array de bytes dentro da carga útil que armazena todos os WODs relativos ao prólogo e ao(s) epílogo(s) de um fragmento. |
| Deslocamento de IP | O deslocamento em bytes sem sinal de uma instrução em relação ao início do prólogo ou epílogo. |
| Payload | A região de comprimento variável imediatamente após o cabeçalho UNWIND_INFO_V3 de 4 bytes, cujo tamanho é definido por PayloadWords palavras de 16 bits. Contém a extensão prolog grande, os deslocamentos de IP do prolog, os descritores do epilog (com os seus deslocamentos de IP) e o pool WOD. |
| RVA | O Endereço Virtual Relativo é o deslocamento em relação ao endereço base da imagem no momento do carregamento. |
Estrutura geral do ficheiro-objeto
O Unwind V3 reutiliza sem alterações as convenções de secção existentes de PE/COFF .pdata e .xdata:
.pdata - sorted array of IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY (12 bytes each)
.xdata - UNWIND_INFO_V3 structures referenced by .pdata entries
Cada RUNTIME_FUNCTION entrada é:
| Deslocamento | Size | Campo |
|---|---|---|
| 0 | 4 |
BeginAddress - RVA do início do fragmento |
| 4 | 4 |
EndAddress - RVA do primeiro byte após o fim do fragmento |
| 8 | 4 |
UnwindInfoAddress - RVA de UNWIND_INFO_V3 em .xdata |
Sem alterações em relação à V1 ou V2.
UNWIND_INFO_V3 Cabeçalho
Todos os campos de múltiplos bytes são little-endian. O bit 0 é o LSB de cada byte.
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0 | [2:0] |
Version (= 3) |
3 bits |
| 0 | [7:3] | Flags |
5 bits |
| 1 | [7:0] | SizeOfProlog |
8 bits |
| 2 | [7:0] | PayloadWords |
8 bits |
| 3 | [4:0] | NumberOfOps |
5 bits |
| 3 | [7:5] | NumberOfEpilogs |
3 bits |
Semântica de Campo
| Campo | Description |
|---|---|
Version |
Deve ser 3. |
Flags |
Mesmas definições de bandeira que V1 ou V2: UNW_FLAG_EHANDLER (0x01), UNW_FLAG_UHANDLER (0x02), UNW_FLAG_CHAININFO (0x04). Novidade na V3: UNW_FLAG_LARGE (0x08). Bit 4 reservado (zero). |
SizeOfProlog |
Deslocamento de bytes para o início da primeira instrução que não faz parte do prólogo. Quando UNW_FLAG_LARGE está definido, este campo de 8 bits é o byte mais baixo de um tamanho de prólog de 16 bits. Para mais informações sobre o formato de 16 bits, consulte UNW_FLAG_LARGE e UNWIND_INFO_LARGE_V3. |
PayloadWords |
Número de palavras de carga útil de 16 bits após este cabeçalho. A carga útil contém deslocamentos IP prolog, todos os descritores epilog (incluindo os seus deslocamentos IP) e o pool WOD. Não inclui o gestor de exceções RVA ou chained RUNTIME_FUNCTION que possa seguir. O algoritmo para localizar os dados do handler/chain é o mesmo do V1 ou V2: header + 4 + PayloadWords * 2, alinhado com DWORD. |
NumberOfOps |
Número de WODs no prólogo (0–31). Zero significa sem prólogo. Se uma função precisar de mais de 31 operações de prólogo, use um subfragmento. |
NumberOfEpilogs |
Número de descritores EPILOG_INFO_V3 que seguem os deslocamentos de IP do prólogo (0–7). Zero significa que não há epílogos neste fragmento. Se forem necessários mais de 7, use um subfragmento. |
UNW_FLAG_LARGE e UNWIND_INFO_LARGE_V3
Quando UNW_FLAG_LARGE está definido no cabeçalho Flags, o primeiro byte da carga útil é uma extensão de 1 byte UNWIND_INFO_LARGE_V3 :
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0 | [7:0] | SizeOfPrologHighByte |
8 bits |
SizeOfPrologHighByte é combinado com UNWIND_INFO_V3.SizeOfProlog para formar um tamanho de prólog de 16 bits: SizeOfProlog16 = (SizeOfPrologHighByte << 8) | SizeOfProlog.
Além disso, quando UNW_FLAG_LARGE está definido:
- As entradas de deslocamento IP do Prolog são de 16 bits não assinados (2 bytes cada), em vez de terem 8 bits.
O UNWIND_INFO_LARGE_V3 byte faz parte da carga útil e está incluído em PayloadWords. A fórmula do manipulador-offset mantém-se inalterada.
Esta flag é necessária apenas para prólogos que excedam 255 bytes.
Localizar Processador de Exceções / Informações da Cadeia
Idêntico ao V1 ou V2:
handler_offset = ALIGN_UP(sizeof(UNWIND_INFO_V3) + PayloadWords * 2, 4)
Se UNW_FLAG_EHANDLER ou UNW_FLAG_UHANDLER estiver definido, um RVA de 4 bytes está em handler_offset, seguido de dados específicos de cada linguagem.
Se UNW_FLAG_CHAININFO estiver definido, um RUNTIME_FUNCTION de 12 bytes encontra-se em handler_offset.
Disposição da Carga Útil
Imediatamente após o cabeçalho de 4 bytes, a carga útil está organizada pela seguinte ordem (todos empacotados, semântica #pragma pack(1)):
-
UNWIND_INFO_LARGE_V3- 1 byte, presente apenas quandoUNW_FLAG_LARGEfor definido. - Deslocamentos de IP Prolog -
NumberOfOpsbytes (ou palavras de 16 bits seUNW_FLAG_LARGE). - Para cada epílogo (repetido
NumberOfEpilogsvezes):-
EPILOG_INFO_V3descritor. -
EPILOG_INFO_EX_V3ouEPILOG_INFO_LARGE_EX_V3descritor estendido (presente apenas quandoNumberOfOps > 0). - Matriz de deslocamento IP -
NumberOfOpsbytes (ou palavras de 16 bits seEPILOG_INFO_LARGE).
-
- WOD Pool - bytes restantes.
Tamanho total = PayloadWords × 2 bytes (pode incluir enchimento para completar palavras).
Deslocamentos de IP do Prolog
Uma matriz de NumberOfOps entradas. Cada entrada é um byte sem sinal que indica o deslocamento de IP a partir do início do fragmento da instrução que executa a operação de desenrolamento correspondente. Quando UNW_FLAG_LARGE estiver definido, cada entrada passa a ser uma palavra não assinalada de 16 bits.
Ordenação: A primeira entrada corresponde à operação mais próxima do corpo da função (a última instrução do prólogo com efeito de recuperação). A última entrada corresponde à operação mais próxima do ponto de entrada da função (a primeira instrução prolog). Isto está em conformidade com a convenção V1 ou V2 de enumeração dos códigos de desenrolamento pela ordem inversa de execução. Os WODs prolog começam sempre no deslocamento de bytes zero do pool de WOD, e esta mesma ordem aplica-se.
Descritores do Epilog
Seguem-se zero ou mais estruturas EPILOG_INFO_V3 aos deslocamentos de IP do prolog. Cada um pode ter um EPILOG_INFO_EX_V3 opcional (ou EPILOG_INFO_LARGE_EX_V3 quando o sinalizador EPILOG_INFO_LARGE está definido), além da sua própria matriz de deslocamentos IP de tamanho variável, com tamanho NumberOfOps:
Quando NumberOfOps > 0 (descrição completa)
Forma padrão (EPILOG_INFO_LARGE não definida):
EPILOG_INFO_V3:
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bits |
| 0 | [7:3] | NumberOfOps |
5 bits |
| 1–2 | [15:0] | EpilogOffset |
16 bits com sinal |
EPILOG_INFO_EX_V3:
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
16 bits sem sinal |
| 2 | [7:0] | IpOffsetOfLastInstruction |
8 bits não assinado |
| 3 .. | - | Matriz de deslocamento IP |
NumberOfOps Bytes, sem assinatura |
Formato grande (conjunto EPILOG_INFO_LARGE):
EPILOG_INFO_V3:
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bits |
| 0 | [7:3] | NumberOfOps |
5 bits |
| 1–2 | [15:0] | EpilogOffset |
16 bits com sinal |
EPILOG_INFO_LARGE_EX_V3:
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
16 bits sem sinal |
| 2–3 | [15:0] | IpOffsetOfLastInstruction |
16 bits sem sinal |
| 4 .. | - | Matriz de deslocamento IP |
NumberOfOps × 2 bytes, 16 bits sem sinal |
Quando NumberOfOps == 0 (descritor herdado)
EPILOG_INFO_V3:
| byte | Bits | Campo | Largura |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 bits |
| 0 | [7:3] |
NumberOfOps (= 0) |
5 bits |
| 1–2 | [15:0] | EpilogOffset |
16 bits com sinal |
Um registo EPILOG_INFO_V3 com NumberOfOps == 0 herda os valores efetivos dos campos NumberOfOps, FirstOp e IpOffsetOfLastInstruction, bem como a matriz de deslocamentos de IP, do primeiro EPILOG_INFO_V3 anterior com NumberOfOps != 0. O compilador deve garantir que Flags os bits 0 e 1 num registo de epílogo com NumberOfOps == 0 têm o mesmo valor que os do primeiro EPILOG_INFO_V3 precedente com NumberOfOps != 0.
Semântica dos Campos do Epilog
| Campo | Description |
|---|---|
Flags |
Bit 0: EPILOG_INFO_PARENT_FRAGMENT_TRANSFER - definido caso este epílogo transfira o controlo de volta para o fragmento pai (por exemplo, através de JMP) em vez de regressar ao chamador. Bit 1: EPILOG_INFO_LARGE - quando definido, o descritor estendido usa EPILOG_INFO_LARGE_EX_V3 (16 bits IpOffsetOfLastInstruction) e cada entrada de deslocamento IP é de 16 bits, acomodando epílogos superiores a 255 bytes. Bit 2: reservado (0). |
NumberOfOps |
Número de WODs neste epílogo (0–31). Zero é um valor especial que significa "herdar do descritor epilógico anterior." |
EpilogOffset |
deslocamento de 16 bits com sinal até à primeira instrução deste epílogo. Para o primeiro descritor do epílogo: valores positivos são deslocamentos de bytes do início do fragmento, e valores negativos são deslocamentos de bytes da cauda do fragmento, ou seja, o primeiro byte após o fim. Para descritores do epílogo subsequente: delta do início do epílogo anterior. Todos os epílogos devem usar o mesmo sinal – ou todos ordenados ascendendo desde o início, ou todos ordenados descendo a partir da cauda. |
FirstOp |
Índice de bytes para o pool WOD onde reside o primeiro WOD deste epílogo. Isto é um deslocamento de bytes, não um índice WOD. Os epílogos podem partilhar WODs com o prólogo ou entre si apontando para a mesma região do pool. |
IpOffsetOfLastInstruction |
Deslocamento de byte não assinado desde o início do epílogo até à última instrução do epílogo, normalmente RET ou JMP. 8 bits em EPILOG_INFO_EX_V3; 16 bits em EPILOG_INFO_LARGE_EX_V3. O desenrolador usa este valor para determinar onde termina o epílogo e onde o corpo da função recomeça. |
Deslocamentos IP da Epilog
Imediatamente após cada descritor completo de epílogo (quando NumberOfOps > 0), uma matriz de entradas NumberOfOps fornece o deslocamento do IP de cada instrução de epílogo que tenha um WOD correspondente. Cada entrada é um byte sem sinal, ou uma palavra sem sinal de 16 bits quando EPILOG_INFO_LARGE está definida. Ordenação: primeira entrada = operação mais próxima do corpo (primeira instrução do epílogo com efeito de desempilhamento), última entrada = operação mais próxima da instrução de transferência de controlo.
Piscina WOD
Os bytes restantes na carga útil formam o pool WOD. Os WODs do prólogo começam implicitamente no deslocamento do byte 0 deste pool. A lista de WOD de cada epilog começa no deslocamento em bytes indicado pelo campo FirstOp. Os WODs estão agrupados, sem alinhamento nem espaçamento interno entre si.
O número de WODs consumidos para o prólogo é UNWIND_INFO_V3.NumberOfOps. O número consumido por cada epílogo é o seu respetivo EPILOG_INFO_V3.NumberOfOps (ou herdado).
Referência de codificação do WOD
Os WODs são de comprimento variável (1–5 bytes), preenchidos com #pragma pack(1). O opcode é codificado nos bits de ordem baixa do primeiro byte. A decodificação requer inspeção de bits multinível.
Tabela de Despacho de Opcode
Para decodificar um WOD, leia o primeiro byte e teste os seus bits mais baixos na seguinte ordem:
| Test | Valor do Opcode | Tipo de WOD | Size |
|---|---|---|---|
byte[0] == 0x00 |
0 | WOD_SET_FPREG |
2 bytes |
byte[0] == 0x01 |
1 | WOD_ALLOC_HUGE |
5 bytes |
byte[0] == 0x02 |
2 | WOD_ALLOC_LARGE |
3 bytes |
byte[0] == 0x03 |
3 | WOD_PUSH_CANONICAL_FRAME |
2 bytes |
(byte[0] & 0x07) == 0x04 |
4 | WOD_PUSH |
1 byte |
(byte[0] & 0x07) == 0x05 |
5 | WOD_SAVE_NONVOL_FAR |
5 bytes |
(byte[0] & 0x07) == 0x06 |
6 | WOD_SAVE_NONVOL |
3 bytes |
(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 bytes |
(byte[0] & 0x0F) == 0x0A |
10 | WOD_SAVE_XMM128 |
3 bytes |
(byte[0] & 0x3F) == 0x20 |
32 | WOD_PUSH2 |
2 bytes |
** Algoritmo de decodificação WOD (pseudocódigo):**
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;
}
Esquemas WOD (ao nível dos bits)
Todas as posições dos bits são numeradas LSB-first dentro de cada byte. Os campos de inteiros com vários bytes estão em formato little-endian.
WOD_PUSH - 1 byte
Byte 0: [2:0] = 100b (opcode 4)
[7:3] = Register (5 bits, AMD64 integer register number)
Efeito:PUSH <reg> ajusta o RSP em 8 e armazena o registo.
WOD_PUSH2 - 2 bytes
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)
Efeito:PUSH2 <reg1>, <reg2> empurra dois registos com uma única instrução (APX). Ajusta o RSP em 16.
WOD_PUSH_CONSECUTIVE_2 - 1 byte
Byte 0: [2:0] = 111b (opcode 7)
[7:3] = Register (5 bits)
Efeito: Empurra Register e Register+1 consecutivamente. Aumenta RSP em 16 no total.
Register O valor deve ser limitado a [0, 30], pois o valor de 31 colocaria Register+1 fora dos limites.
WOD_ALLOC_SMALL - 1 byte
Byte 0: [3:0] = 1000b (opcode 8)
[7:4] = Size (4 bits)
Alocação efetiva:(Size + 1) * 8 bytes. Alcance: 8–128 bytes em passos de 8.
WOD_ALLOC_LARGE - 3 bytes
Byte 0: [7:0] = 0x02 (opcode 2)
Bytes 1–2: Size (16-bit unsigned, little-endian)
Alocação efetiva:Size * 8 bytes. Alcance: até 524.280 bytes.
WOD_ALLOC_HUGE - 5 bytes
Byte 0: [7:0] = 0x01 (opcode 1)
Bytes 1–4: Size (32-bit unsigned, little-endian)
Alocação efetiva: Contagem bruta de bytes (sem escalonamento). Alcance: até 4 GiB.
WOD_SET_FPREG - 2 bytes
Byte 0: [7:0] = 0x00 (opcode 0)
Byte 1: [3:0] = Register (4 bits, 0–15)
[7:4] = Offset (4 bits)
Efeito: Estabelece um apontador de frame.
<reg> = RSP + Offset * 16.
WOD_SAVE_NONVOL - 3 bytes
Byte 0: [2:0] = 110b (opcode 6)
[7:3] = Register (5 bits)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Efeito:MOV [RSP + Displacement * 8], <reg> guarda um registo de números inteiros não volátil na pilha.
WOD_SAVE_NONVOL_FAR - 5 bytes
Byte 0: [2:0] = 101b (opcode 5)
[7:3] = Register (5 bits)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Efeito: O mesmo que WOD_SAVE_NONVOL mas com um deslocamento de byte sem escala de 32 bits. Usado quando o offset não cabe em 16 bits escalados por 8.
WOD_SAVE_XMM128 - 3 bytes
Byte 0: [3:0] = 1010b (opcode 10)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Efeito:MOVAPS [RSP + Displacement * 16], <xmm> poupa um registo XMM de 128 bits.
WOD_SAVE_XMM128_FAR - 5 bytes
Byte 0: [3:0] = 1001b (opcode 9)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Efeito: O mesmo que WOD_SAVE_XMM128 mas com um deslocamento de byte sem escala de 32 bits.
WOD_PUSH_CANONICAL_FRAME - 2 bytes
Byte 0: [7:0] = 0x03 (opcode 3)
Byte 1: [7:0] = Type (8 bits)
Efeito: Indica que o hardware/sistema operativo colocou um quadro canónico na pilha. Os valores de tipo distinguem:
- Estrutura da máquina sem código de erro
- Estrutura da máquina com código de erro
- Estrutura da máquina com inserção na pilha sombra
- Registo de contexto
(Os valores exatos dos tipos são definidos pelo sistema operativo; consulte os cabeçalhos do SDK do Windows.)
Resumo da Constante do Opcode
#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ção de registos
Os registos inteiros utilizam a numeração padrão AMD64 (5 bits, 0–31):
| Value | Registar |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | DERP |
| 5 | RBP |
| 6 | LER |
| 7 | IDI |
| 8 - 15 | R8 - R15 |
| 16 - 31 | R16 - R31 (APX) |
Os campos de registo XMM têm 4 bits (0–15), correspondendo diretamente a XMM0–XMM15.