如何给Linux kernel 5添加一个系统调用
如何给Linux Kernel 5添加一个系统调用
本篇博客从英文博客 Adding a Hello World System Call to Linux Kernel 翻译而来。原文链接为: https://medium.com/anubhav-shrimal/adding-a-hello-world-system-call-to-linux-kernel-dad32875872,在这里感谢原作者的创作和分享。
在编译之前保证系统所在的磁盘有至少30G的空间。如果是虚拟机,最好设置为80G。
在开始之前,可以通过以下两个命令查找到所在linux系统的发行版信息和内核信息:
cat /etc/*release
uname -a
得到信息如下:
我们可以看到这是一个使用了5.0版本Linux内核的64位Ubuntu 18.04发行版操作系统。
0x01 下载Linux内核源码
内核源码可从Linux内核的官网www.kernel.org下载,也可以从github上面得到。在Linux里面可以通过wget命令直接下载:
cd
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.3.2.tar.xz
命令运行完成之后,可以看到在当前目录下面多了一个名称为linux-5.3.2.tar.xz的压缩文件。
0x02 解压Linux内核源码压缩文件
使用命令:
tar -xvf linux-5.3.2.tar.xz
解压文件,运行完成之后可以看到当前目录下面多了一个包含linux源码的文件夹linux-5.3.2,使用命令:
cd ./linux-5.3.2
进入linux的源码文件下。
注意: 接下来的操作都是默认的所在目录位置为linux源码文件夹的位置。
0x03 添加系统调用函数
在linux源码目录下面添加一个文件夹,并进入这个文件夹。在文件夹下面创建一个hello.c的文件:
mkdir hello
cd hello
gedit hello.c
在hello.c文件里面加入以下代码:
#include <linux/kernel.h>
asmlinkage long sys_hello(void)
{
printk("Hello world\n");
return 0;
}
printk函数为将内容打印到kernel日志的函数。
0x04 将系统调用函数文件加入到内核编译里面
在hello文件夹下面创建一个Makefile文件:
touch Makefile
gedit Makefile
将以下内容加入到Makefile文件里面:
obj-y := hello.o
进入到linux源码文件夹,修改总的Makefile文件:
cd ..
gedit Makefile
在linux源码总的Makefile文件里面找到下面一行:
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
在其末尾加入我们添加系统调用函数文件的目录,修改如下:
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ hello/
以上步骤即为将系统调用函数文件及所在文件夹加入到linux源码编译模块里面去。
0x05 将新的系统调用注册到系统调用表里面
如果是在32位系统里面,则需要修改syscall_32.tbl文件,如果是64位系统,修改syscall_64.tbl文件。
系统调用文件在目录arch/x86/entry/syscalls/文件夹里面:
gedit arch/x86/entry/syscalls/syscall_64.tbl # or syscall_32.tbl for 32-bit system
修改的系统调用表如下:
在最后一行加入递增的系统调用号548,并记住系统调用号,如下:
548 64 hello sys_hello
0x06 将新的系统调用加入到系统调用头文件里面
系统调用头文件位置为include/linux/syscalls.h,使用如下命令编辑syscalls.h:
gedit include/linux/syscalls.h
将系统调用函数声明加入到里面,系统调用函数声明如下:
asmlinkage long sys_hello(void);
关于asmlinkage宏定义的说明: asmlinkage声明此函数的所有参数均需到内存调用栈中寻找,而不是使用寄存器得到调用参数(系统调用传参有两种方式,一种是在调用之前将参数压栈,一种是通过寄存器传参)。
在syscalls.h文件中加入函数声明后的文件如下:
0x07 编译内核
安装编译所需要的各种工具,使用如下命令安装:
sudo apt-get install update
sudo apt-get install gcc
sudo apt-get install make
sudo apt-get install libncurses5-dev
sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install libssl-dev
sudo apt-get install libelf-dev
使用如下的命令选择内核所需要的功能:
make menuconfig
如果使用默认配置,则只需要进入之后按Esc退出,退出的时候保存即可。
编译内核的命令为make,如果想加速编译过程,可以直接使用make -j[n],其中n为指定的cpu核心数。例如如果我的电脑有6个物理核心,则可以使用make -j6加速编译。
make -j6
0x08 安装内核
安装内核的命令为:
sudo make modules_install install
使用以上命令安装内核之后,Ubuntu1804系统会默认将最新版本的内核放到第一启动项的位置。如果编译的Linux内核版本为比当前老,则需要开启开机的grub引导页面,在引导页面选择自己编译的内核。如果编译的Linux内核版本比当前版本新,则跳过下一步,直接重启即可。
0x09 开启grub引导页面
修改/etc/default/grub文件,用于开机启动弹出启动项选择页面,修改为以下内容:
使用sudo update-grub2更新grub列表,然后重启,进入选择项之后选择advanced options for ubuntu选项,进入如下页面:
选择自己编译的内核版本,然后进入系统。
0x0a 验证
重启完成之后,使用命令uname -a查看目前的linux内核版本,以确认内核是否切换成功。
在用户自己的目录下面创建一个test.c文件,编写如下代码:
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
int main()
{
long ret = syscall(548);
printf("return code is: %ld\n", ret);
return 0;
}
编译并运行
gcc -o test.out ./test.c
./test.out
dmesg
运行效果:
dmesg命令为打印kernel内核的日志到控制台。
0x0b 如何删除编译安装的内核
编译安装的内核对应下面的文件,将其删除之后更新grub即可:
/boot/vmlinuz*KERNEL-VERSION*
/boot/initrd*KERNEL-VERSION*
/boot/System-map*KERNEL-VERSION*
/boot/config-*KERNEL-VERSION*
/lib/modules/*KERNEL-VERSION*/
/var/lib/initramfs/*KERNEL-VERSION*/
// for debian directory is /var/lib/initramfs-tools/*KERNEL-VERSION*/
更新grub:
sudo update-grub2
Samson_Miller: 我用的gen2不好使,F1-F12是默认功能,用快捷功能必须加Fn,还有指示灯只有大写锁定会亮,静音键就算生效了指示灯也不会亮
Samson_Miller: 或者数据线没喊好/短路
Samson_Miller: 应该是线序接错了
WilsonAir: 双击就可以了,可以用手机摄像头调到专业模式,然后快门速度拉到最快,就可以看到那个调光的间隔(黑线)变密了
纯情↗筱默: 怎么确定文件执行成功了?