Hi
(Sorry for my english)
I use masm with RadASM. When i define a function, then after disassembling i see that at the beginning of my function:
push ebp
mov ebp,esp
How can i avoid this ? I want to program the stack by myself.
btw: i am new to assembler
Hi gbreuer,
Quote from: gbreuer on November 25, 2013, 12:09:11 AM
Hi
(Sorry for my english)
I use masm with RadASM. When i define a function, then after disassembling i see that at the beginning of my function:
push ebp
mov ebp,esp
How can i avoid this ? I want to program the stack by myself.
btw: i am new to assembler
That's a frame function, which can call other functions, declare local variables etc. Without this prologue it would be a leaf function. You will find more details here. (http://www.agner.org/optimize/calling_conventions.pdf) And welcome to the forum.
Gunther
Quote from: gbreuer on November 25, 2013, 12:09:11 AMHow can i avoid this ? I want to program the stack by myself.
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
OPTION PROLOGUE:None
OPTION EPILOGUE:None
DelGdiObj PROC lphGdiObject:LPVOID
mov edx,[esp+4]
mov eax,[edx]
.if eax
and dword ptr [edx],0
INVOKE DeleteObject,eax
.endif
ret 4 ;with the prologue/epilogue disabled, you must pop 4 bytes for each passed dword parm
DelGdiObj ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
You don't even need the prologue/epilogue thing:
include \masm32\include\masm32rt.inc
.code
AppName db "Masm32:", 0
start:
push MB_OK
push offset AppName
push chr$("Hello World")
call MyBox
push MB_OK
push offset AppName
push chr$("Another box")
call MyOtherBox
exit
MyBox proc ; arg1, arg2, arg3
invoke MessageBox, 0, [esp+3*4], [esp+3*4], [esp+3*4]
retn 3*DWORD
MyBox endp
MyOtherBox:
invoke MessageBox, 0, [esp+3*4], [esp+3*4], [esp+3*4]
retn 3*DWORD
end start
I doubt that you will do yourself a favour using frameless procs. It is rarely faster, rarely more compact, but frequently a source of very obscure bugs that will cost you sleepless nights.
However, in case you cannot suppress your masochistic tendencies, Hutch has left an easter egg for you: \Masm32\examples\exampl07\slickhuh\slickhuh.asm :lol:
there are times when it is nice :t
The advantage of a procedure with no stack frame is limited to very short leaf procedures that are hit at very high interval rates. In this context you save some overhead but with anything much larger or anything that calls another procedure, the complexity is very high and for no gain.
Basically you have to manually keep track of the register ESP and any PUSH or POP instruction changes ESP.
Now with a procedure call that has arguments passed to it on the stack in the normal manner it has the return address at the stack memory address [esp], the first user argument is at [esp+4], second at [esp+8] etc ....
If you have an argument at [esp+4] then use two PUSH instructions, you must add 4 * 2 to the stack address so [esp+4] becomes [esp+12] or to help you keep track of it the alternative notation [esp+4][8]. NOTE also that you must balance the stack manually on exit if you are using the STDCALL convention, the RET(n) instruction takes a numeric argument so if you have 2 * 4 byte stack arguments you use RET 8 on exit to balance the stack. If this sounds complicated, you are right. :icon_mrgreen:
that's not the only advantage of creating your own stack frame
MyFunc PROC USES EBX lpString:LPSTR
LOCAL dwSomeVal :DWORD
;body code
ret
MyFunc ENDP
the assembler generates something like this...
MyFunc PROC lpString:LPSTR
push ebp
mov ebp,esp
sub esp,4 ;or ADD ESP,-4; LEA ESP,[ESP-4] might be better, as no flags are affected
push ebx
;body code
;the stack must be balanced before exit so that EBX is properly restored
pop ebx
leave
ret 4
MyFunc ENDP
however, if you take charge of the stack frame....
OPTION PROLOGUE:None
OPTION EPILOGUE:None
MyFunc PROC lpString:LPSTR
push ebx
push ebp
mov ebp,esp
lea esp,[esp-4] ;flags not affected (though we rarely use current flag state)
;body code
;the stack is balanced by LEAVE before EBX is restored
;i.e., you can exit with an imbalanced stack
leave
pop ebx
ret 4
MyFunc ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
one example of this being particularly useful...
your PROC creates a variable-sized (string perhaps) buffer on the stack
no need to restore the original ESP, as LEAVE takes care of everything
Here is another one :
include SimpleFunc.inc
.data
str1 db 'Function test',0
.code
start:
_invoke StdOut,ADDR str1
invoke ExitProcess,0
StdOut:
sub esp,2*4
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov DWORD PTR [esp+4],eax
invoke lstrlen,DWORD PTR [esp+12]
mov edx,esp
invoke WriteFile,DWORD PTR [esp+20],\
DWORD PTR [esp+24],\
eax,edx,0
add esp,2*4
ret 4
END start
and - if you want to use names for parms and locals....
OPTION PROLOGUE:None
OPTION EPILOGUE:None
MyFunc PROC lpString:LPSTR
;----------------------------------------
_lpString TEXTEQU <dword ptr [EBP+12]> ;lpString PARM alias
; [EBP+8] return address
; [EBP+4] preserved EBX
; [EBP] preserved EBP
_dwSomeVal TEXTEQU <dword ptr [EBP-4]> ;dwSomeVal LOCAL alias
;----------------------------------------
push ebx
push ebp
mov ebp,esp
lea esp,[esp-4] ;flags not affected (though we rarely use current flag state)
;body code
;the stack is balanced by LEAVE before EBX is restored
;i.e., you can exit with an imbalanced stack
leave
pop ebx
ret 4
MyFunc ENDP
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
it's easy to update the TEXTEQU list when changes are made :t
one more advantage of maintaining your own stack frame
is that you can initialize local variables, as they are calculated
for example.....
push ebx
push esi
push edi
push ebp ;[EBP] = saved EBP contents
mov esi,cg.v.rcDiv.top ;ESI = window height
mov ebp,esp
dec esi ;ESI = window height-1
xor ecx,ecx
movzx eax,byte ptr dwGridState
mov ebx,36
inc ecx
.if esi>=265
mov cl,35
.if eax
mov bx,605h
.endif
.elseif esi>=83
mov cl,7
.if eax
mov bx,601h
.endif
.elseif esi>=59
mov cl,5
.if al==2
mov bx,401h
.endif
.elseif esi>=43
mov cl,5
.if al==2
mov bl,1
.endif
.endif
mov eax,esi
xor edx,edx
push ecx ;[EBP-4] = loop count+1
push esi ;[EBP-8] = Y position high dword
push edx ;[EBP-12] = Y position low dword
div ecx
push eax ;[EBP-16] = Y step high dword
xor eax,eax
div ecx
shl edx,1
cmp edx,ecx
sbb eax,-1
push eax ;[EBP-20] = Y step low dword
push ebx ;[EBP-24] = grid line count reset
;
;
LOCAL's can be ordered "as they are created" on the stack
this is probably more efficient than a bunch of MOV [EBP-xx],reg32 instructions
Thanks, for this detailed answers.
The reason for my question was, that i explore the principles of local "variables" via stack.
Then i found during debugging the "epilog"-code and was not sure, whether the assembler confuse my experiments by adding code ;-)
Now i know, that this code is common and i can immediately work with ebp, as soon the function is started.
what you may want to know...
if you use no PROC line arguements (parameters) and no LOCAL's, EBP is not used
you can preserve it, and use it as a working register
the windows ABI.....
EBX, ESI, EDI, and EBP should be preserved across calls
and - the direction flag should remain cleared (if you set it, clear it when done)
EAX, ECX, and EDX may be destroyed across calls
those are the rules for windows API functions
we usually try to follow the same rules when writing PROC's
i may write a PROC that preserves EBX, ESI, EDI, EBP
then, inside that PROC, call "low-level" support PROC's that do not preserve them - that's ok, too
Here is a simple test piece that demonstrates how ESP behaves in a no stack frame proc. It also shows ESP before and after the call to show that the stack is balanced.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
testproc PROTO arg1:DWORD,arg2:DWORD
.data?
value dd ?
.data
item dd 0
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
LOCAL var1 :DWORD
print ustr$(esp)," stack before call",13,10,13,10 ; stack BEFORE procedure call
invoke testproc,499, 501
mov var1, eax
print ustr$(esp)," stack after call",13,10,13,10 ; stack AFTER procedure call
print ustr$(var1)," Calculation result",13,10,13,10
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
testproc proc arg1:DWORD,arg2:DWORD
push ebx ; add 4 to ESP
push esi ; add another 4 to ESP
push edi ; add another 4 to ESP
push ebp ; add another 4 to ESP
; --------------------------------------
; you now have 7 32 bit registers to use
; EAX ECX EDX EBX EBP ESI EDI
; [ESP] is treated as a memory operand
; --------------------------------------
; [esp+4] = arg1
; [esp+8] = arg2
; the appended [16] is the extra bytes for the 4 PUSH instructions
mov eax, [esp+4][16] ; load arg1 into EAX
mov ecx, [esp+8][16] ; load arg2 into ECX
add ecx, eax ; do something simple in the test proc
mov eax, ecx ; place the return value if needed in EAX
; ------------------------------------------------
; restore the contents of the 4 required registers
; ------------------------------------------------
pop ebp
pop edi
pop esi
pop ebx
retn 8 ; balance the stack with 8 bytes
testproc endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
Hi
here a little test-function. I call it via invoke.
As we discussed above, the assembler inserts at its beginning
push ebp
mov ebp,esp
I want a local variable (named here "stringvar") that is 3 Bytes long (inclusive the 0-Byte).
The first 4 Bytes of the stack are taken by the "push ebp" above. So i subtract 7 from
ebp to skip this 4 Bytes and then to make space for the needed 3 bytes for "stringvar":
Now the offset for "stringvar" is calculated.
Before this i move the stackpointer to make space for "stringvar".
But this only works, when i move the stackpointer about 12 Bytes. Otherwise i get an
empty MessageBox. (Even the button is empty).
I followed this step by step via ollydbg. But i found no explanation.
What i am doing wrong ?
functionxyz proc testarg:dword
; here the assembler later inserts automatically "push ebp" and "mov ebp,esp"
STACK_ADDER equ 12 ; only 12 works. lower or higher results in an empty messagebox below
; 7 for skipping the 4 bytes of the stack and then the 3 needed bytes for stringvar
stringvar equ ebp-7
sub esp,STACK_ADDER ; reserve space on stack
mov byte ptr [stringvar], 'o'
mov byte ptr [stringvar+1], 'k'
mov byte ptr [stringvar+2], 0
invoke MessageBox,0,addr [stringvar],offset mbt_title,MB_OK
mov eax,0affeh ; return-test-value
add esp,STACK_ADDER ; restore stack
ret
functionxyz endp
ok - don't mix "the assembler handles the stack frame" with "the programmer handles it" in the same PROC
use one method or the other
in this case, just create a LOCAL or LOCAL's
MyFunc PROC dwParm:DWORD
LOCAL dwVal1 :DWORD ;1 dword
LOCAL dwVal2 :DWORD ;1 dword
LOCAL abBytes[4] :BYTE ;4 bytes
Quote from: gbreuer on November 27, 2013, 05:20:28 AM
But this only works, when i move the stackpointer about 12 Bytes. Otherwise i get an
empty MessageBox. (Even the button is empty).
I followed this step by step via ollydbg. But i found no explanation.
In Win32 code, the stack
must be DWORD-aligned. Otherwise, you will see such crashes all the time.
Thanks for the hints. I have to learn more. At the moment i will use LOCAL in such cases.
But there is a problem with "LOCAL": How can i make it compatible to underscores in variablenames?
I got a syntax-error for "ebp" as long as i used a name like this: local a_b ...
Then i removed the underscore and it worked.
LOCAL a_b works fine with ML 6.14 ... 10.0 and JWasm.
Do you get Error A2056: Symbol already defined: a_b ?
Can you post a complete example?
I got this error:
error A2008: syntax error : ebp
But i found the problem: The length of variablenames, that are used with LOCAL
is limited. It has nothing to do with the underscore. I shortened the name, and it works now.