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 函式的物件來實...