Linux 子系统 Ubuntu 上 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出

Assembly compiled executable using INT 0x80 on Ubuntu on Windows Subsystem for Linux doesn't produce output

我一直在看汇编教程,我正在尝试为 运行 编写一个 hello world 程序。我在 Windows 上的 Ubuntu 上使用 Bash。

这是程序集:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov ecx,msg     ;message to write
    mov ebx,1       ;file descriptor (stdout)
    mov eax,4       ;system call number (sys_write)
    int 0x80        ;call kernel

    mov eax,1       ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

我正在使用这些命令创建可执行文件:

nasm -f elf64 hello.asm -o hello.o
ld -o hello hello.o -m elf_x86_64

我 运行 它使用:

./hello

程序似乎 运行 没有分段错误或错误,但它没有产生任何输出。

我不明白为什么代码不会产生输出,但我想知道在 Ubuntu 上使用 Bash 在 Windows 上是否与它有什么关系?为什么它不产生输出,我该如何解决?

问题出在 Ubuntu for Windows(Windows Subsystem for Linux)。它只支持64位syscall接口和not the 32-bit x86 int 0x80系统调用机制。

除了无法在 64 位二进制文​​件中使用 int 0x80(32 位兼容性)之外,Ubuntu 在 Windows (WSL) doesn't support running 32-bit executables 上也是如此。


您需要从使用 int 0x80 转换为 syscall. It's not difficult. A different set of registers are used for a syscall and the system call numbers are different from their 32-bit counterparts. Ryan Chapman's blog 具有有关 syscall 接口、系统调用及其参数的信息。 Sys_writeSys_exit 是这样定义的:

%rax  System call  %rdi               %rsi              %rdx          %r10 %r8 %r9
----------------------------------------------------------------------------------
0     sys_read     unsigned int fd    char *buf         size_t count          
1     sys_write    unsigned int fd    const char *buf   size_t count
60    sys_exit     int error_code     

使用syscall也会破坏RCXR11寄存器。它们被认为是不稳定的。不要依赖它们在 syscall.

之后是相同的值

您的代码可以修改为:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov edx,len     ;message length
    mov rsi,msg     ;message to write
    mov edi,1       ;file descriptor (stdout)
    mov eax,edi     ;system call number (sys_write)
    syscall         ;call kernel

    xor edi, edi    ;Return value = 0
    mov eax,60      ;system call number (sys_exit)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string

注意:在 64 位代码中,如果指令的 目标 寄存器是 32 位(如 EAXEBXEDIESI等)64位寄存器的processor zero extends the result into the upper 32-bitsmov edi,1mov rdi,1 效果相同。


这个答案不是关于编写 64 位代码的初级读物,只是关于使用 syscall 接口。如果您对编写调用 C 库并符合 64 位 System V ABI 的代码的细微差别感兴趣,可以使用合理的教程来帮助您入门,例如 Ray Toal's NASM tutorial .他讨论了堆栈对齐、红色区域、寄存器使用以及 64 位 System V 调用约定的基本概述。

正如 Ross Ridge 在评论中指出的那样,在编译 64 位时不要使用 32 位调用内核函数。

为 32 位编译或 "translate" 将代码编译成 64 位系统调用。 这可能是这样的:

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
    mov rdx,len     ;message length
    mov rsi,msg     ;message to write
    mov rdi,1       ;file descriptor (stdout)
    mov rax,1       ;system call number (sys_write)
    syscall         ;call kernel

    mov rax,60      ;system call number (sys_exit)
    mov rdi,0       ;add this to output error code 0(to indicate program terminated without errors)
    syscall         ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;string to be printed
    len equ $ - msg     ;length of the string