Ofe*_*fey 15 c linux gcc x86-64 disassembly
我正在自学,编译器是如何工作的.我正在学习从小型64位Linux程序中读取GCC生成代码的反汇编.
我写了这个C程序:
#include <stdio.h>
int main()
{
for(int i=0;i<10;i++){
int k=0;
}
}
Run Code Online (Sandbox Code Playgroud)
使用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]
Run Code Online (Sandbox Code Playgroud)
现在我有些疑惑.
那个NOP到底是什么,为什么会出现?(对准?)
我正在编译gcc -Wall <program.c>
.为什么我没有得到警告 control reaches end of non-void function
?
为什么编译器不在堆栈上分配空间sub rsp,0x10
?为什么不使用rbp
寄存器来引用本地堆栈数据?
PS:如果我printf
在for
循环中调用函数(如),为什么编译器会突然生成sub rsp,0x10
?为什么它仍然使用rsp
寄存器引用本地数据.我希望生成的代码能够引用本地堆栈数据rbp
!
Som*_*ude 12
关于第二个问题,由于C99标准允许return 0
在main
函数中没有显式,编译器将隐式添加它.请注意,这仅适用于该main
功能,不适用于其他功能.
至于第三个问题,rbp
寄存器充当帧指针.
最后是PS.被调用的函数可能是使用16
bytes(0x10
)来传递给函数的参数.减法是从堆栈中"移除"那些变量的.它可能是你作为参数传递的两个指针吗?
如果您正在认真学习编译器的工作方式,并且可能想要创建自己的编程器(这很有趣!:)),那么我建议您投资一些关于它的理论和实践的书籍.龙书是任何程序员书架的绝佳补充.
ret
无法依赖的任何东西都是代码.解码为nop
"无操作"
第二点是编译器检测到你离开main
函数而没有返回值,它插入一个return 0
(仅定义为main
).
该rbp
寄存器具有bp
"Base Pointer"的含义,指向当前函数的堆栈帧.函数调用通常会导致函数条目保存rbp
并使用rsp
for 的当前值rbp
.获取/存储函数参数和局部变量是相对于rbp
.
sub rsp,0x10
?为什么它不使用rbp寄存器来引用本地堆栈数据? "
实际上,编译器会在堆栈上分配空间.但它并没有改变stackpointer.它可以做到这一点,因为功能不会调用其他功能.它只是使用了curent下方的空间sp
(堆栈向下增长),它用于rbp
访问i
([rbp-0x8]
)和k
([rbp-0x4]
).
sp
局部变量的使用似乎不是中断安全,因此编译器依赖于硬件在发生中断时自动切换到系统堆栈.否则,出现的第一个中断会将指令指针推入堆栈并覆盖本地变量.
在 不调整RSP的情况下使用局部变量在编译器中解决了中断问题
是的,nop用于对齐.编译器对所需的不同填充长度使用不同的指令,因为他们知道现代CPU将预先取出并解码几条指令.
正如其他人所说,如果没有明确的return语句(参见C99 TC3中的 5.1.2.2.3 ),C99标准默认从main()返回0 ,因此不会引发警告.
在64位系统V的Linux ABI保留该叶功能(即不调用任何其他函数的函数-和你的main()就是这样的)当前堆栈指针低于128字节的"红番区"可以使用局部变量和其他临时值而不必使用sub rsp/add rsp.所以rbp == rsp.
而对于PS:当您在for()循环中调用函数(或main()中的任何位置)时,main()不再是叶函数,因此编译器不能再使用红色区域.这就是为什么它使用sub rsp,0x10在堆栈上分配空间.但是,它知道rsp和rbp之间的关系,所以它可以在访问数据时使用.