彙編器註解¶
版權所有 (c) 2017-2019 Jiri Slaby
本文件描述了用於彙編中資料和程式碼註解的新宏。特別是,它包含關於 SYM_FUNC_START、SYM_FUNC_END、SYM_CODE_START 等宏的資訊。
基本原理¶
一些程式碼,如入口、跳板或啟動程式碼,需要用匯編語言編寫。與 C 語言一樣,此類程式碼被組織成函式並伴隨資料。標準彙編器不強制使用者精確地將這些片段標記為程式碼、資料,甚至不強制指定其長度。然而,彙編器為開發者提供了此類註解,以幫助偵錯程式進行彙編。除此之外,開發者還希望將某些函式標記為全域性,以便在其翻譯單元之外可見。
隨著時間的推移,Linux 核心已從各種專案(如 binutils)中引入宏,以方便此類註解。因此,出於歷史原因,開發者一直在彙編中使用 ENTRY、END、ENDPROC 和其他註解。由於缺乏文件,這些宏在某些位置的使用上下文相當錯誤。顯然,ENTRY 旨在表示全域性符號(無論是資料還是程式碼)的開始。END 過去用於標記資料的結束或具有非標準呼叫約定的特殊函式的結束。相比之下,ENDPROC 應該只註解標準函式的結束。
當這些宏被正確使用時,它們有助於彙編器生成一個漂亮的目標檔案,其中大小和型別都設定正確。例如,arch/x86/lib/putuser.S 的結果:
Num: Value Size Type Bind Vis Ndx Name
25: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 __put_user_1
29: 0000000000000030 37 FUNC GLOBAL DEFAULT 1 __put_user_2
32: 0000000000000060 36 FUNC GLOBAL DEFAULT 1 __put_user_4
35: 0000000000000090 37 FUNC GLOBAL DEFAULT 1 __put_user_8
這不僅對除錯目的很重要。當存在這樣正確註解的物件時,可以在其上執行工具以生成更多有用的資訊。特別是,在正確註解的物件上,可以執行 objtool 來檢查並在需要時修復物件。目前,objtool 可以報告函式中缺失的幀指標設定/銷燬。它還可以為大多數程式碼自動生成 ORC unwinder (ORC unwinder) 的註解。這兩點對於支援可靠的堆疊跟蹤尤其重要,而堆疊跟蹤又是核心即時補丁 (Livepatch) 所必需的。
注意事項和討論¶
正如人們可能意識到的,之前只有三個宏。這確實不足以涵蓋所有情況的組合:
標準/非標準函式
程式碼/資料
全域性/區域性符號
曾有過一次討論,結果決定不擴充套件當前的 ENTRY/END* 宏,而是引入全新的宏。
So how about using macro names that actually show the purpose, instead
of importing all the crappy, historic, essentially randomly chosen
debug symbol macro names from the binutils and older kernels?
宏描述¶
新宏以 SYM_ 字首開頭,可分為三大類:
SYM_FUNC_*-- 用於註解類 C 函式。這意味著函式遵循標準的 C 呼叫約定。例如,在 x86 上,這意味著堆疊在預定義的位置包含返回地址,並且函式可以以標準方式返回。當啟用幀指標時,幀指標的儲存/恢復也應分別在函式的開始/結束時進行。像
objtool這樣的檢查工具應確保標記的函式符合這些規則。這些工具還可以輕鬆地自動為這些函式註解除錯資訊(如ORC 資料)。SYM_CODE_*-- 使用特殊堆疊呼叫的特殊函式。無論是具有特殊堆疊內容的中斷處理程式、跳板還是啟動函式。檢查工具大多會忽略對這些函式的檢查。但一些除錯資訊仍然可以自動生成。為了正確的除錯資料,此程式碼需要開發者提供的
UNWIND_HINT_REGS等提示。SYM_DATA*-- 顯然是屬於.data段而不是.text段的資料。資料不包含指令,因此工具必須特殊處理它們:它們不應將位元組視為指令,也不應為其分配任何除錯資訊。
指令宏¶
本節涵蓋了上面列舉的 SYM_FUNC_* 和 SYM_CODE_*。
objtool 要求所有程式碼都必須包含在 ELF 符號中。帶有 .L 字首的符號名稱不會發出符號表條目。.L 字首的符號可以在程式碼區域內使用,但應避免透過 SYM_*_START/END 註解來表示程式碼範圍。
SYM_FUNC_START和SYM_FUNC_START_LOCAL應是最常使用的標記。它們用於具有標準呼叫約定的函式——全域性和區域性。與 C 語言中一樣,它們都將函式對齊到特定於架構的__ALIGN位元組。還有_NOALIGN變體,用於開發者不希望進行這種隱式對齊的特殊情況。SYM_FUNC_START_WEAK和SYM_FUNC_START_WEAK_NOALIGN標記也作為彙編器中與 C 語言中已知的weak 屬性相對應的形式提供。所有這些都應與
SYM_FUNC_END結合使用。首先,它將指令序列標記為函式,並計算其大小到生成的目標檔案中。其次,它還簡化了對此類目標檔案的檢查和處理,因為工具可以輕鬆找到精確的函式邊界。因此在大多數情況下,開發者應該像下面的例子那樣編寫,當然,宏之間需要有一些彙編指令:
SYM_FUNC_START(memset) ... asm insns ... SYM_FUNC_END(memset)事實上,這種註解與現在已棄用的
ENTRY和ENDPROC宏相對應。SYM_FUNC_ALIAS、SYM_FUNC_ALIAS_LOCAL和SYM_FUNC_ALIAS_WEAK可用於為一個函式定義多個名稱。典型的用法是:SYM_FUNC_START(__memset) ... asm insns ... SYN_FUNC_END(__memset) SYM_FUNC_ALIAS(memset, __memset)在此示例中,呼叫
__memset或memset會得到相同的結果,不同之處在於指令的除錯資訊只生成一次到目標檔案中——對於非ALIAS的情況。SYM_CODE_START和SYM_CODE_START_LOCAL應該只在特殊情況下使用——如果您知道自己在做什麼。這專門用於中斷處理程式和類似情況,其中呼叫約定不是 C 語言的。_NOALIGN變體也存在。用法與上面的FUNC類別相同:SYM_CODE_START_LOCAL(bad_put_user) ... asm insns ... SYM_CODE_END(bad_put_user)再次強調,每個
SYM_CODE_START*都應與SYM_CODE_END結合使用。在某種程度上,此類別與已棄用的
ENTRY和END宏相對應。只不過END還有其他幾個含義。SYM_INNER_LABEL*用於表示SYM_{CODE,FUNC}_START和SYM_{CODE,FUNC}_END內部的標籤。它們與 C 標籤非常相似,只是它們可以設定為全域性。用法示例:SYM_CODE_START(ftrace_caller) /* save_mcount_regs fills in first two parameters */ ... SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL) /* Load the ftrace_ops into the 3rd parameter */ ... SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL) call ftrace_stub ... retq SYM_CODE_END(ftrace_caller)
資料宏¶
與指令類似,彙編中也有一些宏用於描述資料。
SYM_DATA_START和SYM_DATA_START_LOCAL標記某些資料的開始,應與SYM_DATA_END或SYM_DATA_END_LABEL結合使用。後者還在末尾新增一個標籤,以便人們可以在以下示例中使用lstack和(區域性)lstack_end:SYM_DATA_START_LOCAL(lstack) .skip 4096 SYM_DATA_END_LABEL(lstack, SYM_L_LOCAL, lstack_end)SYM_DATA和SYM_DATA_LOCAL是簡單、通常為單行資料的變體:SYM_DATA(HEAP, .long rm_heap) SYM_DATA(heap_end, .long rm_stack)
最終,它們在內部擴充套件為帶有
SYM_DATA_END的SYM_DATA_START。
支援宏¶
所有上述宏最終都歸結為對 SYM_START、SYM_END 或 SYM_ENTRY 的某種呼叫。通常,開發者應避免使用這些宏。
此外,在上面的示例中,可以看到 SYM_L_LOCAL。還有 SYM_L_GLOBAL 和 SYM_L_WEAK。所有這些都旨在表示由它們標記的符號的連結型別。它們用於早期宏的 _LABEL 變體中,或用於 SYM_START 中。
覆蓋宏¶
架構也可以在其自己的 asm/linkage.h 中覆蓋任何宏,包括指定符號型別的宏(SYM_T_FUNC、SYM_T_OBJECT 和 SYM_T_NONE)。由於此檔案中描述的每個宏都由 #ifdef + #endif 包圍,因此只需在前面提到的依賴於架構的標頭檔案中以不同方式定義這些宏即可。