2016-07-25 76 views
10

x86_64 SysV ABI的函数调用约定定义了要在rcx寄存器中传递的整数参数#4。另一方面,Linux内核系统调用ABI为了同样的目的使用r10。所有其他参数都传递到函数和系统调用的相同寄存器中。x86_64 Linux函数和系统调用之间的ABI差异

这会导致一些奇怪的事情。退房,例如,mmap glibc中实现了X32平台(针对同一存在差异):

00432ce0 <__mmap>: 
    432ce0:  49 89 ca    mov %rcx,%r10 
    432ce3:  b8 09 00 00 40   mov $0x40000009,%eax 
    432ce8:  0f 05     syscall 

因此,所有的寄存器都已经到位,但我们移动rcxr10

我想知道为什么不把系统调用ABI定义为与函数调用ABI相同,因为它们已经如此相似。

+0

在[另一个ABI答案](http://stackoverflow.com/a/35619528/224132)中,我发现了一些AMD架构师和Linux内核开发人员发布amd64邮件列表帖子的链接,之后发布了第一个AMD64芯片。这里有一些有趣的东西,比如实验结果(从编译SPECint并查看代码大小和指令数量)导致x86-64 SysV ABI选择哪个寄存器用于什么。 –

回答

5

syscall instruction旨在提供进入Ring-0以执行系统调用的更快方法。这意味着对旧方法的改进,即提高软件中断(Linux上的int 0x80)。

指令更快的部分原因是它不会更改内存,甚至不会将rsp更改为指向内核堆栈。与软件中断不同,在该软件中断中,CPU被迫允许操作系统在不发生任何破坏的情况下恢复操作,对于该命令,允许CPU假定软件知道这里发生了某些事情。

特别是,syscall在寄存器中存储用户空间状态的两个部分。在呼叫存入rcx后要返回RIP,标志存储在R11because RFLAGS is masked with a kernel-supplied value before entry to the kernel)。这意味着这两个寄存器都被指令破坏了。

由于它们被破坏,系统调用ABI使用另一个寄存器而不是rcx,因此使用r10作为第四个参数。

r10是一个自然的选择,因为in the x86-64 SystemV ABI它不是用于传递函数参数和功能并不需要保存的r10调用者的价值。所以一个系统调用包装函数mov %rcx, %r10没有任何保存/恢复。对于6-arg系统调用和SysV ABI的函数调用约定,这是不可能的。


顺便说一句,在32位系统调用ABI还访问与sysenter,这就需要用户空间和内核空间之间的合作,以允许sysenter后返回到用户空间。 (即在运行sysenter之前在用户空间中存储某个状态)。这比int 0x80更高的性能,但很尴尬。不过,glibc使用它(通过跳转到内核映射到每个进程的地址空间的vdso页面中的用户空间代码)。

AMD的syscall是与英特尔的sysenter相同的想法的另一种方法:通过不保留绝对一切而使内核的入口/出口更便宜。

+0

这比用寄存器移动替换一对商店更微妙。它不会将'rsp'更改为指向内核堆栈,因此没有理智的地方推送任何想要保存的内容。入口处的内核代码必须自行完成。 (使用'swapgs'启用'[gs:absolute_address]'来访问每个任务的内核数据)。 CPU不会在内部保存一个内核堆栈指针来用于'syscall',只是一个保存的'gs'值。我认为这是执行复杂度降低的原因。 (并且'swapgs'是一个单独的指令)。 –

+0

关于C/C++不使用'r10'的部分没有意义。内核不允许使用哪种语言来执行调用。 –

+0

没关系。我编辑它。 –

4

AMD's syscall clobbers rcx register,因此使用r10代替。

+1

而'r10'是一个纯粹的scratch寄存器:不用于函数参数传递,也不用调用保存。这可以让其他包装函数(如动态链接程序存根)将其用作临时目录,并且仍然可以使用“jmp”而不是“call”/“pop”/“ret”进行尾部调用。所以'r10'是系统调用的好选择。 'syscall' /'sysret'也使用r11。 –

+0

哎呀,我在想'r11'。 ABI表示'r10'用于传递“静态链指针”。 C/C++不使用它,所以实际上'r10'也是一个纯粹的scratch寄存器。 –

相关问题