Binding in Unix on z/OS. A whole new world

I had been happy compiling and binding small programs in Unix on z/OS. It got a bit more complex as my programs got bigger and needed more files at bind time.

I stumbled into an area which I knew nothing about, (the pirate world of ar!) and which may help with doing binds.

My simple compile and bind script

name=wr 
rm ${name}.o
export _C89_CCMODE=1
p1="-Wc,arch(8),target(zOSV2R4),list,source,ilp32,gonum,asm,float(ieee) "

p8="-Wc,LIST(c.lst),SOURCE,NOWARN64,XREF,SHOWINC"

xlc $p1 $p8 -c $name.c
xlc $name.o -o wr -V 1>a

This script

  • compiles a program (wr.c) putting the compile listing into c.lst
  • binds the .o file and outputs (-o) into a file wr, and puts the output listing into a

You can use the ld command to invoke the binder, or use the xlc command. The xlc command looks at the types of the files to decide the action. For example *.c means compile *.o means bind.

My messy compile and bind script

name=colinm 
rm ${name}.o
export _C89_CCMODE=1
p1="-Wc,arch(8),target(zOSV2R4),list,source,ilp32,gonum,asm,float(ieee) "
p7="-Wc,ASM,ASMLIB(//'SYS1.MACLIB') "
p8="-Wc,LIST(c.lst),SOURCE -Wa,LIST(133),RENT"

# compile it

xlc $p1 $p7 $p8 $p2 -c $name.c -o $name.o

as -mgoff -aegmrsx=a.lst BPXTPOST.s
as -mgoff -aegmrsx=a.lst BPXTWAIT.s
as -mgoff -aegmrsx=a.lst DUMMY.s
as -mgoff -aegmrsx=a.lst br14.s
as -mgoff -aegmrsx=a.lst cpfetch.s

# create a load module in a PDS

include=" BPXTPOST.o BPXTWAIT.o pcexit.o br14.o DUMMY.o DUMMY2.o mywto.o cpfetch.o "
l1="-Wl,LIST,MAP,XREF,AC=1 "

/bin/xlc $name.o $include -o "//'COLIN.LOAD(colinm)'" -V $l1 1>a

This script

  • compiles colinm.c into colinm.o, puts the listing into c.lst
  • assembles the files BPXTPOST… putting the listing into a.lst
  • binds the list of “text files” into a load module COLIN.LOAD(COLINM), setting AC=1, and producing a load map.

Linkedit control statements

name=pcexit 
export _C89_CCMODE=1
l2="-l //'CEE.SCEESPC' "
l3="-O EDCXSTRT"
# create a load module in a PDS
l1="-b LIST,MAP,XREF,AC=1 "
ss="//'CEE.SCEESPC.OBJ(EDCXSTRT)'"
ld $ss $name.o -v -o "//'COLIN.LOAD(PCEXIT)'" -V $l1 $l2 $l3 1>ab

This script generates binder like JCL statements. (ld does not use them – it is a JCL equivilent of what is used)

