News:

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

Main Menu

Help, problems in procedure with variable number of arguments

Started by x64Core, June 24, 2012, 03:27:03 PM

Previous topic - Next topic

x64Core

hello guys :)
I'm trying finish a code, it's a procedure with variable number of arguments, also I wanna link it with a C project, but for example,
I did this tiny code, and I cannot assemble it... could anyone help me  :bgrin:


asm:

.386
.model flat,stdcall
include \masm32\include\masm32rt.inc

MyVarFunction PROTO C :DWORD,:VARARG

defparam1 equ [ebp+08h]
defparam2 equ [ebp+0ch]
defparam3 equ [ebp+10h]
defparam4 equ [ebp+14h]
defparam5 equ [ebp+18h]

.code

MyVarFunction proc C param1:DWORD,ParamVar:VARARG

push ebp
mov ebp,esp

mov eax,defparam2
mov ebx,defparam3
mov ecx,defparam4
mov edx,defparam5

invoke MessageBox,eax,ebx,ecx,edx


mov eax,defparam1
mov edx,04h
mul edx ; eax = 4*nparams

mov esp,ebp
pop ebp
ret         eax ; <--- ERROR

MyVarFunction endp
end


how to would be correct? also I want to return a value in EAX


c:

#include <windows.h>
extern  unsigned long MyVarFunction(DWORD param1,...);
int main()
{

MyVarFunction(4,0,"my test","tittle",MB_OK);

return 0;
}


in c file i think it's correct :P

jj2007

Your code assembles fine if

- you use ret, not ret eax (there is no magic other register or whatever value that could be returned, so ret = return something in eax)
- you insert start: and end start

Now to pass a variable number of args, you need to tell the proggie how many you have passed. That is the purpose of the first arg (the last one that got pushed).
In assembler, the invoke macro takes care of adjusting the stack afterwards, if you use the C calling convention.
Below a complete example. If you don't have MasmBasic, replace the deb stuff with your own macro (but make sure ecx is not being trashed).

include \masm32\MasmBasic\MasmBasic.inc

.code
MyVarFunction proc C uses esi param1:DWORD,ParamVar:VARARG
lea esi, param1
lodsd
deb 4, "# args", eax
xchg eax, ecx
.Repeat
lodsd
deb 4, "Arg", eax
dec ecx
.Until Zero?
ret
MyVarFunction endp

start: invoke MyVarFunction, 3, 11, 22, 33
invoke MyVarFunction, 5, 11, 22, 33, 44, 55
inkey "bye"
exit
end start


Output:
# args  eax             3
Arg     eax             11
Arg     eax             22
Arg     eax             33
# args  eax             5
Arg     eax             11
Arg     eax             22
Arg     eax             33
Arg     eax             44
Arg     eax             55
bye

x64Core

hi jochen, thanks you buddy :)
now I have a problem with the linking :P I already many changes to the code and I can not see the problem :P

main.obj : error LNK2001: unresolved external symbol "unsigned long __cdecl MyVarFunction(unsigned long,...)" (?MyVarFun
ction@@YAKKZZ)
main.exe : fatal error LNK1120: 1 unresolved externals



I'm just assemble it to get the obj file, then I move the obj file to path C project,  I pass the obj in the VC linker :P

qWord

You must disable CPP's name mangling (or compile your project as C).
http://blogs.msdn.com/b/oldnewthing/archive/2012/05/25/10310148.aspx
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

RHL,

When you use the C calling convention, you normally pass an argument count then the arguments. The procedure you call must be designed to handle the variable number of arguments. The reason why you only use RET is because the code that does the calling must balance the stack after the call.

In MASM notation you would have this basic logic.


invoke MyCfunc,argcount,arg1,arg2,arg3 etc ....


The MASM procedure then is like this.


MyCfunc proc C argcount:DWORD,args:VARARG

    ; you code that evaluates the arg count etc ....

    ret

MyCfunc endp

x64Core

thanks for the replys

qWord: My C project has a .C file... not Cpp :P  I tried that,  but did not work , qWord

hutch: I changed the code, hutch, but still I can not compile it

my current code:

.C
#include <windows.h>

extern unsigned long MyVarFunction(DWORD,...);
int main()
{
MyVarFunction(4,0,"my test","tittle",MB_OK);
return 0;
}


.asm

.386
.model flat,stdcall
include \masm32\include\masm32rt.inc

MyVarFunction PROTO C :DWORD,:VARARG

defparam1 equ [ebp+08h]
defparam2 equ [ebp+0ch]
defparam3 equ [ebp+10h]
defparam4 equ [ebp+14h]
defparam5 equ [ebp+18h]

.code

MyVarFunction proc C param1:DWORD,ParamVar:VARARG

push         ebp
mov ebp,esp

mov eax,defparam2
mov ebx,defparam3
mov  ecx,defparam4
mov edx,defparam5

