Problem calling C program

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?

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

      }

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

  • > 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.

  • 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

  • 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.

  • 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?

  • Try changing:

    "Buffer says hello %s".

    to

    z"Buffer says hello %s".

  • 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.

  • 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.

  • 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.

  • Verified Answer

    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.