【问题标题】:Syscall or sysenter on 32 bits Linux?32 位 Linux 上的系统调用或系统输入?
【发布时间】:2013-03-13 23:36:18
【问题描述】:

从 MS‑DOS 开始,我就知道使用中断进行系统调用。在旧论文中,我看到引用 int 80h 在 Linux 上调用系统函数。很长一段时间以来,我知道 int 80h 已被弃用,取而代之的是 syscall 指令。但我无法让它在我的 32 位机器上运行。

问题

syscall 指令是否只能在 64 位平台上使用? 32位Linux不使用syscall吗?

样本测试

在我的 32 位 Linux (Ubuntu Precise) 上,该程序以核心转储终止:

global _start

_start:
        mov     eax, 4                ; 4 is write
        mov     ebx, 1                ; 1 is stdout
        mov     ecx, message          ; address of string
        mov     edx, length           ; number of bytes
        syscall

        mov     eax, 1                ; 1 is exit
        xor     ebx, ebx              ; return code 0
        syscall

message:
        db  10,"Hello, World",10,10
length  equ $ - message

我尝试使用 sysenter 而不是 syscall,但它以同样的方式崩溃。

【问题讨论】:

  • 确实有一些相关的东西,here,但它没有回答这个问题。他说syscall 在 Intel CPU 的 32 位模式下不可用,但汇编程序以 32 位模式编译它;断言要么不清楚,要么是错误的。然后,由于一条非法指令,我得到了核心转储,但这条指令在从 Pentium II 开始的所有 Intel CPU 上都可用,而我的远不止于此。他提到了sysenter,我尝试了同样的结果。无论如何,我从未在 Linux ABI 中看到任何提及 sysenter,只有 int 80hsyscall
  • 嗯,根据“System Calls (wiki.osdev.org)”,syscall 相当于 AMD 的 Intel sysenter。它说“在 Intel CPU 上,从 Pentium II 开始,出现了一个新的指令对 sysenter/sysexit。通过限制更改模式的开销,它可以更快地从用户模式切换到内核模式。 AMD 创建了一个类似的指令对:Syscall/Sysret。但是这些指令的行为与 Intel 的不同。
  • 供参考,int 0x80 一直是 Linux 的东西。 Windows 和 MS-DOS 使用 int 0x2E
  • 对于操作系统内核开发人员的建议是二选一。然后他们的选择成为 ABI 的一部分,如果您正在为某个操作系统开发,您必须尊重该 ABI。例如,Linux/i386 使用 int 0x80 并在寄存器中传递参数,而 MirBSD/i386 使用 int 0x80 并在堆栈上传递参数,中间有一个帧指针(这意味着使用 cdecl 时用户空间没有设置成本)。跨度>

标签: linux assembly 32-bit system-calls sysenter


【解决方案1】:

经过一些网络搜索,我找到了 StackOverflow 上的另一个主题:Linux invoke a system call via sysenter tutorial。它说调用系统的推荐方式,既不是使用int 80h,也不是syscall,也不是sysenter,而是linux-gate.so

关于崩溃和核心转储的问题仍然存在。最后我的猜测是,尽管syscallsysenter 指令都可以作为CPU 指令使用,但可能是Linux 内核没有正确设置这个“入口点”,当它决定它在给定的硬件上不是真正有用时平台。

似乎在 32 位平台上,sysentersyscall 可能可用,但始终可用,仅在 64 位平台上。

虽然我觉得这回答了我的问题,但我仍然欢迎更多材料,例如我上述猜测的权威参考。

-- 更新--

至少,我可以找到证实上述内容的这一点。这仍然不是一个权威的参考,但我相信它似乎足够可信。

What is linux-gate.so.1?,说:

调用系统调用的首选方式是 由内核在启动时确定,以及 显然这个盒子使用 sysenter。

另外,从另一个来源,一个示例 FASM 程序集源(如果您使用 NASM,需要一些翻译),通过linux-gate.soFinding linux-gate.so.1 in Assembly 调用系统函数。

【讨论】:

  • 请注意,使用 sysenter/syscall 时,约定可能与使用 int 0x80 的约定完全不同。对于sysenter,你需要,iirc,push ecx push edx push ebp mov ebp,esp,就在sysenter之前
  • 请注意,由于它是 determined by the kernel at boot time,因此您将获得 int 80、sysenter 或 syscall,具体取决于 CPU 的功能。 Int 80 将始终有效,但速度较慢,并且在较新的 CPU 上,只有 sysenter 或 syscall 之一有效。这不是程序应该做出的选择,内核会为您做出最佳选择。
【解决方案2】:

Intel manual 表示syscall 在兼容(32 位)模式下无效,因此不应被内核使用。

这似乎是英特尔独有的限制:https://stackoverflow.com/a/29784932/895245 AMD 没有,但 Linux 肯定必须支持英特尔 :-)

sysenter 似乎是当今最好的方法,因为它比 int 0x80 更快,但它应该通过 VDSO 间接使用,如 How to invoke a system call via sysenter in inline assembly (x86/amd64 linux)? 中所述

【讨论】:

  • 有人提出了与最初问题相同的评论。我回答说指令是由我使用的汇编程序编译的,甚至没有警告,并且操作码被记录为 32 位。可能这仅在 64 位架构的 32 位模式下无效……无论如何,正如您所说,在 Linux 的上下文中,最好依赖 VDSO(即使没有 Glibc,也有方法可以检索它)。
  • @Hibou57:根据this comment thread:它仅在 AMD CPU 上的 32 位模式下有效。我也对它以 32 位代码进行汇编和反汇编感到惊讶,但显然 AMD 引入了与 AMD64 架构分开(也许之前)的系统调用。英特尔的指令赢得了 32 位模式(和往常一样),但 AMD 成为 AMD64 的标准。
猜你喜欢
  • 1970-01-01
  • 2014-11-21
  • 1970-01-01
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 2011-06-30
  • 2011-03-19
  • 2023-04-08
相关资源
最近更新 更多