此问题在此处已有答案:
x86-64 assembly order of operands(1个答案)
上个月关门了。
下面的代码是如何编写的:
#include <stdio.h>
#include <stdint.h>
uint32_t fibonacci(uint32_t pos) {
long next = 1, current = 0, tmp;
for (long n = 1; n <= pos; n++) {
tmp = current;
current = next;
next += tmp;
}
return current;
}
int main() {
uint32_t target_pos, result;
scanf("%u", &target_pos);
result = fibonacci(target_pos);
printf("%u\n", result);
return 0;
}
这将产生以下汇编代码:
0000000000001189 <fibonacci>:
1189: endbr64
118d: push %rbp
118e: mov %rsp,%rbp
1191: mov %edi,-0x24(%rbp)
1194: movq $0x1,-0x20(%rbp)
119c: movq $0x0,-0x18(%rbp)
11a4: movq $0x1,-0x10(%rbp)
11ac: jmp 11cb <fibonacci+0x42>
11ae: mov -0x18(%rbp),%rax
11b2: mov %rax,-0x8(%rbp)
11b6: mov -0x20(%rbp),%rax
11ba: mov %rax,-0x18(%rbp)
11be: mov -0x8(%rbp),%rax
11c2: add %rax,-0x20(%rbp)
11c6: addq $0x1,-0x10(%rbp)
11cb: mov -0x24(%rbp),%eax
11ce: cmp %rax,-0x10(%rbp)
11d2: jle 11ae <fibonacci+0x25>
11d4: mov -0x18(%rbp),%rax
11d8: pop %rbp
11d9: retq
我做了一些调查,这是我的发现:
1.第一条指令(endbr 64)与安全性相关[ref],在我的例子中,它将是一个NOP
1.指令0x 118 d、0x 118 e、0x 11 d8和0x 11 d9与返回[ref]相关
%rbp
是堆栈的基底
有了这些信息,我们就有了这个图表:
我试着去理解它,但有些操作完全是无稽之谈:
%eax
(我发现它用于返回数据)被保存(而不是加载),未更改%edi
在开始时加载,并且从该点起不会更改/查询- asm在
-0x10(%rbp)
上执行“变量++”;所以你期望在-0x10(%rbp)
中存储n
,这是唯一一个加1的变量。但是在一条指令中,%rax
与-0x10(%rbp)
进行了“小于或等于”的比较(查看原始C代码,我假设asm正在执行pos <= n
,而实际上应该是相反的)。
就像这样越来越多...
有人能解释一下这到底是怎么回事吗?我用一个没有优化的AMD 3950X编译了C代码。
1条答案
按热度按时间9q78igpj1#
由于这显然是原来的问题,我会继续下去,并张贴它作为一个答案,所以这个问题可以得到关闭。
我相信你把它弄反了,在at&t语法中,
mov %edi,-0x24(%rbp)
意味着把edi移到rbp之前的内存地址0x24,这是有意义的,因为在x64调用约定中(对于linux),edi包含第一个参数。如果你更喜欢阅读intel语法(哪个逻辑人不喜欢呢?),告诉你的反汇编器这就是你想要的输出格式。如果你使用objdump来产生你的输出,那就使用
-M intel
。虽然我还没有回顾你的其他问题,但这可能是所有问题的原因。
至于为什么intel语法不是默认的,我只想说 * 有些人 * 更喜欢at&t语法(可能就是那些认为制表符应该是2个空格的人),我希望大多数linux工具默认使用这种格式,至少现在你知道要注意它了。