6

I was debugging a simple x86-64 program in Visual Studio 2010 and I noticed that the main function preamble is different from the GNU GCC compiled version of the same C program.

To illustrate what I mean here is the C code for the main function:

int main() {
  int a,b,c;
  a=1;
  b=2;
  c=proc(a,b);
  return c;
}

The Visual Studio 2010 disassembly of the main function preamble for the VC++ version is:

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]

The rest of the function code of the VC++ version is:

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 

The disassembly of the main function preamble for the GCC compiled version is:

00400502  push   rbp
00400503  mov    rbp,rsp
00400506  sub    rsp,0x10

The rest of the main function code of the GCC version is:

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

The same disassembly is given by objdump version 2.22.90.20120924.

I realize that the first 3 instructions for both preambles do the following:

  1. Save old EBP (later needed to remove stack frame)
  2. Top of old stack frame becomes EBP of new frame
  3. Reserve space for local variables. The function has 3 integer local variables.

Question 1: What is the purpose of 4th instruction in the VC++ version? I see its saving EBX, but why? It never uses it afterwards.

For the remaining instructions of the VC++ version preamble, I relized that it initializes 39h dwords with the value 0CCCCCCCCh. Which makes sense because 39h * 4h = 0E4h.

Question 2: Why is this space initialized with the value 0CCCCCCCCh? Is this value better than 00000000h in some way?

Question 3: Why does the VC++ version allocate 0E4h bytes for 3 local variables? Is this number random? If not, how is it computed?

Question 4: Is this space used for something else beside local variables? If yes, for what?

  • I suggest you post separate questions, one for each of your subquestions above. And for each question, post the function's source code and the function's full disassembly. This will make it easier to respond with confidence instead of guessing. – Jason Geffner Mar 26 '14 at 21:09
  • @JasonGeffner I posted the C source of the function. It is extremely simple. I think the 4 sub-questions are quite related. I don't see the point in posting 4 different questions with the same initial description and also very similar title. – Benny Mar 26 '14 at 21:31
  • There's definitely some interesting "optimization" going on there... for example, notice that the disassembled VC++ code will always return 0, not the value of c... – Jason Geffner Mar 26 '14 at 22:24
  • MSVC initializes uninitialized memories with 0xCC in debug mode. This helps debugging some problem much easier – phuclv Jun 16 '14 at 13:10
  • You should change the MSVC project to x64. Compare MSVC 32-bit code and GCC 64-bit code is pointless – phuclv Jun 19 '14 at 3:59
6

The extra space on the stack is there to support the Edit and Continue functionality and can be eliminated by changing /Zl to /Zi. The saved ebx and initialization of the stack to 0xcc is done by the /RTC Runtime Checking Option.

There was a similar question asked on SO.

The windows example, by the way, is clearly a 32 bit binary. x64 windows calling convention uses RCX, RDX, R8, and R9 as the first 4 integer/pointer arguments (instead of the stack).

  • Yes. In x86_64 there's no way to push ebp. The OP should change the project to x86_64 and see – phuclv Jun 16 '14 at 13:12
2
  1. Not sure, perhaps it's 'standard' so that the compiler can use ebx as a matter of course, (without needing to check if it can).
  2. As you should know, 0x0CC is int 3, a debug breakpoint. It might be that if you suddenly jump to uninitialized memory, you will get an int 3 instead of an A/V or similar exception. Just a guess though. One thing that wasn't mentioned was the compiler flags. Was it debug build?
  3. Not sure
  4. Not sure, try turning off the stack guards and see if you still get the big 0x0E4 allocation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.