The terrible
I have some terrible old C code for chaining through z/OS control blocks, which I wrote when I was first learning C.
#define FLTCVT 16L
#define CVTASCBH 564L
#define ASCBJBNI 172L
#define ASCBJBNS 176L
char *plStor = (char*)FLTCVT;
char *plCVT = (char*)*(long*)plStor;
char *plASCB = (char*)*(long*)(plCVT+CVTASCBH);
char *pJBNI = (char*)*(long*)(plASCB+ASCBJBNI);
char *pJBNS = (char*)*(long*)(plASCB+ASCBJBNS);
if( pJBNI != 0 ) // pointer to job name
printf("JobnameI %8.8s\n",pJBNI);
if( pJBNS != 0 ) // pointer to started task
printf("JobnameS %8.8s\n",pJBNS);
This prints out the job name. This has worked, and is not the world’s best C code. *(long*)plStor says take the value of (*) plStor, treating it as a pointer to a long ((long *)).
It needs to have “char *plCVT” so when I add an offset, the offset units is char. If I had had long * plCVT I would have to use offset CVTASCBH/4 instead of CVTASCBH to give the correct offset. The offset of CVTASCBH chars is 564. The offset of CVTASCBH longs is 2256.
Chaining – the right way
In the code above I specified offsets. This is not best practice. It is better to use header files as it makes the code easier to understand – and future proof.
#include <cvt.h>
#include <ihaascb.h>
struct cvtmap * cvt = *((struct cvtmap **)0x10);
struct ascb * pASCB = (struct ascb *)(pCVT -> cvtascbh);
char * pName = pASCB -> ascbjbns;
printf("name %8.8s\n",pName);
The “*((struct cvtmap **)0x10)” code does not feel very elegant, but that’s C for you. Thanks to David Crayford for improving my C code. Who also said
It’s good practice to always code for 64 bit, even if you compile 31bit.
The above code is right – but it can be done in one instruction (which may not be as clear).
pName =
((struct ascb *) ((struct cvtmap *) *((struct cvtmap **)0x10) ) -> cvtascbh) -> ascbjbns;
111111111111111 11111111111111111 111111111111111111
2222222222222222222222222
333333333333333333333333333333333333333333333
444444444444444444444444444444444444444444444444444444444444444444444444444
Where the 1,2,3 are the extent of the parenthesis. This is harder to understand than the previous example.
You can do
typedef struct ascb * ASCB;
typedef struct cvtmap * CVT;
pName = ((ASCB) ((CVT) *((struct cvtmap **)0x10) ) -> cvtascbh)-> ascbjbns;
or
#define zASCB ( struct ascb *)
#define zCVT ( struct cvtmap *)
pName = (zASCB (zCVT *((struct cvtmap **)0x10) ) ) > cvtascbh)-> ascbjbns;
But I do not think these are as clear as the first examples (too many brackets for one thing).
Some clever code – which you should not use
I saw some “clever” code chaining along control blocks.
char * p = (char * )((int * __ptr32 * __ptr32 * )0)[4][165][53];
This is a good example of something which takes an expert seconds to write, but takes people not familiar with this a long time to understand.
The interpretation (thanks Bobby)
- Consider address 0 as pointing to an array of 4-byte elements (the last ptr32), take the [4]th element, so at 16 bytes after 0, which is where CVT lives,
- take that address and also consider that pointing to an array of 4-byte elements (the first ptr32), take the [141]th element, so (141* 4= )564 bytes beyond the start of the CVT, which is indeed CVTASCBH,
- take that address and also consider that pointing to an array of integer elements, also 4 bytes, take the 44th element, so (44 * 4= )176 bytes beyond the start of the ASCB, which points to the jobname.
The generated code looks like
* ppp = (char * )((int * __ptr32 * __ptr32 * )0)[4][141][44];
5810 0010 L r1,16
5810 1234 L r1,(*)int*(,r1,564) 4 * 141
5800 10B0 L r0,(*)int(,r1,176) 4* 44
5000 D0A8 ST r0,ppp(,r13,168)
Changing it slightly
* char* pppq= (char * )((char * __ptr32 * __ptr32 * )0)[4][141][44];
5810 0010 L r1,16
5810 1234 L r1,(*)uchar*(,r1,564) 4 * 141
E300 102C LLC r0,(*)uchar(,r1,44) 1 * 44 because it is a character
5000 D0AC ST r0,pppq(,r13,172)
This loads the single character at offset 44 (not 4 * 44 as it was for long *) and stores the single character. The offsets were of length 4.
If you know what you are doing the above code is compact and concise. For anyone else it could take hours to understand it. (I had to ask!)
If you make a small change it may not behave as you expect!
64 bit programs
The examples above were for 31 bit programs, referencing 31 bit addresses. You need to allow for 64 bit programs. You can specify an address is a 31 bit address by using the C qualifier __ptr32.
When compiled with 64 bit addressing the output is
ppp = (char * )((int * * * )0)[4][141][44];
LG r6,32
LG r6,(*)int*(,r6,1128)
LGF r0,(*)int(,r6,176)
STG r0,ppp(,r4,2240)
Where some offsets are now 64 bit – and the CVT at offset 16 is now at offset 32 ( 4 * 8 byte longs) and so wrong.
You can force it to treat an address as 31 bit using
char * ppp = (char * )((int * __ptr32 * __ptr32 )0)[4][141] ;
LLGF r6,16
LLGF r6,(*)uchar*(,r6,564)
LLGC r0,(*)uchar(,r6,44)
STG r0,pppq(,r4,2240)
and it now treats the offsets as length 4.
You can use __ptr64 to say this is a 64 bit address. __ptr32 and __ptr64 both work in both 31 and 64 bit programs.