PRB: Mixed Language Programs with Pascal 4.0 and C/C++ 7.0
PSS ID Number: Q106391
Article last modified on 02-01-1994

7.00

MS-DOS


----------------------------------------------------------------------
The information in this article applies to:

 - Microsoft C/C++ compiler for MS-DOS, version 7.0
 - Microsoft Pascal compiler for MS-DOS, version 4.0
----------------------------------------------------------------------

SYMPTOMS
========

To link objects created with Microsoft Pascal version 4.0 and
Microsoft C/C++ version 7.0 into the same executable, certain objects
must be removed from the standard Pascal library.

If these objects are not removed, the linker error "L2025: symbol
defined more than once" will occur on several symbols:

   libpase.lib(nmalloc.asm) : error L2025: __nfree :
       symbol defined more than once
   libpase.lib(nmalloc.asm) : error L2025: __nmalloc :
       symbol defined more than once
   libpase.lib(amalloc.asm) : error L2025: __amblksiz :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __aexit_rtn :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __asizds :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __astart :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __atopsp :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __cintDIV :
       symbol defined more than once
   libpase.lib(dos\crt0.asm) : error L2025: __amsg_exit :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __osversion :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: _errno :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __exit :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __child :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __nfile :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __cinit :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: ___argc :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __intno :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __dosvermajor :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __oserr :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: ___argv :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __dosverminor :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __osfile :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __osmode :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __pspadr :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __ovlvec :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __pgmptr :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __acfinfo :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __ovlflag :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __aintdiv :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __osmajor :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __osminor :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __umaskval :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __ctermsub :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __doserrno :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __fac :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: _exit :
       symbol defined more than once
   libpase.lib(dos\crt0dat.asm) : error L2025: __psp :
       symbol defined more than once

RESOLUTION
==========

The following LIB command removes the proper objects from the Pascal
library. The LIB utility creates a backup of the original library
called LIBPASE.BAK. Keep this backup copy for straight Pascal linking.

   lib libpase -crt0 -nmalloc -fmalloc -amalloc -pnmsize;

When using Microsoft C 6.0, this procedure is all that is necessary,
as stated in the Microsoft Knowledge Base article Q66458. However,
when using Microsoft C/C++ 7.0, there are additional errors because
C/C++ 7.0 adds an additional underscore to the beginning of the symbol
STKHQQ in order to be compliant with ANSI requirements for symbol
names in the run-time library.

Therefore, it is necessary to map the STKHQQ symbol that the Pascal
code is looking for to _STKHQQ. This can be accomplished by linking
the following assembly language module with the mixed language
program:

   ;
   ;   STKHQQ.ASM
   ;
   ;   Maps STKHQQ to _STKHQQ for mixing Pascal 4.0 and C/C++ 7.0.
   ;
   ;   To assemble with Microsoft MASM 6.x use:
   ;       ml /c STKHQQ.ASM
   ;
   ;   To assemble with Microsoft MASM 5.x use:
   ;       masm STKHQQ.ASM;
   ;

       .MODEL MEDIUM

       public STKHQQ

       .DATA

   EXTRN   _STKHQQ:WORD

   STKHQQ  dw _STKHQQ

       END

It is necessary to use LINK version 5.3 or later to link the mixed
language application because there are new object module formats in
the C/C++ run-time library that are not recognized by older versions
of the linker. LINK version 5.3 is shipped with Microsoft C/C++ 7.0.

The /NOD and /NOE switches must be used on the link command line, and
the C libraries must be listed before the Pascal library. In addition,
it is necessary to include the STKHQQ object module that was
previously assembled, for example:

   link /NOD /NOE c.obj pascal.obj stkhqq.obj,test.exe,,mlibce libpase;

MORE INFORMATION
================

The procedure described above is necessary because the newer libraries
that ship with C/C++ use different startup and memory handling
routines. The routines in the Pascal libraries don't work correctly
with the C/C++ libraries so the objects must be removed.

Remember when building your application to compile the C/C++ code with
/AM, /AL, or /AH option, because the Pascal code will require far
function calls.

Sample Code
-----------

