Highlighted
Absent Member.
Absent Member.
5381 views

Problem calling C program

Jump to solution

Environment is 64-bit AIX, Visual COBOL 2.1 but compiling on the command line.

I have a simple COBOL program calling a simple C program. The COBOL program is compiled as 64-bit. The C program is, as far as I am aware, compiled as 64-bit - I have used the compilation option -maix64 and the loader option -b64. Running the program generates the error "253 Cannot load file - unsupported format". I have also tried compiling the COBOL program as 32-bit but with the same result. A previous question in the forum about this same result was apparently due to a 64-bit COBOL program calling a 32-bit C program, but I think I've eliminated that.

To eliminate anything else, I edited the two programs down to absolute basics (the return 19 in the C program is so I can check that a value is definitely being returned):

       program-id.                     testc.

       special-names.

           call-convention 0 is aix-c.

       working-storage section.

       01  ws-name                     pic x(4) value "fred".

       01  ws-return-val               pic s9(9) comp-5.

       procedure division.

       a-control section.

           move zero                   to ws-return-val

           call aix-c "helloworld"     using ws-name
                                       returning ws-return-val

           display "Returned " ws-return-val

           goback
           .

#include <stdio.h>

int
main( int argc, char **argv )
{

printf( "Hello %s\n", argv[1] );

return( 19 );

}


So have I missed something blindingly obvious?

0 Likes
1 Solution

Accepted Solutions
Highlighted
Visitor.

RE: Problem calling C program

Jump to solution

Going back to your original description, I suspect the problem is that you're trying to link (and then CALL) a C program with a main() function. Try renaming your main() function to something else (like "helloworld"). The name of the C source file is somewhat irrelevant - it's just used by the C compiler to provide a name for the object file that is in turn linked into your final executable or shared library. In your COBOL program you CALL "helloworld", so that should be the name of the C function that gets called.

View solution in original post

0 Likes
10 Replies
Highlighted
Micro Focus Frequent Contributor
Micro Focus Frequent Contributor

RE: Problem calling C program

Jump to solution

Hi

just copy the below into doit.sh on the AIX machine.

In a temporary directory

make sure cobsetenv has been run

. ./doit

>>>>>>> doit.sh

JAVA_HOME=/usr/java6

export JAVA_HOME

PATH=$HOME/bin:$JAVA_HOME/bin:/sbin:/usr/sbin:$PATH

export PATH

LANG=en_US

export LANG

TERM=vt220

export TERM

#

java -version

#

COBMODE=32

export COBMODE

#

#. /home/products/vcdevhub22/bin/cobsetenv

#

#

cat >testc.cbl <<EOF

      program-id.                     testc.

      working-storage section.

      01  ws-name                     pic x(5) value "fred" & x'00'.

      01  ws-return-val               pic s9(9) comp-5.

      procedure division.

      a-control section.

          move zero                   to ws-return-val

          call "helloworld" using ws-name

                            returning ws-return-val

          display "Returned " ws-return-val

          goback

          .

EOF

#

# the c program

#

cat >helloworld.c <<EOF

#include <stdio.h>

#include <stdlib.h>

#include <malloc.h>

#include <ctype.h>

#include <unistd.h>

#include <string.h>

int helloworld(char *dptr_char)

{

   printf("In C helloworld\n");

   int result = 0;

   if(strcmp(dptr_char, "abcdefghijklmnopqrst") == 0)

   {

           printf("char * passed correctly <%s>\n", dptr_char);

   }

   else

   {

           printf("char * failed <%s>\n", dptr_char);

           result = result + 1000;

   }

   return(result);

}

EOF

#

# compile and link

#

cob -xvV testc.cbl helloworld.c

#

./testc

#

# end

#

>>>>>>>>> end doit.sh

Some extra stuff

Notes

I attach some c examples interfacing with cobol which might help you?

when I mix C and COBOL development I use CDT eclipse plugin this does not do remote development.

