科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34

用户态内嵌汇编触发系统调用

配置ARM64环境:

sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev  build-essential git bison flex libssl-dev
sudo apt install gdb-multiarch
# 为arm64配置内核编译选项:
make defconfig ARCH=arm64
make menuconfig ARCH=arm64

触发系统调用的方法

time 系统调用为例,分别使用C库函数和 int $0x80/syscallsvc 指令汇编代码触发系统调用。
C库函数 time_t time(time_t *seconds) 返回自 1970-01-01 00:00:00(UTC国际时区)起经过的时间,以秒为单位。如果 seconds 不为空,则返回值也存储在变量 seconds 中。
C库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 stuct tm 结构体。timer 的值被分解到 stuct tm 结构体中,并用本地时区表示。

使用C库函数触发time系统调用

#include 
#include 
int main()
{
	time_t tt;
	struct tm *t;
	tt = time (NULL);
	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;
}

gcc -o time time.c -static
objdump -S time > time64.S
64位机器上编出32位代码需加-m32
sudo apt-get install gcc-multilib
gcc -o time time.c -static -m32
objdump -S time > time32.S

系统调用的参数传递和系统调用号

系统调用表

Linux源代码中的 arch/x86/entry/syscalls/syscall_32.tblarch/x86/entry/syscalls/syscall_64.tbl 分别定义了32位 x86x86-64 的系统调用内核处理函数,它们最终通过脚本转换按照系统调用号依次存入ia32_sys_call_tablesys_call_table 数组中。而系统调用内核处理函数则是由系统调用入口 entry_INT80_32entry_SYSCALL_64 分别调用的do_int80_syscall_32do_syscall_64 来调用执行。do_int80_syscall_32do_syscall_64 的代码如 arch/x86/entry/common.c

start_kernel 函数开始执行之前是用汇编代码初始化 CPU ,其中非常重要的就是将异常向量表的基地址(vectors)配置到 VBAR_EL1 寄存器中,从arch\arm64\kernel\head.S 中可以找到代码,这段代码配置了不仅配置了异常向量表,还配置了0号进程的内核堆栈和进程描述符。

科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34_第1张图片

异常向量表分为4组,每组有4个向量入口地址,分别处理4种不同类型的异常。每个向量入口空间128字节,也就是说,在这个异常向量空间里可以放入32条指令(每条指令4字节)。举个例子,如果用户态程序执行了 svc 指令,这时CPU自动把 VBAR_EL1 寄存器的值(vectors),和第3组Synchronous 的偏移量 0x400 相加,即 vectors + 0x400 ,得出该异常向量空间的入口地址,然后跳转到那里,执行里面的第一条指令。

科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34_第2张图片

ARM64处理保存现场和恢复现场

保存现场

在Linux系统中系统调用发生时,CPU会把当前程序指针寄存器PC放入ELR_EL1 寄存器里,把 PSTATE 放入SPSR_EL1 寄存器里,同时Linux系统从用户态切换到内核态(从EL0切换到EL1),这时SP指的是SP_EL1 寄存器,用户态堆栈的栈顶地址依然保存在 SP_EL0 寄存器中。也就是说异常(这里是指系统调用)发生时CPU的关键状态sppcpstate 分别保存在SP_EL0ELR_EL1SPSR_EL1 寄存器中。保存现场的主要工作如上代码所示,是保存 x0-x30sppcpstate ,这和 struct pt_regs 数据结构的起始部分正好一一对应。
科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34_第3张图片

恢复现场

kernel_exit 0 负责恢复现场的代码和 kernel_entry 0 负责保存现场的代码相对应
科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34_第4张图片

内核堆栈pt_regs等

pt_regs 的数据结构如下:
科软Linux | lab4.以time/gettimeofday系统调用为例分析ARM64 Linux 5.4.34_第5张图片

你可能感兴趣的:(——,计算机考研,——,linux,运维,服务器)