flat assembler
Message board for the users of flat assembler.

Index > Main > Noobish question about LEAVE

Author
Thread Post new topic Reply to topic
randomdude



Joined: 01 Jun 2012
Posts: 83
randomdude
I have noticed in one application that most functions either works like this:

Code:
push    ebp
mov     ebp, esp
sub     esp, n
...
add     esp, n
pop     ebp
    


Or:

Code:
push    ebp
mov     ebp, esp
sub     esp, n
...
leave
    


But i can't get it, whats the exact difference between both? Question
Post 11 Nov 2012, 17:58
View user's profile Send private message Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu
Just use an instruction reference like the intel docs.
"leave" sets esp to ebp, cleaning up all local bytes, and pops ebp, removing the current stack frame and restoring the old one.
It's a high-level instruction which essentially does "mov esp,ebp | pop ebp", however it's shorter and faster than those two instructions.

Where did you get that "add esp,n"? It obviously does the same as "mov esp,ebp" in your code snippet, but it's 1 to 4 bytes bigger, so it's only useful to decrement esp by the same value as it got incremented before if there is no stack frame set up (no "push ebp | mov ebp,esp").
Post 11 Nov 2012, 18:25
View user's profile Send private message Reply with quote
randomdude



Joined: 01 Jun 2012
Posts: 83
randomdude
Thanks for the fast answer. I'm just trying to rewrite some functions, but i have some problems to understand how to balance the stack frame. I want to replace those functions which have the 'add esp, n' with 'leave' but not sure if that can screw up something?

