nasm 中的 MOV 以从内存中注册不使用 BITS 32

MOV to register from memory not working with BITS 32 in nasm

我刚开始使用 x86 汇编,正在尝试使用 MOV 指令进行一些基础操作。 (下面的代码)

BITS 32

SECTION .data                   
    somedata: db "Hello world",10

SECTION .text                   
    global _start                   
_start: 
    mov eax, somedata
    mov al, [eax]
    mov edx, [somedata]

我好像不明白为什么nasm使用RIP相对寻址,当指定BITS 32 在程序集中(我认为相对寻址仅在 64 位模式下)。此外,它在 32 位模式 中使用 RAX。如果我不指定任何东西,它似乎不使用相对寻址并使用EAX。

代码为 BITS 32

Disassembly of section .text:

00000000004000b0 <_start>:
  4000b0:   b8 c0 00 60 00          mov    eax,0x6000c0
  4000b5:   8a 00                   mov    al,BYTE PTR [rax]
  4000b7:   8b 15 c0 00 60 00       mov    edx,DWORD PTR [rip+0x6000c0]        # a0017d <_end+0x4000ad>

代码没有 BITS 32

Disassembly of section .text:

00000000004000b0 <_start>:
  4000b0:   b8 c0 00 60 00          mov    eax,0x6000c0
  4000b5:   67 8a 00                mov    al,BYTE PTR [eax]
  4000b8:   8b 14 25 c0 00 60 00    mov    edx,DWORD PTR ds:0x6000c0

我知道不是汇编,是我。我做错了什么?

PS:

  • 使用 nasm,64 位计算机使用 linux。

  • 使用nasm -f elf64 -F stabs -g sandbox.asm -o sandbox.o

  • 组装
  • 反汇编使用objdump -M intel -d sandbox

我还尝试了以下汇编器和链接器标志:

nasm -f elf32 -F stabs -g sandbox.asm -o sandbox.o
ld -oformat=elf32-i386 -o sandbox sandbox.o

但是说 ld: i386 architecture of input file `sandbox.o' is incompatible with i386:x86-64 output

是行不通的
nasm -f elf64 -F stabs -g sandbox.asm -o sandbox.o

这将始终生成 64 位 ELF 可执行文件,无论您将 32 位代码放入其中的事实如何。这会导致反汇编程序将您的 32 位机器代码解码为 64 位代码,因此会出现奇怪的结果。反汇编类似于原始代码的事实只是巧合,因为 64 位模式下的指令编码与 32 位等效指令非常相似甚至相同(可能更改默认寄存器大小)。

使用-f elf32 获取 32 位 ELF 可执行文件。

TL:DR:永远不要使用 BITS 指令,除非它是 必要的

BITS 指令不会更改 -felf64-felf32 选择的输出文件类型。 (-felf-felf32 的同义词,以防您在示例中看到它。)

为了生成 32 位静态可执行文件,我使用了一个 asm-link shell 脚本,它最终是这样做的:

nasm -felf32 -g -Fdwarf foo.asm   &&
ld -melf_i386 -o foo foo.o

stabs 调试格式已过时,但只要您的调试器支持它,将 asm 源代码行映射到 asm 指令可能没问题。无论如何,如果您使用 -g-Fstabs 是默认值。 (我还没有全部读完,但是 https://www.ibm.com/developerworks/library/os-debugging/index.html 有一些关于 STAB vs. DWARF 的信息。)


大多数时候,BITS 指令充其量是无用的,在最坏的情况下是有害的。 而不是像 push ebx 这样的有用错误,如果你尝试将 32 位代码构建到 64 位 object 文件中,它会让这样的事情发生。 (虽然它不会在这里拯救你,因为所有这些代码都是双向组装的。)

只有当你想使用 nasm -fbin 并制作一个可以提供给 ndisasm 或用作 shell编码,或者使用 db (A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux) 自己定义 ELF 或任何其他元数据 headers。 nasm 不提供 command-line 选项来更改 -fbinBITS 16 默认模式。

或者如果你真的想在一个以 16 位启动并切换到 64 位模式的文件中混合使用 16、32 和 64 位代码:这是主要的 use-case 对于 BITS. 或在 64 位可执行文件中包含一些 BITS 32BITS 16 机器代码 作为数据


不要在文件顶部添加 BITS 32 行作为样板文件的一部分,这没有帮助,也不是好的做法。 使用如下注释 ;;; 32-bit x86 Linux code, NASM syntax 如果您想描述此源文件中的内容以及如何构建它/运行。

你可以并且应该使用DEFAULT REL,所以如果你正在构建 64 位代码,你将获取内存操作数的 RIP-relative 寻址模式,例如 [somedata](没有 GP 寄存器的符号名称)。这比 32 位绝对寻址模式短一个字节,并且可以在 PIE 可执行文件中工作。

有趣的事实:32 位模式有 2 种冗余方式来编码 [disp32] 绝对寻址模式。 x86-64 将较短的一个(无 SIB 字节)重新用于 RIP-relative。 这就是为什么你的 32 位机器代码的 64 位反汇编具有 DWORD PTR [rip+0x6000c0],其中 rel32 是符号的绝对地址。