我一直在寻找有关汇编的教程,并且正在尝试运行一个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是否与此有关?为什么它不产生输出,我该如何解决?
问题出在Windows的Ubuntu(Linux的Windows子系统)上。它仅支持64位syscall接口, 不int 0x80支持32位x86系统调用机制。
syscall
int 0x80
除了无法int 0x80在64位二进制文件中使用(32位兼容性)之外,Windows上的Ubuntu(WSL)也不支持运行32位可执行文件。
您需要从使用转换int 0x80为syscall。不难 a使用一组不同的寄存器,syscall并且系统调用号与其32位对应号不同。Ryan Chapman的博客中包含有关syscall界面,系统调用及其参数的信息。Sys_write并Sys_exit以这种方式定义:
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
%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位寄存器的高32位。mov edi,1具有与相同的效果mov rdi,1。
mov edi,1
mov rdi,1
这个答案不是编写64位代码的入门,仅是关于使用syscall接口的。如果您对编写调用 C 库并符合64位System V ABI的代码的细微差别感兴趣,那么可以使用Ray Toal的NASM教程之类的合理教程来入门。他讨论了堆栈对齐,红色区域,寄存器使用情况以及64位System V调用约定的基本概述。