在Linux(内核3.14.4)中,进程通常有两个堆栈 - 用户级堆栈和内核堆栈。在进程中调用系统调用时,操作系统将首先将当前寄存器推入内核堆栈,然后切换到内核堆栈以执行系统调用。当系统调用结束时,先前推入内核堆栈的寄存器的值将被输出并恢复到相应的寄存器,以便用户级进程可以继续执行。 (如果我错了,请纠正我)
在下面的代码中,函数main()调用系统调用,并在系统调用之前和之后打印出用户级别寄存器RSP(指向堆栈顶部)的值。通常,在这种情况下,rsp_beofer等于rsp_after。
我想查看系统调用是否可以更改用户级别的RSP。因此,在系统调用内部,我首先验证了用户堆栈的RSP是在内核堆栈上。然后我修改了内核堆栈上的RSP值(减去0x10)。我原以为rsp_before比rsp_after大0x10。但是,事实证明rsp_before仍然等于rsp_after。
我想知道为什么即使我在系统调用中修改了用户级别的RSP后这两个值仍然相等?系统调用在完成执行后如何恢复用户级别寄存器?
int main(void){
#define __syscall_clobber "r11","rcx","memory"
#define __syscall "syscall"
long ret;
register unsigned long rsp_before asm("rsp");
printf("rsp_before = %p\n", (unsigned long *)rsp_before);
/*invoke the system call, in which the value of user level RSP on the kernel stack is decreased by 0x10*/
__asm__ volatile
(
__syscall
: "=a" (ret)
: "0" (316) : __syscall_clobber
);
register unsigned long rsp_after asm("rsp");
printf("rsp_after = %p\n", (unsigned long *)rsp_after);
}
/*syscall 316*/
asmlinkage long syscall_316(void)
{
/*get the bottom of current kernel stack*/
register unsigned long rbp asm("rbp");
/*the user level RSP is stored at '(unsigned long *)rbp + 15'*/
*((unsigned long *)rbp + 15) = *((unsigned long *)rbp + 15) - 0x10;
return 0;
}