IBM Z and LinuxONE - Languages - Group home

Bridging the 31/64 bit gap with Metal C

  

Note: Language Environment, LE, doesn't permit mixing of 64bit and 31bit runtime environments. Metal C runs in an environment independent of LE.

 

There are use cases that requires calls to be made to a module in a different addressing mode. Say, an application is running in a 64-bit environment but needs to call out to an application that is compiled and available only 31-bit. There are also cases where from a 31-bit mode application a 64-bit mode application need to be called.


Metal C can bridge this gap. We have had many customers using Metal C to do this, and questions of how to do so. There appears to be a strong interest in this, and an easy-to-read guide of how to do this would go a long way in helping customers to utilize this. I chose to demonstrate this from a JNI call run in a 64-bit Java, which calls to a 64-bit Metal C; from 64-bit Metal C I call a 31-bit Metal C. To demonstrate that we are in 31-bit, I return the sizeof (long) back and print it in the 64-bit source. I expect to see 4 and not 8.

 

First the source code for HelloWorld.java, where the main entry point is defined. In this Java class, I statically load the library, "HelloWorld". 

 

class HelloWorld {

 private native void print();

 public static void main(String[] args) {

  new HelloWorld().print();

 }

 static {

  try {

   System.loadLibrary("HelloWorld");

  }

  catch (Exception e) {

   System.out.println( "ERROR: Unable to load libHelloWorld.so");

   e.printStackTrace();

  }

 }

}

 

I create the C native calls via the javah interface. This is standard step in writing the JNI interface and the output of that is HelloWorld.h and its content looks like below:

 

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class HelloWorld */

#ifndef _Included_HelloWorld

#define _Included_HelloWorld

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:  HelloWorld

 * Method:  print

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);

#ifdef __cplusplus

}

#endif

#endif

 

Now I construct the body of the function Java_HelloWorld_print(JNIEnv*, jobject) in a separate file, HelloWorld.c. 

 

#include <jni.h>

#include <stdio.h>

#include "HelloWorld.h"

#pragma linkage (metalc_64bit_call, OS) /*Metal C only supports MVS linkage, therefore its important to define it as such to the compiler*/

extern int metalc_64bit_call();    /*This function declaration is defined in a separate source that is compiled 64 bit with METAL */

JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) {

 int rc;

 rc = metalc_64bit_call();                                         

 printf("Hello World\n");

 /*I expect 4 printed on this statement, although this source file will be compiled in 64-bit mode*/             

 printf("sizeof_long_in_31bit_is=%d\n",rc);  

 return;

}

 

metalc_64bit_call() function definition is in MetalC64.c source file, and it looks like below: 

 

/*metalc_64bit_call*/

#pragma prolog(metalc_64bit_call, main)

#pragma epilog(metalc_64bit_call, main)

 /*Identifying metalc_31bit_call() with __attribute__((amode31)) switches the addressing mode to 31-bit before calling it*/

int metalc_31bit_call()__attribute__((amode31));                   

int metalc_64bit_call() {                                      

 return metalc_31bit_call();

}

 

metalc_31bit_call() has to be defined in a separate source file, MetalC31.c, the definition looks like this:

 

/* metalc_31bit_call() */

int metalc_31bit_call() {

 return sizeof(long);

}

 

And this is the last source file needed. Now I can start the build process. I plan to build this in Unix System Services. I need to find where Java 64-bit SDK is located on my system and add its location to my PATH. I also need to clear the xlc's include search path first, nosearch, and then include the header files from the Metal C include directory. My C files will be compiled and the corresponding object files will become part of the libHelloWorld.so. There's an extra step of assembling required to build the object files from a Metal C compile. These are the lines that start with "as". As a side note I should mention why I am passing the goff, and nodeck options to HLASM. The goff option is to process the HLASM ALIAS statements produced by the LONGNAME option in the previous compile step. The nodeck is to tell the HLASM to ignore the SYSPUNCH DD statement and avoid getting ASMA930U HLASM severe error. Moving back to the main topic, after producing all the object files, I create the shared object using xlc. Then I run the simple java -version command to ensure I am indeed running 64-bit Java. Then I run my Java program.

 

export PATH=$PATH:/usr/lpp/java/IBM/current_64/bin

javac HelloWorld.java

javah -jni HelloWorld

xlc "$@" -qLONGNAME -qMETAL -S -q64 MetalC64.c

xlc "$@" -qLONGNAME -qMETAL -S -qnosearch -I/usr/include/metal MetalC31.c

as -mgoff --nodeck MetalC64.s

as -mgoff --nodeck MetalC31.s

xlc "$@" -c -qEXPORTALL -qDLL -q64 -qLANGLVL=EXTENDED -o HelloWorld.o -I/usr/lpp/java/IBM/current_64/include HelloWorld.c

xlc -o libHelloWorld.so -qDLL -q64 MetalC64.o MetalC31.o HelloWorld.o

java -Xjit:version -version

java -Djava.library.path=. HelloWorld > HelloWorld.out 2>&1

echo $?

0

cat HelloWorld.out

Hello World

sizeof_long_in_31bit_is=4

 

Although I am running in a 64-bit environment, the size of long printed is 4 bytes, because I get this within metalc_31bit_call() which is a 31-bit mode compiled source.


This may look like a few more steps added to your application build process, but it's something that you can do once in your environment and take advantage of it many times.