Calling a MASM proc from C++ - how to get to the cstd lib?

Started by allynm, July 17, 2009, 07:54:42 PM

Previous topic - Next topic

allynm

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


allynm

No. I haven't tried CRT_printf.  Are you suggesting that this should go into the EXTERN statement? 

Thanks,

Mark

ToutEnMasm


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

MichaelW

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.
eschew obfuscation

MichaelW

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;
}

eschew obfuscation

allynm

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

allynm

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

allynm

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

MichaelW

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.


eschew obfuscation

Vortex

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]

allynm

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


hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Vortex

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.


allynm

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.