invoke MessageBox,eax,ebx,ecx,edx

mov esp,ebp
pop ebp
ret

MyVarFunction endp
end

main.obj : error LNK2001: unresolved external symbol "unsigned long __cdecl MyVarFunction(unsigned long,...)" (?MyVarFunction@@YAKKZZ)


I have two-three hours trying  to compile my code, and still I can not :( lol



Vortex

Try this one :

extern "C" unsigned long MyVarFunction(DWORD,...);

MichaelW

Testproc.asm (with the test code components commented out):

;==============================================================================
;include \masm32\include\masm32rt.inc
.386
.model flat,stdcall

;==============================================================================
.data
.code
;==============================================================================
;-----------------------------------------------------------------------------
; This derived from an old 16-bit example in the MASM 6.0 Programmer's Guide.
;
; The code prefixed with ";;" is generated by MASM, included here to show
; what the default prologue and epilogue are adding to the procedure.
;-----------------------------------------------------------------------------
addup proc C argCount:DWORD, args:VARARG
    ;;push ebp
    ;;mov ebp, esp
    xor eax, eax          ; zero work reg
    xor edx, edx          ; zero arg index
    mov ecx, argCount
    test ecx, ecx
    jnz  @F
    ;;leave
    ret                   ; return if zero args
  @@:
    add eax, args[edx*4]  ; add eax,[ebp+edx*4+12]
    add edx, 1            ; adjust to next arg
    sub ecx, 1
    jnz @B
    ;;leave
    ret

addup endp
;==============================================================================
start:
;==============================================================================

comment |
    invoke addup, 0
    printf("%d\n", eax )
    invoke addup, 1, 1
    printf("%d\n", eax )
    invoke addup, 2, 1, 2
    printf("%d\n", eax )
    invoke addup, 3, 1, 2, 3
    printf("%d\n", eax )
    invoke addup, 4, 1, 2, 3, 4
    printf("%d\n", eax )

    inkey
    exit
|
;==============================================================================
;end start
end

test.c:

#include <windows.h>
#include <conio.h>
#include <stdio.h>

extern int addup(int argCount, ... );

int main( void )
{
    printf("%d\n",addup(0));
    printf("%d\n",addup(1,1));
    printf("%d\n",addup(2,1,2));
    printf("%d\n",addup(3,1,2,3));
    printf("%d\n",addup(4,1,2,3,4));
    getch();
}

And the batch file that I used to compile:

set file="test"
set PATH=C:\Program Files\Microsoft Visual C++ Toolkit 2003\bin;%PATH%
set INCLUDE=C:\Program Files\Microsoft SDK\include;C:\Program Files\Microsoft Visual C++ Toolkit 2003\include;%INCLUDE%
set LIB=C:\Program Files\Microsoft SDK\Lib;C:\Program Files\Microsoft Visual C++ Toolkit 2003\lib;%LIB%

cl /W4 /FA %file%.c testproc.obj
pause


Well Microsoft, here's another nice mess you've gotten us into.

qWord

It is obviously that you compile it as C++. If you are using visual studio, you must use the /TC command line switch to force C.
MREAL macros - when you need floating point arithmetic while assembling!

x64Core

 :shock:

thanks a lot Vortex, I think it was referring qWord  :redface: thanks people!  :biggrin:

EDIT: Thanks MichaelW for your example!

EDIT: oh! now I understand what he meant, hutch :O  I'm sorry guys but sometimes I do not understand  :lol:

hutch--

One more thing, you do not need to limit yourself to .386 code unless you are writing code for a very old 386 processor. If your code is for any modern Windows version, use this.


.686p
.mmx
.xmm


This will allow you to use any instruction in any version of MASM.

jj2007

In case that doesn't work, here a very simple example of C code named jjCalc.cpp that can be linked to assembler.

commandline:
"%ProgramFiles%\Microsoft Visual Studio 10.0\VC\bin\cl.exe" /c /Oty2 /Zl /EHsc /Gs /GS- /FojjCalc.obj /Fa jjCalc.cpp

extern "C"
int _fltused=1; //get rid of a stupid error message

extern "C"
int _stdcall jjMult(int x , int y)
{
  return(x*y);
}


(from \masm32\MasmBasic\MB2C\CalcTest.asc)

NoCforMe

Quote from: hutch-- on June 24, 2012, 06:37:04 PM
When you use the C calling convention, you normally pass an argument count then the arguments.

There are exceptions to this, of course. One notable one would be an implementation of sprintf(), where the number of arguments after the first two (the receiving buffer and the format string) depends on the number of fields found in the format string (each field, such as "%s", should have an argument pushed on the stack).

Other than that, what you said.

GregL

Quote from: qwordIf you are using visual studio, you must use the /TC command line switch to force C.


Or change the file extension to .c.  If the file extension is .c (versus .cpp), it forces visual studio to C mode also.