COBOLMainframe

COBOL Subprograms: CALL, USING, BY REFERENCE, and LINKAGE SECTION

TT
TopicTrick
COBOL Subprograms: CALL, USING, BY REFERENCE, and LINKAGE SECTION

COBOL modular programming uses the CALL statement to invoke subprograms, passing data between caller and called program through a defined parameter list. This is how large mainframe systems are composed from reusable business logic modules — a CICS front-end calls an account validation subprogram, which calls a database access subprogram, which calls a logging subprogram. Understanding CALL mechanics is essential for both writing new modules and maintaining existing ones.

What you'll learn in this guide:

  • CALL BY REFERENCE vs CALL BY CONTENT and when each is appropriate
  • LINKAGE SECTION structure and how caller and called program share memory addresses
  • Static vs dynamic CALL — the trade-off between performance and deployment flexibility
  • GOBACK vs STOP RUN vs EXIT PROGRAM — the classic subprogram termination bug explained

Next lesson: COBOL Copybooks: COPY, REPLACING, and Shared Data Definitions

Subprogram Anatomy

A COBOL subprogram looks almost identical to a main program, with two key differences: it receives data through a LINKAGE SECTION, and it returns with GOBACK (not STOP RUN):

cobol
IDENTIFICATION DIVISION.
PROGRAM-ID. VALACCT.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LOCAL-BALANCE     PIC S9(11)V99 COMP-3.
01 WS-VALIDATION-RESULT PIC X(1).

LINKAGE SECTION.
01 LS-PARMS.
   05 LS-ACCOUNT-ID     PIC X(10).
   05 LS-AMOUNT         PIC S9(9)V99 COMP-3.
   05 LS-RETURN-CODE    PIC S9(4) COMP.
   05 LS-ERROR-MSG      PIC X(50).

PROCEDURE DIVISION USING LS-PARMS.

MAIN-LOGIC.
    MOVE ZERO TO LS-RETURN-CODE
    MOVE SPACES TO LS-ERROR-MSG
    PERFORM VALIDATE-ACCOUNT-ID
    IF LS-RETURN-CODE = ZERO
        PERFORM VALIDATE-AMOUNT
    END-IF
    GOBACK.

VALIDATE-ACCOUNT-ID.
    IF LS-ACCOUNT-ID = SPACES
        MOVE -1 TO LS-RETURN-CODE
        MOVE 'ACCOUNT ID IS REQUIRED' TO LS-ERROR-MSG
    END-IF.

VALIDATE-AMOUNT.
    IF LS-AMOUNT <= ZERO
        MOVE -2 TO LS-RETURN-CODE
        MOVE 'AMOUNT MUST BE POSITIVE' TO LS-ERROR-MSG
    END-IF.

The LINKAGE SECTION defines the parameter structure. The PROCEDURE DIVISION USING clause lists the parameters. GOBACK returns control to the calling program.

CALL Statement

Static CALL (Literal Program Name)

cobol
WORKING-STORAGE SECTION.
01 WS-CALL-PARMS.
   05 WS-ACCOUNT-ID     PIC X(10).
   05 WS-AMOUNT         PIC S9(9)V99 COMP-3.
   05 WS-RETURN-CODE    PIC S9(4) COMP.
   05 WS-ERROR-MSG      PIC X(50).

PROCEDURE DIVISION.
    MOVE 'ACC0012345' TO WS-ACCOUNT-ID
    MOVE 500.00 TO WS-AMOUNT

    CALL 'VALACCT' USING WS-CALL-PARMS

    IF WS-RETURN-CODE NOT = ZERO
        DISPLAY 'VALIDATION FAILED: ' WS-ERROR-MSG
        PERFORM HANDLE-VALIDATION-ERROR
    END-IF.

CALL 'VALACCT' uses a literal program name — this is a static call. The linker resolves the reference at link-edit time and both programs share a single load module.

Dynamic CALL (Identifier Program Name)

cobol
WORKING-STORAGE SECTION.
01 WS-PROG-NAME         PIC X(8).

PROCEDURE DIVISION.
    MOVE 'VALACCT ' TO WS-PROG-NAME
    CALL WS-PROG-NAME USING WS-CALL-PARMS.

When the program name is stored in a field (WS-PROG-NAME), the CALL is dynamic. The runtime loads the program from the load library at first call. Subsequent CALLs within the same run unit find the program already resident in memory.

Dynamic CALL enables runtime polymorphism — you can change WS-PROG-NAME based on a configuration table to call different validation routines without recompiling the calling program.

Passing Parameters

BY REFERENCE (Default)

BY REFERENCE passes the address of the data item. The called program maps the LINKAGE SECTION directly onto the caller's memory:

cobol
CALL 'VALACCT' USING BY REFERENCE WS-CALL-PARMS.

