为什么需要LEA(负载有效地址)?

Why is LEA (Load Effective Address) necessary?

提问人:Guichi 提问时间:11/3/2023 最后编辑:Sep RolandGuichi 更新时间:11/5/2023 访问量:93

问:

我读这个,这个,还有这个,还没有找到我想要的东西。

首先,我了解它们可以达到的结果之间的区别,简单地说:leamov

mov  eax, ebp   ;put the value in ebp register into eax register
lea  eax, [ebp] ;same as above, and they are equivalent

然而:

mov  eax, ebp+8   ;invalid register set size
lea  eax, [ebp+8] ;calculate in sum of ebp value and 8, then assign it to eax

那么,为什么虽然可以,但是非法的呢?我的书说:mov eax, ebp+8lea eax, [ebp+8]

MOV 存储到 EAX 中的值必须由汇编程序计算 (也就是说,它最终必须是一个常数)

但这对我来说毫无意义!CONSTANT是什么意思?显而易见的理解是,在程序运行之前,CONSTANT应该由汇编器/链接器计算,但是,考虑是一个LEGAL指令。在程序运行之前,汇编器/链接器无法知道(作为 C 术语)的值!mov eax, [ebp+8][ebp+8]*(ebp+8)

程序集 x86 链接器 nasm mov

评论

0赞 GSerg 11/3/2023
您链接的第一个问题的第一个答案下的评论不是回答了吗?
0赞 Guichi 11/3/2023
@GSerg 你怎么可能找到它,哈哈
0赞 fuz 11/4/2023
请注意,至少 Go 汇编器确实允许您编写以生成 .诚然,这有点奇怪。MOVL $8(BP), AXlea eax, [rbp+8]

答:

2赞 Peter Cordes 11/3/2023 #1

你的书似乎只在谈论 mov-immediate,比如 or(符号地址)。mov eax, 1234mov eax, foo

一个更完整的规则是,源操作数必须是:mov

  • 链接时间常量值 (mov-immediate)
  • 或已存在于其他位置的值(内存或其他寄存器)。

mov 只能复制,在写入目的地之前,它不会通过 ALU 馈送数据。


内存源操作数可以使用寻址模式(如 或),但这与从该地址加载或存储到该地址的数据发生的情况无关。地址生成是在 CPU 的单独部分完成的(或者在原始 8086 上,在处理指令的微码的单独阶段)。[ebp+8][edx + eax*4 + my_array]

x86 的机器码格式对几乎所有指令的寻址模式(以及寄存器与内存源)的编码方式都相同,因此不必做任何特殊的事情来支持,就像在进行地址计算和加载后,它也对数据进行添加。只有操作码字节之间不同,指定如何处理数据,而不是操作数的位置(寄存器与内存寻址模式)。(大多数指令有两个操作码,一个是源可以是内存,另一个是目标可以是内存。 我在这里说的是两个内存源操作码。movmov eax, [ebp+8]add eax, [ebp+8]

的机器码也与操作码字节以外的机器码相同。LEA 的特殊之处在于,它使用寻址模式的机器码格式来编码实际上不访问内存的 shift/add 指令。请参阅对不是地址/指针的值使用 LEA?lea eax, [ebp+8]

没有其他指令可以做到这一点,因此您永远不能用作任何指令的源操作数。例如,没有办法只用一条指令来做。eax+3imul ecx, eax+3


有两个独立的概念:指令的数据来源(即时、寄存器或由寻址模式寻址的内存)与该数据发生的情况(复制、imul、sub、popcount 和...)。从这个意义上说,LEA 没有数据输入,因为它不会取消 .[ebp+8]

LEA 只获取地址,就像 C 的 address-of 运算符一样,它抵消了本来会被取消引用的内容,例如 相同,但语法不同。LEA 有趣的一件事是 x86 ADD 只能执行 ,就地修改寄存器(或内存),但 LEA 可以像 .&&ptr[3]ptr+3ptr+=3tmp = &ptr[3]

(对于 32 位或 64 位寻址模式,还可以添加移位寄存器,例如tmp = &ptr[x*4 + 3])


有点跑题了,但可能相关:

大多数经典的 x86 整数指令只有一个或两个操作数(x86 cpu 有什么样的地址指令?虽然可以在将结果写入不是输入的寄存器时进行数学运算。(与大多数直接源指令不同,这种新的 in-186 形式不会将 ModRM 中的字段作为额外的操作码位窃取,因此它可以有 3 个操作数,包括由操作码发出信号的直接操作数。但是,硬件乘法器的输入仍然是直接来自内存的即时值; 不可编码。imul ecx, [edx+8], 123imul/rimul ecx, edx+8, 123

评论

0赞 Guichi 11/4/2023
在不知道 CPU 究竟如何工作的情况下学习汇编是不够的,因为指令规则只是连线的。从你的回答中,我可以得出结论,(或等)的操作数是可寻址的,而不知道确切的地址(这解释了有效但不能)。并且可以计算出 的操作数!无法想象如何在没有额外内存/寄存器的情况下使用机器代码来完成计算。我真的很难理解规则。movadd[ebp+3]ebp+3leaout of thin air
0赞 Guichi 11/4/2023
如果可以使用机器码中计算,为什么不对其他人做同样的事情,比如 或。另一方面,如果不能工作,怎么能呢?ebp+3leamovaddebp+3[ebp+3]
0赞 Peter Cordes 11/4/2023
@Guichi:作为 LEA 的操作数,是一种寻址模式,使用 ModR/M 字节的 mode 和 R/M 字段进行寻址 (wiki.osdev.org/...);大多数操作码都意味着存在一个 ModR/M 字节来对操作数进行编码。就像这个答案试图解释的那样,只有 LEA 在进行地址计算方面是特殊的,有点像 C 中的 address-of 运算符,它可以使取消引用而不是实际上取消引用。[ebp+3]&
0赞 Peter Cordes 11/4/2023
@Guichi:有两个独立的概念:数据的来源(即时、寄存器或内存,由寻址模式寻址)与数据发生的情况(复制、imul、sub、popcount 和...)。从这个意义上说,LEA 没有数据输入,因为它不会取消 .它只是接受地址。[ebp+3]