x86_64 ABI:反汇编问题
Posted
技术标签:
【中文标题】x86_64 ABI:反汇编问题【英文标题】:x86_64 ABI: disassembly issue 【发布时间】:2015-09-11 17:30:28 【问题描述】:我有以下 C 代码:
#include <stdio.h>
int function(int a, int b)
int res = a + b;
return res;
int main()
function(1,2);
exit(0);
我使用 gcc 4.8.2(在 Ubuntu 14 下)为 x86-64 编译它并生成以下代码:
000000000040052d <function>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: 89 7d ec mov %edi,-0x14(%rbp)
400534: 89 75 e8 mov %esi,-0x18(%rbp)
400537: 8b 45 e8 mov -0x18(%rbp),%eax
40053a: 8b 55 ec mov -0x14(%rbp),%edx
40053d: 01 d0 add %edx,%eax
40053f: 89 45 fc mov %eax,-0x4(%rbp)
400542: 8b 45 fc mov -0x4(%rbp),%eax
400545: 5d pop %rbp
400546: c3 retq
有些东西我听不懂。
一开始我们推送rbp并将rsp保存在rbp中。然后在上面 然后堆栈(并且在 %rbp)我们已经保存了 rbp。那么 rbp 下面的所有内容都是空闲空间。
然后我们将来自 edi 和 esi 的传递参数放置在 -0x14(%rbp) 及以下位置。
但是为什么我们不能把它们直接放在 rbp/rsp 指向的下方呢? edi 和 esi 有 4 个字节长,那为什么不是 -0x8(%rbp) 和 -0xc(%rbp) 呢?它与内存对齐有关吗?
为什么会有一个奇怪的保存 eax 来堆叠并在返回之前读取它?
【问题讨论】:
【参考方案1】:首先,请注意您正在查看未优化的编译器输出。在关闭优化的情况下,编译器输出通常看起来有点愚蠢,因为编译器将 C 的每一行字面意思转换为等效的程序集运行,甚至不费心进行最简单、最明显的优化。
对于您的第一个问题,答案是“因为这是您的编译器决定变量应该去的地方”。没有更好的答案 - 编译器的堆栈分配方案差异很大。例如,我机器上的 Clang 改为输出:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %esi
addl -8(%rbp), %esi
movl %esi, -12(%rbp)
movl -12(%rbp), %eax
popq %rbp
retq
您可以清楚地看到a
存储在-4,b
存储在-8,result
存储在-12。这比你的 GCC 给你的包装更紧凑,但这只是 GCC 的一个怪癖,仅此而已。
对于第二个问题,让我们看看指令如何映射到 C:
标准函数序言(设置栈帧):
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
将两个参数存储到堆栈变量a
和b
:
400531: 89 7d ec mov %edi,-0x14(%rbp)
400534: 89 75 e8 mov %esi,-0x18(%rbp)
为a + b
加载a
和b
400537: 8b 45 e8 mov -0x18(%rbp),%eax
40053a: 8b 55 ec mov -0x14(%rbp),%edx
其实做a + b
40053d: 01 d0 add %edx,%eax
设置result = (result of a+b)
40053f: 89 45 fc mov %eax,-0x4(%rbp)
复制result
到返回值(return result;
)
400542: 8b 45 fc mov -0x4(%rbp),%eax
实际返回:
400545: 5d pop %rbp
400546: c3 retq
所以你可以看到eax
的冗余保存和加载只是因为保存和加载对应于你原始C文件的不同语句:保存来自result =
,加载来自return result;
。
为了比较,这里是 Clang 的优化输出 (-O
):
pushq %rbp
movq %rsp, %rbp
addl %esi, %edi
movl %edi, %eax
popq %rbp
retq
更智能:没有堆栈操作,整个函数体只有两条指令addl
和movl
。 (当然,如果您声明函数 static
,那么 GCC 和 Clang 都会很高兴地检测到该函数从未被有效使用并直接将其删除。
【讨论】:
以上是关于x86_64 ABI:反汇编问题的主要内容,如果未能解决你的问题,请参考以下文章