我正在 Visual Studio 2010 中调试一个简单的 x86-64 程序,我注意到main
函数前导与同一 C 程序的 GNU GCC 编译版本不同。
为了说明我的意思,这里是main
函数的 C 代码:
int main() {
int a,b,c;
a=1;
b=2;
c=proc(a,b);
return c;
}
Visual Studio 2010 反汇编VC++ 版本的main
函数序言是:
01211410 push ebp
01211411 mov ebp,esp
01211413 sub esp,0E4h
01211419 push ebx
0121141A push esi
0121141B push edi
0121141C lea edi,[ebp-0E4h]
01211422 mov ecx,39h
01211427 mov eax,0CCCCCCCCh
0121142C rep stos dword ptr es:[edi]
VC++版本的其余功能代码为:
0081141E mov dword ptr [a],1
00811425 mov dword ptr [b],2
0081142C mov eax,dword ptr [b]
0081142F push eax
00811430 mov ecx,dword ptr [a]
00811433 push ecx
00811434 call proc (81114Fh)
00811439 add esp,8
0081143C mov dword ptr [c],eax
0081143F mov eax,dword ptr [c]
00811442 pop edi
00811443 pop esi
00811444 pop ebx
00811445 add esp,0E4h
0081144B cmp ebp,esp
0081144D call @ILT+300(__RTC_CheckEsp) (811131h)
00811452 mov esp,ebp
00811454 pop ebp
00811455 ret
GCC编译版本的main
函数序言反汇编为:
00400502 push rbp
00400503 mov rbp,rsp
00400506 sub rsp,0x10
main
GCC版本的其余功能代码为:
004004e8 mov DWORD PTR [rbp-0xc],0x1
004004ef mov DWORD PTR [rbp-0x8],0x2
004004f6 mov edx,DWORD PTR [rbp-0x8]
004004f9 mov eax,DWORD PTR [rbp-0xc]
004004fc mov esi,edx
004004fe mov edi,eax
00400500 call 0x4004cc <proc>
00400505 mov DWORD PTR [rbp-0x4],eax
00400508 mov eax,DWORD PTR [rbp-0x4]
0040050b leave
0040050c ret
objdump
版本 2.22.90.20120924给出了相同的反汇编。
我意识到两个序言的前 3 条指令执行以下操作:
- 保存旧的 EBP(稍后需要删除堆栈帧)
- 旧堆栈帧的顶部成为新帧的 EBP
- 为局部变量保留空间。该函数有 3 个整型局部变量。
问题1: VC++版本中第4条指令的目的是什么?我看到它节省了 EBX,但为什么呢?之后它再也不会使用它了。
对于 VC++ 版本序言的其余指令,我认为它39h
使用值初始化双字 0CCCCCCCCh
。这是有道理的,因为39h * 4h = 0E4h
.
问题2:为什么这个空间用值初始化0CCCCCCCCh
?这个值比00000000h
在某些方面更好吗?
问题三:为什么VC++版本0E4h
要为3个局部变量分配字节?这个数字是随机的吗?如果不是,它是如何计算的?
问题 4:除了局部变量之外,这个空间还用于其他用途吗?如果是,是为了什么?