とある課題でシステムコールをLinuxカーネルに追加することになりました ググった限り一応方法は書いてあるのですが分かりづらいこともあったので備忘録として記録〜
カーネルのダウンロード 基本的にはsu環境で実行しています 必要なソフトウェアをインストールして今回はカーネルのバージョンを5.1で実行します 適当な場所(/usr/src以下)に解凍してディレクトリ内でmake olddefconfig
を実行します 他にもoldconfig
、defconfig
があるのですがoldだと現状のカーネルの設定を、defだと既定値を設定するようです(?) 実験PCは6コアに設定しているのでmake -j 6
で6コア使ってコンパイルしていただきます できたカーネルをインストールし、grub-mkconfig
で実際に起動するカーネルの一覧に設定します
1 2 3 4 5 6 7 8 9 10 $ apt install flex bison libssl-dev libelf-dev -y $ cd /usr/src $ wget http://ftp.riken.jp/Linux/kernel/v5.x/linux-5.1.tar.xz $ xz -dc linux-5.1.tar.xz | tar xfv - $ cd linux-5.1 $ make olddefconfig $ make -j 6 $ make modules_install && make install $ grub-mkconfig $ reboot
ただしこのままだと起動時にESCキー連打してGRUBのコンフィグから直接起動するしかできなかったので別途指定したカーネルで起動させます (一部長いので省略しています)/boot/grub/grub.cfg
にgrubの設定があるのでmenuentryでgrepします
1 2 3 4 5 6 7 8 9 10 $ grep menuentry /boot/grub/grub.cfg menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os ~~ submenu 'Advanced options for Ubuntu' $menuentry_id_option ~~ menuentry 'Ubuntu, with Linux 5.3.0-42-generic' ~~ menuentry 'Ubuntu, with Linux 5.3.0-42-generic (recovery mode)' ~~ menuentry 'Ubuntu, with Linux 5.3.0-40-generic' ~~ ~~~ menuentry 'Ubuntu, with Linux 5.1.0' ~~ menuentry 'Ubuntu, with Linux 5.1.0 (recovery mode)' ~~
自分が起動したいのはサブメニュー以下のUbuntu, with Linux 5.1.0
なので/etc/default/grub
にそのような設定を加えます
を
1 GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.1.0"
にすると指定したカーネルが設定されます
== 追記(2020/04/13) ==update-grub
を実行してGRUB_DEFAULTの設定を反映させる必要がありました == ここまで ==
再起動後はuname -r
で自分の設定したカーネルかどうか確認します
システムコールの追加 システムコールの登録 システムコールは/usr/src/linux-5.1/arch/x86/entry/syscalls/syscall_64.tbl
に番号とx64 or x86、関数名が登録されています 空いている番号を適当に選んで登録します 定数を返す、システムログに定数を記録する、システムログに任意の文字列を記録する(配列を渡す)、構造体を受け渡しするの4種類を今回は設定します 慣習らしくプレフィックスsys_
をつけるらしいです また引数を取るために少し工夫が必要らしく__x64
を更に加えます
/usr/src/linux-5.1/arch/x86/entry/syscalls/syscall_64.tbl (追記) 1 2 3 4 428 common numacall_const sys_numacall_const 429 common numacall_syslog sys_numacall_syslog 431 common numacall_array __x64_sys_numacall_array 432 common numacall_struct __x64_sys_numacall_struct
システムコールの本体 システムコールの本体を記述しますがそのまんまって感じがします 引数を取る関数だけちょっと特殊でその設定を引き受けてくれるのがSYSCALL_DEFINEN
(最後のNは1-6)です 普通のプログラムのような引数の取り方ではだめなようでそれを楽に記述できる方法(中身はマクロ?)らしい 取りたい引数の数をNに設定してカンマ区切りで関数名、引数1の型、引数1の引数名、…と書きます
/usr/src/linux-5.1/arch/x86/kernel/numacall.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <linux/kernel.h> #include <linux/syscalls.h> #include <asm/current.h> typedef struct { char name[20 ]; int id; int point; } user; asmlinkage int sys_numacall_const (void ) { return 256 ; } asmlinkage void sys_numacall_syslog (void ) { printk (KERN_INFO "Hello Numa Wrold!\n" ); }; SYSCALL_DEFINE1(numacall_array, char *, str){ char buf[512 ]; long copied = strncpy_from_user(buf, str, sizeof (buf)); if (copied < 0 || copied == sizeof (buf)) return -EFAULT; printk (KERN_INFO "%s\n" ,buf); return copied; }; SYSCALL_DEFINE1(numacall_struct, user*, u){ user a; int oldpoint; copy_from_user(&a,u,sizeof (user)); printk (KERN_INFO "User Struct name:%s id: %d point: %d\n" ,a.name,a.id,a.point); oldpoint = a.point; a.point *=2 ; copy_to_user(u,&a,sizeof (user)); return oldpoint; }
Makefile このままではカーネルに取り込まれないのでMakefile
に追加
/usr/src/linux-5.1/arch/x86/kernel/Makefile (追記)
ビルド! 一応これで準備は整ったのでビルドします
1 2 3 $ make -j 6 $ make modules_install && make install && grub-mkconfig $ reboot
テスト! テスト用のプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <stdio.h> #include <unistd.h> #include <sys/types.h> #define numacall_const 428 #define numacall_syslog 429 #define numacall_array 431 #define numacall_struct 432 int main () { typedef struct { char name[20 ]; int id; int point; } user; user u = { "numa" , 124 , 5000 }; printf ("syscall(numacall_const) = %d\n" ,(int )syscall (numacall_const)); printf ("syscall(numacall_syslog)\n" ); syscall(numacall_syslog); char str[512 ] = "Hello Test Syscall Now" ; printf ("syscall(numacall_array) len: %d\n" , (int )syscall(numacall_array,str)); printf ("syscall(numacall_struct) old point %d\n" ,(int )syscall(numacall_struct,&u)); printf ("syscall after point %d\n" ,u.point); }
実際に実行するとこうなります ちゃんとシステムログに記録されています
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $sudo dmesg | tail -n 5[ 20.009740] audit: type =1400 audit(1585545304.224:9): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/usr/lib/NetworkManager/nm-dhcp-client.action" pid=611 comm="apparmor_parser" [ 20.009744] audit: type =1400 audit(1585545304.224:10): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/usr/lib/NetworkManager/nm-dhcp-helper" pid=611 comm="apparmor_parser" [ 20.009748] audit: type =1400 audit(1585545304.224:11): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/usr/lib/connman/scripts/dhclient-script" pid=611 comm="apparmor_parser" [ 24.778661] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready [ 66.927158] hv_balloon: Max. dynamic memory size: 4096 MB $./a.out syscall(numacall_const) = 1024 syscall(numacall_syslog) syscall(numacall_array) len: 22 syscall(numacall_struct) old point 5000 syscall after point 10000 $sudo dmesg | tail -n 5[ 24.778661] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready [ 66.927158] hv_balloon: Max. dynamic memory size: 4096 MB [ 107.795123] Hello Numa Wrold! [ 107.795126] Hello Test Syscall Now [ 107.795131] User Struct name:numa id: 124 point: 5000 $
参考 チュートリアル – システムコールの書き方 Linux4.0でシステムコールを追加する方法 Unable to add a custom system call on x86 ubuntu linux