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.
fooBar proto stdcall
or
PROTO@0 TYPEDEF PROTO STDCALL
EXTERNDEF STDCALL fooBar:PROTO@0
or
EXTERNDEF STDCALL fooBar:PROTO
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
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:NEARtry the same with "extern".
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
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?
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
EXTERNDEF STDCALL fooBar:PROTO STDCALL
It's the second solution in one line.
Ah- so it lacked that "STDCALL" on the end.
That works.
So why is it necessary to use STDCALL twice?
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
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?!
Quotea bug with your original question
Can you elaborate?
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
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".
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...
works :bg
thanks for your help, Drizz!
I'm sure I'll be back.