JNI - java.lang.UnsatisfiedLinkError but right functions signatures - java

First, I'm using JDK 8 32bits with Eclipse 32bits.
I'm trying to figure out how to solve this error:
java.lang.UnsatisfiedLinkError :
I checked signature's functions in C and package in JAVA but it looks right.
The complete error is :
Exception in thread "main" java.lang.UnsatisfiedLinkError:
fr.Model.initModel(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)V
Java's function:
void initModel(String, String, String, int[]) in package fr and class Model
C function:
JNIEXPORT void JNICALL Java_fr_Model_initModel (JNIEnv *, jobject, jstring, jstring, jstring, jintArray);
Launching it in a separate JVM but it does find the .dll. All dependencies checked, they are in system32.
Already doing the extern "C" { ... } trick to deal with C++.
Any ideas?

Related

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.

G++ compiled DLL has different function names

I'm trying to write a set of JNI calls to use some C++ code in my java project. When I write it as C code and compile with GCC, it seems to be fine and the function names are correct (Java_myPackage_MyClass_myFunction).
But I'm finding it easier to do what I want in C++ instead of C. When I try to compile the code I have with C++, the header files for all I can see are correct, everything looks fine, but when I compile it, the dll generated by g++ causes this error in my java code:
Exception in thread "main" java.lang.UnsatisfiedLinkError: package.class.function(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
at package.class.function(Native Method)
When I check the dll in dependency walker, the function has the wrong name to it, it's been changed to _Z52Java_package_class_functionP7JNIENV_P8_jobjectP8_jstringS4_
when I think it should just be showing up in dependency walker as Java_package_class_function
This is the command I'm using to compile the dll with g++
g++.exe -Wl,--add-stdcall-alias -I "C:\Program Files\Java\jdk1.8.0_73\include" -I "C:\Program Files\Java\jdk1.8.0_73\include\win32" -shared -o C:/repos/myproject.dll myproject.cpp -lssl -lcrypto
Is there something I'm missing in compilation? I do have the functions in my .h file listed as JNExport and wrapped in extern "C" {}. I'm just not entirely clear why the function naming/calls get changed when it compiles.
Environment is G++ compiling in Cygwin on Win7 with JDK 1.8 for Java.
function declarations in my .h file:
extern "C" {
JNIEXPORT jstring JNICALL Java_package_class_function(JNIEnv *, jclass, jstring, jstring);
JNIEXPORT jstring JNICALL Java_package_class_function(JNIEnv *, jclass, jstring, jstring);
char * aes_encrypt_string(char *, char *, char *);
char * aes_decrypt_string(char *, char *, char *);
}
I have the externc on there, and the aes functions are being exported correctly in the .dll file, but the other 2 functions are getting the extra decoration.
C++ compilers uses name mangling to create unique entries in the symbol table. This isn't needed in C code since each function must already be unique. To prevent name mangling wrap your function definitions in extern "C" { }
For more information see in-c-source-what-is-the-effect-of-extern-c and a duplicate of your question.

Why isn't JNIOnLoad running?

I have an Android project with a Java base Activity, Java JNI interface class, and native code. I have the proper
System.loadlibrary(_libraryname_);
In my Java interface class and from the logcat output:
11-29 15:11:20.737: D/dalvikvm(8940): No JNI_OnLoad found in /data/data/com.example.testjni/lib/libTestJNI.so 0x406ef030, skipping init
In my cpp file header:
extern "C" {
JNIEXPORT jint JNICALL JNIOnLoad(JavaVM *, void *);
}
In the cpp file:
JNIEXPORT jint JNICALL JNIOnLoad(JavaVM *vm, void *reserved)
{
LOGI("JNIOnLoad");
jvm = vm;
return JNI_VERSION_1_6; /* the required JNI version */
}
But still the OnLoad function never is called. I've tried uninstalling the app and re-installing it but it never runs.
Function signature is JNI_OnLoad. There is an underscore / _ between JNI and OnLoad.

Another JNI, C++, DLL, UnsatisfiedLinkError <Native Method>

I've been looking for 2 days now and no solution could help me, so here we go again:
How to fix the UnsatisfiedLinkError... in JNI?
So here's my java code:
package org.lingenio.util;
import java.util.*;
public class PTAPIWrapperForOmegaT {
private native String translateWithPTAPI(String sentence);
private native void test();
public PTAPIWrapperForOmegaT(String sentence) throws Exception{
System.out.println(sentence);
test();
}
static {
System.load("C:/Users/michael/Desktop/OmegaT/OmegaT2.3_src/native/PTAPIWrapperForOmegaT.dll");
}
}
And here's my C++ Code:
#include <iostream>
#include <windows.h>
#include <jni.h>
#include "PTAPIWrapperForOmegaT.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)
{
/* stuff */
}
JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test(JNIEnv *, jobject)
{
cout << "This comes from PTAPIWrapperForOmegaT.cpp test();" << endl;
}
int main(){
return 0;
}
And the header file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class PTAPIWrapperForOmegaT */
#ifndef _Included_PTAPIWrapperForOmegaT
#define _Included_PTAPIWrapperForOmegaT
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: PTAPIWrapperForOmegaT
* Method: translateWithPTAPI
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
(JNIEnv *, jobject, jstring);
/*
* Class: PTAPIWrapperForOmegaT
* Method: test
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_PTAPIWrapperForOmegaT_test
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
and how I build it:
call g++ -Wl,--add-stdcall-alias -c -DBUILDING_EXAMPLE_DLL -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp
call g++ -shared -Wl,-kill-at -o PTAPIWrapperForOmegaT.dll -I G:/Software/Java/jdk1.7.0_01/include -I G:/Software/Java/jdk1.7.0_01/include/win32 PTAPIWrapperForOmegaT.cpp
and finally, the error:
10211: Error: Uncatched exception in thread [Thread-14]
10211: Error: java.lang.UnsatisfiedLinkError: org.lingenio.util.PTAPIWrapperForOmegaT.test()V
10211: Error: at org.lingenio.util.PTAPIWrapperForOmegaT.test(Native Method)
10211: Error: at org.lingenio.util.PTAPIWrapperForOmegaT.<init>(PTAPIWrapperForOmegaT.java:13)
10211: Error: at org.omegat.core.machinetranslators.LingenioTranslate.translate(LingenioTranslate.java:32)
10211: Error: at org.omegat.core.machinetranslators.BaseTranslate.getTranslation(BaseTranslate.java:64)
10211: Error: at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:122)
10211: Error: at org.omegat.gui.exttrans.MachineTranslateTextArea$FindThread.search(MachineTranslateTextArea.java:102)
10211: Error: at org.omegat.gui.common.EntryInfoSearchThread.run(EntryInfoSearchThread.java:85)
I don't know exactly about these two lines of g++ here, I think the second one would be sufficient, but some tutorial must have offered the other line as well and I kept it.
I'm on Windows 7, using MingW and the latest Java (1.7xxx I believe).
Any help is appreciated, I suspect the error lies in the compilation, but I just don't know how to go on from here.
EDIT:
Looking into the dll with DependencyWalker I can see the functions are named like I named them in the .cpp file. Of course I am calling them from the Java Wrapper with their respective names, i.e. test(). Could that be a problem? Can someone who used JNI often in the past tell me whether this is the correct way?
Turns out all the code is fine. Actually I did make mistakes compiling the header files. You can see if you look at the header files' function names, i.e.:
JNIEXPORT jstring JNICALL Java_PTAPIWrapperForOmegaT_translateWithPTAPI
(JNIEnv *, jobject, jstring);
Now, take a look at your Java files' package membership, in my case:
package org.lingenio.util;
Because I did compile the header file the wrong way, JNI was later not able to find the symbols it was looking for, because it was actually looking for this:
JNIEXPORT jstring JNICALL Java_org_lingenio_util_PTAPIWrapperForOmegaT_translateWithPTAPI(JNIEnv *env, jobject obj, jstring sentence)
So, good luck to the people out there dangling with the same problems. I'm obviously not the greatest Java programmer, that's why I had to worry about this for so long. I should have compiled my header files in the correct way in the first place.
Check your package and classpath!

JNI invocation fails with UnsatisfiedLinkError

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.

Categories

Resources