Start of changes for 11.0.14.1

Using 31-bit native C or C++ code with the 64-bit Java VM

From release 11.0.14.1, you can recompile your native 31-bit code and use it with a Java™ application running on a 64-bit Java virtual machine (VM). This feature makes it easier for you to migrate Java applications that use native code from the 31-bit SDK to the 64-bit SDK, with minimal changes to the native code.

Before you begin

This feature requires APAR PH28966 on z/OS® 2.3 or later.

If your 31-bit C or C++ JNI code uses direct byte buffers, ensure that the native buffer storage is allocated in the 31-bit memory range, otherwise the code will not be able to access it. The JNI function GetDirectBufferAddress() checks whether it was called from JNI C or C++ code that is running in 31-bit addressing mode (AMODE 31). If so, the function returns either the buffer address, if it is in the 31-bit range, or null. To create a direct buffer and ensure that the native buffer memory is in the 31-bit range, you might have to modify your C or C++ code as follows:
  1. Allocate the 31-bit memory by using the appropriate function. If the C or C++ code will run in AMODE 31, use the malloc() function to allocate the memory. If the C or C++ code will run in AMODE 64, use the __malloc31() function to request memory in the 31-bit range.
  2. Create the Java direct byte buffer that references this 31-bit memory by passing the buffer in the address parameter of the NewDirectByteBuffer() function:
    jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);
    For more information about this function, see the Oracle JNI documentation.
  3. Track the resulting ByteBuffer object. When the object is no longer used by any Java methods, free the native buffer memory. This step is required because the JVM does not free the native buffer memory when it processes the ByteBuffer object during garbage collection.

About this task

IBM® Semeru Runtime Certified Edition for z/OS is 64-bit only. Usually, Java applications that run 31-bit native code can run only with a 31-bit SDK; mixing 31-bit code and 64-bit code in the same process space is challenging because of the incompatibility of the pointer sizes and memory addressability between these two types of code.

With APAR PH28966 on z/OS 2.3 and later, z/OS Language Environment® (LE) provides the capability to run AMODE 31 programs and AMODE 64 programs in the same address space. Separate LE environments are established for each addressing mode (AMODE). The IBM SDK takes advantage of this capability to simplify the migration of Java applications that use 31-bit native code to the 64-bit SDK. The Invocation and JNI APIs in the 64-bit SDK allow 31-bit native code to compile, link, and interoperate with the 64-bit SDK. The 64-bit VM also supports the loading and invocation of 31-bit native libraries and functions.

As part of this feature, the 64-bit SDK provides the following files:
  • A 31-bit compatible set of C/C++ JNI header files, which contain JNI data and argument types to support Invocation and JNI APIs. These files are in the javahome/include/jni31 directory, where javahome is the JAVA_HOME path of your IBM SDK installation. JNI types that represent object references and other VM constructs, like jobject, jarray, jclass, jmethodID, and jfieldID, are updated to be 64-bit representations.
    Note: Because the C language does not provide a way to define a 64-bit pointer type in a 31-bit application, a primitive 64-bit long long data type is used instead. This might lead to compiler warnings for code that assigns or compares JNI object references with NULL. You might need to update the code to assign or compare with 0 instead.
  • A 31-bit shared library, libjvm31.so, with a matching libjvm31.x side-deck file to enable 31-bit native code to link and resolve the Invocation and JNI APIs.
The high level steps to use this feature are as follows:
  1. Recompile any C or C++ native code that references JNI constructs against the include/jni31 set of JNI header files, and link against the libjvm31.so library.
  2. Run your Java application with the 64-bit SDK, specifying the -XX:+Enable3164Interoperability VM option.
