Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Scope
Сведения об очистке версии 3 добавляют поддержку Intel APX (расширенные расширения производительности). Кроме того, он обеспечивает дополнительную гибкость в создании кода, разрешенном как в прологах функций, так и в эпилогах, что позволяет компиляторам лучше оптимизировать функции в целом.
Для кода с поддержкой APX требуется Unwind V3. Код, отличный от APX, должен по-прежнему использовать обычные (предварительная версия 3) сведения о очистке.
Предупреждение
Unwind Information V3 — это предварительная спецификация. Существует по-прежнему риск критических изменений или упущений. Код, созданный в соответствии с этим руководством, следует считать кодом предварительной версии.
Терминология
| Срок | Определение |
|---|---|
| Фрагмент | Непрерывная область машинного кода, описанная одной RUNTIME_FUNCTION / UNWIND_INFO_V3 парой. Функция может состоять из основного фрагмента и нуля или нескольких подфрагментов , связанных друг с другом. Дополнительные сведения см. в разделе Структуры связанной информации о размотке. |
| WOD | Дескриптор операции развёртывания стека — это упакованное представление переменной длины одной операции развёртывания стека (push, alloc, save и т. д.). |
| Список WOD | Массив байтов в составе полезной нагрузки, в котором хранятся все WOD для пролога и эпилога(ов) фрагмента. |
| Смещение IP-адресов | Беззнаковое байтовое смещение команды относительно начала пролога или эпилога. |
| Полезная нагрузка | Область переменной длины, расположенная сразу после 4-байтового заголовка UNWIND_INFO_V3, размер которой задаётся в PayloadWords 16-разрядных словах. Содержит большое расширение пролога, смещения IP пролога, дескрипторы эпилога (с соответствующими смещениями IP) и пул WOD. |
| RVA | Относительный виртуальный адрес — это смещение от базового адреса образа во время загрузки. |
Общая структура объектного файла
Отмена очистки версии 3 повторно использует существующие соглашения PE/COFF .pdata и .xdata разделов без изменений:
.pdata - sorted array of IMAGE_AMD64_RUNTIME_FUNCTION_ENTRY (12 bytes each)
.xdata - UNWIND_INFO_V3 structures referenced by .pdata entries
Каждая RUNTIME_FUNCTION запись:
| Смещение | Size | Поле |
|---|---|---|
| 0 | 4 |
BeginAddress — RVA запуска фрагмента |
| 4 | 4 |
EndAddress — RVA первого байта после конца фрагмента |
| 8 | 4 |
UnwindInfoAddress — RVA UNWIND_INFO_V3 в .xdata |
Никаких изменений из версии 1 или версии 2.
UNWIND_INFO_V3 Заголовка
Все поля с несколькими байтами являются маленькими. Бит 0 — это LSB каждого байта.
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0 | [2:0] |
Version (= 3) |
3 бита |
| 0 | [7:3] | Flags |
5 битов |
| 1 | [7:0] | SizeOfProlog |
8 бит |
| 2 | [7:0] | PayloadWords |
8 бит |
| 3 | [4:0] | NumberOfOps |
5 битов |
| 3 | [7:5] | NumberOfEpilogs |
3 бита |
Семантика поля
| Поле | Description |
|---|---|
Version |
Этот параметр должен содержать значение 3. |
Flags |
Те же определения флагов, что и V1 или V2: UNW_FLAG_EHANDLER (0x01), (0x02), UNW_FLAG_UHANDLERUNW_FLAG_CHAININFO (0x04). Новое в V3: UNW_FLAG_LARGE (0x08). Бит 4 зарезервирован (ноль). |
SizeOfProlog |
Смещение в байтах до начала первой инструкции, не входящей в пролог. Если UNW_FLAG_LARGE задано, это 8-разрядное поле является низким байтом 16-разрядного размера пролога. Дополнительные сведения о 16-разрядной форме см. UNW_FLAG_LARGE и UNWIND_INFO_LARGE_V3. |
PayloadWords |
Количество 16-разрядных слов полезных данных, следующих за этим заголовком. Полезные данные содержат смещения IP пролога, все дескрипторы эпилога (включая их смещения IP) и пул WOD. Не включает RVA обработчика исключений или цепочку RUNTIME_FUNCTION , которая может следовать. Алгоритм для определения местоположения данных обработчика/цепочки такой же, как в V1 или V2: header + 4 + PayloadWords * 2, с выравниванием по DWORD. |
NumberOfOps |
Количество WOD в прологе (0–31). Ноль означает отсутствие пролога. Если для функции требуется более 31 операций пролога, используйте подфрагмент. |
NumberOfEpilogs |
Количество EPILOG_INFO_V3 дескрипторов, которые следуют за смещениями IP пролога (0–7). Ноль означает отсутствие эпилогов в этом фрагменте. Если требуется более 7, используйте подфрагмент. |
UNW_FLAG_LARGE и UNWIND_INFO_LARGE_V3.
Если в заголовке UNW_FLAG_LARGE установлен Flags, первый байт полезной нагрузки представляет собой 1-байтовое расширение UNWIND_INFO_LARGE_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0 | [7:0] | SizeOfPrologHighByte |
8 бит |
SizeOfPrologHighByte в сочетании с UNWIND_INFO_V3.SizeOfProlog образуют 16-разрядный размер пролога: SizeOfProlog16 = (SizeOfPrologHighByte << 8) | SizeOfProlog
Кроме того, если UNW_FLAG_LARGE задано:
- Записи смещения IP в прологе являются 16-разрядными беззнаковыми (по 2 байта каждая), а не 8-разрядными.
Байт UNWIND_INFO_LARGE_V3 является частью полезной нагрузки и входит в состав PayloadWords. Формула смещения обработчика остается неизменной.
Этот флаг необходим только для прологов, превышающих 255 байт.
Поиск обработчика исключений или сведений о цепочке
Идентичен версии 1 или версии 2:
handler_offset = ALIGN_UP(sizeof(UNWIND_INFO_V3) + PayloadWords * 2, 4)
Если UNW_FLAG_EHANDLER или UNW_FLAG_UHANDLER задано, 4-байтовый обработчик RVA имеет значение handler_offset, за которым следует данные обработчика, зависящие от языка.
Если UNW_FLAG_CHAININFO задано, 12-байт RUNTIME_FUNCTION находится в handler_offset.
Структура полезной нагрузки
Сразу после 4-байтового заголовка полезная нагрузка располагается в следующем порядке (всё упаковано, семантика #pragma pack(1)):
-
UNWIND_INFO_LARGE_V3- 1 байт, присутствует только в том случае, еслиUNW_FLAG_LARGEзадано. - Смещения IP-адресов пролога —
NumberOfOpsбайты (или 16-разрядные слова ifUNW_FLAG_LARGE). - Для каждого эпилога (повторяется
NumberOfEpilogsраз):-
EPILOG_INFO_V3Дескриптор. -
EPILOG_INFO_EX_V3илиEPILOG_INFO_LARGE_EX_V3расширенный дескриптор (присутствует только в том случаеNumberOfOps > 0). - Массив смещения IP —
NumberOfOpsбайты (или 16-разрядные слова ifEPILOG_INFO_LARGE).
-
- Пул WOD — оставшиеся байты.
Общий размер = PayloadWords × 2 байта (может включать заполнение для заполнения слов).
Смещения IP-адресов Prolog
Массив записей NumberOfOps. Каждая запись является несписанным байтом, предоставляющим смещение IP с начала фрагмента инструкции, выполняющей соответствующую операцию очистки. Если UNW_FLAG_LARGE установлено, каждая запись вместо этого представляет собой 16-разрядное слово без знака.
Порядок: Первая запись соответствует операции, наиболее близкой к телу функции (инструкция пролога последняя с эффектом развёртывания стека). Последняя запись соответствует операции, ближайшей к точке входа функции ( первая инструкция prolog). Это соответствует соглашению V1 или V2 о перечислении кодов отмены в обратном порядке выполнения. WOD пролога всегда начинаются с нулевого байтового смещения в пуле WOD, и к ним применяется тот же порядок.
Дескрипторы эпилога
Ноль или более EPILOG_INFO_V3 структур следуют смещениям IP-адресов пролога. Каждый может иметь необязательный EPILOG_INFO_EX_V3 (или EPILOG_INFO_LARGE_EX_V3, если установлен флаг EPILOG_INFO_LARGE), а также собственный массив смещений IP переменного размера, размером NumberOfOps:
Когда NumberOfOps > 0 (полный дескриптор)
Стандартная форма (EPILOG_INFO_LARGE не задана):
EPILOG_INFO_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 бита |
| 0 | [7:3] | NumberOfOps |
5 битов |
| 1–2 | [15:0] | EpilogOffset |
16-разрядная подпись |
EPILOG_INFO_EX_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
16-разрядная без знака |
| 2 | [7:0] | IpOffsetOfLastInstruction |
8 бит без знака |
| 3 .. | - | Массив смещения IP |
NumberOfOps байты, без знака |
Крупный формат (EPILOG_INFO_LARGE набор):
EPILOG_INFO_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 бита |
| 0 | [7:3] | NumberOfOps |
5 битов |
| 1–2 | [15:0] | EpilogOffset |
16-разрядная подпись |
EPILOG_INFO_LARGE_EX_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0–1 | [15:0] | FirstOp |
16-разрядная без знака |
| 2–3 | [15:0] | IpOffsetOfLastInstruction |
16 бит без знака |
| 4 .. | - | Массив смещения IP |
NumberOfOps × 2 байта, беззнаковое 16-разрядное |
Когда NumberOfOps == 0 (унаследованный дескриптор)
EPILOG_INFO_V3:
| Байт | Биты | Поле | Ширина |
|---|---|---|---|
| 0 | [2:0] | Flags |
3 бита |
| 0 | [7:3] |
NumberOfOps (= 0) |
5 битов |
| 1–2 | [15:0] | EpilogOffset |
16-разрядная подпись |
Запись EPILOG_INFO_V3 с NumberOfOps == 0 наследует действующие значения для полей NumberOfOps, FirstOp, IpOffsetOfLastInstruction и массива смещений IP от первой предшествующей записи EPILOG_INFO_V3 с NumberOfOps != 0. Компилятор должен гарантировать, что Flags биты 0 и 1 в записи эпилога с NumberOfOps == 0 имеют те же значения, что и в первой предшествующей EPILOG_INFO_V3 с NumberOfOps != 0.
Семантика поля эпилога
| Поле | Description |
|---|---|
Flags |
Bit 0: EPILOG_INFO_PARENT_FRAGMENT_TRANSFER — устанавливается, если этот эпилог передаёт управление обратно в родительский фрагмент (например, через JMP), а не возвращает управление вызывающей стороне. Бит 1: EPILOG_INFO_LARGE — при установке расширенный дескриптор использует EPILOG_INFO_LARGE_EX_V3 (16-разрядный IpOffsetOfLastInstruction), и каждая запись смещения IP имеет длину 16 бит, что позволяет учитывать эпилоги размером более 255 байт. Бит 2: зарезервирован (ноль). |
NumberOfOps |
Количество WOD для этого эпилога (0–31). Ноль — это специальное значение, которое означает "наследование от предыдущего дескриптора эпилога". |
EpilogOffset |
16-разрядное смещение со знаком до первой инструкции этого эпилога. Для первого дескриптора эпилога: положительные значения представляют собой байтовые смещения от начала фрагмента, а отрицательные значения — байтовые смещения от конца фрагмента, то есть от первого байта сразу после его конца. Для последующих дескрипторов эпилога: разность с начала предыдущего эпилога. Все эпилоги должны использовать один и тот же знак - либо все отсортированные по возрастанию от начала, либо все отсортированные по убыванию от хвоста. |
FirstOp |
Индекс байтов в пул WOD, где находится первый WOD для этого эпилога. Это смещение в байтах, а не индекс WOD. Эпилоги могут совместно использовать WOD с прологом или друг с другом, указывая на один и тот же регион пула. |
IpOffsetOfLastInstruction |
Беззнаковое смещение в байтах от начала эпилога до последней инструкции в эпилоге, обычно RET или JMP. 8-разрядный в EPILOG_INFO_EX_V3; 16-разрядный в EPILOG_INFO_LARGE_EX_V3. Механизм раскрутки стека использует это значение, чтобы определить, где заканчивается эпилог и где возобновляется выполнение тела функции. |
Смещения IP Epilog
Сразу после каждого полного дескриптора эпилога (когда NumberOfOps > 0) массив из записей NumberOfOps содержит смещение IP для каждой инструкции эпилога, для которой имеется соответствующий WOD. Каждая запись представляет собой неподписанный байт или 16-разрядное слово без знака при EPILOG_INFO_LARGE установке. Упорядочение: первая запись = операция, близкая к телу (первая инструкция эпилога с эффектом очистки), последняя запись = операция, близкая к инструкции передачи элементов управления.
Пул WOD
Оставшиеся байты полезной нагрузки образуют пул WOD. WOD-ы пролога неявно начинаются с байтового смещения 0 в этом пуле. Список WOD каждого эпилога начинается с байтового смещения, которое задаётся его полем FirstOp. WOD упаковываются без выравнивания и заполнения между ними.
Количество WOD, используемых для пролога, составляет UNWIND_INFO_V3.NumberOfOps. Число, используемое для каждого эпилога, является соответствующим EPILOG_INFO_V3.NumberOfOps (или унаследованным).
Справочник по кодировке WOD
WOD имеют переменную длину (1–5 байт) и упаковываются с помощью #pragma pack(1). Код опкода закодирован в битах низкого порядка первого байта. Для декодирования требуется многоуровневая проверка битов.
Таблица отправки opcode
Чтобы декодировать WOD, прочитайте первый байт и проверьте его низкие биты в следующем порядке:
| Тест | Значение кода операции | Тип WOD | Size |
|---|---|---|---|
byte[0] == 0x00 |
0 | WOD_SET_FPREG |
2 байта |
byte[0] == 0x01 |
1 | WOD_ALLOC_HUGE |
5 байт |
byte[0] == 0x02 |
2 | WOD_ALLOC_LARGE |
3 байта |
byte[0] == 0x03 |
3 | WOD_PUSH_CANONICAL_FRAME |
2 байта |
(byte[0] & 0x07) == 0x04 |
4 | WOD_PUSH |
1 байт |
(byte[0] & 0x07) == 0x05 |
5 | WOD_SAVE_NONVOL_FAR |
5 байт |
(byte[0] & 0x07) == 0x06 |
6 | WOD_SAVE_NONVOL |
3 байта |
(byte[0] & 0x07) == 0x07 |
7 | WOD_PUSH_CONSECUTIVE_2 |
1 байт |
(byte[0] & 0x0F) == 0x08 |
8 | WOD_ALLOC_SMALL |
1 байт |
(byte[0] & 0x0F) == 0x09 |
9 | WOD_SAVE_XMM128_FAR |
5 байт |
(byte[0] & 0x0F) == 0x0A |
10 | WOD_SAVE_XMM128 |
3 байта |
(byte[0] & 0x3F) == 0x20 |
32 | WOD_PUSH2 |
2 байта |
** Алгоритм декодирования WOD (псевдокод):**
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;
}
Макеты WOD (Bit-Level)
Все битовые позиции нумерованы LSB-first в каждом байте. Поля целочисленного числа с несколькими байтами являются маленькими.
WOD_PUSH - 1 байт
Byte 0: [2:0] = 100b (opcode 4)
[7:3] = Register (5 bits, AMD64 integer register number)
Эффект:PUSH <reg> настраивает RSP на 8 и сохраняет регистр.
WOD_PUSH2 - 2 байта
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)
Эффект:PUSH2 <reg1>, <reg2> отправляет два регистра с одной инструкцией (APX). Корректирует RSP на 16.
WOD_PUSH_CONSECUTIVE_2 - 1 байт
Byte 0: [2:0] = 111b (opcode 7)
[7:3] = Register (5 bits)
Эффект: Толкает Register и Register+1 последовательно. Изменяет RSP в общей сложности на 16.
Register должно быть ограничено диапазоном [0, 30], так как значение 31 выведет Register+1 за пределы допустимого диапазона.
WOD_ALLOC_SMALL - 1 байт
Byte 0: [3:0] = 1000b (opcode 8)
[7:4] = Size (4 bits)
Фактическое выделение:(Size + 1) * 8 Байт. Диапазон: 8–128 байт в шагах от 8.
WOD_ALLOC_LARGE - 3 байта
Byte 0: [7:0] = 0x02 (opcode 2)
Bytes 1–2: Size (16-bit unsigned, little-endian)
Фактическое выделение:Size * 8 Байт. Диапазон: до 524 280 байт.
WOD_ALLOC_HUGE - 5 байт
Byte 0: [7:0] = 0x01 (opcode 1)
Bytes 1–4: Size (32-bit unsigned, little-endian)
Фактическое выделение: Необработанное число байтов (без масштабирования). Диапазон: до 4 ГиБ.
WOD_SET_FPREG - 2 байта
Byte 0: [7:0] = 0x00 (opcode 0)
Byte 1: [3:0] = Register (4 bits, 0–15)
[7:4] = Offset (4 bits)
Эффект: Устанавливает указатель кадра.
<reg> = RSP + Offset * 16.
WOD_SAVE_NONVOL - 3 байта
Byte 0: [2:0] = 110b (opcode 6)
[7:3] = Register (5 bits)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Эффект:MOV [RSP + Displacement * 8], <reg> сохраняет неволатильный целочисленный регистр в стек.
WOD_SAVE_NONVOL_FAR - 5 байт
Byte 0: [2:0] = 101b (opcode 5)
[7:3] = Register (5 bits)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Эффект: То же, что и WOD_SAVE_NONVOL, но с 32-разрядным немасштабируемым байтовым смещением. Используется, если смещение не соответствует 16 битам, масштабируемым на 8.
WOD_SAVE_XMM128 - 3 байта
Byte 0: [3:0] = 1010b (opcode 10)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–2: Displacement (16-bit unsigned, little-endian)
Эффект:MOVAPS [RSP + Displacement * 16], <xmm> сохраняет 128-разрядную регистрацию XMM.
WOD_SAVE_XMM128_FAR - 5 байт
Byte 0: [3:0] = 1001b (opcode 9)
[7:4] = Register (4 bits, XMM0–XMM15)
Bytes 1–4: Displacement (32-bit unsigned, little-endian)
Эффект: То же, что и WOD_SAVE_XMM128, но с 32-разрядным немасштабируемым байтовым смещением.
WOD_PUSH_CANONICAL_FRAME - 2 байта
Byte 0: [7:0] = 0x03 (opcode 3)
Byte 1: [7:0] = Type (8 bits)
Эффект: Указывает, что оборудование или ОС поместило канонический фрейм в стек. Значения типа различают:
- Кадр компьютера без кода ошибки
- Рама машины с кодом ошибки
- Кадр компьютера с push-стеком теневого стека
- Контекстная запись
(Точные значения типов определяются ОС; обратитесь к заголовкам пакета SDK Windows.)
Сводка констант 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
Регистрация кодировки
Целые регистры используют стандартную нумерацию AMD64 (5 бит, 0–31):
| Ценность | Регистрация |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | РСИ (индекс относительной силы) |
| 7 | RDI |
| 8–15 | R8 — R15 |
| 16 - 31 | R16 — R31 (APX) |
Поля регистра XMM имеют длину 4 бита (0–15) и напрямую соответствуют XMM0–XMM15.