Assembly compiled executable using INT 0x80 on Ubuntu on Windows Subsystem for Linux doesn't produce output
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.
Exec format error 32-bit executable Windows Subsystem for Linux?
32-bit ELF support isn't provided by WSL (yet). There doesn't seem to be any progress since the UserVoice was raised - you are out luck.
See UserVoice: Please add 32 bit ELF support to the kernel and Support for 32-bit i386 ELF binaries.
If possible, switch to a real Linux ;-)
Since this was originally posted, the support has been available on WSL2 which does support real Linux kernel! So that should be the preferred way.
As noted in the linked github issue, there's also qemu-user
which can be used if WSL1 is still used.
Does WSL 2 really support 32 bit program?
If you run the command uname -a
in WSL you should get a result that contains the version of your WSL. This should be something like Linux COMPUTER_NAME 4.4.0-18362-Microsoft ...
. the number 18362 in that output is your WSL version and it needs to be at least 19041 to be a WSL2 build (only WSL2 supports 32-bit apps).
You could also run wsl --list --verbose
in a CMD shell and you will see the version of your WSL instance is 1
.
WSL2 will be part of the Windows update later this month
WSL2 will be released as part of Windows 10 2004 on May 12, 2020. If you don't want to wait you can sign up for the preview builds through the "Windows Insider Program".
Get WSL2 through Windows Insider Program now (about 1 hour of work):
In Windows go to Settings -> Windows Insider Program and register for the program. Then go check for updates. It will take a while to download, then follow the prompts to do all the restarts, etc required.
Ensure "Virtual Machine Platform" is enabled in Turn Windows Features On or Off
- In CMD or powershell run
wsl --set-default-version 2
to make all future WSL installs use WSL2 - run
wsl --set-version <Distro> 2
to change an already installed instance to WSL2- This will take a LONG time. I gave up after reading online that it could take hours. Instead I uninstalled Ubuntu and reinstalled it. That took about 10 minutes. As long as you set the default to WSL2, the re-install will be WSL2.
- You can verify your WSL is now version 2 by running
wsl --list --verbose
- Your 32-bit binaries should now work
I just did all of this over the last hour, because I needed to run a Zephyr simulation binary I built this morning. It worked, and I am very happy :)
Sources:
Github issue - scroll to end
Install WSL2
Related Topics
Exploring Docker Container'S File System
"Failed to Load Platform Plugin "Xcb" " While Launching Qt5 App on Linux Without Qt Installed
How to Remove ^[, and All of the Escape Sequences in a File Using Linux Shell Scripting
Bash Script Process Substitution Syntax Error: "(" Unexpected
How to Debug the Linux Kernel With Gdb and Qemu
What Happens If There Is No Exit System Call in an Assembly Program
How to Install Latest Version of Git on Centos 8.X/7.X/6.X
How to Redirect the Output of the Time Command to a File in Linux
Use of Floating Point in the Linux Kernel
Adding a New Entry to the Path Variable in Zsh
How Many Socket Connections Possible
How to Ensure Only One Instance of a Bash Script Is Running
How to Create a File With a Given Size in Linux
Determine Direct Shared Object Dependencies of a Linux Binary