C/MASM Relationship [they love each other] is better Static or Dynamic ?

Started by frktons, May 27, 2010, 09:45:35 AM

Previous topic - Next topic

frktons

After I create a Proc or Function in MASM, and I want to call it
from a C program, how do I manage that? Do I link the asm.obj
with the c.obj? Do I create a static funct.lib to include and reuse?
Is it better to have a DLL? When is it better?  Inline Assembly?
If the external MASM Proc/func uses 2 DD addresses of 2 strings
to manipulate,  a source and a target string I mean, how should I
declare the MASM proto function? How do I Assemble it?
Do I need a some.h file to include in the C program? Or I simple
have to declare the external function?

To be practical:

I have this small chunk of assembly that I use to reverse a 4 bytes
string

  mov edx, p_str         ; Pointer to first 4 bytes string - the one to reverse
  mov ebx, p_str1       ; Pointer to second 4 bytes string - the one that holds the result
  mov eax, [edx]
  bswap eax
  mov [ebx], eax


and a C program like this:


#include <stdio.h>

int main(void)
{
   char str1[5] = "ABCD";
   char str2[5] = "XXXX";

   printf("original string = %s\n",str1);
   reverse(&str1,&str2);
   printf("reversed string = %s\n",str2);
   getchar();

   return 0;
}


I didn't check the C syntax, so it is probably uncorrect, in case
let me know what please.

I think it can be done at least in 4 different ways:

1 - inline assembly   [do Pelle's C support it? I hope so]
      Could anyone show me how to do it?
2 - adding the proper MASM lines to the chunk I propose and
     compile without linking [how do I do both adding and compiling?]
     into an obj file to LINK to the main C program.
3 - using a new lib created with the obj file and LIB command
     to include during the linking process
4 - using a DLL

Please can you show me how do I perform these tasks using the
code I provided?

Thanks

Frank


P.S. well! it looks like Pelle's C has got an Inline Assembler:

#include <stdio.h>

int main(void)
{
   char str1[5] = "ABCD";
   char str2[5] = "XXXX";
   char *p_str1, *p_str2;
   
   p_str1 = str1;
   p_str2 = str2;

   printf("original string = %s\n",str1);

__asm

{
  mov edx, p_str1         ; Pointer to first 4 bytes string - the one to reverse
  mov ebx, p_str2         ; Pointer to second 4 bytes string - the one that holds the result
  mov eax, [edx]
  bswap eax
  mov [ebx], eax
}

   printf("reversed string = %s\n",str2);
   getchar();

   return 0;
}

:P

The only annoying thing is the exe of 30 Kb.
In MASM if should be less than 3 Kb I guess.
What about the other possibilities?

Frank

 
Mind is like a parachute. You know what to do in order to use it :-)

clive

Generally I would do it statically, unless the code was really big. The advantage of a .LIB or a .OBJ is that the linker will only pull the functions it needs, so ideally you'd break down the functions one to an OBJ. Or use the CL command line option -Gy to separate functions for the linker.

If I'm prototyping something, or it doesn't need an ASM file, I'd use inline'd code.

rev_c.c
// ml -c -coff -Fl rev_a.asm
// cl -c -Ox rev_c.c
// link rev_a.obj rev_b.obj

// ml -c -coff -Fl rev_a.asm
// cl -Ox rev_c.c rev_a.obj

// cl -c -Ox rev_c.c
// ml -coff -Fl rev_a.asm rev_c.obj

#include <windows.h>

#include <stdio.h>
#include <stdlib.h>

extern void __cdecl TestRev1(char *, char *);
extern void __stdcall TestRev2(char *, char *);

int main(int argc, char **argv)
{
char str1[5] = "ABCD";
char str2[5] = "XXXX";

TestRev1(str1, str2);
TestRev2(str1, str2);

return(1);
}


rev_a.asm
.486

.MODEL FLAT

.CODE

TestRev1 PROC C USES ebx, p_str:PTR BYTE, p_str1:PTR BYTE
mov edx, p_str         ; Pointer to first 4 bytes string - the one to reverse
mov ebx, p_str1       ; Pointer to second 4 bytes string - the one that holds the result
mov eax, [edx]
bswap eax
mov [ebx], eax
ret
TestRev1 ENDP

TestRev2 PROC STDCALL USES ebx, p_str:PTR BYTE, p_str1:PTR BYTE
mov edx, p_str         ; Pointer to first 4 bytes string - the one to reverse
mov ebx, p_str1       ; Pointer to second 4 bytes string - the one that holds the result
mov eax, [edx]
bswap eax
mov [ebx], eax
ret
TestRev2 ENDP

END


To create a .LIB, use LIB to make the library and pull in the .OBJs you want. The use FOO.LIB in the command line to the C compiler, MASM, or LINK.

lib /OUT:foo.lib rev_a.obj
cl -Ox rev_c.c foo.lib


