I am 99% sure the answer is YES, but want to verify with the people who have more experience than I have.
In my code, can I set EBP to a value and assume that it will be preserved when I call library procedures, such as WriteChar and ReadConsole?
As far as I know ebp is used to set up a stack frame (for each function that has parameters or uses local variables) and thus ebp will be changed after a function call. I don't believe the functions restore the value of ebp before returning
Quote from: gelatine1 on October 17, 2015, 12:59:14 AMI don't believe the functions restore the value of ebp before returning
Believing is not knowing :biggrin:
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
.code
MyTest proc uses esi edi ebx hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar
mov MyLocVar, rv(GetTickCount) ; WinAPI test
print str$(ebp), 9, "ebp in the proc", 13, 10 ; more WinAPI calls under the hood
push 123
push 456
push 789 ; seriously misaligned stack!
ret
MyTest endp
Init
mov esi, 11111111
mov edi, 22222222
mov ebx, 33333333
deb 4, "Before the proc", ebp, esi, edi, ebx
invoke MyTest, 1, 2, 3, 4
deb 4, "After the proc", ebp, esi, edi, ebx
EndOfCodeOutput:
Before the proc
ebp 1638292
esi 11111111
edi 22222222
ebx 33333333
1638260 ebp in the proc
After the proc
ebp 1638292
esi 123
edi 456
ebx 789
when you write a PROC, MASM creates a "stack frame", by default
well - it does if there are any arguments on the PROC line, or if there are any LOCAL variables
if you write a PROC like this....
SomeFunc PROC USES EBX ESI EDI Arg1:DWORD,Arg2:DWORD
LOCAL dwLocalVar1 :DWORD
LOCAL dwLocalVar2 :DWORD
mov ebx,87654321h
mov esi,Arg1
mov edi,Arg2
mov dwLocalVar1,12345678h
mov dwLocalVar2,87654321h
mov eax,dwLocalVar1
add eax,ebx
ret
SomeFunc ENDP
the assembler will generate code that looks like this
notice that StdCall convention is the default for most ASM programs, because windows API functions use it
for C, Basic, Pascal, or other conventions, the code will be a little different
SomeFunc PROC Arg1:DWORD,Arg2:DWORD
push ebp ;preserve EBP
mov ebp,esp ;EBP is now the stack frame base pointer
sub esp,8 ;make space for 2 local dwords
push ebx
push esi
push edi
mov ebx,87654321h
mov esi,[ebp+8] ;Arg1
mov edi,[ebp+12] ;Arg2
mov [ebp-4],12345678h ;dwLocalVar1
mov [ebp-8],87654321h ;dwLocalVar2
mov eax,[ebp-4] ;dwLocalVar1
add eax,ebx
pop edi
pop esi
pop ebx
leave ;same as MOV ESP,EBP, then POP EBP
ret 8 ;return and remove 2 dword arguments from the stack
SomeFunc ENDP
the first part is called a "prologue"
the last part is called an "epilogue"
an epilogue will be generated any place there is a RET, so try to use one exit point :P
per the windows32 ABI, EBX, EBP, ESI, EDI should be preserved
also, it is assumed that the direction flag is cleared at entry
if you set the direction flag inside a function, you should clear it before exit
EAX, ECX, and EDX are volatile, EAX is generally used to return a status or result
one is no more volatile than the other - most often, they all get trashed
Excellent info. So long I can be confident that EBP isn't going to change out from under me when calling routines, I'm good.
Thanks.
Perhaps I should drop a word on the "seriously misaligned stack" in my example:
SomeTest proc uses esi edi ebx args:DWORD
...
ret
SomeTest endp
"uses esi edi ebx" means "preserve these three registers", which is required in certain situations by Windows (more precisely, in callback functions).
The stack frame uses ebp on entry, and restores it on exit, providing also the correct stackpointer esp. That is a great feature, especially if you are bad in math, and don't balance the number of push's and pop's. Unfortunately, your "uses esi edi ebx" gets invalid because these regs are popped in the wrong place. This kind of bug is really difficult to chase, so better make an effort to control if your esp is the same on entry and before the ret. If in doubt, use a global variable to store esp on proc entry, and compare it to the stored one on proc exit, i.e. before the ret.
I absolutely get it that if I push something on the return stack, and I don't pop it before returning, then my program will fail miserably.
On the other hand though, if you know what you are doing, you can do some pretty trick stuff by pushing a valid code address on the stack and then returning. That kind of thing is pretty common in Forth, but I currently have no need or desire to do it at the machine level.
Forth (that is what I am implementing) has separate stacks for parameters and return addresses, so it can avoid most of this fancy stack juggling fun.
The thing you are looking for here is a "balanced" stack which mean that it must be the same AFTER the called procedure has returned. In STDCALL this is done by the procedure, with C call you must correct ESP yourself. As far as manipulating the stack, stick to CALL / RET(N) number as it is balanced in hardware. If you use a normal MASM stack frame it does this for you but once you start writing procedures with no stack frame you will need to do this yourself.
i often write my own prologue and epilogue
and, the way i do it, you can leave with an unbalanced stack, here's why...
;these directives turn off the default prologue and epiloge
OPTION PROLOGUE:None
OPTION EPILOGUE:None
MyFunc PROC Arg1:DWORD,Arg2:DWORD
push ebx
push esi
push edi
push ebp
mov ebp,esp ;notice that EBP holds an unaltered value throughout the routine
;here, i can PUSH whatever i like - POP is optional
leave ;the value in EBP is loaded back into ESP, effectively reseting the stack
;LEAVE also POP's EBP from the stack
pop edi
pop esi
pop ebx
ret 8 ;RETurn and discard Arg1 and Arg2
MyFunc ENDP
;these directives restore the default prologue and epilogue for the next PROC
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
Quote from: CCurl on October 17, 2015, 08:17:25 AMI absolutely get it that if I push something on the return stack, and I don't pop it before returning, then my program will fail miserably.
The nasty thing is, no, with stack frames it won't fail miserably. It will return correctly even if the stack is unbalanced, but the preserved regs (usually esi, edi, ebx) are trashed. And it is difficult to predict how Windows or your own routines react to such trashing, and where... but no worries, if you reach that stage, we can discuss ways to prevent that.
Dave's code is one option, as it preserves the regs before setting the frame. Which means you can write a proc with an unbalanced stack at the end - that works perfectly, if it is by design 8)
But we are indulging in unnecessary details here. In 99.9% of all cases, simply take care that the stack is balanced, fullstop.
QuoteOn the other hand though, if you know what you are doing, you can do some pretty trick stuff by pushing a valid code address on the stack and then returning. That kind of thing is pretty common in Forth, but I currently have no need or desire to do it at the machine level.
Forth (that is what I am implementing) has separate stacks for parameters and return addresses, so it can avoid most of this fancy stack juggling fun.
We've seen such tricks, of course. Speed-wise, they are rarely efficient.
Quote from: CCurl on October 17, 2015, 04:56:41 AM
So long I can be confident that EBP isn't going to change out from under me when calling routines, I'm good.
you can pretty much rely on that one
if it were any other way, all kinds of code would crash :biggrin:
i have disassembled some Forth code, and saw things like this
pop edi ;get the return address off the stack
;body of code
jmp edi
it's not all that inefficient, really
as Hutch will tell you, newer processors attempt to "match up" CALL's and RET's (prediction)
ok - you break that prediction algorithm, but the ouch isn't too serious
JMP EDI is pretty fast :t
but, the processor may try to cache code unnecessarily, up to the next RET instruction
Quote from: CCurl on October 17, 2015, 04:56:41 AM
Excellent info. So long I can be confident that EBP isn't going to change out from under me when calling routines, I'm good.
Thanks.
Just one moment. Are you putting anything into EBP and expecting it to be present when you return from a library routine? How do you preserve what is in EBP as you enter your function?
Dave.
Quote from: KeepingRealBusy on October 18, 2015, 06:49:59 AM
Just one moment. Are you putting anything into EBP and expecting it to be present when you return from a library routine? How do you preserve what is in EBP as you enter your function?
Dave.
Yes, that is absolutely my expectation and understanding. Is it invalid?
Since no outside code is calling my procedures, I believe I can manage EBP however I choose, can't I?
I understand that if I choose to write procedures that some 3rd party code can call, then I will need to follow the rules regarding register preservation.
If no one is calling your procedures, how does your program ever get into execution?
Dave.
Quote from: KeepingRealBusy on October 20, 2015, 02:25:29 PM
If no one is calling your procedures, how does your program ever get into execution?
Dave.
It is a command line program. This is my main function. All the work is done in processLine.
main proc
call bootStrap
mainLoop:
; Display prompt
mov edx, offset msgPrompt ; string addr in edx
call WriteString
; Wait for commands from console
vmToAbsolute edx, MemLoc_InpBuf
inc edx ; save room for the count BYTE
INVOKE ReadConsole, hStdIn, edx, 100, ADDR bytesRead, 0
mov eax, [bytesRead] ; count includes CR and LF
sub eax, 2 ; remove CR/LF
MemSet_8 MemLoc_InpBuf, al ; store the count
m_fPush 0
m_fPush MemLoc_InpBuf
m_execW code_Count
m_execO I_PLUS
m_execO I_STORE_8
; is the command "bye"?
push edi
mov edi, offset cmdBye
vmToAbsolute esi, MemLoc_InpBuf
call strCmpC
pop edi
cmp eax, 0 ; strCmpX returns result in EAX
jne exitMain
call processLine ; Where all the work gets done
mov edx, offset msgOK ; string addr in edx
call WriteString
jmp MainLoop
exitMain:
mov edx, offset msgBye ; string addr in edx
call WriteString
; vmToAbsolute edx, MemLoc_InpBuf
; INVOKE ReadConsole, hStdIn, edx, 100, ADDR bytesRead, 0
invoke GetProcessHeap
invoke HeapFree, eax, HEAP_NO_SERIALIZE, theMemory
invoke ExitProcess,0
main endp
end main
Quote from: KeepingRealBusy on October 18, 2015, 06:49:59 AM
Quote from: CCurl on October 17, 2015, 04:56:41 AM
Excellent info. So long I can be confident that EBP isn't going to change out from under me when calling routines, I'm good.
Thanks.
Just one moment. Are you putting anything into EBP and expecting it to be present when you return from a library routine? How do you preserve what is in EBP as you enter your function?
Dave.
Isn't protecting the registers the responsibility of the code being called, not the code making the call? The caller cannot depend on EAX, EDX, or ECX retaining their values, but the other registers are supposed to be restored to their original values upon return.
Dave & CCurl,
Don't argue :eusa_naughty:
Especially when you essentially agree:
yes you can change ebp (but caution with the locals),
yes you have to restore it before the ret.