在32位Intel架构中,mmap2系统调用有6个参数。第六个参数存储在ebp
寄存器中。但是,在通过sysenter进入内核之前,会发生这种情况(在linux-gate.so.1中,内核映射到用户进程的代码页):
push %ebp
movl %esp, %ebp
sysenter
这意味着ebp
现在应该有堆栈指针的内容而不是第六个参数。 Linux如何正确获取参数?
答案 0 :(得分:2)
您在评论中链接的blog post有一个链接to Linus's post,它为我提供了答案的线索:
这意味着现在内核可以很高兴地将
%ebp
作为其中的一部分 第六个参数设置,因为系统调用重新启动将重新初始化 它要指向%ebp
中我们需要的用户级堆栈,因为 否则就完全迷失了。我是一头令人作呕的猪,并以此为荣。
- Linus Torvalds
事实证明sysenter
设计要求用户空间与内核合作以保存返回地址和用户空间堆栈指针。 (进入内核后,%esp
将成为内核堆栈。)它比int 0x80
更少的东西,这就是为什么它的速度更快。
进入内核后,内核在%esp
中拥有用户空间的%ebp
值,无论如何都需要它。它从用户空间堆栈内存访问第6个参数,以及SYSEXIT的返回地址。输入后,(%ebp)
立即拥有第6个系统调用参数。来自迈克尔的评论:"这里是32-bit sysenter_target code:看看从第417行开始的部分"
来自英特尔SYSENTER的指令参考手册条目(x86 wiki中的链接):
SYSENTER和SYSEXIT指令是配套说明,但是 它们不构成呼叫/返回对。执行SYSENTER时 指令,处理器不保存状态信息 用户代码(例如,指令指针),以及SYSENTER SYSEXIT指令也不支持在堆栈上传递参数。 使用SYSENTER和SYSEXIT指令作为配套指令 用于特权级别3代码和特权级别0之间的转换 操作系统程序,必须遵循以下约定 如下:
特权级别0代码和的段描述符 堆栈段和特权级别3代码和堆栈段 必须在描述符表中连续。这个惯例允许 处理器根据输入的值计算段选择器 SYSENTER_CS_MSR MSR。
快速系统调用“存根”例程 由用户代码执行(通常在共享库或DLL中)必须 如果a,保存所需的返回IP和处理器状态信息 需要返回调用过程。同样,操作 用SYSENTER指令调用的系统或执行程序必须 有权访问并使用此保存的返回和状态信息 返回用户代码。