JNI invocation fails with UnsatisfiedLinkError - java

I'm getting UnsatisfiedLinkError when invoking C functions from JNI though my setup seems correct. Here's what I've done:
There's a Java class:
package com.mycompany.myproduct;
public class Foo {
static {
System.loadLibrary("external");
}
public void native do_foo();
}
I've placed libexternal.so to the LD_LIBRARY_PATH, compiled the class, and executed javah over it. Resulting com_mycompany_myproduct_Foo.h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_mycompany_myproduct_Foo */
#ifndef _Included_com_mycompany_myproduct_Foo
#define _Included_com_mycompany_myproduct_Foo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject);
Implemented a C delegation in ctinative.c (not sure if extern "C" is needed there):
#include "com_mycompany_myproduct_Foo.h"
#include "External.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_com_mycompany_myproduct_Foo
* Method: do_foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_mycompany_myproduct_Foo_do_1foo(JNIEnv *, jobject) {
do_foo(); // this is a function that defined in External.h
}
#ifdef __cplusplus
}
#endif
Compiled that and got ctinative.o:
gcc -x c -g -m64 -DUNIX=1 -DUSE_SBUF=1 -DMAIN_VERSION=0 -DC_VER=7 -I$(EXTERNAL_SDK_ROOT)/include -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -o ctinative.o -c ctinative.c
Here's the output of nm ctinative.o (is having U there normal?):
0000000000000000 T Java_com_mycompany_myproduct_Foo_do_1foo
U do_foo
Placed that ctinative.o to LD_LIBRARY_PATH. Now when invoking Foo.do_foo() I'm getting UnsatisfiedLinkError:
java.lang.UnsatisfiedLinkError: com.mycompany.myproduct.Foo.do_foo()V
at com.mycompany.myproduct.Foo.do_foo(Native Method)
If I remove ctinative.o from LD_LIBRARY_PATH the error does not change. If I remove libexternal.so from LD_LIBRARY_PATH then of course I'm getting:
java.lang.UnsatisfiedLinkError: no external in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at com.mycompany.myproduct.Foo.<clinit>
Any idea on what I'm doing wrong?

OK, my experience with native libraries on Linux is limited to toy tests, however I have used them pretty extensively on Windows. I expect the mechanism is similar, but proceed with caution :)
Java ends up calling the Java_com_mycompany_myproduct_Foo_do_1foo() native function when you execute the method fooInstance.do_foo(). This is the native function that needs to be defined in libexternal.so (or whatever you choose to load with loadLibrary()).
If I understand your question correctly, you have compiled the function Java_com_mycompany_myproduct_Foo_do_1foo() into ctinative.o, and the implementation does not appear in libexternal.so. You can check this with objdump --dynamic-reloc libexternal.so.
I believe you need to have your native implementation of Java_com_mycompany_myproduct_Foo_do_1foo() compiled into libexternal.so, or alternatively you could link ctinative.o to produce a dynamic link library something like libctinative.so.
EDIT: To join the dots, the complete mechanism would be:
Your java code calls loadLibrary() on a .so file that implements the function Java_com_mycompany_myproduct_Foo_do_1foo(). Let's call this libctinative.so.
libctinative.so dynamically loads libexternal.so through the O/S's dynamic linking mechanism --- you don't need to do anything special to make this happen apart from compiling and linking libctinative.so in the right way
Your program runs correctly, assuming no other issues :)

You have Java_com_mycompany_myproduct_Foo_do_1foo() but native void do_foo(). Was do_1foo() its name when you generated the .h/.c files? If you've changed it you have to regenerate.

Related

call c/c++ DLL from Java in Eclipse IDE

