为什么x86-64 Linux系统调用会修改RCX,这个值是什么意思?

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 调用约定,我希望 return 值位于 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 个字节开头的指针?

系统调用 return 值一如既往地在 rax 中。参见 What are the calling conventions for UNIX & Linux system calls on i386 and x86-64

请注意,sys_brkbrk / sbrk POSIX 函数的界面略有不同;见C library/kernel differences section of the Linux brk(2) man page. Specifically, Linux sys_brk sets the program break; the arg and return value are both pointers. See Assembly x86 brk() call use。该答案需要点赞,因为它是该问题上唯一好的答案。


你的问题中另一个有趣的部分是:

I do not quite understand the value in the rcx register in this case

您正在了解 syscall / sysret 指令如何设计以允许内核恢复用户 space 执行但仍然很快的机制。

syscall 不做任何加载或存储,它只修改寄存器。它没有使用特殊寄存器来保存 return 地址,而是使用常规整数寄存器。

RCX=RIPR11=RFLAGS 在内核 return 之后给您的用户 space 代码 并非巧合. 不是的唯一方法是,如果ptrace系统调用在进程内部修改了进程保存的rcxr11值内核。 (ptrace 是 gdb 使用的系统调用)。在那种情况下,Linux 将使用 iret 而不是 sysret 到 return 到用户 space,因为较慢的一般情况 iret 可以做到那。 (有关 Linux 的系统调用入口点的一些演练,请参阅 。主要是来自 32 位进程的入口点,而不是来自 64 位进程中的 syscall,不过。)


而不是将 return 地址压入内核堆栈(就像 int 0x80 那样),syscall:

  • sets RCX=RIP, R11=RFLAGS(因此在执行 syscall 之前内核甚至不可能看到这些 regs 的原始值)。

  • masks RFLAGS 使用来自配置寄存器(IA32_FMASK MSR)的预配置掩码。这让内核禁用中断 (IF) 直到它完成 swapgs 并设置 rsp 指向内核堆栈。即使 cli 作为入口点的第一条指令,也会存在 window 漏洞。您还可以通过屏蔽 DF 免费获得 cld,因此 rep movs / stos 即使用户-space 使用了 std 也可以向上移动。

    有趣的事实:AMD 首次提出 syscall / swapgs 设计并没有屏蔽 RFLAGS,而是 they changed it after feedback from kernel developers on the amd64 mailing list(大约在 2000 年,比第一个硅产品早几年)。

  • 跳转到配置的syscall入口点(设置CS:RIP=IA32_LSTAR)。我认为旧的 CS 值没有保存在任何地方。

  • 它没有做任何其他事情,内核必须使用 swapgs 来访问它保存内核堆栈指针的信息块,因为 rsp 仍然具有来自 user-space.

    的值

所以 syscall 的设计需要一个破坏寄存器的系统调用 ABI,这就是为什么值是它们的原因。