MOV 和 LEA 有什么区别?

发布于 2024-08-10 07:15:24 字数 128 浏览 3 评论 0原文

我想知道这些说明之间的区别是什么:

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]

I would like to know what the difference between these instructions is:

MOV AX, [TABLE-ADDR]

and

LEA AX, [TABLE-ADDR]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

悍妇囚夫 2024-08-17 07:15:24
  • LEA 表示加载有效地址
  • MOV 表示加载值

简而言之,LEA 加载指向您正在寻址的项目的指针,而 MOV 加载实际值在那个地址。

LEA 的目的是允许执行一项重要的地址计算并存储结果[供以后使用],

LEA ax, [BP+SI+5] ; Compute address of value

MOV ax, [BP+SI+5] ; Load value at that address

其中仅涉及常量,MOV(通过汇编程序的常量计算)有时可能会与 LEA 最简单的使用情况重叠。如果您有多个基地址等的多部分计算,它很有用。

  • LEA means Load Effective Address
  • MOV means Load Value

In short, LEA loads a pointer to the item you're addressing whereas MOV loads the actual value at that address.

The purpose of LEA is to allow one to perform a non-trivial address calculation and store the result [for later usage]

LEA ax, [BP+SI+5] ; Compute address of value

MOV ax, [BP+SI+5] ; Load value at that address