/* CPAS.C
   Example of a C program calling a Pascal module.
*/
/* Compile Options needed: /c /AL
*/

   #include <stdio.h>

   /* Include the _pascal attribute for Pascal functions. */
   void _pascal maxparam( int _near *a, int _near *b );
   void _pascal swap( int _near *a, int _near *b );
   unsigned int _pascal fact( unsigned int n );

   void main( void )
   {
       int a = 3;
       int b = 5;

       printf( "\na = %d, b = %d", a, b );
       swap( &a, &b );
       printf( "\na = %d, b = %d", a, b );
       swap( &a, &b );
       printf( "\na = %d, b = %d", a, b );
       printf( "\nThe factorial of %d is %-5d", a, fact( a ) );
       printf( "\nThe factorial of %d is %-5d", b, fact( b ) );
       printf( "\nThe factorial of %d is %-5u", a+b,fact( a+b ) );
       maxparam( &a, &b );
       printf( "\na = %d, b = %d", a, b );
   }

/* PAS_MOD.PAS
   Example of a Pascal module called from a C program.
*/
/* Compile Options needed: /c
*/

   MODULE Psub;

   PROCEDURE MaxParam( VAR a:INTEGER; VAR b:INTEGER );
   BEGIN
     IF a > b THEN
       b := a
     ELSE
       a := b;
   END;

   PROCEDURE Swap( VAR a:INTEGER; VAR b:INTEGER );

   VAR temp:INTEGER;

   BEGIN
     temp := a;
     a := b;
     b := temp
   END;

   FUNCTION Fact( n: WORD ): WORD;

   VAR temp: WORD;

   BEGIN
     temp := 1;

     WHILE n > 0 DO
       BEGIN
         temp := temp * n;
         n := n - 1;
       END;

     Fact := temp;

   END;

   END.

----------------------------------------------------------------

/* CPP_PAS.CPP
   Example of a C++ program calling a Pascal module.
*/
/* Compile options needed: /c /AM
*/

   #include <iostream.h>

   /*
      Declare these as extern "C" so the C++ compiler won't
      decorate the function names. The Pascal
      compiler will not decorate the names.

      All data is considered _near to the Pascal compiler,
      therefore all pointer parameters that are passed must
      be declared _near or cast to _near in the C++ code.
   */
   extern "C"
   {
       void _pascal maxparam( int _near *a, int _near *b );
       void _pascal swap( int _near *a, int _near *b );
       unsigned int _pascal fact( unsigned int n );
   }

   void main( void )
   {
       int a = 3;
       int b = 5;

       cout << "\na = " << a << ", b = " << b;
       swap( (int _near*)&a, (int _near*)&b );
       cout << "\na = " << a << ", b = " << b;
       swap( (int _near*)&a, (int _near*)&b );
       cout << "\na = " << a << ", b = " << b;
       cout << "\nThe factorial of " << a << " is " << fact( a );
       cout << "\nThe factorial of " << b << " is " << fact( b );
       cout << "\nThe factorial of " << a + b << " is " << fact( a + b );
       maxparam( (int _near*)&a, (int _near*)&b );
       cout << "\na = " << a << ", b = " << b;

   }

/* PAS_MOD.PAS
   Example of a Pascal module called from a C++ program.
*/
/* Compile options needed: /c
*/

   MODULE Psub;

   PROCEDURE MaxParam( VAR a:INTEGER; VAR b:INTEGER );
   BEGIN
     IF a > b THEN
       b := a
     ELSE
       a := b;
   END;

   PROCEDURE Swap( VAR a:INTEGER; VAR b:INTEGER );

   VAR temp:INTEGER;

   BEGIN
     temp := a;
     a := b;
     b := temp
   END;

   FUNCTION Fact( n: WORD ): WORD;

   VAR temp: WORD;

   BEGIN
     temp := 1;

     WHILE n > 0 DO
       BEGIN
         temp := temp * n;
         n := n - 1;
       END;

     Fact := temp;

   END;

   END.

-------------------------------------------------------------

