←Part 11  Part 13→

The HP 3000--for Complete Novices

Part 12: Working with SOFTWARE LIBRARIES


by George Stachnik
Last time out, we talked about the reasons why large, complex applications are often built using software libraries. This month we're going to focus on the "how-to" of creating and using runtime software libraries.

To begin with, let's look at a simple application program. We'll start with a simple variation on the "Hello World" program often used as an introduction to programming. The source code shown in Figure 1 is a "Hello World" program written in COBOL and broken into two parts named HELLO1S and HELLO2S.

Each file contains the COBOL code for a part of our program. Execution of our program will begin with the code shown in HELLO1S, which displays the message "Hello World." HELLO1S then calls a subprogram called HELLO2.

The source code for HELLO2 is in HELLO2S, also shown in Figure 1. HELLO2 displays the message "Have a nice day" and then returns control to the calling program, which terminates.

Figure 1: "Hello World" Program Sources

$CONTROL Statements

Almost everything you're seeing here is generic, industry standard COBOL. HELLO2S contains the only line of code in this example that is proprietary, i.e., unique to the HP 3000. The first line of code in the HELLO2S source file is a $CONTROL statement. This $CONTROL statement is used to specify special parameters for the HP 3000 COBOL compiler. In the simple programs we've seen up until now, we haven't needed it because the compiler's defaults worked fine.

But HELLO2S needs to have a $CONTROL statement. Every program that we've seen up until now has executed as a process, but this program will not. We defined processes in one of the first articles in this series, but we'll review that definition now.

A process is an event: "the unique execution of a particular program, at a particular time, by a particular user." Normally, when you run a program (typically by entering a :RUN command), the HP 3000 creates a process for this event. This entails allocating a number of tables and blocks of storage, including a special area in memory called the stack. Every process has exactly one stack. The stack contains the data owned by that process. (In COBOL terminology, you might think of the stack as corresponding to the working storage of your program.)

But in the case of HELLO2, the situation is different. HELLO2 is not designed to run as a process. HELLO2 is a subprogram--it will be invoked by another program (in this case the main program shown in HELLO1S). The $CONTROL DYNAMIC parameter in HELLO2S tells the compiler not to generate the code that would allocate a stack for it. Instead, code will be generated for HELLO2 that will force it to share the stack of the calling program.

Compiling Programs

Figure 2 shows the two programs being compiled using the commands and procedures we introduced in part 9 of this series. (The compiler listings have been eliminated from Figure 2 to save space.) The COB74XL command invokes the native mode COBOL compiler, and compiles the code using the 1974 standard for COBOL. If you had wanted to use the 1985 standard, you'd have entered the COB85XL command instead. (In this case, the program is generic enough that it doesn't make any difference which standard you use.)

Figure 2: Compiling the Program

Each of the commands shown in Figure 2 creates an object file. The PA-RISC machine instructions that correspond to the COBOL code in HELLO1S are stored in an object file called HELLO1O. Similarly, the compiler output for HELLO2S is stored in a file called HELLO2O.

At this point, you could link both object files into a single program file using the following command:
:link (hello1o,hello2o),hello
But doing things this way would defeat the purpose of software runtime libraries. Instead of linking the two modules together into a single program file, we want to store them separately, and delay linking them together until somebody actually wants to run the program. In this way, we will achieve all the benefits we talked about in last month's article.

Let's begin by creating a program file for the main program (HELLO1). Figure 3 shows the object code in HELLO1O being linked to create a program file called HELLO1P. This program file contains the executable instructions that correspond to the COBOL code that we saw in HELLO1S. But remember, HELLO1P doesn't contain the code from HELLO2S. As a result, when we try to run the program, it fails. Figure 3 shows the error message that you'll receive:
UNRESOLVED EXTERNALS: hello2.
This error message is telling us that HELLO1P contains a reference to a procedure called hello2 which is not stored in this program file. Before HELLO1P can run, it must be linked to the procedure hello2. There is a way to display the contents of a program file and learn about the unresolved externals that it needs (without running it and letting it fail, as we did in Figure 3).

Figure 3: Linking and Running

The Linkage Editor

The MPE/iX linkage editor is a utility program that works with program files, object files, and software libraries. To run the linkage editor, enter the LINKEDIT command, as shown in Figure 4. You'll be prompted (LinkEd>) for linkage editor commands.

Figure 4 shows the LISTPROG command being used to display the contents of the program file called HELLO1P. The LISTPROG command displays details about the contents of the program file. For example, we see this program was linked with only two special capabilities: IA and BA (the defaults).

