z/OS MVS and z/OS UNIX Interop in PL/I Program? z/OS MVS and z/OS UNIX Interop in PL/I Program? unix unix

z/OS MVS and z/OS UNIX Interop in PL/I Program?


One purpose of IBM's Language Environment (LE) runtime was to make COBOL, PL/I, Assembler, and FORTRAN interoperable. C and C++ later came along for the ride.

The compilers that generated non-LE conforming code didn't play well with each other (you could get all the players to work together if you were careful). The compilers that generate LE conforming code do play well with each other. I've written COBOL code that uses C runtime routines (fopen, fseek, fread, fclose, various regex routines) and that worked fine due to LE.

Your response of "Well, kind of" to my question about whether you're using IBM Enterprise PL/I may indicate you're already in an unsupported configuration.

If your runtime is LE, you should be okay calling C runtime routines IBM supplies. If your runtime includes some of the old unsupported OS PL/I routines, you might be able to get calls to C runtime routines IBM supplies to work -- but it it were me in that situation I wouldn't sleep soundly. If you can relink your old code to use LE versions of the old OS PL/I runtime routines you may find yourself on more solid ground.


There's a lot of confusion about the relationship between z/OS and UNIX Services, but the thing to remember is that these aren't two distinct things...pretty much any task can become a UNIX process and make USS function calls, with the proper setup.

So really your question is two questions in one:

  1. How do I get my task dubbed as a UNIX process so that I can issue USS kernel functions?
  2. Is my PL/I compiler compatible with the LE runtime and with object libraries having code built using other LE languages?

The first part - how to get your process dubbed as a UNIX process - is pretty straightforward. IBM's approach requires you to be connected to the USS kernel (the OMVS address space) before you can invoke UNIX functions, but typically this happens automatically the first time you invoke a USS function.

You do need a certain amount of system setup before USS can be used. Of course, OMVS itself has to be active (though not having it is a rarity these days). Your security administrator needs to give you a UID number, and possibly create a home directory for you. Assuming this part is okay, all you need to do is invoke a USS function and now you're a UNIX Services process.

Pretty much any app can invoke IBM's USS callable services (that's all the modules with names starting with BPX1/BPX4) - all that's needed is something supporting standard OS linkage. Indeed, this is pretty much what IBM's runtime libraries do. A good test would be to call BPX1GPI (that's UNIX "getpid()")...it returns your UNIX process ID, and if you can get this working, you're good to go with most of the other UNIX Services. If you were to trace through the LE "getpid()" implementation, you'd find it's not much more than a thin layer over BPX1GPI, so there's no reason you can't call the underlying function yourself...this works with most of the USS kernel functions, not just getpid(), so if you can't figure out how to open a socket, calling BPX1SOC is always a good "plan B".

Keep in mind that there's a difference between being a UNIX process and running under something like the bash shell...the shell does things like setup STDIN/OUT/ERR and so on - if you need these things, you'll need to do this yourself if you just connect to USS out of nowhere. You can't call things like printf() until you have standard file descriptors setup.

A simple way to get started might be to write yourself a short C program that runs as a UNIX process, setting up STDIN/OUT/ERR (and whatever else you need), and then invoking your current PL/I program. This "wraps" your current code in a way that sets up whatever USS items you need, without having to do any of this in PL/I. You might also find this a convenient way to do some things that are otherwise challenging in PL/I, such as calling DLL functions...just write a short C function to do what you need, and then call this function from your PL/I code.

As for the second part of your question, if your PL/I uses the LE runtime (which it will unless it is very old), then mixing code from other libraries is much simpler than it sounds. As for the core runtime stuff, in most cases, the LE runtime functions are smart enough to be "bi-modal" in the sense that the same runtime function works in both USS processes and non-USS processes. Object code is object code, and the difference between (say) a z/OS file open and a UNIX Services file open is nothing more than whether the runtime calls SVC 19 (for z/OS OPEN) or BPX1OPN (for UNIX Services files). This just means that once your code is dubbed as a USS process, you can pretty much generally intermix z/OS and USS functions as you see fit.

This also means that there's no reason you can't use things like "libxyz.a" in a PL/I program, assuming there are no incompatibilities at the LE level and so on. It may be a challenge to get the binder to resolve everything the way you expect, but it should all work if you persist.

There will be a few things that are difficult from PL/I (and I apologize - I'm not a PL/I expert). An example might be calling something like your libcurl example where the library is a DLL and not a static archive. Again, the trick here might be to use a short C "stub" that you can call from PL/I, and that code can do the magic necessary to load and call a DLL. Otherwise, I think you'll find this a pretty easy exercise once you get all the pieces pulled together.


Answer just the 'static linking of C and PL/I objects' part of the question.

A compiled object, from C, or PL/I are just compiled objects. Where you compile them (TSO or USS) make no differences; and also where they resides (PDS/PDSE, or HFS directory) also make no differences.

In fact, you can easily copy from HFS (example, hello.o) to a PDS.

cp hello.o "//'MYHLQ.OBJ(HELLO)'"

or even compiled at USS and output the .o to a PDS

c89 -c hello.c -o "//'MYHLQ.OBJ(HELLO)'"

and then use BINDER to link (or the reverse, copy the OBJECT from PDS to HFS, and use ld to link)

However, there are 2 object file formats, XOBJ and GOFF.

GOFF is the newer format, and if you use XPLINK, it's a pre-req.64bit LE program on z/OS also pre-req XPLINK.GOFF itself also pre-req PDS/E.