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

问题描述 投票:0回答:1

我已经阅读了thisthisthis,但尚未找到我想要的东西。

首先,我了解

lea
mov
在所能达到的结果方面的区别,简单地说:

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+8
是非法的,而
lea  eax, [ebp+8]
却可以呢?我的书上说:

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

但这对我来说毫无意义! 恒定是什么意思?显而易见的理解是,CONSTANT应该在程序运行之前由汇编器/链接器计算,但是,想想

mov  eax, [ebp+8]
是一条LEGAL指令。在程序运行之前,汇编器/链接器无法知道
[ebp+8]
*(ebp+8)
作为 C 语言)的值!

assembly x86 linker nasm mov
1个回答
2
投票

你的书似乎只谈论mov-immediate,比如

mov eax, 1234
mov eax, foo
(符号地址)。

更完整的规则是

mov
的源操作数必须是:

  • 链接时间常数值(mov-immediate)
  • 或其他地方已经存在的值(内存或另一个寄存器)。

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


内存源操作数可以使用

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

x86 的机器代码格式对几乎所有指令都以相同的方式编码 寻址模式(以及寄存器与内存源),因此

mov
无需执行任何特殊操作即可支持
mov eax, [ebp+8]
,就像
add eax, [ebp+8] 一样
在完成地址计算和加载后,它还会对数据进行加法。它们之间只有操作码字节不同,指定如何处理数据,而不是操作数所在的位置(寄存器与内存寻址模式)。 (大多数指令有两种操作码,一种操作码的源可以是内存,另一种操作码的目标可以是内存。 我在这里谈论两个内存源操作码。)

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

没有其他指令可以做到这一点,因此您永远不能使用

eax+3
作为任何指令的源操作数。例如仅用一条指令是无法完成
imul ecx, eax+3
的。


有两个独立的概念:指令的数据来自何处(立即数、寄存器或由寻址模式寻址的内存)与该数据发生的情况(复制、imul、sub、popcount、popcount) ……)。从这个意义上说,LEA 没有数据输入,因为它不会取消引用

[ebp+8]

LEA 只是获取地址,就像 C 的

&
地址操作符一样,它取消了否则会取消引用的内容,例如
&ptr[3]
ptr+3
相同,但语法不同。让 LEA 有趣的一件事是,x86 ADD 只能执行
ptr+=3
,就地修改寄存器(或内存),但 LEA 可以像
tmp = &ptr[3]
那样进行复制和添加。

(对于32位或64位寻址模式,还添加一个移位寄存器,如

tmp = &ptr[x*4 + 3]


有点偏离主题,但可能相关:

大多数经典的x86整数指令只有一个或两个操作数(x86 cpu有什么样的地址指令?)。尽管

imul ecx, [edx+8], 123
可以在将结果写入不是输入的寄存器时进行数学运算。 (与大多数直接源指令不同,
imul
的 new-in-186 形式不会窃取 ModRM 中的
/r
字段作为额外的操作码位,因此它可以有 3 个操作数,包括立即数,其存在由操作码。)但是硬件乘法器的输入仍然是立即数和直接来自内存的值;
imul ecx, edx+8, 123
不可编码。

© www.soinside.com 2019 - 2024. All rights reserved.