![](/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.