6

I'm trying to use JNI and getting java.lang.UnsatisfiedLinkError. Unlike the other million questions asked about this, I have the lib on my path, and have even seen the exception change when I remove it. I'm sure that something is wrong with the dll I have created, but I'm not sure what.

Here is my java class code:

package com;

public class Tune {
    static {
        System.loadLibrary("lala");
    }
    public static void main(String[] args) {
        Tune j = new Tune();
        System.out.println("2+6="+j.add(2, 6));
    }
    native public int add(int x,int y);
}

Here is the abridged portion of my javah produced header file:

/*
 * Class:     com_Tune
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_Tune_add
  (JNIEnv *, jobject, jint, jint);

Here is my c++ code:

#include <jni.h>
#include <com_Tune.h>

JNIEXPORT jint JNICALL Java_com_Tune_add
  (JNIEnv * env, jobject obj, jint x, jint y) {
    return x+y;
  }

Here is the runtime exception I get from eclipse:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.Tune.add(II)I
    at com.Tune.add(Native Method)
    at com.Tune.main(Tune.java:9)

I read that the above exception means it DID find the library "lala", but that the method "add" is still not defined. The only things I see different between my project and the tutorial are:

  • Mine uses a package, instead of the default package (shouldn't tutorials really do this?!?! come on let's get professional)
    • Mine has a return value.
    • I moved my dll after it was created (I don't think this will break it since my path is configured.)

How is this possible?

Other Info:

OS: Windows 7
JDK: 1.6.0_31 (for x86, 32 bit jvm)
C++ IDE: Code::Blocks (the dll was compiled automatically by the Code::Blocks IDE)
C++ compiler: MinGW32-g++ (the GNU C++ compiler)

I have jni.h and com_Tune.h in C:\_\include
I have lala.dll in C:\_\lib

Environment Variables:
PATH: C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;%CommonProgramFiles%\Microsoft Shared\Windows Live;C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\Apps;%JAVA_HOME%\bin;C:\Program Files\MySQL\MySQL Server 5.5\bin;%MAVEN_HOME%\bin;%HADOOP_INSTALL%\bin;c:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;c:\Program Files\Microsoft SQL Server\100\Tools\Binn\;c:\Program Files\Microsoft SQL Server\100\DTS\Binn\;C:\MinGW\bin;C:\Program Files (x86)\GnuWin32\bin;C:_\path;C:\_\lib;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin;C:\_\include

msknapp
  • 1,595
  • 7
  • 22
  • 39
  • The code seems correct. Could you check DLL export table (i.e. with Dependency Walker) and make sure that your function is present in exports? – lxbndr Feb 20 '12 at 20:27

5 Answers5

4

The problem is with the name compiler has generated: Java_com_Tune_add@16

Use either of two

gcc -Wl,-kill-at

Or

gcc -Wl,--add-stdcall-alias

This will ensure generation of Java_com_Tune_add

And then your method call will be successful.

vitaly
  • 2,755
  • 4
  • 23
  • 35
kool
  • 41
  • 2
  • For the further note, this answer is the correct answer and this fixes my problem. I think this is the answer which should be accepted. Instead others who dig to this thread later will mislead. – sandun dhammika Apr 17 '15 at 16:50
2

One possible source of the problem might be that you compiled the code using a C++ compiler, which uses a different [calling convention] than plain C. If thats the case then the solution would be to wrap the code for the method in a extern "C" block like this:

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL Java_com_Tune_add
...

#ifdef __cplusplus
}
#endif
Jörn Horstmann
  • 33,639
  • 11
  • 75
  • 118
  • thanks for trying to help, but that unfortunately did not fix it. – msknapp Feb 20 '12 at 20:28
  • I think msknapp have omitted preprocessor directives in his question. `javah`, the tool that generates headers for JNI, adds necessary stuff automatically. – lxbndr Feb 20 '12 at 20:29
  • Like I said in the question, the javah output was abridged to keep the question short. I did not change anything that was output by javah. If you really want to see all of it, here you go: /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_Tune */ #ifndef _Included_com_Tune #define _Included_com_Tune #ifdef __cplusplus extern "C" { #endif /* * Class: com_Tune * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_Tune_add (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif – msknapp Feb 20 '12 at 20:35
  • @Mersenne: You're right, that `extern` declaration is only needed in the header, which is generated. I looked at the wrong part of my own sample code. – Jörn Horstmann Feb 20 '12 at 20:38
