在 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出 [英] Assembly compiled executable using INT 0x80 on Ubuntu on Windows Subsystem for Linux doesn't produce output

查看:27
本文介绍了在 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这里是程序集:

section .textglobal _start ;必须为链接器 (ld) 声明_start: ;告诉链接器入口点mov edx,len ;消息长度mov ecx,msg ;要写的消息mov ebx,1 ;文件描述符(标准输出)mov eax,4 ;系统调用号(sys_write)int 0x80 ;调用内核mov eax,1 ;系统调用号(sys_exit)int 0x80 ;调用内核.data 节msg db 'Hello, world!', 0xa ;要打印的字符串len equ $ - msg ;字符串的长度

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

nasm -f elf64 hello.asm -o hello.old -o 你好 hello.o -m elf_x86_64

我使用:

./hello

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

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

解决方案

问题在于 Ubuntu for Windows(Windows Subsystem for Linux).它仅支持 64 位 syscall 接口和 32 位 x86 int 0x80 系统调用机制.

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

<小时>

您需要从使用 int 0x80 转换为 系统调用.这并不难.syscall 使用一组不同的寄存器,并且系统调用号与它们的 32 位对应物不同.Ryan Chapman 的博客有关于 syscall 接口的信息,系统调用, 以及它们的参数.Sys_writeSys_exit 是这样定义的:

<块引用>

%rax 系统调用 %rdi %rsi %rdx %r10 %r8 %r9----------------------------------------------------------------------------------0 sys_read unsigned int fd char *buf size_t 计数1 sys_write unsigned int fd const char *buf size_t 计数60 sys_exit int error_code

使用syscall 也会破坏RCXR11 寄存器.它们被认为是易变的.不要依赖 syscall 之后它们是相同的值.

您的代码可以修改为:

section .textglobal _start ;必须为链接器 (ld) 声明_start: ;告诉链接器入口点mov edx,len ;消息长度mov rsi,msg ;要写的消息mov edi,1 ;文件描述符(标准输出)mov eax,edi ;系统调用号(sys_write)系统调用;调用内核xor edi, edi ;返回值 = 0mov eax,60 ;系统调用号(sys_exit)系统调用;调用内核.data 节msg db 'Hello, world!', 0xa ;要打印的字符串len equ $ - msg ;字符串的长度

注意:在 64 位代码中,如果指令的目标寄存器是 32 位的(例如 EAXEBXEDIESI 等)处理器零将结果扩展到高 32 位 的 64 位寄存器.mov edi,1mov rdi,1 效果相同.

<小时>

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

I've been looking at a tutorial for assembly, and I'm trying to get a hello world program to run. I am using Bash on Ubuntu on Windows.

Here is the assembly:

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

I am using these commands to create the executable:

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

And I run it using:

./hello

The program then seems to run without a segmentation fault or error, but it produces no output.

I can't figure out why the code won't produce an output, but I wonder if using Bash on Ubuntu on Windows has anything to do with it? Why doesn't it produce output and how can I fix it?

解决方案

The issue is with Ubuntu for Windows (Windows Subsystem for Linux). It only supports the 64-bit syscall interface and not the 32-bit x86 int 0x80 system call mechanism.

Besides not being able to use int 0x80 (32-bit compatibility) in 64-bit binaries, Ubuntu on Windows (WSL) doesn't support running 32-bit executables either.


You need to convert from using int 0x80 to 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 has information on the syscall interface, the system calls, and their parameters. Sys_write and Sys_exit are defined this way:

%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     

Using syscall also clobbers RCX and the R11 registers. They are considered volatile. Don't rely on them being the same value after the syscall.

Your code could be modified to be:

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

Note: in 64-bit code if the destination register of an instruction is 32-bit (like EAX, EBX, EDI, ESI etc) the processor zero extends the result into the upper 32-bits of the 64-bit register. mov edi,1 has the same effect as mov rdi,1.


This answer isn't a primer on writing 64-bit code, only about using the syscall interface. If you are interested in the nuances of writing code that calls the C library, and conforms to the 64-bit System V ABI there are reasonable tutorials to get you started like Ray Toal's NASM tutorial. He discusses stack alignment, the red zone, register usage, and a basic overview of the 64-bit System V calling convention.

这篇关于在 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