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.
Related
Am just starting learn jni and decided to do a hello world. i created a helloworld java file whose code is
public class HelloWorld {
public native void displayHelloWorld();
static{
System.loadLibrary("hello");
}
public static void main(String[] args){
new HelloWorld().displayHelloWorld();
}
}
and i compiled it to get the following header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: displayHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
followed by creating a c file whose code is
#include<jni.h>
#include "HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj){
printf("Hello World \n");
return;
}
and compiled it to create the helloworld.o file
and now when i tried to create the .dll file using the command
gcc -shared -o hello.dll HelloWorld.o -Wl,--add-stdcall-alias
i get this error
At line:1 char:42
+ gcc -shared -o hello.dll HelloWorld.o -Wl,--add-stdcall-alias
+ ~
Missing argument in parameter list.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument
what am i missing?
The problem is that in PowerShell comma has a special meaning of comma operator unlike, e.g., in Bash, where in such context there is no any special meaning to it. When your command is parsed, -Wl is considered a parameter name and , is interpreted to form an array (parameter list) as its argument. As seen in linked documentation, , is necessarily binary operator unless in expression mode, whereas for command invocation argument mode is used. But there's no left argument for this comma, thus Missing argument in parameter list error.
To solve this, you can change the command to make PowerShell stop intepreting , in a special way by making -Wl,--add-stdcall-alias a quoted string using one of the types of strings. For our simple case any string type would do, e.g. with single-quoted strings we have:
gcc -shared -o hello.dll HelloWorld.o '-Wl,--add-stdcall-alias'
Another option is to use stop parsing token --%:
gcc --% -shared -o hello.dll HelloWorld.o -Wl,--add-stdcall-alias
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?
I'm trying to emend a JNI function that takes a string argument from the java layer.
Initially, the java code from the .java file was:
callJNIMethod();
I'm trying to change it so that I can give it a string, like this:
String arg = "abcd";
callJNIMethod(arg);
In my JNI code, this is what the function previously looked like:
JNIEXPORT jboolean JNICALL Java_com_file_app_Activity_callJNIMethod(
JNIEnv *env, jclass clazz){
//use the string
}
This is what I changed it to (in the wrapper .cpp file and the header .h file):
JNIEXPORT jboolean JNICALL Java_com_file_app_Activity_callJNIMethod(
JNIEnv *env, jclass clazz, jstring str);
I'm getting an error that the method callJNIMethod cannot be applied to the given types.
required: no arguments
found: java.lang.String
reason: actual and formal argument lists differ in length
Note: Some input files use or override a deprecated API.
Any idea what's wrong?
You went astray by editing the .h file. Changes to native methods should begin in your Java source.
The .h file should be generated by javah from the compiled .class file. You can set up your build system to re-run javah with each build. That way you'll see right away if the native method implementations no longer match the native declarations in your Java source.
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)
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.