C/C++

C/C++

C/C++

Your one-stop destination to learn and collaborate about the latest innovations in IBM C/C++ for z/OS compilers to develop high-performing C/C++ applications and system programs on z/OS while maximizing hardware use and improving application performance.

 View Only

Building a shared library with IBM Open XL C/C++ 2.1 in the z/OS UNIX environment

By Kai Nacke posted Mon March 10, 2025 06:06 PM

  

Building a shared library in the z/OS UNIX environment is very similar to Linux and other Unix systems. However, there are differences which you need to know.

Most examples presented here use 64 bit XPLINK (ibm-clang64 or ibm-clang -m64), but work with 32 bit XPLINK (ibm-clang -m32) in exactly the same way. See the section "Standard Linkage (-m32n) considerations" for the required options when using 32 bit StdLink (ibm-clang or ibm-clang -m32n).

The sample library

Let's begin with creating a static library which provides a way to greet a person. The interface in file greet.h is as follows:

void greet(const char *name);

The implementation in file greet.c makes use of a name if passed to the function, otherwise it uses a generic message:

#include <stdio.h>

void greet_stranger() {
  printf("Hello stranger\n");
}

void greet_buddy(const char *name) {
  printf("Hello %s\n", name);
}

void greet(const char *name) {
  if (name)
    greet_buddy(name);
  else
    greet_stranger();
}

Please note that the functions greet_stranger() and greet_buddy() are not part of the library interface. However, they may be useful to other compilation units in the library, so they cannot be made static.

A possible use of the library is in file hello.c:

#include <unistd.h>
#include "greet.h"

int main(int argc, char **argv) {
  greet(argc > 1 ? argv[1] : NULL);
  return 0
}

The library and the application are built as follows:

$ ibm-clang64 -c greet.c
$ ar -crs libgreet.a greet.o
$ ibm-clang64 -o hello hello.c -L$PWD -lgreet


Now we can check that the application works as intended:

$ ./hello
Hello stranger
$ ./hello Kai
Hello Kai

Building a shared library

Now that we have a working library we can turn it into a shared library. As an additional step we need to decide which symbols should be visible to a client of the shared library. By default, all symbols are hidden. To make all symbols visible we add the command line option -fvisibility=default when compiling the file.

$ ibm-clang64 -fvisibility=default -c greet.c

Please note that Linux and other ELF-based systems use the same command line option to control the visibility of symbols. However, the default setting is different: on z/OS it is hidden (-fvisibility=hidden) while on Linux it is export (-fvisibility=default).

To create the shared library, we need to specify the option -shared:

$ ibm-clang64 -shared -o libgreet.so greet.o

This command creates the shared library libgreet.so and the side deck file libgreet.x. The side deck file contains the list of exported symbols, and is needed when we bind an application to the shared library.

Also please note that there is no need to specify -fPIC which virtually all Linux systems require since the code generated by Open XL C/C++ is always position-independent.

When building the application, we need to specify the side deck file instead of the static library:

$ ibm-clang64 -o hello hello.c libgreet.x

Now, when running the application, the system needs to know where to find the shared library. The directories to search are given in the environment variable LIBPATH. We can prepend the current directory to the variable when running the application (bash syntax)

$ LIBPATH="$PWD${LIBPATH:+:${LIBPATH}}" ./hello

or we set it separately once, possibly in our .profile file:

$ export LIBPATH="$PWD${LIBPATH:+:${LIBPATH}}"

After that, we can call the application without additional settings:

$ ./hello

Controlling the symbol visibility

Usually, we want to hide implementations details, thus we require fine grained control over the symbol visibility. There is no standard way of doing that; Open XL C/C++ uses the #pragma export for it. To only export the function greet() from the shared library, we need to add #pragma export(greet) anywhere in the file greet.c, preferably before the function definition:

#pragma export(greet)
void greet(const char *name) {
  // Implementation.
}

With this change, we can rebuild the shared library:

$ ibm-clang64 -c greet.c
$ ibm-clang64 -shared -o libgreet.so greet.o

The difference compared to the previously built library is that now only the function greet() is visible. Even if we know that the implementation uses a function greet_stranger() we cannot call it because it is not visible.

Managing the side deck file

The side deck file is unique to z/OS. By default, it is created in the same directory as the shared library, and it shares the same base name with the library name, with the suffix .x added. With the option -Wl,-x we can change the name and location of the side deck file. For example,

$ ibm-clang64 -shared -o libgreet.so greet.o -Wl,-xgreet.x

produces the shared library file libgreet.so but names the side deck file greet.x, with both files being in the same directory.

Other platforms use import libraries instead of side deck files. We can create an import library by putting the side deck file into an archive. The advantage of this approach is that the command to bind an application is the same, independent of the use of a static or shared library. Doing so, the entire sequence of commands to build the shared library and the application becomes

$ ibm-clang64 -c greet.c
$ ibm-clang64 -shared -o libgreet.so greet.o
$ ar -crs libgreet.a libgreet.x
$ ibm-clang64 -o hello hello.c -L$PWD -lgreet

With this approach the side deck file becomes an intermediate file created during the build process.

There are some restrictions when mixing object and non-object files in an archive. See Autocall with archive libraries for more information.

Standard Linkage (-m32n) considerations

The default 32 bit StdLink (no mode option, or -m32n; called NOXPLINK in the XL C/C++ compiler) linkage does not enable shared library support as is. To build a shared library using 32 bit StdLink you need to specify the option -m32n-dll on all steps:

$ ibm-clang -m32n-dll -c greet.c
$ ibm-clang -m32n-dll -shared -o libgreet.so greet.o
$ ibm-clang -m32n-dll -o hello hello.c libgreet.x

When using 32 bit StdLink (no mode option, or -m32n) then option -shared automatically implies -m32n-dll. Thus, we could also run

$ ibm-clang -shared -o libgreet.so greet.o
with the same result. However, my recommendation would be to explicitly specify both options, because it helps when tracking down build problems.

Other points to consider

There are several limitations when using the option -mno-rent. For example, building a shared library in 32 bit StdLink does not support "norent". And while a shared library using XPLINK can be built as "norent", it cannot be used from an application built as "rent".

In general, we need to be careful about the used compiler options building the shared library and the applications using the shared library. For example, different values specified for the character mode (-fzos-le-char-mode=) can lead to strings printed in the wrong encoding, and using a different kinds of floating point values (-mzos-float-kind=) most likely results in wrong calculations, and may result in crashes.

Summary

In this blog we build a shared library on z/OS, which is very similar to Linux and other Unix systems. A difference to those other platforms is the use of the side deck file. However, with the addition of the side deck file to an archive we can mimic an import library, which is a well-known concept on other platforms.

0 comments
21 views

Permalink