深入理解系统调用
深入理解系统调用
ps:可能好看些,hhh~
实验目的
1.找一个系统调用,系统调用号为学号最后2位相同的系统调用,本人学号最后两位为80,即要测试的系统调用号为80
2.通过汇编指令触发该系统调用
3.通过gdb跟踪该系统调用的内核处理过程
4.重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化
环境准备
1.安装开发工具
sudo apt install build-essential
sudo apt install qemu # install QEMU
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev
2.下载内核源码
sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ linux-5.4.34.tar.xz
xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar
cd linux-5.4.34
ps:出毛病,下载不了的,可以直接主机下载,拖过去。个人尝试多次后,逐渐难受,因此直接下载了
3.配置内核编译选项
make defconfig # Default configuration is based on 'x86_64_defconfig'
make menuconfig打开debug相关选项
关闭KASLR,否则会导致打断点失败
# 打开debug相关选项
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
[*] Kernel debugging
# 关闭KASLR,否则会导致打断点失败
Processor type and features ---->
[] Randomize the address of the kernel image (KASLR)
4.编译内核
make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage # 此时应该不能正常运行
5.制作根文件系统
#下载
axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1
#制作根文件系统
make menuconfig
#记得要编译成静态链接,不⽤动态链接库。
Settings --->
[*] Build static binary (no shared libs)
#然后编译安装,默认会安装到源码⽬录下的 _install ⽬录中。
make -j$(nproc) && make install
6.制作内存根文件系统镜像
mkdir rootfs
cd rootfs
cp ../busybox-1.31.1/_install/* ./ -rf
mkdir dev proc sys home
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
7.准备init脚本文件放在根文件系统跟目录下(rootfs/init),添加如下内容到init文件。
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Welcome to SSE_314 OS!"
echo "--------------------"
cd home
/bin/sh
8.给init脚本添加可执⾏权限
chmod +x init
9.打包成内存根文件系统镜像
#打包成内存根⽂件系统镜像
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
#测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
ps:这上面那么多文件夹位置并不固定,最后都会打包成系统镜像,这里提供的命令基于的目录结构是
10.查看系统调用表
vim /linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl
ps:我的学号末尾是14,因此我的实验目标是14号系统调用--__x64_sys_rt_sigprocmask
编写汇编调用代码
int main()
{
asm volatile(
"movl $0x0E,%eax\n\t" //使⽤EAX传递系统调⽤号14
"syscall\n\t" //触发系统调⽤
);
return 0;
}
就长这样,haha~
由于我们搭建的系统不支持动态链接,因此这里我们在使用gcc编译时要用-static静态编译参数):gcc -o sigprocmask-test sigprocmask-test.c -static
然后将生成的可执行文件文件拷贝至rootfs/home文件夹下(当然也可以是其他文件加下,但一定要在rootfs中,不然内核运行时找不到,还有,执行文件和原文件不要放在一起,不知道为啥,一放在一起就都找不到了...),
由于我们对系统做了修改,需要重新打包成内存根文件系统镜像。因此要再次使用命令:
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
打包完毕后,我们的构建系统的根目录下应该已经有sigprocmask-test这一可执行文件了,用qemu运行,检查一下。如下图所示:
gdb调试
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
再打开另一个终端,执行下面的命令:
cd linux-5.4.34
gdb vmlinux
target remote:1234
ps:这里一定要切换到linux-5.4.34目录下,我看群里有人问出现很多0000000之类的乱码怎么解决,其实不一定的gdb出错,有很大的可能是当前目录不对,血的教训。错误情况如下:
再让我先执行下,试试,中断的话,就ctrl + c
加个断点
继续
ps:这里总结下,c就是continue,b就是break,如果不是continue状态的话,内核是停止的,我们是先在暂停的状态下,用b设置断点,然后用c让内核运行,再在内核上通过运行我们写的程序调用sigprocmask,这时系统会自动捕捉到断点,内核状态变为暂停
使用 l 命令查看代码情况, n 命令单步执行, step 命令进入函数内部 ,bt查看堆栈
查看此时堆栈情况,有4层
第一层 __x64_sys_rt_sigprocmask 系统调用函数所在
第二层 do_syscall_64 获取系统调用号, 前往系统调用函数
第三层 entry_SYSCALL_64 () 中断入口,做保存线程工作,调用 do_syscall_64
gdb单步调试
调用返回
这里是调用,根据注释,将rip存入rcx中
这里是返回,通过两个pop返回原栈和rip,至此系统调用完成
总结
根据实验和网上的资料,大概了解了一下sigprocmask:
它用于改变进程的当前阻塞信号集,也可以用来检测当前进程的信号掩码。
函数说明:
一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。sigprocmask()可以用来检测或改变信号屏蔽字,其操作依参数how来决定,如果参数oldset不是NULL指针,那么信号屏蔽字会由此指针返回。如果set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
网上找到的比较全面的大佬的经验
sigprocmask学习资料
ps:本次实验,在实验过程中,虚拟机卡log界面一次,网上方法全部无解,只能重装,实验全部重写,实验做到一半从头再来次数至少3次,不可预料的各种不能下载,不能编译,编译出错,找不到文件,解压报错等小问题无算,心态爆炸次数无算,期间还将显卡驱动卸载重装。。。真的崩溃,但最后还是做完了,到最后了博客园还出问题了,不能写随笔,只能把以前的随笔改成现在的,难受,真的不容易,感觉还是蛮有收获的,加油!