Compiling, Linking and Language Environment

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), which says nothing about compilers, linkers or run-time options, so everything here is implementation-defined; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2, whose compile, bind and run steps and Language Environment (LE) run time are described; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every program on it was compiled with cobc -x -Wall and run on 2026-10-20 (and again with -std=ibm, which produced the same results). z/OS examples are labelled "z/OS; not run here" and are checked against the Programming Guide and the Language Environment manuals.

A COBOL source file is not a program. It becomes one in a chain of tools: the compiler turns the source into an object module, a binder (linkage editor) joins object modules and library routines into something the system can load, and the run time (LE on z/OS, libcob in GnuCOBOL) provides storage, files, messages and error handling while it runs. This page follows the chain on z/OS first, then reads the compiler listing, goes through the compiler options that matter most with their defaults, describes Language Environment, and ends with the GnuCOBOL counterpart of every step, which is the part that can be run on a laptop. The JCL around the steps is on JCL for COBOL Programmers; the platform itself is introduced on Mainframe Environment Primer; how cobc works and its everyday options are on Getting Started; and the dialect switches (-std=) are on Compilers and Dialects. Choosing options for speed is the subject of Performance and Compiler Options, and finding a failure with the listing and the dump is on Debugging and Troubleshooting.

Where the book is outdated: Taulli’s Modern Mainframe Development (2022) treats Enterprise COBOL 6.3 as the latest compiler and z/OS 2.4 as the platform. Enterprise COBOL 6.5 is current, ARCH(11) is now the default and the older levels are no longer accepted, and cob2 for compiling from the z/OS UNIX shell received further enhancements in a 2026 PTF. The compile options, the listing, the severity codes and the Language Environment options on this page come from the 6.5 and z/OS 3.2 manuals, not from the book.

The build chain on z/OS

Four steps turn the Circulation program LOANCNT (a main program that calls the subprogram FEECALC) into something that can run, and a fifth runs it.

flowchart LR A["COBOL source: LOANCNT with COPY LOANREC"] --> B["Compile: IGYCRCTL, options SQL and CICS handle EXEC statements"] B --> C["Object module: SYSLIN"] B -.-> D["DBRM for Db2: DBRMLIB"] B -.-> L["Listing: SYSPRINT"] C --> E["Bind: IEWBLINK joins FEECALC and the LE library routines"] E --> F[("Program object: PDSE member in USERID.CIRC.LOADLIB")] F --> G["Run: EXEC PGM=LOANCNT under Language Environment"] G --> H["Output, CEEDUMP, return code"]
  • Compile. The compiler program is IGYCRCTL. It reads the source from SYSIN, COPY members from SYSLIB, writes the object module to SYSLIN and the listing to SYSPRINT, and uses work data sets SYSUT1 to SYSUT15 and SYSMDECK. Several of its output data sets exist only when an option asks for them: SYSPUNCH with DECK, SYSADATA with ADATA, SYSDEBUG with TEST(SEPARATE) and the Db2 DBRMLIB with SQL.

  • Coprocessors (precompile or translate). Older shops ran a separate Db2 precompiler or CICS translator before the compiler. Enterprise COBOL instead has the SQL and CICS options, so that the compiler calls the Db2 coprocessor or the CICS translation while it compiles; see SQL and CICS: the integrated translation.

  • Bind (link-edit). The binder, IEWBLINK in the IBM procedures, joins the object module with other object modules and the Language Environment libraries into a program object, the form of an executable that lives in a PDSE or a z/OS UNIX file; the older name for the same step is link-edit, which is why the DD is still called SYSLMOD and the procedures end in L.

  • Run. The system loads the program object and calls LE, which creates an enclave, initializes the run time and passes control to the main program.

The program object is the current form of the load module. A conventional load module sits in a PDS; a program object needs a PDSE (or a z/OS UNIX file), and the Programming Guide asks for the program object of the COBOL program to be linked into a PDSE in its Java interoperability tasks. The IBM procedure allocates SYSLMOD with DSNTYPE=LIBRARY, which makes a PDSE. This page does not rely on any detail of the binder beyond the Programming Guide; the binder’s own manual (MVS Program Management) defines program objects and the control statements precisely.

The IBM compile procedures

IBM supplies three cataloged procedures, in increasing scope:

Procedure Steps What you supply

IGYWC

COBOL (compile only)

COBOL.SYSIN and, for COPY, COBOL.SYSLIB

IGYWCL

COBOL, then LKED (binder)

The same, plus extra object modules through LKED.SYSIN if needed; the procedure parameters LNGPRFX (default IGY.V6R5M0), LIBPRFX (default CEE), PGMLIB (default &&GOSET), GOPGM (default GO) and SYSLBLK (default 3200)

IGYWCLG

COBOL, LKED, then GO (run the result)

The same, plus the run-time DDs under GO

In IGYWCL the LKED step runs only if the compile ended with a return code of 8 or less (COND=(8,LT,COBOL)), reads SYSLIB from the LE SCEELKEX and SCEELKED libraries, takes the object module from &&LOADSET and writes the result to SYSLMOD. The data set prefixes and some libraries are installation-dependent, so the values above are those of the IBM-supplied procedure, not necessarily those of a site. Calling the procedure and overriding its DDs is the same technique as for any procedure: the DD name is written as step.ddname.

This job compiles LOANCNT with listing options, binds it with the already-compiled FEECALC, and stores the program object in the Circulation load library (z/OS; not run here; the DD names and procedure parameters are checked against the Programming Guide, the binder control statements INCLUDE and NAME are standard binder syntax that this page did not check against the MVS Program Management manual):

