在網路上尋找有關新增 system call 至 Linux 核心的文章時,發現有些文章所提供的方式無法順利執行,可能是核心版本的差異導致作法不同,因此想紀錄下自己嘗試後能夠成功的方法。
這篇文章除了是自己的筆記之外,也告訴大家如何新增一個簡單的 system call,並且透過 QEMU 運作。同時也感謝網路上許多大神的無私奉獻,提供各種教學文章。
在本篇筆記的命令列範例中,若前綴為 $ 者,表示其執行在 host 端;而前綴為 # 者,表示其須執行在 guest 端 (QEMU 內)
測試環境如下:
首先需要安裝以下套件:
新增一個專案資料夾並進入該資料夾。
下載 kernel 的 source code 並且 build 起來,可以使用 wget 下載核心壓縮檔或者使用 git clone 取得原始碼,這裡使用 wget 作為示範e。
使用 git clone:
接著設定 config。
將下列所有選項都勾起來:
編譯 kernel,參數 $(nproc) 代表系統最大核心數量。
針對 Arm64 處理器架構,將指令更改為:
編譯結束後,預期會見到以下訊息:
回到專案資料夾 linux-kernel
內下載 busybox 並編譯。
截至目前為止,專案資料夾結構應如下:
移動至 busybox-1.36.1
資料夾內並執行 make menuconfig
。
選擇 Settings ---> Build static binary
並執行。
接著要製作 mount 至 kernel 的資料夾。
寫入開機之後需要的腳本,首先利用 vim 建立 rcS 檔案。
將以下腳本寫入至 rcS 並儲存 (# 為註解符號,並非執行於 QEMU 內):
設定 rcS 腳本的權限並且建立 rootfs 的 image。
測試 kernel 是否能順利運作,首先移動至 linux-6.6 資料夾並啟動 QEMU 。
當看到以下畫面,代表順利進入 QEMU 執行環境,可以輸入 ls
指令做確認:
若要離開測試環境,可以按下 Ctrl-A 放開再按下 X 按鍵。
syscall_64.tbl
首先打開 arch/x86/entry/syscalls/syscall_64.tbl
並且在 377 行後面新增我們自己的 system call 以及對應的編號。
這行有 4 個部份,每項之間由空白或是 tab 相隔。,它們代表的意義是:
454
common
64
、 x32
或 common
,分別表示「只支援 amd 64」、「只支援 x32」或是「都支援」。my_syscall
sys_my_syscall
sys
開頭來代表 system call 的實作在 syscall_64.tbl
檔案的最上方也相有對應的說明。
syscalls.h
開啟 include/linux/syscalls.h
並在 asmlinkage long sys_map_shadow_stack();
的後方 (約第 943 行) 加入宣告。
新增一個檔案叫做 my_syscall.c
,路徑為 kernel/my_syscall.c
。
SYSCALL_DEFINE0
表示定義該 system call 無參數,SYSCALL_DEFINE1
代表可定義一個參數,依此類推。
e.g.
接下來要將檔案新增到 makefile 裡面,開啟 kernel/Makefile
並將新增我們的 system call 。
再重新編譯一次 kernel。
接下來我們需要在外部先編譯靜態連結的測試程式碼,並呼叫我們所寫好的 system call 。首先在 linux-6.6
資料夾的外面 (專案資料夾內) 新增一個檔案叫做 project1.c
。
在 project1.c
內輸入以下程式碼:
到目前為止,專案資料夾內的檔案架構應如下:
接下來編譯這個檔案。
將編譯完成的執行檔複製到 busybox-1.36.1/_install
內,並重新製作 image。
到 linux-6.6
底下再執行一次 QEMU。
按下 enter 之後就可以開始輸入指令了,首先輸入 ls
。
可以看到我們的執行檔案 project1
在裡面,接下來直接執行它。