18

我目前正在 Linux 上学习汇编语言。我一直在使用“从头开始编程”一书,所有示例都是 32 位的。我的操作系统是 64 位的,我一直在尝试在 64 位中完成所有示例。但是我遇到了麻烦:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rbx
int $0x80

这只是调用 Linux 退出系统调用或者它应该调用。相反,它会导致 SEG FAULT,而当我这样做时

.section .data

.section .text
.global _start
_start:
movq $1, %rax
movq $2, %rbx
int $0x80

有用。显然问题是我移动到 %rax 的值。我在第二个示例中使用的价值 1 美元是“从头开始编程”所说的使用,但是 Internet 上的多个来源说 64 位系统调用号是 60 美元。参考 我做错了什么?我还应该注意哪些其他问题以及我应该使用什么作为参考?以防万一你需要知道,我在从头开始编程的第 5 章。

4

5 回答 5

20

您在 i386 和 x86_64 之间遇到了一个令人惊讶的区别:它们不使用相同的系统调用机制。正确的代码是:

movq $60, %rax
movq $2,  %rdi   ; not %rbx!
syscall

中断0x80总是调用 32 位系统调用。它用于允许 32 位应用程序在 64 位系统上运行。

出于学习的目的,您可能应该尝试完全按照本教程进行操作,而不是即时翻译为 64 位——您可能会遇到其他一些重要的行为差异。熟悉了 i386 之后,可以单独上手 x86_64 了。

于 2011-12-14T19:27:08.930 回答
14

请阅读这篇关于 x86-64 上 UNIX 和 Linux 系统调用的调用约定是什么

请注意,int 0x80在 x64 系统上使用 for syscall 是一个旧的兼容层。您应该syscall在 x64 系统上使用指令。

您仍然可以使用这种旧方法,但您需要在 x86 模式下编译二进制文件,有关详细信息,请参阅您的编译器/汇编器手册。

于 2011-12-14T19:26:34.797 回答
7

黄昏回答正确地指出了 64 位 x86 Linux 与 32 位 Linux 的系统调用机制不同。

但是,由于以下几个原因,此答案不完整且具有误导性:

正如评论中所指出的SYSENTER 它实际上不适用于许多 64 位 Linux 系统,即64 位AMD系统。

这是一个令人困惑的情况。血淋淋的细节在这里,但归根结底是:

对于 32 位内核,SYSENTER/SYSEXIT 是唯一兼容的对[在 AMD 和 Intel CPU 之间]

仅对于长模式下的 64 位内核……SYSCALL/SYSRET 是唯一兼容的对[在 AMD 和 Intel CPU 之间]

看来,在 64 位模式下的Intel CPU 上,您可以摆脱使用,SYSENTER因为它与 的作用相同SYSCALL,但 AMD 系统并非如此。

底线:始终SYSCALL在 64 位 x86 系统上的 Linux 上使用。这是 x86-64 ABI 实际指定的内容。(有关更多详细信息,请参阅这个很棒的wiki 答案。)

于 2015-07-20T06:53:28.500 回答
5

i386 和 x86_64 之间发生了很多变化,包括用于进入内核的指令和用于携带系统调用参数的寄存器。这是与您的代码等效的代码:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall

引用this answer到一个相关问题:

系统调用号位于 arch/x86/include/asm/unistd_64.h 下的 Linux 源代码中。系统调用号在 rax 寄存器中传递。参数在rdi, rsi, rdx, r10, r8, r9。使用“syscall”指令调用该调用。系统调用会覆盖 rcx 寄存器。回报是 rax。

于 2011-12-14T19:42:10.797 回答
3

如果您检查/usr/include/asm/unistd_32.hexit 对应1但在 /usr/include/asm/unistd_64.hexit 对应60

于 2012-09-29T15:43:11.660 回答