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.