News:

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

static dword local variables

Started by allynm, September 29, 2009, 12:59:38 AM

Previous topic - Next topic

allynm

Hi Folks,

Been awhile since I visited the forum.  Nice to be back.

Here's the question and the context:

I would like to declare a local variable within a PROC as a "static int".  Is this possible in MASM? 

Context is this:  I am writing a WndProc patterned after a C program originally written and published by Petzold.  He declares his C function locals as "static int".   If I try this declaring my locals as LOCAL DWORD  ?  I run into problems with the EBP when I try to use the locals later in the program.  The program winds up using the "wrong" EBP and therefore doesn't locate the correct offsets to get to the locals.

I can get around the problem by declaring the local variables as .data?  and the program runs fine.  But obviously it would be neater if there was a way to use the stack instead of fixed memory.

Best regards,
Mark Allyn


dedndave

hi Allyn
when you declare a local var in masm, it requires that the ebp register not be used during the routine
(this is not strictly true - you may push it and pop it, so long as you don't try to access locals when ebp is altered)
i prefer to write my procs with the epilogue and prologue turned off
i have to keep track of my own locals, but that is what i was used to in the old days, anyways
with epilogue in it's default mode, the PROC directive automatically generates code like this:

        push    ebp
        mov     ebp,esp

then, as you create local variables, the assembler pushes them onto the stack and references them sometihing like this

        mov     eax,Local1    ;actual code is mov eax,[ebp-4]
        mov     ebx,Local2    ;actual code is mov ebx,[ebp-8]

if prologue is also in it's default mode, the RET instruction generates code like this

        leave      ;same as mov esp,ebp then pop ebp
        ret     n  ;n=4* dword parameters

if you also use the "USES" specifier with PROC, it pushes and pops appropriate registers
so, you can do what you want to do - you just have to write your routine so that ebp doesn't get trashed

sinsi

Anything declared as local in a proc is on the stack, so when your proc returns it is gone, since the stack gets balanced before the ret.
The only places for a 'static' var are really .data or .data?.

If you want to limit the scope to that one proc, you could do

MyProc PROC a:dword
.data?
MyProcStatic dd ?
.code
...
MyProc ENDP

(I think so - not tested).
Light travels faster than sound, that's why some people seem bright until you hear them.

hutch--

Mark,

It depends how you want the variable to behave. In MASM you have 3 choices, 2 in the .DATA and .DATA? sections and the third as a local variable.

1. .DATA  An initialised variable (has a value attribute to it).

2. .DATA? UNinitialised variable (allocates a space for the variable but no starting value).

3. LOCAL variable. Created on the stack when the proc is called, destroyed when the proc exits.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

allynm

Hi everyone -

Somewhere in the PROC ebp is getting trashed, so that when I later refer to the LOCAL it is no longer located at [ebp-8].  I think Sinsi is correct that the only certain fix is to declare the variable in .data or .data? sections.  This bothers me because it doesn't seem like it should be necessary.

But, I don't understand why ebp is getting trashed because when I refer to the LOCAL the WndProc hasn't yet returned.  The stack shouln'd have been torn down. 

Ciao,
Mark


hutch--

Mark,

In a normal MASM proc you should not touch EBP at all. It is only when you write a stack frame free manual procedure that you can use EBP, normally the contents of the stack pointer ESP are copied to EBP on procedure entry.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

japheth


> declare the variable in .data or .data? sections.  This bothers me because it doesn't seem like it should be necessary.

It is necessary. Just write a C program and tell the compiler to create an assembly listing, and you'll see that the compiler does it similiar. Usually local static variables get a name prefix (or suffix) which includes the current procedure/function name, so the full name becomes unique inside the module.

raymond

One good way to get the EBP trashed "inside a proc" is to call another proc with the intention of using some of the local variables from the original proc with that second proc. There are 2 main solutions to avoid problems:
- declare such variables as global variables (i.e. in the .data sections) as suggested by others, or
- pass the variable value from the first proc as an argument to the next proc.

(I've seen code where such variables were being passed half a dozen times between different proc. It would have been a lot simpler to access them from global data. ::))
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

jj2007

Quote from: sinsi on September 29, 2009, 01:58:29 AM
If you want to limit the scope to that one proc, you could do

MyProc PROC a:dword
.data?
MyProcStatic dd ?
.code
...
MyProc ENDP


You can choose to use it only inside the proc, but it is available outside, see example below.

include \masm32\include\masm32rt.inc

.code

MyProc PROC a:dword
.data?
MyProcStatic dd ?
.code
mov MyProcStatic, 123
ret
MyProc ENDP

start:
call MyProc

MsgBox 0, str$(MyProcStatic), "MyProcStatic: ", MB_OK
exit

end start


Quote from: allynm on September 29, 2009, 11:16:30 AM
But, I don't understand why ebp is getting trashed because when I refer to the LOCAL the WndProc hasn't yet returned.  The stack shouln'd have been torn down. 

Are you calling another proc from inside that one? If yes, does it have a stack frame...? A new frame means ebp will be tied to the stack pointer of the new proc...

But in principle you are right. You need a trick, though, to access the local variable:

include \masm32\include\masm32rt.inc

MyTest PROTO:DWORD
MyLocalVar equ dword ptr [ebp-4]

.code
start: invoke MyTest, chr$("whatever")
MsgBox 0, str$(MyLocalVar), "It has disappeared:", MB_OK

exit


MyTest proc arg:DWORD
LOCAL lv1:DWORD

  mov lv1, 123
  call Lev2

  ret
MyTest endp

Lev2 proc
  MsgBox 0, str$(MyLocalVar), "A local variable outside its procedure:", MB_OK
  ret
Lev2 endp

end start

dedndave

any new proc should push ebp prior to setting up its' stack frame and restore it on exit
perhaps that is what you are looking for - one of your procs doesn't save ebp ???

one other method of storing temporary variables is dynamic memory allocation
not saying that is the method you want to use - i just noticed that it was omitted from the discussion

jj2007

Quote from: dedndave on September 30, 2009, 11:48:01 AM
any new proc should push ebp prior to setting up its' stack frame and restore it on exit

Not necessarily - see my last example above with the MyLocalVar equ dword ptr [ebp-4] trick. The sub-proc (Lev2) has no frame, and therefore can use the local variables of the main proc directly, without passing pointers etc. However, the equ is needed because the usage of the name lv1 is limited to the scope of the procedure.

In those cases where you need the sub-proc several times inside one main proc, you can call a Lev2 label instead of MySub proc:

MyTest proc arg:DWORD
LOCAL lv1:DWORD

  mov lv1, 123
  call Lev2

  mov lv1, 456
  call Lev2

ret

Lev2:
  MsgBox 0, str$(lv1), "A local variable outside its procedure:", MB_OK
  retn 0

MyTest endp


EDIT: Lev2 ends with retn 0, the reason being that the simple ret is a macro that would translate into retn 4 (in this case).

PBrennick

As a general principle; unless you are an advanced programmer you should avoild modifying the EBP register or playing with stack frames. Also, making holes in the stack for temporary usage is not a good idea unless you have a real good handle on stack maintainance. I have seen, over the years, many people get themselves into difficulties doing such things.

People such as JJ,Japheth, Hutch, DednDave, Sinsi and others too numerous to mention here are incredible programmers who do such things without batting an eye. Wait until you achieve their level of proficiency before doing such things. Experiment with them as a function of learning but avoid using them in your projects until you feel you are ready.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

KeepingRealBusy

I think the problem is that you did not adjust the stack pointer for the created variable. As soon as you INVOKE or CALL another procedure or push anything on the stack, it will overlay the content of the local variable. It is not that EBP is trashed, it is that the content of what EPB is pointing to is trashed.

Compile a simple C function having a local variable  and having a .cod file output and examine the .cod file to see what what C creates.

Dave.

jj2007

Quote from: KeepingRealBusy on October 01, 2009, 09:58:52 PM
I think the problem is that you did not adjust the stack pointer for the created variable. As soon as you INVOKE or CALL another procedure or push anything on the stack, it will overlay the content of the local variable. It is not that EBP is trashed, it is that the content of what EPB is pointing to is trashed.

LOCAL decreases the stack, and thus protects a memory area from subsequent pushes, calls and invokes. Check the example below - it combines the two options mentioned above. Of course, "RealProc" cannot have a stack frame.

include \masm32\include\masm32rt.inc

.code
MyTest proc arg:DWORD
LOCAL LocVar:DWORD
  LocVar_alias equ dword ptr [ebp-4]
  mov LocVar, 123
  push 888
  call Lev2
 
  push 1
  push 2
  call RealProc

  mov LocVar, 456
  push 999
  call Lev2

  push 1
  push 2
  call RealProc

  ret

Lev2:
  MsgBox 0, str$(LocVar), "A local variable called in a label inside the procedure:", MB_OK
  retn 4

MyTest endp

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
RealProc proc uses esi edi ebx arg1:DWORD, arg2:DWORD
  MsgBox 0, str$(LocVar_alias), "A local variable outside its procedure:", MB_OK
  ret
RealProc endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

start: invoke MyTest, chr$("Test")
exit

end start

allynm

Hi to all,

This is a great site!  Everyone has been really helpful and patient with my novice question.  Been overwhelmed with teaching last couple of days and couldn't reply to the various folks who've tried to help.

The last post by jj was extremely helpful.  I will give it a shot this afternoon and walk thru it with Ollydbg.

Just to make sure everyone is clear on what was going wrong:  The WndProc has a bunch of INVOKES when it tries to make scrollbars and paint.  What was happening was that EBP would lose track of the local var.  Not sure whether this qualifies as "trashing" or not.....more like amnesia.  It forgets where the local pointer is.  What was originally at [ebp - 4] no longer is there.

Early on I sort of figured out the quick fix was to to make the local global and stick it in the .data section.  That certainly does work.  But, this problem seemed like somehow it could be programmed around and still keep the variable on the stack. Quickly looking at JJ's code seems like he has nailed it.  As Paul Brennick observes rightly the folks contributiing to the site are really master craftsmen!

Thanks everyone,
Mark Allyn