Programming in C on OpenVMS

OpenVMS has supported the C programming environment for many years, first with the K&R-class VAX C compiler, and then the subsequent ANSI C compiler variously known as DEC C, Compaq C and HP C. Here you will learn some features specific to using C on OpenVMS…

If you've landed here and are looking for information on getting started with C programming and the associated DCL commands on OpenVMS (eg: edit, compile, link, run), please read the classic “Hello, World!” article first.

This article covers OpenVMS-specific constructs and mechanisms and interfaces that C programmers will want to learn about when working with OpenVMS. If you are just getting started with C programming on OpenVMS, again, please start with the classic “Hello, World!” on OpenVMS article.

The Compiler

The C compiler can use many modes, including the VAX C environment. (/STANDARD=VAXC) HoffmanLabs recommends you avoid using the VAX C compliance environment, as that environment accepts older syntax, and permits many coding constructs and errors that are correctly detected by more modern compilers. VAX C Migration documentation is available.

To fetch the C compiler version (on most any recent compiler), use the following command:
$ CC /VERSION

V6.4 is the final OpenVMS VAX version. V7.3 (or later) is the most recent version on OpenVMS Alpha and on OpenVMS I64.

Where permissible, use if CC /ENABLE=WARN=QUESTCODE can be useful. This (and additional warnings) comprises the OpenVMS analog to the lint tool.

The #include

Another common construct involves #include file syntax, as C on OpenVMS supports both the OpenVMS native file naming and the Unix-style file naming. The OpenVMS C I/O also accepts both OpenVMS native and Unix syntax for filenames.

As a C programmer, you are probably familiar with using include files to control how an application is compiled. With OpenVMS, you can select /FIRST_INCLUDE on the compilation, and this include is added to the front of the compilation. This can be used to select specific behaviors and processing without requiring source code modifications.

Include Files, Include Libraries

The include files used for C programs on OpenVMS are divided up into multiple directories, and from various sources.

The C compiler provides C-level include files specific to the language, while OpenVMS itself provides other definition files. This means that files such as stdio.h, stddef.h and stdlib.h are from the C compiler, while include files such as dvidef.h, jpidef.h and syidef.h are generated from OpenVMS files. The portable pieces are part of C, while the platform-specific pieces are provided by OpenVMS. Comparatively subtle, but it can help you determine where to look for an include file. (And on those C compilers that extract definitions, the OpenVMS-specific include files can be changed when OpenVMS is upgraded, and the reference copies are (only) made available to C applications through re-installation of the C compiler. More on this later.)

In addition to dvidef.h, jpidef.h and syidef.h, there are definition files for the system service routines (starlet.h contains declarations of the sys$-prefixed system service routines), and for various collections of routines such as the OpenVMS run-time library (the lib$ interface routines are found in lib$routines.h) and the string RTL (str$routines.h).

The C language definitions are stored in the DECC$RTLDEF.TLB text library, while the OpenVMS-associated system definitions are located in the public definitions library SYS$STARLET_C.TLB and the system-specific (private) definitions library SYS$LIB_C.TLB. DECC$RTLDEF.TLB and SYS$STARLET_C.TLB are static and generally documented, while SYS$LIB_C.TLB is internal, undocumented, often version-specific, and subject to change.

If you are linking a device driver using C or otherwise linking against the kernel of OpenVMS, you will need to specify the SYS$LIB_C.TLB on your compilation command. Usually by appending +SYS$LIBRARY:SYS$LIB_C.TLB/LIBRARY onto the compilation command. (For an example of resolving a reference from within SYS$LIB_C.TLB, see tr_print, and application- and driver-level tracing, or for a complete C example see C Tips: Generating a Bugcheck; Triggering a System Crash. Also see The Kernel Mode C (Device Driver) Library for information on the routines available to kernel-mode C code.) Otherwise, you can usually ignore this library.

The DECC$RTLDEF.TLB generic C standard definitions and SYS$STARLET_C.TLB OpenVMS-specific C text libraries (.TLB files) are searched by the compiler by default.

