【问题标题】:Assembly language - can LEA instruction be used to load value汇编语言 - 可以使用 LEA 指令来加载值
【发布时间】:2018-07-28 07:27:21
【问题描述】:

我是汇编语言的新手。我目前正在处理这个Assembly Guide。我对 LEA 指令有疑问。我对LEA指令的理解是LEA源操作数的有效地址加载目的操作数

以下示例来自同一链接。

lea edi, [ebx+4*esi] — the quantity EBX+4*ESI is placed in EDI.
lea eax, [var] — the value in var is placed in EAX.
lea eax, [val] — the value val is placed in EAX.

在上面的第 2 和第 3 示例注释中,表示值已加载到 EAX 中。这是我的困惑。请告诉我是否可以使用 LEA 指令加载有效地址以及目标操作数中的值。


上图来自 youtube 的 Open SecurityTraining 频道的 Introduction to assembly language

谢谢。

【问题讨论】:

  • 第二和第三错了。地址被放入eax。如有疑问,请查阅官方参考资料(谷歌查看英特尔手册 2)
  • 在更新中添加的问题的第二部分与stackoverflow.com/questions/46597055/… 完全相同:是的,LEA 可用于移动/添加任何值,即使该值不是地址。第一部分:解决指南/教程中示例中的措辞错误引起的混淆是一个有效的问题。由错误的指南/教程引起的混乱使一个原本太微不足道的问题有效,IMO。

标签: assembly x86 masm


【解决方案1】:

LEA 是一个 ALU 移位+加法指令,它使用寻址模式语法和机器代码编码来利用硬件支持来解码这种操作。 (It doesn't care if the inputs are addresses or not,所以你的问题的第二部分在那里得到了回答:lea eax, [ecx + eax*2] 有效地实现了x*2 + y。)

在任何情况下lea 都不会从内存中加载或存储。见Intel's instruction-manual entry for it

有趣的事实:唯一的例外是 ModR/M 字节编码的是寄存器源而不是内存。例如lea eax, eax 如果您的汇编程序没有拒绝汇编它,或者您使用 db 伪指令手动对其进行编码,则 #UD(未定义指令)会出错。如果您使用 asm 而不是十六进制机器代码编写,这在实践中不必担心。但是它没有任何依赖于数据的异常;它根本不关心它在操作什么值。


在第三个中,我认为他们在谈论val equ 4val = 4 之类的东西,所以“val 的值”是一个汇编时间常数,不存储在内存中。

是的,您可以为此使用 LEA(或任何 32 位常量),但 mov eax, val 更短且更高效。使用具有绝对 disp32 寻址模式的 LEA 毫无意义。

有趣的事实:MASM 忽略 [] 周围的汇编时常量:mov eax, [val]mov eax, imm32,与 mov eax, val 相同。 Ross Ridge 在Confusing brackets in MASM32 上写了一个很好的答案。


lea eax, [var] — the value in var is placed in EAX.

评论有误。 var地址 放在 EAX 中。在正常的 asm 术语中,in 符号名称中的值表示存储在该地址的内存中的值。

mov eax, OFFSET varlea eax, [var] 更高效、更短。 有些人喜欢写lea,因为对于人类读者来说具有“语义”意义:取地址。但是,如果您使用汇编语言编写代码,则人类可读性应该排在效率之后,并且只能作为决胜局获胜,例如choosing esi for a source pointer when the choice makes no other difference. (很好地格式化/缩进你的代码,并很好地注释它。)

lea eax, [var + edi] 是有道理的,你不能用mov 做到这一点。但是您可以使用mov eax, OFFSET var + 1234,因为汇编器+链接器必须能够支持为[var + 1234] 等寻址模式填充32 位符号+偏移值。

在 64 位模式下,lea rax, [rel var] 在与位置无关的代码中有意义:您无法使用mov 获得相对于 RIP 的地址。

【讨论】:

  • 非常感谢您提供有关 MASM 的信息(忽略括号)。我添加了一张图片。我再次怀疑 LEA 指令。由于 LEA 不会从内存中加载或存储。示例中对 LEA 的先前指令将地址加载到 eax 和 ecx 中,执行加法和乘法并将结果存储在 eax 中。这仍然让我感到困惑。
  • @vanquish:LEA 是移位加指令。这就是编译器(正确地)选择实现2*x + y 的方式。在这种情况下,操作数不是地址;阅读我第一段中的链接。这与您最初关于误导性指南/教程的问题确实无关,并且是该链接的副本。
  • 我喜欢添加/反对 LEA 在引用局部变量方面有其独特的用途。在这种情况下,它不能被MOV reg, offset var 替换。因此,根据经验,offset var 用于编译/汇编时寻址,LEA reg, var 用于运行时寻址。
  • @zx485:我习惯了 NASM 语法,您可以将其写为 lea rax, [rsp+16][rbp-8] 之类的,也许使用 16 的汇编时符号常量。所以,是的,如果您以这种方式使用 MASM 来隐藏静态变量和局部变量之间的差异,那么这是有道理的。
  • @zx485:它确实隐藏了语法上的差异,但我想如果你习惯了var 可能会扩展到[ebp+k],那么它看起来并不那么奇怪。如果你在 asm 中手写一个函数,那么寻找优化方法是有意义的,比如重用相同的堆栈槽以在不同的时间溢出不同的东西,如果你需要溢出的话。并且在 32 位模式下将 ebp 绑定为帧指针非常昂贵,只剩下 6 个 GP 寄存器。 (但如果没有固定的 EBP,堆栈中同一点的偏移量将随着 push/pop 的变化而变化)。通过 push/pop + ESP 寻址,您可以获得堆栈同步 uops :/
【解决方案2】:

LEA 字面意思是“加载有效地址”。它首先计算一个地址 - 称为“有效”,因为它可能由多个部分组成,结果有效最终到达目的地 - 并将该地址加载到某处。
[var]使用寻址模式,lea 仅计算地址并将其存储在目标位置,它不使用它从主内存加载某些内容。 “var 中的值”是错误的,“var 值”更重要,如果“var”指的是标签的地址,而不是存储的值。

由于源明确表示“[...] 内存位置的内容未加载,仅计算有效地址并将其放入寄存器”,我认为这只是一个误导性的错字,而不是错误信息。

【讨论】:

  • lea 只是计算地址并将其存储在目标位置 那么下面的说明是否相同?如果 var 被声明为 var dw 4 lea eax, var mov eax, var
  • lea eax, varmov eax, offset var 相同。对于像var 这样的内存操作数,括号不是必需的,所以lea eax, [var]lea eax, var 相同,IIRC。例如,您可以使用lea 执行的操作是将lea eax, [ebx+esi*4] 加载到ebx+esi*4eax。否则,您需要mov eax, esi; shl eax, 2; add eax, ebx 之类的东西。
  • 非常感谢这个例子lea eax, [ebx+esi*4]所以ebxesi 也可以包含内存中的地址,对吧?
  • 是的,它们可能包含地址或其他内容。重点是地址ebx+esi*4是计算出来的,但是后面并没有访问RAM,而是简单的存储在eax中。
猜你喜欢
  • 2020-09-22
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多