在Windows上的Ubuntu上的Bash上汇编编译可执行文件不会产生输出

时间:2017-12-10 05:14:35

标签: linux assembly x86 nasm windows-subsystem-for-linux

我一直在寻找装配教程,我正在尝试运行一个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

程序似乎在没有分段错误或错误的情况下运行,但它不产生输出。

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

2 个答案:

答案 0 :(得分:12)

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

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

您需要使用int 0x80转换为syscall。这并不困难。一组不同的寄存器用于syscall,系统调用号与32位对应号不同。 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 clobbers RCX R11 寄存器。他们被认为是不稳定的在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位(如 EAX EBX EDI ESI 等)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调用约定的基本概述。

答案 1 :(得分:3)

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

编译为32位或将代码“转换”为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