/* PAS_C.PAS
   Example of a Pascal program calling a C module.
*/
/* Compile options needed: /c
*/

   PROGRAM PascalCallC(Output);

   (*
      Add the [C] attribute to the external C functions.

      When calling the Fact() function, INTEGERs must be cast to
      WORDs because of Pascal's strict type-checking.
   *)
   PROCEDURE MaxParam( VAR a : INTEGER; VAR b : INTEGER ) [C]; EXTERN;
   PROCEDURE Swap( VAR a : INTEGER; VAR b : INTEGER ) [C]; EXTERN;
   FUNCTION  Fact( n : WORD ) : WORD [C]; EXTERN;

   VAR
     a : INTEGER;
     b : INTEGER;

   BEGIN
     a := 3;
     b := 5;

     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     Swap( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     Swap( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     WriteLn( 'The factorial of ', a:1, ' is ', Fact( WRD( a ) ):-5 );
     WriteLn( 'The factorial of ', b:1, ' is ', Fact( WRD( b ) ):-5 );
     WriteLn( 'The factorial of ', (a + b):1, ' is ', Fact( WRD( a + b ) ):-
5);
     MaxParam( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );

   END.

/* C_MOD.C
   Example of a C module called from a Pascal program.
*/
/* Compile Options needed: /c
*/

   void MAXPARAM(int _near *a, int _near *b)
   {
     if (*a > *b) {
       *b = *a;
     }
     else {
       *a = *b;
     }

   }

   void SWAP(int _near * a, int _near *b)
   {
     int temp;

     temp = *a;
     *a   = *b;
     *b   = temp;
   }

   unsigned int FACT(unsigned int n)
   {
     int temp;

     temp = 1;

     while (n > 0) {
         temp *= n;
         n--;
     };

     return(temp);

   }

   -------------------------------------------------------------

/* PAS_CPP.PAS
   Example of a Pascal program calling a C++ module.
*/
/* Compile Options needed: /c
*/

   PROGRAM PascalCallCPP(Output);

   (*
      Add the [C] attribute to the external C++ functions.

      When calling the Fact() function, INTEGERs must be cast to
      WORDs because of Pascal's strict type-checking.
   *)
   PROCEDURE MaxParam( VAR a : INTEGER; VAR b : INTEGER ) [C]; EXTERN;
   PROCEDURE Swap( VAR a : INTEGER; VAR b : INTEGER ) [C]; EXTERN;
   FUNCTION  Fact( n : WORD ) : WORD [C]; EXTERN;

   VAR
     a : INTEGER;
     b : INTEGER;

   BEGIN
     a := 3;
     b := 5;

     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     Swap( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     Swap( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );
     WriteLn( 'The factorial of ', a:1, ' is ', Fact( WRD( a ) ):-5 );
     WriteLn( 'The factorial of ', b:1, ' is ', Fact( WRD( b ) ):-5 );
     WriteLn( 'The factorial of ', (a + b):1, ' is ', Fact( WRD( a + b ) ):-
5);
     MaxParam( a, b );
     WriteLn( 'a = ', a:1, ', b = ', b:1 );

   END.

/* CPP_MOD.CPP
*/
/* Compile options needed: /c
*/

   /*
      Declare these as extern "C" so the C++ compiler won't
      decorate the function names. The Pascal
      compiler will not decorate the names.

      All data is considered _near to the Pascal compiler,
      therefore all pointer parameters that are passed must
      be declared _near or cast to _near in the C++ code.
   */
   extern "C"
   {
      void MAXPARAM(int _near *a, int _near *b);
      void SWAP(int _near * a, int _near *b);
      unsigned int FACT(unsigned int n);
   }

   void MAXPARAM(int _near *a, int _near *b)
   {
     if (*a > *b) {
       *b = *a;
     }
     else {
       *a = *b;
     }

   }

   void SWAP(int _near * a, int _near *b)
   {
     int temp;

     temp = *a;
     *a   = *b;
     *b   = temp;
   }

   unsigned int FACT(unsigned int n)
   {
     int temp;

     temp = 1;

     while (n > 0) {
         temp *= n;
         n--;
     };

     return(temp);
   }

Additional reference words: 4.00 7.00 7.00a Q66458 s_pascal
KBCategory: Prg
KBSubCategory:

=============================================================================

Copyright Microsoft Corporation 1994.
