您可以使用 Android 11 以上版本提供的應用程式二進位介面 (ABI) 監控工具,穩定 Android 核心的內核心 ABI。這項工具會收集並比較現有核心二進位檔 (vmlinux
+ GKI 模組) 的 ABI 表示法。這些 ABI 代表是 .stg
檔案和符號清單。表示法提供檢視畫面的介面稱為「核心模組介面」(KMI)。您可以使用工具追蹤及減輕 KMI 變更的影響。
ABI 監控工具在 AOSP 中開發,並使用 STG (或 Android 13 以下版本的 libabigail
) 生成及比較表示法。
本頁說明工具、收集及分析 ABI 表示法的程序,以及如何使用這類表示法,確保核心內 ABI 的穩定性。本頁面也提供將變更內容貢獻給 Android 核心的相關資訊。
程序
分析核心的 ABI 需要多個步驟,其中大部分可以自動化:
下列操作說明適用於任何可使用支援的工具鍊 (例如預先建構的 Clang 工具鍊) 建構的核心。repo manifests
適用於所有 Android 通用核心分支版本和多個裝置專屬核心,可驗證您在建構核心發行版本以供分析時,是否使用正確的工具鍊。
符號清單
KMI 不包含核心中的所有符號,甚至不包含所有匯出的 30,000 多個符號。而是會在一組符號清單檔案中明確列出供應商模組可使用的符號,這些檔案會公開維護在核心樹狀結構中 (Android 15 以下版本為 gki/{ARCH}/symbols/*
或 android/abi_gki_{ARCH}_*
)。所有符號清單檔案中的所有符號聯集,定義了維持穩定的 KMI 符號集。符號清單檔案範例為 gki/aarch64/symbols/db845c
,其中宣告了 DragonBoard 845c 所需的符號。
只有符號清單中列出的符號,以及相關結構和定義,才屬於 KMI。如果找不到所需符號,可以發布符號清單的變更。新介面加入符號清單並成為 KMI 說明的一部分後,就會維持穩定狀態,且分支版本凍結後,不得從符號清單中移除或修改。
每個 Android Common Kernel (ACK) KMI 核心分支都有自己的符號清單。不同 KMI 核心分支之間不會嘗試提供 ABI 穩定性。舉例來說,android12-5.10
的 KMI 完全獨立於 android13-5.10
的 KMI。
ABI 工具會使用 KMI 符號清單,限制必須監控穩定性的介面。供應商應提交及更新自己的符號清單,以驗證所依賴的介面是否維持 ABI 相容性。舉例來說,如要查看 android16-6.12
核心的符號清單,請參閱 https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols
符號清單包含據報特定供應商或裝置所需的符號。工具使用的完整清單是所有 KMI 符號清單檔案的聯集。ABI 工具會判斷每個符號的詳細資料,包括函式簽章和巢狀資料結構。
KMI 凍結後,現有的 KMI 介面就無法再變更,不過,只要新增的符號不會影響現有 ABI 的穩定性,供應商隨時都能將符號新增至 KMI。只要 KMI 符號清單引用新加入的符號,這些符號就會維持穩定狀態。除非可以確認沒有任何裝置依附於該符號,否則不應從核心的清單中移除符號。
如要為裝置產生 KMI 符號清單,請按照「如何使用符號清單」一文中的操作說明進行。許多合作夥伴會為每個 ACK 提交一份符號清單,但這並非必要條件。 如果這樣有助於維護,您可以提交多份符號清單。
延長 KMI
KMI 符號和相關結構會維持穩定 (也就是說,如果核心的 KMI 已凍結,就無法接受會破壞穩定介面的變更),但 GKI 核心仍可擴充,因此在 KMI 凍結前,今年稍晚出貨的裝置不必定義所有依附元件。如要擴充 KMI,您可以為新匯出或現有匯出的核心函式新增符號,即使 KMI 已凍結也沒問題。如果新核心修補程式不會破壞 KMI,也可能會被接受。
關於 KMI 損壞
核心有來源,而二進位檔是從這些來源建構而來。
ABI 監控的 Kernel 分支包含目前 GKI ABI 的 ABI 代表 (以 .stg
檔案的形式)。建構二進位檔 (vmlinux
、Image
和任何 GKI 模組) 後,即可從二進位檔擷取 ABI 表示法。對核心來源檔案所做的任何變更,都可能影響二進位檔,進而影響擷取的 .stg
。ABI 遵循情況分析會比較已提交的 .stg
檔案與從建構構件中擷取的檔案,並在 Gerrit 中對變更設定 Lint-1 標籤 (如果發現語意差異)。
處理 ABI 中斷
舉例來說,下列修補程式會造成非常明顯的 ABI 損壞:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 42786e6364ef..e15f1d0f137b 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -657,6 +657,7 @@ struct mm_struct {
ANDROID_KABI_RESERVE(1);
} __randomize_layout;
+ int tickle_count;
/*
* The mm_cpumask needs to be at the end of mm_struct, because it
* is dynamically sized based on nr_cpu_ids.
套用這個修補程式後,如果您執行建構 ABI,工具會以非零錯誤碼結束,並回報類似以下的 ABI 差異:
function symbol 'struct block_device* I_BDEV(struct inode*)' changed
CRC changed from 0x8d400dbd to 0xabfc92ad
function symbol 'void* PDE_DATA(const struct inode*)' changed
CRC changed from 0xc3c38b5c to 0x7ad96c0d
function symbol 'void __ClearPageMovable(struct page*)' changed
CRC changed from 0xf489e5e8 to 0x92bd005e
... 4492 omitted; 4495 symbols have only CRC changes
type 'struct mm_struct' changed
byte size changed from 992 to 1000
member 'int tickle_count' was added
member 'unsigned long cpu_bitmap[0]' changed
offset changed by 64
在建構期間偵測到 ABI 差異
最常見的錯誤原因是驅動程式使用了核心中的新符號,但符號清單中沒有該符號。
如果符號未列在符號清單中,請先確認符號是否已透過 EXPORT_SYMBOL_GPL(symbol_name)
匯出,然後更新符號清單和 ABI 表示法。舉例來說,下列變更會將新的 Incremental FS 功能新增至 android-12-5.10
分支,包括更新符號清單和 ABI 表示法。
- 如需特徵變更範例,請參閱 aosp/1345659。
- 符號清單範例位於 aosp/1346742。
- ABI 表示法變更範例位於 aosp/1349377。
如果符號已匯出 (由您匯出或先前已匯出),但沒有其他驅動程式使用該符號,您可能會收到類似下列內容的建構錯誤。
Comparing the KMI and the symbol lists:
+ build/abi/compare_to_symbol_list out/$BRANCH/common/Module.symvers out/$BRANCH/common/abi_symbollist.raw
ERROR: Differences between ksymtab and symbol list detected!
Symbols missing from ksymtab:
Symbols missing from symbol list:
- simple_strtoull
如要解決這個問題,請更新核心和 ACK 中的 KMI 符號清單 (請參閱「更新 ABI 表示法」)。如需更新符號清單和 ACK 中 ABI 表示法的範例,請參閱 aosp/1367601。
解決核心 ABI 中斷問題
您可以重構程式碼,避免變更 ABI,或更新 ABI 表示法,處理核心 ABI 中斷問題。請參考下表,找出最適合您情況的做法。
圖 1. 解決 ABI 中斷問題
重構程式碼,避免 ABI 變更
請盡量避免修改現有 ABI。在許多情況下,您可以重構程式碼,移除影響 ABI 的變更。
重構結構欄位變更。如果變更會修改偵錯功能的 ABI,請在欄位 (結構體和來源參照中) 周圍新增
#ifdef
,並確保用於#ifdef
的CONFIG
已針對正式版 defconfig 和gki_defconfig
停用。如需瞭解如何將偵錯設定新增至結構體,且不會破壞 ABI 的範例,請參閱這個修補程式集。重構功能,避免變更核心核心。如果需要將新功能新增至 ACK,以支援合作夥伴模組,請嘗試重構變更的 ABI 部分,避免修改核心 ABI。如要瞭解如何使用現有核心 ABI 新增其他功能,而不變更核心 ABI,請參閱 aosp/1312213。
修正 Android Gerrit 上無效的 ABI
如果不是有意中斷核心 ABI,則需要使用 ABI 監控工具提供的指引進行調查。最常見的故障原因包括資料結構變更和相關符號 CRC 變更,或是因設定選項變更而導致上述情況。首先,請解決工具發現的問題。
您可以在本機重現 ABI 發現結果,請參閱「建構核心及其 ABI 表示法」。
關於 Lint-1 標籤
如果您將變更上傳至含有凍結或最終 KMI 的分支,變更必須通過 ABI 相容性分析,確保 ABI 代表的變更反映實際 ABI,且不含任何不相容項目 (符號移除或型別變更)。
這些 ABI 分析可能會設定 Lint-1 標籤,並封鎖變更提交,直到所有問題都解決或標籤遭到覆寫為止。
更新核心 ABI
如果無法避免修改 ABI,則必須將程式碼變更、ABI 表示法和符號清單套用至 ACK。如要讓 Lint 移除 -1,且不破壞 GKI 相容性,請按照下列步驟操作:
等待接收修補程式集的 Code-Review +2。
合併程式碼變更和 ABI 更新變更。
將 ABI 程式碼變更上傳至 ACK
更新 ACK ABI 的方式取決於所做的變更類型。
如果 ABI 變更與影響 CTS 或 VTS 測試的功能相關,通常可以將變更直接挑選至 ACK。舉例來說:
- 音訊功能需要 aosp/1289677。
- USB 運作需要 aosp/1295945。
如果 ABI 變更適用於可與 ACK 共用的功能,則該變更可直接選取至 ACK。舉例來說,下列變更不需要 CTS 或 VTS 測試,但可與 ACK 共用:
- aosp/1250412 是熱能功能變更。
- aosp/1288857
是
EXPORT_SYMBOL_GPL
變更。
如果 ABI 變更導入的新功能不需要納入 ACK,您可以使用存根將符號導入 ACK,詳情請參閱下一節。
使用 ACK 的存根
只有在核心核心變更對 ACK 沒有好處 (例如效能和電力變更) 時,才需要存根。以下清單詳細列出 ACK 中 GKI 的存根和部分 Cherry-pick 範例。
核心隔離功能存根 (aosp/1284493)。ACK 中不一定要有這些功能,但 ACK 中必須有這些符號,模組才能使用。
供應商模組的預留位置符號 (aosp/1288860)。
僅限 ABI 的每程序
mm
事件追蹤功能 Cherry-pick (aosp/1288454)。原始修補程式已透過 Cherry-pick 方式套用至 ACK,然後經過修剪,只保留解決task_struct
和mm_event_count
ABI 差異的必要變更。這個修補程式也會更新mm_event_type
列舉,以包含最終成員。部分熱結構 ABI 變更的 Cherry-pick,不只是新增 ABI 欄位。
修補程式 aosp/1255544 解決了合作夥伴核心和 ACK 之間的 ABI 差異。
修補程式 aosp/1291018 修正了先前修補程式的 GKI 測試期間發現的功能問題。 修正內容包括初始化感應器參數結構,以便向單一感應器註冊多個熱區。
CONFIG_NL80211_TESTMODE
ABI 異動 (aosp/1344321)。這個修補程式為 ABI 新增了必要的結構體變更,並確保額外欄位不會造成功能差異,讓合作夥伴能在生產核心中加入CONFIG_NL80211_TESTMODE
,同時維持 GKI 相容性。
在執行階段強制執行 KMI
GKI 核心會使用 TRIM_UNUSED_KSYMS=y
和 UNUSED_KSYMS_WHITELIST=<union
of all symbol lists>
設定選項,將匯出的符號 (例如使用 EXPORT_SYMBOL_GPL()
匯出的符號) 限制為符號清單中列出的符號。所有其他符號都不會匯出,且系統會拒絕載入需要未匯出符號的模組。這項限制會在建構時強制執行,並標示缺少的項目。
為進行開發,您可以使用不含符號修剪作業的 GKI 核心建構版本 (也就是說,可以使用所有通常匯出的符號)。如要尋找這些版本,請前往 ci.android.com 尋找 kernel_debug_aarch64
版本。
使用模組版本控管強制執行 KMI
通用核心映像檔 (GKI) 核心會使用模組版本控管 (CONFIG_MODVERSIONS
) 做為額外措施,在執行階段強制執行 KMI 相容性。如果模組的預期 KMI 與 vmlinux
KMI 不符,模組版本控管可能會在模組載入時導致循環冗餘檢查 (CRC) 不符失敗。舉例來說,以下是因符號 module_layout()
的 CRC 不符,而導致模組載入時發生的典型失敗:
init: Loading module /lib/modules/kernel/.../XXX.ko with args ""
XXX: disagrees about version of symbol module_layout
init: Failed to insmod '/lib/modules/kernel/.../XXX.ko' with args ''
模組版本管理功能用途
模組版本控管有下列好處:
模組版本控管會偵測資料結構瀏覽權限的變更。如果模組變更不透明的資料結構 (即不屬於 KMI 的資料結構),結構日後變更時就會中斷。
舉例來說,請考慮
struct device
中的fwnode
欄位。這個欄位「必須」對模組不透明,這樣模組才無法變更device->fw_node
的欄位,或對其大小做出假設。不過,如果模組包含
<linux/fwnode.h>
(直接或間接),則struct device
中的fwnode
欄位對該模組而言就不再是不透明。模組隨後可以變更device->fwnode->dev
或device->fwnode->ops
。這個情境會造成一些問題,如下所述:這可能會破壞核心核心程式碼對內部資料結構所做的假設。
如果日後的核心更新變更了
struct fwnode_handle
(fwnode
的資料型別),模組就無法再與新核心搭配運作。此外,由於模組直接操控內部資料結構,因此stgdiff
不會顯示任何差異,因為只有檢查二進位表示法無法擷取這些資料。
如果新核心不相容,且在稍後載入時,系統會將目前的模組視為 KMI 不相容。模組版本控管功能會新增執行階段檢查,避免意外載入與核心不相容的 KMI 模組。這項檢查可防止難以偵錯的執行階段問題和核心當機,這些問題可能源自 KMI 中未偵測到的不相容性。
啟用模組版本控管功能即可避免上述所有問題。
檢查 CRC 不符情形,不必啟動裝置
stgdiff
會比較並回報核心之間的 CRC 不符情形,以及其他 ABI 差異。
此外,啟用 CONFIG_MODVERSIONS
的完整核心建構作業會產生 Module.symvers
檔案,這是正常建構程序的一部分。這個檔案的每一行都代表核心 (vmlinux
) 和模組匯出的符號。每一行都包含 CRC 值、符號名稱、符號命名空間、匯出符號的 vmlinux
或模組名稱,以及匯出類型 (例如 EXPORT_SYMBOL
與 EXPORT_SYMBOL_GPL
)。
您可以比較 GKI 建構版本和您建構版本之間的 Module.symvers
檔案,檢查 vmlinux
匯出的符號是否有任何 CRC 差異。如果 vmlinux
匯出的任何符號有 CRC 值差異,且該符號是由您在裝置中載入的其中一個模組使用,則該模組不會載入。
如果您沒有所有建構構件,但有 GKI 核心和您核心的 vmlinux
檔案,可以在兩個核心上執行下列指令,比較特定符號的 CRC 值,並比較輸出內容:
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
舉例來說,下列指令會檢查 module_layout
符號的 CRC 值:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
解決 CRC 不符問題
載入模組時發生 CRC 不符問題,請按照下列步驟解決:
使用
--kbuild_symtypes
選項建構 GKI 核心和裝置核心,如下列指令所示:tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist
這個指令會為每個
.o
檔案產生.symtypes
檔案。詳情請參閱 Kleaf 中的KBUILD_SYMTYPES
。如果是 Android 13 以下版本,請在用於建構核心的指令前面加上
KBUILD_SYMTYPES=1
,建構 GKI 核心和裝置核心,如下列指令所示:KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
使用
build_abi.sh,
時,系統會隱含設定KBUILD_SYMTYPES=1
旗標。使用下列指令,找出匯出 CRC 不符符號的
.c
檔案:git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);
.c
檔案在 GKI 中有對應的.symtypes
檔案,以及裝置核心建構構件。使用下列指令找出.symtypes
檔案:cd bazel-bin/common/kernel_aarch64/symtypes ls -1 kernel/module/version.symtypes
在 Android 13 以下版本中,使用舊版建構指令碼時,位置可能為
out/$BRANCH/common
或out_abi/$BRANCH/common
。每個
.symtypes
檔案都是純文字檔案,包含類型和符號說明:每一行都採用
key description
格式,其中說明可以參照同一檔案中的其他鍵。[s|u|e|t]#foo
等按鍵是指[struct|union|enum|typedef] foo
。 例如:t#bool typedef _Bool bool
沒有
x#
前置字串的鍵只是符號名稱。例如:find_module s#module * find_module ( const char * )
比較這兩個檔案,並修正所有差異。
建議您在發生問題的變更之前和之後,分別產生 symtypes
。儲存所有檔案後,即可大量比較檔案。
例如:
for f in $(find good bad -name '*.symtypes' | sed -r 's;^(good|bad)/;;' | LANG=C sort -u); do
diff -N -U0 --label good/"$f" --label bad/"$f" <(LANG=C sort good/"$f") <(LANG=C sort bad/"$f")
done
否則,請只比較感興趣的特定檔案。
案例 1:資料類型瀏覽權限造成的差異
新的 #include
可將新的型別定義 (例如 struct foo
) 提取至來源檔案。在這些情況下,對應 .symtypes
檔案中的說明會從空白的 structure_type foo { }
變更為完整定義。
這會影響 .symtypes
檔案中所有符號的 CRC,這些符號的說明直接或間接依附於型別定義。
舉例來說,在核心的 include/linux/device.h
檔案中加入下列程式碼,會導致 CRC 不符,其中一個是 module_layout()
:
#include <linux/fwnode.h>
比較該符號的 module/version.symtypes
,會發現以下差異:
$ diff -u <GKI>/kernel/module/version.symtypes <your kernel>/kernel/module/version.symtypes
--- <GKI>/kernel/module/version.symtypes
+++ <your kernel>/kernel/module/version.symtypes
@@ -334,12 +334,15 @@
...
-s#fwnode_handle structure_type fwnode_handle { }
+s#fwnode_reference_args structure_type fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
...
如果 GKI 核心具有完整型別定義,但您的核心缺少該定義 (機率很低),請將最新的 Android Common Kernel 合併至您的核心,確保您使用的是最新的 GKI 核心基礎。
在大多數情況下,GKI 核心缺少 .symtypes
中的完整型別定義,但您的核心有這些定義,因為有額外的 #include
指令。
Android 16 以上版本的解決方案
請確認受影響的來源檔案包含 Android KABI 穩定性標頭:
#include <linux/android_kabi.h>
針對每個受影響的型別,在受影響的來源檔案中,於全域範圍新增 ANDROID_KABI_DECLONLY(name);
。
舉例來說,如果 symtypes
差異如下:
--- good/drivers/android/vendor_hooks.symtypes
+++ bad/drivers/android/vendor_hooks.symtypes
@@ -1051 +1051,2 @@
-s#ubuf_info structure_type ubuf_info { }
+s#ubuf_info structure_type ubuf_info { member pointer_type { const_type { s#ubuf_info_ops } } ops data_member_location(0) , member t#refcount_t refcnt data_member_location(8) , member t#u8 flags data_member_location(12) } byte_size(16)
+s#ubuf_info_ops structure_type ubuf_info_ops { member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } , formal_parameter t#bool ) -> base_type void } complete data_member_location(0) , member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } ) -> base_type int byte_size(4) encoding(5) } link_skb data_member_location(8) } byte_size(16)
問題在於 struct ubuf_info
現在在 symtypes
中有完整定義。解決方法是在 drivers/android/vendor_hooks.c
中新增一行:
ANDROID_KABI_DECLONLY(ubuf_info);
這會指示 gendwarfksyms
將具名型別視為檔案中未定義的型別。
更複雜的情況是,新的 #include
本身位於標頭檔案中。在這種情況下,您可能需要在來源檔案中分配不同的 ANDROID_KABI_DECLONLY
巨集調用集,間接引入額外的型別定義,因為部分型別定義可能已存在。
為方便閱讀,請將這類巨集調用放在來源檔案開頭附近。
Android 15 以下版本的解決方法
通常只要從 genksyms
隱藏新的 #include
即可修正問題。
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
否則,如要找出造成差異的 #include
,請按照下列步驟操作:
開啟定義符號或資料類型的標頭檔案,這些符號或資料類型有這個差異。例如,編輯
struct fwnode_handle
的include/linux/fwnode.h
。在標頭檔案頂端新增下列程式碼:
#ifdef CRC_CATCH #error "Included from here" #endif
在 CRC 不符的模組
.c
檔案中,於任何#include
行之前,新增下列程式碼做為第一行。#define CRC_CATCH 1
編譯模組。產生的建構時間錯誤會顯示導致 CRC 不符的標頭檔
#include
鏈結。例如:In file included from .../drivers/clk/XXX.c:16:` In file included from .../include/linux/of_device.h:5: In file included from .../include/linux/cpu.h:17: In file included from .../include/linux/node.h:18: .../include/linux/device.h:16:2: error: "Included from here" #error "Included from here"
這個
#include
鏈結中的其中一個連結是因核心變更而產生,但 GKI 核心中缺少該連結。
案例 2:因資料類型變更而出現差異
如果符號或資料類型的 CRC 不符並非因顯示狀態差異所致,而是因為資料類型本身發生實際變更 (新增、移除或變更)。
舉例來說,在核心中進行下列變更會導致多個 CRC 不符,因為許多符號會間接受到這類變更的影響:
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -259,7 +259,7 @@ struct iommu_ops {
void (*iotlb_sync)(struct iommu_domain *domain);
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
- dma_addr_t iova);
+ dma_addr_t iova, unsigned long trans_flag);
int (*add_device)(struct device *dev);
void (*remove_device)(struct device *dev);
struct iommu_group *(*device_group)(struct device *dev);
其中一個 CRC 不符的項目是「devm_of_platform_populate()
」。
如果比較該符號的 .symtypes
檔案,可能會看到類似下列內容:
$ diff -u <GKI>/drivers/of/platform.symtypes <your kernel>/drivers/of/platform.symtypes
--- <GKI>/drivers/of/platform.symtypes
+++ <your kernel>/drivers/of/platform.symtypes
@@ -399,7 +399,7 @@
...
-s#iommu_ops structure_type iommu_ops { ... ; t#phy
s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t ) ; int
( * add_device ) ( s#device * ) ; ...
+s#iommu_ops structure_type iommu_ops { ... ; t#phy
s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t , unsigned long ) ; int ( * add_device ) ( s#device * ) ; ...
如要找出變更的類型,請按照下列步驟操作:
在原始碼中找出符號的定義 (通常位於
.h
檔案中)。- 如要瞭解核心和 GKI 核心之間的符號差異,請執行下列指令來找出提交內容:
git blame
- 如要刪除符號 (在樹狀結構中刪除符號,同時也想在其他樹狀結構中刪除),請找出刪除該行的變更。在刪除行的樹狀結構中使用下列指令:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
查看傳回的提交清單,找出變更或刪除作業。您要找的應該是第一個提交。如果不是,請瀏覽清單,直到找到該提交內容為止。
找出提交內容後,請在核心中還原該內容,或更新該內容以抑制 CRC 變更,然後上傳至 ACK 並合併。 每個殘餘 ABI 中斷都需要經過安全審查,必要時可記錄允許的中斷。
偏好使用現有邊框間距
GKI 中的部分結構會經過填補,以便擴充,而不會中斷現有的供應商模組。舉例來說,如果上游提交 (例如) 將成員新增至這類結構,則可能可以變更該結構,改為使用部分填補。這項變更隨後會從 CRC 計算中隱藏。
標準化的自我記錄巨集 ANDROID_KABI_RESERVE
會保留 u64
價值的 (對齊) 空間。用來取代成員聲明。
例如:
struct data {
u64 handle;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
可使用填補內容,不會影響符號 CRC,ANDROID_KABI_USE
(或 ANDROID_KABI_USE2
或其他可能定義的變體)。
成員 sekret
可供使用,就像是直接宣告一樣,但巨集實際上會展開為包含 sekret
的匿名聯集成員,以及 gendwarfksyms
用於維持符號型別穩定性的項目。
struct data {
u64 handle;
ANDROID_KABI_USE(1, void *sekret);
ANDROID_KABI_RESERVE(2);
};
Android 16 以上版本的解決方案
CRC 是由 gendwarfksyms
計算,該工具使用 DWARF 偵錯資訊,因此支援 C 和 Rust 類型。解決方式會因變更類型而異。以下是幾個例子。
新增或修改列舉值
有時會新增列舉值,有時 MAX
或類似的列舉值也會受到影響。如果這些變更不會「逸出」GKI,或是我們能確定供應商模組不會在意這些值,那麼這些變更就是安全的。
例如:
enum outcome {
SUCCESS,
FAILURE,
RETRY,
+ TRY_HARDER,
OUTCOME_LIMIT
};
在全域範圍中透過巨集調用新增 TRY_HARDER
和變更 OUTCOME_LIMIT
,即可隱藏 CRC 計算:
ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);
為方便閱讀,請將這些項目放在 enum
定義之後。
新結構體成員佔用現有孔洞
由於對齊,urgent
和 scratch
之間會有未使用的位元組。
void *data;
bool urgent;
+ bool retry;
void *scratch;
新增 retry
不會影響現有的成員位移或結構大小。不過,這可能會影響符號 CRC 或 ABI 表示法,或兩者皆受影響。
這會將其從 CRC 計算中隱藏:
void *data;
bool urgent;
+ ANDROID_KABI_IGNORE(1, bool retry);
void *scratch_space;
成員 retry
可供使用,就像是直接宣告一樣,但巨集實際上會展開為包含 retry
的匿名聯集成員,以及 gendwarfksyms
用於維持符號型別穩定性的項目。
擴大結構,納入新成員
有時成員會加到結構的結尾。這不會影響現有成員的位移,也不會影響僅透過指標存取結構體的現有使用者。結構體的大小會影響 CRC,如要禁止這項變更,可以在全域範圍內額外叫用巨集,如下所示:
struct data {
u64 handle;
u64 counter;
ANDROID_KABI_IGNORE(1, void *sekret);
};
ANDROID_KABI_BYTE_SIZE(data, 16);
為了方便閱讀,請將這個函式放在 struct
定義後方。
對型別或符號型別的所有其他變更
有時可能會出現不屬於上述任一類別的變更,導致 CRC 變更無法使用先前的巨集抑制。
在這些情況下,您可以在全域範圍內呼叫 ANDROID_KABI_TYPE_STRING
,提供型別或符號的原始 symtypes
說明。
struct data {
/* extensive changes */
};
ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");
為方便閱讀,請將此項目放在型別或符號定義之後。
Android 15 以下版本的解決方法
類型和符號類型變更必須從 genksyms
隱藏。方法是使用 __GENKSYMS__
控制前處理作業。
任意程式碼轉換都可以用這種方式表示。
舉例來說,如要隱藏現有結構中的新成員,請按照下列步驟操作:
struct parcel {
void *data;
bool urgent;
#ifndef __GENKSYMS__
bool retry;
#endif
void *scratch_space;
};