x86/x64增加位移寻址

9
我正在编写一个x86/x64 CPU指令的编译器,但我似乎无法理解人们所说的“位移”地址。例如,这里详细介绍了Add指令: http://www.c-jump.com/CIS77/CPU/x86/X77_0150_encoding_add_edx_displacement.htm 我只是想实现将寄存器加到普通内存地址的add指令。问题是,该地址是一个“位移地址”。这是否意味着该地址是从指令位置偏移的带符号值?

3
你是否正在生成汇编代码?你能否生成C语言代码或使用LLVM?或者使用http://code.google.com/p/asmjit/或其他库发出机器代码?你是否对x86/64指令集很熟悉?你是否学习过http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html? - Basile Starynkevitch
3个回答

14

x86架构中有几种不同形式的间接操作数:

  1. [reg]
  2. [reg + displacement]
  3. [displacement]
  4. [reg * constant + reg]
  5. [reg * constant + reg + displacement]

“位移(displacement)”只是一个常量,它会被添加到地址的其余部分。在除常量外没有其他地址组成部分的情况下,仍称其为“位移”。这主要是为了与其他寻址形式保持一致。

另一种看待它的方式是,所有地址都采用以下格式:

[reg * constant + reg + displacement]

每个组件都允许值为0。

[displacement]格式只是编码时,除了位移以外的所有组件均为零。

作为编译器编写者,最后两种形式特别有趣。它们使得像pArray [index] -> field + 1这样的东西可以在单条指令中轻松编码。


那么[reg * constant + reg + displacement]如何编码成机器指令呢?假设我有一个存储在内存位置0x00000001的数组,我想访问它的索引AL。我猜我想使用移动指令并执行MOV AH 0x00000001 [AL]。我认为那只是[reg+displacement]。这个页面的第6节展示了编码R/M字节,但它真的很令人困惑:http://www.c-jump.com/CIS77/CPU/x86/lecture.html - Ryan Brown
看一下英特尔手册的第二卷。每个指令都指定了它的编码形式。列出 r/m 操作数的编码形式在 mod/rm 字节中接受寄存器或内存操作数。 - Scott Wisniewski
看一下英特尔手册的第2卷。每个指令都指定了它的编码形式。列出r/m操作数的编码形式接受在mod/rm字节中的寄存器或内存操作数。在第2卷的第2章第2.1节中,有一个表格显示了mod r/m字节的含义。那些列出[--][--]的形式表示使用了SIB字节的编码。SIB寻址的形式是regconstant + reg。某些mod/rm字节的形式表明SIB字节后面跟着一个位移。这些形式给出了regconstant + reg + constant的形式。还有一个解释SIB的表格。 - Scott Wisniewski
哦,好的,谢谢!我也在阅读关于64位处理器上位移地址仍然是32位的内容?所以在任何更大的情况下,您必须将其加载到寄存器中才能读取它? - Ryan Brown
@Scott,可以将寄存器用作位移吗? 当汇编lea %rax(%r12), %r14时,gcc会报错“Error: junk (%r12) after register”。 - anon
不,如果它是一个寄存器,那么它就不会是“位移”。位移是跟随modrm字节的常量。但你可以使用两个寄存器的和。请查看英特尔手册。 - Scott Wisniewski

4
没有什么“特殊的添加需要位移量”的说法,那个页面只是在不必要地混淆——这只是普通内存操作数编码的一部分。
ADD是一种相当标准的指令,其编码方式与所有ALU操作相同:有一个使用al作为目标和立即数作为源的特殊情况(04 ib),使用ax/eax/rax作为目标和立即数作为源(+ 05 imm),三个版本的add r/m, imm(一个用于8位目标,一个用于更宽的目标和带符号扩展的8位源,一个用于更宽的目标和宽源),当然还有一个add r,r/m和add r/m,这只是add r,r/m的一个特例,其中r/m采取位移形式:请参见ModRM编码的注释#1。
所以他们的意思只是add edx,[sdword]。(但他们误编码了reg字段,edx对应于010而不是011)

那么,如果要将 AL(8位寄存器0)添加到内存位置0x00000000中,CPU会接受(以十六进制表示)00 05 00000000吗? - Ryan Brown

4
那个页面不准确。它所谈论的“带位移的add”指的是形式为add r[16|32], r/m[16|32]add edx, [0xdisp],你可能在反汇编输出中看到它。假设它是在讨论带有操作码0x03的ADD指令,
  • 在ModR/M字节中对edx寄存器目的地进行编码,并指定一个32位位移作为有效地址,将使其值为0x15(参见Intel® 64和IA-32体系结构软件开发人员手册第2卷,第41页,表2-2)。
  • 这条指令的效果是将内存地址disp处的双字加到edx的内容中。
  • 因此,该指令的实际编码将为:\x03\x15\x00\x00\x00\x01,对于1字节的位移。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接