2012-02-24 82 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文檔指出的那樣,中間棧幀的本地在調用回調之前不會被銷燬,如果某個語言的異常對象被分配在該函數的堆棧幀上,這非常有用。

相關問題