//* ld  --------------------------------------------------------------- 
//LINKEDIT EXEC PGM=IEWBLINK,
// PARM='TERM=YES,PRINT=NO,MSGLEVEL=4,STORENX=NEVER,LIST=NOIMP,XREF=YESc
// ,MAP=YES,PRINT=YES,MSGLEVEL=0,LIST,MAP,XREF,AC=1'
//LDIN0001 DD DSN=CEE.SCEESPC.OBJ(EDCXSTRT),DISP=SHR
//LDAT0001 DD DSN=CEE.SCEESPC,DISP=SHR,DCB=DSORG=DIR
//SYSLMOD DD DSN=COLIN.LOAD(PCEXIT),DISP=REP,DATACLAS=,DSNTYPE=LIBRARc
// Y,MGMTCLAS=,SPACE=,STORCLAS=,UNIT=,DCB=(RECFM=U,LRECL=0,c
// BLKSIZE=6144,DSORG=PO)
//SYSPRINT DD PATH='/dev/fd1',PATHOPTS=(ORDWR,OCREAT,OAPPEND),FILEDATAc
// =TEXT,PATHMODE=(SIROTH,SIRGRP,SIRUSR,SIWOTH,SIWGRP,SIWUSc
// R)
//SYSTERM DD PATH='/dev/fd2',PATHOPTS=(ORDWR,OCREAT,OAPPEND),FILEDATAc
// =TEXT,PATHMODE=(SIROTH,SIRGRP,SIRUSR,SIWOTH,SIWGRP,SIWUSc
// R)
//SYSLIN DD *
INCLUDE LDIN0001
INCLUDE './pcexit.o'
LIBRARY LDAT0001
ORDER EDCXSTRT
/*

Where

  • -l //’CEE.SCEESPC’ becomes a binder LIBRARY statement which points to a PDS, which gets searched before SYSLIB
  • -O EDXCSTRT becomes the binder ORDER statement. For some programs, some CSECTS have to be in the right order. You can specify -O name1,name2.
  • -b…. pass these parameters to the binder
  • //’CEE.SCEESPC.OBJ(EDCXSTRT)’ contains some precompiled code. It gets mapped to INCLUDE … from a JCL dataset
  • $name.o is the precompiled object input to the binder.

    Using an archive file (the ar command)

    An archive file is something between a tar file, and a PDS with the output of compiles. (I have a dataset COLIN.OBJLIB in ISPF).

    * compile an assembler file
    as -mgoff -aegmrsx=a.lst BPXTPOST.s
    ar -rc arch.a BPXTPOST.o

    This

    • assembles the program BPXTPOST.s, creating BPXTPOST.o
    • stores it in the archive arch.a

    I can then bind with it

    /bin/xlc colinm.o   arch.a                 -o out.load   -V $l1    1>a 

    This takes the colinm.o and any .o files it needs from the arch.a archive.

    What is in the archive?

    The ar command

    ar -tv  arch.a

    gives

    rw-r--r-- 990021/990000     220 May 14 09:36 2026 __.SYMDEF  
    rw-r--r-- 990021/0 1200 Apr 27 10:29 2026 BPXTPOST.o
    rw-r--r-- 990021/0 1200 Apr 28 19:18 2026 BPXTWAIT.o
    rw-r--r-- 990021/0 720 May 11 15:11 2026 br14.o

    The command nm (Display symbol table of object, library, or executable files)

    nm -M   arch.a

    gives more information

    BPXTPOST.o:                               
    0 d --- --- - BPXTPOST
    0 T ANY ANY - BPXTPOST
    0 d --- --- - B_IDRL
    0 d --- --- - B_PRV
    0 d ANY ANY - B_TEXT
    0 U --- --- - CEESTART

    Where

    • 0 is offset from the start
    • d is Initialized data (bbs), local
    • T is Text symbol, global
      • ANY ANY – is RMODE(ANY) AMODE(ANY) Compiler_options(-)
    • U is undefined

    Using the nm command on an executable gave

    ...     
    128 U --- --- - B_TEXT
    1312 T MIN MIN - CEEARLU
    1312 U --- --- - CEEARLU
    728 U --- --- - CEEBETBL
    728 T MIN MIN - CEEBETBL
    ...
    104 D --- --- - locs
    152 T ANY ANY - main
    152 U --- --- - main
    40 U --- --- - optarg
    40 D --- --- - optarg
    ...

    we can see the offset of the main entry point is at offset 152 in the module.

    Using Rexx under z/OS Unix Services to display thread information

    I wanted to display information about threads running in a big Java application, and see what was executing during set up.

    Rexx can provide information on threads, and so I was able to create a script which provided process thread data. There was documentation, but it was not very clear, so I’m documenting what I learned

    My program

    /* rexx */ 
    do k = 1 to 20
    call procinfo
    say "jobname asid ppid pid threadid tcb cmdline"
    do i=1 to bpxw_pid.0
    x =procinfo(bpxw_pid.i,'process')
    if x = '' then iterate
    if bpxw_LOGNAME.i <> "ZWESVUSR" then iterate
    y = procinfo(bpxw_pid.i,'thread')
    if y = '' then iterate
    do j=1 to bpxw_threads
    xtcb = d2x( bpxw_TCB.j)
    say bpxw_JOBNAME d2x(bpxw_ASID) right(bpxw_PPID,8),
    right(bpxw_PID,8) bpxw_THREAD_ID.j xtcb bpxw_CMDLINE
    end
    end
    sleep(1)
    end
    • call procinfo returns a list of process ids in a stem bpxw_pid. and userids in a stem bpxw_LOGNAME
    • do i=1 to bpxw_pid.0 iterate through the list of the processes
    • x =procinfo(bpxw_pid.i,’process’) get the process information about the process id in bpxw_pid.i.
    • if x = ” then iterate if the thread no longer exists – do the next one
    • if bpxw_LOGNAME.i <> “ZWESVUSR” then iterate ignore threads from other userids
    • y = procinfo(bpxw_pid.i,’thread’) get the thread information for the process id in bpxw_pid.i.
    • if y = ” then iterate if no data is returned skip this
    • do j=1 to bpxw_threads for each thread in the process …
    • xtcb = d2x( bpxw_TCB.j) convert the thread TCB from decimal to hex.
    • say …
      • bpxw_JOBNAME this is from the process information
      • d2x(bpxw_ASID) display the ASID of the process in hex
      • bpxw_THREAD_ID.j the thread id.