![](/img/trans.png)
[英]What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64
[英]Why do x86-64 Linux system calls modify RCX, and what does the value mean?
我正在嘗試使用sys_brk
系統調用在 linux 中分配一些內存。 這是我嘗試過的:
BYTES_TO_ALLOCATE equ 0x08
section .text
global _start
_start:
mov rax, 12
mov rdi, BYTES_TO_ALLOCATE
syscall
mov rax, 60
syscall
事情是按照 linux 調用約定,我希望返回值在rax
寄存器中(指向分配的內存的指針)。 我在 gdb 中運行它,在進行sys_brk
系統調用后,我注意到以下寄存器內容
在系統調用之前
rax 0xc 12
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
系統調用后
rax 0x401000 4198400
rbx 0x0 0
rcx 0x40008c 4194444 ; <---- What does this value mean?
rdx 0x0 0
rsi 0x0 0
rdi 0x8 8
在這種情況下,我不太了解rcx
寄存器中的值。 哪個用作指向我用sys_brk
分配的 8 個字節開頭的指針?
與往常一樣,系統調用返回值在rax
中。 請參閱i386 和 x86-64 上 UNIX 和 Linux 系統調用的調用約定是什么。
請注意, sys_brk
的接口與brk
/ sbrk
POSIX 函數略有不同; 請參閱Linux brk(2)
手冊頁的 C 庫/內核差異部分。 具體來說, Linux sys_brk
設置程序中斷; arg 和返回值都是指針。 請參閱匯編 x86 brk() 調用使用。 該答案需要投票,因為它是該問題上唯一好的答案。
您問題的另一個有趣部分是:
在這種情況下,我不太了解 rcx 寄存器中的值
您將看到syscall
/ sysret
指令如何設計為允許內核恢復用戶空間執行但仍然很快的機制。
syscall
不做任何加載或存儲,它只修改寄存器。 它不使用特殊寄存器來保存返回地址,而是簡單地使用常規整數寄存器。
在內核返回到您的用戶空間代碼之后, RCX=RIP
和R11=RFLAGS
並非巧合。 避免這種情況的唯一方法是,如果ptrace
系統調用修改了進程保存在內核中的rcx
或r11
值。 ( ptrace
是 gdb 使用的系統調用)。 在這種情況下,Linux 將使用iret
而不是sysret
來返回用戶空間,因為較慢的通用情況iret
可以做到這一點。 (請參閱如果您在 64 位代碼中使用 32 位 int 0x80 Linux ABI 會發生什么?了解一些 Linux 系統調用入口點的演練。主要是來自 32 位進程的入口點,而不是來自 64 位系統syscall
的入口點位過程,雖然。)
syscall
不是將返回地址推送到內核堆棧(如int 0x80
所做的那樣):
設置 RCX=RIP, R11=RFLAGS (因此內核在執行syscall
之前甚至不可能看到這些 regs 的原始值)。
使用來自配置寄存器( IA32_FMASK
MSR)的預配置掩碼掩碼RFLAGS
。 這讓內核禁用中斷 (IF),直到它完成swapgs
並將rsp
設置為指向內核堆棧。 即使將cli
作為入口點的第一條指令,也會有一個漏洞窗口。 您還可以通過屏蔽DF
免費獲得cld
,因此即使用戶空間使用了std
, rep movs
/ stos
也會上升。
有趣的事實:AMD 首次提出的syscall
/ swapgs
設計並沒有掩蓋 RFLAGS,但他們在內核開發人員在 amd64 郵件列表上的反饋后更改了它(大約在 2000 年,比第一個芯片早幾年)。
跳轉到配置的syscall
入口點(設置 CS:RIP = IA32_LSTAR
)。 我認為舊的CS
值沒有保存在任何地方。
它不做任何其他事情,內核必須使用swapgs
來訪問它保存內核堆棧指針的信息塊,因為rsp
仍然具有來自用戶空間的值。
所以syscall
的設計需要一個系統調用 ABI 來破壞寄存器,這就是為什么這些值就是它們的樣子。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.