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

标签 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 是否与它有任何关系?为什么它不产生输出,我该如何解决?

最佳答案

相关:WSL2 允许 32 位用户空间程序,而 WSL1 不允许。参见 Does WSL 2 really support 32 bit program? 回复:确保您实际使用的是 WSL2。这个答案的其余部分是在 WLS2 存在之前写的。


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

除了无法在 64 位二进制文​​件中使用 int 0x80(32 位兼容性)之外,Windows 上的 Ubuntu (WSL1) doesn't support running 32-bit executables任何一个。 (就像你构建了一个真正的 Linux 内核一样 without CONFIG_IA32_EMULATION ,就像一些 Gentoo 用户所做的那样。)


您需要从使用 int 0x80 转换为 syscall .这并不难。 系统调用 使用了一组不同的寄存器,并且系统调用编号与对应的 32 位系统调用编号不同。 Ryan Chapman's blog包含有关syscall 接口(interface)、系统调用及其参数的信息。 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 等)processor zero extends the result into the upper 32-bits的 64 位寄存器。 mov edi,1mov rdi,1 效果相同。


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

关于linux - 在适用于 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47736104/

相关文章:

linux - 创建一个别名以进行适用于任何目录的相同 ant 命令调用

linux - 通过 ssh 浏览(或下载)R vignettes

linux - OpenShift : External Access using NodePort

assembly - 如何实现逻辑或||在 easy68k 的 if() 条件下?

c++ - clflush 不刷新指令缓存

mono - 无法让 MonoDroid 4.0.6 x86 Emulation 或 GoogleTV 工作

assembly - Haswell/Skylake 上的部分寄存器究竟如何执行?写AL好像对RAX有假依赖,AH不一致

linux - 为什么 if [ !$(grep -q) ] 不起作用而 if grep -q 起作用?

c - (进位标志)和汇编中的系统调用(Mac Os 上的 x64 Intel 语法)之间有什么关系?

assembly - 如何在 NASM 中创建第二个堆栈