C 異常的幕後10 Unwind 與呼叫幀資訊

2021-09-18 01:18:48 字數 2735 閱讀 7591

nicolasbrailo

我們讓我們的小abi專案(鏈結

)能夠丟擲異常了,現在我們著力捕捉它們;上次我們實現了乙個能夠檢測並處理異常的personality函式,不過它仍然有點不完整:即使它能正確地通知棧回滾器它何時應該停止,但我們版本的__gxx_personality_v0不能執行catch塊裡的**。有人會說這總比coredump要好,但要成為有用的異常處理abi,仍然有長的路要走。我們能改進它嗎?

我們如何能告訴_unwind_我們的著陸墊在**,使得我們可以執行catch語句裡的**?回到abi規範

,有一些上下文管理函式可能對我們有用:

備註:你可以從我的github repo

讓我們通過gdb檢查這些函式。在我的機器上:

breakpoint 1, __gxx_personality_v0 (version=1, actions=6, exceptionclass=134515400, unwind_exception=0x804a060,

context=0xbffff0f0) at mycppabi.cpp:77const uint8_t* lsda = (const uint8_t*)_unwind_getlanguagespecificdata(context);uintptr_t ip = _unwind_getip(context) - 1;uintptr_t funcstart = _unwind_getregionstart(context);uintptr_t ipoffset = ip - funcstart;

檢查這些變數,看到_unwind_getregionstart確實指向當前棧幀(try_but_dont_catch),_unwind_getip是下乙個棧幀呼叫完成位置的ip。_unwind_getregionstart把我們指向異常第一次被丟擲的地方;解釋起來有點複雜,我們後面會用到它,不是現在。同樣,這裡我們沒有看到lsda,但我們可以推斷它在函式**後,因為_unwind_getlanguagespecificdata直接指向函式結尾:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

_unwind_getip = (void *) 0x804861d

_unwind_getregionstart = (void *) 0x8048612

_unwind_getlanguagespecificdata = (void *) 0x8048e3c

function pointer to try_but_dont_catch = 0x8048612

(gdb) disassemble /m try_but_dont_catch

dump of assembler code for function try_but_dont_catch():

10  void try_but_dont_catch() catch(fake_exception&)

16

17      printf("try_but_dont_catch handled the exception\n");

0x0804861e <+12>:  movl   $0x8048948,(%esp)

0x08048625 <+19>:  call   0x80484c0

18  }

0x0804862a <+24>:  add    $0x24,%esp

在_unwind_的幫助下,現在我們能夠獲得關於當前棧幀的足夠資訊來決定是否可以處理異常,以及我們應該如何處理它。在我們可以檢測我們希望的著陸墊前,需要更多一步:我們需要解析在函式末尾的cfi(呼叫幀資訊)。這是dwarf規範的部分,gdb也用於除錯目的,這不是容易實現的規範。就像對我們的abi那樣,我們把它維持在最小程度。

C 異常的幕後(1)

每個人都知道良好的異常處理是困難的。在異常 生命期 的每個層面,出現這種情況的原因有許多 編寫異常安全的 是困難的,異常可能從不期望的位置丟擲 雙關語 理解設計不良的異常架構是複雜的,因為幕後發生了許多巫術,它是慢的 因為不正確地丟擲異常可能導致呼叫不可原諒的std terminate,它是危險的。...

C 異常的幕後11 閱讀CFI表

nicolasbrailo 要從我們已經為我們的abi實現的personality函式裡正確處理異常,我們需要閱讀lsda 語言特定資料區 來了解哪個呼叫幀 即哪個函式 可以處理哪個異常,以及了解 可以找到著陸墊 catch塊 lsda是cfi格式的,我們將在本章裡學習如何讀它。讀cfi資料是相當直...

C 異常的幕後8 兩階段處理

nicolasbrailo 上一章以新增乙個 unwind 能夠呼叫的personality函式而結束。它沒做什麼,但它在那裡。我們已經實現的abi現在可以丟擲異常,捕捉也已經完成一半,但需要正確選擇catch塊 著陸墊 的personality函式目前有點傻。我們通過嘗試理解personality...