深入理解系统调用
一.实验要求:
1.找一个系统调用,系统调用号为学号最后2位相同的系统调用
2.通过汇编指令触发该系统调用
3.通过gdb跟踪该系统调用的内核处理过程
4.重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化
二.配置实验环境:
1,下载工具、Linux内核源码
sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev
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
2.配置内核编译选项
make defconfig # Default configuration is based on 'x86_64_defconfig'
make menuconfig
# 打开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)
3.编译运行内核
make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage # 此时应该不能正常运行,会报kernel pannic错误,因为没有文件系统。
4.制作内存根文件系统
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/<br>
将init脚本放入该目录下,然后执行:
a打包成内存根文件系统镜像
b测试挂载根文件系统,看内核启动完成后是否执行init脚本!
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
看到Wellcome os!说明成功执行了init脚本
三.用汇编代码编写系统调用程序
1.学号后两位为44,查看syscall_64.tbl对应系统调用为sendto,如下图所示;
sendto系统调用用于向指定的目的地址发送数据
2.这里写一个test_sendto.c文件,调用44号系统调用:
#include<stdio.h>
int main()
{
asm volatile(
"movl $0x2c,%eax\n\t" //使⽤EAX传递系统调⽤号44
"syscall\n\t" //触发系统调⽤
);
printf("sendto\n");
return 0;
}
3.用gcc静态编译,生成可执行文件
gcc -o test_sendto test_sendto.c -static
4.将文件放至rootfs/home路径下,重新执行如下两条语句:
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
四.gdb调试
1.启动qemu
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -s -S
2.新打开一个终端命令分别执行如下步骤
cd linux-5.4.34/ # 切到对应的文件夹
gdb vmlinux
(gdb) target remote:1234
3.根据前面提到的,44号系统调用的内核处理函数为__x64_sys_sendto,因此我们给它打上断点: b __x64_sys_sendto,如下图
4.保存现场:
可以看到保存现场使用了特殊的swapgs,加快了系统调用的速度,swapgs之后push了一堆寄存器,将其入内核堆栈,然后转换成pt_regs结构体
5.现场恢复
执行了一个宏USERGS_SYSRET64, 在linux5.4.34/arch/x86/include/asm/irqflags.h中有他的如下定义
swapgs——恢复现场和sysretq——系统调用返回。
五.总结
应用程序调用API,然后trap陷入,进入系统调用,进入内核态,保存现场执行中断处理程序 然后恢复现场返回用户态。其中保存现场和恢复现场都是通过操作内核的堆栈实现的