Non-XPLINK applications

To create a complex DLL or DLL application, you must comply with the following rules that dictate how you compile source modules. The first decision you must make is how you should compile your code. You determine whether to compile with either the DLL or NODLL compiler option based on whether or not your code references any other DLLs. Even if your code is a DLL, it is safe to compile your code with the NODLL compiler option if your code does not reference other DLLs.

The second decision you must make is whether to compile with the default compiler suboption for DLL|NODLL, which is NOCBA, or use the alternative suboption CBA. This decision is based upon your knowledge of the code you reference. If you are sure that you do not reference any function calls through function pointers that point to a function entry rather than a function descriptor, use the NOCBA suboption. Otherwise, you should use the CBA suboption.

As of V2R4 of OS/390® C/C++, use the following options to ensure that you do not have undefined results as a result of the function pointer pointing to a function entry rather than a function descriptor:

  1. Compile your source module with the CBA suboption of DLL|NODLL. This option inserts extra code whenever you have a function call through a function pointer. The inserted code invokes a runtime service of z/OS® Language Environment® which enables direct function calls through C/C++ function pointers. Direct function calls are function calls through function pointers that point to actual function entry points rather than function descriptors. The drawback of this method is that your code will run slower. This occurs because whenever you have function calls through function pointers z/OS Language Environment is called at run time to enable direct function calls. See Figure 1 for an example of the CBA suboption and an explanation of what the called z/OS Language Environment routine does at run time when using the CBA suboption.
  2. Compile your C source module with the NOCBA suboption of DLL|NODLL. This option has the benefit of faster running but with more restrictions placed on your coding style. If you do not follow the restrictions, your code may behave unpredictably. See DLL restrictions for more information.

Compile your C source modules as DLL when:

  1. Your source module calls imported functions or imported variables by name.
  2. Your source module contains a comparison of function pointers that may be DLL function pointers.

    The comparisons shown in Function pointer comparison in non-DLL code are undefined. To obtain valid comparisons, compile the source modules as DLL code.

  3. Your source module may pass a function pointer to DLL code through a parameter or a return value.

    If the sort() routine in Figure 5 is compiled as DLL code instead of non-DLL code, non-DLL applications can no longer call it. To be able to call the DLL code version of sort(), the original non-DLL application must be recompiled as DLL code.

  4. Your source module may define a global function pointer and another source module changes it.

    Consider the example programs shown in Table 1. You have the following two options when compiling them.

    1. If source module 1 is compiled as DLL code, source module 2 must also be compiled as DLL code.
    2. Alternately, you can compile source module 1 as DLL and source module 2 as NODLL(CBA).
    Table 1. Example programs to demonstrate compiling options
    Source module 1 Source module 2
    void (*fp)(void);
    extern void goo (void);
    void main() {
      goo();
      (*fp)();     /* call hello function */
    }
    #include <stdio.h>
    extern void (*fp)(void);
    void hello(void) {
      printf("hello\n");
    }
    void goo(void) {
      fp = hello;
    }

    Table 2 summarizes some of the ways that you could compile the two source modules and list the results. Both modules are linked into a single executable.

    Table 2. Examples of how to compile two source modules and list result
    How Modules Were Compiled Result
    • Source module 1 NODLL (NOCBA)
    • Source module 2 DLL(NOCBA)
    fp contains a function descriptor. Execution of fp will succeed because it is valid to the address of a function descriptor.
    • Source module 1 DLL(NOCBA)
    • Source module 2 NODLL(NOCBA)
    fp contains the address of hello. The execution of fp would abend because source module 1 expects fp to contain a function descriptor for hello.
    • Source module 1 DLL(CBA)
    • Source module 2 DLL(NOCBA)
    fp contains a function descriptor. The generated code will function correctly. It will run slower than if the source modules were compiled as DLL(NOCBA) because it will use Language Environment to make the function call.
    • Source module 1 NODLL(CBA)
    • Source module 2 DLL(NOCBA)
    A call to Language Environment made by the function call through the function pointer prevents a problem that would have occurred had a direct function call been made.

    If you do not use the DLL compiler option, and your source module calls imported functions or imported variables by name, there will be unresolved references to these variables and functions at bind time. A DLL or DLL application that does not comply with these rules may produce undefined runtime behavior. For a detailed explanation of incompatibilities between DLL and non-DLL code, see Compatibility issues between DLL and non-DLL code.