DLL restrictions

Consider the following restrictions when creating DLLs and DLL applications:
  • The entry point for a DLL must be either z/OS® XL C/C++ or Language Environment® conforming. An entry point is considered Language Environment conforming if it includes CEESTART or if it was compiled using a Language Environment conforming compiler.
    Note: If the entry point for a DLL does not meet either of the above conditions, Language Environment issues an error and terminates the application.
  • In a DLL application that contains main(), main() cannot be exported.
  • The AMODE of a DLL application must be the same as the AMODE of the DLL that it calls.
  • DLL facilities are not available:
    • Under MTF, CSP or SPC
    • To application programs with main() written in PL/I that dynamically call z/OS XL C functions
  • You cannot implicitly or explicitly perform a physical load of a DLL while running C++ static destructors. However, a logical load of a DLL (meaning that the DLL has previously been loaded into the enclave) is allowed from a static destructor. In this case, references from the load module containing the static destructor to the previously-loaded DLL are resolved.
  • If a DLL contains static objects, the constructors are called during DLL load. ISO C++ requires that the global objects must be defined within the same compilation unit, but does not specify any order for these to be called; hence the objects are constructed in the order that they are defined. z/OS XL C/C++ enhances the standard behavior by providing #pragma priority to control the construction order for all global objects within the same execution load module. For more information, see the priority pragma in z/OS XL C/C++ Language Reference for the details of this pragma. A DLL is one execution load module and the #pragma priority allows you to control global object construction within a single DLL. On the other hand, you still have no control over the initialization order across different DLLs, or across a DLL application and the DLLs it references. If such order is important, the DLL provider has to define a protocol for applications to follow so that the interaction between the DLL and the applications happens in the required manner. The protocol must be part of the DLL interface design. Take note of the restriction in the previous bullet when defining such a protocol. A simple example would be requiring an application to call a setup() function, which is exported by a DLL, before any other references to the same DLL are made. More elaborate designs are possible. The techniques for controlling static initialization are well-discussed in C++ literature; you can reference, for example, Item 47 of Scott Meyers's Effective C++, 50 Specific Ways to Improve Your Programs and Designs.
  • You cannot use the functions set_new_handler() or set_unexpected() in a DLL if the DLL application is expected to invoke the new handler or unexpected function routines.
  • When using the explicit DLL functions in a multithreaded environment, avoid any situation where one thread frees a DLL while another thread calls any of the DLL functions. For example, this situation occurs when a main() function uses dllload() or dlopen() to load a DLL, and then creates a thread that uses the ftw() function. The ftw() target function routine is in the DLL. If the main() function uses dllfree() or dlclose() to free the DLL, but the created thread uses ftw() at any point, you will get an abend.
    To avoid a situation where one thread frees a DLL while another thread calls a DLL function, do either of the following:
    • Do not free any DLLs by using dllfree() or dlclose() (the z/OS Language Environment will free them when the enclave is terminated).
    • Have the main() function call dllfree() or dlclose() only after all threads have been terminated.
  • For DLLs to be processed by IPA, they must contain at least one function or method. Data-only DLLs will result in a compilation error.
  • Use of circular DLLs may result in unpredictable behavior related to the initialization of non-local static objects. For example, if a static constructor (being run as part of loading DLL "A") causes another DLL "B" to be loaded, then DLL "B" (or any other DLLs that "B" causes to be loaded before static constructors for DLL "A" have completed) cannot expect non-local static objects in "A" to be initialized (that is what static constructors do). You should ensure that non-local static objects are initialized before they are used, by coding techniques such as counters or by placing the static objects inside functions.
  • DLLs are enclave-level resources and, when opening and closing DLLs in a multithreaded environment, an application must control DLL load ordering with its own serialization mechanism to avoid unpredictable results.
    Example: Unless the application controls the order of DLL loads, unpredictable results can occur when different threads perform the following operations at the same time:
    • One thread uses a global symbol object handle, obtained via dlopen(), to search for a symbol whose name has been defined in various DLLs with different values.
    • Another thread closes the DLL that defines the symbol whose value is being sought.