64位汇编,何时使用较小尺寸的寄存器

mk1*_*k12 8 64-bit assembly x86-64 nasm cpu-registers

我理解在x86_64汇编中有例如(64位)rax寄存器,但它也可以作为32位寄存器,eax,16位,ax和8位来访问.在什么情况下我不会只使用完整的64位,以及为什么会有什么优势?

举个例子,通过这个简单的hello world程序:

section .data
msg: db "Hello World!", 0x0a, 0x00
len: equ $-msg

section .text
global start

start:
mov rax, 0x2000004      ; System call write = 4
mov rdi, 1              ; Write to standard out = 1
mov rsi, msg            ; The address of hello_world string
mov rdx, len            ; The size to write
syscall                 ; Invoke the kernel
mov rax, 0x2000001      ; System call number for exit = 1
mov rdi, 0              ; Exit success = 0
syscall                 ; Invoke the kernel
Run Code Online (Sandbox Code Playgroud)

rdi和rdx,至少只需要8位而不是64位,对吧?但是,如果我将它们分别更改为dil和dl(它们的低8位当量),程序会组装并链接但不输出任何内容.

但是,如果我使用eax,edi和edx,它仍然有效,那么我应该使用那些而不是完整的64位吗?为什么或者为什么不?

Bo *_*son 6

你在这里问几个问题.

如果只加载寄存器的低8位,寄存器的其余部分将保持其先前的值.这可以解释为什么你的系统调用得到了错误的参数.

当你需要的时候使用32位的一个原因是许多使用EAX或EBX的指令比使用RAX或RBX的指令短一个字节.它也可能意味着加载到寄存器中的常量更短.

指令集已经发展了很长时间,并且有很多怪癖!


Mat*_*y K 3

首先也是最重要的是将较小的(例如 8 位)值从内存(读取字符、处理数据结构、反序列化网络数据包等)加载到寄存器中。

MOV AL, [0x1234]
Run Code Online (Sandbox Code Playgroud)

相对

MOV RAX, [0x1234]
SHR RAX, 56
# assuming there are actually 8 accessible bytes at 0x1234,
# and they're the right endianness; otherwise you'd need
# AND RAX, 0xFF or similar...
Run Code Online (Sandbox Code Playgroud)

或者,当然,将所述值写回到内存中。


(编辑,就像 6 年后):

由于这种情况不断出现:

MOV AL, [0x1234]
Run Code Online (Sandbox Code Playgroud)
  • 只读取 0x1234 处的单个字节内存(相反只会覆盖单个字节内存)
  • 保留 RAX 的其他 56 位中的内容
    • 这会在 RAX 的过去值和未来值之间创建依赖关系,因此 CPU 无法使用寄存器重命名来优化指令。

相比之下:

MOV RAX, [0x1234]
Run Code Online (Sandbox Code Playgroud)
  • 读取从 0x1234 开始的 8 字节内存(相反会覆盖 8 字节内存)
  • 覆盖所有RAX
  • 假设内存中的字节与 CPU 具有相同的字节序(在网络数据包中通常不正确,因此我SHR多年前的指令)

还需要注意的是:

MOV EAX, [0x1234]
Run Code Online (Sandbox Code Playgroud)

然后,正如评论中提到的,有:

MOVZX EAX, byte [0x1234]
Run Code Online (Sandbox Code Playgroud)
  • 只读取 0x1234 处的单个字节内存
  • 扩展该值以用零填充所有 EAX(以及 RAX)(消除依赖性并允许寄存器重命名优化)。

在所有这些情况下,如果您想从“A”寄存器写入内存,您必须选择宽度:

MOV [0x1234], AL   ; write a byte (8 bits)
MOV [0x1234], AX   ; write a word (16 bits)
MOV [0x1234], EAX  ; write a dword (32 bits)
MOV [0x1234], RAX  ; write a qword (64 bits)
Run Code Online (Sandbox Code Playgroud)

  • 呃... x86_64 _始终_小端,因此您的示例将产生不同的结果。 (2认同)
  • 这里最好的选择是“movzx eax,[0x1234]”。 (2认同)