assembly 为什么需要莱亚(加载有效地址)?

gpfsuwkq  于 7个月前  发布在  其他
关注(0)|答案(1)|浏览(64)

我读过thisthis,和this还没有找到我想要的。
首先,我理解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+8是非法的,而lea eax, [ebp+8]是可以的呢?我的书上说:
MOV存储到EAX中的值必须由汇编程序计算(也就是说,它最终必须是一个常量)
但是这对我来说没有意义!CONSTANT是什么意思?显而易见的理解是,CONSTANT应该在程序运行之前由汇编器/链接器计算,但是,想想mov eax, [ebp+8]是一条法律的指令。汇编器/链接器在程序运行之前没有办法知道[ebp+8]*(ebp+8)作为C语言)的值!

wecizke3

wecizke31#

你的书似乎只讨论了移动即时,比如mov eax, 1234mov eax, foo(符号地址)。
一个更完整的规则是mov的源操作数必须是:

  • 链接时间常量值(mov-immediate)
  • 或者已经存在于其他地方(存储器或另一寄存器)的值。
    mov只能拷贝,不会先通过ALU送入数据再写入目的地。

内存源操作数可以使用像[ebp+8][edx + eax*4 + my_array]这样的寻址模式,但这与从该地址加载或存储到该地址的数据发生的情况无关。地址生成在CPU的单独部分中完成(或在原始8086上,在微代码处理指令的单独阶段)。
x86的机器码格式编码addressing modes(以及寄存器与内存源)几乎所有指令都是相同的方式,所以mov不必做任何特殊的事情来支持mov eax, [ebp+8],就像add eax, [ebp+8]在进行地址计算和加载后也会对数据进行加法。只有操作码字节不同,指定如何处理数据,而不是操作数在哪里(寄存器与内存寻址模式)。(有two opcodes for most instructions,一个源可以是内存,一个目的地可以是内存。我在这里谈论两个内存源操作码。)
lea eax, [ebp+8]的机器码除了操作码字节外,其他都是一样的。**莱亚的特殊之处在于,它使用寻址模式的机器码格式来编码移位/加法指令,该指令实际上并不访问内存。**参见 * Using LEA on values that aren't addresses / pointers? *
没有其他指令可以做到这一点,所以你永远不能使用eax+3作为任何指令的源操作数。

有两个独立的概念:指令的数据来自哪里(立即、寄存器或寻址模式寻址的内存)与该数据发生的情况(复制、imul、sub、popcount、..)。从这个意义上说,莱亚没有数据输入,因为它没有解引用[ebp+8]

莱亚只接受地址,就像C的& address-of运算符一样,它取消了原本是解引用的操作,例如&ptr[3]ptr+3相同,但语法不同。莱亚有趣的一点是,x86 ADD只能做ptr+=3,修改寄存器(或内存),但莱亚可以像tmp = &ptr[3]一样复制和添加。
(And对于32位或64位寻址模式,还可以添加移位寄存器,如tmp = &ptr[x*4 + 3]
有点离题,但可能相关:
大多数经典的x86整数指令只有一个或两个操作数(* What kind of address instruction does the x86 cpu have? *)。尽管imul ecx, [edx+8], 123可以在将结果写入非输入寄存器时进行数学运算。(与大多数立即源指令不同,新的186形式的imul没有窃取ModRM中的/r字段作为额外的操作码位,所以它可以有3个操作数,包括立即数,其存在由操作码发出信号。)但硬件乘法器的输入仍然是立即数和直接来自内存的值; imul ecx, edx+8, 123是不可编码的。

相关问题