【问题标题】:What is the correct constant for the exit system call?退出系统调用的正确常量是什么?
【发布时间】: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

这也导致了分段错误。我还尝试将eaxebx 换成raxrbx

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 是进程退出(libc exit() 调用后者)。它们更有可能在您的情况下以相同的方式工作,但在更复杂的设置中,可能会出现额外的线程。

标签: gcc assembly x86-64


【解决方案1】:

获取系统调用号的正确头文件是sys/syscall.h。这些常量称为SYS_###,其中### 是您感兴趣的系统调用的名称。__NR_### 宏是实现细节,不应使用。根据经验,如果标识符以下划线开头,则不应使用,如果以 2 开头,则绝对不应使用。参数进入rdirsirdxr10r8r9。这是一个适用于 Linux 的示例程序:

#include <sys/syscall.h>

    .globl _start
_start:
    mov $SYS_exit,%eax
    xor %edi,%edi
    syscall

这些约定大多可移植到其他类 UNIX 操作系统。

【讨论】:

  • 在我的 64 位 Ubuntu 上,我发现文件是 /usr/include/x86_64-linux-gnu/bits/syscall.h
  • @user2023370 该文件来自sys/syscall.h。你不应该直接包含它。
  • 此文件不存在。
  • @user2023370 试试echo '#include &lt;sys/syscall.h&gt;' | cc -E -。这将向您显示sys/syscall.h 的位置。在使用 glibc 的 Linux 系统上,它通常不在 /usr/include 中。
  • @PeterCordes __NR_ 宏仅适用于 Linux,与 SysV 和 POSIX 指定的 SYS_ 宏相反。这就是为什么后者更便携的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
  • 2010-10-26
  • 2018-07-25
  • 1970-01-01
相关资源
最近更新 更多