*> 'BY REFERENCE' is the default — this is equivalent:
CALL 'VALACCT' USING WS-CALL-PARMS.

When the subprogram modifies LS-RETURN-CODE, it is modifying WS-CALL-PARMS's return code field directly. No copying occurs — the change is immediately visible to the calling program after CALL returns.

BY CONTENT

BY CONTENT passes a copy of the data. The called program receives its own copy; modifications do not affect the caller:

cobol
CALL 'CALCINTEREST' USING
    BY CONTENT  WS-PRINCIPAL
    BY CONTENT  WS-INTEREST-RATE
    BY REFERENCE WS-CALCULATED-INTEREST.

Here, PRINCIPAL and RATE are passed as copies (the subprogram cannot accidentally change them), but the RESULT field is passed by reference so the subprogram can return the calculated value.

BY VALUE

BY VALUE passes the actual value for scalar items — used primarily when interfacing with C programs or system routines:

cobol
CALL 'CGETENV' USING
    BY REFERENCE WS-ENV-VAR-NAME
    BY VALUE     FUNCTION LENGTH(WS-ENV-VAR-NAME)
    BY REFERENCE WS-ENV-VAR-VALUE.

Passing Individual Fields vs Group Items

You can pass individual fields or entire group records:

cobol
*> Pass entire group record (most common):
CALL 'UPDATEBAL' USING WS-UPDATE-PARMS.

*> Pass individual fields:
CALL 'UPDATEBAL' USING
    WS-ACCOUNT-ID
    WS-TRANSACTION-AMOUNT
    WS-NEW-BALANCE
    WS-RETURN-CODE.

The LINKAGE SECTION of UPDATEBAL must match the layout — either as a single group with the same substructure, or as four separate 01-level items in the same order:

cobol
LINKAGE SECTION.
*> Matching the individual-field CALL:
01 LS-ACCOUNT-ID         PIC X(10).
01 LS-TRANSACTION-AMOUNT PIC S9(9)V99 COMP-3.
01 LS-NEW-BALANCE        PIC S9(11)V99 COMP-3.
01 LS-RETURN-CODE        PIC S9(4) COMP.

PROCEDURE DIVISION USING
    LS-ACCOUNT-ID
    LS-TRANSACTION-AMOUNT
    LS-NEW-BALANCE
    LS-RETURN-CODE.

CALL Exception Handling

ON EXCEPTION / ON OVERFLOW

cobol
CALL 'EXTPROC' USING WS-PARMS
    ON EXCEPTION
        DISPLAY 'EXTPROC NOT FOUND OR LOAD FAILED'
        MOVE -99 TO WS-RETURN-CODE
    NOT ON EXCEPTION
        PERFORM CHECK-RETURN-CODE
END-CALL.

ON EXCEPTION fires when:

  • The called program cannot be found in the load library (dynamic CALL)
  • The program failed to load due to authorization or storage issues

Without ON EXCEPTION, a load failure causes an S806 abend.

CANCEL Statement

CANCEL releases a dynamically loaded program from storage, forcing it to be reloaded on next CALL. This also reinitializes its WORKING-STORAGE:

cobol
CANCEL 'VALACCT'.
CANCEL WS-PROG-NAME.

Use CANCEL when:

  • You need the subprogram's WORKING-STORAGE reinitialized (it normally retains values between calls)
  • You are done with a subprogram and want to release its storage
  • You need to pick up a new version of a subprogram that was updated in the load library

Do NOT CANCEL a program that is currently executing (including any program in your call chain above you).

Nested CALL Chains

COBOL supports call chains of arbitrary depth. A typical production pattern:

text
MAINPGM → CALL 'TRXPROCESS'
              → CALL 'VALIDATETRX'
                   → CALL 'DBREAD' (database access)
                   → CALL 'DATECHEK' (date validation)
              → CALL 'UPDATEDB'
                   → CALL 'DBWRITE'
              → CALL 'LOGAUDIT'

Each called program gets its own WORKING-STORAGE. LINKAGE SECTION items point back to the caller's storage — there is no stack copying for BY REFERENCE parameters.

Returning Multiple Values

COBOL has no native return value — all output goes through LINKAGE SECTION fields. The standard convention is:

cobol
LINKAGE SECTION.
01 LS-SERVICE-PARMS.
   05 LS-INPUT-ACCOUNT-ID    PIC X(10).   *> INPUT
   05 LS-INPUT-AMOUNT        PIC S9(9)V99 COMP-3. *> INPUT
   05 LS-OUTPUT-NEW-BALANCE  PIC S9(11)V99 COMP-3. *> OUTPUT
   05 LS-OUTPUT-RETURN-CODE  PIC S9(4) COMP.       *> OUTPUT: 0=ok, negative=error
   05 LS-OUTPUT-ERROR-MSG    PIC X(80).             *> OUTPUT: populated on error

Document clearly which fields are INPUT (set by caller before CALL), OUTPUT (set by subprogram before GOBACK), or both (caller sets, subprogram may update).

Complete Example: Account Update Service

cobol
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCTUPD.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DB-STATUS          PIC S9(4) COMP VALUE ZERO.
01 WS-ACCOUNT-BALANCE    PIC S9(11)V99 COMP-3.

LINKAGE SECTION.
01 LS-REQUEST.
   05 LS-ACCOUNT-ID      PIC X(10).
   05 LS-AMOUNT          PIC S9(9)V99 COMP-3.
   05 LS-TRANS-TYPE      PIC X(2).
      88 LT-CREDIT              VALUE 'CR'.
      88 LT-DEBIT               VALUE 'DR'.
   05 LS-RETURN-CODE     PIC S9(4) COMP.
   05 LS-MESSAGE         PIC X(80).

PROCEDURE DIVISION USING LS-REQUEST.

MAIN-LOGIC.
    MOVE ZERO   TO LS-RETURN-CODE
    MOVE SPACES TO LS-MESSAGE
    PERFORM FETCH-ACCOUNT-BALANCE
    IF LS-RETURN-CODE = ZERO
        PERFORM APPLY-TRANSACTION
    END-IF
    GOBACK.

FETCH-ACCOUNT-BALANCE.
    CALL 'DBFETCH' USING LS-ACCOUNT-ID
                         WS-ACCOUNT-BALANCE
                         WS-DB-STATUS
    IF WS-DB-STATUS NOT = ZERO
        MOVE -1 TO LS-RETURN-CODE
        MOVE 'ACCOUNT NOT FOUND' TO LS-MESSAGE
    END-IF.

APPLY-TRANSACTION.
    EVALUATE TRUE
        WHEN LT-CREDIT
            ADD LS-AMOUNT TO WS-ACCOUNT-BALANCE
        WHEN LT-DEBIT
            IF LS-AMOUNT > WS-ACCOUNT-BALANCE
                MOVE -2 TO LS-RETURN-CODE
                MOVE 'INSUFFICIENT FUNDS' TO LS-MESSAGE
                GOBACK
            END-IF
            SUBTRACT LS-AMOUNT FROM WS-ACCOUNT-BALANCE
        WHEN OTHER
            MOVE -3 TO LS-RETURN-CODE
            MOVE 'UNKNOWN TRANSACTION TYPE' TO LS-MESSAGE
            GOBACK
    END-EVALUATE
    CALL 'DBUPDATE' USING LS-ACCOUNT-ID
                          WS-ACCOUNT-BALANCE
                          WS-DB-STATUS
    IF WS-DB-STATUS NOT = ZERO
        MOVE -4 TO LS-RETURN-CODE
        MOVE 'DATABASE UPDATE FAILED' TO LS-MESSAGE
    END-IF.

Next Steps

Subprograms establish the call structure. The next essential tool is copybooks — COPY statements that share record definitions, SQL declarations, and data layouts across programs without duplication. See COBOL Copybooks, or return to the COBOL Mastery course.

Frequently Asked Questions

Q: What is the difference between a static and dynamic CALL in COBOL?

A static CALL resolves the called program's address at link-edit time — the called program is bound into the same load module as the calling program. It has the lowest call overhead but requires relinking when the called program changes. A dynamic CALL (CALL with a data-item containing the program name, or compiled with the DYNAM option) loads the called program from the library at runtime. Dynamic calls allow the called program to be replaced without relinking the caller, which is important for large systems where independent deployment of subprograms is required.

Q: How does COBOL handle data sharing between a calling program and a subprogram?

Data is shared through the CALL statement's USING phrase and the LINKAGE SECTION of the called program. The calling program lists the data items to pass in its CALL USING clause; the called program defines corresponding items in its LINKAGE SECTION. By default (BY REFERENCE), both programs share the same memory — changes by the subprogram are immediately visible to the caller. The LINKAGE SECTION does not allocate storage; it merely maps the caller's memory addresses to named data items for use inside the subprogram.

Q: What is the difference between GOBACK and STOP RUN in a called subprogram?

GOBACK returns control to the calling program — it is the correct verb to end a subprogram. STOP RUN terminates the entire run unit, including the main program and all programs in the call chain — it is only appropriate in the main program at the top of the call hierarchy. Using STOP RUN inside a subprogram abruptly ends the job step, bypassing any cleanup logic in the calling program. This is one of the most common and impactful bugs in COBOL subprogram development, particularly when subroutines migrated from standalone programs to called subprograms.


Ready to Master COBOL?

This lesson is part of the COBOL Mastery Course — the complete reference from first program to production mainframe. 20 modules covering COBOL syntax, file handling, DB2, CICS, JCL, and modern features. Free, fresher to senior.

→ View the full COBOL Mastery Course