Where there are just constants involved, MOV (through the assembler's constant calculations) can sometimes appear to overlap with the simplest cases of usage of LEA. Its useful if you have a multi-part calculation with multiple base addresses etc.

痴者 2024-08-17 07:15:24

在 NASM 语法中:

mov eax, var       == lea eax, [var]   ; i.e. mov r32, imm32
lea eax, [var+16]  == mov eax, var+16
lea eax, [eax*4]   == shl eax, 2        ; but without setting flags

在 MASM 语法中,使用 OFFSET var 获取 mov-immediate 而不是加载。

In NASM syntax:

mov eax, var       == lea eax, [var]   ; i.e. mov r32, imm32
lea eax, [var+16]  == mov eax, var+16
lea eax, [eax*4]   == shl eax, 2        ; but without setting flags

In MASM syntax, use OFFSET var to get a mov-immediate instead of a load.

红玫瑰 2024-08-17 07:15:24

之前的答案都没有完全解决我自己的困惑,所以我想添加我自己的答案。

我缺少的是 lea 操作对括号的使用与 mov 的处理方式不同。

想想 C。假设我有一个 long 数组,我称之为 array。现在,表达式 array[i] 执行取消引用,从地址 array + i * sizeof(long) [1] 处的内存加载值。

另一方面,考虑表达式&array[i]。这仍然包含子表达式 array[i],但不执行取消引用! array[i] 的含义已更改。它不再意味着执行遵从,而是充当一种规范,告诉&我们正在寻找什么内存地址。如果您愿意,您也可以将 & 视为“取消”取消引用。

由于这两个用例在很多方面都很相似,因此它们共享语法 array[i],但 & 的存在或不存在会改变该语法的解释方式。如果没有 &,它是取消引用并实际上从数组中读取。对于 & 来说,情况并非如此。值 array + i * sizeof(long) 仍会计算,但不会取消引用。

movlea 的情况非常相似。使用 mov 会发生取消引用,而使用 lea 则不会发生这种情况。尽管两者都使用了括号,但情况仍然如此。例如,movq (%r8)、%r9leaq (%r8)、%r9。对于mov,这些括号意味着“取消引用”;对于 lea,他们不会。这类似于当没有 &array[i] 仅表示“取消引用”。

下面是一个例子。

考虑以下代码

movq (%rdi, %rsi, 8), %rbp

:它将内存位置 %rdi + %rsi * 8 处的值加载到寄存器 %rbp 中。即:获取寄存器%rdi中的值和寄存器%rsi中的值。将后者乘以 8,然后与前者相加。 找到此位置的值并将其放入寄存器%rbp中。

此代码对应于 C 行 x = array[i];,其中 array 变为 %rdii变为 %rsix 变为 %rbp8 是数组中包含的数据类型的长度。

现在考虑使用 lea 的类似代码:

leaq (%rdi, %rsi, 8), %rbp

正如使用 movq 对应于取消引用一样,这里使用 leaq 对应于 not 取消引用。这行汇编代码对应于 C 行x = &array[i];。回想一下,&array[i] 的含义从解除引用更改为简单地指定位置。同样,使用 leaq 会将 (%rdi, %rsi, 8) 的含义从取消引用更改为指定位置。

这行代码的语义如下:获取寄存器%rdi中的值和寄存器%rsi中的值。将后者乘以 8,然后与前者相加。将此值放入寄存器 %rbp 中。不涉及内存加载,仅涉及算术运算 [2]。

请注意,我对 leaq 和 movq 的描述之间的唯一区别是 movq 进行了取消引用,而 leaq 则没有't。事实上,为了编写leaq描述,我基本上复制+粘贴了movq的描述,然后删除了“在这个位置查找值”。

总结一下: movqleaq 比较棘手,因为它们对待括号的使用,如 (%rsi)(% rdi,%rsi,8),不同。在 movq(以及除 lea 之外的所有其他指令)中,这些括号表示真正的取消引用,而在 leaq 中它们不是,并且纯粹是方便的语法。


[1] 我已经说过,当 array 是一个 long 数组时,表达式 array[i] 从地址 array[i] 加载值代码>数组+ i * sizeof(long)。这是事实,但有一个微妙之处需要解决。如果我编写 C 代码,

long x = array[5];

这与键入它相同,

long x = *(array + 5 * sizeof(long));

它似乎应该基于我之前的陈述,但事实并非如此。

发生的事情是 C 指针加法有一个技巧。假设我有一个指针 p 指向 T 类型的值。表达式 p + i 确实表示“p 处的位置加上 i 字节”。相反,表达式 p + i 实际上 表示“p 处的位置加上 i * sizeof(T) 字节”。

这样做的便利之处在于,要获取“下一个值”,我们只需编写 p + 1 而不是 p + 1 * sizeof(T)

这意味着 C 代码 long x = array[5]; 实际上相当于,

long x = *(array + 5)

因为 C 会自动将 5 乘以 sizeof(long)

那么,在 StackOverflow 问题的背景下,这一切有何相关性?这意味着当我说“地址 array + i * sizeof(long)”时,我的意思是“array + i * sizeof(long)””被解释为 C 表达式。我自己乘以 sizeof(long) 是为了让我的答案更明确,但请理解,因此,这个表达式不应该读作 C。就像使用 C 的普通数学一样句法。

[2] 旁注:因为 lea 所做的只是算术运算,因此它的参数实际上不必引用有效地址。因此,它通常用于对可能不打算取消引用的值执行纯算术。例如,经过 -O2 优化的 cc 会转换

long f(long x) {
  return x * 5;
}

为以下内容(删除了不相关的行):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret

None of the previous answers quite got to the bottom of my own confusion, so I'd like to add my own.

What I was missing is that lea operations treat the use of parentheses different than how mov does.

Think of C. Let's say I have an array of long that I call array. Now the expression array[i] performs a dereference, loading the value from memory at the address array + i * sizeof(long) [1].

On the other hand, consider the expression &array[i]. This still contains the sub-expression array[i], but no dereferencing is performed! The meaning of array[i] has changed. It no longer means to perform a deference but instead acts as a kind of a specification, telling & what memory address we're looking for. If you like, you could alternatively think of the & as "cancelling out" the dereference.

Because the two use-cases are similar in many ways, they share the syntax array[i], but the existence or absence of a & changes how that syntax is interpreted. Without &, it's a dereference and actually reads from the array. With &, it's not. The value array + i * sizeof(long) is still calculated, but it is not dereferenced.

The situation is very similar with mov and lea. With mov, a dereference occurs that does not happen with lea. This is despite the use of parentheses that occurs in both. For instance, movq (%r8), %r9 and leaq (%r8), %r9. With mov, these parentheses mean "dereference"; with lea, they don't. This is similar to how array[i] only means "dereference" when there is no &.

An example is in order.

Consider the code

movq (%rdi, %rsi, 8), %rbp

This loads the value at the memory location %rdi + %rsi * 8 into the register %rbp. That is: get the value in the register %rdi and the value in the register %rsi. Multiply the latter by 8, and then add it to the former. Find the value at this location and place it into the register %rbp.

This code corresponds to the C line x = array[i];, where array becomes %rdi and i becomes %rsi and x becomes %rbp. The 8 is the length of the data type contained in the array.

Now consider similar code that uses lea:

leaq (%rdi, %rsi, 8), %rbp

Just as the use of movq corresponded to dereferencing, the use of leaq here corresponds to not dereferencing. This line of assembly corresponds to the C line x = &array[i];. Recall that & changes the meaning of array[i] from dereferencing to simply specifying a location. Likewise, the use of leaq changes the meaning of (%rdi, %rsi, 8) from dereferencing to specifying a location.

The semantics of this line of code are as follows: get the value in the register %rdi and the value in the register %rsi. Multiply the latter by 8, and then add it to the former. Place this value into the register %rbp. No load from memory is involved, just arithmetic operations [2].

Note that the only difference between my descriptions of leaq and movq is that movq does a dereference, and leaq doesn't. In fact, to write the leaq description, I basically copy+pasted the description of movq, and then removed "Find the value at this location".

To summarize: movq vs. leaq is tricky because they treat the use of parentheses, as in (%rsi) and (%rdi, %rsi, 8), differently. In movq (and all other instruction except lea), these parentheses denote a genuine dereference, whereas in leaq they do not and are purely convenient syntax.


[1] I've said that when array is an array of long, the expression array[i] loads the value from the address array + i * sizeof(long). This is true, but there's a subtlety that should be addressed. If I write the C code

long x = array[5];

this is not the same as typing

long x = *(array + 5 * sizeof(long));

It seems that it should be based on my previous statements, but it's not.

What's going on is that C pointer addition has a trick to it. Say I have a pointer p pointing to values of type T. The expression p + i does not mean "the position at p plus i bytes". Instead, the expression p + i actually means "the position at p plus i * sizeof(T) bytes".

The convenience of this is that to get "the next value" we just have to write p + 1 instead of p + 1 * sizeof(T).

This means that the C code long x = array[5]; is actually equivalent to

long x = *(array + 5)

because C will automatically multiply the 5 by sizeof(long).

So in the context of this StackOverflow question, how is this all relevant? It means that when I say "the address array + i * sizeof(long)", I do not mean for "array + i * sizeof(long)" to be interpreted as a C expression. I am doing the multiplication by sizeof(long) myself in order to make my answer more explicit, but understand that due to that, this expression should not be read as C. Just as normal math that uses C syntax.

[2] Side note: because all lea does is arithmetic operations, its arguments don't actually have to refer to valid addresses. For this reason, it's often used to perform pure arithmetic on values that may not be intended to be dereferenced. For instance, cc with -O2 optimization translates

long f(long x) {
  return x * 5;
}

into the following (irrelevant lines removed):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret
能否归途做我良人 2024-08-17 07:15:24

MOV reg,addr 指令的意思是将地址addr 处存储的变量读取到寄存器reg 中。 LEA reg,addr 指令表示将地址(不是该地址存储的变量)读入寄存器 reg 中。

MOV指令的另一种形式是MOV reg,immdata,意思是将立即数(即常量)immdata读入寄存器reg。请注意,如果 LEA reg,addr 中的 addr 只是一个常量(即固定偏移量),则该 LEA 指令本质上与加载相同常量作为立即数据的等效 MOV reg,immdata 指令完全相同。

The instruction MOV reg,addr means read a variable stored at address addr into register reg. The instruction LEA reg,addr means read the address (not the variable stored at the address) into register reg.

Another form of the MOV instruction is MOV reg,immdata which means read the immediate data (i.e. constant) immdata into register reg. Note that if the addr in LEA reg,addr is just a constant (i.e. a fixed offset) then that LEA instruction is essentially exactly the same as an equivalent MOV reg,immdata instruction that loads the same constant as immediate data.

忘年祭陌 2024-08-17 07:15:24

如果仅指定文字,则没有区别。不过,LEA 有更多功能,您可以在这里阅读它们:

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136

If you only specify a literal, there is no difference. LEA has more abilities, though, and you can read about them here:

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136

离鸿 2024-08-17 07:15:24

正如其他答案中所述:

  • MOV 将抓取括号内地址处的数据,并将该数据放入目标操作数中。
  • LEA 将对括号内的地址执行计算,并将计算出的地址放入目标操作数中。这种情况的发生并没有实际进入内存并获取数据。 LEA 所做的工作是计算“有效地址”。

由于可以通过多种不同的方式对内存进行寻址(请参阅下面的示例),LEA 有时用于将寄存器相加或相乘,而不使用显式的 ADDMUL< /code> 指令(或等效指令)。

由于每个人都在展示 Intel 语法的示例,因此这里有一些 AT&T 语法:

MOVL 16(%ebp), %eax       /* put long  at  ebp+16  into eax */
LEAL 16(%ebp), %eax       /* add 16 to ebp and store in eax */

MOVQ (%rdx,%rcx,8), %rax  /* put qword at  rcx*8 + rdx  into rax */
LEAQ (%rdx,%rcx,8), %rax  /* put value of "rcx*8 + rdx" into rax */

MOVW 5(%bp,%si), %ax      /* put word  at  si + bp + 5  into ax */
LEAW 5(%bp,%si), %ax      /* put value of "si + bp + 5" into ax */

MOVQ 16(%rip), %rax       /* put qword at rip + 16 into rax                 */
LEAQ 16(%rip), %rax       /* add 16 to instruction pointer and store in rax */

MOVL label(,1), %eax      /* put long at label into eax            */
LEAL label(,1), %eax      /* put the address of the label into eax */

As stated in the other answers:

  • MOV will grab the data at the address inside the brackets and place that data into the destination operand.
  • LEA will perform the calculation of the address inside the brackets and place that calculated address into the destination operand. This happens without actually going out to the memory and getting the data. The work done by LEA is in the calculating of the "effective address".

Because memory can be addressed in several different ways (see examples below), LEA is sometimes used to add or multiply registers together without using an explicit ADD or MUL instruction (or equivalent).

Since everyone is showing examples in Intel syntax, here are some in AT&T syntax:

MOVL 16(%ebp), %eax       /* put long  at  ebp+16  into eax */
LEAL 16(%ebp), %eax       /* add 16 to ebp and store in eax */

MOVQ (%rdx,%rcx,8), %rax  /* put qword at  rcx*8 + rdx  into rax */
LEAQ (%rdx,%rcx,8), %rax  /* put value of "rcx*8 + rdx" into rax */

MOVW 5(%bp,%si), %ax      /* put word  at  si + bp + 5  into ax */
LEAW 5(%bp,%si), %ax      /* put value of "si + bp + 5" into ax */

MOVQ 16(%rip), %rax       /* put qword at rip + 16 into rax                 */
LEAQ 16(%rip), %rax       /* add 16 to instruction pointer and store in rax */

MOVL label(,1), %eax      /* put long at label into eax            */
LEAL label(,1), %eax      /* put the address of the label into eax */
找个人就嫁了吧 2024-08-17 07:15:24

这取决于所使用的汇编器,因为

mov ax,table_addr

在 MASM 中,它的工作原理是

mov ax,word ptr[table_addr]

,它从 table_addr 加载第一个字节,而不是 table_addr 的偏移量。您应该改用

mov ax,offset table_addr

或 ,

lea ax,table_addr

其效果相同。

如果 table_addr 是局部变量,lea 版本也可以正常工作,例如

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

It depends on the used assembler, because

mov ax,table_addr

in MASM works as

mov ax,word ptr[table_addr]

So it loads the first bytes from table_addr and NOT the offset to table_addr. You should use instead

mov ax,offset table_addr

or

lea ax,table_addr

which works the same.

lea version also works fine if table_addr is a local variable e.g.

some_procedure proc

local table_addr[64]:word

lea ax,table_addr
辞慾 2024-08-17 07:15:24

基本上......“计算后进入REG......”
它似乎也适用于其他目的:)

