Hello eveyone:
I have a begnner's question regarding how to call a MASM32 proc from a c++ calling program.
The C++ caller includes the usual stdio.h header. It also contains a function prototype for the asm function. The prototype includes an EXTERN keyword along with the function prototype.
The MASM function has a call to _printf (the C printf() ) function we all know and love.
The C++ program compiles successfully from the command line. However, MASM emits a message from the assembler that the _printf sysmbol is undefined. I have been assuming that the include stdio.h in the C++ program would take care of bringing in the appropriate cstdlib functions. I also understand that the MASM function code must somehow resolve the reference to _printf. The question is: What directive has to be included in the MASM file in order to resolve the _printf call.
I have tried to use EXTERN in the MASM file but it doesn't work, at least as I have coded it. My code reads EXTERN _printf. What am I missing?
Experience with NASM tells me that some kind of EXTERN statement is necessary. But to this point I haven't got it figured out.
Best regards,
Mark Allyn
Have you tried crt_printf?
No. I haven't tried CRT_printf. Are you suggesting that this should go into the EXTERN statement?
Thanks,
Mark
Have a look at the msvcrt.inc
Quote
externdef _imp__printf:PTR c_msvcrt
crt_printf equ <_imp__printf>
include msvcrt.inc
includelib msvcrt.lib
The MASM32 package includes an include file and import library for msvcrt.dll, and in these to avoid various naming conflicts the function names are prefixed with "crt_". A minimal example:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\kernel32.inc
include \masm32\include\msvcrt.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
fmt db "%xh%c",0
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
myproc proc
invoke crt_printf, ADDR fmt, 12345678h, 10
ret
myproc endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
call myproc
invoke Sleep, 2000
ret
end start
BTW, if your goal is to create an object module from the procedure you have no need for any "main" code, so the only thing after the procedure definition should be an end directive.
This time I'll try to provide an answer that is more likely to be useful. I am not set up for C++, so I tested with a C source, and to make it simple I specified the C calling convention for the procedure and used a minimal prototype for printf.
Assemble this to an object module:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
printf PROTO C args:VARARG
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
fmt db "%xh%c"
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
myproc proc C
invoke printf, ADDR fmt, 12345678h, 10
ret
myproc endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end
And add the object module to the CL command line when you compile this:
#include <windows.h>
void myproc();
int main(void)
{
myproc();
getch();
return 0;
}
Hi MichaelW-
I can't thank you enough for your help on this problem. Code and life being what they are, i have no doubt that further questions will arise, but this has been a really great way to start a weekend.
Compared to the parallel process in NASM the MASM approach has more code lines. With NASM once you have the C/C++ code compiled there is just a single extern statement that goes into the code segment and that is the end of it. Mind you, I'm not asserting that NASM is superior in general, as experience suggests that few things are always and forever better. But, for a beginner trying to get the hang of asm in any dialect, the NASM is easier to grasp.
Put differently, I would never have gotten to your code on my own.
kind regards,
Mark Allyn
Hi Michael W-
I just looked more closely at your second batch of code and if I read it correctly all the includes/includlibs go away and are replaced by just the code in the PROTO statement. If so, I retract my comment about the verbosity of MASM compared to NASM for this particular problem.
Mark Allyn
Hi again, Michael W.-
I coded up the material you sent me exactly as you sent it. I wound up getting error messages regarding undefined references, one for _printf and the other for myproc_. In addition, there was an error message regarding an undefined symbol, namely to _printf.
So, I'm thinking there is more code needed in order to fix this problem. Of course, I may not be compiling correctly.
The command I used was :
cl exmpl_caller.c firstpgm.obj .
There may be some missing options/switches I needed to set?? Or, there may be some includes in the asm program to msvcrt that also need to be added??
Regards,
Mark Allyn
In my second post the files were named test_asm.asm and test.c, and here is the batch file that I used to assemble, compile, and link:
if exist "test_asm.obj" del "test_asm.obj"
\masm32\bin\ml /c /coff "test_asm.asm"
pause
set PATH=C:\Program Files\Microsoft Visual C++ Toolkit 2003\bin;%PATH%
set INCLUDE=C:\Program Files\Microsoft SDK\include;%INCLUDE%
set LIB=C:\Program Files\Microsoft SDK\lib;%LIB%
cl /Fm test.c test_asm.obj
pause
I added the /Fm switch in case a map file might be useful. You will need to ensure, as a minimum, that the PATH and LIB environment variables are correct for your system. And for this source the:
#include <windows.h>
Is not necessary, and I'm fairly certain that no include files are necessary.
Hi allynm,
Here is a quick example of calling a Masm function from C++. All the trick is to specify the EXTERN "C" statement to declare your external functions.
#include <windows.h>
extern "C" {
void __stdcall Mbox(char *, char*);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
char *Msg="Masm function called from MS VC++";
char *Capt="Hello";
Mbox(Msg,Capt);
return 0;
}
[attachment deleted by admin]
Hi Vortex -
Thanks for the code. I will try it out. Could you kindly elaborate on the _stdcall keyword. What does it do that is different from _cdecl? That is question 1. Question 2: are these two keywords used only for C++? Vanilla C doesn't seem to care about them. It may be that one or the other is the default option in Microsoft's compiler so we generally are not aware of them. Question 3: GCC doesn't seem to know about these keywords either. GCC may have its own version of them as opposed to Microsoft.
Thanks for the help.
Mark Allyn
Mark,
The difference between the _cdecl and _stdcall is an important one. The C calling convention can handle variable parameter counts but at the cost that the calling app must correct the stack after the call has returned. The STDCALL convention passes a set number of arguments to a procedure and the stack is corrected at the end of the called procedure, not by the caller.
Normall C code uses the C calling convention but if you call a direct Windows API it must be prototyped as STDCALL. Microsoft C and MASM both handle the two calling conventions so its a matter of correctly matching them up and using correct prototypes if you write your own in either language.
Hi Mark,
__stdcall means that the the function follows the STDCALL calling convention. The _cdecl conventions expects that the main application is responsible for the stack balancing while STDCALL functions are doing this before returning the control to the main application.
The two keywords are also used by Masm like the following :
__stcall -> STDCALL , Example : .model flat, stdcall
_cdecl -> C , Example : .model flat, c
__stdcall and _cdelc can be used with C.
Hi to Michael -
I tried running your last batch of code, including the environment setting commands. When I ran the cl command I got back an error message stating that the myproc_ symbol was still undefined.
Taking a cue from you I investigated the environment setting commands on my system. There are departures from the Program Files from what you entered. Specifically:
1. for the path setting, the program file that comes closest for me is \Program Files\Microsoft visual studio 9.0\SDK\v3.5\bin. However, the bin file has nothing in it on my system. No mention anywyhere of Visual C++ Toolkit 2003\bin
2. The include and lib set commands appear to refer to the following:
c:\Program Files\microsoft SDKs\Windows\v6.0A\bin\ ----in which directory I find folders for Lib and Include. These folders are populated.
If you still have patience enough to carry on, I would be delighted. But, I can understand if you choose not to....
Regards,
Mark A.
Mark,
There should be no "myproc_" symbol. With the underscore added by ML the symbol should be "_myproc". If you are in doubt as to what symbol names are in an object module, library, exe, etc open it in a text editor and take a look. Most of it will not be readable, but the symbol names, strings, and similar will be.
I did everything from a single directory, and here is a listing:
07/17/2009 04:17p 744 test_asm.asm
07/18/2009 12:56a 322 makeit.bat
07/18/2009 12:54a 110 test.c
07/18/2009 12:56a 555 test.obj
07/18/2009 12:56a 28,149 test.map
07/18/2009 12:56a 40,960 test.exe
07/18/2009 12:56a 332 test_asm.obj
Which C/C++ compiler are you using, or want to use, and where on your system is it?
Hi Michael -
I neglected to mention that all my files are in the same directory. I examined the obj file for the ml command and it shows _myproc listed in exactly this manner. I checked the obj file for the c caller after invoking cl and it shows myproc as myproc_.
_printf is no longer the problem as far as linking is concerned. That symbol appears to be defined satisfactorily. Makes me think that there may be something wrong with the prototype in the calling function. That prototype is currently:
external void myproc( );
I'd really like to solve this problem for the reason that there are many many standatd c functions I'd like to be able to call. I know there are similar calls in the MASM macros (like "print" and "input") but I can find no documentation on them.
You will find the documentation for the masm32 macros in the high level help file that comes with masm32. You can use the C runtime functions if you need them but they are generally large and force the inclusion of other code that make your app much larger.
Hi Hutch -
Thanks. I did find them just now.
mark
Quote from: hutch-- on July 19, 2009, 01:04:36 PMYou can use the C runtime functions if you need them but they are generally large and force the inclusion of other code that make your app much larger.
Can you give an example?
include \masm32\include\masm32rt.inc
.code
hw db "Hello World at 1024 bytes", 13, 10, 0 ; with polink.exe (2560 bytes with link.exe)
start:
invoke crt_printf, chr$("%s"), offset hw
exit ; short form of invoke ExitProcess, 0
end start
Hi Hutch -
Thank you for the code. I will test it immediately. I take your point as regards the size of the app. This would certainly be disadvantageous under many circumstances if an alternative existed. In looking thru the Macros you to which you alerted me, it appears that for a lot of calls there would be no justification for using the c equivalents.
I am still working on the problem of calling from a c/c++ main caller, as you can see from my previous comment.
Mark
We could provide better answers if we knew which C/C++ compiler you are using, or want to use, and where on your system it is.
Quote from: allynm on July 19, 2009, 10:56:13 PM
... as regards the size of the app.
Don't worry about the size. My example above calls printf and assembles at 1024 bytes, of which roughly 30 bytes are related to the crt call.
Quote
I am still working on the problem of calling from a c/c++ main caller, as you can see from my previous comment.
As MichaelW wrote, it would be good to know what exactly you are using. Have you thought of assembling your code as a dll? In one of my apps, I call Win32 assembly code from a trusty 16-bit/Win 3.1 Basic dialect. I guess this should be even easier from a 32-bit C app...
Hello Hutch -
I did code your program using the masm32rt.inc include instruction. Not surprisingly it ran just fine. BUT, when I assembled it using ml /c /coff command the compiler sent back messages that I don't understand and was hoping you could clarify. First, it gives the following warning:
warning a4011: multiple .model directives found; .model ignored
and it also identifies 5 different duplicate include files:
1. windows.inc
2. masm32.inc
3. gdi32.inc
4. user32.inc
5. kernel132.inc
I tested what happens if these inc files are removed and the answer is that I get a blizzrd of error messages.
As I say, the code compiles and runs just fine, but I'd like to elliminate these warnings.
Regards,
Mark A.
MArk,
Actually have a look at the masm32rt.inc file as it shows you what you need to have for a MASM source code. It does the normal include files with WINDOWS.INC first then the library files.
From your error messages you have duplicates in the .MODEL directive and the includes. If you are setting your own include files and libraries, don't them use the masm32rt.inc file as this will give you duplicates.
Hutch -
Thanks as always for your help on this. You folks are extraodinarily generous with your assistance. Wish I could do something in return. If fhere is, let me know.
Mark
money is always nice, Mark - send money
Mark,
Have no fear, once you are up to speed we will get our pound of flesh with your support for other new members. :bg
Hutch -
I've been playing around with the include files trying to get rid of the duplicates that occur when I include masm32rt.inc in the code. If I DON'T include masm32rt then I appear to be missing some basic defines like STDOUT, etc. I don't know which other include files contain these defines but the little bit of code I am writing in the code section requires them....Also, if I DON't include explicitly the windows.inc file then I get a flurry of error messages, even though that file appears to be referenced in the masm32rt.inc file. The other files previously mentioned besides windows.inc can be scrapped and nothing goes awry. And, getting rid of them gets rid of the problem of duplicates and the code runs fine. But, there is something special about the windows.inc file that is essential. Doesn't make sense to me.
Regards,
Mark Allyn
Hi Hutch again-
Back to this business of including masm32.rt. If this is the ONLY include file I use then everything works like a clock. I should have paid attention to what you and Michael and several others had done and included only this file (and also no .model and .486 directives either). I thought in their code snippets they were assuming that the other includes were already present in my file and were suggesting that I ADD the additional masm32rt.inc file.
I should have looked inside the masm32rt.inc in the first place. i would have seen that the needed msvcrt.inc and lib files were already included. Beginner's ignorance, I guess.
But, to your point about what i can contribute as far as helping other novices is concerned: The first thing I would do is direct their attention to what is inside masm32rt. And, I suppose that the next thing I would do is direct them to the Demos in the Tutorial folder and tell them to NOT include all the files they see there.....
Best regards,
Mark Allyn
it has everything you need usually
i have had occasion where i needed to use advapi32 (to use the registry functions) - so i add that to my assembly file
sometimes, you may have to bump up the .x86 number
if you use instructions like cpuid or rdtsc, i think the minimum is .586
in my template file, i use these lines, then remove the semicolons if needed
INCLUDE \masm32\include\masm32rt.inc
; .586
; .686
; .MMX
; .XMM
; INCLUDE \masm32\include\advapi32.inc
; INCLUDELIB c:\masm32\lib\advapi32.lib
; INCLUDE \masm32\macros\timers.asm
Hi everyone -
SUCCESS! I finally got the llittle asm proc that calls a c stdlib function (crt_printf) to work when called from a C caller. Man oh man...
Here were the decisive additions.
1. In the asm file I had to add the PUBLIC directive (PUBLIC <procname>)
2. In the function prototype in the C caller I had to use the following expression:
extern void __stdcall myproc( ); ; double underscore prefix to stdcall
So....in the future, when some green novice shows up with my question (see the thread start) you may direct them to me for assistance. This way Michael, Hutch, Dedndave, and a number of others of you can attend to more pressing matters.
Best regards to all
Mark Allyn
Quote from: allynm on July 22, 2009, 08:27:10 PM
SUCCESS! I finally got the llittle asm proc that calls a c stdlib function (crt_printf) to work when called from a C caller. Man oh man...
Here were the decisive additions.
1. In the asm file I had to add the PUBLIC directive (PUBLIC <procname>)
Congrats :U