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

时间:2017-12-26 20:23:15

标签: linux assembly x86-64 system-calls

我正在尝试使用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个字节的开头的指针?

1 个答案:

答案 0 :(得分:5)

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

请注意,sys_brk的界面与brk / sbrk POSIX函数略有不同;见C library/kernel differences section of the Linux brk(2) man page。具体来说, Linux sys_brk 设置程序中断; arg和返回值都是指针。见Assembly x86 brk() call use。这个答案需要upvotes,因为它是这个问题上唯一的好人。

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

  

在这种情况下,我不太了解rcx寄存器中的值

您正在看到syscall / sysret指令如何设计为允许内核恢复用户空间执行但仍然很快的机制。

syscall不执行任何加载或存储,它只修改寄存器。它不使用特殊寄存器来保存返回地址,而只使用常规整数寄存器。

内核返回用户空间代码后,RCX=RIPR11=RFLAGS并非巧合。这个的唯一方法是,ptrace系统调用修改了进程在内核中保存的rcxr11值。 (ptrace是gdb使用的系统调用)。在这种情况下,Linux将使用iret而不是sysret来返回用户空间,因为较慢的通用情况iret可以做到这一点。 (参见What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?了解Linux系统调用入口点的一些步骤。主要是32位进程的入口点,而不是64位进程中的syscall。)

而不是将返回地址推送到内核堆栈(如int 0x80那样), syscall

  • 设置RCX = RIP,R11 = RFLAGS(因此在执行syscall之前,内核甚至无法查看这些regs的原始值。)
  • 使用配置寄存器(RFLAGS MSR)中预先配置的掩码屏蔽IA32_FMASK。这允许内核禁用中断(IF),直到它完成swapgs并将rsp设置为指向内核堆栈。即使cli作为入口点的第一条指令,也会有一个漏洞窗口。您还可以通过屏蔽cld免费获得DF,这样即使用户空间使用rep movsstos / 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仍有其值用户空间。

因此syscall的设计需要一个系统调用ABI,这些ABI会被注册,这就是为什么这些值就是它们的原因。