This routine will calculate the number of bytes in arguments passed to a VARARG function and call the function correcting the stack afterwards. My question is why the sub eax,52 ? I know that this can be done other ways but I was a bit interested and decided to figure it out.

callvararg PROTO C :DWORD,:VARARG


[color=red];###############
;example :
;[b]invoke callvararg, pFunction, Arg1, Arg2, Arg3, Arg4, .....[/b]
;###############[/color]

callvararg proc C pFunction:DWORD,args:VARARG
LOCAL cbParams :DWORD

mov eax,[ebp]
sub eax,ebp
sub eax,52
jns @F
add eax,44
@@:
mov [cbParams],eax

sub eax,4
@@:
push [ebp+eax+8]
sub eax,4
jnz @B

CALL [pFunction]

mov eax,[cbParams]
sub eax,4 ; did not use the first dword
add esp,eax

ret
callvararg endp
Posted on 2003-12-16 23:00:55 by donkey
Since the stack grows upward that you are being pointed back to the current stack pointer. The 56 is probably number of byte positions or divided by 4, the number of entries place in the stack by Windows before control is passed to your program.
Posted on 2003-12-16 23:16:11 by mrgone
But why always 56 if it is called from within a proc and 52 if called from outside a proc. It must have something to do with the way a stack frame is built. For example is there an extra DWORD that is pushed onto the stack if a procedure is called from within a proc ? If yes is it possible to know this at run-time ?
Posted on 2003-12-16 23:28:38 by donkey
Hmmm, changed it a bit because the numbers are different if called from within a stack frame. The changes will adjust for a call from within another procedure but also allow for a call from outside of a procedure. This is really weird.
Posted on 2003-12-16 23:45:54 by donkey
mov eax, ; = move the old value of ebp into eax
sub eax, ebp ; = difference between the old ebp and the current value in ebp
sub eax, 52 ; = this number differs, depending on whether you have local variables, preserved edi, esi etc. Also the parameters pushed onto the stack affects it. There the value you are subtracting from eax does depends on where you call it from etc.
Posted on 2003-12-17 01:17:48 by roticv
Actually Roticv, it remains the exact same if I have locals or not and the same if I preserve registers. The number is pretty much constant. I think it mainly depends on how many local variables and how many parameters are in the proc where it is called from. Anyway I think I'll give up for a while ;)
Posted on 2003-12-17 01:35:26 by donkey
52 is = return address+ old base pointer + 44
44 or 12 dwords are the maximun arguments passed.

the first two lines is for calculate the distance between frames.


Here you can have two options:

a)one that pass the limit 52 (as is 12 dwords + ret addr + o bp) and
b) one that the distance is less or equal to 52.



sub eax,52
jns @F
add eax,44
@@:
mov [cbParams],eax


with add eax, 44 you are calculating the number of arguments inside this, they can only be 0<arguments<12.
if the jump is taked, then I think the maximun numerb of args is 12, and are taked from ebp+eax+8


a explanation for a and b.


? let D be distance between stack frames a positive integer. (first to lines) 0<D< MAX
? check againt the limit 52 or the maximus of 12 args +

a) D > 52.
eg D=144
144 is (136+4+4) or (34 dwords or arguments+retadr+obp)
then 144-52 = 92 (not causes sign)
then = 92 or 23* dwordssee the diference between 144 and 92 = 52 or 13 dwords-1 is the maximun args, but is not aplied

b) 52> D > 0.
eg D = 32
32 is (24+4+4) or (11 dwords+retadr+obp)
then 32-52= -20 (cause sign)
then -20+44 = 24 or 11 dwords passed (the next 12 is the maximun = 36)
then = 24 or 11* dw's

now take the sub eax, 4. substract a dword marked with * from a)23 and b)11, you get 22 and 10 dwords this is the number of times that you will repush an argument......




That is what I can say, i not think is correct, specially when D>52, you can see the 12 dword(limit i think) diference in some parts of a, eg: 144 is (136+4+4) or (34 dwords and = 92 or 23* dwords because 23 minux a dword is 22, and 34 dwords- 22 dwords = 12 dwords, this characteristic is showed in the last line where i say: "but is not aplied", this diference of 12.

I think it will only run for stack frames like this:
obp stand for old base pointer












where not exist locals like this












___ebp

because the distance between the stack frames is: 8*4, then 32-52 = -20, then -20+44 = 24 or 6 dwords in or 20 ;)... is counting locals!!!! in my point of view sure...




I will draft a posible best.. solution...

First, you know that inside you will trash eax, then is not problem trash before... :D


then before thecallvar you need do:

mov eax, esp
invoke thecallvar, ptrfunct, sss,...
!!!!!!!!!!!!




funct thecallvar pFunction, args
local cbParams, dword
sizef 4 ;only one local :P

sub eax,ebp
mov [.cbParams],eax

sub eax,4
.pushOtherArgument:
push [ebp+eax+8] ;the +8 is for skip ret addr and obp
sub eax,4
jnz .pushOtherArgument

call dword[.pFunction]

mov eax,[.cbParams]
sub eax,4 ; did not use the first dword
add esp,eax
endfunct
this will function in nasm.

for masm Is the same:


callvararg proc C pFunction:DWORD,args:VARARG
LOCAL cbParams :DWORD

sub eax,ebp
mov [cbParams],eax

sub eax,4
@@:
push [ebp+eax+8] ;the +8 is for skip ret addr and obp
sub eax,4
jnz @B

call dword[pFunction]

mov eax,[cbParams]
sub eax,4 ; did not use the first dword
add esp,eax

ret
callvararg endp


I think it should work.

Nice day or night.
Posted on 2003-12-17 03:25:48 by rea