【发布时间】:2017-01-28 21:27:25
【问题描述】:
我正在尝试学习 x86_64 汇编,并使用 GCC 作为我的汇编程序。我正在使用的确切命令是:
gcc -nostdlib tapydn.S -D__ASSEMBLY__
我主要使用 gcc 作为它的预处理器。这里是tapydn.S:
.global _start
#include <asm-generic/unistd.h>
syscall=0x80
.text
_start:
movl $__NR_exit, %eax
movl $0x00, %ebx
int $syscall
这会导致分段错误。我认为问题出在以下行:
movl $__NR_exit, %eax
我使用了__NR_exit,因为它比某个幻数更具描述性。但是,看来我对它的使用是不正确的。我相信是这种情况,因为当我将有问题的行更改为以下内容时,它运行良好:
movl $0x01, %eax
进一步支持这一思路的是usr/include/asm-generic/unistd.h的内容:
#define __NR_exit 93
__SYSCALL(__NR_exit, sys_exit)
我预计 __NR_exit 的值为 1,而不是 93!显然,我误解了它的目的,因此误解了它的用法。据我所知,我很幸运 $0x01 案例工作(很像 C++ 中的未定义行为),所以我一直在挖掘......
接下来,我查找了sys_exit 的定义。我找不到它。无论如何我尝试使用它如下(有和没有前面的$):
movl $sys_exit, %eax
这不会链接:
/tmp/cc7tEUtC.o: In function `_start':
(.text+0x1): undefined reference to `sys_exit'
collect2: error: ld returned 1 exit status
我的猜测是它是系统库之一中的一个符号,由于我将-nostdlib 传递给 GCC,我没有将它链接起来。如果可能的话,我想避免为一个符号链接这么大的库。
针对 Jester 关于混合 32 位和 64 位常量的评论,我尝试按照建议使用值 0x3C:
movq $0x3C, %eax
movq $0x00, %ebx
这也导致了分段错误。我还尝试将eax 和ebx 换成rax 和rbx:
movq $0x3C, %rax
movq $0x00, %rbx
分段错误仍然存在。
Jester 然后评论说我应该使用syscall 而不是int $0x80:
.global _start
#include <asm-generic/unistd.h>
.text
_start:
movq $0x3C, %rax
movq $0x00, %rbx
syscall
这行得通,但我后来被告知,根据 System V AMD64 ABI,我应该使用 rdi 而不是 rbx:
movq $0x00, %rdi
这也可以正常工作,但最终仍然使用幻数 0x3C 作为系统调用号。
结束,我的问题如下:
-
__NR_exit的正确用法是什么? - 对于
exit系统调用,我应该使用什么来代替幻数?
【问题讨论】:
-
您正在混合 32 位和 64 位常量(它们不一样)。实际上,您甚至使用与任何一个都完全无关的 asm-generic。 32 位常量为
1,64 位常量为60。 -
@Jester 感谢您的帮助!但是我对此有点困惑:我尝试将值从
0x01更改为0x3C并再次出现分段错误。命令“file a.out”表明可执行文件是 x86_64,我在这里缺少什么? -
64位模式实际使用
syscall指令,int 0x80是遗留的32位兼容接口。 -
另外,64 位调用约定使用
rdi来传递退出代码。见wikipedia article。 -
注意在现代 Linux 中
exit是线程退出,exit_group是进程退出(libcexit()调用后者)。它们更有可能在您的情况下以相同的方式工作,但在更复杂的设置中,可能会出现额外的线程。