zoukankan      html  css  js  c++  java
  • 深入理解Linux系统调用

    一、实验内容

    1.学号末尾为14,故采用14号系统调用

    2.通过汇编指令触发系统调用

    3.通过gdb跟踪该系统调用的内核处理过程

    4.阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及关注系统调用过程中内核堆栈状态的变化

    二、环境准备

    安装开发工具

    sudo apt install build-essential
    sudo apt install qemu # install QEMU 
    sudo apt install libncurses5-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 
    cd linux-5.4.34

    配置内核编译选项

    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)

    编译内核

    make -j$(nproc) # nproc gives the number of CPU cores/threads available
    # 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统终会kernel panic 
    qemu-system-x86_64 -kernel arch/x86/boot/bzImage  #  此时应该不能正常运行

    制作根文件系统

    #下载
    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

    制作内存根文件系统镜像

    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),添加如下内容到init文件。

    #!/bin/sh
     mount -t proc none /proc 
     mount -t sysfs none /sys
     echo "Welcome to SSE_314 OS!"
     echo "--------------------" 
     cd home
     /bin/sh 

    给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

     

    三、查看系统调用表和汇编改写

    打开  linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl,查看要选择进行实验的系统调用。

    找到进行实验的系统调用--14号系统调用

    14号系统调用为 rt_sigprocmask。对应的内核处理函数为 __x64_sys_rt_sigprocmask。作用:设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞)。使用内联汇编小程序new_test.c如下:

    int main()
    {
        asm volatile(
        "movl $0x0E,%eax
    	" //使⽤EAX传递系统调⽤号14
        "syscall
    	" //触发系统调⽤ 
        );
        return 0;
    }

    由于我们搭建的系统不支持动态链接,因此这里我们在使用gcc编译时要用-static静态编译参数):gcc -o test test.c -static

    然后将生成的可执行文件文件拷贝至rootfs/home文件夹下

    由于我们对系统做了修改,需要重新打包成内存根文件系统镜像。因此要再次使用命令:

    find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

    打包完毕后,我们的构建系统的根目录下应该已经有test这一可执行文件了,用qemu运行,检查一下。如下图所示: 

    接下来我们首先需要检查是否成功触发了14号系统调用,然后逐步跟踪了解内核的处理过程、系统调用入口的保存现场、恢复现场和系统调用返回等流程

    四、gdb调试

    纯命令行下启动虚拟机

    qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"

    此时虚拟机会停在启动界面,如图

    在另一个terminal中开启gdb调试

    gdb捕获到了断点 : __x64_sys_rt_sigprocmask 函数,14号系统调用触发成功。如下图

    gdb输入命令c,使得虚拟机继续执行,到初始界面

    在虚拟机中执行 ./test 会卡住,在gdb界面查看断点分析,

    使用 l 命令查看代码情况, n 命令单步执行, step 命令进入函数内部 bt查看堆栈

     

     查看此时堆栈情况,有4层

    第一层   __x64_sys_rt_sigprocmask 系统调用函数所在

    第二层  do_syscall_64  获取系统调用号, 前往系统调用函数

    第三层  entry_SYSCALL_64 ()  中断入口,做保存线程工作,调用 do_syscall_64 

     

    gdb单步调试过程

    执行完这个函数,发现回到了函数堆栈上一层的 do_syscall_64中 接下来要执行的 syscall_return_slowpath(regs) 函数要为恢复现场做准备。

     

    继续执行,发现再次回到了函数堆栈的上一层  entry_SYSCALL_64 ()   接下来执行的是用于恢复现场的汇编指令

     

    最后伴随着两个 pop 指令 恢复了 rdi 和 rsp 寄存器。系统调用完成。

    五、总结

    整理一下整个系统调用的过程:

    1.汇编指令syscall 触发系统调用,通过MSR寄存器找到了中断函数入口,此时,代码执行到 

    /home/sy/linux_lesson/linux-5.4.34/arch/x86/entry/entry_64.S 目录下的 ENTRY(entry_SYSCALL_64) 入口  后开始通过  swapgs 和 压栈动作 保存现场

    ENTRY(entry_SYSCALL_64) 是X64系统调用入口

    swapgs 是快照保存

    call    do_syscall_64           /* returns with IRQs disabled */  是中断向量表查找系统调用号

    2.然后跳转到了 home/sy/linux_lesson/linux-5.4.34/arch/x86/entry/common.c 中的 do_syscall_64 函数  在ax寄存器中获取到系统调用号,然后去执行系统调用内容

    regs->ax = x32_sys_call_table[nr](regs) ;// 从sys_call_table 获得系统调用号

    3.然后程序跳转到 linux-5.4.34/rootfs/home/test.c函数 ,开始执行

    4.函数执行完后回到步骤3中的 syscall_return_slowpath(regs)  准备进行现场恢复操作,

    5.接着程序再次回到 arch/x86/entry/entry_64.S  执行现场的恢复,最后两句,完成了堆栈的切换

    popq    %rdi
    popq    %rsp
  • 相关阅读:
    【BestCoder #48】
    【一场模拟赛?】
    【普通の随笔】6.30
    【BestCoder #45】
    【BestCoder #44】
    【普通の惨败】GDOI2015卖萌记
    我的新博客
    【BZOJ 2964】Boss单挑战
    【NOI 2015】软件包管理器
    【NOI 2015】程序自动分析
  • 原文地址:https://www.cnblogs.com/ustc314/p/12962042.html
Copyright © 2011-2022 走看看