struct UNWIND_CODE
The latest version of this topic can be found at struct UNWIND_CODE.
The unwind code array is used to record the sequence of operations in the prolog that affect the nonvolatile registers and RSP. Each code item has the following format:
UBYTE | Offset in prolog |
UBYTE: 4 | Unwind operation code |
UBYTE: 4 | Operation info |
The array is sorted by descending order of offset in the prolog.
Offset in prolog
Offset from the beginning of the prolog of the end of the instruction that performs this operation, plus 1 (that is, the offset of the start of the next instruction).
Unwind operation code
Note: Certain operation codes require an unsigned offset to a value in the local stack frame. This offset is from the start (lowest address) of the fixed stack allocation. If the Frame Register field in the UNWIND_INFO is zero, this offset is from RSP. If the Frame Register field is nonzero, this is the offset from where RSP was located when the FP reg was established. This equals the FP reg minus the FP reg offset (16 * the scaled frame register offset in the UNWIND_INFO). If an FP reg is used, then any unwind code taking an offset must only be used after the FP reg is established in the prolog.
For all opcodes except UWOP_SAVE_XMM128 and UWOP_SAVE_XMM128_FAR, the offset will always be a multiple of 8, because all stack values of interest are stored on 8 byte boundaries (the stack itself is always 16-byte aligned). For operation codes that take a short offset (less than 512K), the final USHORT in the nodes for this code holds the offset divided by 8. For operation codes that take a long offset (512K <= offset < 4GB), the final two USHORT nodes for this code hold the offset (in little-endian format).
For the opcodes UWOP_SAVE_XMM128 and UWOP_SAVE_XMM128_FAR, the offset will always be a multiple of 16, since all 128-bit XMM operations must occur on 16-byte aligned memory. Therefore, a scale factor of 16 is used for UWOP_SAVE_XMM128, permitting offsets of less than 1M.
The unwind operation code is one of the following:
UWOP_PUSH_NONVOL (0)1 node
Push a nonvolatile integer register, decrementing RSP by 8. The operation info is the number of the register. Note that, because of the constraints on epilogs, UWOP_PUSH_NONVOL unwind codes must appear first in the prolog and correspondingly, last in the unwind code array. This relative ordering applies to all other unwind codes except UWOP_PUSH_MACHFRAME.
UWOP_ALLOC_LARGE (1)2 or 3 nodes
Allocate a large-sized area on the stack. There are two forms. If the operation info equals 0, then the size of the allocation divided by 8 is recorded in the next slot, allowing an allocation up to 512K – 8. If the operation info equals 1, then the unscaled size of the allocation is recorded in the next two slots in little-endian format, allowing allocations up to 4GB – 8.
UWOP_ALLOC_SMALL (2)1 node
Allocate a small-sized area on the stack. The size of the allocation is the operation info field * 8 + 8, allowing allocations from 8 to 128 bytes.
The unwind code for a stack allocation should always use the shortest possible encoding:
Allocation Size | Unwind Code |
8 to 128 bytes | UWOP_ALLOC_SMALL |
136 to 512K-8 bytes | UWOP_ALLOC_LARGE, operation info = 0 |
512K to 4G–8 bytes | UWOP_ALLOC_LARGE, operation info = 1 |
UWOP_SET_FPREG (3)1 node
Establish the frame pointer register by setting the register to some offset of the current RSP. The offset is equal to the Frame Register offset (scaled) field in the UNWIND_INFO * 16, allowing offsets from 0 to 240. The use of an offset permits establishing a frame pointer that points to the middle of the fixed stack allocation, helping code density by allowing more accesses to use short instruction forms. Note that the operation info field is reserved and should not be used.
UWOP_SAVE_NONVOL (4)2 nodes
Save a nonvolatile integer register on the stack using a MOV instead of a PUSH. This is primarily used for shrink-wrapping, where a nonvolatile register is saved to the stack in a position that was previously allocated. The operation info is the number of the register. The scaled-by-8 stack offset is recorded in the next unwind operation code slot, as described in the note above.
UWOP_SAVE_NONVOL_FAR (5)3 nodes
Save a nonvolatile integer register on the stack with a long offset, using a MOV instead of a PUSH. This is primarily used for shrink-wrapping, where a nonvolatile register is saved to the stack in a position that was previously allocated. The operation info is the number of the register. The unscaled stack offset is recorded in the next two unwind operation code slots, as described in the note above.
UWOP_SAVE_XMM128 (8)2 nodes
Save all 128 bits of a nonvolatile XMM register on the stack. The operation info is the number of the register. The scaled-by-16 stack offset is recorded in the next slot.
UWOP_SAVE_XMM128_FAR (9)3 nodes
Save all 128 bits of a nonvolatile XMM register on the stack with a long offset. The operation info is the number of the register. The unscaled stack offset is recorded in the next two slots.
UWOP_PUSH_MACHFRAME (10)1 node
Push a machine frame. This is used to record the effect of a hardware interrupt or exception. There are two forms. If the operation info equals 0, the following has been pushed on the stack:
RSP+32 | SS |
RSP+24 | Old RSP |
RSP+16 | EFLAGS |
RSP+8 | CS |
RSP | RIP |
If the operation info equals 1, then the following has instead been pushed:
RSP+40 | SS |
RSP+32 | Old RSP |
RSP+24 | EFLAGS |
RSP+16 | CS |
RSP+8 | RIP |
RSP | Error code |
This unwind code will always appear in a dummy prolog, which is never actually executed but instead appears before the real entry point of an interrupt routine, and exists only to provide a place to simulate the push of a machine frame. UWOP_PUSH_MACHFRAME records that simulation, which indicates the machine has conceptually done the following:
Pop RIP return address from top of stack into Temp
Push SS
Push old RSP
Push EFLAGS
Push CS
Push Temp
Push Error Code (if op info equals 1)
The simulated UWOP_PUSH_MACHFRAME operation decrements RSP by 40 (op info equals 0) or 48 (op info equals 1).
Operation info
The meaning of these 4 bits depends upon the operation code. To encode a general-purpose (integer) register, the following mapping is used:
0 | RAX |
1 | RCX |
2 | RDX |
3 | RBX |
4 | RSP |
5 | RBP |
6 | RSI |
7 | RDI |
8 to 15 | R8 to R15 |