簡體   English   中英

為什么64位Windows無法解除用戶內核用戶異常?

[英]Why can't 64-bit Windows unwind user-kernel-user exceptions?

為什么64位Windows在異常期間不能展開堆棧,如果堆棧跨越內核邊界 - 當32位Windows可以?

整個問題的背景來自:

消失的OnLoad異常 - x64中的用戶模式回調異常的情況

背景

在32位Windows中,如果我在我的用戶模式代碼中拋出異常,那是從內核模式代碼調用的,這是從我的用戶模式代碼調用的,例如:

User mode                     Kernel Mode
------------------            -------------------
CreateWindow(...);   ------>  NtCreateWindow(...)
                                   |
WindowProc   <---------------------+                                   

Windows中的結構化異常處理(SEH)可以展開堆棧,通過內核模式展開回到我的用戶代碼,在那里我可以處理異常,並且我看到有效的堆棧跟蹤。

但不是在64位Windows中

64位版本的Windows無法執行此操作:

由於復雜的原因,我們無法在64位操作系統 (amd64和IA64) 上傳播異常 自從Server 2003的第一個64位版本發布以來,情況一直如此。在x86上,情況並非如此 - 異常通過內核邊界傳播,並最終將幀移回

由於在這種情況下無法回溯可靠的堆棧跟蹤,因此必須做出決定:讓您看到非荒謬的異常,或者完全隱藏它:

當時的內核架構師決定采用保守的AppCompat友好方法 - 隱藏異常,並希望最好。

本文接着討論了所有64位Windows操作系統的表現如何:

  • Windows XP 64位
  • Windows Server 2003 64位
  • Windows Vista 64位
  • Windows Server 2008 64位

但是從Windows 7(和Windows Server 2008)開始,架構師改變了主意 - 有點像。 對於 64位應用程序(不是32位應用程序),它們(默認情況下)會停止抑制這些用戶內核用戶異常。 所以,默認情況下,在:

  • Windows 7 64位
  • Windows Server 2008

所有64位應用程序都會看到這些異常,他們從來沒有看到它們。

在Windows 7中,當本機x64應用程序以這種方式崩潰時,將通知程序兼容性助手 如果應用程序沒有Windows 7清單 ,我們會顯示一個對話框,告訴您PCA已應用了應用程序兼容性填充程序 這是什么意思? 這意味着,下次運行應用程序時,Windows將模擬Server 2003行為並使異常消失。 請記住,Server 2008 R2上不存在PCA,因此該建議不適用。

所以問題

問題是為什么 64位Windows無法通過內核轉換解除堆棧, 而32位版本的Windows可以?

唯一的提示是:

由於復雜的原因,我們無法在64位操作系統 (amd64和IA64) 上傳播異常

暗示是復雜的

我可能不理解這個解釋,因為我不是一個操作系統開發人員 - 但我想知道為什么會這樣做。


更新:停止抑制32位應用程序的修補程序

微軟發布了一個修復程序,使32位應用程序也不再有被抑制的異常:

KB976038:忽略從64位版本的Windows中運行的應用程序引發的異常

  • 在回調例程中引發的異常在用戶模式下運行。

在這種情況下,此異常不會導致應用程序崩潰。 相反,應用程序進入不一致狀態。 然后,應用程序拋出一個不同的異常並崩潰。

用戶模式回調函數通常是由內核模式組件調用的應用程序定義的函數。 用戶模式回調函數的示例是Windows過程和掛鈎過程。 Windows調用這些函數來處理Windows消息或處理Windows掛鈎事件。

然后,此修補程序可讓您阻止Windows全局使用異常:

 HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options DisableUserModeCallbackFilter: DWORD = 1 

或每次申請:

 HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\Notepad.exe DisableUserModeCallbackFilter: DWORD = 1 

在KB973460中的XP和Server 2003上也記錄了這種行為:


一個提示

我在調查使用xperf捕獲64位Windows上的堆棧跟蹤時發現了另一個提示:

