简体   繁体   English

x86_64 汇编 Linux 系统调用混淆

[英]x86_64 Assembly Linux System Call Confusion

I am currently learning Assembly language on Linux.我目前正在 Linux 上学习汇编语言。 I have been using the book 'Programming From the Ground Up' and all the examples are 32-bit.我一直在使用“从头开始编程”这本书,所有的例子都是 32 位的。 My OS is 64-bit and I have been trying to do all the examples in 64-bit.我的操作系统是 64 位的,我一直在尝试用 64 位来做所有的例子。 I am having trouble however:但是,我遇到了麻烦:

.section .data

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

This merely just calls the Linux exit System call or it should.这只是调用 Linux 退出系统调用或者它应该调用。 Instead it causes a SEG FAULT and when I instead do this相反,它会导致 SEG FAULT,而当我这样做时

.section .data

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

it works.有用。 Clearly the problem is the value I move to %rax.显然问题是我移动到 %rax 的值。 The value $1 that I use in the second example is what 'Programming From the Ground Up' said to use however multiple sources on the Internet have said that the 64-bit System Call Number is $60.我在第二个示例中使用的值 $1 是“从头开始编程”所说的使用值,但是 Internet 上的多个来源表示 64 位系统调用号是 $60。 Reference What am I doing wrong? 参考我做错了什么? Also what other issues should I watch out for and what should I use for a reference?另外我应该注意哪些其他问题以及我应该使用什么作为参考? Just in case you need to know, I am on Chapter 5 in Programming From The Ground Up.以防万一你需要知道,我在从头开始编程的第 5 章。

You're running into one surprising difference between i386 and x86_64: they don't use the same system call mechanism.您在 i386 和 x86_64 之间遇到了一个令人惊讶的差异:它们不使用相同的系统调用机制。 The correct code is:正确的代码是:

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

Interrupt 0x80 always invokes 32-bit system calls.中断0x80总是调用 32 位系统调用。 It's used to allow 32-bit applications to run on 64-bit systems.它用于允许 32 位应用程序在 64 位系统上运行。

For the purposes of learning, you should probably try to follow the tutorial exactly, rather than translating on the fly to 64-bit -- there are a few other significant behavioral differences that you're likely to run into.出于学习目的,您可能应该尝试完全按照教程进行操作,而不是立即将其转换为 64 位——您可能会遇到其他一些重要的行为差异。 Once you're familiar with i386, then you can pick up x86_64 separately.一旦你熟悉了 i386,那么你就可以单独拿起 x86_64。

please read this What are the calling conventions for UNIX & Linux system calls on x86-64请阅读此x86-64 上 UNIX 和 Linux 系统调用的调用约定是什么

and note that using int 0x80 for syscall on x64 systems is an old compatibility layer.请注意,在 x64 系统上使用int 0x80进行系统调用是一个旧的兼容层。 you should use syscall instruction on x64 systems.您应该在 x64 系统上使用syscall指令。

you can still use this old method, but you need to compile your binaries in a x86 mode, see your compiler/assembler manual for details.您仍然可以使用这种旧方法,但是您需要在 x86 模式下编译二进制文件,有关详细信息,请参阅您的编译器/汇编器手册。

duskwuff 's answer points out correctly the mechanism for system calls is different for 64-bit x86 Linux versus 32-bit Linux. duskwuff回答正确地指出了 64 位 x86 Linux 与 32 位 Linux 的系统调用机制不同。

However, this answer is incomplete and misleading for a couple reasons:但是,由于以下几个原因,此答案不完整且具有误导性:

As pointed out in the comments , SYSENTER does not actually work on many 64-bit Linux systems —namely 64-bit AMD systems.正如评论中指出的那样SYSENTER实际上不适用于许多 64 位 Linux 系统——即64 位AMD系统。

It's an admittedly confusing situation.这是一个公认的令人困惑的情况。 The gory details are here , but what it comes down to is this:血腥的细节在这里,但归结为:

For a 32bit kernel, SYSENTER/SYSEXIT are the only compatible pair [between AMD and Intel CPUs]对于 32 位内核,SYSENTER/SYSEXIT 是唯一的兼容对[AMD 和 Intel CPU 之间]

For a 64bit kernel in Long mode only… SYSCALL/SYSRET are the only compatible pair [between AMD and Intel CPUs]仅对于长模式下的 64 位内核...... SYSCALL/SYSRET 是唯一兼容的对[AMD 和 Intel CPU 之间]

It appears that on an Intel CPU in 64-bit mode, you can get away with using SYSENTER because it does the same thing as SYSCALL , however this is not the case for AMD systems.似乎在 64 位模式下的Intel CPU 上,您可以使用SYSENTER因为它与SYSCALL执行相同的SYSCALL ,但是对于 AMD 系统,情况并非如此。

Bottom line: always use SYSCALL on Linux on 64-bit x86 systems .底线:始终在 64 位 x86 系统上的 Linux 上使用SYSCALL It's what the x86-64 ABI actually specifies.这是 x86-64 ABI 实际指定的内容。 (See this great wiki answer for even more details.) (有关更多详细信息,请参阅这个很棒的wiki 答案。)

Quite a lot has changed between i386 and x86_64 including both the instruction used to go into the kernel and the registers used to carry system call arguments. i386 和 x86_64 之间发生了很多变化,包括用于进入内核的指令和用于携带系统调用参数的寄存器。 Here is code equivalent to yours:这是与您的代码等效的代码:

.section .data

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

Quoting from this answer to a related question:引用这个对相关问题的回答:

The syscall numbers are in the Linux source code under arch/x86/include/asm/unistd_64.h.系统调用号位于 arch/x86/include/asm/unistd_64.h 下的 Linux 源代码中。 The syscall number is passed in the rax register.系统调用号在 rax 寄存器中传递。 The parameters are in rdi, rsi, rdx, r10, r8, r9.参数在rdi、rsi、rdx、r10、r8、r9中。 The call is invoked with the "syscall" instruction.使用“syscall”指令调用该调用。 The syscall overwrites the rcx register.系统调用会覆盖 rcx 寄存器。 The return is in rax.回报是 rax。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM