News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Zero locals

Started by JK, March 27, 2021, 05:18:13 AM

Previous topic - Next topic

JK

In 32 bit i can zero all locals quite easy whenever i use an EBP based stack frame. Everything between ESP and EBP is "local" right after the prologue. The more, locals are located on the stack in the same order they are defined in code in 32 bit.

Running the same code in 64 bit, i see that locals don´t start always at RBP and i cannot rely on the fact that the defined order of locals, will be the same on the stack. E.g. in code i have 3 locals: a DWORD, a structure and another DWORD, on the stack i find 2 DWORDs one right after the other followed by the structure.

The basic idea in 64 bit for the following code:
local x :dword
local y :rect
local z: dword

was:
cld
xor rax, rax
lea rcx, x
add rcx, sizeof(x)
lea rdi, z
sub rcx, rdi
rep stosb

(get the address of the first local, add it´s size, get the address of the last local to zero out, fill everything in beween with zero)

But obviously this doesn´t work! What else could i do? I want to reliably zero out all or certain portions of my locals in a procedure. Of course i could explicitly zero out each local (mov rax, 0 -> mov ..., rax), but there must be a much more elegant and faster way - how?


Thanks


JK

jj2007

Quote from: JK on March 27, 2021, 05:18:13 AMin 64 bit, i see that locals don´t start always at RBP and i cannot rely on the fact that the defined order of locals, will be the same on the stack. E.g. in code i have 3 locals: a DWORD, a structure and another DWORD, on the stack i find 2 DWORDs one right after the other followed by the structure.

With which frameword/assembler does this happen? I use my own PROLOGUE and EPILOGUE, which assigns LOCALs exactly where the programmer wants them to be; and the "ZeroLocals" is built into the PROLOGUE macro:

SayHi proc <cb> arg:SIZE_P ; <cb> indicates ok for usage as a callback function; the arg is a pointer
Local v1, v2, v3, v4, rc:RECT
  Print Str$(" \nLocals v1...v4: %i %i %i %i", v1, v2, v3, v4)
  jinvoke MessageBox, 0, arg, Chr$("Hi"), MB_OK or MB_SETFOREGROUND
  ret
SayHi endp

0000000140001002  | 55                      | push rbp                        |
0000000140001003  | 48 C7 C5 E0 FF FF FF    | mov rbp,FFFFFFFFFFFFFFE0        | clear locals
000000014000100A  | 83 24 2C 00             | and dword ptr ss:[rsp+rbp],0    |
000000014000100E  | 48 83 C5 04             | add rbp,4                       |
0000000140001012  | 78 F6                   | js 14000100A                    |
0000000140001014  | 48 8B EC                | mov rbp,rsp                     |
0000000140001017  | 48 89 4D 10             | mov qword ptr ss:[rbp+10],rcx   | load arguments (option <cb>)
000000014000101B  | 48 89 55 18             | mov qword ptr ss:[rbp+18],rdx   |
000000014000101F  | 4C 89 45 20             | mov qword ptr ss:[rbp+20],r8    |
0000000140001023  | 4C 89 4D 28             | mov qword ptr ss:[rbp+28],r9    |

JK

Thanks JJ, i´m using UASM with option win64:15 and option stackbase:rbp. But maybe i should run my own PROLOGUE and EPILOGUE like you do. I suspect that some of UASM´s "optimizations" make a procedure look different depending on number and type of parameters, number and type of locals, if it´s a leaf procedure or not and maybe some other stuff. This makes it difficult to find a generic approach.

Could you please expand a bit on how you do it (PROLOGUE and EPILOGUE).

I´m very much interested in from case to case not zeroing all locals all the time, but sometimes only some of it. Something like this:
local a ...
local b ...
local c ...
zero_until_here (or zero_from_here)
local x ...
local y
 

This would require a custom PROLOGUE and an additional macro for setting a list of locals to zero. If i knew the start or end address of the locals block and i could rely on locals appearing on the stack in the order, they were defined in code, i could specify one local as a start/end point for a loop setting everything in between to zero. Maybe better like this:
local a ...
local b ...
local c ...
local x ...
local y
zero_until(c)
...
 

would zero a,b and c, and:
local a ...
local b ...
local c ...
local x ...
local y
zero_until(yc)
...
   
would zero out all locals.

How to do this, preferably for both 32 and 64 bit ?


JK

jj2007

Quote from: JK on March 27, 2021, 08:00:07 AMI´m very much interested in from case to case not zeroing all locals all the time, but sometimes only some of it

