2012-02-24 128 views
1

我想了解如何通过Visual C++运行时在x64上实现C++异常处理。当ExceptionCode为STATUS_UNWIND_CONSOLIDATE时,RtlRestoreContext会执行什么操作?

在阅读了关于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在它的帧和上下文记录中指定的帧之间合并了调用帧,这隐藏了在回调函数中可能发生的任何异常处理中的帧”。

“合并框架和上下文记录中指定的框架之间的调用框架”是什么意思?这是如何“隐藏框架从回调函数中可能发生的任何异常处理”? “框架合并”意味着什么以及框架在哪里被整合?我们可以说一个C++ catch处理程序被RtlRestoreContext调用,它会抛出另一个异常 - 这个(重新)抛出的异常由某种SEH块保护吗?或者,这种框架整合业务会以某种方式照顾它?如果是,如何?

回答

0

如果按照功能,你会在这种情况下看意思是说你指的是,代码在传递给RtlRestoreContext的上下文中,在堆栈上建立假机器框架(相对于只填充了RIP和RSP的[r8])。然后它将原始上下文复制到机器框架下分配的堆栈空间中。

有关机器框架的更多信息(在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。

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

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的角度来看,callstack是OriginalContext - > RtlRestoreContext - > [用户提供的回调],两者之间没有任何内容。因此,如果堆栈被“解开”,ContextRecord描述的帧与RtlRestoreContext中的当前上下文之间的所有中间帧都被“遗忘”了。即帧被合并为一个单独的帧,作为单个功能展开。 因此,如果在ExceptionRecord中传递的回调函数内发生异常,那些中间帧中的任何异常处理程序都将被隐藏。正如博客指出的那样,这个功能在语言异常处理中非常有用。

正如MS文档指出的那样,中间栈帧的本地在调用回调之前不会被销毁,如果某个语言的异常对象被分配在该函数的堆栈帧上,这非常有用。

相关问题