x86_64汇编Linux系统调用混淆

时间:2011-12-14 19:16:04

标签: assembly 64-bit 32-bit system-calls gas

我目前正在学习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值是“从头开始编程”所说的,但互联网上有多个来源说64位系统呼叫号码是60美元。 Reference 我究竟做错了什么?还应该注意哪些其他问题以及我应该使用什么作为参考?万一你需要知道,我在第5章“从头开始编程”。

5 个答案:

答案 0 :(得分:17)

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

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

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

出于学习的目的,您可能应该尝试完全遵循教程,而不是动态地转换为64位 - 还有一些其他重要的行为差异,您可能会遇到这些差异。一旦熟悉了i386,然后,你就可以单独拿起x86_64了。

答案 1 :(得分:12)

请阅读此What are the calling conventions for UNIX & Linux system calls on x86-64

并注意在x64系统上使用int 0x80进行系统调用是一个旧的兼容性层。你应该在x64系统上使用syscall指令。

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

答案 2 :(得分:7)

duskwuffanswer指出正确的系统调用机制对于64位x86 Linux与32位Linux不同。

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

作为pointed out in the commentsSYSENTER 实际上并不适用于许多64位Linux系统 - 即64位 AMD < / strong>系统。

这是一个令人困惑的情况。 gory details are here,但它归结为:

  

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

     

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

似乎在64位模式的 Intel CPU上,您可以使用SYSENTER,因为它与SYSCALL完全相同,但这是不是AMD系统的情况。

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

答案 3 :(得分:4)

在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。

答案 4 :(得分:2)

如果您检查/usr/include/asm/unistd_32.h退出对应于1但是在 /usr/include/asm/unistd_64.h退出对应60