Wad
Wad

Reputation: 1534

Stack must be clean before function epilogue confusion

I'm studying assembly language from the book "Assembly Language Step-by-Step: Programming with Linux" by Jeff Dunteman, and have come across an interesting paragraph in the book which I'm most likely misunderstanding, hence would appreciate some clarification on:

"The stack must be clean before we destroy the stack frame and return control. This simply means that any temporary values that we may have pushed onto the stack during the program’s run must be gone. All that is left on the stack should be the caller’s EBP, EBX, ESI, and EDI values.

...

Once the stack is clean, to destroy the stack frame we must first pop the caller’s register values back into their registers, ensuring that the pops are in the correct order.

...

We restore the caller’s ESP by moving the value from EBP into ESP, and finally pop the caller’s EBP value off the stack."

Consider the following code generated from Visual Studio 2008:

int myFuncSum( int a, int b)
{
001B1020  push        ebp  
001B1021  mov         ebp,esp 
001B1023  push        ecx       <------------------
    int c;
    c = a + b;
001B1024  mov         eax,dword ptr [ebp+8] 
001B1027  add         eax,dword ptr [ebp+0Ch] 
001B102A  mov         dword ptr [ebp-4],eax 
    return c;
001B102D  mov         eax,dword ptr [ebp-4] 
}
001B1030  mov         esp,ebp 
001B1032  pop         ebp  
001B1033  ret     

The value of ecx (indicated), pushed to make space on the stack for my variable c, is, as far as I can see, only gone from the stack when we reset ESP; however, as quoted, the book states that the stack must be clean before we reset ESP. Can someone please clarify whether or not I am missing something?

Upvotes: 2

Views: 233

Answers (2)

lurker
lurker

Reputation: 58244

The example from Visual Studio 2008 doesn't contradict the book. The book is covering the most elaborate case of a call. See the x86-32 Calling Convention as a cross-reference which spells it out with pictures.

In your example, there were no caller registers saved on the stack, so there are no pop instructions to be performed. This is part of the "clean up" that must occur before mov esp, ebp that the book is referring to. So more specifically, let's suppose the callee is saving si and di for the caller, then the prelude and postlude for the function might look like this:

push  ebp        ; save base pointer
mov   ebp, esp   ; setup stack frame in base pointer
sub   esp, 4     ; reserve 4 bytes of local data
push  si         ; save caller's registers
push  di

; do some stuff, reference 32-bit local variable -4(%ebp), etc
;   Use si and di for our own purposes...

; clean up

pop   di          ; do the stack clean up
pop   si          ;   restoring the caller's values
mov   esp, ebp    ; restore the stack pointer
pop   ebp
ret

In your simple example, there were no saved caller registers, so no final pop instructions needed at the end.

Perhaps because it's simpler or faster, the compiler elected to do the following instruction in place of sub esp, 4:

push  ecx

But the effect is the same: reserve 4 bytes for a local variable.

Upvotes: 1

Christophe
Christophe

Reputation: 73376

Notice the instruction:

push        ebp  
mov         ebp,esp   ; <<<<=== saves the stack base pointer

and the instruction:

mov         esp,ebp   ;  <<<<<== restore the stack base pointer 
pop         ebp  

So after this sequence the stack is clean again

Upvotes: 0

Related Questions