UAPI 檢查器¶
UAPI 檢查器 (scripts/check-uapi.sh) 是一個 shell 指令碼,用於檢查 UAPI 標頭檔案在整個 git 樹中與使用者空間的向後相容性。
選項¶
本節將描述執行 check-uapi.sh 時可用的選項。
用法
check-uapi.sh [-b BASE_REF] [-p PAST_REF] [-j N] [-l ERROR_LOG] [-i] [-q] [-v]
可用選項
-b BASE_REF Base git reference to use for comparison. If unspecified or empty,
will use any dirty changes in tree to UAPI files. If there are no
dirty changes, HEAD will be used.
-p PAST_REF Compare BASE_REF to PAST_REF (e.g. -p v6.1). If unspecified or empty,
will use BASE_REF^1. Must be an ancestor of BASE_REF. Only headers
that exist on PAST_REF will be checked for compatibility.
-j JOBS Number of checks to run in parallel (default: number of CPU cores).
-l ERROR_LOG Write error log to file (default: no error log is generated).
-i Ignore ambiguous changes that may or may not break UAPI compatibility.
-q Quiet operation.
-v Verbose operation (print more information about each header being checked).
環境變數
ABIDIFF Custom path to abidiff binary
CC C compiler (default is "gcc")
ARCH Target architecture of C compiler (default is host arch)
退出碼
0) Success
1) ABI difference detected
2) Prerequisite not met
示例¶
基本用法¶
首先,讓我們嘗試對 UAPI 標頭檔案進行更改,這顯然不會破壞使用者空間
cat << 'EOF' | patch -l -p1
--- a/include/uapi/linux/acct.h
+++ b/include/uapi/linux/acct.h
@@ -21,7 +21,9 @@
#include <asm/param.h>
#include <asm/byteorder.h>
-/*
+#define FOO
+
+/*
* comp_t is a 16-bit "floating" point number with a 3-bit base 8
* exponent and a 13-bit fraction.
* comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
EOF
現在,讓我們使用指令碼來驗證
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
All 912 UAPI headers compatible with x86 appear to be backwards compatible
讓我們新增另一個可能會破壞使用者空間的更改
cat << 'EOF' | patch -l -p1
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -74,7 +74,7 @@ struct bpf_insn {
__u8 dst_reg:4; /* dest register */
__u8 src_reg:4; /* source register */
__s16 off; /* signed offset */
- __s32 imm; /* signed immediate constant */
+ __u32 imm; /* unsigned immediate constant */
};
/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
EOF
指令碼會捕獲此問題
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ====
[C] 'struct bpf_insn' changed:
type size hasn't changed
1 data member change:
type of '__s32 imm' changed:
typedef name changed from __s32 to __u32 at int-ll64.h:27:1
underlying type 'int' changed:
type name changed from 'int' to 'unsigned int'
type size hasn't changed
==================================================================================
error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible
在這種情況下,指令碼報告型別更改是因為它可能會破壞傳入負數的使用者空間程式。現在,假設您知道沒有使用者空間程式可能在 imm 中使用負值,因此將其更改為無符號型別不應造成任何損害。您可以將 -i 標誌傳遞給指令碼,以忽略使用者空間向後相容性不明確的更改
% ./scripts/check-uapi.sh -i
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
All 912 UAPI headers compatible with x86 appear to be backwards compatible
現在,讓我們進行類似的將破壞使用者空間的更改
cat << 'EOF' | patch -l -p1
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -71,8 +71,8 @@ enum {
struct bpf_insn {
__u8 code; /* opcode */
- __u8 dst_reg:4; /* dest register */
__u8 src_reg:4; /* source register */
+ __u8 dst_reg:4; /* dest register */
__s16 off; /* signed offset */
__s32 imm; /* signed immediate constant */
};
EOF
由於我們正在重新排序現有結構成員,因此沒有歧義,即使您傳遞 -i,指令碼也會報告破壞
% ./scripts/check-uapi.sh -i
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
==== ABI differences detected in include/linux/bpf.h from HEAD -> dirty tree ====
[C] 'struct bpf_insn' changed:
type size hasn't changed
2 data member changes:
'__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits)
'__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits)
==================================================================================
error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible
讓我們提交破壞性更改,然後提交無害的更改
% git commit -m 'Breaking UAPI change' include/uapi/linux/bpf.h
[detached HEAD f758e574663a] Breaking UAPI change
1 file changed, 1 insertion(+), 1 deletion(-)
% git commit -m 'Innocuous UAPI change' include/uapi/linux/acct.h
[detached HEAD 2e87df769081] Innocuous UAPI change
1 file changed, 3 insertions(+), 1 deletion(-)
現在,讓我們再次執行沒有引數的指令碼
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from HEAD... OK
Installing user-facing UAPI headers from HEAD^1... OK
Checking changes to UAPI headers between HEAD^1 and HEAD...
All 912 UAPI headers compatible with x86 appear to be backwards compatible
它沒有捕獲任何破壞性更改,因為預設情況下,它僅比較 HEAD 和 HEAD^1。破壞性更改已在 HEAD~2 上提交。如果我們希望搜尋範圍進一步追溯,則必須使用 -p 選項來傳遞不同的過去參考。 在這種情況下,讓我們將 -p HEAD~2 傳遞給指令碼,以便它檢查 HEAD~2 和 HEAD 之間的 UAPI 更改
% ./scripts/check-uapi.sh -p HEAD~2
Installing user-facing UAPI headers from HEAD... OK
Installing user-facing UAPI headers from HEAD~2... OK
Checking changes to UAPI headers between HEAD~2 and HEAD...
==== ABI differences detected in include/linux/bpf.h from HEAD~2 -> HEAD ====
[C] 'struct bpf_insn' changed:
type size hasn't changed
2 data member changes:
'__u8 dst_reg' offset changed from 8 to 12 (in bits) (by +4 bits)
'__u8 src_reg' offset changed from 12 to 8 (in bits) (by -4 bits)
==============================================================================
error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible
或者,我們也可以使用 -b HEAD~ 執行。這將把基本參考設定為 HEAD~,然後指令碼會將其與 HEAD~^1 進行比較。
架構特定的標頭¶
考慮此更改
cat << 'EOF' | patch -l -p1
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -70,6 +70,7 @@ struct sigcontext {
struct _aarch64_ctx {
__u32 magic;
__u32 size;
+ __u32 new_var;
};
#define FPSIMD_MAGIC 0x46508001
EOF
這是對 arm64 特定 UAPI 標頭檔案的更改。在此示例中,我正在從具有 x86 編譯器的 x86 機器執行指令碼,因此預設情況下,該指令碼僅檢查 x86 相容的 UAPI 標頭檔案
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
No changes to UAPI headers were applied between HEAD and dirty tree
使用 x86 編譯器,我們無法檢查 arch/arm64 中的標頭檔案,因此指令碼甚至沒有嘗試。
如果我們想檢查標頭檔案,則必須使用 arm64 編譯器並相應地設定 ARCH
% CC=aarch64-linux-gnu-gcc ARCH=arm64 ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
==== ABI differences detected in include/asm/sigcontext.h from HEAD -> dirty tree ====
[C] 'struct _aarch64_ctx' changed:
type size changed from 64 to 96 (in bits)
1 data member insertion:
'__u32 new_var', at offset 64 (in bits) at sigcontext.h:73:1
-- snip --
[C] 'struct zt_context' changed:
type size changed from 128 to 160 (in bits)
2 data member changes (1 filtered):
'__u16 nregs' offset changed from 64 to 96 (in bits) (by +32 bits)
'__u16 __reserved[3]' offset changed from 80 to 112 (in bits) (by +32 bits)
=======================================================================================
error - 1/884 UAPI headers compatible with arm64 appear _not_ to be backwards compatible
我們可以看到,透過為檔案正確設定 ARCH 和 CC,可以正確報告 ABI 更改。另請注意,指令碼檢查的 UAPI 標頭檔案總數發生了變化。這是因為為 arm64 平臺安裝的標頭檔案數量與 x86 不同。
跨依賴項破壞¶
考慮此更改
cat << 'EOF' | patch -l -p1
--- a/include/uapi/linux/types.h
+++ b/include/uapi/linux/types.h
@@ -52,7 +52,7 @@ typedef __u32 __bitwise __wsum;
#define __aligned_be64 __be64 __attribute__((aligned(8)))
#define __aligned_le64 __le64 __attribute__((aligned(8)))
-typedef unsigned __bitwise __poll_t;
+typedef unsigned short __bitwise __poll_t;
#endif /* __ASSEMBLY__ */
#endif /* _UAPI_LINUX_TYPES_H */
EOF
在這裡,我們正在更改 types.h 中的 typedef。這不會破壞 types.h 中的 UAPI,但是樹中的其他 UAPI 可能會因此更改而被破壞
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
==== ABI differences detected in include/linux/eventpoll.h from HEAD -> dirty tree ====
[C] 'struct epoll_event' changed:
type size changed from 96 to 80 (in bits)
2 data member changes:
type of '__poll_t events' changed:
underlying type 'unsigned int' changed:
type name changed from 'unsigned int' to 'unsigned short int'
type size changed from 32 to 16 (in bits)
'__u64 data' offset changed from 32 to 16 (in bits) (by -16 bits)
========================================================================================
include/linux/eventpoll.h did not change between HEAD and dirty tree...
It's possible a change to one of the headers it includes caused this error:
#include <linux/fcntl.h>
#include <linux/types.h>
請注意,指令碼注意到失敗的標頭檔案沒有更改,因此它假定其包含項之一導致了破壞。實際上,我們可以看到 linux/types.h 是從 eventpoll.h 使用的。
UAPI 標頭刪除¶
考慮此更改
cat << 'EOF' | patch -l -p1
diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild
index ebb180aac74e..a9c88b0a8b3b 100644
--- a/include/uapi/asm-generic/Kbuild
+++ b/include/uapi/asm-generic/Kbuild
@@ -31,6 +31,6 @@ mandatory-y += stat.h
mandatory-y += statfs.h
mandatory-y += swab.h
mandatory-y += termbits.h
-mandatory-y += termios.h
+#mandatory-y += termios.h
mandatory-y += types.h
mandatory-y += unistd.h
EOF
此指令碼從安裝列表中刪除 UAPI 標頭檔案。讓我們執行指令碼
% ./scripts/check-uapi.sh
Installing user-facing UAPI headers from dirty tree... OK
Installing user-facing UAPI headers from HEAD... OK
Checking changes to UAPI headers between HEAD and dirty tree...
==== UAPI header include/asm/termios.h was removed between HEAD and dirty tree ====
error - 1/912 UAPI headers compatible with x86 appear _not_ to be backwards compatible
刪除 UAPI 標頭被認為是破壞性更改,指令碼會將其標記為如此。
檢查歷史 UAPI 相容性¶
您可以使用 -b 和 -p 選項來檢查 git 樹的不同塊。例如,要檢查 v6.0 和 v6.1 標記之間的所有已更改的 UAPI 標頭檔案,您將執行
% ./scripts/check-uapi.sh -b v6.1 -p v6.0
Installing user-facing UAPI headers from v6.1... OK
Installing user-facing UAPI headers from v6.0... OK
Checking changes to UAPI headers between v6.0 and v6.1...
--- snip ---
error - 37/907 UAPI headers compatible with x86 appear _not_ to be backwards compatible
注意:在 v5.3 之前,指令碼所需的標頭檔案不存在,因此指令碼無法檢查之前的更改。
您會注意到,指令碼檢測到許多與向後不相容的 UAPI 更改。 考慮到核心 UAPI 應該永遠穩定,這是一個令人震驚的結果。這使我們進入下一節:注意事項。
注意事項¶
UAPI 檢查器對作者的意圖不做任何假設,因此即使某些型別的更改有意破壞 UAPI,也可能會被標記。
用於重構或棄用的刪除¶
有時會刪除非常舊的硬體的驅動程式,例如在此示例中
% ./scripts/check-uapi.sh -b ba47652ba655
Installing user-facing UAPI headers from ba47652ba655... OK
Installing user-facing UAPI headers from ba47652ba655^1... OK
Checking changes to UAPI headers between ba47652ba655^1 and ba47652ba655...
==== UAPI header include/linux/meye.h was removed between ba47652ba655^1 and ba47652ba655 ====
error - 1/910 UAPI headers compatible with x86 appear _not_ to be backwards compatible
指令碼將始終標記刪除(即使它們是有意的)。
結構擴充套件¶
根據結構在核心空間中的處理方式,擴充套件結構的更改可能不是破壞性的。
如果結構用作 ioctl 的引數,則核心驅動程式必須能夠處理任何大小的 ioctl 命令。除此之外,從使用者複製資料時需要小心。例如,假設像這樣更改了 struct foo
struct foo {
__u64 a; /* added in version 1 */
+ __u32 b; /* added in version 2 */
+ __u32 c; /* added in version 2 */
}
預設情況下,指令碼將標記這種型別的更改以供進一步稽核
[C] 'struct foo' changed:
type size changed from 64 to 128 (in bits)
2 data member insertions:
'__u32 b', at offset 64 (in bits)
'__u32 c', at offset 96 (in bits)
但是,此更改可能是安全進行的。
如果使用版本 1 構建了使用者空間程式,它會認為 sizeof(struct foo) 為 8。該大小將編碼在傳送到核心的 ioctl 值中。如果使用版本 2 構建了核心,它會認為 sizeof(struct foo) 為 16。
核心可以使用 _IOC_SIZE 宏來獲取使用者傳入的 ioctl 程式碼中編碼的大小,然後使用 copy_struct_from_user() 安全地複製該值
int handle_ioctl(unsigned long cmd, unsigned long arg)
{
switch _IOC_NR(cmd) {
0x01: {
struct foo my_cmd; /* size 16 in the kernel */
ret = copy_struct_from_user(&my_cmd, arg, sizeof(struct foo), _IOC_SIZE(cmd));
...
copy_struct_from_user 會將核心中的結構清零,然後僅複製從使用者傳入的位元組(將新成員清零)。如果使用者傳入了更大的結構,則會忽略多餘的成員。
如果您知道核心程式碼中考慮到了這種情況,則可以將 -i 傳遞給指令碼,並且會忽略這樣的結構擴充套件。
Flex 陣列遷移¶
雖然指令碼處理擴充套件到現有 flex 陣列,但它仍然標記從 1 元素偽 flex 陣列到 flex 陣列的初始遷移。例如
struct foo {
__u32 x;
- __u32 flex[1]; /* fake flex */
+ __u32 flex[]; /* real flex */
};
指令碼會標記此更改
[C] 'struct foo' changed:
type size changed from 64 to 32 (in bits)
1 data member change:
type of '__u32 flex[1]' changed:
type name changed from '__u32[1]' to '__u32[]'
array type size changed from 32 to 'unknown'
array type subrange 1 changed length from 1 to 'unknown'
目前,沒有辦法過濾這些型別的更改,因此請注意這種可能的誤報。
總結¶
雖然指令碼過濾掉了許多型別的誤報,但在某些情況下,指令碼可能會標記不破壞 UAPI 的更改。也可能指令碼不會標記破壞使用者空間的更改。雖然該指令碼已在大部分核心歷史上執行,但可能仍然存在未考慮到的極端情況。
目的是將此指令碼用作維護者或自動化工具的快速檢查,而不是作為補丁相容性的最終權威。最好記住:使用您最好的判斷力(最好是在使用者空間中使用單元測試)來確保您的 UAPI 更改向後相容!