//LOANCNTB JOB (ACCT),'CIRC BUILD',CLASS=A,MSGCLASS=X
//BUILD    EXEC IGYWCL,PARM.COBOL='OPT(2),LIST,MAP,XREF,SSRANGE'
//COBOL.SYSIN   DD DISP=SHR,DSN=USERID.CIRC.COBOL(LOANCNT)
//COBOL.SYSLIB  DD DISP=SHR,DSN=USERID.CIRC.COPYLIB
//LKED.OBJLIB   DD DISP=SHR,DSN=USERID.CIRC.OBJLIB
//LKED.SYSLMOD  DD DISP=SHR,DSN=USERID.CIRC.LOADLIB(LOANCNT)
//LKED.SYSIN    DD *
  INCLUDE OBJLIB(FEECALC)
  NAME LOANCNT(R)
/*

Running the result is a separate job step (z/OS; not run here). The STEPLIB makes the program object and the LE run-time libraries visible, CEEOPTS supplies LE options (see Runtime options), and CEEDUMP receives the dump that LE writes after an unhandled error:

//LOANCNTR JOB (ACCT),'CIRC RUN',CLASS=A,MSGCLASS=X
//RUN      EXEC PGM=LOANCNT
//STEPLIB  DD DISP=SHR,DSN=USERID.CIRC.LOADLIB
//         DD DISP=SHR,DSN=CEE.SCEERUN
//         DD DISP=SHR,DSN=CEE.SCEERUN2
//LOANTRAN DD DISP=SHR,DSN=USERID.CIRC.LOANTRAN
//SYSOUT   DD SYSOUT=*
//CEEDUMP  DD SYSOUT=*
//CEEOPTS  DD *
  RPTOPTS(ON),TERMTHDACT(DUMP)
/*

A batch compilation can hold several programs in one SYSIN. The NAME compiler option decides how they are bound: the compiler writes a binder NAME statement, and every program from one NAME to the next goes into one program object, which takes the name of the last program of the group. The batch must end each program with END PROGRAM, or the next program is nested inside the previous one. Source: Programming Guide: Compiling multiple programs (batch compilation)

SQL and CICS: the integrated translation

A program with EXEC SQL or EXEC CICS statements needs the matching option, because the compiler would otherwise not process them.

  • SQL (default NOSQL) makes the compiler call the Db2 coprocessor, which checks the SQL, replaces it with calls and writes the database request module (DBRM) to the DBRMLIB DD (or, with cob2, to a file). With NOSQL, any SQL statement found is diagnosed and discarded. A string in quotation marks or apostrophes after SQL carries Db2 suboptions, and a long string can be split across several CBL statements. The separate Db2 precompiler is not supported with LP(64), where the SQL option is required for embedded SQL. The DBRM is later bound into a package; that step is on Embedded SQL and Db2.

  • CICS (default NOCICS) makes the compiler process the EXEC CICS statements. A program compiled with CICS does not run outside CICS. With NOCICS, the statements are diagnosed and discarded. The transaction side is on CICS Online Programming.

Both options are used at compile time only, so they appear in the listing’s "options in effect" section and in the PARM of the compile step, for example PARM.COBOL='SQL("...")'. DYNAM and CICS conflicted until the September 2026 PTF PH73033, which allows the two to be used together; before it, the compiler gave a warning when both were given. The copy of the Programming Guide read for this page still lists the restriction that DYNAM must not be used when programs with EXEC SQL run under CICS or are called by a program that uses the Db2 call attach facility.

Static and dynamic linkage

How the binder is used depends on how programs call each other. With NODYNAM (the default), a CALL "FEECALC" with a literal is a static call: the object module of FEECALC is bound into the same program object as LOANCNT, as in the job above. With DYNAM, the call is dynamic: FEECALC is a separate program object found at run time through STEPLIB or the link list, and changing it needs no re-bind of the caller. A CALL of a data item always loads the target at run time. For RENT programs (the default) the Programming Guide recommends binding the program object with the RENT binder option (or REUS if non-COBOL serially reusable programs are included); if any program in the object is not reentrant, NOREUS is needed so that CANCEL gives a fresh copy. The language side of CALL, CANCEL and LINKAGE SECTION is on Subprograms: CALL and Linkage; the GnuCOBOL form of the same choice is shown below with cobc -m.

cob2 and the z/OS UNIX shell

cob2 compiles and links from a z/OS UNIX shell with one command, which is how COBOL fits into make, Git and CI on z/OS. It passes only files that end in .cbl to the compiler (everything else goes to the linker), always adds the RENT and TERM options, writes a requested listing to file.lst, and uses the shell command c89, the shell interface to the binder, for the link step. A few forms (z/OS; not run here, taken from the Programming Guide’s examples with the Circulation names):

cob2 -c loancnt.cbl
cob2 loancnt.cbl feecalc.cbl -o loancnt
cob2 -qlist,map,xref loancnt.cbl -o loancnt
cob2 -g -I /u/circ/copy loancnt.cbl -o loancnt
cob2 -qsql loancnt.cbl -o loancnt -dbrmlib=USERID.CIRC.DBRMLIB

The first compiles only (loancnt.o); the second compiles and links two sources into loancnt; the third passes compiler options with -q; the fourth asks for debugging information (-g is TEST with no suboptions) and a copybook directory; the last uses the Db2 coprocessor and writes the DBRM to an existing data set (without a name after -dbrmlib it goes to a .dbrm file). The options that matter most:

Option Effect

-c

Compile only; do not link.

-o name

Name of the output; the default is a.out.

-q opts

Compiler options, separated by commas.

-I dir

Directory searched for copybooks that have no library name. After PTF PH72005 it can also name a PDS written as // followed by an unqualified data set name, or // followed by a quoted qualified name.

-M, -MF file, -MT name

Write a make dependency file listing the copybooks; -MF names the file and -MT the target. The reference page for 6.5 lists both.

-g

TEST with no suboptions.

-comprc_ok=n

Highest compiler return code with which cob2 still links; default 4.

-v, -#

Show the generated compile and link commands (-v then runs them, -# does not).

-b opts

Pass options to the binder as name=value pairs; -bdll builds a DLL and adds DLL, EXPORTALL and RENT.

-e name

Entry point program of the module; the default is the first program on the command line.

PTF PH72005 (July 2026) added, for cob2 and z/OS UNIX: automatic code page conversion of tagged z/OS UNIX input files, UTF-8 files included, when their code page differs from the CODEPAGE option of the compile; the PDS forms of -I; -MF and -MT for -M; paths for input and output files handled like xlc, gcc and clang (a relative path to the source is accepted); and the new TERM(SOURCELINE) option, which prefixes diagnostics sent to the terminal with the source file name and the relative line number. This is the same set of enhancements named on Compilers and Dialects and Mainframe Environment Primer. The OPTFILE option is ignored when compiling with cob2. Whether a given site has applied the PTF is a question for its system programmer.

Reading the compiler listing

The compiler writes its report to SYSPRINT (or to file.lst under cob2). What it contains depends on options, and a listing that was not requested cannot be recovered after the fact, so a production compile saves it:

Option (default) Section of the listing Use it for

SOURCE (SOURCE)

The source with compiler-assigned line numbers (or yours with NUMBER)

Every message and every other section refers to these numbers.

XREF (XREF(FULL))

Sorted cross-reference of programs, data names and procedure names with the lines that define and use them, plus a cross-reference of COPY and BASIS members to the data sets they came from; XREF(SHORT) lists explicit references only

Finding where a field is changed; embedded flags show an undefined (UND), duplicate (DUP), implicit (IMP) or external (EXT) name.

MAP (NOMAP)

The DATA DIVISION map: every item with level, displacement, length and USAGE; nested program structure; size of WORKING-STORAGE and LOCAL-STORAGE. MAP(HEX) or MAP(DEC) choose how offsets within groups are shown

Matching a dump address to a field; checking REDEFINES and record lengths.

LIST (NOLIST)

The assembler-language listing of the generated code, with the constant area, prolog areas, external symbol dictionary and storage maps

Locating the instruction of an abend when you know the offset.

OFFSET (NOOFFSET)

A condensed PROCEDURE DIVISION listing with line numbers, statement references and the offset of each block of code; the optimizer may split or move a statement’s code, so a statement can have more than one entry

Finding the failing statement from a CEEDUMP offset at lower cost than LIST.

VBREF (NOVBREF)

Alphabetical list of the statements used, with a count of each

Impact analysis ("where do we use ALTER?").

OFFSET and LIST are mutually exclusive, and if both are given, OFFSET wins. The listing statements EJECT, SKIP1 to SKIP3 and TITLE, and the *CONTROL (*CBL) LIST, MAP and SOURCE switches inside the program, narrow what is printed. A listing, in the order the sections appear, looks like this (a shape only; real listings have a product banner and many more lines):

[ banner: product, release, date ]
[ invocation parameters, CBL/PROCESS statements, options in effect ]
[ SOURCE: numbered source lines, with messages embedded at FLAG(x,y) level ]
[ MAP: data division map ]
[ XREF: programs, data names, procedure names, COPY members ]
[ diagnostics: messages of FLAG(x) level and above, ordered by line number ]
End of compilation 1, program LOANCNT, highest severity 0.

Under a source line the compiler prints diagnostics in the form nnnnnn IGYppxxxx-l message-text, where nnnnnn is the source line number, IGY marks a compiler message, pp two characters naming the compiler phase (relevant only when reporting a suspected compiler error to IBM), xxxx the message number and l the severity letter. Messages found while processing options, CBL and PROCESS statements and COPY or REPLACE statements appear near the top; messages about the program are ordered by line number near the end; and a summary closes the listing. The severity letter decides the return code of the compile step:

Severity Return code Meaning

Informational (I)

0

No action; the program runs correctly.

Warning (W)

4

Possible error; the program probably runs correctly as written.

Error (E)

8

A definite error that the compiler tried to correct; results may differ from what you expect.

Severe (S)

12

The compiler could not correct the error; the program does not run correctly and object code might not exist.

Unrecoverable (U)

16

The compilation was ended.

The final return code is generally the highest one met. FLAG(x,y) chooses which messages are printed (default FLAG(I,I): all, embedded in the source listing too), NOCOMPILE(x) stops generating code once a message of severity x or higher occurs (the printed default is NOCOMPILE(S), so a severe error stops code generation), and a message exit (EXIT(MSGEXIT)) lets a site change a message’s severity or suppress it, which changes the return code as well. In cob2, -comprc_ok plays the role of the COND test of IGYWCL: with the default of 4, a warning does not stop the link, an error does. Compiling a program named ERRMSG prints the complete list of messages with their numbers, severities and texts. The runtime messages, with the prefix IGZ and a severity letter, are different: they come from the library at execution time and are shown in the job output, not in the compiler listing. The meanings of both are in the Messages and Codes manual.

Compiler options by group, with 6.5 defaults

Options are set at several levels, and the higher level wins. For a compile with JCL the order from highest to lowest is: installation defaults that the site has fixed; the values of BUFSIZE, OUTDD, SQL and SQLIMS for the first program of a batch; options on CBL or PROCESS statements before the IDENTIFICATION DIVISION; options on the invocation (the PARM of the step, or cob2 -q); and installation defaults that are not fixed. Two conflicting options at the same level resolve to the last one. A mutually exclusive pair is resolved by a table in the Programming Guide, of which the rows most often met are these: OFFSET forces NOLIST; NUMPROC(PFD) is not allowed together with NUMCHECK(ZON(LAX)); CICS forces LP(32) and RENT; LP(64) forces NOCICS, RENT, NOSMARTBIN and NOTHREAD; TEST forces OBJECT and NODECK. The installation can change every default below, so the reliable way to know the defaults of a site is to compile a program with no options and read "options in effect" in the listing.

The defaults below were taken from the Enterprise COBOL for z/OS 6.5 Programming Guide, page "COBOL compiler options" and the page of each option.

Group Option 6.5 default What it decides

Machine

ARCH(n)

ARCH(11)

The oldest processor model the generated code must run on: 11 is z13, 12 z14, 13 z15, 14 z16 and 15 z17. A higher level uses newer instructions and may abend on an older machine, disaster-recovery machines included; IBM recommends setting it in the installation defaults, not in JCL. 6.5 accepts 11 to 15 and no longer accepts 10.

TUNE(n)

The same as ARCH; both are 11 if neither is given

The machine the code is scheduled for, within the instructions ARCH allows. The page that lists all defaults prints TUNE(10), but the option’s own page and the 6.5 What’s New text both say 11, which is used here.

Speed

OPTIMIZE(n) (OPT)

OPTIMIZE(0)

0 gives the shortest compile and best debugging; 1 and 2 optimize progressively. IBM recommends OPT(2) for best performance. Optimization assumes valid data.

Debugging

TEST

NOTEST(NODWARF,NOSOURCE,NOSEPARATE)

Debug information in DWARF format, for z/OS Debugger and for CEEDUMP (variables and statement numbers). TEST alone means TEST(NOEJPD,DWARF,SOURCE,NOSEPARATE).

Run-time checks

SSRANGE

NOSSRANGE

Checks subscripts, indexes and reference modification at run time; SSRANGE alone means SSRANGE(NOZLEN,ABD). The compiled-in checks cannot be switched off by the LE option CHECK(OFF).

NUMCHECK

NONUMCHECK

Class tests on the sending fields of DISPLAY and COMP-3 data, and size-error checks on binary. NUMCHECK alone means NUMCHECK(ZON(ALPHNUM,STRICT),PAC,BIN(TRUNCBIN),MSG); with ABD instead of MSG an invalid field abends.

INITCHECK

NOINITCHECK

Compile-time warnings for data used before it is initialized; INITCHECK alone means INITCHECK(LAX) (warn only when no path initializes the item), STRICT warns when any path does not.

Numeric

TRUNC

TRUNC(STD)

How binary receiving fields are truncated: STD to the digits of the PICTURE, OPT assumes the data conforms (fast, unpredictable otherwise), BIN uses the full binary size. COMP-5 always behaves as BIN. See USAGE and Numeric Representation.

NUMPROC

NUMPROC(NOPFD)

Accept any valid sign configuration (NOPFD, recommended) or assume the IBM standard signs (PFD, faster, but wrong for data that does not conform).

Addressing and linkage

LP(n)

LP(32)

31-bit (32) or 64-bit (64) code. LP(64) programs cannot use XML or JSON statements, object orientation, ALTER or the GO TO statement with no procedure-name, cannot run in CICS or IMS, and cannot be mixed with AMODE 31 programs in one application.

RENT

RENT

Reentrant code, needed above the 16 MB line and for CICS and z/OS UNIX.

DYNAM

NODYNAM

Whether CALL with a literal is dynamic (DYNAM) or static (NODYNAM).

SMARTBIN

SMARTBIN when LP(32), and not supported with LP(64)

Adds binary metadata that IBM’s Automatic Binary Optimizer can use later (introduced in 6.4; a NAMES suboption adds symbol names). Disk use of the module grows by about two to three times.

Coprocessors

SQL

NOSQL

Db2 coprocessor; DBRM to DBRMLIB.

CICS

NOCICS

CICS translation by the compiler.

Listing

LIST, MAP, XREF, OFFSET, SOURCE

NOLIST, NOMAP, XREF(FULL), NOOFFSET, SOURCE

See the previous section.

Options can also be written in the source with a CBL (or PROCESS) statement before the IDENTIFICATION DIVISION, which is how a program carries the options it must be compiled with; the compiler directives that sit inside the program are on Compiler Directives and Conditional Compilation. An example is CBL OPT(2),SSRANGE,XREF in columns 8 to 72.

Language Environment

Language Environment is the common run time under which Enterprise COBOL, PL/I, C/C++ and assembler routines run on z/OS. The compiler generates calls to it for storage, file and I/O services, DISPLAY, condition handling and the termination of the program, which is why a COBOL program needs the LE libraries (CEE.SCEERUN and SCEERUN2 in the procedures above) when it runs.

Enclaves

LE organizes a running application in three levels. A process is the outermost: the resources of the application, the LE library among them; z/OS Language Environment supports one process per application. An enclave is the unit a program runs in: one or more load modules containing separately compiled and bound routines, the first of which is the main routine and the rest subroutines. The enclave defines the scope of the language semantics: for COBOL it is the run unit, so that STOP RUN ends the whole enclave, EXTERNAL data lasts as long as the enclave, and the first routine called is the main program. A thread is the path of execution inside an enclave.

When an enclave ends, LE computes the enclave return code: the user return code (what the program put in RETURN-CODE) plus an enclave reason code, and places it in register 15, with the reason code in register 0. For a main program that ends normally this is what the job step reports as its condition code, the value COND= tests in JCL.

Runtime options

Runtime options are the settings of the LE run time. Some are COBOL-only (for example CBLOPTS, CHECK, DEBUG, AIXBLD, RTEREUS, UPSI); most are shared. Those most often needed, with their defaults for batch (non-CICS) applications, from the z/OS 3.2 Programming Reference, are:

Option Default (non-CICS) What it controls

HEAP

HEAP(32K,32K,ANYWHERE,KEEP,8K,4K)

The user heap: initial size, increment, location, whether increments are kept or freed, and the two sizes below the 16 MB line. COBOL WORKING-STORAGE data is among the heap contents.

STACK

STACK(128K,128K,ANYWHERE,KEEP,512K,128K)

The thread’s stack storage: initial and increment size for the upward-growing stack (where COBOL LOCAL-STORAGE lives), location, keep or free, and sizes for the downward-growing stack of an XPLINK environment.

TERMTHDACT

TERMTHDACT(TRACE,CESE,96)

What LE produces when a condition of severity 2 or higher is not handled: QUIET (nothing), MSG (a message), TRACE (message and traceback), DUMP (message, traceback and an LE dump), UATRACE, UADUMP, UAONLY and UAIMM (also a system dump of the address space when a dump DD is present). The last number is the amount of storage dumped around registers, 96 bytes. For AMODE 64 the default is TERMTHDACT(TRACE,,96).

RPTOPTS

RPTOPTS(OFF)

With ON, prints a report of every runtime option in effect and where each got its value, to the MSGFILE. It slows the run, so it is a development aid.

RPTSTG

RPTSTG(OFF)

With ON, prints a storage report at the end, useful to size HEAP and STACK.

MSGFILE

MSGFILE(SYSOUT,FBA,121,0,NOENQ)

The DD name of the file for run-time messages, here SYSOUT.

TRAP

TRAP(ON,SPIE)

Whether LE handles abends and program interrupts as conditions (ON); with OFF the system’s own abend handling applies.

ABTERMENC

ABTERMENC(ABEND)

What an enclave ending with an unhandled severity 2 or higher condition does: end with an abend.

ABPERC

ABPERC(NONE)

A single abend code to pass through without LE condition handling.

Option values keep their default unless something changes them, and the sources are ordered. From the Programming Guide, from highest to lowest precedence: storage options set by the storage tuning user exit; options set by the assembler user exit CEEBXITA; options on invocation (the PARM of the step, written for COBOL as the program’s arguments, a slash, then the runtime options; under z/OS UNIX the _CEE_RUNOPTS environment variable); options in a CEEOPTS DD; options in a CEEUOPT module linked into the program; and the installation defaults. CEEOPTS is ignored under CICS and for programs started with an exec function. The step PARM for a run with a runtime option and no program argument is therefore PARM='/RPTOPTS(ON)', and the CEEOPTS DD in the job above is the easier form for several options. Option strings are comma-separated, and a skipped suboption keeps its comma (STACK(,,ANYWHERE,FREE) is valid).

Callable services

LE’s services are called with CALL and have names that begin with CEE. A call passes its arguments by reference, and many end with a 12-byte feedback code (a condition token) that is all zeros when the service worked. The ones a COBOL developer meets most often:

Service Purpose

CEE3DMP

Write a formatted LE dump on demand, with a title (80 bytes), an options string and a feedback code. The options select the sections: TRACEBACK (default), FILES (default), VARIABLES (needs the program compiled with TEST), BLOCKS (default NOBLOCKS), STORAGE, and the extent: ENCLAVE(ALL) (default) and THREAD(CURRENT) (default).

CEE3ABD

End the enclave with an abend, given the abend code (a fullword integer up to 4095) and a clean-up value: 0 abends at once and requests a system dump; 1 processes the abend as a normal one with termination processing; 2 to 5 combine termination processing with choices about the CEEDUMP and the system dump. There is no return and no feedback code.

CEE3GRC, CEE3SRC

Get and set the user enclave return code.

CEEMSG

Get, format and write the message for a condition token to the message file.

CEEDAYS and the other date and time services

Convert dates to and from Lilian form; they are the LE side of the date functions on Dates and Times.

A request for a dump of the file and traceback state at a point of interest, here before the end of the nightly update, can be written as follows (z/OS; not run here; the call and its three parameters follow the Programming Guide and the Programming Reference):

       WORKING-STORAGE SECTION.
       01  WS-DUMP-TITLE       PIC X(80) VALUE "LOANCNT BEFORE CLOSE".
       01  WS-DUMP-OPTIONS     PIC X(255) VALUE "TRACEBACK FILES".
       01  WS-FEEDBACK         PIC X(12).
       PROCEDURE DIVISION.
           CALL "CEE3DMP" USING WS-DUMP-TITLE WS-DUMP-OPTIONS
               WS-FEEDBACK

The dump goes to the CEEDUMP DD, so the job needs //CEEDUMP DD SYSOUT=*. An unhandled error produces the same kind of dump through TERMTHDACT(DUMP), with the title "Condition processing resulted in the unhandled condition" and trace options of TRACE COND THR(ALL). Compiling with TEST (or NOTEST(DWARF), which gives a smaller amount of information) makes the dump show the failing statement number and the values of variables; without it the dump shows only offsets, which is why OFFSET or LIST output of the same build is kept. A dump of a program that is staged by LLA/VLF needs the IGZLOAD or STEPLIB DD to point at the library; otherwise the variables and statement numbers are missing. How to read the dump is on Debugging and Troubleshooting, and the language side of handling errors is on Error Handling and Declaratives.

The GnuCOBOL counterpart

GnuCOBOL has the same chain with different names. cobc runs the preprocessor, the parser and the translator to C, then the C compiler, then the linker; the "binder" is the system linker; the program object is an executable (-x) or a shared module (-m); and the run time is libcob, configured by runtime.cfg and by COB_* environment variables. The general picture and cobc -v are in the figure and tables on Getting Started. This section runs each stage on the Circulation programs and shows the output. The subprogram FEECALC prices a late return (0.35 per day, capped at 5.00, as in the fines of Arithmetic and COMPUTE), and the main program LOANCNT counts the day’s transactions and calls it.

      * FEECALC - overdue fine for one loan (called by LOANCNT)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FEECALC.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LS-DAYS-LATE        PIC 9(3).
       01  LS-FEE              PIC 9(3)V99.
       PROCEDURE DIVISION USING LS-DAYS-LATE LS-FEE.
       MAIN-PARA.
           COMPUTE LS-FEE ROUNDED = LS-DAYS-LATE * 0.35
           IF LS-FEE > 5.00
               MOVE 5.00 TO LS-FEE
           END-IF
           GOBACK.
      * LOANCNT - count the day's loan transactions, price a late return
       IDENTIFICATION DIVISION.
       PROGRAM-ID. LOANCNT.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT LOAN-FILE ASSIGN TO "LOANTRAN"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  LOAN-FILE.
           COPY LOANREC.
       WORKING-STORAGE SECTION.
       01  WS-STATUS           PIC XX.
       01  WS-OUT              PIC 9(3) VALUE 0.
       01  WS-BACK             PIC 9(3) VALUE 0.
       01  WS-DAYS             PIC 9(3) VALUE 20.
       01  WS-FEE              PIC 9(3)V99.
       01  WS-FEE-EDIT         PIC ZZ9.99.
       PROCEDURE DIVISION.
       MAIN-PARA.
           OPEN INPUT LOAN-FILE
           IF WS-STATUS NOT = "00"
               DISPLAY "LOANTRAN not opened, status " WS-STATUS
               STOP RUN
           END-IF
           PERFORM UNTIL WS-STATUS NOT = "00"
               READ LOAN-FILE
                   AT END CONTINUE
                   NOT AT END
                       IF LOAN-CHECKOUT
                           ADD 1 TO WS-OUT
                       END-IF
                       IF LOAN-RETURN
                           ADD 1 TO WS-BACK
                       END-IF
               END-READ
           END-PERFORM
           CLOSE LOAN-FILE
           CALL "FEECALC" USING WS-DAYS WS-FEE
           MOVE WS-FEE TO WS-FEE-EDIT
           DISPLAY "Checkouts " WS-OUT " returns " WS-BACK
           DISPLAY "Fine for " WS-DAYS " days: " WS-FEE-EDIT
           STOP RUN.

The input LOANTRAN is the Circulation transaction file (four lines of the data set of the other pages: three checkouts and a return) placed in a subdirectory data, so that the effect of the file path is visible. Source: GnuCOBOL 3.2 manual

The stages: -E, -C, -c and -x

-E stops after the preprocessor and writes the source with COPY and REPLACE already expanded. The LOANREC copybook arrives with a #line marker that names the file it came from (the copybook text is in LOANREC.cpy next to the source, found with -I .):

$ cobc -E -I . -o loancnt.i loancnt.cob
$ sed -n 12,24p loancnt.i
 FILE SECTION.
 FD LOAN-FILE.

#line 1 "LOANREC.cpy"

 01 LOAN-RECORD.
 05 LOAN-TYPE PIC X.
 88 LOAN-CHECKOUT VALUE "O".
 88 LOAN-RETURN VALUE "R".
 05 LOAN-MEMBER-ID PIC 9(6).
 05 LOAN-BOOK-ID PIC 9(6).
 05 LOAN-DATE PIC 9(8).
 05 FILLER PIC X(9).

-C stops after translation to C. For each source it leaves the .c file and two headers, which hold the working storage and the literals; the call to a subprogram shows how a CALL literal is resolved at run time (cob_resolve_cobol):

$ cobc -C -Wall -I . loancnt.cob feecalc.cob
$ ls -1 *.c *.h
feecalc.c
feecalc.c.h
feecalc.c.l.h
loancnt.c
loancnt.c.h
loancnt.c.l.h
$ wc -l loancnt.c
370 loancnt.c
$ grep -n 'LOANCNT (void)\|"FEECALC"' loancnt.c
24:COB_EXT_EXPORT int		LOANCNT (void);
190:    call_FEECALC.funcvoid = cob_resolve_cobol ("FEECALC", 0, 1);

-c stops after the C compiler and leaves an object file for each source, which is the equivalent of the compile step alone (IGYWC):

$ cobc -c -Wall -I . loancnt.cob feecalc.cob
$ ls -1 *.o
feecalc.o
loancnt.o

Linking the two objects into an executable is the bind step. Whether a program is a main program is decided at compile time, not at link time: cobc generates the C main function only for a source compiled with -x. Linking objects compiled without it fails, which is a common surprise:

$ cobc -x -Wall -o loancnt loancnt.o feecalc.o
(.text+0x1c): undefined reference to `main'
/usr/bin/ld: (.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

Compiling the main source with -x -c and linking again works (no warning in any of the commands; -Wall was given to each):

$ cobc -x -c -Wall -I . loancnt.cob
$ cobc -x -Wall -o loancnt loancnt.o feecalc.o
$ COB_FILE_PATH=data ./loancnt
Checkouts 003 returns 001
Fine for 020 days:   5.00

The one-command form compiles and links both sources into one executable, the static-call equivalent of the NODYNAM job above:

$ cobc -x -Wall -I . -o loancnt loancnt.cob feecalc.cob
$ COB_FILE_PATH=data ./loancnt
Checkouts 003 returns 001
Fine for 020 days:   5.00

(The COB_FILE_PATH variable is explained below. The first run without it fails because LOANTRAN is not in the current directory.) Compiled with -std=ibm as well, cobc -x -Wall -std=ibm -I . -o loancnt loancnt.cob feecalc.cob printed no warning and gave the same output. Source: GnuCOBOL 3.2 manual: Compile; GnuCOBOL 3.2 manual: Compiler options

Dynamic linkage with -m and COB_LIBRARY_PATH

-m builds a loadable module instead of an executable, which is the counterpart of DYNAM: the main program is built without FEECALC, and libcob loads the module when the CALL runs. The name of the module file is the program name, so it is built as FEECALC.so into a directory mods here. Without a search path the run fails; with COB_LIBRARY_PATH pointing at the directory it works (the current directory and the installation’s extras directory are always searched in addition):

$ mkdir mods
$ cobc -m -Wall -o mods/FEECALC.so feecalc.cob
$ cobc -x -Wall -I . loancnt.cob
$ COB_FILE_PATH=data ./loancnt
libcob: error: module 'FEECALC' not found
$ echo $?
1
$ COB_FILE_PATH=data COB_LIBRARY_PATH=mods ./loancnt
Checkouts 003 returns 001
Fine for 020 days:   5.00

On z/OS the corresponding cure is the library concatenation (STEPLIB or the link list) that holds the dynamically called program, here the search path. The call forms and cobcrun are on Subprograms: CALL and Linkage.

Listings, -save-temps and -v

GnuCOBOL’s compiler listing is much smaller than the IBM one: -t file writes the source, with the text of every COPY member marked with a C after its line number, and ends with the count of warnings and errors. It has no data map or cross-reference. The 0 warnings line is the equivalent of the final severity line of an IBM listing:

$ cobc -x -Wall -I . -t loancnt.lst loancnt.cob
$ sed -n 1,6p loancnt.lst
GnuCOBOL 3.2.0          loancnt.cob          Tue Oct 20 2026 17:23:17  Page 0001

LINE    PG/LN  A...B............................................................

000001        * LOANCNT - count the day's loan transactions, price a late return
000002         IDENTIFICATION DIVISION.
$ tail -2 loancnt.lst
0 warnings in compilation group
0 errors in compilation group

cobc normally deletes its intermediate files. -save-temps keeps them: the expanded source (.i), the C file and its headers, and the object file. -save-temps=dir puts them in an existing directory (it prints a warning and falls back to the current directory if the directory does not exist):

$ mkdir tmp
$ cobc -x -save-temps=tmp -Wall -I . loancnt.cob
$ ls -1 tmp
loancnt.c
loancnt.c.h
loancnt.c.l.h
loancnt.i
loancnt.o
$ ls -1 loancnt
loancnt

cobc -v prints each step with its return status, which is the listing of the build; -v on the same program shows (the temporary names differ on every run):

$ cobc -v -x -Wall -I . -o v loancnt.cob
loading standard configuration file 'default.conf'
command line:	cobc -v -x -Wall -I . -o v loancnt.cob
preprocessing:	loancnt.cob -> /tmp/cob124_0.cob
return status:	0
parsing:	/tmp/cob124_0.cob (loancnt.cob)
return status:	0
translating:	/tmp/cob124_0.cob -> /tmp/cob124_0.c (loancnt.cob)
executing:	gcc -c -pipe -I/usr/local/include -Wno-unused -fsigned-char
		-Wno-pointer-sign -I"." -o "/tmp/cob124_0.o" "/tmp/cob124_0.c"
return status:	0
executing:	gcc -Wl,--export-dynamic -o "v" "/tmp/cob124_0.o"
		-L/usr/local/lib -lcob
return status:	0
cobc (GnuCOBOL) 3.2.0
Built     Oct 20 2026 16:19:46	Packaged  Jul 28 2023 17:02:56 UTC
C version "14.2.0"

The C compiler and its flags come from the build of GnuCOBOL (cobc -i shows them), so a diagnostic from the C step is shown as the C compiler prints it. The preprocess, parse and translate steps correspond to the compiler’s own passes on z/OS, and -v is the closest thing to the compile listing’s "options in effect": the line loading standard configuration file names the dialect configuration.

Run-time configuration: runtime.cfg and COB_* variables

The libcob run time takes its settings from three places. First the built-in defaults; then a configuration file, runtime.cfg in the installation’s config directory or the file named by COB_RUNTIME_CONFIG; then environment variables, which are read last and therefore win. Every setting has both names, a variable such as COB_FILE_PATH and a parameter in the configuration file such as file_path; the file also accepts setenv, include and ${VAR:default} substitution. The file below sets the data directory and the module directory, so the program runs without any variable:

$ cat circ.cfg
# Circulation run-time settings
file_path: data
library_path: mods
$ ./loancnt
LOANTRAN not opened, status 35
$ COB_RUNTIME_CONFIG=circ.cfg ./loancnt
Checkouts 003 returns 001
Fine for 020 days:   5.00
$ COB_FILE_PATH=. COB_RUNTIME_CONFIG=circ.cfg ./loancnt
LOANTRAN not opened, status 35

Status 35 is "file not found" (see File Status Codes); the last line shows the precedence: the environment variable COB_FILE_PATH=. overrides the file_path of the file, so the data file is no longer found. COB_FILE_PATH gives names like LOANTRAN a common location, which is the role that the DD statements of a job have on z/OS: the file behind the name is chosen outside the program. The nearest equivalent of one DD statement is a variable with the prefix DD_ and the file name, which maps that single name to any path:

$ DD_LOANTRAN=data/LOANTRAN ./loancnt
Checkouts 003 returns 001
Fine for 020 days:   5.00
The variables used on this page, from the `runtime.cfg` comments of the 3.2 installation, are:
Variable Parameter Effect (default)

COB_FILE_PATH

file_path

Directory prefixed to file names in ASSIGN; the current directory.

COB_LIBRARY_PATH

library_path

Directories searched for loadable modules; . and the extras directory are always added.

COB_SET_TRACE

set_trace

Switch on the COBOL trace (false). Only a program compiled with -ftrace or -ftraceall (or -d) emits trace events.

COB_TRACE_FILE

trace_file

Where the trace goes (stderr); a name that starts with + is appended to, and $$ becomes the process id.

COB_TRACE_FORMAT

trace_format

Layout of each trace line (%P %S Line: %L, where %P is the program with prefix, %I the program name, %L the line, %S the statement, %F the source file).

COB_RUNTIME_CONFIG

The configuration file to read in addition to runtime.cfg.

COB_SORT_MEMORY, COB_SORT_CHUNK

sort_memory, sort_chunk

Memory and chunk sizes of the built-in sort (see Sort and Merge).

The trace shows the equivalent of a statement-level traceback. The program is compiled with -ftraceall (without it, with -ftrace, only the entry and the paragraphs are traced), and the variables switch it on and send it to a file; the same binary with no variables prints nothing extra:

$ cobc -x -Wall -ftraceall -I . -o loantr loancnt.cob
$ COB_RUNTIME_CONFIG=circ.cfg COB_SET_TRACE=1 COB_TRACE_FILE=trace.log ./loantr
Checkouts 003 returns 001
Fine for 020 days:   5.00
$ wc -l trace.log
37 trace.log
$ head -14 trace.log
Source: 'loancnt.cob'
Program-Id:  LOANCNT
Program-Id:  LOANCNT              Entry: LOANCNT                         Line:     22
Program-Id:  LOANCNT          Paragraph: MAIN-PARA                       Line:     22
Program-Id:  LOANCNT                     OPEN                            Line:     23
Program-Id:  LOANCNT                     IF                              Line:     24
Program-Id:  LOANCNT                     PERFORM                         Line:     28
Program-Id:  LOANCNT                     UNTIL                           Line:     28
Program-Id:  LOANCNT                     READ                            Line:     29
Program-Id:  LOANCNT                     IF                              Line:     32
Program-Id:  LOANCNT                     ADD                             Line:     33
Program-Id:  LOANCNT                     IF                              Line:     35
Program-Id:  LOANCNT                     UNTIL                           Line:     28
Program-Id:  LOANCNT                     READ                            Line:     29
$ COB_RUNTIME_CONFIG=circ.cfg COB_SET_TRACE=1 COB_TRACE_FORMAT="%I %L %S" COB_TRACE_FILE=trace2.log ./loantr
$ sed -n 3,8p trace2.log
LOANCNT     22     Entry: LOANCNT
LOANCNT     22 Paragraph: MAIN-PARA
LOANCNT     23            OPEN
LOANCNT     24            IF
LOANCNT     28            PERFORM
LOANCNT     28            UNTIL

The trace is the closest GnuCOBOL has to the statement numbers in a CEEDUMP traceback. The compiled-in checks (-d, which also switches the trace code on, and the -fec= options) and the debugger are on Debugging and Troubleshooting.

Side by side

Concern Enterprise COBOL on z/OS GnuCOBOL 3.2

Compile only

IGYWC, cob2 -c

cobc -c

Expanded source (COPY resolved)

MDECK option (SYSMDECK)

cobc -E

Intermediate form

assembler listing with LIST

cobc -C, -save-temps

Make a main program

first program of the program object, or cob2 -e

cobc -x at compile time

Static call

NODYNAM, bound into one program object

several sources on one cobc -x line, or linked objects

Dynamic call

DYNAM; library from STEPLIB

cobc -m; COB_LIBRARY_PATH

Listing and cross-reference

SOURCE, MAP, XREF, OFFSET, LIST

cobc -t file (source with copybooks, then warning and error counts), -v

Which file a name means

DD statement in the JCL

COB_FILE_PATH, or the variable DD_LOANTRAN naming the file

Run-time options

CEEOPTS, PARM, CEEUOPT

runtime.cfg, COB_RUNTIME_CONFIG, environment

Failure report

CEEDUMP, TERMTHDACT

libcob: error: messages, COB_SET_TRACE, stack trace

Common mistakes

  • Linking without marking the main program. On GnuCOBOL, cobc -x is needed when the main source is compiled, not only when linking, or the link ends with undefined reference to 'main'.

  • Forgetting COB_LIBRARY_PATH or STEPLIB. A dynamically called program that is not in the search path stops the run (module 'FEECALC' not found; on z/OS an abend S806), even though the build succeeded.

  • Reading a RC=4 compile as clean. A warning gives return code 4, and the default -comprc_ok=4 and COND=(8,LT,COBOL) allow the bind to continue; read the messages and treat each W as a decision.

  • Compiling without a saved listing. A production abend is read from the listing of the exact build. Without MAP, XREF and OFFSET (or LIST) from that compile, the dump offsets cannot be tied to statements.

  • Setting a high ARCH for a mixed estate. Code built with ARCH(15) can abend on a z16 or on the disaster-recovery machine. Use the lowest architecture in the estate and set it in the installation defaults.

  • Assuming optimization is free of risk. OPT(1) and OPT(2) assume valid data; invalid zoned or packed data that "worked" at OPT(0) can behave differently, so use NUMCHECK while testing.

  • Using NUMPROC(PFD) or TRUNC(OPT) as a tuning knob. Both are correct only when the data conforms; otherwise results are unpredictable.

  • Leaving NOSSRANGE in test builds. A subscript outside the table stores into the next field and nothing reports it. The compiled-in checks cannot be turned off by CHECK(OFF) at run time.

  • Relying on a runtime option without checking the source. RPTOPTS(ON) shows which source (PARM, CEEOPTS, installation) gave each value; do not guess.

  • Mixing AMODE 64 and AMODE 31 programs. LP(64) programs cannot be combined with AMODE 31 programs in one application, and cannot run in CICS or IMS.

  • Copying a COB_* setting into a z/OS job. Environment variables on GnuCOBOL have no meaning in a batch job; the data set behind a name is bound by the DD statement.