Go on z/OS - Group home

Calling a COBOL function from Go

  

You can use cgo functionality to execute a COBOL or PL/I function. The following example will show you how this can be achieved for a simple COBOL procedure, which is almost identical to PL/I.

Start with the following COBOL sample program to call the XDUMP function from Go.

CBL OPT(FULL),LIST
       ID DIVISION.
       PROGRAM-ID. 'XDUMP' recursive.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT Section.
       DATA DIVISION.
       FILE SECTION.

       Working-Storage Section.

       01 aprint.
         02 agroup.
           05 a0 pic X(16) VALUE '................'.  
           05 a1 pic X(16) VALUE '................'. 
           05 a2 pic X(16) VALUE ' !"#$%&''()*+,-./'. 
           05 a3 pic X(16) VALUE '0123456789:;<=>?'. 
           05 a4 pic X(16) VALUE '@ABCDEFGHIJKLMNO'. 
           05 a5 pic X(16) VALUE 'PQRSTUVWXYZ[\]^_'. 
           05 a6 pic X(16) VALUE '`abcdefghijklmno'. 
           05 a7 pic X(16) VALUE 'pqrstuvwxyz{|}~.'. 
           05 a8 pic X(16) VALUE '................'. 
           05 a9 pic X(16) VALUE '................'. 
           05 aa pic X(16) VALUE '................'. 
           05 ab pic X(16) VALUE '................'. 
           05 ac pic X(16) VALUE '................'. 
           05 ad pic X(16) VALUE '................'. 
           05 ae pic X(16) VALUE '................'. 
           05 af pic X(16) VALUE '................'. 
         02 atable redefines agroup pic x(256).

       01 eprint.
         02 egroup.
           05 e0 pic X(16) VALUE '................'.
           05 e1 pic X(16) VALUE '................'.
           05 e2 pic X(16) VALUE '................'.
           05 e3 pic X(16) VALUE '................'.
           05 e4 pic X(16) VALUE ' .........¢.<(+|'.
           05 e5 pic X(16) VALUE '&.........!$*);^'.
           05 e6 pic X(16) VALUE '-/........¦,%_>?'.
           05 e7 pic X(16) VALUE '.........`:#@''="'.
           05 e8 pic X(16) VALUE '.abcdefghi......'.
           05 e9 pic X(16) VALUE '.jklmnopqr......'.
           05 ea pic X(16) VALUE '.~stuvwxyz...[..'.
           05 eb pic X(16) VALUE '.............]..'.
           05 ec pic X(16) VALUE '{ABCDEFGHI......'.
           05 ed pic X(16) VALUE '}JKLMNOPQR......'.
           05 ee pic X(16) VALUE '\.STUVWXYZ......'.
           05 ef pic X(16) VALUE '0123456789......'.
         02 etable redefines egroup pic x(256).

       01 hprint.
         02 hgroup.
           05 h0 pic X(32) VALUE '000102030405060708090a0b0c0d0e0f'.
           05 h1 pic X(32) VALUE '101112131415161718191a1b1c1d1e1f'.
           05 h2 pic X(32) VALUE '202122232425262728292a2b2c2d2e2f'.
           05 h3 pic X(32) VALUE '303132333435363738393a3b3c3d3e3f'.
           05 h4 pic X(32) VALUE '404142434445464748494a4b4c4d4e4f'.
           05 h5 pic X(32) VALUE '505152535455565758595a5b5c5d5e5f'.
           05 h6 pic X(32) VALUE '606162636465666768696a6b6c6d6e6f'.
           05 h7 pic X(32) VALUE '707172737475767778797a7b7c7d7e7f'.
           05 h8 pic X(32) VALUE '808182838485868788898a8b8c8d8e8f'.
           05 h9 pic X(32) VALUE '909192939495969798999a9b9c9d9e9f'.
           05 ha pic X(32) VALUE 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf'.
           05 hb pic X(32) VALUE 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf'.
           05 hc pic X(32) VALUE 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf'.
           05 hd pic X(32) VALUE 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'.
           05 he pic X(32) VALUE 'e0e1e2e3e4e5e6e7e8e9eaebecedeeef'.
           05 hf pic X(32) VALUE 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'.
         02 xtable redefines hgroup pic x(512).

       Local-Storage Section.

       01 i      pic s9(9) usage is comp-5.
       01 j      pic s9(9) usage is comp-5.
       01 l      pic s9(9) usage is comp-5.
       01 dummy  pic s9(9) usage is comp-5.
       01 rem    pic s9(9) usage is comp-5.
       01 rcnt   pic s9(9) usage is comp-5.
       01 aarea  pic x(16).
       01 earea  pic x(16).
       01 xarea  pic x(32).
       01 xarea2 pic x(32).
      * big endian only
       01 convchar2int.
          05 chargroup.
             10 zz  pic x value x'00'.
             10 num1byte pic x.
          05 nval redefines chargroup pic s9(2) comp-5.

       Linkage Section.

       01 p      usage is pointer.
       01 cnt    pic 9(9) usage is comp-5.
       01 dat    pic x(65536).

       Procedure Division using by value p by value cnt.
       Begin. 
           display 'offset_____ 0_______ 4_______ 8_______ 12______ ',
                            'ASCII___________ ',  
                            'EBCDIC__________ '.
           move 0 to rcnt.
           move "@@@" to xarea2.
           set address of dat to p;
           move 0 to i.
           move i to l;
           perform with test after until i = cnt
             divide i by 16 giving dummy remainder rem end-divide
             move dat( 1 + i : 1) to num1byte
             move nval to j
             move atable( 1 + j : 1) to aarea( 1 + rem : 1) 
             move etable( 1 + j : 1) to earea( 1 + rem : 1) 
             move xtable( 1 + (j * 2) : 2 ) to xarea( 1 + (rem * 2) : 2)
             if (i > 0 ) and (rem = 15) then 
                if (xarea equal to xarea2) then
                   add 1 to rcnt
                else 
                   if rcnt greater than 0 then 
                     display ". . . . . . . . . >> " rcnt 
                             " lines same as above"
                     move 0 to rcnt
                   end-if
                   display l, ': ', xarea(1:8), ' ',
                        xarea(9:8), ' ',
                        xarea(17:8), ' ',
                        xarea(25:8), ' ',
                        aarea, ' ', earea
                end-if
                move xarea to xarea2
                add 16 to l;
             end-if 
             add 1 to i
           end-perform.
           if rcnt greater than 0 then 
             display ". . . . . . . . . >> " rcnt 
                     " lines same as above"
             move 0 to rcnt
           end-if
           if (rem not equal 15) then 
                initialize aarea( rem + 2:)
                initialize earea( rem + 2:)
                initialize xarea( 1 + (rem + 1) * 2 :)
                display l, ': ', xarea(1:8), ' ',
                        xarea(9:8), ' ',
                        xarea(17:8), ' ',
                        xarea(25:8), ' ',
                        aarea, ' ', earea
           end-if.
           goback.
       END PROGRAM 'XDUMP'.

