News:

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

Access LOCAL ByteArray with EDI

Started by AgentSmithers, June 19, 2009, 11:26:17 PM

Previous topic - Next topic

AgentSmithers

I got a Proc that I launch with create thread and I got this little Sniblet to clear the variable which worked when i had it as a Global Heap var but now that I made it local with the thread I cant seem to find a way to move the address into EDI

LOCAL RecvBuff[128]:Byte; Recv Buffer

mov edi, RecvBuff   <--- Error
                                mov eax,SIZEOF RecvBuff
                                xor edx,edx
                                    clrbuff:
                                    mov [edi],edx
                                    add edi,4
                                    mov ebx,[edi]
                                    test ebx,ebx
                                    jnz clrbuff
                                mov edi,OFFSET RecvBuff



Ive tried

mov edi, OFFSET RecvBuff ^^^^^use to work when on the HEAP ^^^^
mov edi, ADDR RecvBuff
mov edi, [RecvBuff]
mov edi, RecvBuff
mov byte ptr [RecvBuff], edi

All five do not work =(

Whats the proper way?


EDIT: Ahhh Found LEA, I guess thats the key to local Var's

I just removed the [solved] part of the title as this forum is not a help desk but members helping each other. Your question and the members answers live in the forum for later searches so it won't be wasted.

hutch--

dedndave

use lea for local vars

lea edi,RecvBuff

ecube

it's important to clear local buffers, always. Otherwise they can and usually do contain garbage

local buf[255]:BYTE

invoke RtlZeroMemory,addr buf,255

lea edi,buf
of mov edi,offset buf should work, they're pretty much the same, I use them interchangably 32bit

keep in mind it's a good idea to use lea, as mov, offset watever doesn't exist in 64bit, but lea does

jj2007

Quote
.data
buffer  db "Test", 0

.code
MyTest proc uses esi
LOCAL LocBuffer[260]:BYTE
  lea esi, LocBuffer
  ret
MyTest endp

This is a typical usage. The syntax mov esi, offset buffer can only be used for global variables, the reason being that the location of a LOCAL variable is relative to the stack pointer.

The syntax
   invoke MessageBox, hWin, addr MyMessage, addr MyTitle, MB_OK
works for both local and global variables, but a disassembly would show that for local variables, the invoke macro inserts a lea eax, MyMessage before pushing the address.
This behaviour of invoke is related to one specific MASM error message:

QuoteMyTest proc
LOCAL MyLocVar:DWORD
LOCAL LocBuffer[260]:BYTE
  mov eax, chr$("MyProc:")
  invoke MessageBox, 0, addr LocBuffer, eax, MB_OK   ; OK, no problem
  invoke MessageBox, 0, eax, addr LocBuffer, MB_OK   ; A2133:register value overwritten by INVOKE
  ret
MyTest endp
invoke needs eax for the lea eax, LocBuffer step - so it will be trashed and cannot be used again for the "first" parameter, i.e. the eax that has to be pushed after the address of "MyProc".

ToutEnMasm


If you use the standard masm proc,better way to clear the locals is a macro
Quote
ZEROLOCALES MACRO dernierelocale:REQ
   mov edx,edi
   mov ecx,ebp
   lea edi,dernierelocale
   sub ecx,edi
   xor eax,eax
   shr ecx,2
   rep stosd
   mov edi,edx   
ENDM         
usage:

Quote
ZEROLOCALES lastlocalname
;in your case
ZEROLOCALES LocBuffer


jj2007

For big and medium-sized projects, a call ClearLocals is probably more convenient: It is 16 bytes shorter (for each proc where it is being used) and does not trash ecx and edx.

Quoteinclude \masm32\include\masm32rt.inc

ZEROLOCALES MACRO dernierelocale:REQ
   mov edx,edi
   mov ecx,ebp
   lea edi,dernierelocale
   sub ecx,edi
   xor eax,eax
   shr ecx,2
   rep stosd
   mov edi,edx   
ENDM

.code
AppName   db "Masm32:", 0
HiWorld1   db "Hello World with call", 0
HiWorld2   db "Hello World with macro", 0

start:   call MyTest1
   call MyTest2
   print "Code sizes:", 13, 10, "with call: ", 9
   mov eax, MyTest1_END
   sub eax, MyTest1
   print str$(eax), 13, 10, "with macro:", 9
   mov eax, MyTest2_END
   sub eax, MyTest2
   print str$(eax), 13, 10
   getkey
   exit

MyTest1 proc
LOCAL MyLocVar:DWORD, LocBuffer[260]:BYTE
  call ClearLocals         ; optional: zero-initialise local variables
  push esi         ; esi to stack
  lea esi, LocBuffer      ; use esi as pointer to a local buffer
  invoke lstrcpy, esi, addr HiWorld1   ; copy data to local buffer
  print addr LocBuffer, 13, 10
  pop esi         ; restore the stack
  ret
MyTest1 endp
MyTest1_END:

MyTest2 proc
LOCAL MyLocVar:DWORD, LocBuffer[260]:BYTE
  ZEROLOCALES LocBuffer
  push esi         ; esi to stack
  lea esi, LocBuffer      ; use esi as pointer to a local buffer
  invoke lstrcpy, esi, addr HiWorld2   ; copy data to local buffer
  print addr LocBuffer, 13, 10
  pop esi         ; restore the stack
  ret
MyTest2 endp
MyTest2_END:

ClearLocals proc         ; put "call ClearLocals" as first instruction after LOCALS - eax will be zero on exit
.data?
   EspGlob   dd ?
.code
   pop EspGlob   ; save the return address to a global variable - now
               ; the stack is identical to the calling procedure   
   xchg eax, ecx   ; save ecx
   mov ecx, ebp   ; base page of calling procedure
   sub ecx, esp   ; ebp - esp = No. of bytes in locals
   mov esp, ebp   ; discard existing locals
   shr ecx, 2      ; divide by four
@@:   push 0         ; dwords on stack
   loop @B
   xchg eax, ecx   ; restore ecx, 0 to eax
   push EspGlob   ; restore the return address
   ret
ClearLocals endp

end start

qWord

here an other variant to clear locals:

; clear locals
cll macro
LOCAL cll_end
mov eax,esp
sub eax,ebp
jz cll_end
@@: mov DWORD ptr [ebp+eax],0
add eax,4
jnz @B
@@:
cll_end:
endm

example:

myproc proc
LOCAL dwX:DWORD
cll
....
ret
myproc endp

regards, qWord
FPU in a trice: SmplMath
It's that simple!

ToutEnMasm

it is clever than the cll macro use a negative index.
Quote
jz cll_end            ;seems not very useful

     cll macro lastlocal:REQ                  ;be sure there is a local
      mov eax,esp
      sub eax,ebp
   @@:   mov DWORD ptr [ebp+eax],0
      add eax,4
      jnz @B         
     ENDM
after there is just to know what is faster , rep stosd    or  a loop with add ?


jj2007

12 bytes instead of 18:

cll macro
  mov eax, esp
@@:
  and DWORD ptr [eax], 0
  add eax, 4
  cmp eax, ebp
  jb @B
endm


It requires that the programmer knows that at least one LOCAL must be present :wink

dedndave

that will depend largely on how many local variables you have
or, more correctly, how much local variable space needs to be cleared
quite often, not all of it needs to be cleared out
i usually make my own stack frames...
xor eax,eax
push eax
  :P

jj2007

#10
I just realised that my macro was unnecessarily bloated. Here is the good version, 10 bytes, trashes only eax:

ClearLocVars macro
  mov eax, esp
  mov esp, ebp
@@:
  push 0
  cmp esp, eax
  ja @B
endm

On the other hand, call ClearLocals needs only 5 bytes... [edit] so for a project with many procedures, a proc might be more convenient. Based on size and timings, I would probably pick the last of the five timed below, also because it does not trash any registers (remember: FASTCALL etc...).

Timings on Celeron M:
188     cycles for ZEROLOCALES, eax+ecx+edx trashed
145     cycles for cll, eax trashed
145     cycles for ClearLocVars, eax trashed
172     cycles for call ClearLocalsB, eax trashed
152     cycles for call ClearLocals, ALL regs preserved

Code sizes (macro or call only):
MyTest1      = 21
MyTest2      = 18
MyTest3      = 10
MyTest4      = 5
MyTest5      = 5

plus once for procs:
ClearLocalsB = 15
ClearLocals  = 18

[attachment deleted by admin]

Jimg

A different way, make the capability intrinsic to procs-

PrologueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
xprocname textequ <&procname>
if (parambytes+localbytes) ne 0 or @InStr(,<userparams >,<FORCEFRAME>)
    push ebp
    mov ebp,esp
    if localbytes ne 0
        add esp,-localbytes
        if (@InStr(,<&userparams >,<CLEARLOCALS>))
            if localbytes ne 0
                pushfd      ; save everything, or not, as you wish
                push ecx
                push edi
                push eax
                mov ecx,localbytes/4
                mov edi,ebp
                add edi,-localbytes
                mov eax,0
                rep stosd
                pop eax
                pop edi
                pop ecx
                popfd
            endif
        endif
    endif
endif
for reg,reglist
    push reg
endm
exitm %localbytes
endm

EpilogueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
for reg,reglist
    pop reg
endm
if (parambytes+localbytes ne 0) or (@InStr(,<userparams >,<FORCEFRAME>))
    leave ;; mov esp,ebp / pop ebp
endif
if (flags and 10000b) ;; if flags bit 5 is set then caller cleans stack
    ret
else
    ret parambytes
endif
xprocname textequ <>
endm
OPTION PROLOGUE:PrologueDef32
OPTION EPILOGUE:EpilogueDef32


This adds a new CLEARLOCALS prologuearg to the existing FORCEFRAME and LOADDS

usage:

tststs proc <CLEARLOCALS> a,b
local abc,def,ghi,jkl,rty[100]:byte,mno

ret
tststs endp

ToutEnMasm

Quote
334     cycles for ZEROLOCALES, eax+ecx+edx trashed
178     cycles for cll, eax trashed
209     cycles for ClearLocVars, eax trashed
339     cycles for call ClearLocalsB, eax trashed
257     cycles for call ClearLocals, ALL regs preserved

Code sizes:
MyTest1      = 21
MyTest2      = 18
MyTest3      = 10
MyTest4      = 5
MyTest5      = 5

plus once for procs:
ClearLocalsB = 15
ClearLocals  = 18


with LocBytes   = MAX_PATH * 10

1373    cycles for ZEROLOCALES, eax+ecx+edx trashed
1449    cycles for cll, eax trashed
1349    cycles for ClearLocVars, eax trashed
2038    cycles for call ClearLocalsB, eax trashed
1415    cycles for call ClearLocals, ALL regs preserved

Code sizes:
MyTest1      = 21
MyTest2      = 18
MyTest3      = 10
MyTest4      = 5
MyTest5      = 5

plus once for procs:
ClearLocalsB = 15
ClearLocals  = 18



jj2007

#13
Quote from: Jimg on June 20, 2009, 07:30:14 PM
A different way, make the capability intrinsic to procs-

Interesting, and works perfectly.

87      cycles for ZEROLOCALES, eax+ecx+edx trashed
95      cycles for cll, eax trashed
75      cycles for ClearLocVars, eax trashed
104     cycles for call ClearLocalsB, eax trashed
70      cycles for call ClearLocals, ALL regs preserved
107     cycles for JimG

Code sizes:
MyTest1      = 18
MyTest2      = 18
MyTest3      = 10
MyTest4      = 5
MyTest5      = 5
MyTest6      = 25

plus once for procs (call ClearLoc*):
ClearLocalsB = 17
ClearLocals  = 19


EDIT: Added an align 4 before the innermost loop of ClearLocals (30% faster)

[attachment deleted by admin]

Jimg

Results on an AMD-

57      cycles for ZEROLOCALES, eax+ecx+edx trashed
109     cycles for cll, eax trashed
77      cycles for ClearLocVars, eax trashed
98      cycles for call ClearLocalsB, eax trashed
85      cycles for call ClearLocals, ALL regs preserved
72      cycles for JimG

Code sizes:
MyTest1      = 18
MyTest2      = 18
MyTest3      = 10
MyTest4      = 5
MyTest5      = 5
MyTest6      = 25

plus once for procs (call ClearLoc*):
ClearLocalsB = 17
ClearLocals  = 19


Note: stosd relies upon direction flag being set normally, otherwise a fatal error occurs.  Perhaps not the best design.