1

Just guessing... Is your dll depends on another dll that is not on the path? MinGW modules usually depend on specific C runtime library.

lxbndr
  • 2,159
  • 1
  • 15
  • 17
  • ok, I'm trying your advice to use Dependency walker. I'm unfortunately new to C++, and this is the first I heard of dependency walker. Not sure how to use it to detect missing dependencies. I opened "lala.dll" with it, and selected the root node in the tree. The "Pi" table is empty. The "E" table has one entry for a function. Function: Java_com_Tune_add@16. Ordinal: 1(0x0 0 0 1). Hint: 0(0x0 0 0 0). Entry Point: 0x0 0 0 0 1 2 5 4. Can you please tell me what I should be looking for? – msknapp Feb 20 '12 at 21:24
  • Also, the "E" column has a block with the letter "C" in it, not "C++". Could that be the problem? – msknapp Feb 20 '12 at 21:27
  • Yes, that is it. Your function is exported. The "C" mark is normal, "C++" marks methods of C++ classes, as i know. Look at direct children of root node. That is imported libraries. What is there exactly? For your "just-simple-method-library" case there must be few entries. I.e. in my test dll (also with one simple method) there are only two libraries. – lxbndr Feb 20 '12 at 22:02
  • immediate children: KERNEL32.DLL, MSVCRT.DLL, LIBGCC_S_DW2-1.DLL. The first two have a lot of their own child nodes. I clicked on each node in the tree, never saw a red block under PI or E. – msknapp Feb 20 '12 at 23:21
  • Last step to make my assumption wrong :) Copy LIBGCC_S_DW2-1.DLL to directory with your dll. And add to static init block `System.loadLibrary("LIBGCC_S_DW2-1");` before loading your lib. – lxbndr Feb 20 '12 at 23:38
  • that did not fix it, should I try searching on the other libs and seeing if they are on my PATH? – msknapp Feb 21 '12 at 00:46
  • I decided to rebuild the project with Visual Studio. Guess what, it works! So obviously the problem is somehow produced by the MinGW32 compiler. – msknapp Feb 21 '12 at 01:20
  • To make things extra weird, I decided to use dependency walker on the working dll. To my surprise, it says that it's missing two libraries: GPSVC.DLL, and IESHIMS.DLL. Despite that, it's functional?!?! The more I work with c++, the less sense it makes. I give up. – msknapp Feb 21 '12 at 02:59
  • Dependency walker just say that some libraries are not accessible in usual dll loading manner. But they still may be loadable by target application. Also look at solution [here](http://stackoverflow.com/questions/4984612/program-cant-find-libgcc-s-dw2-1-dll). Recommendation is to do static linkage, so result dll will contain all necessary code. – lxbndr Feb 21 '12 at 07:16
0

I had the same issue and the flag -Wl,-kill-at worked for me.

0

Try with following example for Windows: (remember that the Java class name must be the same that corresponding file name)

Step 1. Create the following Java file (P.java):

class P
{
  static
  {
    // "P" is the name of DLL without ".dll"
    System.loadLibrary ("P");
  }

  public static native void f(int i);

  public static void main(String[] args)
  {
    f(1);
  }
}

Step 2. javac P.java

Step 3. javah P

Then, "javah" generates the header file "P.h"

Step 4. Create the file "P.def" including the following two lines (this file defines the exported symbols, in this case the name of C function):

EXPORTS
Java_P_f

Step 5. Create your C file (P.c):

#include "P.h"

JNIEXPORT void JNICALL Java_P_f(JNIEnv *env, jclass c, jint i)
{
  printf("%i\n",i);
}

Step 6. Within Visual Studio command promt, define the following variables:

set JAVA_HOME= the path of JDK

set include=%include%;%JAVA_HOME%\include;%JAVA_HOME%\include\win32

Step 7. Generate DLL:

cl /LD P.c P.def

Step 8. Run the Java program:

java P

(Note: P.dll and P.class are located in the same directory)