实验内容:
- 找一个系统调用,系统调用号为学号最后2位相同的系统调用
- 通过汇编指令触发该系统调用
- 通过gdb跟踪该系统调用的内核处理过程
- 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化
一、搭建Linux内核调试环境
1.配置内核选项
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)
2.编译和运行内核
make -j$(nproc) # nproc gives the number of CPU cores/threads available
qemu-system-x86_64 -kernel arch/x86/boot/bzImage # 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统最终会kernel panic
启动 qemu 后,从下图的最后一行可以看出来内核不能正常加载运行,并且终止在 kernel panic
3.利用busybox制作内存根文件系统
# 下载 busybox 源代码并解压
axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 # 下载 busybox 源代码
tar -jxvf busybox-1.31.1.tar.bz2 # 解压
cd busybox-1.31.1
# 配置编译,并安装
make menuconfig # 配置编译
#编译成静态链接,不⽤动态链接库。
#Settings --->
# [*] Build static binary (no shared libs)
make -j$(nproc) && make install # 编译安装,默认会安装到源码⽬录下的 _install ⽬录中。
# 制作内存根⽂件系统镜像
cd ..
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/
# 准备init脚本⽂件放在根⽂件系统根⽬录下(rootfs/init)并添加可执⾏权限
chmod +x init
init文件添加如下内容
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Wellcome MengningOS!"
echo "--------------------"
cd home
/bin/sh
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz # 打包成内存根⽂件系统镜像
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz # 测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本
从下图能看到输出的 "Wellcome MengningOS!",说明内核启动后已经成功执行init脚本
二、跟踪系统调用
学号后两位为06,查找到系统调用为 lstat,该系统调用的功能为获取文件属性。
自己写汇编代码,不设置参数, 直接将 0x06 放入 %eax,系统调用会出错,并且在操作过程中发现 ls 指令会调用 lstat,所以直接通过 ls 指令来捕捉 lstst 系统调用中断。
如下图所示,系统调用中断入口为 entry_SYSCALL_64, 然后调用 do_syscall_64, 最后进入系统调用执行函数 __x64_sys_newlstat。
1. entry_SYSCALL_64保护现场和恢复现场过程
通过源码跟踪发现 USERGS_SYSRET64 是一个宏定义,实际上执行了 快照恢复指令 swapgs 和 系统调用返回指令 sysretq
2. do_syscall_64 分析
gdb跟踪到的行为与代码也是一致的,do_syscall_64 最后执行的是 syscall_return_slowpath(regs)