Linux x86-64 系统调用何时破坏 %r8、%r9 和 %r10?
When does Linux x86-64 syscall clobber %r8, %r9 and %r10?
我刚刚浏览了 Linux 内核源代码树并阅读了文件 tools/include/nolibc/nolibc.h。
我看到此文件中的 syscall
在 clobber 列表中使用了 %r8
、%r9
和 %r10
。
还有一条评论说:
rcx and r8..r11 may be clobbered, others are preserved.
据我所知,syscall
只会破坏 %rax
、%rcx
和 %r11
(以及记忆)。
有没有 syscall
破坏 %r8
、%r9
和 %r10
的真实例子?
只有 64 位模式下的 32 位系统调用(例如,通过 int 0x80
)才会对这些寄存器以及 R11 进行操作。 ().
syscall
正确地 saves/restores 所有 regs 包括 R8、R9 和 R10,因此使用它的用户 space 可以假设它们保持它们的值,除了 RAX return 价值。 (内核的系统调用入口点甚至保存了 RCX 和 R11,但此时它们已经被 syscall
指令本身用原始 RIP 和屏蔽前 RFLAGS 值覆盖。)
那些与 R11 一起,是在 函数 调用约定中被调用破坏的非遗留寄存器,因此编译器自然地为内核中的 C 函数生成代码保留 R12-R15,即使 asm 入口点没有保存它们。
目前,64 位 int 0x80
入口点只是将 0
推送到进程状态结构中的调用破坏 R8-R11 寄存器,它将从 return 之前恢复发送给用户 space,而不是原始寄存器值。
从历史上看,来自 32 位用户的 int 0x80
入口点 - space 根本没有 save/restore 这些寄存器。所以它们的值是编译器生成的内核代码。这被认为是无辜的,因为 32 位模式无法读取这些寄存器,直到意识到 user-space 可以远跳到 64 位模式,使用内核使用的相同 CS 值正常的 64 位用户 space 进程,选择系统范围的 GDT 条目。 因此存在内核数据的实际信息泄漏,通过将这些寄存器置零来修复。
IDK 64 位用户 space 与 32 位用户是否曾经或仍然存在单独的入口点,或者它们在 struct pt_regs
布局上有何不同。 int 0x80
泄露 r8..r11 的历史情况对 64 位用户来说没有意义-space;泄漏本来是显而易见的。所以如果他们现在统一了,他们一定不是过去。
根据 x86-64 ABI 关于系统调用部分 A.2 AMD64 Linux 内核
约定,A.2.1 调用约定 [1]:
User-level applications use as integer registers for passing the
sequence %rdi
, %rsi
, %rdx
, %rcx
, %r8
and %r9
. The kernel interface
uses %rdi
, %rsi
, %rdx
, %r10
, %r8
and %r9
.
A system-call is done via the syscall
instruction. The kernel
destroys registers %rcx
and %r11
.
The number of the syscall
has to be passed in register %rax
.
System-calls are limited to six arguments, no argument is passed
directly on the stack.
Returning from the syscall
, register %rax
contains the result of
the system-call. A value in the range between -4095 and -1
indicates an error, it is -errno.
Only values of class INTEGER or class MEMORY are passed to the
kernel.
从 (2)、(5) 和 (6),我们可以得出结论,Linux x86-64 系统调用破坏了 %rax
、%rcx
和 %r11
(和 "memory"
).
Link: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/x86-64-psABI [1]
我刚刚浏览了 Linux 内核源代码树并阅读了文件 tools/include/nolibc/nolibc.h。
我看到此文件中的 syscall
在 clobber 列表中使用了 %r8
、%r9
和 %r10
。
还有一条评论说:
rcx and r8..r11 may be clobbered, others are preserved.
据我所知,syscall
只会破坏 %rax
、%rcx
和 %r11
(以及记忆)。
有没有 syscall
破坏 %r8
、%r9
和 %r10
的真实例子?
只有 64 位模式下的 32 位系统调用(例如,通过 int 0x80
)才会对这些寄存器以及 R11 进行操作。 (
syscall
正确地 saves/restores 所有 regs 包括 R8、R9 和 R10,因此使用它的用户 space 可以假设它们保持它们的值,除了 RAX return 价值。 (内核的系统调用入口点甚至保存了 RCX 和 R11,但此时它们已经被 syscall
指令本身用原始 RIP 和屏蔽前 RFLAGS 值覆盖。)
那些与 R11 一起,是在 函数 调用约定中被调用破坏的非遗留寄存器,因此编译器自然地为内核中的 C 函数生成代码保留 R12-R15,即使 asm 入口点没有保存它们。
目前,64 位 int 0x80
入口点只是将 0
推送到进程状态结构中的调用破坏 R8-R11 寄存器,它将从 return 之前恢复发送给用户 space,而不是原始寄存器值。
从历史上看,来自 32 位用户的 int 0x80
入口点 - space 根本没有 save/restore 这些寄存器。所以它们的值是编译器生成的内核代码。这被认为是无辜的,因为 32 位模式无法读取这些寄存器,直到意识到 user-space 可以远跳到 64 位模式,使用内核使用的相同 CS 值正常的 64 位用户 space 进程,选择系统范围的 GDT 条目。 因此存在内核数据的实际信息泄漏,通过将这些寄存器置零来修复。
IDK 64 位用户 space 与 32 位用户是否曾经或仍然存在单独的入口点,或者它们在 struct pt_regs
布局上有何不同。 int 0x80
泄露 r8..r11 的历史情况对 64 位用户来说没有意义-space;泄漏本来是显而易见的。所以如果他们现在统一了,他们一定不是过去。
根据 x86-64 ABI 关于系统调用部分 A.2 AMD64 Linux 内核 约定,A.2.1 调用约定 [1]:
User-level applications use as integer registers for passing the sequence
%rdi
,%rsi
,%rdx
,%rcx
,%r8
and%r9
. The kernel interface uses%rdi
,%rsi
,%rdx
,%r10
,%r8
and%r9
.A system-call is done via the
syscall
instruction. The kernel destroys registers%rcx
and%r11
.The number of the
syscall
has to be passed in register%rax
.System-calls are limited to six arguments, no argument is passed directly on the stack.
Returning from the
syscall
, register%rax
contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.Only values of class INTEGER or class MEMORY are passed to the kernel.
从 (2)、(5) 和 (6),我们可以得出结论,Linux x86-64 系统调用破坏了 %rax
、%rcx
和 %r11
(和 "memory"
).
Link: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/x86-64-psABI [1]