I want to call this minimal dummy C program (named "TEST.c"):
extern "C"
void Java_TEST_run() {}
from this Java code (named "Example.java"):
public class Example
{
public static void main(String args[])
{
System.out.println("START");
TEST test = new TEST();
test.dll_call();
System.out.println("ALL DONE!");
}
}
class TEST
{
public void dll_call()
{
run();
}
static {
try {
System.out.println("Load DLL = start ");
System.load("/home/user/Desktop/TEST.dll");
System.out.println("Load DLL = finish ");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n");
System.exit(1);
}
}
public native void run();
}
I create the C dll file by the following commands:
g++ -c TEST.c
g++ -shared -o TEST.dll TEST.o
This works all fine within the console environment, esp. I got the successful Java program execution output:
START
Load DLL = start
Load DLL = finish
ALL DONE!
Now, if I try to run the Java program from the Eclipse IDE, I got the following error:
START
Load DLL = start
Load DLL = finish
Exception in thread "main" java.lang.UnsatisfiedLinkError: 'void Example.TEST.run()'
at test1/Example.TEST.run(Native Method)
at test1/Example.TEST.dll_call(Example.java:21)
at test1/Example.Example.main(Example.java:11)
To my understanding, this means the Java program running from Eclipse does successfully find the c dll file, but when trying to enter the dll file, it fails by finding the dummy function Java_TEST_run().
As the c code "TEST.c" is already as minimal as possible, and as the execution works fine from the console, I do not understand, why it fails from Eclipse.
Can someone please advice me, how to make this minimal c code working from Java in Eclipse?
Operating system is Ubuntu 18.04 with openjdk 11.0.6 and Eclipse 4.14.0.
This is a follow up question from this question.
Any help much appreciated, thank you!
I was able to resolve the issue with Eclipse.
The C/C++ code needs to include the name of the Eclipse project, e.g. "Example", in the function name. In above code, this means:
extern "C"
void Java_TEST_run() {}
needs to be changed to:
extern "C"
void Java_Example_TEST_run() {}
I'll give you quick answer
First of all you will need JNI for c/c++ and it will generate header for your .java file.
For example, if we have class from your example we will have header output like this
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TEST */
#ifndef _Included_TEST
#define _Included_TEST
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TEST
* Method: run
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_TEST_run
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
You can generate this type of header by typing
javac -h . TEST.java for your given class
Last thing we have to do is create our .cpp file to add function body, you can just copy decleration from JNI generated header file
#include "TEST.h"
JNIEXPORT void JNICALL Java_TEST_run(JNIEnv * env, jobject obj){
//my very special code
}
And that would be it.
In order to add JNI to your Visual Studio (if you are using one) you will have to go to project properties and add additional include and additional library from jdk folder.
If you are using g++, add include directory of JDK and library.

A java runtime error with a native JNI static method for C++ implementation

I have a repo with two simple implementations for JNI with C/C++.
I have the java.lang.UnsatisfiedLinkError error for C++ static JNI method.
(base) GlushenkovYuri:java y.glushenkov$ /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/bin/java -Djava.library.path=. MyJNIExample
Hello World from C!
Exception in thread "main" java.lang.UnsatisfiedLinkError: MyJNIExample.sayHelloCpp()V
at MyJNIExample.sayHelloCpp(Native Method)
at MyJNIExample.main(MyJNIExample.java:51)
But for C native method the same approach work well.
For C++ works without static.
You can reproduce the same behaviour in the following way:
1) Uncomment lines with the static native method for C++ and this one. And comment lines with the NON static native method for C++ and this one;
2) And just perform the next steps described in my README.md file;
Can somebody explain to me why the native method static/non static works for C, but only the native NON static method works for C++?
UPD: my header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyJNIExample */
#ifndef _Included_MyJNIExample
#define _Included_MyJNIExample
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyJNIExample
* Method: sayHelloC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloC
(JNIEnv *, jclass);
/*
* Class: MyJNIExample
* Method: sayHelloCpp
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCpp
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
The type of the function that javah generates differs according to whether the native method is static or not:
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCppstatic
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_MyJNIExample_sayHelloCppnonstatic
(JNIEnv *, jobject);
In addition, the generated header contains an extern "C" declaration to make sure the compiler keeps the name intact of mangling it like __Z29Java_MyJNIExample_sayHelloCppP7JNIEnv_P8_jobject.
So, what happened here is that you generated a header file with a static native void sayHelloCpp, so there is an extern "C" declaration for the function with signature (JNIEnv *, jclass), but you provide a (JNIEnv *, jobject) function. The compiler uses its default mangling scheme as the signature does not match, and the Java runtime fails to find it.
So, long story short: always regenerate your header file and check C++ function signatures if you change your Java class. I could not reproduce your issue because I wrote a Makefile that always regenerated the header file after recompiling the Java file.

UnsatisfiedLinkError: no libhello in java.library.path

I'm doing a tutorial about JNI to get comfortable with it for my project. However, I'm stuck on this particular part of the tutorial which is running the Java program. I'm using Eclipse where I've created a Java project and put the files inside a package. When I try to follow the tutorial without using Eclipse, I get no errors, so I assume it's a path related issue.
As for my error, I get an
UnsatisfiedLinkError: no libhello in java.library.path
when I run this in my terminal:
java -Djava.library.path=. helloJNI.HelloJNI
Even when I specify my path, it doesn't work. If you want to reproduce my steps, here's what I did:
After creating HelloJNI.java, I ran the following in the terminal:
javac -h . HelloJNI.java
This command created the generated header file given below.
After this, I wrote HelloJNI.c, navigated to the package folder and then compiled it using this command in the same directory:
gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libhello.so HelloJNI.c
Then I moved up one directory to ~/eclipse-workspace/HelloJNI/src and ran this command:
java -Djava.library.path=. HelloJNI
Which yields the error described above.
Here's my code:
HelloJNI.java
package helloJNI;
public class HelloJNI { // Save as HelloJNI.java
static {
System.loadLibrary("hello"); // Load native library hello.dll (Windows) or libhello.so (Unixes)
// at runtime
// This library contains a native method called sayHello()
}
// Declare an instance native method sayHello() which receives no parameter and returns void
private native void sayHello();
// Test Driver
public static void main(String[] args) {
new HelloJNI().sayHello(); // Create an instance and invoke the native method
}
}
HelloJNI.c
// Save as "HelloJNI.c"
#include <jni.h> // JNI header provided by JDK
#include <stdio.h> // C Standard IO Header
#include "helloJNI_HelloJNI.h" // Generated
// Implementation of the native method sayHello()
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
helloJNI_HelloJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class helloJNI_HelloJNI */
#ifndef _Included_helloJNI_HelloJNI
#define _Included_helloJNI_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: helloJNI_HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_helloJNI_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
I have also tried setting the path to my working directory, but I still get the same result. What am I doing wrong?
I moved up one directory to ~/eclipse-workspace/HelloJNI/src
This means that your libhello.so is in ~/eclipse-workspace/HelloJNI/src/helloJNI directory, isn't it?
Now, to let Java find this library, run
java -Djava.library.path=helloJNI helloJNI.HelloJNI
or use the absolute path
java -Djava.library.path=~/eclipse-workspace/HelloJNI/src/helloJNI helloJNI.HelloJNI

