我正在自学,编译器是如何工作的。我正在学习从小型64位Linux程序中读取 GCC 生成的代码的反汇编。
我写了这个 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
引用本地堆栈数据!
答案 0 :(得分:12)
关于第二个问题,由于C99标准允许sigmoid_cross_entropy_with_logits
函数中没有明确的return 0
,编译器将隐式添加它。请注意,这仅适用于main
函数,不包含其他函数。
关于第三个问题,main
注册表充当frame pointer。
最后是PS。被调用的函数可能正在使用rbp
字节(16
)来传递给函数的参数。减法是&#34;删除&#34;来自堆栈的那些变量。它可能是你作为参数传递的两个指针吗?
如果您认真学习编译器的工作原理,并且可能想创建自己的编程器(很有趣!:),那么我建议您投资一些关于理论和实践的书籍。它。 The dragon book是任何程序员书架的绝佳补充。
答案 1 :(得分:6)
ret
之后的任何内容都不能成为代码。解码为nop
表示&#34;没有操作&#34;
第二点是编译器检测到你离开main
函数而没有返回值,它插入return 0
(仅为main
定义)。
rbp
寄存器,bp
含义&#34; Base Pointer&#34;,指向当前函数的堆栈帧。函数调用通常会导致函数条目保存rbp
并使用rsp
的当前值rbp
。获取/存储函数参数和局部变量是相对于rbp
完成的。
sub rsp,0x10
的堆栈上分配空间?为什么它不使用rbp寄存器来引用本地堆栈数据?&#34;
实际上,编译器会在堆栈上分配空间。但它并没有改变stackpointer。它可以做到这一点,因为功能不会调用其他功能。它只使用了当前sp
下方的空间(堆栈向下增长),并使用rbp
来访问i
([rbp-0x8]
)和k
({{1 }})。
[rbp-0x4]
使用局部变量似乎不是中断安全,因此编译器依赖于硬件在发生中断时自动切换到系统堆栈。否则,出现的第一个中断会将指令指针推入堆栈并覆盖局部变量。解决了中断问题
答案 2 :(得分:6)
是的,nop用于对齐。编译器对不同长度的填充使用不同的指令,因为他们知道现代CPU将预先取出并解码几条指令。
正如其他人所说,如果没有明确的return语句(参见C99 TC3中的5.1.2.2.3),C99标准默认从main()返回0,因此不会发出警告。 / p>
64-bit System V Linux ABI在当前堆栈指针下面保留一个128字节的“红色区域”,叶子函数(不调用任何其他函数的函数 - 而你的main()就是这样)可以使用对于局部变量和其他临时值而不必使用sub rsp / add rsp。所以rbp == rsp。
对于PS:当你在for()循环(或main()中的任何地方)调用函数时,main()不再是叶函数,因此编译器不能再使用红色区域。这就是为什么它使用sub rsp,0x10在堆栈上分配空间。但是,它知道rsp和rbp之间的关系,所以它可以在访问数据时使用。