lab4:以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34

1.环境准备

环境配置,安装ARM64 Linux 5.4.34

下载qemu

apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev libpython-dev python-pip python-capstone virtualenv
wget https://download.qemu.org/qemu-4.2.1.tar.xz
tar xvJf qemu-4.2.1.tar.xz
cd qemu-4.2.1
./configure --target-list=x86_64-softmmu,x86_64-linux-user,arm-softmmu,arm-linux-user,aarch64-softmmu,aarch64-linux-user --enable-kvm
make 
sudo make install

配置arm64环境

 在linux-5.3.34文件夹中,配置内核编译选项:

make defconfig ARCH=arm64
 
make menuconfig ARCH=arm64

在menuconfig中完成以下更改

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)

更改后开始编译:

export ARCH=arm64
 
export CROSS_COMPILE=aarch64-linux-gnu-
 
make Image -j$(nproc)

制作跟文件系统:

与上个实验类似,下载busybox并解压:

wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
 
tar -jxvf busybox-1.36.0.tar.bz2
 
cd busybox-1.36.0

编译前需要设置:

export ARCH=arm64
 
export CROSS_COMPILE=aarch64-linux-gnu-

然后配置:

make menuconfig
 
Settings  --->
    [*] Build static binary (no shared libs)

之后就可以编译:

make -j$(nproc) && make install

/rootfs目录下添加init脚本:

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Wellcome TryOS!"
echo "--------------------"
cd home
/bin/sh

并加上运行权限:

chmod +x init

打包成镜像文件存放在与rootfs的同级目录下:

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

启动内核:

qemu-system-aarch64 -m 128M -smp 1 -cpu cortex-a57 -machine virt -kernel ~/linux_lab4/linux-5.4.34/arch/arm64/boot/Image -initrd ~/linux_lab4/rootfs.cpio.gz -append "rdinit=/init console=ttyAMA0 loglevel=8" -nographic -s

其中:-m 128M                 内存为128M  

          -smp  1                    单核

         -smp cortex-a57       cpu为cortex-a57 

         -kernel                      镜像文件

内核启动后:

其中的tty问题可以忽略,成功启动内核。

在rootfs目录下创建test.c文件,文件内容如下:

#include <stdio.h>
#include <time.h>
#include <sys/time.h>
 
int main()
{
      time_t tt;
      struct timeval tv;
      struct tm *t;
#if 0
      gettimeofday(&tv,NULL);         // 使用库函数的方式触发系统调用
#else 
      asm volatile(                  // 使用内嵌汇编的方式触发系统调用
          "add   x0, x29, 16\n\t"    //X0寄存器用于传递参数&tv
          "mov   x1, #0x0\n\t"       //X1寄存器用于传递参数NULL
          "mov   x8, #0xa9\n\t"      //使用X8传递系统调用号169
          "svc   #0x0\n\t"            //触发系统调用
      );
#endif
      tt = tv.tv_sec;                    //tv是保存获取时间结果的结构体
      t = localtime(&tt);                //将世纪秒转换成对应的年月日时分秒
      printf("time: %d/%d/%d %d:%d:%d\n",
             t->tm_year + 1900,
             t->tm_mon,
             t->tm_mday,
             t->tm_hour,
             t->tm_min,
             t->tm_sec);
      return 0;
}

将其编译后,重新生成镜像文件:

aarch64-linux-gnu-gcc -o test test.c -static
 
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

在linux-5.4.34目录下运行python脚本:

python3 ./scripts/gen_compile_commands.py

同时在该目录下创建.vscode文件夹:,并在该文件夹下创建4个文件:

