14

I just noticed some strange assembly language code of empty main method.

//filename: main.c
void main()
{

}

disassembly:

push        ebp  
mov         ebp,esp  
sub         esp,0C0h; why on the earth is it reserving 192 bytes?  
push        ebx  
push        esi  
push        edi  ; good compiler. Its saving ebx, esi & edi values.
lea         edi,[ebp-0C0h]  ; line 1
mov         ecx,30h  ; line 2
mov         eax,0CCCCCCCCh  ; line 3
rep stos    dword ptr es:[edi]  ; line 4


xor         eax,eax  ; returning value 0. Code following this line is explanatory.
pop         edi  ; restoring the original states of edi,esi & ebx
pop         esi  
pop         ebx  
mov         esp,ebp  
pop         ebp  
ret   
  1. why on the earth is it reserving 192 bytes for function where there aren't any variables
  2. whats up with the four lines: line 1, line 2, line 3, line 4? what is it trying to do & WHY?
claws
  • 52,236
  • 58
  • 146
  • 195
  • why isn't the code syntax highlighted? – claws Jul 29 '10 at 13:25
  • What assembly is produced when you a assemble a non-empty main statement, and what happens when you change the return type of main to int? – Joe D Jul 29 '10 at 13:29
  • 5
    It's worth pointing out that main returns int. Using void for main is not good juju. – nmichaels Jul 29 '10 at 13:30
  • Also, the fact that you return `void' results in undefined behaviour if I remember correctly. Which means the compiler can produce whatever assembly it wants. It doesn't even have to compile your code, for all the standard cares your compiler can sit in a corner and sulk. – Joe D Jul 29 '10 at 13:32
  • 2
    @Joe D: Fortunately, compilers are generally not depressed robots with a brain the size of a planet. – Greg Hewgill Jul 29 '10 at 14:15
  • Also `void main(void)` and `void(int, char **)` are valid. – Matt Joiner Jul 31 '10 at 13:20
  • @Matt: No there not, main **MUST** return int, unless your compiling for a freestanding environment. So, if you are going to return something other than int, you musn't depend on any of the standard library headers except for limits.h, float.h, stdint.h, stdbool.h, stddef.h, stdarg.h and iso646.h. – Joe D Aug 01 '10 at 16:11
  • @Joe D: A few minutes reading the standard and I see you are correct. Nevertheless, some platforms (embedded etc.) deviate from this norm. – Matt Joiner Aug 01 '10 at 16:27
  • 1
    That is usually the definition of a freestanding implementation. Freestanding implementations are allowed to, but have no requirement to, provide a full c library. On freestanding implementations __STDC_HOSTED__ is defined to 0, whereas on hosted it's defined to 1. – Joe D Aug 01 '10 at 17:08
  • I meant to type `__STDC_HOSTED__`, (underscores on either side) but stackoverflow interpreted it as marking it is as bold. – Joe D Aug 02 '10 at 13:45

2 Answers2

18

Greg already explained how the compiler generates code to diagnose uninitialized local variables, enabled by the /RTCu compile option. The 0xcccccccc value was chosen to be distinctive and easily recognized in the debugger. And to ensure the program bombs when an uninitialized pointer is dereferenced. And to ensure it terminates the program when it is executed as code. 0xcc is pretty ideal to do all of these jobs well, it is the instruction opcode for INT3.

The mysterious 192 bytes that are allocated in the stack frame are there to support the Edit + Continue feature, /ZI compile option. It allows you to edit the code while a breakpoint is active. And add local variables to a function those 192 bytes are available to provide the space for those added locals. Exceeding that space will make the IDE force you to rebuild your program.

Btw: this can cause a problem if you use recursion in your code. The debug build will bomb with this site's name a lot quicker. Not normally much of an issue, you debug with practical dataset sizes.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
15

The four code lines you've indicated are the debug build clearing out the local variable space with the "clear" special value (0xCCCCCCCC).

I'm not sure why there are 192 bytes of seemingly dead space, but that might be VC++ building some guard space into your local variable area to try to detect stack smashing.

You will probably get a very different output if you switch from Debug to Release build.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 1
    why `0xCCCCCCCC`=`1100 1100 1100 1100 1100 1100 1100 1100`? wouldn't clearing out with 0x00000000 would be more meaningful? – claws Jul 29 '10 at 13:39
  • @claws: Microsoft chose that value because it's distinct and easy to identify if your program ends up using an uninitialised variable. Using zero would mask a lot of potential bugs because often variables would be initialised to zero anyway, and having the compiler do so in a debug build would cause the release build to fail. – Greg Hewgill Jul 29 '10 at 13:42
  • @Greg Hewgill: How to view disassembly in Release configuration in Visual Studio? I was putting a breakpoint at the closing bracket and viewing disassembly. This doesn't seem to work in release mode. – claws Jul 29 '10 at 14:06
  • @claws: You can enable debug info (so you can see the disassembly) while still leaving all the other settings as they are for Release mode. I'm not sure exactly where the knob is on your version of VS, but it's on the project properties page somewhere. – Greg Hewgill Jul 29 '10 at 14:10
  • @Greg Hewgill: Are you referring to "Program Database for Edit And Continue (/ZI)" option for "Program Database Format"?Because thats already enabled. Actually, In release mode. I'm able to set breakpoints and its even showing the disassembly looks like this: http://pastebin.com/raw.php?i=YnjjgnnL . Why is it so? – claws Jul 29 '10 at 14:42
  • 1
    @claws: Your pastebin is precisely what I would expect for a release mode build of an empty function that returns 0. As I said, you'll get very different output when the compiler doesn't emit any code that's unnecessary. – Greg Hewgill Jul 29 '10 at 20:42
  • 1
    @Greg Hewgill: Ah yes, I remember it well. In fact, once this uninitialised pattern caused us a huge problem when a bug was reported by a customer that only occurred in the release build, we could not reproduce it at all. It turned out that we were using a boolean uninitialised. In the release build it was often zero, but in the debug build it never was. It's a far better idea to turn on the warning about use of uninitialised variables. – JeremyP Jul 30 '10 at 09:39
  • 1
    The term for the "special value" is memory poison. – Matt Joiner Jul 31 '10 at 13:20