2016-07-25 109 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寄存器。 –

相關問題