To create a DLL, you will need to add a DllMain function, and create a .DEF file containing the EXPORTS. Then using the linker the output will be a .DLL, .LIB and .EXP

revdll_a.asm
; ml -c -Fl -coff revdll_a.asm
; link revdll_a.obj /OUT:revdll.dll /DEF:revdll.def \masm32\lib\kernel32.lib

                .486
                .MODEL FLAT, STDCALL

GetModuleHandleA PROTO :DWORD

NULL                    equ     0

FALSE                   equ     0
TRUE                    equ     -1

DLL_PROCESS_DETACH      equ     0
DLL_PROCESS_ATTACH      equ     1
DLL_THREAD_ATTACH       equ     2
DLL_THREAD_DETACH       equ     3

                .DATA

hInstance       dd      ?

                .CODE

                align   4

TestRev1 PROC C USES ebx, p_str:PTR BYTE, p_str1:PTR BYTE
                mov     edx, p_str      ; Pointer to first 4 bytes string - the one to reverse
                mov     ebx, p_str1     ; Pointer to second 4 bytes string - the one that holds the result
                mov     eax, [edx]
                bswap   eax
                mov     [ebx], eax
                ret
TestRev1 ENDP

                align   4

TestRev2 PROC STDCALL USES ebx, p_str:PTR BYTE, p_str1:PTR BYTE
                mov     edx, p_str      ; Pointer to first 4 bytes string - the one to reverse
                mov     ebx, p_str1     ; Pointer to second 4 bytes string - the one that holds the result
                mov     eax, [edx]
                bswap   eax
                mov     [ebx], eax
                ret
TestRev2 ENDP

                align   4

_DllMainCRTStartup       PROC public handle:dword, reason:dword, reserved:dword

    .if reason == DLL_PROCESS_ATTACH
      invoke GetModuleHandleA,NULL   ; use the calling application's instance handle
      mov hInstance, eax            ; so that the icon from the caller can be used.

      mov eax, TRUE                 ; put TRUE in EAX to continue loading the DLL

    comment * ====================================
      These can be used if there is thread specific
      information that needs to be set up as the DLL
      is called.

    .elseif reason == DLL_THREAD_DETACH
    .elseif reason == DLL_THREAD_ATTACH
              ================================== *

    .elseif reason == DLL_PROCESS_DETACH
      ; ---------------------------------------
      ; perform any exit code you require here
      ; ---------------------------------------

    .endif

                ret

_DllMainCRTStartup       ENDP

                END     _DllMainCRTStartup


revdll.def
LIBRARY revdll

EXPORTS

TestRev1
TestRev2



ml -c -Fl -coff revdll_a.asm
link revdll_a.obj /OUT:revdll.dll /DEF:revdll.def \masm32\lib\kernel32.lib
cl -Ox rev_c.c revdll.lib
It could be a random act of randomness. Those happen a lot as well.

frktons

That's great Clive, thanks a lot  :bg

Very clear example.  :U

Looking forward for your examples for LIB and DLL, that I cannot understand actually.

Frank

P.S. As I guessed, the MASM32 version [thanks Hutch] is 3 Kb more or less:


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

.data

str1    db 'ABCD',0
str2    db 'xxxx',0

.code

start:

  mov edx, offset str1         ; Pointer to first 4 bytes string - the one to reverse
  mov ebx, offset str2         ; Pointer to second 4 bytes string - the one that holds the result
  mov eax, [edx]
  bswap eax
  mov [ebx], eax

  print offset str1,13,10
  print offset str2,13,10
  inkey
  exit
 
END start



If I don't use <stdio.h> and use the print MASM32 MACRO probably the
size of the EXE would be a lot smaller. What do you think?

Frank
Mind is like a parachute. You know what to do in order to use it :-)

Ghandi

Because you are writing something simple, you can specify to the linker to ignore the runtime library and also where the entrypoint of the program is.


/IGNORE:libcmt.lib /ENTRY:Main


As long as you only use C functions from dynamic runtime (msvcrt.dll) and other system dlls, you will have an executable which is free from bloat. Another option as well is to roll your own runtime by taking the functions you need from the VC source (it ships with every copy of VC++ afik) and compiling them into your own C library.

HR,
Ghandi

frktons

Quote from: Ghandi on May 27, 2010, 01:28:08 PM

As long as you only use C functions from dynamic runtime (msvcrt.dll) and other system dlls, you will have an executable which is free from bloat.

HR,
Ghandi

Do you mean "as long as I use only win32 API"?

Mind is like a parachute. You know what to do in order to use it :-)

hutch--

Frank,

MSVCRT is a dynamic C runtime library, its just been around for so long that its now a "known" DLL. If you are really chasing the speed difference and that is if it exists, the static libraries may be faster but MSVCRT will certainly give you a much smaller file and probably lose little if any to the static libraries. If you were writing portable code it would matter but the window design is specifically API so paying the price of portability does not make sense here.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