文件内容如下:

 c_cpp_properties.json: 

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/arch/x86/include/**",
                "${workspaceFolder}/include/**",
                "${workspaceFolder}/include/linux/**",
                "${workspaceFolder}/arch/x86/**",
                "${workspaceFolder}/**"
            ],
            "cStandard": "c11",
            "intelliSenseMode": "gcc-x64",
            "compileCommands": "${workspaceFolder}/compile_commands.json"
        }
    ],
    "version": 4
}

 launch.json: 

{
    "version": "0.2.0",
    "configurations": [
      {
        "name": "(gdb) linux",
        "type": "cppdbg",
        "request": "launch",
        "preLaunchTask": "vm",
        "program": "${workspaceRoot}/vmlinux",
        "miDebuggerPath":"/usr/bin/gdb-multiarch",
        "miDebuggerServerAddress": "localhost:1234",
        "args": [],
        "stopAtEntry": true,
        "cwd": "${workspaceFolder}",
        "environment": [],
        "externalConsole": false,
        "MIMode": "gdb",
        "miDebuggerArgs": "-n",
        "targetArchitecture": "x64",
        "setupCommands": [
          {
            "text": "dir .",
            "ignoreFailures": false
          },
          {
            "text": "add-auto-load-safe-path ./",
            "ignoreFailures": false
          },
          {
            "text": "-enable-pretty-printing",
            "ignoreFailures": true
          }
        ]
      }
    ]
  }

 settings.json:

{
    "search.exclude": {
        "**/.git": true,
        "**/.svn": true,
        "**/.DS_Store": true,
        "**/drivers": true,
        "**/sound": true,
        "**/tools": true,
        "**/arch/alpha": true,
        "**/arch/arc": true,
        "**/arch/c6x": true,
        "**/arch/h8300": true,
        "**/arch/hexagon": true,
        "**/arch/ia64": true,
        "**/arch/m32r": true,
        "**/arch/m68k": true,
        "**/arch/microblaze": true,
        "**/arch/mn10300": true,
        "**/arch/nds32": true,
        "**/arch/nios2": true,
        "**/arch/parisc": true,
        "**/arch/powerpc": true,
        "**/arch/s390": true,
        "**/arch/sparc": true,
        "**/arch/score": true,
        "**/arch/sh": true,
        "**/arch/um": true,
        "**/arch/unicore32": true,
        "**/arch/xtensa": true
    },
    "files.exclude": {
        "**/.*.*.cmd": true,
        "**/.*.d": true,
        "**/.*.o": true,
        "**/.*.S": true,
        "**/.git": true,
        "**/.svn": true,
        "**/.DS_Store": true,
        "**/drivers": true,
        "**/sound": true,
        "**/tools": true,
        "**/arch/alpha": true,
        "**/arch/arc": true,
        "**/arch/c6x": true,
        "**/arch/h8300": true,
        "**/arch/hexagon": true,
        "**/arch/ia64": true,
        "**/arch/m32r": true,
        "**/arch/m68k": true,
        "**/arch/microblaze": true,
        "**/arch/mn10300": true,
        "**/arch/nds32": true,
        "**/arch/nios2": true,
        "**/arch/parisc": true,
        "**/arch/powerpc": true,
        "**/arch/s390": true,
        "**/arch/sparc": true,
        "**/arch/score": true,
        "**/arch/sh": true,
        "**/arch/um": true,
        "**/arch/unicore32": true,
        "**/arch/xtensa": true
    },
    "[c]": {
        "editor.detectIndentation": false,
        "editor.tabSize": 8,
        "editor.insertSpaces": false
    },
    "C_Cpp.errorSquiggles": "disabled"
}

tasks.json:

{
    "version": "2.0.0",
    "tasks": [
      {
        "label": "vm",
        "type": "shell",
        "command": "qemu-system-aarch64 -m 128M -smp 1 -cpu cortex-a57 -machine virt -kernel arch/arm64/boot/Image -initrd ~/linux_lab4/rootfs.cpio.gz -append \"rdinit=/init console=ttyAMA0 loglevel=8\" -nographic -s",
        "presentation": {
          "echo": true,
          "clear": true,
          "group": "vm"
        },
        "isBackground": true,
        "problemMatcher": [
          {
            "pattern": [
              {
                "regexp": ".",
                "file": 1,
                "location": 2,
                "message": 3
              }
            ],
            "background": {
              "activeOnStart": true,
              "beginsPattern": ".",
              "endsPattern": ".",
            }
          }
        ]
      },
      {
        "label": "build linux",
        "type": "shell",
        "command": "make",
        "group": {
          "kind": "build",
          "isDefault": true
        },
        "presentation": {
          "echo": false,
          "group": "build"
        }
      }
    ]
}

在vscode终端运行test,可以看到成功打断: 

 

查看调用堆栈,分析过ARM64 下系统调用的执行过程:

ARM64 架构下 Linux 系统调用由同步异常 svc 指令触发,当用户态(EL0 级)程序调用库函数 gettimeofday() 从而触发系统调用的时候,先把系统调用的参数依次放入 X0-X5 这 6 个寄存器(Linux 系统调用最多有 6 个参数,ARM64 函数调用参数可以使用 X0-X7 这 8 个寄存器),然后把系统调用号放在 X8 寄存器里,最后执行 svc 指令,CPU 即进入内核态(EL1 级)。本文使用内嵌汇编触发系统调用,我们也编写相应的汇编代码完成了上述过程。

ARM64 架构的 CPU 中,Linux 系统调用(同步异常)和其他异常的处理过程大致相同。异常发生时,CPU 首先把异常的原因(比如执行 svc 指令触发系统调用)放在 ESR_EL1 寄存器里;把当前的处理器状态(PSTATE)放入 SPSR_EL1 寄存器里;把当前程序指针寄存器 PC 的值存入 ELR_EL1 寄存器里(保存断点),然后 CPU 通过异常向量表(vectors)基地址和异常的类型计算出异常处理程序的入口地址,即 VBAR_EL1 寄存器加上偏移量取得异常处理的入口地址,接着开始执行异常处理入口的第一行代码。这一过程是 CPU 硬件自动完成的,不需要程序干预。

随后,以 svc 指令对应的 el0_sync 为例,el0_sync 处的内核汇编代码首先做的就是保存异常发生时程序的执行现场(保存现场,即用户栈、通用寄存器等),然后根据异常发生的原(ESR_EL1 寄存器中的内容)跳转到 el0_svc,el0_svc 会调用 el0_svc_handler、el0_svc_common 函数,将 X8 寄存器(regs->regs[8])中存放的系统调用号传递给 invoke_syscall 函数。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值