C 異常的幕後11 閱讀CFI表

2021-09-19 18:09:18 字數 3993 閱讀 9118

nicolasbrailo

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

讀cfi資料是相當直截了當的,但有一些我們需要首先考慮的陷阱。實際上,兩個:

關於.gcc_except_table格式的文件非常少(實際上,我僅找到關於它的幾封郵件),因此我們將需要讀大量的源**,並反彙編來理解它。

雖然格式本身不是特別複雜,它使用乙個使得讀這個表不那麼直截了當的leb編碼。

就我所知,大多數dwarf代價像這樣編碼,使用leb格式

,對一頭霧水的程式設計師看起來不錯,在編碼任意長的整數時節省**空間。幸運地,在這裡我們可以耍個小花招:大多數時間裡,可以uint8_t來讀leb編碼的數字,因為我們不準備處理大的異常表或任何類似的東西。

備註:你可以從我的github repo

讓我們直接從彙編分析cfi資料,然後看我們是否能構建某些在我們的personality函式上讀它的東西。我將重新命名這些標記,使它們對我們更友好些。lsda有三部分,試著在下面找出它們:

1

2

3

4

.local_frame_entry:

.globl  __gxx_personality_v0

.section    .gcc_except_table,"a",@progbits

.align 4

這個非常簡單:它只是宣告我們將使用__gxx_personality_v0作為全域性物件,並讓鏈結器知道我們準備為.gcc_except_table節宣告內容的頭部。繼續:

1

2

3

4

5

6

7

8

9

10

11

.local_lsda_1:

# this declares the encoding type. we don't care.

.byte   0xff

# this specifies the landing pads start; if zero, the func's ptr is

# assumed (_unwind_getregionstart)

.byte   0

# length of the lsda area: check that llsdatt1 and llsdattd1 point to the

# end and the beginning of the lsda, respectively

.uleb128 .local_lsda_end - .local_lsda_call_site_table_header

現在這有更多一些資訊。這些標籤相當模糊,但確實遵循乙個模式。lsda表示語言特定資料區,前面的l表示本地,因此這是本地(對於編譯單元,.o檔案)語言特定資料區編號1。其他標記遵循類似的模式,但我還沒有時間把它們算出來。不過,我們確實不需要。

1

2

3

4

5

6

.local_lsda_call_site_table_header:

# encoding of items in the landing pad table. again, we don't care.

.byte   0x1.

# the length of the call site table (ie the landing pads)

.uleb128 .local_lsda_call_site_table_end - .local_lsda_call_site_table

另乙個單調的頭。繼續:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

.local_lsda_call_site_table:

.uleb128 .lehb0-.lfb1

.uleb128 .lehe0-.lehb0

.uleb128 .l8-.lfb1

.uleb128 0x1

.uleb128 .lehb1-.lfb1

.uleb128 .lehe1-.lehb1

.uleb128 0

.uleb128 0

.uleb128 .lehb2-.lfb1

.uleb128 .lehe2-.lehb2

.uleb128 .l9-.lfb1

.uleb128 0

.local_lsda_call_site_table_end:

這有趣得多,現在我們看到呼叫表本身。不管怎樣,在所有這些項裡,我們應該能夠找到我們的著陸墊。根據一些網頁,每個呼叫項的格式應該是:

1

2

3

4

5

6

7

8

9

10

11

12

13

struct lsda_call_site_entry ;

看起來了我們在正軌上,雖然我們還不知道為什麼在我們僅定義了乙個著陸墊時,有3個呼叫項。在任何情形裡,我們可以耍點小花招:通過檢視彙編,我們可以推斷cfi上的所有值將小於128,這意味著在leb編碼中,它們可以作為uchar來讀。這使得我們讀cfi的**大為容易,下次我們將看到如何在personality函式裡使用它。

C 異常的幕後(1)

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

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

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

C 異常的幕後3 取悅鏈結器的ABI

在我們理解異常的路程上,我們發現重擔在libstdc 裡完成,如c abi說明的那樣。閱讀了一些鏈結器錯誤,我們上次推斷要處理異常我們需要c abi的輔助 我們建立了乙個丟擲異常的c 程式,把它與乙個c程式鏈結,發現編譯器有時把我們的throw指令翻譯為某些現在呼叫幾個libstdc 函式的物件來實...