The bottom half of Figure 4 shows the various procedures that are stored in, and referenced by, the HELLO1P program file. Quickly scanning down this list, you can see that the COBOL compiler has generated a number of procedures for this program, with symbolic names such as $START$ (execution starts here for COBOL programs), and "hello" (the name we specified as the PROGRAM-ID in our COBOL source code).

Figure 4: Using the Linkage Editor

Below that are references to a number of procedures that are external to this program. The external references are marked with an "ext" in the column headed "Sym Scope." Note that hello2 (which is at the very bottom of the list) is not the only external reference. There are also external references to procedures such as COB_ CLOSE_FILES, COB_DISPLAY, and TERMINATE.

You might be wondering why these names were not called out when our attempt to run this program failed in Figure 3. The reason is that although these are external references, they are not unresolved externals. These names refer to procedures that are used by virtually all COBOL programs. Because these routines are shared among a large number of COBOL programs, the COBOL compiler does not link them into the program file. Instead they are stored in system libraries.

When you run a program on the HP 3000, system libraries are automatically searched so that references to system routines, such as TERMINATE, COB_ CLOSE_FILES, and COB_DISPLAY, can be resolved without any fuss.

But how are we going to link our program with HELLO2? We could put HELLO2 into a system library. That way, when we ran our program, the reference to hello2 would be resolved along with the references to the system routines. But it's not generally a good idea to mix system code and user code in the same library. Instead, we will build our own runtime library.

The linkage editor's BUILDXL command is used to build runtime executable libraries (also called "XLs"). Figure 5 shows the simplest form of this command. The example shows how to create an XL with the filename HELLOXL. Adding our hello2 procedure to this library is as simple as entering the ADDXL command, also shown in Figure 5. This command opens an object file and copies the procedures found there into an executable library. The "FROM=" parameter specifies the name of the object file, and the "TO=" parameter specifies the name of the library that is to receive the executables.

Figure 5: Building Runtime Executable Libraries

The LISTXL command, also shown in Figure 5, is used to display the results of our work. The "MODULE COUNT" tells us that there is now one module in the library. The "MODULE LIMIT" tells us that there is room for up to 500 modules. If we wanted to create a library with room for more (or fewer) than 500 modules, we could have specified the size when we entered the BUILDXL command.

Below the module counts, we see the names of the procedures stored in this library. The module HELLO2S contains the procedure called hello2. Note also that this procedure contains unresolved externals of its own (COB_DISPLAY, COB_ DISPLAY_FIN, and so forth). These will be resolved using the system libraries when the program is run, as we saw before.

All that remains now is to link the procedure stored in HELLOXL to the program file. Remember that we don't want to do this in advance--rather we want the linkage to be delayed until we're ready to run the program. We've seen that for system routines like COB_DISPLAY, this linkage is done automatically. But MPE/iX doesn't know where to find the procedure called hello2 (which is why the RUN command shown in Figure 3 failed). So we have to do something special at run time to make the linkage happen.

Running Programs with External References

Figure 6 shows a form of the :RUN command that we haven't seen yet. The "LIB=" parameter specifies the name of a library (or libraries) that will be searched (along with the system libraries) in order to resolve any external references. This time, the "hello" procedure is successfully located, and instead of an error message, we see the output of the program.

To recap, Figure 6 shows a listing of the various files that we've worked with thus far. The :LISTFILE command in the figure displays information about any files whose names begin with the characters "HELLO." Note the "file code," which appears under the heading "CODE." Using the file code, you can tell at a glance if a file is an object file (NMOBJ), a program file (NMPRG), or an executable library (XL).

Figure 6: Running the Program with External References

Compatibility Mode

If you've been with us from the beginning of this series of articles, you should remember that the HP 3000 supports two kinds of program files. Native mode program files are based on the 32-bit PA-RISC architecture; Compatibility mode program files are based on the older 16-bit "classic" HP 3000 architecture. Similarly, there are also two kinds of executable libraries: native mode executable libraries (XLs) and compatibility mode shared libraries (SLs). Since many HP 3000 applications still have components that remain in compatibility mode, we'll next turn our attention to CM libraries.

Figure 7 shows the COBOLII compiler being used to compile the two parts of our program. The COBOLII compiler is a compatibility mode compiler. That means it generates 16-bit machine instructions suitable for execution on an older 16-bit "classic" HP 3000 or on a 32-bit PA-RISC model of the 3000 using the compatibility mode emulator.

