新增 System Call 至 Linux 核心
新增 System Call 至 Linux 核心
在網路上尋找有關新增 system call 至 Linux 核心的文章時,發現有些文章所提供的方式無法順利執行,可能是核心版本的差異導致作法不同,因此想紀錄下自己嘗試後能夠成功的方法。
這篇文章除了是自己的筆記之外,也告訴大家如何新增一個簡單的 system call,並且透過 QEMU 運作。同時也感謝網路上許多大神的無私奉獻,提供各種教學文章。
在本篇筆記的命令列範例中,若前綴為 $ 者,表示其執行在 host 端;而前綴為 # 者,表示其須執行在 guest 端 (qemu 內)
測試環境如下:
OS: Ubuntu 22.04 |
Build Linux Kernel
首先需要安裝以下套件:
sudo apt update && sudo apt upgrade |
新增一個專案資料夾並進入該資料夾。
mkdir -p linux-kernel && cd linux-kernel |
下載 kernel 的 source code 並且 build 起來,可以使用 wget 下載核心壓縮檔或者使用 git clone 取得原始碼,這裡使用 wget 作為示範e。
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz |
使用 git clone:
git clone --depth=5 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux
cd linux
git checkout -b linux-6.1.y origin/linux-6.1.y
接著設定 config。
make menuconfig |
將下列所有選項都勾起來:
Linux/x86 6.6.0 Kernel Configuration |
編譯 kernel,參數 $(nproc) 代表系統最大核心數量。
make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- -j$(nproc) |
針對 Arm64 處理器架構,將指令更改為:
make ARCH=arm64 CORSS_COMPILE=aarch64-linux-gun- -j$(nproc)
編譯結束後,預期會見到以下訊息:
Kernel: arch/x86/boot/bzImage is ready |
Build Root FS
回到專案資料夾 linux-kernel
內下載 busybox 並編譯。
cd .. |
截至目前為止,專案資料夾結構應如下:
linux-kernel |
移動至 busybox-1.36.1
資料夾內並執行 make menuconfig
。
cd busybox-1.36.1 |
選擇 Settings ---> Build static binary
並執行。
make install |
接著要製作 mount 至 kernel 的資料夾。
cd _install |
寫入開機之後需要的腳本,首先利用 vim 建立 rcS 檔案。
vim ./etc/init.d/rcS |
將以下腳本寫入至 rcS 並儲存:
!/bin/sh |
設定 rcS 腳本的權限並且建立 rootfs 的 image。
chmod +x etc/init.d/rcS |
測試 kernel 是否能順利運作,首先移動至 linux-6.6 資料夾並啟動 qemu 。
cd ../../linux-6.6 |
當看到以下畫面,代表順利進入 qemu 執行環境,可以輸入 ls
指令做確認:
若要離開測試環境,可以按下 Ctrl-A 放開再按下 X 按鍵。
Add System Call
修改 syscall_64.tbl
首先打開 arch/x86/entry/syscalls/syscall_64.tbl
並且在 377 行後面新增我們自己的 system call 以及對應的編號。
這行有 4 個部份,每項之間由空白或是 tab 相隔。,它們代表的意義是:
454
System call numver ,在使用系統呼叫所使用的數字。common
支援的 ABI ,只能是64
、x32
或common
,分別表示「只支援 amd 64」、「只支援 x32」或是「都支援」。my_get_physical_addresses
System call 的名字
在 syscall_64.tbl
檔案的最上方也相有對應的說明。
# |
修改 syscalls.h
開啟 include/linux/syscalls.h
並在 asmlinkage long sys_map_shadow_stack();
的後方 (約第 943 行) 加入宣告。
939 | asmlinkage long sys_cachestat(unsigned int fd, |
撰寫 system call 程式碼
新增一個檔案叫做 my_syscall.c
,路徑為 kernel/my_syscall.c
。
|
SYSCALL_DEFINE0
表示定義該 system call 無參數,SYSCALL_DEFINE1
代表可定義一個參數,依此類推。
e.g.
SYSCALL_DEFINE1(my_syscall, long, arg1)
接下來要將檔案新增到 makefile 裡面,開啟 kernel/Makefile
並將新增我們的 system call 。
obj-y = fork.o exec_domain.o panic.o \ |
再重新編譯一次 kernel。
make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- -j$(nproc) |
測試 System Call
接下來我們需要在外部先編譯靜態連結的測試程式碼,並呼叫我們所寫好的 system call 。首先在 linux-6.6
資料夾的外面 (專案資料夾內) 新增一個檔案叫做 project1.c
。
cd .. |
在 project1.c
內輸入以下程式碼:
|
到目前為止,專案資料夾內的檔案架構應如下:
linux-kernel |
接下來編譯這個檔案。
gcc -static project1.c -o project1 |
將編譯完成的執行檔複製到 busybox-1.36.1/_install
內,並重新製作 image。
cd busybox-1.36.1/_install |
Run User Program
到 linux-6.6
底下再執行一次 qemu。
cd ../../linux-6.6/ |
按下 enter 之後就可以開始輸入指令了,首先輸入 ls
。
可以看到我們的執行檔案 project1
在裡面,接下來直接執行它。