Assembly Linux 64位Windows子系统上的32位系统调用或可执行文件?还是一个工作的64位Hello World?

Assembly Linux 64位Windows子系统上的32位系统调用或可执行文件?还是一个工作的64位Hello World?,assembly,x86-64,nasm,system-calls,windows-subsystem-for-linux,Assembly,X86 64,Nasm,System Calls,Windows Subsystem For Linux,我试图学习NASM,从helloworld项目开始。它本身就是Linux上的32位NASM 我复制并粘贴创建的helloworld.asm(使用int 0x8032位系统调用)并输入命令 nasm -f elf helloworld.asm ld -m elf_i386 helloworld.o -o helloworld 这两个看起来不错,但运行/helloworld会产生 -bash:./helloworld:无法执行二进制文件:Exec格式错误 然后我搜索了这个错误,喜欢上了这个。我输入

我试图学习NASM,从helloworld项目开始。它本身就是Linux上的32位NASM

我复制并粘贴创建的
helloworld.asm
(使用
int 0x80
32位系统调用)并输入命令

nasm -f elf helloworld.asm
ld -m elf_i386 helloworld.o -o helloworld
这两个看起来不错,但运行
/helloworld
会产生

-bash:./helloworld:无法执行二进制文件:Exec格式错误

然后我搜索了这个错误,喜欢上了这个。我输入了该答案的命令:

sudo apt-get install gcc-multilib g++-multilib
之后,我安装了这个:

nasm -f elf64 helloworld.asm -o helloworld.o
ld -o helloworld helloworld.o -m elf_x86_64
两者都来自该答案,没有出现错误。
然后我执行了
/helloworld
,得到了以下错误:

分段故障(堆芯转储)

好的,然后我也搜索了这个新错误。(编者按:解释确切原因:WSL 1不支持64位代码中的32位
int0x80
系统调用,这通常不是一个好主意)

大多数答案并没有给出确切的答案。我唯一得到的。“看起来我需要修改NASM代码,”他说

64bit sys_exit = 60 32bit sys_exit = 1
64bit sys_write = 1 32bit sys_write = 4

所以我修改了原始代码,它变成了:
原代码:

; Hello World Program - asmtutor.com
; Compile with: nasm -f elf helloworld.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld.o -o helloworld
; Run with: ./helloworld
 
SECTION .data
msg     db      'Hello World!', 0Ah
 
SECTION .text
global  _start
 
_start:
 
    mov     edx, 13
    mov     ecx, msg
    mov     ebx, 1
    mov     eax, 4
    int     80h
 
    mov     ebx, 0      ; return 0 status on exit - 'No Errors'
    mov     eax, 1      ; invoke SYS_EXIT (kernel opcode 1)
    int     80h
修改后的代码:

; Hello World Program - asmtutor.com
; Compile with: nasm -f elf helloworld.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld.o -o helloworld
; Run with: ./helloworld
 
SECTION .data
msg     db      'Hello World!', 0Ah
 
SECTION .text
global  _start
 
_start:
 
    mov     edx, 13
    mov     ecx, msg
    mov     rdi, 1
    mov     rax, 1      ; SYS_write 64-bit ABI
    syscall
 
    mov     rdi, 0      ; return 0 status on exit - 'No Errors'
    mov     rax, 60     ; invoke SYS_exit (kernel opcode 1)
    syscall
修改代码后,我再次输入上面的命令:

nasm -f elf64 helloworld.asm -o helloworld.o
ld -o helloworld helloworld.o -m elf_x86_64
./helloworld
不再有错误。但是也没有输出,应该是“helloworld”


然后我回到前面,首先尝试了教程中给出的命令,从64位模式下工作的源代码构建32位可执行文件:

nasm -f elf helloworld.asm
其结果是:

helloworld.asm:16:错误:在32位模式下不支持指令
helloworld.asm:17:错误:32位模式下不支持指令
helloworld.asm:20:错误:32位模式下不支持指令
helloworld.asm:21:错误:32位模式下不支持指令

