News:

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

mixing calling conventions, calling C functions

Started by skippyV, March 14, 2011, 09:37:10 PM

Previous topic - Next topic

skippyV

Trying to call a C function from assembly with specific calling convention (stdcall).
Assembly file has type C so there is no problem calling "printf" but when I
added a C definition with the stdcall naming convention - I can't get it to
link.  Assuming some syntax error.
Not sure if I should upload the files or just paste the code...
Its a small example posted on msdn that I tweaked.

// main.c
#include <stdio.h>
#include <stdlib.h>

int __stdcall euclid(int, int);
void usage(char*);
void __stdcall fooBar();

int main(int argc, char* argv[])
{
  int i, j;

  if (argc<3)
    usage(argv[0]);

  i = atoi(argv[1]);
  j = atoi(argv[2]);

  if (!(i>0) || !(j>0)) {
    printf("%s %d %d\n", argv[0], i, j);
    usage(argv[0]);
  }

  printf("The greatest common divisor of %d and %d: %d\n", i, j, euclid(i, j));
  return 0;
}

void usage(char* str)
{
    printf("usage: %s num1 num2\n", str);
    printf("  for non-zero, positive num1 and num2\n");
    exit(1);
}

void __stdcall  fooBar()
{
   printf("Foo Bar \n");
}

;;; euclid.asm file
.386
.model flat, c

.data
msg db "Hello Skippy",0
.code

INCLUDELIB MSVCRT
EXTRN   printf:NEAR


extern  STDCALL fooBar:NEAR  ; Can't get this to work


euclid PROC STDCALL x:SDWORD, y:SDWORD
  mov ecx, x
  mov eax, y
@@:
  cmp ecx, eax
  jge noswap
  xchg eax, ecx
noswap:
  sub ecx, eax
  jnz @B
; return value already in eax
;;;;;;;;;;;;;;;;;; MY INSERT
push eax
mov eax, offset msg
push eax
call printf
call fooBar
pop ecx
pop eax
;;;;;;;;;;;;;;;;;;
  RET
euclid ENDP

end
;;; end of asm file

I'm using Visual Studio 2008.  Thanks for advice however I
know there are other ways to skin this cat.


drizz

fooBar proto stdcall
   
or
   
PROTO@0 TYPEDEF PROTO STDCALL
EXTERNDEF STDCALL fooBar:PROTO@0

or

EXTERNDEF STDCALL fooBar:PROTO


The truth cannot be learned ... it can only be recognized.

skippyV

The 2nd suggestion resulted in an assembler error: "symbol language attribute conflict : fooBar".
However your 1st suggestion worked fine.  I didn't realize it was necessary to use the @N... decoration.
I thought specifying the calling convention would remove that need.

As for externdef - the documentation states one should use it to be more "flexible" but when used with
a module just referencing the call - it amounts to the same as using extern.

I'll play with it more later - gotta go - but thank you for your advice! :bg

drizz

Quote from: skippyV on March 14, 2011, 10:11:57 PM
The 2nd suggestion resulted in an assembler error: "symbol language attribute conflict : fooBar".
No error here
.686
.model flat,c
option casemap:none

PROTO@0 TYPEDEF PROTO STDCAL
EXTERNDEF STDCALL fooBar:PROTO@0

.code

somefunc proc
call fooBar
invoke fooBar
lea eax,fooBar
ret
somefunc endp

end


It's equivalent to this C code:
typedef void (__stdcall *PROTOat0(void));
extern PROTOat0 __stdcall fooBar;


Quote from: skippyV on March 14, 2011, 10:11:57 PM
I didn't realize it was necessary to use the @N... decoration.

It's just a name, you can skin the cat with @N naming also, like this:

EXTERNDEF C fooBar@0:NEAR; no need for "C" if c is default language

or

EXTERNDEF SYSCALL _fooBar@0:PROC


default language (calling convention)  is specified with ".model flat, <lang>" or using "option language:<lang>"

stdcall decoration is: _ sym @ args*4
c decoration is: _ sym
syscall decoration is: sym

Quote from: skippyV on March 14, 2011, 10:11:57 PM

As for externdef - the documentation states one should use it to be more "flexible" but when used with
a module just referencing the call - it amounts to the same as using extern.
"externdef" does not create dependency if symbol is not used, "extern" forces the definition of a symbol.

if you add this line to your asm file it will compile and link:
EXTERNDEF bogusFunction:NEAR
try the same with "extern".




The truth cannot be learned ... it can only be recognized.

jj2007

I got it working, more or less:

Foo Bar
Now invoking main:

Arg count=3
Argv 0=A little text as arg 0?
Argv 1=111
Argv 2=222
Argv 3=This is arg 3
i=111
j=222
Hello Skippy
Foo Bar
The greatest common divisor of 111 and 222: 111

