News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

PROCEDURES

Started by seymour_glass, March 30, 2006, 09:08:25 PM

Previous topic - Next topic

Mark Jones

"Stack Balance" issues arise when the stack becomes mis-aligned. Take this code for example:


one:
    push 1000
    push 100
    call Beep

two:
;    push 1000   we forgot to include this
    push 100
    call Beep


The first code runs normally. This is actually what "invoke Beep,100,1000" gets converted to by ML.EXE. Inside the Beep procedure it POPs two values off the stack to use as its frequency and duration parameters. (This is how parameter passing works in MASM.) In the second example, we forgot to push a parameter, but Beep still POPs two off the stack, unbalancing it. So Beep just popped off some other value, which will probably cause a crash (or a very unusual tone!) But if this particular function does not crash the app, the stack is no longer correct (it is missing a value) so something else will probably crash. Since the stack is a Last-In-First-Out buffer, unbalancing it is usually catastrophic because nothing else lines up. So you only want to POP things off the stack if you're sure you PUSHed them on. :bg

Procedures are a little more complicated. In this procedure here,


MyProc PROC szText:DWORD
    invoke MessageBox,0,szText,0,MB_OK
    push eax
    ret
MyProc ENDP


Say we call this from somewhere else in our code. What happens when a procedure is called, is the EIP (the address where program execution originates from) is pushed to the stack, then execution branches to the procedure. The RET instruction POPs the topmost value off the stack as EIP and resumes program execution there. This works great for normal procedures but can you guess what will happen to the one above? Right, program execution will branch to whatever value was in EAX! This will probably result in the dreaded "Page Fault" error.

To further complicate things, it has been my experience (and I have not fully tested this) but PUSHing extraneous values to the stack without imbalancing it is not a problem. i.e.,


    push eax
    push 1234
    push HiMom!
    invoke ExitProcess,0


Those three PUSHes are simply not used for anything. Apparently Windows discards an application's stack when it exits, so those values are not stuck in memory anywhere. This isn't good coding practice, but it doesn't seem to hurt anything either. :)
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

tenkey

By default, the PROC protects you from excessive PUSHing. The RET instruction, when it appears between PROC and ENDP is expanded to a stack balancing code sequence. The stack balancing act is equivalent to the following...

; set ESP to beginning of stack frame
; by definition, this deallocates all local variables,
;   and any unpopped stack data
        mov esp,ebp

; recover pushed EBP to satisfy register preservation rules
; this would normally be a pointer to the previous stack frame,
;   but in reality, it doesn't matter what it is, as long as it's restored
        pop ebp

; the stack is now properly pointing to the return address
; nn is always 0 for C or CDECL type PROC
; the RET nn will pop nn bytes of "arguments" AFTER the return address
;   has been removed from the stack
        ret nn

A programming language is low level when its programs require attention to the irrelevant.
Alan Perlis, Epigram #8