An overview of structured exception handling and C++ exception handling coding conventions and behavior on the x64. For general information on exception handling, see . Exception Handling in Visual C++
The following example throws a char * exception, but does not contain a handler designated to catch exceptions of type char *. The call to set_terminate instructs terminate to call term_func.
// exceptions_Unhandled_Exceptions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
void term_func() {
cout << "term_func was called by terminate." << endl;
exit( -1 );
}
int main() {
try
{
set_terminate( term_func );
throw "Out of memory!"; // 没有对应的catch句柄对应该异常
}
catch( int )
{
cout << "Integer exception raised." << endl;
}
return 0;
}
Unwind data for exception handling, debugger support 支持调试器的异常处理的数据展开
Several data structures are required for exception handling and debugging support.
为了处理异常并支持调试器, 本段将涉及到一下几个数据结构
struct RUNTIME_FUNCTION
Table-based exception handling requires a table entry for all functions that allocate stack space or call another function (for example, nonleaf functions). Function table entries have the format:
The RUNTIME_FUNCTION structure must be DWORD aligned in memory. All addresses are image relative, that is, they are 32-bit offsets from the starting address of the image that contains the function table entry. These entries are sorted, and put in the .pdata section of a PE32+ image. For dynamically generated functions [JIT compilers], the runtime to support these functions must either use RtlInstallFunctionTableCallback or RtlAddFunctionTable to provide this information to the operating system. Failure to do so will result in unreliable exception handling and debugging of processes.
The unwind data info structure is used to record the effects a function has on the stack pointer and where the nonvolatile registers are saved on the stack:
The UNWIND_INFO structure must be DWORD aligned in memory. Here's what each field means:
UNWIND_INFO结构体在内存中必须为四字节对齐, 以下是各成员的含义:
Version Version number of the unwind data, currently 1.
版本 展开数据的版本号, 当前为1
Flags Three flags are currently defined:
标志位 三个标志位现在被定义为:
类型
说明
翻译
UNW_FLAG_EHANDLER
The function has an exception handler that should be called when looking for functions that need to examine exceptions.
当寻找需要检查异常的函数时, 函数需要调用该异常处理程序
UNW_FLAG_UHANDLER
The function has a termination handler that should be called when unwinding an exception.
当展开一个异常时,函数应调用该终止处理程序。
UNW_FLAG_CHAININFO
This unwind info structure is not the primary one for the procedure. Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry. For informations, see Chained unwind info structures. If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared. Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info.
Size of prolog Length of the function prolog in bytes.
Prolog的大小 以字节为单位的 该函数的Prolog的长度
Count of unwind codes The number of slots in the unwind codes array. Some unwind codes, for example, UWOP_SAVE_NONVOL, require more than one slot in the array.
Frame register If nonzero, then the function uses a frame pointer (FP), and this field is the number of the nonvolatile register used as the frame pointer, using the same encoding for the operation info field of UNWIND_CODE nodes.
Frame register offset (scaled) If the frame register field is nonzero, this field is the scaled offset from RSP that is applied to the FP register when it's established. The actual FP register is set to RSP + 16 * this number, allowing offsets from 0 to 240. This offset permits pointing the FP register into the middle of the local stack allocation for dynamic stack frames, allowing better code density through shorter instructions (more instructions can use the 8-bit signed offset form).
Unwind codes array An array of items that explains the effect of the prolog on the nonvolatile registers and RSP. See the section on UNWIND_CODE for the meanings of individual items. For alignment purposes, this array always has an even number of entries, and the final entry is potentially unused. In that case, the array is one longer than indicated by the count of unwind codes field.
Address of exception handler An image-relative pointer to either the function's language-specific exception or termination handler, if flag UNW_FLAG_CHAININFO is clear and one of the flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER is set.
Language-specific handler data The function's language-specific exception handler data. The format of this data is unspecified and completely determined by the specific exception handler in use.
Chained Unwind Info If flag UNW_FLAG_CHAININFO is set then the UNWIND_INFO structure ends with three UWORDs. These UWORDs represent the RUNTIME_FUNCTION information for the function of the chained unwind.
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 this format:
展开码数组被用作记录 会影响寄存器和RSP的运算序列. 素组中的每一个项元素有以下的格式:
类型
说明
翻译
UBYTE
Offset in prolog
在Prolog中的偏移
UBYTE: 4
Unwind operation code
展开的指令代码
UBYTE: 4
Operation info
操作信息
The array is sorted by descending order of offset in the prolog.
该数组在Prolog中以降序排列
Offset in prolog 在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).
从Prolog起始处到执行了该运算的指令的结尾, 再 + 1 (也就是说, 是下一个指令的偏移)
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, that is, the 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 register was established. This equals the FP register minus the FP register offset (16 * the scaled frame register offset in the UNWIND_INFO). If an FP register is used, then any unwind code taking an offset must only be used after the FP register is established in the prolog.
For all opcodes except UWOP_SAVE_XMM128 and UWOP_SAVE_XMM128_FAR, the offset is always 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). 除了UWOP_SAVE_XMM128与UWOP_SAVE_XMM128_FAR操作码之外, 偏移值始终是 8 的倍数, 因为所有的跟栈值有关的数据都储存在8字节边界上(栈本身始终为16字节对齐). 对于短偏移值的字节码(小于512K), 节点中最后的USHORT除以8 则为其偏移值. 对于长偏移值的字节码(大于512K且小于4GB), 节点中最后的两个USHORT为偏移值(小端存储方式)
For the opcodes UWOP_SAVE_XMM128 and UWOP_SAVE_XMM128_FAR, the offset is always 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.
UWOP_PUSH_NONVOL (0) 1 node Push a nonvolatile integer register, decrementing RSP by 8. The operation info is the number of the register. 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. 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 code 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 code 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, one of these frames has been pushed on the stack:
If the operation info equals 1, then one of these frames has been pushed: 如果操作信息等于1, 那么下面的数据之一被压入占中
地址
对应数据
RSP+40
SS
RSP+32
Old RSP
RSP+24
EFLAGS
RSP+16
CS
RSP+8
RIP
RSP
Error code
This unwind code always appears 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.
The meaning of the operation info bits depends upon the operation code. To encode a general-purpose (integer) register, this 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
Chained unwind info structures 链式展开信息结构
If the UNW_FLAG_CHAININFO flag is set, then an unwind info structure is a secondary one, and the shared exception-handler/chained-info address field contains the primary unwind information. This sample code retrieves the primary unwind information, assuming that unwindInfo is the structure that has the UNW_FLAG_CHAININFO flag set.
Chained info is useful in two situations. First, it can be used for noncontiguous code segments. By using chained info, you can reduce the size of the required unwind information, because you do not have to duplicate the unwind codes array from the primary unwind info. 链式信息在以下两种情况下十分有用. 一, 它可以用于非连续的代码段. 通过使用链式信息, 你可以减少需要的展开信息的大小, 因为你不需要从主展开信息中复制展开码数组.
You can also use chained info to group volatile register saves. The compiler may delay saving some volatile registers until it is outside of the function entry prolog. You can record this by having primary unwind info for the portion of the function before the grouped code, and then setting up chained info with a non-zero size of prolog, where the unwind codes in the chained info reflect saves of the nonvolatile registers. In that case, the unwind codes are all instances of UWOP_SAVE_NONVOL. A grouping that saves nonvolatile registers by using a PUSH or modifies the RSP register by using an additional fixed stack allocation is not supported.
An UNWIND_INFO item that has UNW_FLAG_CHAININFO set can contain a RUNTIME_FUNCTION entry whose UNWIND_INFO item also has UNW_FLAG_CHAININFO set, sometimes called multiple shrink-wrapping. Eventually, the chained unwind info pointers arrive at an UNWIND_INFO item that has UNW_FLAG_CHAININFO cleared; this is the primary UNWIND_INFO item, which points to the actual procedure entry point. 一个设置了UNW_FLAG_CHAININFO标志位的 UNWIND_INFO 项可以保存一个 RUNTIME_FUNCTION 项. 该 UNWIND_INFO 同时设置了 UNW_FLAG_CHAININFO 标志位, 被称为 multiple shrink-wrapping. 当链式展开信息的指针到达一个设置了 UNWIND_INFO 项时, 该项的 UNW_FLAG_CHAININFO 标志位已经被清空. 这才是指向实际过程入口点的主 UNWIND_INFO 项,