The Java Native Interface (JNI) provides a powerful platform for integrating code written in languages other than Java--mainly C and C --with that written in the Java programming language. Although, theoretically speaking, JNI does provide a fairly generalized interface, the support structure that comes with JNI is basically aimed at linking C/C code with Java code. The literature that is available also appears to deal exclusively with the methodology of linking Java and C/C .
This article demonstrates the techniques that allow Java code to call code written in assembly language. The version of assembly language used for writing the illustration code is MASM32. I assume that the reader is familiar with assembly language programming in general and with MASM32 in particular. Familiarity with the Java programming language is also assumed. The resource section lists some material that I have relied upon and have learned from. I hope the interested reader too will derive substantial benefit from these resources.
Before proceeding further, there is one point I would like to mention: the approach and the example shown here are basically for Windows platforms. Within this family, however, they work without any modifications. I have personally tried them out on Win 98/2000/XP and have not faced any problem.
As I have already mentioned, the supporting elements available with JNI are designed for C and C . For a developer using assembly language, it is necessary to understand clearly how JNI exposes the interfaces. So let us take a quick look at some of the internal details of JNI.
The JNI Approach
When a Java method calls assembly language code, some information will almost always have to move from one environment to the other. The calling method will usually pass parameters to the called function and the called function may return some information to the caller. In addition to this, each environment requires information about the other to be able to work together. The problem is that data representation within the Java Virtual Machine (JVM) is different from that in the assembly language environment. Also some information, especially within the JVM, is of a specialized nature and there is no provision in native languages (C/C /assembly) to directly access such information. JNI provides a rich set of interface functions that facilitate exchange of such data by providing access to the internal database of the JVM and by providing the required mapping from the data type of one environment to the corresponding data type of the other.
JNI also has certain other support structures that make it easy for C and C programs to call these interface functions. Unfortunately, these support mechanisms are not directly usable by assembly language programs. The assembly language programmer, therefore, needs to understand how the interface functions can be directly accessed, and an appreciation of the structure of JNI is necessary to achieve this understanding.
JNI Structure
Whenever a Java program calls a native method, the called method compulsorily receives two parameters in addition to those specified by the calling method. The first is the JNIEnv pointer and the second is a reference to the calling object or class. It is the first parameter that is the key to the world of JNI.
JNIEnv is a pointer that, in turn, points to another pointer. This second pointer points to a function table that is an array of pointers. Each pointer in the function table points to a JNI interface function. In order to call an interface function, we have to determine the value of the corresponding entry in the function table. Let us see how we can do this in two steps.
First we find out what the value of the second pointer in our chain is. In other words, we get the contents of the location pointed to by JNIEnv. We do that as follows:
mov ebx, JNIEnv
mov eax, [ebx]
The first instruction loads the contents of JNIEnv into ebx and the second loads the contents of the address pointed to by ebx into eax. Since the contents of ebx is the same as that of JNIEnv, eax now has the contents of the location pointed to by JNIEnv--the second pointer referred to above. Which means eax now contains the starting address of the function table.
Next we need to retrieve the contents of the entry in the function table that corresponds to the function we want to call. To do this, we have to multiply the zero based index of the function (see Sheng Liang's book) by four--since each pointer is four bytes long--and add the result to the starting address of the function table which we have formed in eax earlier. We do it thus:
mov ebx, eax ; save pointer to function table
mov eax, index ; move the value of index into eax
mov ecx, 4
mul ecx ; multiply index by 4
add ebx, eax ; ebx points to the desired entry
mov eax, [ebx] ; eax points to the desired function
The contents of eax can now be used to call the function.
This scheme of accessing JNI interface functions is shown in Figure 1.
Figure 1. Accessing JNI functions
An Example
To see how the above technique can be used to call an assembly language program, let us consider a simple example. In our example a Java class (ShowMessage) calls assembly language code to display a Windows message box. If the message box is displayed, then the assembly language code returns a string to tell the calling class that it was successful. Otherwise, an error message is returned. In either case, the calling class prints the returned string on the console.
The Java class looks like this:
class ShowMessage
{
public native String HelloDLL(String s);
static
{
System.loadLibrary("hjwdll");
}
public static void main(String[] args)
{
ShowMessage sm = new ShowMessage();
String returnMessage = sm.HelloDll("Hello, World of JNI");
System.out.println(returnMessage);
}
}
Those familiar with JNI will notice that the Java class is identical to what it would have been if the called native method had been written in C or C . Which, of course, is as it should be, since the calling method need not be aware of the language used to write the called method. All that matters to the Java code is that it is calling a native method as declared in the third line of the code:
public native String HelloDll(String s);
I will not go into the structure of the Java class--both Sheng Liang and Bruce Eckel have explained it clearly. It is the assembly language code that is of interest to us, and we shall examine it in some detail:
.386
.model flat,stdcall
option casemap:none
include <pathname>\include\windows.inc
include <pathname>\include\user32.inc
include <pathname>\include\kernel32.inc
includelib <pathname>\lib\user32.lib
includelib <pathname>\lib\kernel32.lib
Java_ShowMessage_HelloDll PROTO :DWORD, :DWORD, :DWORD
;This macro returns pointer to the function table in fnTblPtr
GetFnTblPtr MACRO envPtr, fnTblPtr
mov ebx, envPtr
mov eax, [ebx]
mov fnTblPtr, eax
ENDM
;This macro returns pointer to desired function in fnPtr.
GetFnPtr MACRO fnTblPtr, index, fnPtr
mov eax, index
mov ebx, 4
mul ebx
mov ebx, fnTblPtr
add ebx, eax
mov eax, [ebx]
mov fnPtr, eax
ENDM
.data
Caption db "JAV_ASM",0
ErrorMsg db "String conversion error",0
SccsMsg db "MessageBox displayed",0
.code
hwEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax, TRUE
ret
hwEntry endp
Java_ShowMessage_HelloDll proc JNIEnv:DWORD, jobject:DWORD,
Msgptr:DWORD
LOCAL fntblptr :DWORD
LOCAL Message :DWORD
LOCAL fnptr :DWORD
GetFnTblPtr JNIEnv, fntblptr ; pointer to function table
GetFnPtr fntblptr, 169, fnptr ; pointer to GETstringUTFChars
push NULL ; push
push Msgptr ; parameters for
push JNIEnv ; GetStringUTFChars
call [fnptr] ; call GetStringUTFChars
mov Message, eax
; if eax is NULL then error
.if eax == NULL
invoke MessageBox, NULL, addr ErrorMsg, addr Caption, 16
GetFnPtr fntblptr, 167, fnptr ; pointer to NewStringUTF
push offset ErrorMsg ; push parameters for
push JNIEnv ; NewStringUTF
call [fnptr] ; call NewStringUTF
.else
invoke MessageBox, NULL, Message, addr Caption, 64
push Message
push Msgptr
push JNIEnv
call [fnptr] ; release string
GetFnPtr fntblptr, 167, fnptr ; pointer to NewStringUTF
push offset SccsMsg ; push parameters for
push JNIEnv ; NewStringUTF
call [fnptr] ; call NewStringUTF
.endif
ret ;return to Java program
Java_ShowMessage_HelloDll endp
End hwEntry
Here again, I will not dwell on the overall structure of the program (for guidance, consult Iczelion's homepage). Note that <pathname> will be determined by the directory structure of your system--on my PC it is C:\masm32. Note also that this program has been written as a .dll as shown by the hwEntry procedure at the beginning of the .code section. A .dll (Dynamic Link Library) can be linked with a Java program at run time. Needless to say, the function of a .dll does not have to be restricted to calling a Windows API; it can do whatever you want it to do. The MASM code should be saved as hjwdll.asm and, on successful assembly, hjwdll.dll and hjwdll.lib files will be created.
The first thing that we need to look at is the name of the native method as it appears in the assembly language code. HelloDll, the name used by the calling method, appears in quite a different form in the called method. This process of transformation from HelloDll to Java_ShowMessage_HelloDll is called mangling and has been explained in The Java™ Native Interface Programmer's Guide and Specification as well as in Thinking in Java. The mangled name can be derived manually by using the algorithm used by JNI or generated automatically by running javah on ShowMessage. You can do this by typing javah -jni ShowMessage at the command line. The resulting file will be ShowMessage.h and will show the mangled name. If you do use the javah approach, do not include the output file in the assembly code. The only thing to be used is the mangled name.
The real points of interest are, of course, the two macros GetFnTblPtr and GetFnPtr. These are modified versions of the code snippets introduced in the preceding section. The modifications enable the macros to operate directly on appropriate memory locations and obviate the need for manipulating input and output variables through the registers. Obtaining the pointer to the function one wants to call becomes fairly simple because of the macros.
The HelloDll procedure first gets the pointer to the function table. It then gets the pointer to the GetStringUTFChars function to convert the String object passed by the Java method into a UTF8 string that can be handled by assembly language. The parameters required for calling GetStringUTFChars are then pushed onto the stack. Note that the right-most parameter is pushed first in accordance with the stdcall convention followed by JNI. The function puts its return value in eax. If this value is NULL, then there was an error. Otherwise, a valid pointer to a UTF8 string is available in eax, which can be used to display the message passed by the Java method. After the message is displayed, the UTF8 string should be released as shown.
The native method returns one of two strings to the calling method depending on whether it succeeded or failed in displaying the message passed to it by the Java method. However, the string generated by the native method has to be converted into a Java String object before being returned. This is done by a call to NewStringUTF. Take note of the fact that the pointer to the function table needs to be derived only once in a thread. That is why it is better to split the pointer translation process into two parts so that the first part need not be executed unnecessarily over and over again.
Once you have compiled the ShowMessage class and have created the hjwdll.lib and hjwdll.dll files, put all the three files in the same folder. If you now execute ShowMessage, you will see a message box like the one in Figure 2.
Figure 2. Windows MessageBox called from Java
Conclusion
JNI is much more than what has been shown here. This article presents one of the ways in which interaction can take place between Java code and assembly language code. But the two issues demonstrated--accessing interface functions and type conversion--not only allow Java programs to call native code but form important building blocks for all the other types of interaction, too.
The book I feel is a "must read" for all developers seriously interested in JNI is "The Java™ Native Interface Programmer's Guide and Specification" by Sheng Liang. This invaluable book is a free download. It is essentially oriented to C/C but the detailed information provided on the workings and structure of JNI is a great help in figuring out how the assembly language approach should be developed.
My preferred resource for MASM32 is Iczelion's home page. The tutorials are excellent. Tutorial number 17 deals with .dlls.
There are many excellent books for Java. One such book is Thinking in Java by Bruce Eckel.
If you are interested in knowing more about Windows APIs, Programming Windows by Charles Petzold is a good book to read.
Some of you may be wondering why should it be necessary at all to mix Java with assembly language. For an answer to this question I would like to refer you to Tal Liron's article in JavaWorld. This article not only tells you about the situations in which it would be desirable to mix native code with Java, but it also has a built-in example. Although the article is oriented towards C/C , much of the rationale remains valid for assembly language. Additionally, keep in mind that assembly language segments may be very useful in fine-tuning programs especially with respect to timing and other operational constraints.
Biswajit Sarkar is an electrical engineer with a specialization in programmable industrial automation.
What do you think of calling assembly language from Java?
Showing messages 1 through 14 of 14.
assembly from java q
2008-02-28 09:32:56 gregoryhill597
[Reply | View]
Great article - everything worked and the explanations were clear.
If I wanted to pass 2 strings from java to asm, say message 1 and message 2 instead of just message 1, how would I do that?
I guess another way to phrase this same question is how does getStringUtfChars know which string to get?
Examples available
2008-01-19 07:14:37 brinkje
[Reply | View]
After sending the comment "Possible correction and question", I found a way to create the DLL and posted several examples on
http://www.cs.plu.edu/~brink/myPrograms/JavaJNI/JNI_Examples.html
The examples include a bat files that will produce the .dll automatically after it is customized for the location of your Java and Visual C folders.
little tip
2007-12-17 12:36:40 melnikvv
[Reply | View]
First of all Thank. I tried your code. everthing is right... But I spent a lot of time trying run it. Problem was in way of creating dllName.dll. With the same code there was a problem to run an d i got an
java.lang.UnsatisfiedLinkError.
solving of this problem is creating dllName.def
LIBRARY dllName
EXPORTS functionName.
Possible correction and question
2007-12-07 22:28:18 brinkje
[Reply | View]
Thank you for your article. I was trying to figure out how one could call assembler from Java so it was very helpful.
It seems to me the following line
GetFnPtr fntblptr, 169, fnptr
should proceed the code
push Message
push Msgptr
push JNIEnv
call [fnptr] ; release string
It seems tthat without it, fnptr would still be pointing to the GetStringUTFChars function.
Now my question:
I am having trouble getting Java to recognize the assembly language Java_ShowMessage_HelloDll in the DLL.
To show that your function is correct, I wrote a simple function Java_ShowMessage_HelloDll in C that calls your assembly method (after it is renamed). It works fine. I use the C compiler, Masm and linker that comes with Visual Studio 8.0. Do you have any suggestions?
Thank you.
sick but interesting
2007-10-08 11:49:07 dog
[Reply | View]
This is interesting.. I had never thought about this. A hacker never knows when he'll need a nifty trick like this.
A bit sick though.. :)
ITS GOOD BUT..
2006-10-25 02:28:00 ranjithmenon2004
[Reply | View]
Its good but please provide little easy example of machine language program..just like print hello in console like because our aim is to study the way of calling machine langauge ...ok thank u for this tutorial
Great article Biswajit! Have read a lot of stuff about assembly in C world, bit hardly any in java world.
After a long time read some 'real programming stuff for java'. Was just sick of object oriented zealotry ;-)
Thanks a lot!
Thanks for your comment. You are right in saying that C or C interfacing with Java will work most of the time. Yet assembly language is not dead. Microsoft still supports masm -- MASM8 was published in june 2006. So there are assembly codes being written and some of these might benefit from a bit of Java. Also let's not forget legacy code.
I haven't even thought of using Assembly language since my MSDOS days. Your use of macros in the code made the sample easier to read than some of the C samples I've looked at in the past when researching JNI.