我正在自学编译器的工作原理。我通过阅读 GCC 从小型 64 位 Linux 程序生成的代码的反汇编来学习。
我写了这个 C 程序:
#include <stdio.h>
int main()
{
for(int i=0;i<10;i++){
int k=0;
}
}
使用 objdump 后我得到:
00000000004004d6 <main>:
4004d6: 55 push rbp
4004d7: 48 89 e5 mov rbp,rsp
4004da: c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0
4004e1: eb 0b jmp 4004ee <main+0x18>
4004e3: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
4004ea: 83 45 f8 01 add DWORD PTR [rbp-0x8],0x1
4004ee: 83 7d f8 09 cmp DWORD PTR [rbp-0x8],0x9
4004f2: 7e ef jle 4004e3 <main+0xd>
4004f4: b8 00 00 00 00 mov eax,0x0
4004f9: 5d pop rbp
4004fa: c3 ret
4004fb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
现在我有些疑惑。
最后的 NOP 是做什么用的,为什么会有它? (对齐?)
我正在使用
gcc -Wall <program.c>
进行编译.为什么我没有收到警告control reaches end of non-void function
?为什么编译器不使用
sub rsp,0x10
在堆栈上分配空间? ?为什么不使用rbp
注册以引用本地堆栈数据?PS:如果我在
printf
中调用函数(如for
)循环,为什么编译器突然生成sub rsp,0x10
?为什么它仍然使用rsp
引用本地数据登记。我希望生成的代码使用rbp
引用本地堆栈数据!
最佳答案
关于第二个问题,由于C99标准允许main
函数中没有明确的return 0
,编译器会隐式添加。请注意,这仅针对 main
函数,没有其他函数。
至于第三个问题,rbp
寄存器充当frame pointer .
最后是PS。很可能被调用的函数使用 16
字节 (0x10
) 作为传递给函数的参数。减法是从堆栈中“删除”那些变量。它可能是您作为参数传递的两个指针吗?
如果您认真学习编译器的一般工作原理,并且可能想创建自己的编译器(这很有趣!:)),那么我建议您购买一些有关它的理论和实践的书籍。 The dragon book是任何程序员书架的绝佳补充。
关于编译器 : Understanding assembly code generated from small programs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42994231/