flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
yoshimitsu 11 Nov 2012, 18:25
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"). |
|||
![]() |
|
randomdude 11 Nov 2012, 18:55
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'? |
|||
![]() |
|
yoshimitsu 12 Nov 2012, 03:26
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. |
|||
![]() |
|
randomdude 12 Nov 2012, 10:43
OK, i think i got it now, thanks
![]() 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? |
|||
![]() |
|
AsmGuru62 12 Nov 2012, 11:55
LEAVE may be shorter, but
Intel Optimization Manual recommends to avoid ENTER/LEAVE and replace them with few simpler instructions. |
|||
![]() |
|
yoshimitsu 12 Nov 2012, 16:13
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. |
|||
![]() |
|
randomdude 12 Nov 2012, 16:40
OK, thanks yoshimitsu. I'll decrease the local variables by 4, the retn will stay 0 as it's a cdecl function
![]() |
|||
![]() |
|
Teehee 02 Dec 2012, 21:30
Quote:
Quote:
![]() |
|||
![]() |
|
baldr 02 Dec 2012, 22:16
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 |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.