在 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 .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_write
和 Sys_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
也会破坏RCX 和R11 寄存器.它们被认为是易变的.不要依赖 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 位的(例如 EAX、EBX、EDI、ESI 等)处理器零将结果扩展到高 32 位 的 64 位寄存器.mov edi,1
与 mov 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屋!