C++中支持使用try-catch的語法處理異常,防止程序崩潰。那麼編譯器是如何實現的呢?html
有以下測試代碼:cookie
int main(int argc,char** argv){ try{ throw "error"; } catch (char* err){ err = nullptr; } return 0; }
這裏直接在try中拋出異常,程序捕獲這個異常將錯誤信息傳給err,爲了使catch塊不被編譯器忽略,這裏隨便附一個值,順便看看這個nullptr是什麼。函數
編譯出的彙編代碼以下:測試
int main(int argc,char** argv){ //ebp用於定位函數局部變量,先保存ebp,而後移動到棧頂 00A91410 push ebp 00A91411 mov ebp,esp //數0xFFFFFFFF和地址0A94EF0壓棧,這個實際上是一個函數的地址(後面有說明) 00A91413 push 0FFFFFFFFh 00A91415 push 0A94EF0h //這裏出現了一個fs寄存器,取fs的0偏移,即獲得「指向SEH鏈指針」(後面說明) //SEH鏈指針壓棧,ecx壓棧 00A9141A mov eax,dword ptr fs:[00000000h] 00A91420 push eax 00A91421 push ecx //分配棧空間 00A91422 sub esp,0D8h //保護現場 00A91428 push ebx 00A91429 push esi 00A9142A push edi 00A9142B lea edi,[ebp-0E8h] //清理棧 00A91431 mov ecx,36h 00A91436 mov eax,0CCCCCCCCh 00A9143B rep stos dword ptr es:[edi] //這裏把ds:0xA99000(0x6DAAB763,應該是某dword型的標誌)的數據和ebp(棧頂)作異或,將結果保存 00A9143D mov eax,dword ptr ds:[00A99000h] 00A91442 xor eax,ebp 00A91444 push eax //這裏ebp-0c顯然是一個SEH鏈指針,由於它被保存到fs:0 00A91445 lea eax,[ebp-0Ch] 00A91448 mov dword ptr fs:[00000000h],eax //保存esp是爲了什麼? 00A9144E mov dword ptr [ebp-10h],esp try{ //ebp-4這個變量的值很關鍵,進入try就被清0 00A91451 mov dword ptr [ebp-4],0 throw "error"; //直接一個地址0A96858是靜態存儲區"error"字符串的首地址,保存到棧空間,臨時變量無疑 00A91458 mov dword ptr [ebp-0E4h],0A96858h //參數 0A98078 -- 第二個參數比較奇怪 00A91462 push 0A98078h //參數 &"error" 00A91467 lea eax,[ebp-0E4h] 00A9146D push eax //調用函數 至關於 __CxxThrowException("error",0x0A98078); //該函數從名字上能夠略知一二,因爲屢次調用比較複雜,因此就不分析裏面了 00A9146E call __CxxThrowException@8 (0A910FAh) } catch (char* err){ err = nullptr; //能夠看到nullptr就是0 00A91473 mov dword ptr [ebp-18h],0 } //標籤$LN7位置的指令地址作返回值,返回處理後可能會再次跳到$LN7,而將ebp-4這個變量取反 //可能意味着出現錯誤 00A9147A mov eax,0A91489h 00A9147F ret 00A91480 mov dword ptr [ebp-4],0FFFFFFFFh //去return 00A91487 jmp $LN7+7h (0A91490h) $LN7: 00A91489 mov dword ptr [ebp-4],0FFFFFFFFh return 0; 00A91490 xor eax,eax
這裏省略最後一個括弧的編譯結果,把重點放在try-catch塊。spa
下面對上面的一些地方進行詳細說明:.net
地址0A94EF0線程
__ehhandler$_main: 00A94EF0 mov edx,dword ptr [esp+8] 00A94EF4 lea eax,[edx+0Ch] 00A94EF7 mov ecx,dword ptr [edx-0ECh] 00A94EFD xor ecx,eax 00A94EFF call @__security_check_cookie@4 (0A9101Eh) 00A94F04 mov eax,0A9804Ch 00A94F09 jmp ___CxxFrameHandler3 (0A91190h) 00A94F0E int 3 00A94F0F int 3 00A94F10 int 3
2. fs寄存器和SEH鏈指針
關於fs寄存器的做用引用一個博客裏面的:code
FS寄存器指向當前活動線程的TEB結構(線程結構)htm
偏移 說明
000 指向SEH鏈指針
004 線程堆棧頂部
008 線程堆棧底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在內存中的鏡像地址
020 進程PID
024 線程ID
02C 指向線程局部存儲指針
030 PEB結構地址(進程結構)
034 上個錯誤號
http://blog.sina.com.cn/s/blog_a5ece79401016os9.html
SEH鏈:
(structured exception handling) 是一種處理程序異常的機制,當程序異常 (例如除零異常,非法存取異常,等等) 發生的時候,系統便會把執行位置切換到thread 的 exception handler。
更多關於SEH機制能夠參考: