正如广泛宣传的那样,现代 x86_64 处理器具有 64 位寄存器,可以向后兼容的方式用作 32 位寄存器、16 位寄存器甚至 8 位寄存器,例如:
0x1122334455667788
================ rax (64 bits)
======== eax (32 bits)
==== ax (16 bits)
== ah (8 bits)
== al (8 bits)
这种方案可以从字面上理解,即人们总是可以使用指定的名称来访问寄存器的一部分,用于读取或写入目的,这将是高度合乎逻辑的。事实上,对于 32 位以下的所有内容都是如此:
mov eax, 0x11112222 ; eax = 0x11112222
mov ax, 0x3333 ; eax = 0x11113333 (works, only low 16 bits changed)
mov al, 0x44 ; eax = 0x11113344 (works, only low 8 bits changed)
mov ah, 0x55 ; eax = 0x11115544 (works, only high 8 bits changed)
xor ah, ah ; eax = 0x11110044 (works, only high 8 bits cleared)
mov eax, 0x11112222 ; eax = 0x11112222
xor al, al ; eax = 0x11112200 (works, only low 8 bits cleared)
mov eax, 0x11112222 ; eax = 0x11112222
xor ax, ax ; eax = 0x11110000 (works, only low 16 bits cleared)
然而,一旦我们进入 64 位的东西,事情似乎就相当尴尬了:
mov rax, 0x1111222233334444 ; rax = 0x1111222233334444
mov eax, 0x55556666 ; actual: rax = 0x0000000055556666
; expected: rax = 0x1111222255556666
; upper 32 bits seem to be lost!
mov rax, 0x1111222233334444 ; rax = 0x1111222233334444
mov ax, 0x7777 ; rax = 0x1111222233337777 (works!)
mov rax, 0x1111222233334444 ; rax = 0x1111222233334444
xor eax, eax ; actual: rax = 0x0000000000000000
; expected: rax = 0x1111222200000000
; again, it wiped whole register
这种行为对我来说似乎非常荒谬和不合逻辑。看起来试图以eax
任何方式写入任何内容都会导致擦除rax
寄存器的高 32 位。
所以,我有两个问题:
我相信这种尴尬的行为必须记录在某个地方,但我似乎无法在任何地方找到详细的解释(关于如何擦除 64 位寄存器的高 32 位)。我写的
eax
总是擦拭是对rax
的,还是更复杂的东西?它适用于所有 64 位寄存器,还是有一些例外?一个高度相关的问题提到了相同的行为,但是,遗憾的是,再次没有对文档的确切引用。
换句话说,我想要一个指向指定此行为的文档的链接。
只是我还是整个事情看起来真的很奇怪和不合逻辑(即 eax-ax-ah-al、rax-ax-ah-al 有一种行为,而 rax-eax 有另一种行为)?可能是我在这里遗漏了一些关于为什么要这样实施的关键点?
对“为什么”的解释将不胜感激。