【问题标题】:32-bit system calls or executables on 64bit Windows Subsystem For Linux? Or a working 64-bit Hello World?64 位 Windows 子系统上的 32 位系统调用或可执行文件对于 Linux?还是一个工作的 64 位 Hello World?
【发布时间】: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


【解决方案1】:

快速修复
您要编译 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 esi, msg 也可以,因为您正在制作非 PIE 可执行文件。在默认代码模型中,符号地址保证在虚拟地址空间的低 32 位中。同样,mov edi, 1 / mov eax,1 如果 NASM 尚未为您优化这些代码,则会节省代码大小。如果您确实 想要将可能为 64 位的符号地址放入寄存器,那么好的方法是 lea rsi, [rel msg]How to load address of function or label into register
  • 请注意,WSL 1 就像没有 CONFIG_IA32_EMULATION 的 Linux 内核,因此在 64 位模式下没有 int 0x80 系统调用,也没有 32 位 ELF 可执行文件。因此,“您要编译 32 位还是 64 位?”的答案。问题必须是“64 位”。
  • 但真正最大的建议是:通常在完成教程之前不要尝试移植教程。为您要使用的系统找到一个教程,因为 asm 是高度不可移植的,正如这个问题充分证明的那样。 (它确实在进行必要的研究以查找 64 位系统调用信息方面做出了很好的尝试,但在将 64 位代码组装回 32 位可执行文件之类的事情上做了很多疯狂的猜测。了解更多关于x86-64 会让这变得不那么混乱。)
  • Pete 你的知识总是受欢迎的,我希望你每次我失败时都能纠正我,但这让我有点难以快速理解你的所有内容,因为它很深 =)。再次感谢您
猜你喜欢
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
  • 2014-01-22
  • 2011-12-18
  • 2011-09-08
相关资源
最近更新 更多