Then just new > select wizard C/C++ convert to a C/C++ project (adds C++ nature)

This will find libraries.

I use cygwin on windows or mingw to give me access to the c header files and gcc.

Not brilliant but a start for the IDE to work, but the compile is actually done on the AIX machine and uses theheader files on the unix machine.

I use the cobol compiler on the AIX machine to compile the c code.

I would compile the c files by adding them to the additional link files.

This would then get them compiled via the cob command.

Some examples come with the product

/home/products/vcdevhub22/demo/cobol/c-cobol >ls

ccob1.sh      cmain1.h      cobfunc1.cbl  cobsub1.cbl   csub1.c       cxxcob1.sh    cxxsub1.C

cmain1.c      cobc1.sh      cobmain1.cbl  cobsub2.cbl   ctypes.cpy    cxxmain1.C    README.txt

If you wanted just c programs built into your application do

Properties>Micro focus COBOL> Build configurations>link>additional link items

And add the *.c programs here.

The examples above in the product need to be libraries so I would do this to make libraries.

Properties>Micro focus COBOL>Build configurations>events>

Use the pre build command event line.

cob -z,CC -v cxxsub1.C -e ""

cob -zv csub1.c

exit 0

you can stop the build here by using exit 1.

Here is a simple example of c calling cobol and cobol calling c

Create your remote project.

Ccallcobolcallc

Cut and paste the 3 files below.

Add the c files to the additional link files

Properties>Micro focus COBOL>COBOL>

set your entry point to

main

Clean build.

>>>>>>>>>>>>>>>

     *> This sets the 2 byte return code from c

     $set rtncode-size"2"

      program-id. acceptingfromc as "acceptingfromc".

      environment division.

      configuration section.

      data division.

      working-storage section.

      01  int           pic s9(9)  comp-5 is typedef.

     $IF P64 set

     77  long          pic s9(18) comp-5 is typedef.

     $ELSE

      77  long          pic s9(9)  comp-5 is typedef.

     $END

      77  d-float                  comp-2 is typedef.

      77  float                    comp-1 is typedef.

      01  i             pic s9(9)  comp-5.

      01  strlength     pic s9(9)  comp-5.

      01  ptrStr        pointer    value null.

          01 cobol-int      int.

          01 cobol-long     long.

      01 cobol-double   d-float.

          01 cobol-float    float.

     *> this is known a c string array of char x'00' terminated

          01 cobol-char     pic x(25) value all x"00".

      01 result pic 9(4) comp-x.

      01  USQ1-DBENV            PIC X(32)    VALUE "BM_DB ".

      01  USQ1-DBNAME           PIC X(32).

      01  USQ1-DBUSER           PIC X(32).

      01  USQ1-SIGNON           PIC X(100).

      LINKAGE SECTION.

     *>

     *> show how to accept a integer and a array of pointers

     *> The main key to this is using the set address

     *> basically have to convert a cobol pointer to a c pointer

    *>

      01 argc           usage int.

      01 argv           usage pointer.

     *>

     *> when using set address need to declare variables inside

     *> the linkage section.

     *>

      01 lkStr          pic x      any length.

      01 lkPtrTB.

         02 lkPtr       pointer    occurs 99.

      PROCEDURE DIVISION using by reference argc

                               by reference argv.

     main section.

      main-010.

       display " In COB acceptingfromc:main"

          perform init

          perform work

          perform fini

          .

      main-090.

          goback.

      init section.

      init-010.

          display " In COB acceptingfromc:init"

         display 'ARGUMENTS GIVEN: ' argc

          set address of lkPtrTB to argv

          perform varying i from 2 by 1 until i > argc

              move           lkPtr(i) to ptrStr

              set address of lkStr    to ptrStr

              if  address of lkStr not = null

                  call "strlen" using        lkStr

                                returning    StrLength

                  if StrLength > 100

                     display "COB cobcmd <" StrLength

                             "> string <" lkStr(1:100) ">"

                             "... (" StrLength  " BYTES)"

                  else

                     display "COB cobcmd <" StrLength

                             "> string <" lkStr(1:StrLength) ">"

                  end-if

              end-if

          end-perform

          .

      init-090.

          exit.

      work section.

      work-010.

          display " In COB acceptingfromc:work"

     *> pasing the simple types acroos

          move 9999 to cobol-double

     *> pass one error across check return code in result

          move 101 to cobol-long

          move 10.5 to cobol-float

          move -10 to cobol-int

          move "abcdefghijklmnopqrst" & x"00" to cobol-char

          call "csub1" using by value cobol-int

                             by reference cobol-float

                             by reference cobol-long

                             by reference cobol-char

                             by reference cobol-double

                       returning result

          display "Result from csub1 <", result, ">"

          .

      work-090.

          exit.

      fini section.

      fini-010.

          display " In COB acceptingfromc:fini"

          move "username1" & x"00" to USQ1-DBUSER

          move z"mydbname" to USQ1-DBNAME

          string "//"        delimited by size

                 USQ1-DBENV  delimited by space

                 "/"         delimited by size

                 USQ1-DBNAME delimited by x"00"

                QUOTE       delimited by size

                 USQ1-DBUSER delimited by x"00"

                 QUOTE       delimited by size

                 x"00"       delimited by size

                 INTO USQ1-SIGNON

     *> These strings must be x'00' terminated

     *> just for testing USQ1-DBENV is not x"00" terminated

          call "MCC807" using by reference USQ1-DBENV

                              by reference USQ1-DBNAME

                              by reference USQ1-DBUSER

                              by reference USQ1-SIGNON

                       returning result

          display "Result from MCC807 <", result, ">"

          .

      fini-090.

          exit.

      end program acceptingfromc.

