【问题标题】:/ptregs in syscall table/ptregs 在系统调用表中
【发布时间】:2017-03-05 14:02:19
【问题描述】:
为什么系统调用表中的某些系统调用有/ptregs?
来自arch/x86/entry/syscalls/syscall_64.tbl的示例:
54 64 setsockopt sys_setsockopt
55 64 getsockopt sys_getsockopt
56 common clone sys_clone/ptregs
57 common fork sys_fork/ptregs
58 common vfork sys_vfork/ptregs
59 64 execve sys_execve/ptregs
60 common exit sys_exit
61 common wait4 sys_wait4
【问题讨论】:
标签:
linux-kernel
system-calls
【解决方案1】:
这些是特殊的系统调用,需要在堆栈上布置完整的寄存器转储(作为struct pt_regs)。这仅适用于 64 位 x86 架构,因为它有更多的寄存器(与 32 位相比)。
系统调用处理程序 (arch/x86/entry/entry_64.S:entry_SYSCALL_64) 将大部分寄存器保存在系统调用入口的堆栈中。这样做部分是为了支持 ptrace(),部分是为了将参数传递给用 C 编写的实际系统调用处理程序(这就是为什么他们有 asmlinkage 规范,它使函数从堆栈中获取参数)。系统调用最多有 6 个参数(rdi、rsi、rdx、r10、r8、r9),并且一些寄存器用于 SYSCALL 簿记(rax、rcx、r11)。您不需要保存 rbp、rbx、r12、r13、r14、r15(因为它们是被调用者保存的),因此出于性能原因,它们不会在输入时保存。系统调用处理完成后,寄存器会从此备份中恢复,然后再返回用户空间。
但是,一些系统调用(如 execve()、fork()、sigreturn() 等)需要在堆栈上有所有寄存器(包括 rbp、rbx、r12–r15) ,在struct pt_regs。这是因为这些系统调用会导致用户空间从不同的地方重新开始执行,因此它们需要保存准确的寄存器值。它们在 syscall_64.tbl 中用/ptregs 标记,以便发生以下魔术。
通常系统调用处理程序表 (sys_call_table) 包含指向 C 函数的指针。但是对于那些特殊的系统调用,处理程序是small assembly thunks,它首先保存额外的寄存器,然后跳转到 C 代码(这就是慢路径所做的)。表中的 /ptregs 后缀指示脚本将这些存根而不是 C 函数插入到处理程序表中。