-、实验内容
1、触发系统调用。
2、通过gdb跟踪该系统调用的内核处理过程。
3、分析系统调用入口的保存现场、恢复现场和系统调用返回,以及关注系统调用过程中内核堆栈状态的变化。
二、环境
沿用了第一次的压缩包,
xz -d linux-5.4.34.tar.xz tar -xvf linux-5.4.34.tar cd linux-5.4.34
配置内核编译选项
make defcon?g
make menucon?g
进入相关选项:
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)
编译
make -j$(nproc) # nproc gives the number of CPU cores/threads available
测试
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
因为没有?件系统终会kernel panic
接下来就要制作根文件系统了
#下载 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 menucon?g #记得要编译成静态链接,不?动态链接库。 Settings ---> [*] Build static binary (no shared libs) #然后编译安装,默认会安装到源码?录下的 _install ?录中。 make -j$(nproc) && make install 制作内存根文件系统镜像
这里的目录要与busy的平行 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目录下)
touch init 然后将这些文件内容写进去 #!/bin/sh mount -t proc none /proc mount -t sysfs none /sys echo "Wellcome TestOS!" echo "--------------------" cd home /bin/sh 写入文件的指令: vi init 按i进入编辑模式 将上方的脚本内容复制进去
按esc退出编辑模式 按:然后wq 最后回车保存推出
#给init脚本添加可执行文件
chmod +x init #打包成内存根文件系统镜像 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
三、编写系统调用程序
由于我的学号是46结尾,所以打开/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl
,查看要选择进行实验的系统调用。
46号系统调用是sendmsg,这个是一个最通用的I/O函数,可以将各种输出函数调用替换成sendmsg调用。
大部分参数封装在msghdr结构中
struct msghdr{ void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; socklen_t msg_controllen; int msg_flags; }
为了触发这个系统调用,需要写一个小程序
四,调试
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
在另一个终端:
捕获到了断点 —— __x64_sys_lchown函数,系统调用触发成功
再进行单步调试:
五、分析:
1.触发系统调用后,代码执行了/linux-5.4.34/arch/x86/entry/entry_64.S 目录下的ENTRY(entry_SYSCALL_64)入口,然后开始通过swapgs 和压栈动作保存现场:
2.然后跳转到了/linux-5.4.34/arch/x86/entry/common.c
目录下的 do_syscall_64
函数,在ax寄存器中获取到系统调用号,然后去执行系统调用
3.查看entry_syscall_64后续的代码,在完成执行现场的恢复,最后的两个popq出栈指令恢复原 rdi 和 rsp的内容,也就是完成了堆栈的切换。
从系统调用的整个过程来看,主要有以下几个阶段:
(1)用户态程序,发生syscall,触发系统调用;
(2)进入内核态,完成内核初始化后,调用entry_SYSCALL_64 ()
(3)完成现场的保存,将关键寄存器压栈,并从CPU内部的MSR寄存器来查找系统调?处理??,更改CPU的指令指针(eip/rip)到系统调?处理?? ,调用do_syscall_64()
(4)do_syscall_64()函数中得到系统调用号,调用相关的函数gettimeofday()
(5)调用结束后,保存现场和恢复现场时的CPU寄存器也通过CPU内部的存储器快速保存和恢复
(6)系统调用返回,回到用户态程序
原文:https://www.cnblogs.com/gaoweibinonly1/p/12964850.html