我一直在寻找装配教程,我正在尝试运行一个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是否与它有关?为什么不产生输出,我该如何解决?
答案 0 :(得分:12)
问题出在Ubuntu for Windows(适用于Linux的Windows子系统)上。它仅支持64位syscall
接口和not the 32-bit x86 int 0x80
系统调用机制。
除了无法在64位二进制文件中使用int 0x80
(32位兼容性),在Windows上使用Ubuntu(WSL)doesn't support running 32-bit executables。
您需要使用int 0x80
转换为syscall
。这并不困难。一组不同的寄存器用于syscall
,系统调用号与32位对应号不同。 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
clobbers 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调用约定的基本概述。
答案 1 :(得分:3)
正如Ross Ridge的评论中所指出的那样,编译64位时不要使用32位内核函数调用。
编译为32位或将代码“转换”为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