Linux 子系统 Ubuntu 上 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
程序似乎 运行 没有分段错误或错误,但它没有产生任何输出。
我不明白为什么代码不会产生输出,但我想知道在 Ubuntu 上使用 Bash 在 Windows 上是否与它有什么关系?为什么它不产生输出,我该如何解决?
问题出在 Ubuntu for Windows(Windows Subsystem for Linux)。它只支持64位syscall
接口和not the 32-bit x86 int 0x80
系统调用机制。
除了无法在 64 位二进制文件中使用 int 0x80
(32 位兼容性)之外,Ubuntu 在 Windows (WSL) doesn't support running 32-bit executables 上也是如此。
您需要从使用 int 0x80
转换为 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 具有有关 syscall
接口、系统调用及其参数的信息。 Sys_write
和 Sys_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
也会破坏RCX和R11寄存器。它们被认为是不稳定的。不要依赖它们在 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 位(如 EAX,EBX、EDI、ESI等)64位寄存器的processor zero extends the result into the upper 32-bits。 mov edi,1
与 mov rdi,1
效果相同。
这个答案不是关于编写 64 位代码的初级读物,只是关于使用 syscall
接口。如果您对编写调用 C 库并符合 64 位 System V ABI 的代码的细微差别感兴趣,可以使用合理的教程来帮助您入门,例如 Ray Toal's NASM tutorial .他讨论了堆栈对齐、红色区域、寄存器使用以及 64 位 System V 调用约定的基本概述。
正如 Ross Ridge 在评论中指出的那样,在编译 64 位时不要使用 32 位调用内核函数。
为 32 位编译或 "translate" 将代码编译成 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
我一直在看汇编教程,我正在尝试为 运行 编写一个 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
程序似乎 运行 没有分段错误或错误,但它没有产生任何输出。
我不明白为什么代码不会产生输出,但我想知道在 Ubuntu 上使用 Bash 在 Windows 上是否与它有什么关系?为什么它不产生输出,我该如何解决?
问题出在 Ubuntu for Windows(Windows Subsystem for Linux)。它只支持64位syscall
接口和not the 32-bit x86 int 0x80
系统调用机制。
除了无法在 64 位二进制文件中使用 int 0x80
(32 位兼容性)之外,Ubuntu 在 Windows (WSL) doesn't support running 32-bit executables 上也是如此。
您需要从使用 int 0x80
转换为 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 具有有关 syscall
接口、系统调用及其参数的信息。 Sys_write
和 Sys_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
也会破坏RCX和R11寄存器。它们被认为是不稳定的。不要依赖它们在 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 位(如 EAX,EBX、EDI、ESI等)64位寄存器的processor zero extends the result into the upper 32-bits。 mov edi,1
与 mov rdi,1
效果相同。
这个答案不是关于编写 64 位代码的初级读物,只是关于使用 syscall
接口。如果您对编写调用 C 库并符合 64 位 System V ABI 的代码的细微差别感兴趣,可以使用合理的教程来帮助您入门,例如 Ray Toal's NASM tutorial .他讨论了堆栈对齐、红色区域、寄存器使用以及 64 位 System V 调用约定的基本概述。
正如 Ross Ridge 在评论中指出的那样,在编译 64 位时不要使用 32 位调用内核函数。
为 32 位编译或 "translate" 将代码编译成 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