<<<<<<<<<<<<

>>>>>>>>>>>

/*

* acceptingfromcob.c

*

*  Created on: 16 Oct 2012

*      Author: tonyt

*/

#include <stdio.h>

#include <stdlib.h>

#include <malloc.h>

#include <ctype.h>

#include <unistd.h>

#include <string.h>

int csub1(int a_int, float *bptr_float, long *cptr_long, char *dptr_char, double *eptr_double)

{

   printf("In C acceptingfromcob:csub1\n");

       int result = 0;

   if(a_int == -10)

   {

           printf("int passed correctly <%d>\n", a_int);

   }

   else

   {

       printf("int failed <%d>\n", a_int);

           result = result + 1;

   }

   if(*bptr_float == 10.5)

   {

           printf("float passed correctly <%f>\n", *bptr_float);

   }

   else

   {

           printf("float failed <%f>\n", *bptr_float);

           result = result + 10;

   }

   if(*cptr_long == 10)

   {

           printf("long passed correctly <%d>\n", *cptr_long);

   }

   else

   {

           printf("long failed <%d>\n", *cptr_long);

           result = result + 100;

   }

   if(strcmp(dptr_char, "abcdefghijklmnopqrst") == 0)

   {

           printf("char * passed correctly <%s>\n", dptr_char);

   }

   else

   {

           printf("char * failed <%s>\n", dptr_char);

           result = result + 1000;

   }

   if(*eptr_double == 9999)

   {

           printf("double passed correctly <%lf>\n", *eptr_double);

   }

   else

   {

           printf("double failed <%lf>\n", *eptr_double);

       result = result + 10000;

   }

   return(result);

}

int MCC807(char *cptr_env, char *cptr_name, char *cptr_user, char *cptr_signon)

{

   printf("In C acceptingfromcob:MCC807\n");

         int result = 0;

   printf("env <%s>\n", strndup(cptr_env, 32));

   printf("name <%s>\n", cptr_name);

   printf("user <%s>\n", cptr_user);

   printf("signon <%s>\n", cptr_signon);

   return(result);

}

/**

*** test the customer using cobcall to access this entry point

*** InitCDBaseRate

**/

#define PROGRAMID_SIZE                  8