Various C compiler installations will extract the contents of these libraries into set of reference directories, and specifically into the SYS$SYSROOT:[DECC$LIB...] directory tree. These are reference copies of the include files, and are not used for compilations. On the most recent C compilers, these reference libraries are not updated; see the LIBEXT tool on OpenVMS Freeware V7.0 distribution for a mechanism that can be used to create these libraries from the contents of the text libraries.

RTL and System Service Include Files

OpenVMS has two broad groups of include files, those that are language-defined (eg: stdio.h and stddef.h) and those that are platform specific (eg: starlet.h and lib$routines.h).

First, the system-specific definitions. The OpenVMS system services (sys$ routines) are declared in starlet.h, while the rest of the run-time library tends to follow the pattern of lib$routines.h, str$routines.h and smg$routines.h for the LIB$, STR$ and SMG$ routines, respectively.

The C compiler automatically searches the OpenVMS-specific C language definitions library SYS$STARLET_C.TLB. The compiler finds and loads the include files from the text library as part of the compilation pre-processing.

You can use LIBEXT mentioned earlier to extract the contents of the C definition libraries, and then SEARCH them.

Standard C Include Files

Various of the standard C library include files are present on OpenVMS, and you can see which header file is associated with a particular C call by looking at the on-line HELP text for the C routines.

With recent C compiler installations, HELP CRTL gets to the RTL information in the help library.

The C compiler searches the DECC$RTLDEF.TLB generic C standard library definitions library by default. Yes, an OpenVMS library of standard C library includes. Redundancy is intended. The compiler finds and loads the include files from the text library as part of the compilation pre-processing.

You can use LIBEXT mentioned earlier to extract the contents of the C definition libraries, and then SEARCH them.

Available functions

The C library ships with OpenVMS, and newer OpenVMS releases tend to have added C standard library calls.

A list of which C functions are present in which OpenVMS release are included in an appendix of the C Run-time Library Reference manual. This manual is included in the OpenVMS operating system documentation shelf in recent OpenVMS releases, and in the C shelf in older releases.

special cases: fork

Among the more problematic C constructs within portable code is the Unix fork call. Unix fork() has the behavior of creating a new process and cloning the existing process address space. This sets up the new process with a copy-on-write virtual address space.

Here is part of what the BSD man page for the vfork call includes on this topic: Vfork() can be used to create new processes without fully copying the address space of the old process, which is horrendously inefficient in a paged environment. It is useful when the purpose of fork(2) would have been to create a new system context for an execve. Vfork() differs from fork in that the child borrows the parent's memory and thread of control until a call to execve(2) or an exit (either by a call to exit(2) or abnormally.) The parent process is suspended while the child is using its resources.

This address space cloning capability is not available on OpenVMS or various other platforms, which means code based on fork is not portable to OpenVMS. What is available on OpenVMS and on various other platforms is a subset of fork around the process creation mechanisms, and this mechanism is sufficient for many applications. Substitute vfork for existing fork calls. And vfork is available on most (all?) Unix platforms.

If the application actually uses the memory-cloning capabilities of fork, then porting will require reworking the code rather more extensively.

Unix-style File Syntax

Unix-style naming can be used with include files (and with I/O references through the C standard I/O library), and rules for using logical names to locate the specific directories containing the target include files exist in the C programming documentation for OpenVMS. For instance, X Window System include files are often referenced with #include <X/whatever.h>, and the logical name X is defined as the directory containing the X Window includes.

On most recent OpenVMS and DECwindows (X Window System) software releases with C around, this X logical name is defined by default; view the logical name configuration with SHOW LOGICAL X for details.

The Descriptor

Classic C programming uses pointers to various structures, including null-terminated strings; ASCIZ strings. These are used within the OpenVMS standard C library, though most OpenVMS interfaces use descriptors. An OpenVMS construct that will be entirely new to even experienced C programmers is the string descriptor. This is typically a small data structure, containing the data length, data type, descriptor class, and data address for a chunk of data.

Here is a static (class S) text string (data type T) descriptor:
Date.dsc$b_class = DSC$K_CLASS_S;
Date.dsc$b_dtype = DSC$K_DTYPE_T;
Date.dsc$w_length = 0;
Date.dsc$a_pointer = NULL;

