如何给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

修改的系统调用表如下:
64位系统调用表
在最后一行加入递增的系统调用号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文件,用于开机启动弹出启动项选择页面,修改为以下内容:
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
  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要给Linux增加一个系统调用,首先需要对Linux内核进行修改。具体的步骤如下: 1. 在内核源码中确定添加系统调用的位置。通常可以在`arch/x86/entry/syscalls`目录下找到系统调用表的文件,例如`syscall_64.tbl`。 2. 打开相应的系统调用表文件,添加一行来定义新的系统调用。每行包含系统调用号、系统调用名称和对应的处理函数系统调用号是一个唯一的整数,用来标识系统调用。例如: ``` 336 common my_new_syscall sys_my_new_syscall ``` 3. 找到对应的处理函数所在的文件,通常在`kernel/sys.c`中。在该文件中添加一个新的函数来处理新的系统调用函数名与上一步中的处理函数名称一致。在函数中实现具体的功能逻辑。 4. 编译内核,确保新的系统调用被包含在编译过程中。执行适当的编译命令,例如`make`。 5. 重新启动计算机,进入新编译内核。 6. 在用户空间的程序中调用新的系统调用。通过C程序调用系统调用的实例代码如下: ```c #include <stdio.h> #include <unistd.h> #define __NR_my_new_syscall 336 int my_new_syscall() { return syscall(__NR_my_new_syscall); } int main() { int ret = my_new_syscall(); printf("My new system call returned %d\n", ret); return 0; } ``` 以上是向Linux内核添加一个新的系统调用的基本过程。当然,具体实现会根据系统版本和内核的不同而有所差异,所以这只是一个大致的指导,具体操作还需参考内核版本和相应的文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值