Known problem: you have a dozen local dwords and structures, plus a 8kB buffer that doesn't need zeroing.
In JBasic (the dual 64/32 bit demo that comes with MasmBasic), with e.g. useClv=120 you can limit the ClearLocals to 120 bytes

How I do it, that is a long story. The file JBasic.inc comes with the MasmBasic package, but explaining the PROLOGUE macro would require more time than I currently have, sorry...

JK

Thanks anyway - i found JBasic.inc, now i can study how you do it!

One more question - is this your generic PROLOGUE and EPILOGUE, or are there more variations to be found in MasmBasic?


JK

jj2007

It 's the generic one, set in j@start

jj2007

Btw there was a long PROC and prolog/epilog thread three years ago. Sooner or later you will stumble over "undocumented MASM features" when testing your stuff with Watcom vs ML64 assemblers.

hutch--

While the guts of UASM is not my concern, if you need to be able to set local values in your prologue, Bob Zale did it in PB so if you disassemble a PB executable, you will see his technique. It means you must be able to set dynamic code to do this after the entry to the proc.

If I had a reason to, I think it can be done in MASM so if UASM has properly duplicated the MASM pre-processor, it should be able to insert dynamic code after the procedure entry.

jj2007

Quote from: hutch-- on March 27, 2021, 11:02:50 PMI think it can be done in MASM

Indeed, it can be done. I added the "clear the locals" feature to JBasic (my dual 64-/32-bit library) half a year ago.

if useClv lt localbytes and useClv ge 4 ; with e.g. useClv=120, you can limit the ClearLocals to 120 bytes
  lea rbp, [rsp-useClv]
else
  lea rbp, [rsp-localbytes-locBytesOff] ; ML64 and AsmC address locals differently, and create a 16-byte unused zone near the stack pointer
endif
ClearLoc:  and dword ptr [rbp], 0
  add rbp, 4
  cmp rbp, rsp
  js ClearLoc

JK

@hutch

I know how PB does it, a disassembly reveals this. Basically i know and understand what i must do. But i hoped to get away without a custom PROLOGUE/EPILOGUE. UASM does a good job optimzing code and stack space usage, but the downside is - at least in 64 bit - i cannot calculate the stack position of locals from RBP and RSP in a reliable manner, because of these optimizations. I even cannot rely on the order of locals to appear as these are defined. UASM puts larger locals at the start.

@JJ

In the meantime i had a chance to study your code and i understand, what you are doing. As said above, i hoped to be able to use, what´s built-in into UASM, and to add some code clearing the locals, i want to be cleared. But maybe the only way to accheive, what i want, is doing it the hard way by writing my own custom PROLOGUE/EPILGUE. I will be able to learn from your code and extract, what i need!

One more question: the return value of a custom PROLOGUE tells the assembler where the locals are located in relation to EBP/RBP - right ?


JK

Vortex

Here is another attempt to initialize the local variables. This can be combined with the custom PROLOGUE \ EPILOGUE method :

include     \masm32\include\masm32rt.inc


INITLOC MACRO registers

    VarSize=0

    StackPos=0

    IFNB <registers>   

       StackPos=4*registers

    ENDIF

ENDM


LOCALX MACRO _name,_type

    VarSize=VarSize + SIZEOF(_type)

    LOCAL _name : _type

ENDM


ENDLOC MACRO

    lea     eax,[esp+StackPos]
    invoke  memfill,eax,VarSize,0

ENDM


.code

start:

    call    main
    invoke  ExitProcess,0

main PROC USES esi edi ebx

INITLOC 1 ; number of preserved registers

LOCALX rc,RECT
LOCALX x,DWORD
LOCALX y,DWORD

ENDLOC

    lea     eax,rc

    invoke  crt_printf,\
            CTXT("y = %d , x = %d , &rc = %X"),\
            y,x,eax
    ret

main ENDP

END start


rsala

Hi,

UASM now handles primitives first then structs/arrays, so do not expect them to be stored in the same order they are declared.
EC coder

nidud

#12
deleted

nidud

#13
deleted

jj2007

#14
Quote from: JK on March 28, 2021, 08:04:11 AMOne more question: the return value of a custom PROLOGUE tells the assembler where the locals are located in relation to EBP/RBP - right ?

I'm afraid it doesn't, that would make the task easier.

The JBasic library installer has moved here.

I attach a shortened version of the JBasic library. Extract all files to \Masm32\MasmBasic\Res, then drag an asm file over Buildme.bat

(it needs \Masm32\bin\UAsm64.exe, all the rest should be available).