   [java] Linking errors with the JNI
  Hello everybody,Well I'm working on a game in Java (well for bloody too long in fact now!-its begining to feel more like vaporware than anything! [lol]) and its going well so far. One of the things I would like to do is implement my own little timer for doing stuff like frame rate independent movement and such.. Anyhow the long and the short is that I'm attempting write this in C and compile it to a native .dll which can then be used in my java program through the JNI. Why am I not using System.nanoTime() ? Well I won't go into that here.. Even if you want to call me stupid for doing this, I still see this as a little chance to learn about the JNI, something which could be handy in future.Ok, so here's what I've done so far:I created my C code with all the timer functions, using the naming and function conventions as described in the JNI docs. I'm using DevC++ for my IDE so i selected 'compile as win32 dll' under project options and then compiled the code. No problems there.. The .dll is generated as expected with no compile time errors or warnings..Good.. So I then made my java interface to the C functions inside a new class called 'NativeTimer'. The methods are all declared with the correct paramters and returns etc.. and use the 'native' modifier. I also got the class to load the native library upon its initialisation using the System.loadLibrary() method. This much works, I can confirm. Upon removing the .dll from my java project directory, an error is generated saying that the library could not be located- so it IS loading the .dll as expected.However, whenever I try to invoke any of the methods in the 'NativeTimer' class I get a 'UnsatisfiedLinkError' from the JVM meaning that it could not find the native methods.. Here I am lost..I've looked at the JNI docs and my naming *appears* to be correct, i'll say that cautiously now.. I've also looked at some tutorials on the net and nothing appears to be out of order..I have no idea where I am going wrong.. It has to be something to do with the function names, but I cannot figure out what exactly it is.. Any ideas ?Heres the java code for the NativeTimer class:class NativeTimer{// Function prototypesstatic native void init();// initialise native timerstatic native void update();// update and save time between last call to update in secondsstatic native double frameTime(); // return last frame time in seconds// Static initialisationstatic { System.loadLibrary("NativeTimer");}}And heres the C code which compiles into the .dll:/******************************************************************************* Native timer: simple native timer for java. Uses Win32 functions to retrieve time and performance counter. This code is *not* portable, but could be modified to support other platforms Compile as a DLL *******************************************************************************/extern "C"{    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    // Includes    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   #define WIN32_LEAN_AND_MEAN// Trim junk from windows   #include <windows.h>  // windows library    #include "JNI.h" // jni header file   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    // Variables    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  long double clockFrequency = 2000000.0;    // Frequency of system clock    long double lastFrameTime  = 0;  // Time the last frame took in seconds    __int64lastClockTime  = 0;  // Clock timer last time it was invoked    __int64currentClockTime    = 0;  // Current clock timer value/*******************************************************************************Name:  initPurpose:    Initialises the timer. Querys windows for how fast the system clock is.  This is important because a 1GHz machine's clock will not run at the   same speed as a 2Ghz machine- and the timing will be messed up! Parameters: Pointer to JNI environmentReturns:    None*******************************************************************************/   JNIEXPORT void JNICALL Java_NativeTimer_init( JNIEnv * env ){    // Get clock Frequency from windows   LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency);    // Get number of clocks as a 64-bit integer    unsigned __int64 numClocks = *((unsigned __int64*)(&frequency));    // Calculate clock frequency in seconds    clockFrequency = ((long double)(1.0)) / ((long double)(numClocks));}/*******************************************************************************Name:  updatePurpose:    Updates the timer and calculates how long the last frame took in  seconds. We can use this to adjust movement based on how fast or not  the game is running.Parameters: Pointer to JNI environmentReturns:    None*******************************************************************************/ JNIEXPORT void JNICALL Java_NativeTimer_update( JNIEnv *env ){// Get the current clock timeLARGE_INTEGER clock; QueryPerformanceCounter(&clock);// Save clock time as a long unsigned __int64 currentClock = *((unsigned __int64*)(&clock));// Get the amount of clocks between the last invocation of the QueryPeformanceCounter// function and this invocation. Make sure this figure is positive__int64 frameClocks = currentClock - lastClockTime;// Divide the number of clocks by the clock frequency to obtain a frame// time in seconds. Save this figure.lastFrameTime = frameClocks * clockFrequency;// Save the current clock time for the next invocation of this functionlastClockTime = currentClock;}  /*******************************************************************************Name:  frameTime()Purpose:    Returns the number of seconds the last frame took.Parameters: Pointer to JNI environmentReturns:    Double representing the time in seconds the last frame took*******************************************************************************/ JNIEXPORT jdouble JNICALL Java_NativeTimer_frameTime( JNIEnv *env ){   return (double)lastFrameTime;}} // end of extern "C"
  If you wanted to learn something about JNI, WORKING timmer is bad idea to try first.You forgot to set CPU mask (so your timer WILL NOT work on multicore CPU). IIRC you are trying to use CPU frequency to discover difference between steps, however CPU frequency could go down at any time. (for example AMD X2 could drop down to 1000 MHz as it seens fit.)You are using long doubles, thus preventing of caching variables in MMX register, at the same time it provides no important increase in precision.Why are you using static methods? Put loadLibrary into the constructor, and add some simple method for testing.for example private int native return0;I also asume that method names were autogenerated by javah right?BTW is extern C necessary? (I used directly linking from macroassembly 32 bit so I don't have much experience with C++ compiler decorations.)   ;Quote:Original post by RagharYou are using long doubles, thus preventing of caching variables in MMX register, at the same time it provides no important increase in precision.Why are you using static methods? Put loadLibrary into the constructor, and add some simple method for testing.for example private int native return0;I also asume that method names were autogenerated by javah right?BTW is extern C necessary? (I used directly linking from macroassembly 32 bit so I don't have much experience with C++ compiler decorations.)Thanks Raghnar for taking the time to reply..Quote:If you wanted to learn something about JNI, WORKING timmer is bad idea to try first.In retrospect, no it probably wasn't; but I'm doing it now so I'm going to plow on ahead with it. There has turned out to be a lot more complications than I expected from a simple timer... I admit to not knowing a huge amount about the area before this- but there ya go, you learn something new every day! [wink]Quote:You forgot to set CPU mask (so your timer WILL NOT work on multicore CPU)Ok, I wasn't quite sure what you were talking about there for a while so I did a little more digging on timing. I presume you are refering to the SetThreadAffinityMask() function from the windows API ? Hmmm.. As the thread shifts between different processors the function may return inconsistent values due to h/w bugs with the motherboard. Makes sense. Its probably a little more correct though to say it MAY work, but then again it might not either.. Either way its inherrently unsafe though..Quote:IIRC you are trying to use CPU frequency to discover difference between steps, however CPU frequency could go down at any time. (for example AMD X2 could drop down to 1000 MHz as it seens fit.)That is true, and something I have not accounted for. But would using QueryPeformanceCounter / QueryPerformanceFrequency not shield me from such things as opposed to using RDTSC ? Does the Windows API make some attempt to account for this ? I'm not sure... What about the new dedicated hardware timers that are built into some motherboards ? Windows would make an attempt to use these if they were present and the whole issue could be sidestepped then.. But how prevalent are they in PCs ? When were they introduced and how widely supported are they ? Quote:Original post by RagharYou are using long doubles, thus preventing of caching variables in MMX register, at the same time it provides no important increase in precision.Why would the compiler cache these variables into the MMX register ? This doesn't make sense to me. If you want to do floating point operations you have to use either the x87 FPU or the SSE instruction unit. With the x87 FPU you have no direct means of communicating with the other parts of the CPU other than memory, and while you can move data from MMX to XMM registers it would make more sense to just load it directly into the XMM register. Then there's the issue of MMX instructions modifying the FPU state, so you would have to save/restore the FPU state when using MMX- which would add further overhead. I dunno, am I missing out on something ? Now concerning speed / optimisation, one of the first and foremost things about optimising is knowing where to optimise. While this code may not be optimal, a few clocks here and there is not really important, since the timer is only updated once per frame. I'm not using this to profile small segments of code, so it really is not that big of a deal.Yes, using long doubles probably won't have a huge impact especially since the frame time is being returned as a double itself. I'd like it to be as precise as it can though, even if its not really that big of a difference.Quote:why are you using static methods? Put loadLibrary into the constructor, and add some simple method for testing.For the simple reason that I want one and *only* one timer ever in existence in the program. I have no need for any instances, and indeed I don't want any.. Hence the static methods.Quote:I also asume that method names were autogenerated by javah right?Nope. These were functions i had used before in previous C programs of mine so i just copied and pasted and ajusted the function declarations and names as needed. I just used tutorials/examples of the JNI as well as the JNI documentation that came with the JDK docs to transform the function delclarations to the required format.Quote:BTW is extern C necessary? (I used directly linking from macroassembly 32 bit so I don't have much experience with C++ compiler decorations.)Actually, now that I think of it isn't- its completely redundant in this situation. Its just something which I stuck in after following the JNI tutorials, and its something which JavaH sticks into the header files itself. Its only needed when you are declaring functions that reside in external object files that aren't directly a part of your project. All it does is tell the compiler what language the function was written in, and consequently the name mangling and calling conventions which are used. I had to use this before when I wrote some asm modules that I wanted to use in a C project. If I left the extern "C" out then the linker would complain..Ok, so i've since tried using javah to generate the header and function definitions for me (which was more or less the same as what i had before) and I've still had no luck.I can now conclude that the problem lies not with the function name, but with one of the following two things:Either I am somehow compiling the .dll wrong- it is in the wrong format or whatever*or*I am loading the .dll correctly within my java program. Is System.loadLibrary() enough ? Or do I have to do more ?I'm really at a loss now... I seem to be doing everything the tutorials say but I still can't get the blasted thing working.. Sigh... [sad]   ;  Crate DLL with a some function that just returns 1. Name it simply, and put it into the same directory as class file, and somewhere on the path. Try it without static methods, to be sure it was inicialized before you tried to load it. Test the simple DLL.If it fails, use dissasembler to look at the name of the exported method. Just to be sure look if DLL file has proper case. (All my JNI were in lower case, so I don't know if this could do any problem.)   ;Quote:Original post by RagharCrate DLL with a some function that just returns 1. Name it simply, and put it into the same directory as class file, and somewhere on the path. Try it without static methods, to be sure it was inicialized before you tried to load it. Test the simple DLL.If it fails, use dissasembler to look at the name of the exported method. Just to be sure look if DLL file has proper case. (All my JNI were in lower case, so I don't know if this could do any problem.)Hmmm.. Gave all that a go and still no luck. I've tried using upper case and lower case method names and used non-static methods like you suggested but it still refused to work. I had a look at the .dll and .o file and the method names appear to be the correct case. I guess it boils down to one of the two things I mentioned earlier.. I'm more inclined to think this has something to do with the way in which the .dll is compiled rather than how it is loaded in java...I dissasembled the .dll file also. One thing I've noticed which has been put in after the function name is '@8' .. What is the significance of this '@8' ? I wonder is this what could be causing problems ? I admit to not knowing much about name mangling but is this normal after a function name ? It probably is normal though.   ;  Well, starting from this Tutorial I was able to get JNI working from their example and then just simply create a .c file with your functions in them direct copy and paste. I changed the naming of your functions to match the class name that I included them in, in the java code. Everything worked fine with no problems a few UnsatisfiedLinkErrors but those were because my function names didn't match. I think that one of your problems might be the .loadLibrary(String) function. That only uses the default java.library.path unless you've put your dll in that path don't count on it working at all. For testing purposes use .load(String) and send it an absolute path make sure that absolute path is correct.The static initlialization works so there shouldn't be a problem there. Now whether the timer does what it's supposed to do I'm not at liberty to extrapolate on. So for a quick fix just change static { System.loadLibrary("NativeTimer"); }to static { System.load( <currentDirector> + "NativeTimer.dll");}And make sure you put the ABSOLUTE directory. Oh and why would you ever need nano time for a game your rendering method will always take longer than 1 ms so whats the point of setting it to refresh in 30 million nanoseconds opposed to 30ms.  And for timing purposes I use java.util.Timer and java.util.TimerTask. Which are extremely usefull for doing graphics and physics related updates. Just my two cents.[Edit: P.S. I used Dev-Cpp to compile the dll and starting by creating a win32 dll project but didn't use the sources that DevCpp provided][Edited by - 5MinuteGaming on May 9, 2006 1:15:53 PM];Quote:Original post by DarraghI dissasembled the .dll file also. One thing I've noticed which has been put in after the function name is '@8' .. What is the significance of this '@8' ? I wonder is this what could be causing problems ? I admit to not knowing much about name mangling but is this normal after a function name ? It probably is normal though.It means 8 bytes. 8 bytes are allocated on the stack.I would expect bad position of dll relatively to your .class . Try to recompile it by VC7.1 2003 compiler if you think your IDE is doing poor job. Also try to launch it from the command line. (You could also try to put your library into java/ext directory, but this directory is supposed to be used just by stable all applications shared libraries like JOGL, or LWJGL. This directory would be however for sure in path.);  You can't just compile it as a standard win32 DLL, you also have to specify further options to the linker.  Not sure how you'd do it from your IDE but you have to add the following flags to your linker:-D_JNI_IMPLEMENTATION_ -Wl,--kill-atHere's an example line from one of my makefiles:scan.dll: eztwain.o scantest_Scan.o$(CC) -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at $(JNI_CFLAGS) -shared -o scan.dll scantest_scan.o eztwain.o $(LIBS);  Hi everyone. Thanks again for posting. Well i've tried all of your sugesstions and unfortunately i've still had no luck. I must be doing something very simple wrong, or missing something very obvious. I don't know what though.. So as i said i've tried your suggestions:(1) The path/file-name for the DLL was OK, i can validate this simply by removing it from my project. Upon removing it complains that the library can't be found. So it is locating it correctly.(2) I tried recompiling it under VC++ 6 (sorry, I don't have any of the newer versions) and i still got the very same errors. No luck there. I could try other compilers, but i'd bet anything that i'd just get the same errors.(3) I tried adding the linker arguments suggested by necromancer_df and unfortunately that didn't do anything either. Well, I must say i'm lost. I seriously doubt i'll ever get this thing working at this stage. If anyone has any other suggestions then fire away.. I'm all ears..   ;  Create a directory, compile java source file, copy the DLL into the directory, type on command line in that directory "java -cp. yourFile" . Try it again after you removed all DLL files from your ext directory.