You have full control over the length and pointer values and the allocated storage referenced by the pointer, and must maintain this within your application code. (A C preprocessor macro ($DESCRIPTOR( YourSymbolName, "Your String")) is available in the descrip.h include file. In addition to descrip.h, also see the dscdef.h include file.)

A dynamic descriptor turns over the memory buffer associated with the string to the RTL. The buffer is maintained by the OpenVMS memory management routines, and can be resized or released as required.

Here is an example of manually populating a dynamic text string descriptor:
Date.dsc$b_class = DSC$K_CLASS_D;
Date.dsc$b_dtype = DSC$K_DTYPE_T;
Date.dsc$w_length = 0;
Date.dsc$a_pointer = NULL;

Once a dynamic descriptor is initialized (to zero and null) and the initial allocation has been acquired by the RTL, you must not modify the contents of the descriptor structure itself save using RTL routines; you cannot rewrite the length nor pointer. See the lib$sget1_dd call and (when done with the descriptor) lib$sfree1_dd call. Once storage has been allocated to the descriptor, remember to deallocate the string via lib$sfree1_dd or analogous; to avoid an application memory leak.

There are many and varied types of descriptors. The text descriptor is the most common, and what is shown here.

Descriptors are generally passed by reference. That is, descriptors are often associated with the C & ampersand operator and with pointers.

#include <descrip.h>
#include <lib$routines.h>
…
$DESCRIPTOR( MySymbolName, "My Output String")
…
RetStat = lib$put_output( &MySymbolName );

The above code excerpt displays the specified string descriptor via an OpenVMS RTL printf-like routine.

For a C routine that is called with a descriptor among its arguments (from Fortran, COBOL, Pascal, BASIC or another language, from a system service callback, or by C code that uses descriptors), then the general function structure and argument-passing of the receiving function looks like this:

#include <descrip.h>
#include <lib$routines.h>
…
extern void my_c_function( struct dsc$descriptor *MyDescriptorName )
  {
  int RetStat;
…
  RetStat = lib$put_output( MyDescriptorName  );
…
  return;
  }

Descriptors are passed by address; by pointer. This means that the call to lib$put_output shown above passes through the pointer; you don't need to use the & to get a pointer to the descriptor structure.

If you need to look inside a descriptor, you can access the dsc$ data structure directly or you can use lib$analyze_sdesc or other similar mechanisms.

Ancillary Programming Tools

The Structure Definition Language (SDL) on the OpenVMS Freeware can be invaluable for constructing your own language-independent data structures.

For information on SDL, see Using SDL; the Structure Definition Language.

Condition Values and Error Handling

OpenVMS condition values have a defined format. At its simplest, odd values indicate success and even values indicate failure. To detect this, you can use various macros from the stsdef.h include module. The following sequence is a classic completion check:

if (!$VMS_STATUS_SUCCESS( RetStat )) 
  lib$signal( RetStat );

Classic exit values from main() are also accepted:

return EXIT_SUCCESS; or return EXIT_FAILURE;

.

These are converted into DCL $severity and related symbols.

An example of debugging the ACCVIO access violation (which is one of the more common conditions that can arise) and of implementing a basic signal handler are available.

Familiar with gcc or llvm or the Linux and Unix tool chain?

OpenVMS splits out the compilation pass (the cc command) and the linking pass (ld); with the GNU Compiler Chain (gcc) pieces these two steps are typically combined. gcc typically invokes the c compiler and the ld linker on behalf of the user.

When building an executable application program with OpenVMS, you invoke the compiler one or more times, and then the linker one more more times, with each of these as separate commands.

OpenVMS has an analog to the static library (what various Unix and Linux boxes refer to with the .o extension on the filename) with its object libraries and the LIBRARY command. This is a package of objects that can be referenced as a unit during subsequent LINK operations. Object libraries particularly make it easier to create and maintain a LINK command in a command procedure, as you can tell the LINKER to search for referenced symbols from one object library file rather than from an explicit and manually-maintained list of individual object files.

There is also an analog to the dynamic library (what can be the .so type on Unix and Linux), with the OpenVMS construct known as the shareable image. OpenVMS shareable images are pre-compiled and pre-linked libraries of data or (usually) of code. Shareable images can referenced within an application LINK command, and a number of these shareable images (including the Run-Time Library (RTL) images) are referenced automatically and by default by the LINK command.

