3

I've been poking around in various resources in the Internet, but couldn't find a definitive answer that I understood, so I'm asking here:

How can I invoke z/OS UNIX code from z/OS MVS?

I'm aware that BPXBATCH PGM ... can invoke a z/OS UNIX Program from z/OS MVS TSO.

But can I do that e. g. inside a z/OS MVS PL/I program?

What I want to say is,

  • Can I link together statically z/OS MVS PL/I object modules and z/OS UNIX C object modules? (Is there even a difference between both, besides the different programming languages?)
  • Or can I dynamically link both?

My use case is: I have an old PL/I library from the 1970s that now gets the requirement to do networking. And as far as I understood, networking would go smoothly in the z/OS UNIX world.

The old PL/I library is statically linked against multiple other software that I cannot influence directly.

P.S.: Can someone with more reputation please establish a stackoverflow PLI tag? ;-)

cschneid
  • 10,237
  • 1
  • 28
  • 39
fjf2002
  • 872
  • 5
  • 15

3 Answers3

7

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.

cschneid
  • 10,237
  • 1
  • 28
  • 39
  • Thanks for your reply. We already statically link object modules from different languages (COBOL, PL/I, Assembler). In my eyes you missed the topic. My question was: Can I mix in z/OS UNIX object modules - or doesn't that matter? – fjf2002 Jun 08 '18 at 18:48
  • @fjf2002 If you mean "can I call modules that were designed to be called that were compiled via the z/Unix command line" then yes, with the caveat you provide for their prerequisites. If you mean "can I call modules such as curl and nslookup if I statically link them into my existing code" then probably not. – cschneid Jun 08 '18 at 20:43
  • OK, let's see if I understand. I try to rephrase: (1) I log into z/OS UNIX and compile some C code object module . Then I log into ISPF, compile some 1970s PL/I module, and statically link against the UNIX C module, and It will work? (2) If that is correct, what would stop me from linking against the libcurl library or other z/OS UNIX libraries? (3) And what about dynamic linking, are there other circumstances, cf. my first post? (4) I'm not sure what prerequisites do you mean? – fjf2002 Jun 09 '18 at 08:20
6

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.

Valerie R
  • 1,769
  • 9
  • 29
  • 1
    The part that inhibits a direct call from PL/I to C-library-functions is that in LE PL/I-C-ILC the C-program has to define an entry as being called from PL/I (`#pragma linkage(myentry,PLI)`) - which the library-functions don't provide. – piet.t Jun 14 '18 at 06:25
  • 1
    Great point - all the more reason to use a "C" stub around the PLI code...PL/I calls this wrapper "my_printf()" for instance, and the wrapper function can be compiled with the #pragma you mention and call the standard runtime "printf()" function (or whatever). – Valerie R Jun 15 '18 at 15:29
  • Valerie, wanted to ping you, would you please take a look at my Meta post "[Merge/cleanup tags \[zos\], \[mvs\], \[zseries\], and \[mainframe\]](https://meta.stackoverflow.com/questions/370743/merge-cleanup-tags-zos-mvs-zseries-and-mainframe)" and comment as someone who has much more knowledge about these tags than I do? – JoshMc Jul 11 '18 at 16:07
3

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.

Ka Lam
  • 381
  • 1
  • 6