應用程式二進位檔介面 (ABI) 穩定性 僅針對架構更新,因為供應商模組可能會依附於供應商原生模組 位於系統分區的 Development Kit (VNDK) 共用程式庫。 在 Android 版本中,新建構的 VNDK 共用程式庫必須 與先前發布的 VNDK 共用程式庫相容,以便支援供應商模組 且不需重新編譯和執行階段錯誤即可使用這些程式庫。 在 Android 版本之間,VNDK 程式庫可以變更,且沒有 ABI。 保證。
為確保 ABI 相容性,Android 9 包含以下 設定標頭 ABI 檢查工具,如以下各節所述。
關於 VNDK 與 ABI 法規遵循
VNDK 是一組限制性的程式庫,供應商模組可以連結這些程式庫, 啟用純架構更新ABI 法規遵循是指 新版共用程式庫的功能 動態連結模組 (也就是說,如同舊版的 程式庫)。
關於匯出的符號
「匯出符號」 (也稱為「全域符號」) 是指 滿足以下所有條件的符號:
- 由共用資料庫的公開標頭匯出。
- 顯示在
.so
檔案的.dynsym
資料表中 與共用資料庫對應的 - 具有 WEAK 或 GLOBAL 繫結。
- 可見性為「預設」或「PROTECTED」。
- 版面索引不是「UNDEFINED」(未定義)。
- 類型為 FUNC 或 OBJECT。
共用資料庫的公開標頭會定義為標頭
提供給其他程式庫/二進位檔
export_include_dirs
、export_header_lib_headers
、
export_static_lib_headers
,
export_shared_lib_headers
和
Android.bp
中的 export_generated_headers
個屬性
每個共用程式庫的模組定義。
關於可連線類型
可連線類型是任何內建或使用者定義類型,
可透過匯出的符號直接或間接聯絡,「以及」
通過公開標頭例如,libfoo.so
具有函式
Foo
,這是已匯出的符號,位於
.dynsym
資料表。libfoo.so
程式庫包含
包括:
foo_exported.h | foo.private.h |
---|---|
typedef struct foo_private foo_private_t; typedef struct foo { int m1; int *m2; foo_private_t *mPfoo; } foo_t; typedef struct bar { foo_t mfoo; } bar_t; bool Foo(int id, bar_t *bar_ptr); |
typedef struct foo_private { int m1; float mbar; } foo_private_t; |
Android.bp |
---|
cc_library { name : libfoo, vendor_available: true, vndk { enabled : true, } srcs : ["src/*.cpp"], export_include_dirs : [ "exported" ], } |
.dynsym 表格 | |||||||
---|---|---|---|---|---|---|---|
Num
|
Value
|
Size
|
Type
|
Bind
|
Vis
|
Ndx
|
Name
|
1
|
0
|
0
|
FUNC
|
GLOB
|
DEF
|
UND
|
dlerror@libc
|
2
|
1ce0
|
20
|
FUNC
|
GLOB
|
DEF
|
12
|
Foo
|
查看 Foo
時,可直接/間接可存取的類型包括:
類型 | 說明 |
---|---|
bool
|
Foo 的傳回類型。
|
int
|
第一個 Foo 參數的類型。
|
bar_t *
|
第二個 Foo 參數的類型。根據「bar_t * 」的規定,
bar_t 是透過 foo_exported.h 匯出。
bar_t 包含類型為 mfoo 的成員
foo_t (透過 foo_exported.h 匯出)
這會導致匯出更多類型:
不過,由於 foo_private_t 無法使用,因此無法連上
已透過 foo_exported.h 匯出。(foo_private_t * )。
不透明,因此可以對 foo_private_t 進行變更)。
|
可針對可透過基礎類別可連線的類型提供類似的解釋 指定碼和範本參數
確保符合 ABI 規定
針對標示的程式庫,必須確保其符合 ABI 規定
vendor_available: true
和vndk.enabled: true
對應的 Android.bp
檔案。例如:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
如果是以匯出函式直接或間接存取的資料類型, 下列程式庫變更都會歸類為破壞性 ABI 的內容:
資料類型 | 說明 |
---|---|
結構與類別 |
|
聯合工會 |
|
列舉 |
|
全域符號 |
|
* 公開和私人成員函式都必須 因此無法變更或移除,因為公開內嵌函式可能參照 私人成員函式。對私人成員函式的符號參照可以 會保留在呼叫端二進位檔中變更或移除私人成員函式 可能會導致回溯不相容的二進位檔。
** 至公開或私人資料成員的偏移量不得為 因為內嵌函式可參照 函式主體。變更資料成員位移可能會導致 回溯不相容的二進位檔。
*** 這些項目不會影響記憶體配置 語意差異導致程式庫無法擷取文字 正常運作。
使用 ABI 法規遵循工具
建構 VNDK 程式庫時,會比較程式庫的 ABI 與 所建構 VNDK 版本的對應 ABI 參考資料。參考資料 ABI 傾印位於:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
以在 API 級別 27 建構 x86 的 libfoo
時,
系統會將 libfoo
推測的 ABI 與參考以下的參考資料進行比較:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
ABI 中斷錯誤
在 ABI 故障時,建構記錄會顯示警告類型和警示類型
abi-diff 報表的路徑。舉例來說,如果 libbinder
的 ABI
不相容的變更時,建構系統會擲回錯誤訊息
類似這樣:
***************************************************** error: VNDK library: libbinder.so's ABI has INCOMPATIBLE CHANGES Please check compatibility report at: out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_arm64_armv8-a_cortex-a73_vendor_shared/libbinder.so.abidiff ****************************************************** ---- Please update abi references by running platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder ----
建構 VNDK 程式庫 ABI 檢查
建構 VNDK 程式庫時:
header-abi-dumper
會處理編譯為 建立 VNDK 程式庫 (程式庫本身的來源檔案及來源檔案) 從靜態遞移依附元件繼承) 以產生 分別對應到各個來源的.sdump
個檔案。
圖 1.建立 .sdump
檔案header-abi-linker
接著會處理.sdump
檔案 (使用提供給檔案的版本指令碼,或是.so
檔案),並產生.lsdump
檔案,其中記錄了與共用資料庫相關的所有 ABI 資訊。
圖 2.建立 .lsdump
檔案header-abi-diff
比較.lsdump
內含參考.lsdump
參考資料的檔案,以便製作差異比較報表 概述這兩種程式庫的 ABI 的差異。
圖 3.建立差異比較報表
標頭 -abi-dumper
header-abi-dumper
工具剖析 C/C++ 來源檔案並轉儲
從來源檔案推斷至中繼檔案中的 ABI。建構作業
系統在所有已編譯的來源檔案上執行 header-abi-dumper
也建構了程式庫,內含來自轉換程序的來源檔案
依附元件
輸入裝置 |
|
---|---|
輸出 | 說明來源檔案 ABI 的檔案 (例如
foo.sdump 代表 foo.cpp 的 ABI)。
|
目前 .sdump
檔案採用 JSON 格式,但尚未採用
保證日後的版本可穩定運作因此,.sdump
檔案格式應該視為建構系統實作詳細資料。
舉例來說,libfoo.so
包含下列來源檔案
foo.cpp
:
#include <stdio.h> #include <foo_exported.h> bool Foo(int id, bar_t *bar_ptr) { if (id > 0 && bar_ptr->mfoo.m1 > 0) { return true; } return false; }
您可以使用 header-abi-dumper
產生中繼資訊
代表來源檔案提供的 ABI 的 .sdump
檔案
使用:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
這個指令會指示 header-abi-dumper
剖析
foo.cpp
與編譯器旗標 (--
後方) 搭配使用,以及
輸出由公開標頭匯出的 ABI 資訊
exported
目錄。以下是
foo.sdump
已產生
header-abi-dumper
:
{ "array_types" : [], "builtin_types" : [ { "alignment" : 4, "is_integral" : true, "linker_set_key" : "_ZTIi", "name" : "int", "referenced_type" : "_ZTIi", "self_type" : "_ZTIi", "size" : 4 } ], "elf_functions" : [], "elf_objects" : [], "enum_types" : [], "function_types" : [], "functions" : [ { "function_name" : "FooBad", "linker_set_key" : "_Z6FooBadiP3foo", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3foo" } ], "return_type" : "_ZTI3bar", "source_file" : "exported/foo_exported.h" } ], "global_vars" : [], "lvalue_reference_types" : [], "pointer_types" : [ { "alignment" : 8, "linker_set_key" : "_ZTIP11foo_private", "name" : "foo_private *", "referenced_type" : "_ZTI11foo_private", "self_type" : "_ZTIP11foo_private", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3foo", "name" : "foo *", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTIP3foo", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIPi", "name" : "int *", "referenced_type" : "_ZTIi", "self_type" : "_ZTIPi", "size" : 8, "source_file" : "exported/foo_exported.h" } ], "qualified_types" : [], "record_types" : [ { "alignment" : 8, "fields" : [ { "field_name" : "mfoo", "referenced_type" : "_ZTI3foo" } ], "linker_set_key" : "_ZTI3bar", "name" : "bar", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTI3bar", "size" : 24, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "fields" : [ { "field_name" : "m1", "referenced_type" : "_ZTIi" }, { "field_name" : "m2", "field_offset" : 64, "referenced_type" : "_ZTIPi" }, { "field_name" : "mPfoo", "field_offset" : 128, "referenced_type" : "_ZTIP11foo_private" } ], "linker_set_key" : "_ZTI3foo", "name" : "foo", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTI3foo", "size" : 24, "source_file" : "exported/foo_exported.h" } ], "rvalue_reference_types" : [] }
foo.sdump
包含來源檔案匯出的 ABI 資訊
foo.cpp
和公開標頭
record_types
。參照結構體、聯集或定義的類別 。每個記錄類型都有欄位相關資訊 大小、存取指定碼、所定義的標頭檔案 屬性。pointer_types
。直接/間接參照指標類型 匯出的記錄/函式中參照的公開記錄/函式,以及 以及指標指向的類型 (透過referenced_type
傳遞) 欄位)。type_info
)。系統記錄類似資訊時,都會留存在 符合條件的類型、內建 C/C++ 型別、陣列的.sdump
檔案 type,以及 lvalue 和 rvalue 參照類型。這類資訊 遞迴性差異functions
。代表由公開標頭匯出的函式。 同時也會提供函式的破壞名稱、傳回類型、 參數類型、存取指定碼以及其他屬性
頁首-abi-linker
header-abi-linker
工具會接收
以 header-abi-dumper
做為輸入內容,然後連結這些檔案:
輸入裝置 |
|
---|---|
輸出 | 說明共用程式庫 ABI 的檔案 (例如
libfoo.so.lsdump 代表 libfoo 的 ABI)。
|
這項工具會合併所有已取得的中繼檔案中的類型圖表
考量到使用者定義的
同一個完整名稱的翻譯單位
不同) 翻譯單位的差異接著,工具會剖析
版本指令碼或共用資料庫的 .dynsym
表格
(.so
檔案) 建立匯出符號的清單。
例如,libfoo
包含 foo.cpp
,
bar.cpp
。header-abi-linker
可用於叫用
建立 libfoo
的完整連結 ABI 轉儲,如下所示:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
libfoo.so.lsdump
中的指令輸出範例:
{ "array_types" : [], "builtin_types" : [ { "alignment" : 1, "is_integral" : true, "is_unsigned" : true, "linker_set_key" : "_ZTIb", "name" : "bool", "referenced_type" : "_ZTIb", "self_type" : "_ZTIb", "size" : 1 }, { "alignment" : 4, "is_integral" : true, "linker_set_key" : "_ZTIi", "name" : "int", "referenced_type" : "_ZTIi", "self_type" : "_ZTIi", "size" : 4 } ], "elf_functions" : [ { "name" : "_Z3FooiP3bar" }, { "name" : "_Z6FooBadiP3foo" } ], "elf_objects" : [], "enum_types" : [], "function_types" : [], "functions" : [ { "function_name" : "Foo", "linker_set_key" : "_Z3FooiP3bar", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3bar" } ], "return_type" : "_ZTIb", "source_file" : "exported/foo_exported.h" }, { "function_name" : "FooBad", "linker_set_key" : "_Z6FooBadiP3foo", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3foo" } ], "return_type" : "_ZTI3bar", "source_file" : "exported/foo_exported.h" } ], "global_vars" : [], "lvalue_reference_types" : [], "pointer_types" : [ { "alignment" : 8, "linker_set_key" : "_ZTIP11foo_private", "name" : "foo_private *", "referenced_type" : "_ZTI11foo_private", "self_type" : "_ZTIP11foo_private", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3bar", "name" : "bar *", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTIP3bar", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3foo", "name" : "foo *", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTIP3foo", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIPi", "name" : "int *", "referenced_type" : "_ZTIi", "self_type" : "_ZTIPi", "size" : 8, "source_file" : "exported/foo_exported.h" } ], "qualified_types" : [], "record_types" : [ { "alignment" : 8, "fields" : [ { "field_name" : "mfoo", "referenced_type" : "_ZTI3foo" } ], "linker_set_key" : "_ZTI3bar", "name" : "bar", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTI3bar", "size" : 24, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "fields" : [ { "field_name" : "m1", "referenced_type" : "_ZTIi" }, { "field_name" : "m2", "field_offset" : 64, "referenced_type" : "_ZTIPi" }, { "field_name" : "mPfoo", "field_offset" : 128, "referenced_type" : "_ZTIP11foo_private" } ], "linker_set_key" : "_ZTI3foo", "name" : "foo", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTI3foo", "size" : 24, "source_file" : "exported/foo_exported.h" } ], "rvalue_reference_types" : [] }
header-abi-linker
工具:
- 連結提供的
.sdump
檔案 (foo.sdump
和bar.sdump
),篩除不包含在 和exported
目錄中的標頭。 - 剖析
libfoo.so
,並收集符號相關資訊 會透過.dynsym
資料表匯出。 - 加入
_Z3FooiP3bar
和_Z6FooBadiP3foo
。
libfoo.so.lsdump
是下列最終產生的 ABI 轉儲
libfoo.so
。
頁首-abi-diff
header-abi-diff
工具會比較兩個 .lsdump
檔案
會呈現兩個程式庫的 ABI 並產生差異比較表,說明
計算差異
輸入裝置 |
|
---|---|
輸出 | 差異比較報表:說明兩者之間提供的 ABI 差異 共用程式庫 |
ABI 差異檔案位於 protobuf 文字格式。格式可能會變動 。
例如,有兩個版本
libfoo
:libfoo_old.so
和
libfoo_new.so
。libfoo_new.so
後
bar_t
,您將mfoo
類型從
foo_t
到 foo_t *
。由於 bar_t
是
可連線的類型,則應將此標記為 ABI 破壞性變更。
header-abi-diff
。
執行 header-abi-diff
:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
libfoo.so.abidiff
中的指令輸出範例:
lib_name: "libfoo" arch: "arm64" record_type_diffs { name: "bar" type_stack: "Foo-> bar *->bar " type_info_diff { old_type_info { size: 24 alignment: 8 } new_type_info { size: 8 alignment: 8 } } fields_diff { old_field { referenced_type: "foo" field_offset: 0 field_name: "mfoo" access: public_access } new_field { referenced_type: "foo *" field_offset: 0 field_name: "mfoo" access: public_access } } }
libfoo.so.abidiff
包含所有 ABI 破壞性的報告
libfoo
的變更。record_type_diffs
訊息
會指出記錄已變更,並列出不相容的變更,
包括:
- 記錄大小從
24
個位元組變更為8
個位元組。 mfoo
的欄位類型已從foo
變更為foo *
(所有 typedef 都會移除)。
type_stack
欄位會指出 header-abi-diff
如何
達到了變更的類型 (bar
)。這個欄位可能是
解譯為 Foo
是匯出函式
bar *
做為參數,指向 bar
,
已匯出並變更
強制執行 ABI 和 API
如要強制執行 VNDK 共用程式庫的 ABI 和 API,ABI 參照必須
目前在「${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
」中簽到。
如要建立這些參照,請執行下列指令:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
建立參照後,對產生結果的原始碼所做的任何變更 現在 VNDK 程式庫中的不相容 ABI/API 變更會導致建構錯誤。
如要更新特定程式庫的 ABI 參照,請執行下列指令:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
舉例來說,如要更新 libbinder
ABI 參照,請執行:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder