1

Just a couple of curiosity questions on this very simple function that adds two numbers together. Here is the anotated disassembly (questions are in code marked with ???)

Here is the C function

int Add(int x , int y)
{
    int additionAnswer = 0 ;
    additionAnswer = x + y;
    return additionAnswer;
}

Here is the disassembly (DEBUG BUILD) with my anotations and questions

int Add(int x , int y)
{
  push        ebp        ; preserve base pointer 
  mov         ebp,esp    ; move base pointer to start of stack frame for this function
  sub         esp,0CCh   ; ??? is this preserving  space on the stack for local
                         ;  variable..204bytes seems execessive!!!
  push        ebx  //??? Why is this preserving ebx, esi and edi
  push        esi  //??? when clearly this function does not use it!
  push        edi  
  lea         edi,[ebp-0CCh]   ; ??? Why is it loading the address of top of stack into edi?
  mov         ecx,33h          ; ??? What is that doing
  mov         eax,0CCCCCCCCh   ; ??? What is that doing 
  rep stos    dword ptr es:[edi]   ; ??? What is that doing         
  mov         dword ptr [additionAnswer],0  ;int additionAnswer = 0 
  mov         eax,dword ptr [x]  ;eax = x
  add         eax,dword ptr [y]  ;eax = eax + y
  mov         dword ptr [additionAnswer],eax   ;answer = eax
  mov         eax,dword ptr [additionAnswer]   ; return addition in eax
  pop         edi  ;restore edi even though I didn't use it !!!!
  pop         esi  ;restore esi even though I didn't use it !!!!
  pop         ebx  ;restore ebx even though I didn't use it !!!!
  mov         esp,ebp  ; clean up stack frame and restore sp to 
                        ;4 bytes above it's original pre-frame value
  pop         ebp  ;restore base pointer back to it's original value 
                   ;and at same time this will add 4 to sp hence restoring it 
                   ;back to its former pre-frame value, and pointing to return address on stack
  ret  

I dug around a bit and apparently in Win32, the following must be preserved: edi, esi, ebp and ebx. I can understand source/destination index registers (esi /edi) might be used by the calling function , but why doesn't the calling function preserve EBX itself rather than my function doing the unnecessary donkey work, surely the caller knows what it needs to preserve and what it does not! Finally why preserve EBX and not say ECX, again why is it the responsibility of my function to preserve these registers (or even any register at all!).

one last thing I don't see any of these register preservation code when building in release mode (without optimizations)......is this a debug only concept????

Ted
  • 345
  • 5
  • 18
  • 1
    And you compiled with what options? Looks like debug mode, where unused memory is filled with 0xCC to help you catch uses of uninitialized memory. If you're going to evaluate efficiency of the instruction generation, you should compile with all debugging options turned off and all optimizations turned on. (Release mode) – Dark Falcon Apr 21 '14 at 18:34
  • This looks like it was compiled with debug options. Try under release build. – David Heffernan Apr 21 '14 at 18:34
  • Yes the clue was in the last sentence, I did state the register preservation code is not present in release mode, ergo the code above contains register preservation so cannot be release code. I will edit the question. – Ted Apr 21 '14 at 20:47
  • @Dark Falcon. Thanks for the answer. I am not trying to measure efficiency but rather trying to understand intent here. When you say " unused memory is filled with 0xCC to help you catch uses of uninitialized memory" do you mean this line of code "mov eax,0CCCCCCCCh". If so I am not sure how moving a constant into eax does what you say, can you give me any pointers to sites to read more on this..... – Ted Apr 21 '14 at 21:01
  • haha found it! One of the answers to this question http://stackoverflow.com/questions/370195/when-and-why-will-an-os-initialise-memory-to-0xcd-0xdd-etc-on-malloc-free-new states "the opcode 0xCC is the int3 opcode, which is the software breakpoint interrupt. So, if you ever try to execute code in uninitialized memory that's been filled with that fill value, you'll immediately hit a breakpoint, and the operating system will let you attach a debugger (or kill the process)."....it's all coming together nicely – Ted Apr 21 '14 at 21:19
  • Regarding `eax`, go look up what `rep stos` does. (Hint: it uses `eax` and `ecx`, and since it was specified in that instruction, `edi` also.) – Dark Falcon Apr 21 '14 at 21:32
  • Thanks Dark Falcon I answered it below! – Ted May 07 '14 at 21:35

2 Answers2

1

To answer your question

why doesn't the calling function preserve it itself rather than my function doing the unnecessary donkey work, surely the caller knows what it needs to preserve and what it does not!

Because, the calling conventions specify that a function can assume that functions it calls will not modify edi, esi, ebp, and ebx. Many times a caller will also care about values in other registers (eax, ecx, and edx), but it knows that they might be modified, so they need to be saved by the caller if it cares.

Sometimes it is simpler to have the caller preserve things and sometimes it is simpler for the callee to do it. Since calling conventions need to be understood by (and followed by) many different compilers for different languages (in order to allow those compilers to work together), the standard calling conventions give you a mix of caller save and callee save and any compiler can makes its own (indpendent) choices of which kind of register to use for various values, so as to minimize the amount of work done.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Understanding intent is key for me. I understand it is convention but whilst I agree it makes sense for my function to preserve the base and stack pointer in order to maintain stack frame structure consistency, state and integrity, the same cannot be said of the general purpose registers EAx, EBX, ECX, EDI and ESI. Surely the usage of these registers is known by the calling function and it should preserve what it needs and leave what it does not, this is a much cleaner model and optimises away unnecessary register preservation. Hence my question why preserve EBX ESI and EDI if not needed. – Ted Apr 21 '14 at 20:53
  • 1
    @Ted You have to take a holistic view here: think of a chain of a function call instead of just two. Functions are different in nature: what if you have a leaf function `f(a) {return a*2;}` that you are going to call from more complicated functions that use many registers? If you set your calling convention to "caller saves everything", you'll have to push many registers you are currently using every time you call `f` from your complex function and pop them back afterward even though `f` does not overwrite them, so you have done a lot of unnecessary work in the caller... – Mehrdad Afshari Apr 21 '14 at 21:37
  • 1
    ...There is a trade-off here: the disadvantage of having caller save everything is that it'll increase the overhead of calling small functions, effectively making a couple instructions function impractical, and increases code size in the caller (which may lead to instruction cache misses). It makes sense to give some flexibility to the compiler/programmer in the calling convention. – Mehrdad Afshari Apr 21 '14 at 21:41
  • @Leaky that makes sense! so what you are saying if I imagined (arguments sake) there existed a convention whereby calling functions HAD to preserve all registers (except EBP and ESP) and let's take a scenario where for example the calling function (for some dumb reason) was going to call my simple function 5 times, then it would be forced to preserve the registers 5 times when it clearly doesn't need to! So hence as a balance, everyone has agreed that the my function will preserve EBX, ESI and EDI instead. Thanks for that, it makes sense now! – Ted Apr 21 '14 at 21:57
  • P.S I don't know why I thought EBX might have some special usage much like EAX is used to return values, but maybe I am wrong.... – Ted Apr 21 '14 at 21:57
0

Lots of good answers in comments above and after another night of searching I found out what the following code does

  lea         edi,[ebp-0CCh]         ;set edi to point to top of stack
  mov         ecx,33h                ;  set ecx  =  51 
  mov         eax,0CCCCCCCCh   
  rep stos    dword ptr es:[edi]   ; like memset - this line fills a specified amount (ecx, 
                                    which this case is 51 (33h) bytes) of memory (at [edi]) 
                                    with a given value (in eax which in  this case is 
                                    0CCCCCCCCh).  

Here is an explanation of rep stos What does the "rep stos" x86 assembly instruction sequence do?

0xCCCCCCCC is used by Microsoft's C++ debugging runtime library and many DOS environments to mark uninitialized stack memory. 0xCCCCCCCC is used because it resembles (but NOT the same as!) 0xCC which is the opcode of the INT 3 debug breakpoint interrupt on x86 processors. So, if you ever try to execute code 0xCC you'll immediately hit a breakpoint (see How do debuggers guarantee correctness when using INT 3 (0xCC) software breakpoint even though an instruction was patched?) . However if you try executing 0xCCCCCCCC it will most likely just crash your program!

Here is a great explanation from Hans... Why is the stack filled with 0xCCCCCCCC

So the only line left unexplained is the following

sub         esp,0CCh ;why is is it preserving 204 bytes (0CCh) on the stack? For edit/continue?

Thanks for all your help!

Community
  • 1
  • 1
Ted
  • 345
  • 5
  • 18