繁体   English   中英

在 Linux 的 Windows 子系统上的 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

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

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

问题在于 Windows 版 Ubuntu(Linux 版 Windows 子系统)。 它仅支持 64 位syscall接口而不支持32 位 x86 int 0x80系统调用机制。

除了无法在 64 位二进制文​​件中使用int 0x80 (32 位兼容性)之外,Windows 上的 Ubuntu (WSL) 也不支持运行 32 位可执行文件


您需要从使用int 0x80转换为syscall 这并不难。 一组不同的寄存器被用于syscall和系统调用编号是从他们的32位对应不同。 Ryan Chapman 的博客提供了有关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 位寄存器的高 32位。 mov edi,1mov rdi,1具有相同的效果。


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

正如 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

暂无
暂无

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

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