Limitations:
  • The 31-bit native application must be a single-thread. The 64-bit Java application can be multi-threaded but only a single Java application thread is allowed to interoperate across the AMODE boundary. If a second application thread tries to load a 31-bit library or call a 31-bit C or C++ function, a java.lang.UnsatisfiedLinkError is usually thrown.
  • You must compile the 31-bit native application with standard CALL linkage; XPLINK is not supported.
  • LE Condition handling support is not available across the AMODE boundary. Specifically, -XCEEHDLR, -Xsignal:userConditionHandler=percolate, and related options are not supported. LE conditions are not converted into Java exceptions.
  • Signal chaining is not supported across the AMODE boundary.
  • Environment variables are copied across the AMODE boundary on the first transition only. Subsequent updates in one AMODE environment are not propagated back to the other; instead, the variables are tracked separately in each AMODE environment. For more information, see Environment variables propagation to secondary Language Environment in Introduction to AMODE 31 and AMODE 64 programs interoperability.

Procedure

  1. Recompile your 31-bit C or C++ code, link, and bind it into a shared library that can be loaded and called dynamically from a Java application running with a 64-bit IBM SDK.
    You must set the JNI include directory to javahome/include/jni31 and use the libjvm31.x side-deck file from the 64-bit SDK. The native code must be compiled with standard CALL linkage; XPLINK is not supported. For example, the following command creates a shared library called libSample.so for a C program called Sample.c:
    xlc -o libSample.so -W "l,dll" -W "c,langlvl(extended),float(ieee),dll,exportall" -I/javahome/include/jni31 Sample.c /javahome/lib/j9vm/libjvm31.x
    where javahome is the JAVA_HOME path of your IBM SDK installation.
    The elements in the compile and link commands are as follows:
    xlc
    The command that runs the C compiler.
    -o libSample.so
    The output file, which is the shared library that you want to build. The name (the text between lib and .so) should match the name that you used in the System.load(filename) or System.loadLibrary(libname) methods in your Java application.
    -W "l,dll"
    The option -W is passed to the bind phase of the compiler. The binder creates a shared library .dll file. Standard CALL linkage is used by default for 31-bit C or C++ code.
    -W "c,langlvl(extended),float(ieee),dll,exportall"
    The option -W is passed to the compile phase of the compiler.
    • langlvl(extended) enables language extensions that are used by the JNI header files, such as long long.
    • float(ieee) tells the compiler to generate binary IEEE754 floating-point numbers and instructions, which match the float and double primitive types of the Java language.
    • dll tells the compiler to produce DLL code. The DLL code can export or import functions and external variables.
    • exportall specifies that call functions in the shared library are callable by external programs.
    -I/$JAVA_HOME/include/jni31
    Tells the compiler to look in the javahome/include/jni31 directory, in addition to the default directories, for header files (in this case, special JNI header files for running 31-bit native code with the 64-bit SDK). Change javahome to be the JAVA_HOME path of your IBM SDK installation.
    Sample.c
    Your C program.
    /$JAVA_HOME/lib/j9vm/libjvm31.x
    The side-deck file that enables 31-bit native code to link and resolve the Invocation and JNI APIs.
  2. Run the Java application on the 64-bit VM, either directly on the command line or from a C or C++ application:
    Starting the VM from the command line
    • Specify the -XX:+Enable3164Interoperability command-line option to enable 64-bit Java applications to load the 31-bit native library and invoke the methods implemented within it.
    • If your Java application uses the System.loadLibrary(libname) method, which does not accept a path, you must also specify the location of your shared library. You can specify the location either by using the -Djava.library.path system property option on the command line or by using the LIBPATH environment variable.
    For example:
    java -XX:+Enable3164Interoperability -Djava.library.path=. Sample
    Starting the VM from a C or C++ application
    If you are using the JNI_CreateJavaVM Invocation API to instantiate a VM instance from your C or C++ application, pass the -XX:+Enable3164Interoperability option as a VM argument.

Results

You should now be able to use your existing 31-bit native code from the Java application running on the 64-bit VM, and vice versa.
If you see an error message similar to the following, check that you specified the -XX:+Enable3164Interoperability option on the command line or in the JNI_CreateJavaVM API when you ran your application:
An AMODE64 application is attempting to load an AMODE31 DLL load module.
End of changes for 11.0.14.1