#define PARAGRAPHID_SIZE                20

typedef struct

error_struct

{

  char sProgramID[PROGRAMID_SIZE] ;           /* Pgm or Module where error occured*/

  char sParagraphID[PARAGRAPHID_SIZE] ; /* Section of code*/

}

ERROR_STRUCT;

#define BANK_ID_SIZE                         4

#define CURRENT_USER_SIZE                    4

typedef struct control_struct

{

  char sBankId[BANK_ID_SIZE];

  char sCurrentUser[CURRENT_USER_SIZE];

  char *pInstancePtr;

}

CONTROL_STRUCT;

/**

*** they have dll_export = _export ? problem with __export

*** they have DLL_CALLBACK _far and _pascal but the endif in declare is not correct ?

**/

#define DLL_EXPORT

#define DLL_CALLBACK

#if 0

typedef enum status

{

  EXCEPTION = (-1),

  SUCCESS = 0 ,

  FAILURE = 1 ,

  WARNING = 2

}

STATUS;

#endif

#ifndef STATUS

#define STATUS int

#endif

#ifndef EXCEPTION

#define EXCEPTION (-1)

#endif

#ifndef SUCCESS

#define SUCCESS 0

#endif

#ifndef FAILURE

#define FAILURE 1

#endif

#ifndef WARNING

#define WARNING 2

#endif

STATUS DLL_EXPORT DLL_CALLBACK InitCDBaseRate(ERROR_STRUCT *pErrorDetails, CONTROL_STRUCT *pControl)

{

   void *pInstance;

   printf("\nInside InitCDBaseRate...\n");

/**

*** lets just display what we have passed across

**/

   printf("perrordetails <%s>\n", strndup(pErrorDetails, sizeof(ERROR_STRUCT)));

   printf("pControl <%s>\n", strndup(pControl, sizeof(CONTROL_STRUCT)));

   printf("pControl : %p...\n",pControl);

   printf("pControl->pInstancePtr : %p...\n",pControl->pInstancePtr);

   printf("returning from InitCDBaseRate");

   return EXCEPTION;

}

>>>>>>>>>>>>>

/*

* passingtocob.c

*

*  Created on: 16 Oct 2012

*      Author: tonyt

*/

#include <stdio.h>

#include <stdlib.h>

#include <malloc.h>

#include <ctype.h>

#include <unistd.h>

#include <string.h>

  #define MAXPARAMS 2

  main(int argc, char *argv[])

  {

      int i;

      printf("In C passingtocob:main\n");

      /**

      *** Now we need to initialise the Cobol system before we

      *** start making any calls to Cobol.

      **/

      cobinit();

      /**

      *** Just print the data passed to program from command line

      **/

      for (i = 1; i < argc; i++)

          printf("C main length <%d> string <%s>\n", strlen(argv), strndup(argv+0, 100));

      printf("\n");

      char *pcFunction;

      pcFunction="acceptingfromc";

      /**

      *** acceptingfromc has two linkage parameters a interger"argc" a array of pointers to string"argv"

      *** just showing this to show what cobcall is actually doing and expecting.

      **/

      void *acceptingfromcinputarray[2];

      acceptingfromcinputarray[0]=&argc;

      acceptingfromcinputarray[1]=&argv;

      cobcall(pcFunction, 2, acceptingfromcinputarray);

      char *inputarray[3] = {"dummyprogname", "string1", "string2"};

      void *ptrinputarray = &inputarray;

      for (i = 0; i < 3; i++)

          printf("inputarray string len <%d> string <%s>\n", strlen(inputarray), strndup(inputarray+0, 100));

      printf("\n");

      i = 3;

      acceptingfromcinputarray[0]=&i;

      acceptingfromcinputarray[1]=&ptrinputarray;

      cobcall(pcFunction, 2, acceptingfromcinputarray);

      /**

      *** Just call the program not using cobcall

      **/

      acceptingfromc(&argc, &argv);

    /**

      *** To the customers problem using cobcall to call a c routine

      **/

       long lRv = 0;

       char *ppParameter[MAXPARAMS];

       int iParameterCount;

       pcFunction="InitCDBaseRate";

       iParameterCount=2;

             printf("\nInside testcobcall...well sort of actually in <%s>\n", argv[0]);

       ppParameter[0]="8765432112345678901234567890";

       ppParameter[1]="43211234xxxxxxxx";

       printf("pControl : %p...\n",ppParameter[1]);

       lRv = cobcall(pcFunction, iParameterCount, ppParameter);

       printf("lRv <%d>\n", lRv);

      /**

      *** Now we just need to de-initialise the Cobol system.

      **/

      cobtidy();

      char ans;

      printf("press enter to exit>\n");

      ans = getchar();

      printf("Bye\n");

      return 0;

  }