然后我再次搜索此错误,但找不到解决方案。
我的机器是64位Linux Ubuntu,作为64位Windows 10中Linux的Windows子系统

如何正确运行此helloworld程序?
这是否意味着32位Linux NASM教程/程序无法在64位Linux上运行?
还是64位Windows10?
还是在Linux的Windows子系统中

问题是什么?为什么会出现这么多错误?

我应该如何避免将来出现类似的潜在错误?

快速修复
您想编译32位还是64位?
您的程序将工作并显示输出,只需通过rsi更改寄存器ecx即可

; Hello World Program - asmtutor.com
; Compile with: nasm -f elf helloworld.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld.o -o helloworld
; Run with: ./helloworld

SECTION .data
msg     db      'Hello World!',0Ah

SECTION .text
global  _start

_start:

    mov     edx, 13
    mov     rsi, msg
    mov     rdi, 1
    mov     rax, 1
    syscall

    mov     rdi, 0      ; return 0 status on exit - 'No Errors'
    mov     rax, 60      ; invoke SYS_EXIT (kernel opcode 1)
    syscall

使用nasm和ld编译64位的程序

nasm -f elf64 helloworld.asm -o helloworld.o  
ld helloworld.o -o helloworld.elf  

写入系统调用使用了错误的寄存器。将
mov ecx,msg
更改为
mov rsi,msg
。您无法在WSL(Linux的Windows子系统)上执行32位代码。这就是为什么会出现exec格式错误。您可以执行64位代码,但只能使用64位系统调用(正如您已经注意到的)。汇编并链接到
nasm-f elf64 helloworld.asm;ld helloworld.o-o helloworld
你应该可以走了。我已经更改了代码,它工作正常。这是否意味着我需要修改教程中的每个32位nasm?或者可以转换/允许32位代码编译、链接和运行?另外,如果WSL不能执行32位代码,本机Linux可以吗?64位调用约定和系统调用编号与32位不同。显然,指针大小也不同。没有一种简单的方法可以将32位转换为64位。本机linux可以执行32位,除非特别禁用。。永远不要从64b可执行文件调用32b服务。大多数常见的linux安装实际上都会以某种有限的方式(请参阅上面的Q+A了解更多详细信息)(但不是WSL),但是如果您有32b教程,您应该构建32b可执行文件。这在WSL中也是不可能的。我认为目前所有的linux发行版仍然支持32b二进制文件,尽管有些发行版极力反对32b二进制文件。对于类似教程的东西,在虚拟机中安装任何linux都可能足够好(或者可以选择一些较旧的32位ISO)。
movesi,msg
也可以,因为您正在制作一个非PIE可执行文件。在默认代码模型中,符号地址保证在低32位的虚拟地址空间中。类似地,
movedi,1
/
moveax,1
将节省代码大小,如果NASM没有为您进行优化。如果您确实想将一个可能为64位的符号地址放入寄存器,最好的方法是
learsi,[rel msg]
。请注意,WSL1类似于没有配置IA32仿真的Linux内核,因此在64位模式下没有
int0x80
系统调用,也没有32位ELF可执行文件。因此,“您想编译32位还是64位?”问题的答案应该是“64位”。但实际上最大的建议是:通常在完成教程之前不要尝试移植教程。查找您想要使用的系统的教程,因为asm是高度不可移植的,这一问题充分说明了这一点。(它在做必要的研究以找到64位系统调用信息方面做了很好的尝试,但在将64位代码组装回32位可执行文件等方面做了大量的猜测。学习更多关于x86-64的知识会使这一点不那么令人困惑。)Pete欢迎您的知识,我希望每次我失败时你都能纠正我,但这让我有点难以快速理解你的所有内容,因为它是deep=)。再次感谢你
nasm -f elf64 helloworld.asm -o helloworld.o  
ld helloworld.o -o helloworld.elf