x86_64汇编Linux系统调用混淆

Hud*_*den 16 64-bit assembly 32-bit gnu-assembler system-calls

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

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rbx
int $0x80
Run Code Online (Sandbox Code Playgroud)

这只是调用Linux退出系统调用或它应该.相反,它会导致SEG FAULT,而当我改为执行此操作时

.section .data

.section .text
.global _start
_start:
movq $1, %rax
movq $2, %rbx
int $0x80
Run Code Online (Sandbox Code Playgroud)

有用.显然,问题是我转向%rax的价值.我在第二个例子中使用的$ 1值是"从头开始编程"所说的,但互联网上有多个来源说64位系统呼叫号码是60美元.参考 我做错了什么?还应该注意哪些其他问题以及我应该使用什么作为参考?万一你需要知道,我在第5章"从头开始编程".

dus*_*uff 18

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

movq $60, %rax
movq $2,  %rdi   ; not %rbx!
syscall
Run Code Online (Sandbox Code Playgroud)

中断0x80始终调用32位系统调用.它用于允许32位应用程序在64位系统上运行.

出于学习目的,您可能应该尝试完全遵循教程,而不是直接转换为64位 - 您可能会遇到一些其他重要的行为差异.一旦熟悉了i386,可以单独选择x86_64.

  • `sysenter`不能在我的系统上运行.正确的指令是`syscall`. (9认同)

zed*_*xff 13

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

并注意int 0x80在x64系统上使用for syscall是一个旧的兼容层.你应该syscall在x64系统上使用指令.

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


Dan*_*ski 7

duskwuff回答指出,64位x86 Linux与32位Linux的系统调用机制是不同的.

但是,由于以下几个原因,这个答案是不完整和误导性的:

正如在评论中指出,SYSENTER 实际上并没有很多64位Linux系统的工作 -namely 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实际指定的内容.(有关更多详细信息,请参阅这个伟大的维基答案.)


Ada*_*man 5

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

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall
Run Code Online (Sandbox Code Playgroud)

引用这个对相关问题的回答:

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