如果您忘记该值是一个指针,
你可以用它来进行代码优化/最小化......无论如何......

MOV EBX , 1
MOV ECX , 2

;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]

EAX = 8

原来它是:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5

Basically ... "Move into REG ... after computing it..."
it seems to be nice for other purposes as well :)

if you just forget that the value is a pointer
you can use it for code optimizations/minimization ...what ever..

MOV EBX , 1
MOV ECX , 2

;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]

EAX = 8

originaly it would be:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
貪欢 2024-08-17 07:15:24

让我们通过一个例子来理解这一点。

移动eax,[ebx]

lea eax, [ebx]
假设 ebx 中的值为 0x400000。然后 mov 将转到地址 0x400000 并将 4 个字节的数据复制到 eax 寄存器中。而 lea 会将地址 0x400000 复制到 eax 中。因此,在每种情况下执行每条指令后,eax 的值将是(假设在内存 0x400000 处包含的是 30)。

eax = 30(如果是 mov)
eax = 0x400000(如果是 lea)
对于定义,mov 将数据从 rm32 复制到目标 (mov dest rm32),lea(加载有效地址) 会将地址复制到目标 (mov dest rm32)。

Lets understand this with a example.

mov eax, [ebx]
and

lea eax, [ebx]
Suppose value in ebx is 0x400000. Then mov will go to address 0x400000 and copy 4 byte of data present their to eax register.Whereas lea will copy the address 0x400000 into eax. So, after the execution of each instruction value of eax in each case will be (assuming at memory 0x400000 contain is 30).

eax = 30 (in case of mov)
eax = 0x400000 (in case of lea)
For definition mov copy the data from rm32 to destination (mov dest rm32) and lea(load effective address) will copy the address to destination (mov dest rm32).

零度° 2024-08-17 07:15:24

MOV 可以做与 LEA [标签] 相同的事情,但 MOV 指令包含指令本身的有效地址作为立即数(由汇编器预先计算)。 LEA 使用 PC-relative 来计算指令执行期间的有效地址。

MOV can do same thing as LEA [label], but MOV instruction contain the effective address inside the instruction itself as an immediate constant (calculated in advance by the assembler). LEA uses PC-relative to calculate the effective address during the execution of the instruction.

眉目亦如画i 2024-08-17 07:15:24

差异很微妙但很重要。 MOV 指令是“MOVe”,实际上是 TABLE-ADDR 标签所代表的地址的副本。 LEA 指令是“加载有效地址”,它是一种间接指令,这意味着 TABLE-ADDR 指向找到要加载的地址的内存位置。

有效地使用 LEA 相当于在 C 等语言中使用指针,因此它是一个强大的指令。

The difference is subtle but important. The MOV instruction is a 'MOVe' effectively a copy of the address that the TABLE-ADDR label stands for. The LEA instruction is a 'Load Effective Address' which is an indirected instruction, which means that TABLE-ADDR points to a memory location at which the address to load is found.

Effectively using LEA is equivalent to using pointers in languages such as C, as such it is a powerful instruction.

山人契 2024-08-17 07:15:24

LEA(加载有效地址)是移位加法指令。它被添加到8086中是因为硬件可以解码和计算寻址模式。

LEA (Load Effective Address) is a shift-and-add instruction. It was added to 8086 because hardware is there to decode and calculate adressing modes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文