第4章斷點和單步執行
斷點和單步執行是兩個經常使用的調試功能,也是調試器的核心功能。本章我們將介紹IA-32 CPU是如何支持斷點和單步執行功能的。前兩節將分別介紹軟件斷點和硬件斷點,第4.3節介紹用於實現單步執行功能的陷阱標志。在前三節的基礎上,第 4.4節將分析一個真實的調試器程序,看它是如何實現斷點和單步執行功能的。
4.1 軟件斷點
x86系列處理器從其第一代產品英特爾8086開始就提供了一條專門用來支持調試的指令,即INT 3。簡單地說,這條指令的目的就是使CPU中斷(break)到調試器,以供調試者對執行現場進行各種分析。當我們調試程序時,可以在可能有問題的地方插 入一條INT 3指令,使CPU執行到這一點時停下來。這便是軟件調試中經常用到的斷點(breakpoint)功能,因此INT 3指令又被稱為斷點指令。
4.1.1 感受INT 3
下面通過一個小實驗來感受一下INT 3指令的工作原理。在Visual C++ Studio 6.0(以下簡稱為VC6)中創建一個簡單的HelloWorld控制台程序HiInt3,然后在main()函數的開頭通過嵌入式匯編插入一條INT 3指令:
int main(INT argc, char* argv[]) |
當在VC環境中執行以上程序時,會得到圖4-1所示的對話框。點OK按鈕后,程序便會停在INT 3指令所在的位置。由此看來,我們剛剛插入的一行(_asm INT 3)相當於在那里設置了一個斷點。實際上,這也正是通過注入代碼手工設置斷點的方法,這種方法在調試某些特殊的程序時還非常有用。
![]() |
圖4-1 CPU遇到INT 3指令時會把執行權移交給調試器 |
此時打開反匯編窗口,可以看到內存地址00401028處確實是INT 3指令:
10: _asm INT 3; |
打開寄存器窗口,可以看到程序指針寄存器的值也是00401028。
EAX = CCCCCCCC EBX = 7FFDE000 ECX = 00000000 EDX = 00371588 |
根據我們在第3章中的介紹,斷點異常(INT 3)屬於陷阱類異常,當CPU產生異常時,其程序指針是指向導致異常的下一條指令的。但是,現在我們觀察到的結果卻是指向導致異常的這條指令的。這是為什 么呢?簡單地說,是操作系統為了支持調試對程序指針做了調整。我們將在后面揭曉答案。