堆棧走在Xperf

禁用分頁執行

要使跟蹤在64位Windows上運行,您需要設置DisablePagingExecutive注冊表項。 這告訴操作系統不要將內核模式驅動程序和系統代碼分頁到磁盤,這是使用xperf獲取64位調用堆棧的先決條件,因為64位堆棧遍歷取決於可執行映像中的元數據,在某些情況下xperf 堆棧遍歷代碼不允許觸摸分頁頁面 從提升的命令提示符運行以下命令將為您設置此注冊表項。

  REG ADD "HKLM\\System\\CurrentControlSet\\Control\\Session Manager\\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f 

設置此注冊表項后,您需要重新啟動系統,然后才能記錄調用堆棧。 設置此標志意味着Windows內核將更多頁面鎖定到RAM中,因此這可能會消耗大約10 MB的額外物理內存。

這給人的印象是,在64位Windows(並且僅在64位Windows中)中,不允許您遍歷內核堆棧,因為磁盤上可能存在頁面。

我是開發人員之前寫過這個Hotfix的loooooooong以及博客文章。 主要原因是,出於性能原因,當您轉換到內核空間時,並不總是捕獲完整的寄存器文件。

如果進行正常的系統調用,x64 應用程序二進制接口 (ABI)只需要保留非易失性寄存器 (類似於進行正常的函數調用)。 但是,正確解除異常需要您擁有所有寄存器,因此無法實現。 基本上,這是在關鍵場景中的perf(即可能每秒發生數千次的場景)與100%正確處理病態場景(崩潰)之間的選擇。

獎金閱讀

一個非常好的問題。

我可以給出一個暗示為什么跨內核用戶邊界“傳播”異常有些問題。

引用你的問題:

為什么64位Windows在異常期間不能展開堆棧,如果堆棧跨越內核邊界 - 當32位Windows可以?

原因很簡單:沒有“堆棧跨越內核邊界”這樣的東西。 調用內核模式函數絕不能與標准函數調用相比。 它實際上與調用堆棧無關。 您可能知道,內核模式內存在用戶模式下無法訪問。

調用內核模式函數(也稱為syscall )是通過觸發軟件中斷(或類似機制)來實現的。 用戶模式代碼將一些值放入寄存器(標識所需的內核模式服務)並調用CPU指令(例如sysenter ),該指令將CPU傳輸到內核模式並將控制傳遞給OS。

然后是一個處理請求的系統調用的內核模式代碼。 它在一個單獨的內核模式堆棧中運行(與用戶模式堆棧無關)。 處理完請求后 - 控件返回到用戶模式代碼。 根據特定的系統調用,用戶模式返回地址可以是調用內核模式事務的地址,也可以是不同的地址。

有時你會調用一個“中間”應該調用用戶模式調用的內核模式函數。 它可能看起來像一個由用戶內核用戶代碼組成的調用堆棧,但它只是一個仿真 在這種情況下,內核模式代碼將控件傳輸到用戶模式代碼,該代碼包裝您的用戶模式功能。 此包裝器代碼調用您的函數,並在其返回時立即觸發內核模式事務。

現在,如果用戶模式代碼“從內核模式調用”引發了異常 - 這就應該發生:

  1. 包裝器用戶模式代碼處理SEH異常(即停止其傳播,但尚未執行堆棧展開)。
  2. 將控件傳遞給內核模式(OS),就像在正常的程序流程中一樣。
  3. Kenrel模式代碼響應恰當。 它完成了所請求的服務。 根據是否存在用戶模式異常 - 處理可能不同。
  4. 返回用戶模式后,內核模式代碼可以指定是否存在嵌套異常。 在異常的情況下,堆棧不會恢復到其原始狀態(因為還沒有展開)。
  5. 用戶模式代碼檢查是否存在此類異常。 如果是 - 調用堆棧被偽造成包含嵌套的用戶模式調用,並且異常傳播。

因此跨越內核用戶邊界的異常是一種仿真 本機沒有這樣的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM