nav*_*een 117 x86 assembly instruction-set
我想知道这些说明之间的区别是:
MOV AX, [TABLE-ADDR]
Run Code Online (Sandbox Code Playgroud)
和
LEA AX, [TABLE-ADDR]
Run Code Online (Sandbox Code Playgroud)
Rub*_*ink 144
LEA 加载有效地址MOV 表示负载值简而言之,LEA加载指向您正在寻址的项目的指针,而MOV加载该地址的实际值.
目的LEA是允许一个人执行非平凡的地址计算并存储结果[供以后使用]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
Run Code Online (Sandbox Code Playgroud)
如果只涉及常数,MOV(通过汇编程序的常量计算)有时似乎与最简单的使用情况重叠LEA.如果您具有多个基址的多部分计算等,它很有用.
Ami*_*mar 43
在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
Run Code Online (Sandbox Code Playgroud)
在MASM语法中,用于OFFSET var获取mov-immediate而不是load.
Bil*_*ter 26
指令MOV reg,addr意味着将存储在地址addr中的变量读入寄存器reg.LEA reg,addr指令意味着将地址(不是存储在地址中的变量)读入寄存器reg.
MOV指令的另一种形式是MOV reg,immdata,这意味着将立即数据(即常数)immdata读入寄存器reg.注意,如果LEA reg中的addr,addr只是一个常量(即一个固定的偏移量),则该LEA指令基本上与等效的MOV reg,immdata指令完全相同,该指令加载与立即数据相同的常量.
Que*_*lef 14
以前的答案都没有完全解决我自己的困惑,所以我想添加我自己的。
我所缺少的是,lea操作处理括号的使用方式与处理方式不同mov。
想想 C。假设我有一个数组long,我称之为array。现在表达式array[i]执行取消引用,从地址array + i * sizeof(long)[1] 的内存中加载值。
另一方面,考虑表达式&array[i]。这仍然包含子表达式array[i],但不执行解引用!的意思array[i]变了。它不再意味着执行尊重,而是作为一种规范,告诉&我们正在寻找什么内存地址。如果您愿意,也可以将&视为“取消”取消引用。
因为这两个用例在许多方面相似,所以它们共享语法array[i],但是 a 的存在与否会&改变该语法的解释方式。没有&,它是一个取消引用,实际上是从数组中读取的。与&,它不是。该值array + i * sizeof(long)仍会计算,但不会取消引用。
情况与mov和非常相似lea。With mov,会发生取消引用,而lea. 尽管在两者中都使用了括号。例如,movq (%r8), %r9和leaq (%r8), %r9。有了mov这些括号表示“解引用”; 有lea,他们没有。这类似于 how array[i]only 在没有&.
一个例子是有序的。
考虑代码
movq (%rdi, %rsi, 8), %rbp
Run Code Online (Sandbox Code Playgroud)
这会将内存位置的值加载%rdi + %rsi * 8到寄存器中%rbp。即:获取寄存器中%rdi的值和寄存器中的值%rsi。将后者乘以 8,然后将其与前者相加。找到该位置的值并将其放入寄存器%rbp。
此代码对应于 C 行x = array[i];,其中array变成%rdi和i变成%rsi和x变成%rbp。的8是包含在数组中的数据类型的长度。
现在考虑使用的类似代码lea:
leaq (%rdi, %rsi, 8), %rbp
Run Code Online (Sandbox Code Playgroud)
正如使用movq相当于取消引用,使用leaq此对应于未提领。这条装配线对应于 C 线x = &array[i];。回想一下,&将array[i]取消引用的含义更改为简单地指定位置。同样, 的使用leaq将 的含义(%rdi, %rsi, 8)从取消引用更改为指定位置。
这行代码的语义如下:获取寄存器中%rdi的值和寄存器中的值%rsi。将后者乘以 8,然后将其与前者相加。将此值放入寄存器%rbp。不涉及内存加载,只涉及算术运算 [2]。
请注意,我对leaq和 的描述之间的唯一区别movq是movq取消了引用,而leaq没有取消引用。其实写leaq描述,我基本上就是把 的描述复制+粘贴movq,然后去掉“在这个位置查找值”。
总而言之:movqvs.leaq很棘手,因为它们对待括号的使用,如(%rsi)和(%rdi, %rsi, 8),不同。在movq(以及除 之外的所有其他指令lea)中,这些括号表示真正的取消引用,而在leaq它们中,它们不是并且纯粹是方便的语法。
[1] 我说过 whenarray是一个数组long,表达式array[i]从地址加载值array + i * sizeof(long)。这是真的,但有一个微妙之处需要解决。如果我写C代码
long x = array[5];
Run Code Online (Sandbox Code Playgroud)
这和打字不一样
long x = *(array + 5 * sizeof(long));
Run Code Online (Sandbox Code Playgroud)
好像应该是根据我之前的说法,其实不然。
发生的事情是 C 指针添加有一个技巧。假设我有一个指向ptype 值的指针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)
Run Code Online (Sandbox Code Playgroud)
因为C会自动繁衍5的sizeof(long)。
所以在这个 StackOverflow 问题的上下文中,这一切有什么关系?这意味着当我说“地址array + i * sizeof(long)”时,我的意思并不是将“ array + i * sizeof(long)”解释为 C 表达式。我自己做乘法是sizeof(long)为了让我的答案更明确,但我明白由于这个原因,这个表达式不应该被读作 C。就像使用 C 语法的普通数学一样。
[2] 旁注:因为所有lea都是算术运算,所以它的参数实际上不必引用有效地址。出于这个原因,它通常用于对可能不打算取消引用的值执行纯算术。例如,cc通过-O2优化翻译
long f(long x) {
return x * 5;
}
Run Code Online (Sandbox Code Playgroud)
进入以下(删除不相关的行):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
Run Code Online (Sandbox Code Playgroud)
Lar*_*s D 10
如果您只指定文字,则没有区别.LEA有更多的能力,你可以在这里阅读它们:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
Bar*_*cik 10
这取决于使用的汇编程序,因为
mov ax,table_addr
Run Code Online (Sandbox Code Playgroud)
在MASM中工作
mov ax,word ptr[table_addr]
Run Code Online (Sandbox Code Playgroud)
所以它加载了第一个字节table_addr而不是偏移量table_addr.你应该使用
mov ax,offset table_addr
Run Code Online (Sandbox Code Playgroud)
要么
lea ax,table_addr
Run Code Online (Sandbox Code Playgroud)
它的工作方式相同.
lea如果table_addr是局部变量,版本也可以正常工作
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
Run Code Online (Sandbox Code Playgroud)
如其他答案所述:
MOV将抢在数据括号内的地址和位置数据到目标操作数。LEA将执行括号内地址的计算,并将计算出的地址放入目标操作数。发生这种情况时并没有真正进入内存并获取数据。所做的工作LEA是计算“有效地址”。因为内存可以通过几种不同的方式寻址(参见下面的示例),LEA有时用于在不使用显式ADD或MUL指令(或等效指令)的情况下将寄存器相加或相乘。
由于每个人都使用 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 */
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
157335 次 |
| 最近记录: |