OK from Assembler


Here is how main can be called:
.code
start:
call fooBar ; just for fun
; invoke usage, chr$("MyString") ; useless here but would work fine
print "Now invoking main:", 13, 10, 10
mov ebx, esp
if 1
invoke main, 3, esp, chr$("My text"), chr$("11"), chr$("22"), chr$("This one not used, can be commented out")
elseif 1 ; same but more explicit
push chr$("One more arg")
push chr$("200")
push chr$("100")
push chr$("My text")
push esp
push 3
call main
add esp, 24
elseif 0 ; no good, GPF
invoke main, 3, chr$("The text"), chr$("1000"), chr$("2000"), chr$("3000")
endif
sub ebx, esp
inkey str$(ebx), " stack difference"
exit

jj2007

It works fine, except with JWasm and/or the Microsoft linker. In other words, it works only if you use the Microsoft Assembler and PoLink...

Jwasm chokes over call fooBar (but invoke usage, chr$("MyString") works just fine).
Masm link.exe says error LNK2001: unresolved external symbol _usage@4

Here is what the macro creates:
_fooBar@0 PROTO SYSCALL
icCall1 TYPEDEF PROTO
icCall1 PTR _fooBar@0

_usage@4 PROTO SYSCALL
icCall2 TYPEDEF PROTO :DWORD
icCall2 PTR _usage@4


Any ideas?

skippyV

thanks for responding, jj2007.  But for this effort I'm just sticking with MS and C code, not C++.

Drizz, I had time to review your post more carefully now and realize that I responded incorrectly.
QuotefooBar proto stdcall
   
or
   
PROTO@0 TYPEDEF PROTO STDCALL
EXTERNDEF STDCALL fooBar:PROTO@0

or

EXTERNDEF STDCALL fooBar:PROTO
The first 2 solutions worked fine for me. It was the 3rd one
EXTERNDEF STDCALL fooBar:PROTO
that resulted in the following error:
error A2192:symbol language attribute conflict : fooBar
However I am satisfied with the 1st solution and find it aesthetically pleasing  :bg
It removes the "work" of decorating the name with the number of argument bytes.
Thanks again!
p.s. please excuse my newbie attempt in formatting this post

drizz

EXTERNDEF STDCALL fooBar:PROTO STDCALL
It's the second solution in one line.
The truth cannot be learned ... it can only be recognized.

skippyV

Ah- so it lacked that "STDCALL" on the end.
That works.
So why is it necessary to use STDCALL twice?

drizz

Good question. I would say it's a bug with stdcall since all other calling conventions do not require it.

.686
.model flat
option casemap:none

EXTERN C fooBar1:PROC
EXTERN STDCALL fooBar2:PROC
EXTERN SYSCALL fooBar3:PROC
EXTERN PASCAL fooBar4:PROC

.code
main proc
ret
main endp
end



: error LNK2001: unresolved external symbol _fooBar1
: error LNK2001: unresolved external symbol _fooBar2 <-- same as c
: error LNK2001: unresolved external symbol fooBar3
: error LNK2001: unresolved external symbol FOOBAR4


EDIT:

Actually it's a bug with your original question or possibly debatable if "EXTERN STDCALL fooBar:NEAR" should produce _foobar@0

"proto <language>" is for invoke
"extern <language>" is for symbol
The truth cannot be learned ... it can only be recognized.

skippyV

One more question - still not clear.
When I change fooBar to take an input variable I'm unable to tweak your suggestions correctly.
It results in linker error:
unresolved external symbol _fooBar@0
Even though I changed the declaration to:
PROTO@4 TYPEDEF PROTO STDCALL
EXTERNDEF STDCALL fooBar:PROTO@4

Does the syntax of the call change?!

skippyV

Quotea bug with your original question
Can you elaborate?

drizz

PROTO@4 TYPEDEF PROTO STDCALL [<ARGUMENTNAME>]:<TYPE>
EXTERNDEF STDCALL fooBar:PROTO@4

or

fooBar proto stdcall [<ARGUMENTNAME>]:<TYPE>

[] means optional

so:

PROTO@4 TYPEDEF PROTO STDCALL :DWORD
EXTERNDEF STDCALL fooBar:PROTO@4
The truth cannot be learned ... it can only be recognized.

drizz

Quote from: skippyV on March 15, 2011, 03:19:34 PM
Quotea bug with your original question
Can you elaborate?

extern  STDCALL fooBar:NEAR

It's either a bug or by design that this line produces symbol without "@0".
The truth cannot be learned ... it can only be recognized.

skippyV

I'm getting it.
So using the 1st form you suggested - and with 2 input variable, it changes to
fooBar proto stdcall :DWORD, :DWORD
which works.
Now I will change it to return a value...