>>>>>>>>>>>>>>>

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Problem calling C program

Jump to solution

> I use cygwin on windows or mingw to give me access to the c header files and gcc.

Please note, that using a different C compiler that the one our product is created will result in multiple C runtime DLLs being in memory and this can cause problem.  We build our Windows product with the Microsoft C compiler.

0 Likes
Highlighted
Micro Focus Frequent Contributor
Micro Focus Frequent Contributor

RE: Problem calling C program

Jump to solution

As this was a AIX question I am assuming that the customer is using Visual COBOL for eclipse.

Hence the need to use the unix type headers

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Problem calling C program

Jump to solution

Understood but the statement is still true with regards the statement:

> I use cygwin on windows or mingw to give me access to the c header files and gcc.

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Problem calling C program

Jump to solution

I'm using gcc on the AIX machine for C compilations - we don't have IBM's cc and given that we have the grand total of one C program, it's not very likely that we'll be getting it. The programs compile successfully as per original versions, though I have made two minor changes to testc - null byte to terminate ws-name and "by reference" in the call to helloworld. helloworld works quite happily when invoked standalone. I tried putting in an additional call to sprintf

this in working-storage

      01  ws-to-buff                  pic x(80).

      01  ws-line-desc.

          03  filler                  value "Buffer says hello %s".

and this in procedure division

          call "sprintf"              using by reference ws-to-buff

                                            by reference ws-line-desc

                                            by reference ws-name

          display ws-to-buff

and this works. So is it an incompatibility with gcc?

0 Likes
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: Problem calling C program

Jump to solution

Try changing:

"Buffer says hello %s".

to

z"Buffer says hello %s".

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Problem calling C program

Jump to solution

I think you've missed the point. It runs perfectly as is, including correctly displaying the returned string from sprintf, and fails on calling helloworld.

0 Likes
Highlighted
Visitor.

RE: Problem calling C program

Jump to solution

Going back to your original description, I suspect the problem is that you're trying to link (and then CALL) a C program with a main() function. Try renaming your main() function to something else (like "helloworld"). The name of the C source file is somewhat irrelevant - it's just used by the C compiler to provide a name for the object file that is in turn linked into your final executable or shared library. In your COBOL program you CALL "helloworld", so that should be the name of the C function that gets called.

View solution in original post

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Problem calling C program

Jump to solution

That looks like being the answer - the next problem is how to actually link the C program with the function "helloworld" into testc. I'm proceeding from a point of ignorance as the system I'm working on has always been compiled to .ints and .gnts and has never incorporated calls to C routines directly, and the AIX linker doesn't seem to play particularly nicely with anything.

0 Likes
Highlighted
Absent Member.
Absent Member.

RE: Problem calling C program

Jump to solution

In case it's of any helpt o anyone else in the future, the answer, as Chuck says, is that the function "helloworld" should be named rather than having a function "main". I don't know whether my approach to actually making that loadable is the best one, but it works. Compile the C program into an .so file with the options -shared -Wl,-G and make sure it's somewhere that's on $LIBPATH:

gcc -maix64 -shared -Wl,-G helloworld.c -o helloworld.so

0 Likes
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.