1

我试图了解 Visual C++ 运行时如何在 x64 上实现 C++ 异常处理。

在阅读关于在 x64 上实现 SEH 的 Nynaeve 博客http://www.nynaeve.net/?p=110后,似乎 RtlUnwindEx 调用 RtlRestoreContext 并将 ExceptionCode 设置为 STATUS_UNWIND_CONSOLIDATE 以展开框架合并。

我不完全清楚的是 RtlRestoreContext 做了什么?MSDN 在http://msdn.microsoft.com/en-us/site/ms680605声明“RtlRestoreContext 在调用回调函数之前将其框架和上下文记录中指定的框架之间的调用框架合并。这隐藏了任何异常的框架回调函数中可能发生的处理。”

“在其框架和上下文记录中指定的框架之间合并调用框架”是什么意思?这如何“隐藏回调函数中可能发生的任何异常处理的帧”?“框架整合”是什么意思,整合的框架到底在哪里?

假设 RtlRestoreContext 将调用 C++ 捕获处理程序,它会引发另一个异常 - (重新)抛出的异常是否受某种 SEH 块的保护?或者,这个框架整合业务以某种方式处理它?如果是,如何?

4

2 回答 2

1

如果您遵循该函数,您将看到在您所指的情况下,代码从传递给 RtlRestoreContext的原始上下文在堆栈上设置了一个假机器帧(相对于 [r8],仅填充了 RIP 和 RSP)。然后它将原始上下文复制到分配在机器框架下方的堆栈空间中。

有关机器框架(在 UWOP_PUSH_MACHFRAME 下)的更多信息,请参见http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx

0:004> u ntdll!RtlRestoreContext+0x296
00000000`771f0c05 83ec30          sub     esp,30h
00000000`771f0c08 4c8bc4          mov     r8,rsp
00000000`771f0c0b 4881ecd0040000  sub     rsp,4D0h
00000000`771f0c12 488bf1          mov     rsi,rcx
00000000`771f0c15 488bfc          mov     rdi,rsp
00000000`771f0c18 b99a000000      mov     ecx,9Ah
00000000`771f0c1d f348a5          rep movs qword ptr [rdi],qword ptr [rsi]
00000000`771f0c20 488b842498000000 mov     rax,qword ptr [rsp+98h]
0:004> u
ntdll!RtlRestoreContext+0x2ba:
00000000`771f0c28 49894018        mov     qword ptr [r8+18h],rax
00000000`771f0c2c 488b8424f8000000 mov     rax,qword ptr [rsp+0F8h]
00000000`771f0c34 498900          mov     qword ptr [r8],rax
00000000`771f0c37 488bca          mov     rcx,rdx
00000000`771f0c3a eb12            jmp     ntdll!RcFrameConsolidation (00000000`771f0c4e)

代码跳转到博客正在讨论的伪函数 NTDLL!RcFrameConsolidation。

如果我们检查这个函数的函数表和 UNWIND 条目,我们会看到它包含对应于假堆栈帧设置的元数据:

0:004>.fnent ntdll!rcframeconsolidation

...snip...

Unwind info at 00000000`772c8e0c, 52 bytes
  version 1, flags 0, prolog 0, codes 27
  00: offs 0, unwind op 8, op info f    UWOP_SAVE_XMM128 FrameOffset: 290 reg: xmm15.
  02: offs 0, unwind op 8, op info e    UWOP_SAVE_XMM128 FrameOffset: 280 reg: xmm14.
  04: offs 0, unwind op 8, op info d    UWOP_SAVE_XMM128 FrameOffset: 270 reg: xmm13.
  06: offs 0, unwind op 8, op info c    UWOP_SAVE_XMM128 FrameOffset: 260 reg: xmm12.
  08: offs 0, unwind op 8, op info b    UWOP_SAVE_XMM128 FrameOffset: 250 reg: xmm11.
  0a: offs 0, unwind op 8, op info a    UWOP_SAVE_XMM128 FrameOffset: 240 reg: xmm10.
  0c: offs 0, unwind op 8, op info 9    UWOP_SAVE_XMM128 FrameOffset: 230 reg: xmm9.
  0e: offs 0, unwind op 8, op info 8    UWOP_SAVE_XMM128 FrameOffset: 220 reg: xmm8.
  10: offs 0, unwind op 8, op info 7    UWOP_SAVE_XMM128 FrameOffset: 210 reg: xmm7.
  12: offs 0, unwind op 8, op info 6    UWOP_SAVE_XMM128 FrameOffset: 200 reg: xmm6.
  14: offs 0, unwind op 4, op info f    UWOP_SAVE_NONVOL FrameOffset: f0 reg: r15.
  16: offs 0, unwind op 4, op info e    UWOP_SAVE_NONVOL FrameOffset: e8 reg: r14.
  18: offs 0, unwind op 4, op info d    UWOP_SAVE_NONVOL FrameOffset: e0 reg: r13.
  1a: offs 0, unwind op 4, op info c    UWOP_SAVE_NONVOL FrameOffset: d8 reg: r12.
  1c: offs 0, unwind op 4, op info 7    UWOP_SAVE_NONVOL FrameOffset: b0 reg: rdi.
  1e: offs 0, unwind op 4, op info 6    UWOP_SAVE_NONVOL FrameOffset: a8 reg: rsi.
  20: offs 0, unwind op 4, op info 5    UWOP_SAVE_NONVOL FrameOffset: a0 reg: rbp.
  22: offs 0, unwind op 4, op info 3    UWOP_SAVE_NONVOL FrameOffset: 90 reg: rbx.
  24: offs 0, unwind op 1, op info 0    UWOP_ALLOC_LARGE FrameOffset: 4d0.
  26: offs 0, unwind op a, op info 0    UWOP_PUSH_MACHFRAME.

这样做的效果是“欺骗”VirtualUnwindEx/异常处理代码,使其认为 ContextRecord 描述的函数是 RtlRestoreContext 的直接调用者。

从 VirtualUnwind 的角度来看,调用堆栈是 OriginalContext -> RtlRestoreContext -> [用户提供的回调],中间没有任何内容。

因此,如果堆栈被“展开”,则 ContextRecord 描述的帧和 RtlRestoreContext 中的当前上下文之间的所有中间帧都被“忘记”。即框架已被合并为一个单一的框架,作为一个单一的功能展开。因此,如果在 ExceptionRecord 中传递的回调函数内部发生异常,则这些中间帧中的任何异常处理程序都将被隐藏。正如博客所指出的,此功能对于语言异常处理非常有用。

正如 MS 文档还指出的那样,中间堆栈帧的局部变量在调用回调之前不会被破坏,如果在该函数的堆栈帧上分配了某种语言的异常对象,这很有用。

于 2014-08-25T13:41:10.357 回答
0

图 6 从http://www.microsoft.com/msj/0197/exception/exception.aspx中的异常展开

可能这就是“整合”的意思

于 2012-04-25T14:24:21.663 回答