This is one of the functions which uses 'leave' (it's fine, i understand everything):

Code:
var_18          = dword ptr -18h
var_14          = dword ptr -14h
var_8           = dword ptr -8
var_1           = byte ptr -1
arg_0           = dword ptr  8
arg_4           = dword ptr  0Ch

push    ebp
mov     ebp, esp
sub     esp, 18h
mov     eax, [ebp+arg_0]
mov     [ebp+var_18], eax
mov     eax, [ebp+arg_4]
mov     [ebp+var_14], eax
lea     eax, [ebp+var_18]
mov     [ebp+var_8], eax
mov     eax, [ebp+var_8]
movzx   eax, byte ptr [eax]
mov     [ebp+var_1], al
mov     eax, [ebp+var_8]
add     eax, 7
movzx   edx, byte ptr [eax]
mov     eax, [ebp+var_8]
mov     [eax], dl
mov     edx, [ebp+var_8]
add     edx, 7
movzx   eax, [ebp+var_1]
mov     [edx], al
mov     eax, [ebp+var_8]
add     eax, 1
movzx   eax, byte ptr [eax]
mov     [ebp+var_1], al
mov     edx, [ebp+var_8]
add     edx, 1
mov     eax, [ebp+var_8]
add     eax, 6
movzx   eax, byte ptr [eax]
mov     [edx], al
mov     edx, [ebp+var_8]
add     edx, 6
movzx   eax, [ebp+var_1]
mov     [edx], al
mov     eax, [ebp+var_8]
add     eax, 2
movzx   eax, byte ptr [eax]
mov     [ebp+var_1], al
mov     edx, [ebp+var_8]
add     edx, 2
mov     eax, [ebp+var_8]
add     eax, 5
movzx   eax, byte ptr [eax]
mov     [edx], al
mov     edx, [ebp+var_8]
add     edx, 5
movzx   eax, [ebp+var_1]
mov     [edx], al
mov     eax, [ebp+var_8]
add     eax, 3
movzx   eax, byte ptr [eax]
mov     [ebp+var_1], al
mov     edx, [ebp+var_8]
add     edx, 3
mov     eax, [ebp+var_8]
add     eax, 4
movzx   eax, byte ptr [eax]
mov     [edx], al
mov     edx, [ebp+var_8]
add     edx, 4
movzx   eax, [ebp+var_1]
mov     [edx], al
mov     eax, [ebp+var_18]
mov     edx, [ebp+var_14]
leave
retn
    


And this is one of those weird functions which uses add esp, n:

Code:
var_C           = dword ptr -0Ch
var_8           = byte ptr -8
arg_0           = dword ptr  8
arg_4           = dword ptr  0Ch

push    ebp
mov     ebp, esp
push    ebx
sub     esp, 8
mov     eax, [ebp+arg_4]
mov     [ebp+var_8], al
cmp     [ebp+var_8], 0
jz      short loc_1
mov     eax, [ebp+arg_0]
mov     dword ptr [eax+8F8h], 0
loc_1:
mov     eax, [ebp+arg_0]
mov     ecx, [eax+8F8h]
mov     eax, [ebp+arg_0]
mov     edx, [eax+40h]
mov     eax, edx
shl     eax, 2
add     eax, edx
shl     eax, 2
add     eax, 1770h
lea     edx, [ecx+eax]
mov     eax, [ebp+arg_0]
mov     [eax+8F8h], edx
mov     eax, [ebp+arg_0]
mov     ebx, [eax+8F8h]
mov     eax, [ebp+arg_0]
mov     edx, [eax+8F8h]
mov     eax, [ebp+arg_0]
mov     eax, [eax+950h]
mov     ecx, edx
imul    ecx, eax
mov     [ebp+var_C], 51EB851Fh
mov     eax, [ebp+var_C]
imul    ecx
sar     edx, 5
mov     eax, ecx
sar     eax, 1Fh
mov     ecx, edx
sub     ecx, eax
mov     eax, ecx
lea     edx, [ebx+eax]
mov     eax, [ebp+arg_0]
mov     [eax+8F8h], edx
add     esp, 8
pop     ebx
pop     ebp
retn
    


So i wonder why does it use 'add esp, n' instead of 'leave'?
Post 11 Nov 2012, 18:55
View user's profile Send private message Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu
It uses "add esp,x" because it saves registers first and then reserves space on the stack for local variables.
Notice the "push ebx": it's used to preserve ebx's value. If you'd replace the "add esp,x" with a "leave", it'd also discard such pushed registers, because they're between esp's old value (saved in ebp) and its new value.

However, normally you decrement esp for local variables first and preserve registers afterwards, like:

Code:
push ebp    ; preserve old stack frame
mov ebp,esp ; set up a new one
sub esp,8   ; room for local variables
push ebx    ; save ebx
            ; ...
            ; your function's code
            ; ...
pop ebx     ; restore ebx
leave       ; delete current stack frame and restore the old one
retn xx     ; return and clean up the parameters    


Because then there's no need to clean up the stack with an explicit number of bytes as there are no more needed values between the old esp and the new one.
Now a simple leave (mov esp,ebp) sets esp back to its old value, removing the entire current stack frame.

Also notice, in your second code the first local variable ranges from ebp-8 to ebp-5, in my code example it ranges from ebp-4 to ebp-1 like in your first function.
Post 12 Nov 2012, 03:26
View user's profile Send private message Reply with quote
randomdude



Joined: 01 Jun 2012
Posts: 83
randomdude
OK, i think i got it now, thanks Smile .

So it's just the compiler choice? I noticed it just uses 'leave' on functions that doesn't preserve registers.

I could edit the second function like this:

Code:
var_C           = dword ptr -0Ch 
var_8           = byte ptr -8 
arg_0           = dword ptr  8 
arg_4           = dword ptr  0Ch 

push    ebp 
mov     ebp, esp 
sub     esp, 0Ch 
push    ebx
mov     eax, [ebp+arg_4] 
mov     [ebp+var_8], al 
cmp     [ebp+var_8], 0 
jz      short loc_1 
mov     eax, [ebp+arg_0] 
mov     dword ptr [eax+8F8h], 0 
loc_1: 
mov     eax, [ebp+arg_0] 
mov     ecx, [eax+8F8h] 
mov     eax, [ebp+arg_0] 
mov     edx, [eax+40h] 
mov     eax, edx 
shl     eax, 2 
add     eax, edx 
shl     eax, 2 
add     eax, 1770h 
lea     edx, [ecx+eax] 
mov     eax, [ebp+arg_0] 
mov     [eax+8F8h], edx 
mov     eax, [ebp+arg_0] 
mov     ebx, [eax+8F8h] 
mov     eax, [ebp+arg_0] 
mov     edx, [eax+8F8h] 
mov     eax, [ebp+arg_0] 
mov     eax, [eax+950h] 
mov     ecx, edx 
imul    ecx, eax 
mov     [ebp+var_C], 51EB851Fh 
mov     eax, [ebp+var_C] 
imul    ecx 
sar     edx, 5 
mov     eax, ecx 
sar     eax, 1Fh 
mov     ecx, edx 
sub     ecx, eax 
mov     eax, ecx 
lea     edx, [ebx+eax] 
mov     eax, [ebp+arg_0] 
mov     [eax+8F8h], edx 
pop     ebx 
leave
retn
    


(Also edited sub esp, 8 to 0Ch, is that correct?)

And it should work exactly like before, right?
Post 12 Nov 2012, 10:43
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1419
Location: Toronto, Canada
AsmGuru62
LEAVE may be shorter, but
Intel Optimization Manual recommends to avoid ENTER/LEAVE and
replace them with few simpler instructions.
Post 12 Nov 2012, 11:55
View user's profile Send private message Send e-mail Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu
Well, instead of increasing the locals-bytes, you could have edited the locals-pointers and decrement them by 4:
Code:
var_C           = dword ptr -8
var_8           = byte ptr -4    

this way the stack will look like this upon function entry:
Code:
esp+00|00000000 ; <- pushed ebx
esp+04|22222222 ; <- local2    [ebp-8]
esp+08|11111111 ; <- local1    [ebp-4]
esp+0C|00000000 ; <- old ebp   [ebp points here]
esp+10|00401020 ; <- return-addr
esp+14|11111111 ; <- param1    [ebp+8]
esp+18|22222222 ; <- param2    [ebp+C]    

now, as for the epilogue, you pop ebx, the leave sets esp to ebp, then pops ebp's old value into ebp again and at last the retn 8 pops the return-address into EIP and decrements esp by 8.

Other than those 4 wasted locals-bytes it should work.
PS: Avoid using ENTER. LEAVE, however, is more than fine.
Post 12 Nov 2012, 16:13
View user's profile Send private message Reply with quote
randomdude



Joined: 01 Jun 2012
Posts: 83
randomdude
OK, thanks yoshimitsu. I'll decrease the local variables by 4, the retn will stay 0 as it's a cdecl function Very Happy
Post 12 Nov 2012, 16:40
View user's profile Send private message Reply with quote
Teehee



Joined: 05 Aug 2009
Posts: 568
Location: Brazil
Teehee
Quote:

Intel Optimization Manual recommends to avoid ENTER/LEAVE and
Quote:

PS: Avoid using ENTER. LEAVE, however, is more than fine.


Confused
Post 02 Dec 2012, 21:30
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
While enter instruction is quite convoluted (it was designed to support block-structured languages, like Pascal, to provide access to enclosing scopes via stack display frames), leave is plain and simple
Code:
        mov     esp, ebp
        pop     ebp    
equivalent.
Post 02 Dec 2012, 22:16
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.