展开过程

展开代码数组按降序排列。 发生异常时,操作系统将整个上下文存储在上下文记录中。 然后调用异常调度逻辑,该逻辑重复执行下列步骤以查找异常处理程序。

  1. 使用存储在上下文记录中的当前 RIP 来搜索 RUNTIME_FUNCTION 表项,该表项描述当前函数(在链式 UNWIND_INFO 项的情况下为函数部分)。

  2. 如果找不到任何函数表项,则异常处理程序在叶函数中,RSP 将直接对返回指针进行寻址。 将 [RSP] 处的返回指针存储在更新的上下文中,将模拟的 RSP 加 8,然后重复步骤 1。

  3. 如果找到了函数表项,则 RIP 可能位于以下三个区域:a) 在 Epilog 中;b) 在 Prolog 中;c) 在异常处理程序可能包含的代码中。

    • a) 如果 RIP 在 Epilog 中,则控制离开此函数,对此函数来说,可能没有与此异常关联的异常处理程序,并且 Epilog 的效果必须继续保持,以便计算调用方函数的上下文。 为了确定 RIP 是否在 Epilog 中,将对来自 RIP 的代码流进行检查。 如果该代码流可以匹配合法 Epilog 的结尾部分,则该代码流在 Epilog 中,会对 Epilog 的其余部分进行模拟,并且在处理每个指令时对上下文记录进行更新。 然后,重复步骤 1。

    • b) 如果 RIP 在 Prolog 中,则控制尚未进入此函数,对此函数来说,可能没有与此异常关联的异常处理程序,并且 Prolog 的效果必须撤消,以便计算调用方函数的上下文。 如果从函数开始到 RIP 的距离小于或等于在展开信息中编码的 Prolog 大小,则 RIP 在 Prolog 中。 展开 prolog 效果的方法如下:向前扫描整个展开代码数组,找到满足以下条件的第一项,该项的偏移量小于或等于 RIP 距函数开始处的偏移量,然后撤消展开代码数组中其余各项的效果。 然后,重复步骤 1。

    • c) 如果 RIP 不在 Prolog 或 Epilog 中,并且函数有一个异常处理程序(设置了 UNW_FLAG_EHANDLER),则调用语言特定的处理程序。 该处理程序扫描其数据并根据相应情况调用筛选函数, 并可以返回经过处理的异常或返回继续进行的搜索。 此外,该处理程序还可以直接对某个展开进行初始化。

  4. 如果语言特定的处理程序返回一个已处理状态,则执行过程将使用原始上下文记录继续进行。

  5. 如果没有语言特定的处理程序,或者该处理程序返回一个“继续搜索”状态,则必须将上下文记录展开为调用方的状态。 通过处理展开代码数组的所有元素并撤消每个元素的效果,可以完成此操作。 然后,重复步骤 1。

涉及链式展开信息时,仍然遵循这些基本步骤。 唯一的区别在于,当浏览展开代码数组以展开某个 Prolog 的效果时,一旦到达数组末尾,该 prolog 就被链接到父展开信息,从而可以浏览该处的整个展开代码数组。 此链接将一直保持,直至到达没有 UNW_CHAINED_INFO 标志的展开信息为止,从而完成展开代码数组的浏览。

最小的展开数据集为 8 字节。 这可能表示一个函数,该函数仅分配了小于或等于 128 字节的堆栈,并可能保存了一个非易失寄存器。 对于不具有展开代码的零长度 Prolog,这也可能表示其链式展开信息结构的大小。

请参见

参考

异常处理 (x64)