Information on building your own shareable images is available.

Known Bugs

Functions implementing MD5 do not compile with optimization enabled on OpenVMS VAX, using VAX C. Disable the disjoint compiler optimization CC /OPTIMIZE=NODISJOINT when compiling an MD5 function.

This is likely a permanent restriction of the VCG code generator used by VAX C, DEC C, Compaq C and HP C compiler on OpenVMS VAX. This restriction does not affect OpenVMS Alpha nor OpenVMS I64.

Related Topics, Example Source Code

Calling mail from C, processing DCL symbols and logical names from C, using FDL and SDL and related APIs from C, and a large collection of example C source code (newuser), as well as a large library of system service and run-time library (RTL) example C code available, including mixed-language calls (COBOL and C) and string descriptors.

And as you get programming in C on OpenVMS, you'll almost certainly see your own examples of the access violation (ACCVIO), the OpenVMS analog of the bus error or such — and information on debugging an access violation (ACCVIO) error is available here at HoffmanLabs.

Revision History

Most recent changes first.

C Traps and Pitfalls

Go read Andrew Koenig's C Traps and Pitfalls paper. Now.

The C language is like a carving knife: simple, sharp, and extremely useful in skilled hands. Like any sharp tool, C can injure people who don’t know how to handle it. This paper shows some of the ways C can injure the unwary, and how to avoid injury.

CC /SWITCH=REDUCE_RELOCATIONS

The OpenVMS I64 C compiler offers /SWITCH=REDUCE_RELOCATIONS, which is reportedly an undocumented feature of C that causes the code generator to cause psect-relative relocations to be deferred from the typical LINKER processing to the image activation and run-time processing.

Reportedly documented for COBOL and Pascal compilers, but also present in C.

This qualifier can be used to work around the %ILINK-E-NOT22BITS error and the 22-bit psect-relative addressing limits with OpenVMS I64; the short data segments are limited to for megabytes.

Did you mean Freeware V7?

see the LIBEXT tool on OpenVMS Freeware V8.0 distribution

Steve, I didn't spot LIBEXT on the Freeware V8.0 distribution. However, as you mentioned elsewhere on this blog it is available on the Freeware V7.0 distribution.

LIBEXT from the Freeware V7.0 distribution is available on HP's OpenVMS Freeware site, or at SAIC's OpenVMS freeware archive.

Galen

Moderator edit: corrected the site-local URL; also see the Freeware web page for links to various archives.

FW7

Whoops. Brain cramp. Link fixed. Thanks!

types.h

For an example of the int32-related typedef declaration mechanisms (discussed over in a topic on porting application code), see the types.h associated with your typical C compiler environment. These typedefs allow you to specify your variable size based on the bit size, if that's what you are looking for. (Sometimes you are, and sometimes you are looking for the native size for the platform.)

$DESCRIPTOR

The $DESCRIPTOR macro is handy for filling in fixed length string descriptors.

The CRTL on VMS is a large and wonderful thing on VMS and has a big fat manual of its own which is worth looking at.

It has many features which are controlled by logicals.

One oddity is that if you return a value of 0 from main() then the RTL will change this to 1 as it assumes you want to change a unix success value 0 to a VMS success value 1.
________
Ian Miller

Caveat about $DESCRIPTOR

As $DESCRIPTOR uses sizeof() to fill in dsc$w_length, it can't be used with ordinary char* strings. For those, an alternative version has to be defined that uses strlen() instead.

And beware that passing char[] to subroutines promotes them to char*!

Good Points

EXIT_SUCCESS and EXIT_FAILURE are the 0 and 1 values at the application, and the trailing processing converts these into the expected low-bit set (LBS) and low-bit clear (LBC) values expected by DCL and DCL-based error processing.

On current OpenVMS, the C RTL manual is part of the base OpenVMS documentation set; you'll find a version with the C manuals, and often the newest C RTL manual is the one found with the OpenVMS release itself. Not the one found with the compiler. The manual moved from the C compiler documentation set to the base OpenVMS documentation set.