【Linux】系统调用
编译配置安装Linux内核
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 sudo apt install build-essential gcc-multilib libncurses5-dev bison flex libssl-dev libelf-dev sudo apt install qemu sudo apt install make make defconfig make menuconfig #####################在界面内这样设置 Kernel hacking ---> Compile-time checks and compiler options ---> [*] Compile the kernel with debug info [*] Provide GDB scripts for kernel debugging Processor type and features ----> [] Randomize the address of the kernel image (KASLR) ################################ make -j$(nproc) qemu-system-x86_64 -kernel arch/x86/boot/bzImage #没有⽂件系统最终会kernel panic
制作根文件系统
#利用BusyBox构造根文件系统 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) #################### make -j$(nproc) && make 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脚本 touch init chmod +x init
init.c脚本内容如下
#!/bin/sh mount -t proc none /proc mount -t sysfs none /sys echo "Wellcome MyOS!" echo "--------------------" cd home /bin/sh
#打包成内存根⽂件系统镜像 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../ rootfs.cpio.gz #测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本 cd .. qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
以lseek为例,尝试通过汇编指令触发该系统调用
通过查阅/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl文件,能够找到8号系统调用为lseek
lseek是一个用于改变读写一个文件时读写指针位置的一个系统调用:
off_t lseek(int fd, off_t offset, int whence);
编写一个程序调用lseek:
结果:
#查看调用过程的汇编形式 gcc -S -o mylseek.s mylseek.c -static
通过汇编代码调用lseek:
再次打包根目录,再次执行内核
cd roofs find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../ rootfs.cpio.gz cd .. qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0" #另外一个终端运行gdb进行调试 gdb vmlinux #target remote:1234
再输入b __x64_sys_lseek打断点
输入c使内核进行执行
通过gdb跟踪lseek的内核处理过程
在内核端执行根文件下的mylseek,由于gdb的调试断点,程序停了下来
此时通过bt可以看到此时的堆栈信息
连续输入n可以查看lseek的执行过程
总结
当⽤户态进程调⽤⼀个系统调⽤时,CPU切换到内核态并执⾏entry_SYSCALL_64以及do_syscall_64,根据系统调⽤号调⽤对应的内核处理函数。
在执行do_syscall_64之前会上保存⼀些上下文信息,如当前执⾏程序的栈顶地址(ESP、RSP)、状态字(EFlags、RFlags)、CS:EIP 的值等,并且将CS:EIP寄存器的值指向系统调⽤处理的⼊⼝。
在系统调用执行完成后,调用iret或者sysret进行对应的出栈操作恢复现场、回到用户态,系统调用过程便完成了。