In trying to get system exits written in C to work. I found my code was not being executed, even when the first instructions were a deliberate program check. I tried using the tried and trusted program AMASPZAP (known as Super Zap) for displaying the internals of a load module and zapping it – but my code was not there! Where was it hiding? When I took a dump of the address space my code was in the dump. Why was it invisible and not being executed?
HSM archives on tape
Like taking 20 minutes to recall a long unused dataset from HSM (mounting a physical tape to retrieve the data set), I had this vague memory of doing a presentation on the binder and the structure of load modules. After a cup of tea and a chocolate biscuit to help the recall, I remembered about classes etc in a load module.
When I joined IBM over 40 years ago you wrote your program, and used the link editor to create the load module, a single blob of instructions and data.
Things have moved on. Think about a C program, in read only memory. When you issue a load to use it, you get a pointer to the read only (the re-entrant instructions and data), and your own copy of the “global variables” or Writeable Static Area (WSA). When using the C compiler, at bind time it includes a bit of code with 24 bit addressing mode. This means you have code which runs in 31/64 bit mode, and some code resident in 24 bit mode! It is no longer a single blob of instructions and data.
Within the load module there are different classes of data for example
- C_CODE – C code
- C_WSA – for a C program compiled with RENT option. This is the global data which each instance gets its own private copy of
- B_TEXT code from the assembler
- Using the HL Assembler, you can define your own classes using CATTR.
A class has attributes, such as
- Should it be loaded or not. You could store documentation in the load module, which can be used by programs, but not needed at execution time.
- It is reentrant or not.
- Should this code be merged or replaced with similar code. For example the C Globals section would be merged. A block of instructions would be replace.
The binder can take things with similar attributes and store them together within a segment. You can have mixed classes eg B_TEXT and C_CODE, with the same RMODE attributes etc and have them in one segment. The C_WSA needs to be in a different segment because it has different attributes.
So where was my invisible code?
I needed to change my SPZAP job to tell it to dump out the C_CODE section. By default it dumps the B_TEXT sections. You can specify C_* or B_*. See the AMASPZAP documentation.
//STEP EXEC PGM=AMASPZAP
//SYSPRINT DD SYSOUT=A
//SYSLIB DD DISP=SHR,DSN=COLIN.C.LOAD
//SYSIN DD *
DUMPT COLIN CPPROGH C_CODE
This dumps out (decoding the data into instructions) load module COLIN, CSECT CPPROGH and the C_CODE class.
Why wasn’t my code executing? The code to set up the C environment was not invoking my program because I had compiled it with the wrong options!