Make DLL from COBOL

To make COBOL code accessible by the outside world, you need to make a library. Considering the source xd.cbl, make a DLL using the commands below:

cob2  -qexportall -c  xd.cbl                
cob2 -Wl,dll,lp64 -o XDDLL xd.o 

cob2 is the COBOL compiler here, and the result of above was two files, XDDLL and XDDLL.x . Having the XDDLL DLL, there are two ways to call its XDUMP function inside Go.

Dynamic Loading 1

package main

// #include <dlfcn.h>
// #include <_Nascii.h>
// #include <stdio.h>
// #include <stdlib.h>
// #include <string.h>
// int bar(const char * dllname, const char * funcname, const char * pliarg)
// {
//   int omode;
//   void *p = dlopen(dllname, RTLD_GLOBAL);
//   if (!p) {
//     dlerror();
//     fprintf(stderr, "cannot open %s\n", dllname);
//     return -1;
//   }
//   long (*f)(const char *str, int x) =
//       (long (*)(const char *, int))dlsym(p, funcname);
//   if (0 == f) {
//     dlerror();
//     fprintf(stderr, "cannot find %s\n", "foo");
//     dlclose(p);
//     return -1;
//   }
//   omode = __ae_thread_swapmode(__AE_EBCDIC_MODE);
//   f(pliarg, strlen(pliarg));
//   __ae_thread_swapmode(omode);
//   return 0;
// }
import "C"

func main() {
        C.bar(C.CString("XDDLL"), C.CString("XDUMP"), C.CString("Hello from GO. "))
}

Run this program using LIBPATH=. go run main.go. Here, LIBPATH refers to the location of our XDDLL library which in this case is the current directory. And in order to switch to EBCDIC mode before and after calling function f, omode = __ae_thread_swapmode(__AE_EBCDIC_MODE); and __ae_thread_swapmode(omode); are added respectively.

Dynamic Linking 2

The same function call can be done using Dynamic linking. The source code is as follows:

package main

// #cgo CFLAGS: -I${SRCDIR}
// #include <stdlib.h>
// #include <xdump.h>
import "C"

func main() {
        C.XDUMP(C.CString("Hello from GO. "), 15)
}

You need to create a header file(xdump.h) in the source directory as well:

#ifndef XDUMP_H
#define XDUMP_H
void XDUMP(const char * str, int len);
#endif

For simplicity, omit switching to EBCDIC and a wrapper. To run this program, you should explicitly let the compiler know about the DLL. For this purpose, you need to pass the XDDLL.x file as an option to it using the command below:

LIBPATH=. go run -ldflags="-extldflags 'XDDLL.x'" main.go

1 https://developer.ibm.com/tutorials/l-dynamic-libraries/#dynamic-loading-example

2 https://developer.ibm.com/tutorials/l-dynamic-libraries/#dynamic-linking-with-linux