frktons

Quote from: hutch-- on May 27, 2010, 02:36:49 PM
Frank,

MSVCRT is a dynamic C runtime library, its just been around for so long that its now a "known" DLL. If you are really chasing the speed difference and that is if it exists, the static libraries may be faster but MSVCRT will certainly give you a much smaller file and probably lose little if any to the static libraries. If you were writing portable code it would matter but the window design is specifically API so paying the price of portability does not make sense here.

:U
Mind is like a parachute. You know what to do in order to use it :-)

Arash

there is a way to make exe file smaller inside PellesC
here is simlple example:

#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "msvcrt.lib");

int main()s
{
    __asm{
        nop
        nop
    }
    printf("hello\n");
    return 0;
}

void __cdecl mainCRTStartup(void)
{
    exit(main());
}

frktons

Quote from: Arash on May 27, 2010, 08:13:11 PM
there is a way to make exe file smaller inside PellesC
here is simlple example:

#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "msvcrt.lib");

int main()s
{
    __asm{
        nop
        nop
    }
    printf("hello\n");
    return 0;
}

void __cdecl mainCRTStartup(void)
{
    exit(main());
}


Well, probably

int main()s


should be

int main()


Anyway Pelle's C doesn't find the lib:

POLINK: fatal error: File not found: 'msvcrt.lib'.

::)

P.S. well I copied the msvcrt.lib from the vc2010 folder into Pelle's lib folder
and it works ok. Now the size is about 3 Kb.  :U
Mind is like a parachute. You know what to do in order to use it :-)

Vortex

You need to create msvcrt.lib  It does not come with the Pelles C installation.

frktons

Quote from: Vortex on May 27, 2010, 09:08:31 PM
You need to create msvcrt.lib  It does not come with the Pelles C installation.

Right! Read my previous post  :U

I'd like to ask what these directives are:

#pragma comment(lib, "msvcrt.lib");
....
void __cdecl mainCRTStartup(void)
{
    exit(main());
}
.....


and this chunk is just to say we can put some asm here?

{
    __asm{
        nop
        nop
    }


and last but not least, why the program freaks out if I put a

getchar();

after the printf ?

Thanks

Frank
Mind is like a parachute. You know what to do in order to use it :-)

GregL

frktons,

Pelle's C has it's own C Run-Time Library, but as you found out you can use Microsoft's library.


frktons

Quote from: Greg Lyon on May 27, 2010, 09:15:34 PM
frktons,

Pelle's C has it's own C Run-Time Library, but as you found out you can use Microsoft's library.


Yes Greg, I figured it out  :U

Quote from: frktons on May 27, 2010, 09:11:53 PM
Quote from: Vortex on May 27, 2010, 09:08:31 PM
You need to create msvcrt.lib  It does not come with the Pelles C installation.

Right! Read my previous post  :U

I'd like to ask what these directives are:

#pragma comment(lib, "msvcrt.lib");
....
void __cdecl mainCRTStartup(void)
{
    exit(main());
}
.....


and this chunk is just to say we can put some asm here?

{
    __asm{
        nop
        nop
    }


and last but not least, why the program freaks out if I put a

getchar();

after the printf ?

Thanks

Frank

What's happening in this prog? why is it 3 Kb instead of 30 Kb?
The includes for standard libraries are still there but...  ::)
Mind is like a parachute. You know what to do in order to use it :-)

clive

With a MASM32 install

#pragma comment(lib, "\\masm32\\lib\\msvcrt.lib")

Works with MSVC, executable 16KB, Microsoft doesn't like the trailing semicolon.

The #pragma stuffs a comment field in the object file, which the linker then takes as a parameter. ie LINK .. \masm32\lib\msvcrt.lib

The exit(main()); emulates the level above main() in the CRT which you don't normally see. I don't however care for it's command line parsing. The regular run time will actually parse the command line and environment.
It could be a random act of randomness. Those happen a lot as well.

Arash

QuoteWell, probably
Code:
int main()s

should be
Code:
int main()

yes you right, this is my mistake while i copy/paste code here  :bg

QuoteAnyway Pelle's C doesn't find the lib:

well this source is from a little project that i write many years ago,
so i forgot what kinda file i copied to PellesC folder to making this code work :U

QuoteI'd like to ask what these directives are:

it's re-define mainCRTStartup that run befor main function

Quoteand this chunk is just to say we can put some asm here?

yes

Quoteand last but not least, why the program freaks out if I put a
Code:
getchar();
after the printf ?

it seem PellesC define some macro for getchar in stdio.h that make this problem

#define getchar()     (__filetab[0]->ptr < __filetab[0]->getend ? *__filetab[0]->ptr++ : (fgetc)(__filetab[0]))


it can be solve with this code, this make PellesC to use getchar inside msvcrt.lib


#include <stdio.h>
#include <stdlib.h>
#undef getchar


Arash