Compatibility mode compilers place their output in a file called a "User Subprogram Library" or USL for short. USL files are analogous in many ways to object files, the chief difference being that they contain 16-bit instructions instead of 32-bit PA-RISC instructions. The example shown in Figure 7 shows two USL files being created: HELLO1U and HELLO2U.

Figure 7: Compiling in Compatibility Mode

Segmenter and PREP

Just as the :LINK command is used to create a native mode program file, the :PREP command is used to create a compatibility mode program file. And just as object files are used as input to the :LINK command, the :PREP command takes one or more USL files as its input. Figure 7 shows the :PREP command being used to create a compatibility mode program file called HELLO1CM.

But HELLO1CM is not yet complete. It contains an unresolved external reference, just as its native mode counterpart did. And just as we used the linkage editor to manipulate native mode program files and libraries, we will now use a utility called SEGMENTER to manipulate compatibility mode programs and libraries.

At the bottom of Figure 7, we use the SEGMENTER command to invoke this utility. The segmenter's "USL" command is then used to specify the name of the USL file that contains the procedures we want to work on, in this case HELLO2U. Remember that this USL file contains the compatibility mode version of our hello2 procedure.

The last command shown in Figure 7 is the BUILDSL command, which, not surprisingly, is used to build an SL. Classic HP 3000s are more limited than their PA-RISC counterparts in a number of ways, one of which is illustrated here. The BUILDSL command's first parameter is the name of the library that you want to create. You must specify the name of a library here, but practically speaking, you really have only one choice: SL. Classic HP 3000 runtime libraries must be named "SL"--you can't use names like "MYSL" or "HELLOSL." In spite of this, you can have virtually unlimited numbers of compatibility mode SLs on a given system by putting them in different groups and accounts. For example, compatibility mode system routines are kept in a file called SL.PUB.SYS. Your library might be called SL.MYGROUP.MYACCT.

The BUILDSL command has three parameters: first, the name of the library (SL); second, the size of the library (in this example, 300 records); and finally, the number of extents, or physical pieces of disk used to store the library (in this case, 1).

Now we are ready to copy the code in the USL into this SL. But before we copy it, let's pause to examine it first. Figure 8 shows the LISTUSL command being used to display the contents of our USL file, in this case HELLO2U. Not surprisingly, this USL contains the HELLO2 procedure. To add this procedure to our SL, all we need to do is to enter the ADDSL command shown at the bottom of Figure 8. The only parameter we need to specify is the name of the procedure, in this case, HELLO2.

Figure 8: Checking and Copying Code with LISTUSL and ADDSL

To review our work, we can use the LISTSL command shown in Figure 9. Our compatibility mode library now contains the HELLO2 procedure, as well as references to other compatibility mode procedures used by the COBOL compiler--(C'DISPLAY'INIT, C'DISPLAY'FIN, and so on). These references will be resolved at runtime, just as they were in the case of the native mode program.

Figure 9: Reviewing with LISTSL

Running CM Programs with External References

To see how all this is used, refer to Figure 10. At the top of the figure is a :RUN command being used to run the compatibility mode program that we created in Figure 7. It fails for the same reason that the native mode program failed when we tried to run it in Figure 3. The program references an unresolved external (HELLO2) and MPE/iX doesn't know where to find it.

Figure 10: Running the CM Program with External References

Because compatibility mode libraries must be named SL, MPE/iX can search no more than three runtime libraries for unresolved externals:
:RUN MYPROG.XYZ.GEORGE;LIB=P
To recap everything we've talked about, Figure 11 shows a listing of all the files created in this article. Once again, note the way you can use the file codes to identify compatibility mode programs and libraries. The file code of our SL is (predictably enough), SL. Our USL files (HELLO1U and HELLO2U) are identified with the file code USL.

Figure 11: All the Files

I'd like to wrap up this month's article with a puzzler--did you notice that the compatibility mode program file we created (HELLO1CM) is missing from the listing in Figure 11? Can you figure out why? Next month, we're going to begin looking at how to use the application programs we've shown you how to create, and we'll answer that question then.
George Stachnik, the director of Hewlett-Packard's Technology Closeup series of television broadcasts, holds the title of Chief of Customer Communications. He works at Hewlett-Packard's campus in Cupertino, California.
  ←Part 11  Part 13→
     [3khat]3kRanger   [3khat]3kMail   Updated