X86-64 Linux系统为什么调用RCX,该值是什么意思?

发布于 2025-02-03 09:29:27 字数 916 浏览 3 评论 0原文

我正在尝试使用sys_brk syscall在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 register中(指向分配的内存指针)。 以下寄存器内容,

Syscall之前的

rax            0xc      12
rbx            0x0      0
rcx            0x0      0
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

我在GDB中运行了此操作,在制作sys_brk syscall之后,我注意到Syscall之后的

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个字节开始的指针?

I'm trying to allocate some memory in linux with sys_brk syscall. Here is what I tried:

BYTES_TO_ALLOCATE equ 0x08

section .text
    global _start

_start:
    mov rax, 12
    mov rdi, BYTES_TO_ALLOCATE
    syscall

    mov rax, 60
    syscall

The thing is as per linux calling convention I expected the return value to be in rax register (pointer to the allocated memory). I ran this in gdb and after making sys_brk syscall I noticed the following register contents

Before syscall

rax            0xc      12
rbx            0x0      0
rcx            0x0      0
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

After syscall

rax            0x401000 4198400
rbx            0x0      0
rcx            0x40008c 4194444 ; <---- What does this value mean?
rdx            0x0      0
rsi            0x0      0
rdi            0x8      8

I do not quite understand the value in the rcx register in this case. Which one to use as a pointer to the beginning of 8 bytes I allocated with sys_brk?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱冒险 2025-02-10 09:29:27

与往常一样,系统调用返回值在rax中。参见是unix&amp; Linux系统致电I386和X86-64

请注意,sys_brkbrk/sbrk posix functions的接口略有不同;请参阅 c库/内核差异brk(2 )人页。具体而言, linux sys_brk sets 程序折断; ARG和返回值都是指示。参见 issembly x86 brk()brk()该答案需要投票,因为这是该问题上唯一的好问题。


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

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

在这种情况下, “> syscall / 指令旨在允许内核恢复用户空间执行,但仍然很快。

syscall不做任何负载或商店,它仅修改寄存器。它没有使用特殊寄存器保存返回地址,而只是使用常规整数寄存器。

rcx = rip and rflags在内核返回到您的用户空间代码之后,这不是一个巧合。此不是的唯一方法是,如果ptrace系统调用修改了该过程已保存的rcxr11值在内核内。 (ptrace是系统调用GDB使用)。在这种情况下,Linux将使用IRET而不是sysret返回用户空间,因为较慢的通用案例iret可以做到这一点。 (请参阅如果您在64位代码中使用32位INT 0x80 Linux ABI会发生什么?对于Linux的System-call输入点的某些演练。主要是从32位到32位的入口点 在64位过程中syscall


流程,不是 代码> syscall :

  • 设置rcx = rip,r11 = rflags(因此,在执行syscall之前,内核甚至不可能看到这些regs的原始值)。

  • masks rflags带有从配置寄存器(ia32_fmask msr)的预配置掩码。这使内核可以禁用中断(如果)直到完成swapgs和设置rsp指向内核堆栈。即使cli是入口点处的第一个指令,也会有一个脆弱性窗口。您还可以免费获得cld通过掩盖df SO REP MOVS/STOS即使用户空间也向上移动已经使用了std

    有趣的事实:AMD的第一个提议syscall/swapgs设计没有掩盖rflags,而是他们在AMD64邮件列表上的内核开发人员的反馈(在2000年,第一个硅之前的几年)。

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

  • 它无能为它的价值来自用户空间。

因此,syscall的设计需要一个clobbers注册的系统通话ABI,这就是为什么值是它们的原因。

The system call return value is in rax, as always. See What are the calling conventions for UNIX & Linux system calls on i386 and x86-64.

Note that sys_brk has a slightly different interface than the brk / sbrk POSIX functions; see the 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. That answer needs upvotes because it's the only good one on that question.


The other interesting part of your question is:

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

You're seeing the mechanics of how the syscall / sysret instructions are designed to allow the kernel to resume user-space execution but still be fast.

syscall doesn't do any loads or stores, it only modifies registers. Instead of using special registers to save a return address, it simply uses regular integer registers.

It's not a coincidence that RCX=RIP and R11=RFLAGS after the kernel returns to your user-space code. The only way for this not to be the case is if a ptrace system call modified the process's saved rcx or r11 value while it was inside the kernel. (ptrace is the system call gdb uses). In that case, Linux would use iret instead of sysret to return to user space, because the slower general-case iret can do that. (See What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? for some walk-through of Linux's system-call entry points. Mostly the entry points from 32-bit processes, not from syscall in a 64-bit process, though.)


Instead of pushing a return address onto the kernel stack (like int 0x80 does), syscall:

  • sets RCX=RIP, R11=RFLAGS (so it's impossible for the kernel to even see the original values of those regs before you executed syscall).

  • masks RFLAGS with a pre-configured mask from a config register (the IA32_FMASK MSR). This lets the kernel disable interrupts (IF) until it's done swapgs and setting rsp to point to the kernel stack. Even with cli as the first instruction at the entry point, there'd be a window of vulnerability. You also get cld for free by masking off DF so rep movs / stos go upward even if user-space had used std.

    Fun fact: AMD's first proposed syscall / swapgs design didn't mask RFLAGS, but they changed it after feedback from kernel developers on the amd64 mailing list (in ~2000, a couple years before the first silicon).

  • jumps to the configured syscall entry point (setting CS:RIP = IA32_LSTAR). The old CS value isn't saved anywhere, I think.

  • It doesn't do anything else, the kernel has to use swapgs to get access to an info block where it saved the kernel stack pointer, because rsp still has its value from user-space.

So the design of syscall requires a system-call ABI that clobbers registers, and that's why the values are what they are.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文