查看OSdev wiki for details on sysenter
,包括关于如何避免安全/安全问题的说明。另请参阅英特尔/ AMD手册。它们涉及操作系统开发人员所需的大量细节。有关链接,请参阅x86标记wiki。
的各种系统调用指令概述:
int
:可用自永远(8086)
- 通过执行一个无效的指令陷阱,显然是the fastest way to enter the kernel on 80386。 (但情况不再如此)。
- call gate(即,
far call
)。请参阅OSdev链接以获取有关该细节和陷阱的详细信息。
sysenter
:(http://wiki.osdev.org/Sysenter)英特尔在x86-64之前推出,不久之后被AMD采用(多年前)。适用于所有现代x86 CPU。非常简约的设计需要用户空间合作才能使内核能够返回,因为它不会将EIP,ESP或EFLAGS保存在任何地方。
Linux仅在32位和64位内核中支持32位进程的系统调用。 IDK,如果你可以设计一个用于64位系统调用的内核。 (我知道这不是问题,但它是相关的。)
使用sysenter
需要用户空间合作来提供返回地址并保存它自己的ESP和EFLAGS。在Linux中,内核会导出一个代码页面,其中包含跳舞的用户空间。用户空间预计call
这个代码,而不是直接使用sysenter
,但随意设计你的操作系统,但你想要的。如果你在其他地方找不到示例,那么查看Linux的代码可能对此舞蹈的双方都有用。
从64位用户空间syscall
:随处可见,因为英特尔实现了它与AMD64的其余一起。精心设计的接口在进入内核之前会屏蔽RFLAGS(带有可配置的掩码),因此您可以避免竞争窗口(如果必须使用cli
手动禁用中断)。与swapgs
一起用于内核访问其堆栈等。
在主流x86操作系统(如Linux)上,syscall
是进行64位系统调用的唯一方法。
syscall
从32位用户空间:从长模式syscall
,仅在AMD处理器可用一个完全不同的指令。对于32位内核(传统模式)与运行32位用户空间(compat模式)的64位内核,内核端接口不同。
Linux内核有它的一些有用的意见:
entry_64_compat.S
32-bit SYSCALL entry(32位syscall
入口点到64位内核)
/* ...
* - Most programmers do not directly target AMD CPUs, and the 32-bit
* SYSCALL instruction does not exist on Intel CPUs. Even on AMD
* CPUs, Linux disables the SYSCALL instruction on 32-bit kernels
* because the SYSCALL instruction in legacy/native 32-bit mode (as
* opposed to compat mode) is sufficiently poorly designed as to be
* essentially unusable.
也许玩具操作系统可以使用它,而不必担心任何问题使其不适用于Linux,IDK。但除非你只是好奇,否则不要浪费时间。 OTOH,如果你对CPU设计感兴趣,找出ISA设计的问题可能会很有趣。
顺便说一句,当AMD在设计AMD64,他们得到了来自Linux内核开发者认为改善64位syscall
设计的AMD64邮件列表上的一些反馈(以可配置掩盖RFLAGS),因为他们最初的设计本来问题适用于Linux 。链接到那些已存档的邮件列表帖子in this answer。
建议:使用sysenter
为32位内核。它应该可以在任何地方使用,包括在AMD CPU上使用多年。不支持它的古代CPU可以使用int 0x80
ABI(或者您为操作系统挑选的任何数字),如果您想添加第二个兼容性ABI。
Linux内核入口点的注释很好,写得相当易读。在编写What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?时,我很容易地通过使用syscall
(原生64位系统调用)或int 0x80
或sysenter
(32位系统调用,通常来自compat)来确定在64位内核的入口点发生了什么。模式,但int 0x80
是支持64位的进程。但它仍然调用32位ABI!)有复杂的东西的情况下,要在一堆各种跟踪/调试启用,但其他部分是相当容易跟随。查看这个答案,了解一些Linux的系统调用处理内部。
在arch/x86/entry
,这些都是利益的主要文件:
entry_32.S
:从用户空间进入32位内核代码。 (遗留模式)
entry_64_compat.S
:用于从32位用户空间(compat模式 - >长模式)输入的64位内核代码。
entry_64.S
:用于从64位用户空间(长模式 - >长模式)进入的64位内核代码。
你应该能够找到的sysenter
舞蹈是传递内核,它需要返回到用户空间的值的用户空间侧的Linux的VDSO代码。 (What is better "int 0x80" or "syscall"?)。相关:What is better "int 0x80" or "syscall"?和The Definitive Guide to Linux System Calls将提供有关Linux所做设计选择的一些有用信息。
是真的,sysret
指令是不是安全?
当返回到64位用户空间时,英特尔和AMD都有与非规范RIP分离的错误。例如英特尔,Linux's entry_64.S
这样描述:
/*
* On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP
* in kernel space. This essentially lets the user take over
* the kernel, since userspace controls RSP.
如果ptrace
系统调用(例如,通过一个调试器中进行)改变了进程的RIP
的保存价值,非规范的地址,可以发生。 Linux检查是否可以使用sysret
,如果不使用它的iret
返回路径。 (sysret
路径速度足够快,因此值得做更多的工作来检查它是否安全)。
注意,如果一个系统调用块/睡眠状态时,“主副本”用户空间的整数寄存器状态的是它的内核堆栈,系统调用入口点推它。 (在Linux中,其他设计是可能的!)但是,无论如何,这就是为什么最终会出现奇怪的保存状态,用户空间不能运行syscall
(因为它将在jmp
上违反非规范地址)或saved_rcx != saved_RIP
(64位syscall
设置RCX = RIP,和R11 = RFLAGS(屏蔽之前),所以它则会覆盖RCX和R11,但允许内核恢复RIP和RFLAGS。)
我不知道该怎么32位syscall
作品,对不起,我在这里得到话题。但是我怀疑你可能读到的有关sysret
不安全的内容是关于64位内核的。
IDK如果在32位内核sysret
或64位内核sysret
-to-compat-mode中有任何类似的错误。
您是否尝试过在手动或在线资源中查看?你发现了什么? – fuz
可能重复[什么是更好的“int 0x80”或“系统调用”?](https://stackoverflow.com/questions/12806584/what-is-better-int-0x80-or-syscall) –
@BoPersson问题似乎不相关:另一个问题只关注Linux;这个问题关注于一个自编写的内核。 –