【发布时间】:2020-01-14 20:07:33
【问题描述】:
我试图学习 NASM 并从一个 helloworld 程序开始。 The tutorial 本身是 Linux 上的 32 位 NASM。
我复制并粘贴创建的helloworld.asm(使用int 0x80 32 位系统调用)并输入命令...
nasm -f elf helloworld.asm
ld -m elf_i386 helloworld.o -o helloworld
这两个看起来不错,但运行 ./helloworld 会产生
-bash: ./helloworld: 无法执行二进制文件:执行格式错误
然后我搜索了这个错误并喜欢这个SO posting。我输入了那个答案的命令:
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,得到如下错误:
分段错误(核心转储)
好的,然后我也搜索了这个新错误。 (编者注:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? 解释了确切原因:WSL 1 不支持 64 位代码中的 32 位 int 0x80 系统调用,这通常不是一个好主意。
大多数答案都没有给出确切的解决方案。我得到的唯一一个is this。他说,看来我需要修改 NASM 代码
64bit sys_exit = 60 32bit sys_exit = 1
64bit sys_write = 1 32bit sys_write = 4
和
32 bit sys_exit:
mov ebx, ERR_CODE
mov eax, sys_exit ; 1
int 80h
64 bit sys_exit:
mov rdi, ERR_CODE
mov rax, sys_exit ; 60
syscall
所以我修改了原来的代码,变成了这样:
原代码:
; 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 中的 Windows-Subsystem-For-Linux。
如何正确运行这个helloworld程序?
这是否意味着 32 位 Linux NASM 教程/程序无法在 64 位 Linux 上运行?
还是 64 位 Windows10?
还是在 Windows-Subsystem-For-Linux 中?
是什么问题,为什么会出现这么多错误?
以后应该如何避免类似的潜在错误?
【问题讨论】:
-
您为写入系统调用使用了错误的寄存器。将
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 位。
-
stackoverflow.com/q/46087730/4271923 .. 你永远不应该从 64b 可执行文件调用 32b 服务。大多数常见的 linux 安装实际上会以某种有限的方式允许这样做(有关许多详细信息,请参阅上面的 Q+A)(但不是 WSL),但是如果您有 32b 教程,则应该构建 32b 可执行文件。 WSL 也无法做到这一点。我认为所有当前的 linux 发行版仍然支持 32b 二进制文件,尽管有些人正在努力弃用它。对于类似教程的东西,在虚拟机中安装任何 linux 可能就足够了(或者选择一些较旧的 32 位 ISO 以确保)。
标签: assembly x86-64 nasm system-calls windows-subsystem-for-linux