Java JNI "symbol lookup error" with c++ library

I am building a small app on a raspberry pi.
I have a JVM which tries to access a C++ Library called "RCSwitch"
I created a JavaClass
public class NativeRCSwitchAdapter {
private static final NativeRCSwitchAdapter instance = new NativeRCSwitchAdapter();
public static NativeRCSwitchAdapter getInstance(){
return instance;
}
private NativeRCSwitchAdapter(){};
static{
String path = NativeRCSwitchAdapter.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.load(path + "NativeRCSwitchAdapter.so");
}
// methods to redirect to native layer (C++)
public native void switchOn(String group, String channel);
public native void switchOff(String group, String channel);
}
I then ran javac & javah to have java generate my header file for me.
I created a c++ file:
#include "NativeRCSwitchAdapter.h"
#include "RCSwitch.h"
#include <stdio.h>
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
cout<<"teststring output"<<endl;
const char *csGroup = env->GetStringUTFChars(jsGroup, 0);
const char *csChannel = env->GetStringUTFChars(jsChannel, 0);
char sGroup[6];
char sChannel[6];
for (int i = 0; i<5; i++) {
sGroup[i] = csGroup[i];
sChannel[i] = csChannel[i];
}
sGroup[5] = '\0';
sChannel[5] = '\0';
cout<<"ONON"<<endl;
cout<<sGroup<<endl;
cout<<sChannel<<endl;
RCSwitch mySwitch = RCSwitch();
//for testing purposes set to the ELRO Power Plugs
mySwitch.setPulseLength(300);
mySwitch.enableTransmit(0);
mySwitch.setRepeatTransmit(3);
mySwitch.switchOn(sGroup, sChannel);
}
Now this file uses the RCSwitch library which in turn uses the wiringPi library.
Now if i compile i run this:
g++ -shared -I/usr/jdk1.8.0/include -I/usr/jdk1.8.0/include/linux NativeRCSwitchAdapter.cpp -o NativeRCSwitchAdapter.so
Yet I get this error if start everything from java: (simple main, create an instance of my object and run the switchOn()
java: symbol lookup error: /home/pi/applications/Pi-jAutomation433/RCSwitchJNIWrapper/src/NativeRCSwitchAdapter.so: undefined symbol: _ZN8RCSwitchC1Ev
It has been time, since i last coded in C, so please forgive me but I believe it has something to do with the the linking phase of the compiler? Or does the compiler automatically check all dependencies and then their deps until no further dependencies are found and it then links it all nicely together and wraps it in an app?
Oh here is the repo to have an in depth look if anybody cares:
Github repo
Thanks for any help coming my way!
UPDATE
Okay so I managed to get this error away. Turns out (well I kinda knew that already but yeah) I am quiet a duphus when it comes to C++ compiler knowledge. Anyways I managed to get the error changed. I didn't know I had to explicitly tell g++ to include RCSwitch.cpp as well. Okay so now I did. Next error ;-)
I guess this time it should be fairly easy to tackle. I get an undefined symbol "pinMode".
This symbol is part of the wiringPi library. Do I have to include ALL c librarys that are executed in my java file? Or only the one I access and anything after that doesnt matter to java?
Your native function declaration is getting mangled by the c++ compiler. Add extern "C" around your declarations to clear up the issue.
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
#ifdef __cplusplus
}
#endif /* __cplusplus */
Edit:
You need to include all other objects/libraries into your creation of the shared library.
See this Dynamic Link Library Q/A.

JNI: Library is Found on Path, but Method is not (java.lang.UnsatisfiedLinkError)

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
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.
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
Just guessing... Is your dll depends on another dll that is not on the path? MinGW modules usually depend on specific C runtime library.
I had the same issue and the flag -Wl,-kill-at worked for me.
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)

Categories

Resources